Setting class level annotation values from application.yml - spring

I was trying to externalise my configuration of a class level annotation using allication.yaml. But the spring is not loading it right. Any idea how to do this ?
Here is my Service classI am trying to set
#Service
#DefaultProperties(threadPoolProperties = {
#HystrixProperty(name = "coreSize", value =
"${cyclone.hystrix.lease.thread.coreSize}") })
public class LeaseService {
}
And application.yml
cyclone:
hystrix:
lease:
thread:
coreSize: 10
Getting an error --
java.lang.IllegalArgumentException: bad property value. property name 'coreSize'. Expected int value, actual = ${cyclone.hystrix.lease.thread.coreSize}
I can load the same property using #Value("${cyclone.hystrix.lease.thread.coreSize}"). But not working on the above mentioned case.
Any help on how to properly configure this ?

In order to make spring evaluate placeholders you need to register a PropertySourcesPlaceholderConfigurer bean using a static #Bean method when using #Configuration classes as follows:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
According to the JavaDoc:
Specialization of PlaceholderConfigurerSupport that resolves ${...} placeholders within bean definition property values and #Value annotations against the current Spring Environment and its set of PropertySources.

Related

How to filter properties using prefix with BridgePropertyPlaceholderConfigurer in Camel

Is there a way to filter by property prefix when reading in values using #Value annotation in Camel? I'm using the BridgePropertyPlaceholderConfigurer to specify the property files and I've tried setting #ConfigurationProperties(prefix = "SlowEndpoint") on the property bean, but it seems to ignore it.
ConfigurationProperties and Value annotations were not meant to be used together in this scenario. After removing #Value annotations, the ConfigurationProperties annotation picked up properties that were named identical to the property bean members.
public class SlowEndpointConfiguration extends BaseHystrixConfigurationDefinition {
#Configuration
#Profile({"default", "local"})
#PropertySource("classpath:/properties/local/hystrix/SlowEndpointHystrix.properties")
#ConfigurationProperties(prefix = "SlowEndpoint")
static class Defaults extends SlowEndpointConfiguration{
}
}

Set int value from properties file using spring

Trying to bind int value from properties file using spring
But everytime getting below exception :
Failed to instantiate [org.springframework.amqp.rabbit.connection.ConnectionFactory]:
Factory method 'connectionFactory' threw exception; nested exception is java.lang.NumberFormatException: For input string: \"${rabitmq.server.host.connectionclosetimeout}\"
Caused by: java.lang.NumberFormatException: For input string: \"${rabitmq.server.host.connectionclosetimeout}\""}}
My properties file look like below :
rabitmq.server.host.connectionclosetimeout=30000
My Bean
#Value("${rabitmq.server.host.connectionclosetimeout}")
private int connectionCloseTimeOut;
Configuration Class
#Configuration
#PropertySource("classpath:config/service.properties")
public class RabbitMqConfiguration {
#Value("${rabitmq.server.host.connectionclosetimeout}")
private Integer connectionCloseTimeOut;
/**
* Establishing connection
*
* #return
*/
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host);
connectionFactory.setCloseTimeout(connectionCloseTimeOut);
return connectionFactory;
}
}
If I add below bean then its working fine. But I want to work without below bean
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
I have also tried with below method :
#Value("#{new Integer.parseInt('${rabitmq.server.host.connectionclosetimeout}')}")
private int connectionCloseTimeOut;
It's also not working.
Please suggest what is way to get it working.
Your SPEL isn't correct. This should work
#Value("#{ T(java.lang.Integer).parseInt('${rabitmq.server.host.connectionclosetimeout}') }")
private int connectionCloseTimeOut;
Try it out
Below is from the doc of PropertySource https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/PropertySource.html
In order to resolve ${...} placeholders in definitions or #Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer. This happens automatically when using in XML, but must be explicitly registered using a static #Bean method when using #Configuration classes.
So if you are using #PropertySource, to resolve the ${...} placeholders in #Value annotations you must register the PropertySourcesPlaceholderConfigurer either by
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
or by configuring the PSPC in the XML as below
<context:property-placeholder location="classpath:config/service.properties" />
Please refer the spring JIRA ticket https://jira.spring.io/browse/SPR-8539 and the SO thread #Value not resolved when using #PropertySource annotation. How to configure PropertySourcesPlaceholderConfigurer? for some more reference.

Spring #Value assigning null to variable. #PropertySource

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

#Value is not injected

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.

#Value not resolved when using #PropertySource annotation. How to configure PropertySourcesPlaceholderConfigurer?

