skip to Main Content

Storing all my Mongo properties in an external application.properties files and reading them with the provided classes. I’m using them in a bean to build DatabaseFactory objects, but they read null when the bean is accessed – even after reading properly at startup.

MongoProperties.java

package com.mistermorse.flawlessbeansjavaspringapi.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "spring.data.mongodb")
public class MongoProperties {

    private String uri;
    private String database;
   
    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    public String getDatabase() {
        return database;
    }

    public void setDatabase(String database) {
        this.database = database;
    }

}

MongoApplication.java

package com.mistermorse.flawlessbeansjavaspringapi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan("com.mistermorse.flawlessbeansjavaspringapi")
public class FlawlessBeansJavaSpringApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(FlawlessBeansJavaSpringApiApplication.class, args);
    }

}

So, I’m working on user functionality right now, but I made a temporary endpoint to check the property logic, and I’m having trouble using a bean to create a factory.

UserController.java

package com.mistermorse.flawlessbeansjavaspringapi.controller;

import com.mistermorse.flawlessbeansjavaspringapi.config.MongoGeneral;
import com.mistermorse.flawlessbeansjavaspringapi.config.MongoProperties;
import com.mongodb.client.MongoIterable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@EnableConfigurationProperties(MongoProperties.class)
@RestController
@RequestMapping("/user")
public class UserController {

    private ApplicationContext applicationContext;
    private MongoProperties mongoProperties;

    @Autowired
    public UserController(MongoProperties mongoProperties) {
        this.applicationContext = new AnnotationConfigApplicationContext();
        this.mongoProperties = mongoProperties;
    }

    @GetMapping("/collections")
    public MongoIterable<String> getCollections() {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MongoGeneral.class);
        SimpleMongoClientDatabaseFactory simpleMongoClientDatabaseFactory = applicationContext.getBean("mongoDbFactory",SimpleMongoClientDatabaseFactory.class);
        return simpleMongoClientDatabaseFactory.getMongoDatabase("db").listCollectionNames();
    }

}

Now, here’s the problem; I have a config file with a bean method to return a SimpleMongoClientDatabaseFactory using MongoClients.create() and a URI; at first, I tested it with the direct address containing u/p. It worked. Now, I’m trying to use the ConfigurationProperties.

MongoGeneral.java

package com.mistermorse.flawlessbeansjavaspringapi.config;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.lang.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;

@Configuration
@EnableConfigurationProperties(MongoProperties.class)
public class MongoGeneral extends AbstractMongoClientConfiguration {

    private MongoProperties mongoProperties;

    @Autowired
    public MongoGeneral(MongoProperties mongoProperties) {
        this.mongoProperties = mongoProperties;
    }

    @Override
    @NonNull
    public String getDatabaseName() {
        System.out.println("Name: " + this.mongoProperties.getDatabase());
        return this.mongoProperties.getDatabase();
    }

    @Override
    @NonNull
    public MongoClient mongoClient() {
        return MongoClients.create("mongodb+srv://matthewharp:[email protected]/?retryWrites=true&w=majority&appName=Cluster0");
    }

    @Bean
    @NonNull
    @Override
    public SimpleMongoClientDatabaseFactory mongoDbFactory() {
        return new SimpleMongoClientDatabaseFactory(mongoClient(), getDatabaseName());
    }

}

Here’s the problem: when the app starts, the log in getDatabase() dumps out the correct property value, whether I have the hard-coded value or the property, AND the factory gets made if the hard-coded value is used when the bean method is called.

However, the value is only correct if hard-coded when the bean is accessed during the controller call; the property value is null otherwise, and the factory creation fails.

I’ve even tried to attach property values to member fields in MongoGeneral.java in the constructor, but it still shows null when accessed via the controller. Can anyone see, and help with, what I’m doing wrong?

2

Answers


  1. I’ve tried to reproduce your case and with a main/resources/application.properties

    spring.data.mongodb.database=plop
    

    It seems to work when i test the connection from a test with **@Import(MongoGeneral.class)**

    Your problem comes from your controller.
    Why did you recreate an empty ApplicationContext ?

    To make a request you can @Autowired a MongoClient and use it

    mongoClient.getDatabase("plop").listCollectionNames()
    

    or a @Autowired MongoTemplate and use it

        return mongoTemplate.getCollectionNames()
    

    Example

    @Import(MongoGeneral.class)
    @EnableConfigurationProperties(MongoProperties.class)
    @RestController
    @RequestMapping("/user")
    public class UserController {
    
        @Autowire
        MongoTemplate mongoTemplate;
    
        @GetMapping("/collections")
        public List<String> getCollections() {
            return mongoTemplate.getCollectionNames()
        }
    
    }
    
    

    (sorry if the code is not perfect, i’ve actually coded it in kotlin)

    Login or Signup to reply.
  2. How about this for UserController.java:

    package com.mistermorse.flawlessbeansjavaspringapi.controller;
    
    // skip import statements    
    
    @Import(MongoGeneral.class)
    @EnableConfigurationProperties(MongoProperties.class)
    @RestController
    @RequestMapping("/user")
    public class UserController {
    
        private MongoDatabaseFactory mongoDatabaseFactory;
    
        @Autowired
        public UserController(MongoDatabaseFactory mongoDatabaseFactory) {
            this.mongoDatabaseFactory = mongoDatabaseFactory;
        }
    
        @GetMapping("/collections")
        public MongoIterable<String> getCollections() {
            return mongoDatabaseFactory.getMongoDatabase("db").listCollectionNames();
        }
    

    DISCLAIMER: I have not tried to compile the above code, this is just to illustrate what is wrong with the original code. It may contain minor errors.

    Basically, you are creating a brand new Spring application context that does not know anything about your properties files. Instead, you should just let Spring do it’s magic and inject a ready-made MongoDatabaseFactory (or even better, an instance of MongoTemplate as suggested by Tokazio in the other answer).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search