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
I’ve tried to reproduce your case and with a main/resources/application.properties
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
or a @Autowired MongoTemplate and use it
Example
(sorry if the code is not perfect, i’ve actually coded it in kotlin)
How about this for
UserController.java
: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 ofMongoTemplate
as suggested by Tokazio in the other answer).