I'm trying to inject value from properties file into a spring configuration class as follows:
#Configuration
#EnableWebMvc
public class ImagesContext {
#Autowired
#Value("${some.property.com}")
private String property;
#Bean
public MyClass getMyClass() {
return new MyClass(property);
}
}
But the property is not injected correctly. Instead, when I debug, I see that the property string contains ${some.property.com} and not the string value itself.
The #Value annotation is processed by AutowiredAnnotationBeanPostProcessor which is typically registered if you have a <component-scan> or <annotation-config> configuration in XML (or directly with a bean definition). You need to add either of those, probably <annotation-config>
Add the following bean declaration in your Config class if not done already
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
In order for #Value annotations to work PropertySourcesPlaceholderConfigurer should be registered. It is done automatically when using <context:property-placeholder> in XML, but should be registered as a static #Bean when using #Configuration.
Related
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
I am trying to use the #Value annotation on a variable to assign a value, but the value getting assigned is null. What am I doing wrong? I am using the java spring configuration using #Configuration. I am also using #PropertySource to point to a properties file.
#Configuration
#PropertySource("classpath:application.properties")
public class SpringConfiguration {
#Value("${app_prop_1}") Integer aValue;
#Bean
public Integer getInteger() {
return new Integer(aValue); // throws NullPointerException
}
}
The application.properties files is in the maven resources folder of project. It has following content:
app_prop_1=2000
When you add #Value annotation, there is a need to add
//To resolve ${} in #Value
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
This will resolve the ${} expression.
Source
I have a property source defined in Spring where I am trying to load a properties file present in the classpath
#Configuration
#PropertySource(name = "props", value = "classpath:prod.properties")
public class PropertyPlaceholderConfigurerConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[]
{ new ClassPathResource("prod.properties") };
ppc.setLocations(resources);
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
#Value("${DATABASE_NAME}") private String DATABASE_NAME;
#Bean
public String test() {
System.out.println(DATABASE_NAME);
}
}
My prod.properties file has 1 entry
DATABASE_NAME=proddb_123
Now in the test() bean prints as proddb_123
Two problems I am facing is
I have another class in a different package where I am trying to
inject it
#Value("${DATABASE_NAME}") private String DATABASE_NAME;
But the value of this is always "${DATABASE_NAME}"
I have different gradle projects like webapplication-gradle is one
where the #PropertySouce configuration is defined and another
project like storage-gradle. Can I inject the DATABASE_NAME in any
class present in storage-gradle project.
In the other classes where you would like to inject the #Value(${DATABASE_NAME}") you have to also yse the #PropertySource annotation. Another option is to remove the #PropertySource annotations and in the applicaiton-context.xml defined a bean of class org.springframework.beans.factory.condig.PropertyPlaceholderConfigurer and for the property locations list add a value for the classpath:prod.properties
In Spring XML, I can define a bean that instantiates a class annotated with #Configuration. When I do, that bean is post-processed. Any methods inside that class with #Bean are also added to the container. How do I perform a similar post-processing in JavaConfig?
Here's the XML version:
<bean id="test" class="com.so.Test">
<property name="prop" value="set before instantiating #Beans defined in Test"/>
</bean>
The associated Test class:
#Configuration
class Test {
private String prop;
void setProp(final String prop) {
this.prop = prop;
}
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
If I use Spring XML Config, both test and needThisBeanToo are available in the container. needThisBeanToo is added via a BeanPostProcessor, though I can't recall which one. If I use JavaConfig, only test is available in the container. How do I make needThisBeanToo available to the container? #Import would work, except that prop being set is required for needThisBeanToo to be initialized correctly.
The part that makes all of this complicated is that Test is vended from a library I'm consuming. I don't control Test, nor can I change it. If I drive it from JavaConfig, it would look like this:
#Configuration
class MyConfiguration
{
#Bean
Test test() {
Test test = new Test();
test.setProp("needed to init `needThisBeanToo` and others");
return test;
}
}
The JavaConfig example does not instantiate needThisBeanToo despite it being defined in Test. I need to get needThisBeanToo defined, preferably without doing it myself, since I don't want to copy code I don't own. Delegation isn't attractive, since there are a number of subsequent annotations/scopes defined on needThisBeanToo (and others defined inside Test).
Your problem is is that you're ignoring the #Configuration annotation completely. Why is that?
When code reaches this line Test test = new Test(); it just doesn't do anything with #Configuration. Why? Because annotation is not something that a constructor is aware of. Annotation only marks some meta-data for the class. When spring loads classes it searches for annotations, when you call a constructor of a class, you don't. So the #Configuration is just ignored because you instantiate Test with new Test() and not through spring.
What you need to do is to import Test as a spring bean. Either via XML as you showed in your question OR using #Import. You problem with prop is that the setter isn't called because that's just not the way to do it. What you need to be doing is either do something like that:
#Configuration
class Test {
private String prop = "set before instantiating #Beans defined in Test";
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
Or to create a property in spring (this is a different subject) and inject the value:
#Configuration
class Test {
#Autowired
#Value("${some.property.to.inject}") // You can also use SPeL syntax with #{... expression ...}
private String prop;
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
You can also create a bean of type String and inject it as follows:
#Configuration
class Test {
#Autowired
#Qualifer("nameOfBeanToInject")
private String prop;
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
In the last case you can define your original MyConfiguration with this bean:
#Configuration
#Import(Test.class)
class MyConfiguration
{
#Bean(name = "nameOfBeanToInject")
String test() {
return "needed to init `needThisBeanToo` and others";
}
}
In any case you have to import Test either using #Import or as a normal XML bean. It won't work by calling the constructor explicitly.
Here's a way to handle vended #Configuration classes that require some properties to be set prior to creating their #Beans:
Vended #Configuration class:
#Configuration
class Test {
private String property;
public setProperty(final String property) {
this.property = property;
}
#Bean
PropertyUser propertyUser() {
return new PropertyUser(property);
}
#Bean
SomeBean someBean() {
// other instantiation logic
return new SomeBeanImpl();
}
}
Here's the consuming #Configuration class:
#Configuration
class MyConfig {
#Bean
static String myProperty() {
// Create myProperty
}
/**
* Extending Test allows Spring JavaConfig to create
* the beans provided by Test. Declaring
* Test as a #Bean does not provide the #Beans defined
* within it.
*/
#Configuration
static class ModifiedTest extends Test {
ModifiedTest() {
this.setProperty(myProperty());
}
#Override
#Bean
SomeBean someBean() {
return new SomeBeanCustomImpl(this.propertyUser());
}
}
I've created a Spring #Configuration annotated class and I want to autowire a ResourceLoader to it so that I can use it in one of the #Bean methods to lookup a file given by a String. When I am running the app and initialising the context I get a NPE accessing the autowired field, and in debug mode it is shown as being null/not set. Am I wrong expecting the resourceLoader to be present? Am I wrong asserting the autowiring of the Configuration bean happens before its methods get called? The xml configuration loading this bean is tagged with <context:annotation-config/>
#Configuration
public class ClientConfig {
#Autowired
private ResourceLoader resourceLoader;
public #Bean
String configHome() {
return System.getProperty("CONFIG_HOME");
}
public #Bean
PropertiesFactoryBean appProperties() {
String location = "file:" + configHome() + "/conf/webservice.properties";
PropertiesFactoryBean factoryBean = new PropertiesFactoryBean();
factoryBean.setLocation(resourceLoader.getResource(location));
return factoryBean;
}
}
I'm not sure whether this is a bug or is the expected behavior. Sometimes it worked for me, sometimes didn't. Anyway, there is another way of achieving what you want:
public #Bean PropertiesFactoryBean appProperties(ResourceLoader resourceLoader) {
// resourceLoader is injected correctly
...
}