How to access Configuration objects in Spring Boot - spring

Spring Boot has a mechanism for accessing the contents of .properties (or YAML) files that one might want to include in an application.
I currently have a dbase.properties file (residing in src/main/resources) that contains the following information:
app.dbase.name=MyDbase
app.dbase.connect=jdbc:postgresql://localhost:5432
app.dbase.user=auser
app.dbase.password=mypassword
As described in various Spring Boot documents and examples, I have a configuration class that is defined below:
#Configuration
#PropertySource("dbase.properties")
#ConfigurationProperties(prefix = "app.dbase")
public class DbInfo
{
private String name;
private String connect;
private String user;
private String password;
// Getters and setters left out for brevity
}
Unfortunately, while the various documents and examples give good information on how to define a configuration
class, I have been unable to find any description on how to use it! Apparently, a Spring Boot web application
creates an instance of a configuration class upon startup (and it looks like it also initializes them with the
values from the properties file) but my attempts to guess how to access its contents when I need to have failed.
The method of doing so is probably simple, but no one seems to want to describe this method anywhere.
So: how does one access and use one of these configuration classes once they are instantiated?

Note that #ConfigurationProperties would require all of the properties in your file to be prefixed with 'app.dbase', as in 'app.dbase.username' and 'app.dbase.password'. If that's the case, the class you have now should work.
You would call it like this:
#Component
public class Component {
#Autowired DbInfo dbInfo;
public method() {
String username = dbInfo.username();
}
}
If you are having issues, you may be required to add this to a Configuration class:
#Configuration
public class Config {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
...
}
You may also need to add 'classpath:' inside your annotation, as in: #PropertySource("classpath:dbase.properties"), assuming your properties file is in your src/main/resources.

Related

how to use ConfigurationProperties with Inheritance to bind properties value from yml file

I am trying bind properties from yml file to object by using inheritance but getting null values in binding.
I have three classes, StoreConfig is superclass, MessageStoreConfig is subclass and MongodbMessageStore is also subclass of MessageStoreConfig.
#ConfigurationProperties(prefix = "store")
public class StoreConfig {}
#ConfigurationProperties(prefix = "messagestore")
public class MessageStoreConfig extends StoreConfig {}
#Component
#ConfigurationProperties(prefix = "config.mongodb")
public class MongoDbMessageStoreConfig extends MessageStoreConfig {
#Value("${connection_string}")
private String connectionString;
public String getConnectionString() {
return connectionString;
}
public void setConnectionString(String connectionString) {
this.connectionString = connectionString;
}
}
application.yml file
store:
messagestore:
config:
mongodb:
connection_string: mongodb://localhost:27017
I am getting below error.
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'connection_string' in value "${connection_string}"
Am i doing the right thing or spring does not support this kind of binding?
Spring does not work this way. First of all you should not mix #ConfigurationProperties and #Value. They are two different ways of importing configuration from properties. So even if you annotated your class like this:
#ConfigurationProperties(prefix = "store.messagestore.config.mongodb")
and then use #Value("${connection_string}") it still would not work because #Value and #ConfigurationProperties can not be mixed.
Similar question is discussed here: https://github.com/spring-projects/spring-boot/issues/2254
Also extending a class which is annotated with #ConfigurationProperties with another class which is also annotated with #ConfigurationProperties does not mean that prefixes will be concatenated together. This means that Spring expects multiple sets of properties from you (ones of the superclass and ones of the subclass) and they are not related to each other at all.

Inject/Access Spring Bean into Log4j2 Plugin

I have a configuration properties class that I want to inject into a custom log4j2 RewritePolicy.
e.g.
#Plugin(name = "MyPolicy", category = "Core", elementType = "rewritePolicy", printObject = true)
public class MyPolicy implements RewritePolicy {
private MyPolicyProperties myPolicyProperties; // <-- want to inject/autowire this
public MyPolicy() {}
#PluginFactory
public static MyPolicy createPolicy() {
return new MyPolicy();
}
#Override
public LogEvent rewrite(LogEvent logEvent) {
// do something with myPolicyProperties here
return Log4jLogEvent.newBuilder()
.setLoggerName(logEvent.getLoggerName())
.setMarker(logEvent.getMarker())
.setLoggerFqcn(logEvent.getLoggerFqcn())
// ... etc
.build();
}
}
#ConfigurationProperties("app.mypolicy")
#Getter
#Setter
public class MyPolicyProperties {
private String property1;
private int property2;
// ... etc
}
I've tried implementing an ApplicationListener to reconfigure log4j as described here but was can't seem to get the appender and/or rewritepolicy to configure. Also tried implementing ApplicationContextAware described here but also didn't work.
Is there anyway to access the MyPolicyProperties in MyPolicy?
It can be done but it is almost never pretty. This is because Log4j Plugins are loaded by Log4j's plugin system while Spring Beans are loaded by Spring. Furthermore, they are not instantiated at the same time.
If you are using Spring Boot the very first thing that will happen is for Log4j2 to initialize because SpringApplication requests a Logger. So there would be no way to resolve the Spring Bean at that point as it doesn't exist. Later, Spring's bootstrap process will initialize Log4j again and then during application setup it will initialize once or twice more. During these subsequent initializations the bean may be available.
Depending on the type of application you are using you may be able to locate Spring's ApplicationContext so that you can call getBean() and inject it.
There is no automatic way to do this via an annotation or something similar.
The simplest way to do it is to either add a static method in the target class that gets initialized to reference itself when Spring is initialized or to create another class with a method that initializes a static method to reference the Spring created bean. So Spring will cause these static methods to reference the bean it creates. Then have your Log4j plugin call that static method to get the bean reference. Once it is non-null you can save it in the plugin and after that it should function as you want.