I have following configuration class:
#Configuration
#PropertySource(name = "props", value = "classpath:/app-config.properties")
#ComponentScan("service")
public class AppConfig {
and I have service with property:
#Component
public class SomeService {
#Value("#{props['some.property']}") private String someProperty;
I receive error when I want to test the AppConfig configuration class with
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.lang.String service.SomeService.someProperty; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Field or property 'props' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext'
The issue is documented in SPR-8539
but anyway I cannot figure out how to configure PropertySourcesPlaceholderConfigurer to get it work.
Edit 1
This approach works well with xml configuration
<util:properties id="props" location="classpath:/app-config.properties" />
but I want to use java for configuration.
as #cwash said;
#Configuration
#PropertySource("classpath:/test-config.properties")
public class TestConfig {
#Value("${name}")
public String name;
//You need this
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
If you use #PropertySource, properties have to be retrieved with:
#Autowired
Environment env;
// ...
String subject = env.getProperty("mail.subject");
If you want to retrieve with #Value("${mail.subject}"), you have to register the prop placeholder by xml.
Reason:
https://jira.springsource.org/browse/SPR-8539
I found the reason #value was not working for me is, #value requires PropertySourcesPlaceholderConfigurer instead of a PropertyPlaceholderConfigurer. I did the same changes and it worked for me, I am using spring 4.0.3 release. I configured this using below code in my configuration file.
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Don't you need a method on your #Configuration class that returns PropertySourcesPlaceholderConfigurer, annotated #Bean and is static, to register any #PropertySource with Spring?
http://www.baeldung.com/2012/02/06/properties-with-spring/#java
https://jira.springsource.org/browse/SPR-8539
I had the very same problem. #PropertySource is not playing well with #Value. A quick workaround is to have an XML configuration which you'll refer to it from your Spring Java Configuration using #ImportResource as usual and that XML configuration file will include a single entry: <context:property-placeholder /> (of course with the needed namespace ceremony). Without any other change #Value will inject properties in your #Configuration pojo.
This can also be configured in java this way
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setIgnoreUnresolvablePlaceholders(true);
configurer.setIgnoreResourceNotFound(true);
return configurer;
}
That looks mighty complicated, can't you just do
<context:property-placeholder location="classpath:some.properties" ignore-unresolvable="true"/>
then in code reference:
#Value("${myProperty}")
private String myString;
#Value("${myProperty.two}")
private String myStringTwo;
where some.properties looks something like this
myProperty = whatever
myProperty.two = something else\
that consists of multiline string
For java based config you can do this
#Configuration
#PropertySource(value="classpath:some.properties")
public class SomeService {
And then just inject using #value as before
Since Spring 4.3 RC2 using PropertySourcesPlaceholderConfigurer or <context:property-placeholder> is not needed anymore. We can use directly #PropertySource with #Value. See this Spring framework ticket
I have created a test application with Spring 5.1.3.RELEASE.
The application.properties contains two pairs:
app.name=My application
app.version=1.1
The AppConfig loads the properties via #PropertySource.
package com.zetcode.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource(value = "application.properties", ignoreResourceNotFound = true)
public class AppConfig {
}
The Application injects the properties via #Value and uses them.
package com.zetcode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
#ComponentScan(basePackages = "com.zetcode")
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Value("${app.name}")
private String appName;
#Value("${app.version}")
private String appVersion;
public static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext(Application.class);
var app = ctx.getBean(Application.class);
app.run();
ctx.close();
}
public void run() {
logger.info("Application name: {}", appName);
logger.info("Application version: {}", appVersion);
}
}
The output is:
$ mvn -q exec:java
22:20:10.894 [com.zetcode.Application.main()] INFO com.zetcode.Application - Application name: My application
22:20:10.894 [com.zetcode.Application.main()] INFO com.zetcode.Application - Application version: 1.1
The thing is: as far as I get it, <util:propertes id="id" location="loc"/>, is just a shorthand for
<bean id="id" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="loc"/>
</bean>
(see documentation of util:properties). Thus, when you use util:properties, a standalone bean is created.
#PropertySource, on the other hand, as documentation says is an
annotation providing a convenient and declarative mechanism for
adding a PropertySource to Spring's Environment'.
(see #PropertySource doc). So it doesn't create any bean.
Then "#{a['something']}" is a SpEL expression (see SpEL), that means "get something from bean 'a'". When util:properties is used, the bean exists and the expression is meaningful, but when #PropertySource is used, there is no actual bean and the expression is meaningless.
You can workaround this either by using XML (which is the best way, I think) or by issuing a PropertiesFactoryBean by yourself, declaring it as a normal #Bean.
Another thing that may be happening: ensure your #Value annotated values are not static.
In my case, depends-on="bean1" was within property-placeholder was causing the issue. I removed that dependency and used #PostConstruct to achieve the same original functionality and was able to read the new values too.
If you are configuring with xml, after adding
<context:property-placeholder location="..."/>
Make sure your annotations are activated. In my case properties were not fetched for this reason:
<context:annotation-config/>
for me it worked
spring.cloud.config.server.git.default-label=main
spring.cloud.config.server.native.searchLocations=file:///Users/${your username}/Projects/git-local-repositories/
spring.profiles.active=native

Resources