Spring cannot read from property file

have a class which is annotated with #Service, trying to read a property using values, the value always comes as zero
#Service
public class custom implements Conditional
{
#Value("${test.property}")
private boolean properttyValue;
}
#test.properties
test.property= true
If i provide any non existent value in #Value, it throws an error , which means property is loaded,
Any idea whats wrong ?
This is because test.propeties not in the class path. If you are using Spring boot use -Dspring.config.location to specify external configurations. You can do this using the following ways
-Dspring.config.location=your/config/dir/
-Dspring.config.location=classpath:pro1.properties,classpath:prop2.properties
If you are not using Spring boot, this can be done using
#Configuration
#PropertySource("classpath:test.properties")
public class PropertiesWithJavaConfig {
//...
}
#Configuration
#PropertySource({
"classpath:test.properties"
})
public class PropertiesWithJavaConfig {
//...
}
For external file "file:/" can be used instead of classpath e.g. file:./,file:./config/

SpringBoot difference between #Configuration and #configurationProperties

From spring boot documentation, #ConfigurationProperties will
generate your own configuration metadata file from items annotated
with #ConfigurationProperties
I tried use #Configuration and #ConfigurationProperties separately on my configuration class.
#Component
//#Configuration
#ConfigurationProperties
#EnableSpringDataWebSupport
#EnableAsync
public class AppConfig {
...
}
I didn't see any noticable difference.
What's the usage of #ConfigurationProperties or #Configuration?
#Configuration is used to create a class the creates new beans (by annotating its methods with #Bean):
#Configuration
public class CustomConfiguration {
#Bean
public SomeClass someClass() {
return new SomeClass();
}
}
#ConfigurationProperties binds external configuration into the fields of the class which it annotates. It's common to use it with a #Bean method to create a new bean that encapsulates configuration which can be controlled externally.
Here's a real world example of how we've used it. Consider a simple POJO that holds some values related to connecting to ZooKeeper:
public class ZookeeperProperties
{
private String connectUrl;
private int sessionTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(5);
private int connectTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(15);
private int retryMillis = (int) TimeUnit.SECONDS.toMillis(5);
private int maxRetries = Integer.MAX_VALUE;
// getters and setters for the private fields
}
Now we can create a bean of type ZookeeperProperties and automatically populate it using external configuration:
#Configuration
public class ZooKeeperConfiguration {
#ConfigurationProperties(prefix = "zookeeper")
#Bean
public ZookeeperProperties zookeeperProperties() {
// Now the object we create below will have its fields populated
// with any external config that starts with "zookeeper" and
// whose suffix matches a field name in the class.
//
// For example, we can set zookeeper.retryMillis=10000 in our
// config files, environment, etc. to set the corresponding field
return new ZookeeperProperties();
}
}
The benefit of this is that it's less verbose than adding #Value to every field of ZookeeperProperties. Instead, you provide a single annotation on the #Bean method and Spring automatically binds any external configuration it finds with the matching prefix to the fields of that class.
It also lets different users of my class (i.e. anyone who creates a bean type of ZookeeperProperties) use their own prefix to configure the class.
The use case of ConfigurationProperties is for externalizing configuration.
#Configuration
Indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.
#ConfigrationProperties
-- Is added to a class definition or a #Bean method in a #Configuration class if you want to bind and validate some external Properties (e.g. from a .properties file).
See the screenshot to differentiate #Value from #ConfigurationProperties.
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties

How to configure a custom source to feed Spring Boot's #ConfigurationProperties

I'm new to Spring Boot and reading about how #ConfigurationProperties annotation enables auto-injection of field values without #Value annotation.
#Configuration
#ConfigurationProperties(locations = "classpath:some.properties", prefix = "something")
public class MyConfiguration { .. }
I'd like to use Groovy's ConfigSlurper to read my property configuration. Is there a way to associate #ConfigurationProperties with a custom property reader, may be a custom extension of Spring class that deals with ConfigSlurper? Or is there a way to simulate the same behavior with a different feature?
That's not what #ConfigurationProperties is meant to do. #ConfigurationProperties binds whatever is available from the Environment. The locations attribute is deprecated in 1.4 and will be removed in a future release.
The idea is that you specify a prefix and if they are keys matching that prefix in the environment, we inject the relevant properties in your POJO.If you want to use that infrastructure with this mechanism, please remove the locations attribute on the annotation and update the environment with your own property source. The other answer gives you a way to do that and you can use an EnvironmentPostProcessor to hook your implementation to the environment.
You can do so by implementing your own PropertySourceLoader:
public class ConfigSlurperPropertySourceLoader implements PropertySourceLoader {
#Override
public String[] getFileExtensions() {
return new String[] { "groovy" };
}
#Override
public PropertySource<?> load(String name, Resource resource, String profile) throws IOException {
ConfigObject source = new ConfigSlurper(profile).parse(resource.getURL());
return new ConfigObjectPropertySource(name, source);
}
}
Extending PropertySource<T> to read values from ConfigObject (the ConfigObjectPropertySource above). Then you register it inside META-INF/spring.factories:
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.example.ConfigSlurperPropertySourceLoader,\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
The spring-groovy-config already implements it and is available on github.

Resources