Spring, inject properties in a Bean instance based one of that Bean's field value, is it possible? - spring

I have a Pojo I use to configure webservices' clients:
public class ServiceConfig {
private String url;
private String endpoint;
private String serviceName;
public ServiceConfig(String serviceName) {
super();
this.serviceName = serviceName;
}
}
Now, this is what my application.properties file looks like:
service1.url=http://localhost:8087/
service1.endpoint=SOME_ENDPOIT1
service2.url=http://localhost:8085/
service2.endpoint=SOME_ENDPOIT2
service3.url=http://localhost:8086/
service3.endpoint=SOME_ENDPOIT3
service4.url=http://localhost:8088/
service4.endpoint=SOME_ENDPOIT4
What I'm trying to achieve is for Spring to inject the correct properties when I instantiate ServiceConfig like this:
ServiceConfig sc = new ServiceConfig("service1");
Is it possible?

Are you using just spring or also spring-boot?
What about injecting org.springframework.core.env.Environment to your pojo and configuring it with it.
so something like this could work:
public class ServiceConfig {
private String url;
private String endpoint;
private String serviceName;
public ServiceConfig(String serviceName, Environment env) {
// TODO assert on serviceName not empty
this.serviceName = serviceName;
this.url = env.getProperty(serviceName.concat(".url");
this.endpoint = env.getProperty(serviceName.concat(".endpoint");
}
}
I guess there could be a simpler/more elegant solution, but I don't know your case.
spring-boot version
well with spring boot just define your pojo (field names must match property names)
public class ServiceConfig {
private String url;
private String endpoint;
// getters setters
}
and then in some configuration you can do this (note: value in ConfigurationProperties is prefix of your configuration in application.properties):
#Configuration
public class ServicesConfiguration {
#Bean
#ConfigurationProperties("service1")
ServiceConfig service1(){
return new ServiceConfig();
}
#Bean
#ConfigurationProperties("service2")
ServiceConfig service2(){
return new ServiceConfig();
}
}

Related

How to get Redis Hash Configuration such as Time To Live from application.properties at Spring Boot?

I use
#Value("${cache.host}")
private String redisHost;
#Value("${cache.port}")
private int redisPort;
I want to get timeToLive in #RedishHash from application properties. How can get this config?
#RedisHash(value = "UserModel", timeToLive = 5)
I give manually above however I want to give from application.properties
i'm not sure if you can do that from application.properties, but u can do it by configuring a RedisCacheManager bean with java based configuration like below :
#Bean
public RedisCacheManager RedisCacheManager(RedisConnectionFactory redisConnectionFactory) {
Map<String, RedisCacheConfiguration> cacheConfig = new HashMap<String, RedisCacheConfiguration>();
cacheConfig.put("UserModel", RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(5)));
RedisCacheManager rdisCacheManager = new RedisCacheManager(
RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory),
RedisCacheConfiguration.defaultCacheConfig(), cacheConfig);
return rdisCacheManager;
}
PS : this method should be in a class with #Configuration annotation
You can create a #Component where you are going to take the values from properties
#Component
public class RedisHashCustom {
private static String redisHashValue;
public static String getRedisHashVaue() {
return redisHashValue;
}
#Value("${application.redis.redishash.value}")
public void setRedisHashValue(String newRedisHashValue) {
redisHashValue= newRedisHashValue;
}
}
Then you need to reference as
#RedisHash(value = "#{T(com.redis.model.RedisHashCustom).getRedisHashValue() }")

Spring component depends on the configuration which load props from application.properties but props are not loaded

I have some difficulties and I can't realize how to let it work because of lack of knowledge in Spring framework.
What I'm trying to do is pretty simple. I want to use injected AppProperties in RestTemplateComponent constuctor or getRestTemplate method but all props are null.
I think I understand why.. it's because RestTemplateComponent by the order was loaded first and that's why AppProperties props are null.
Is it possible some how to tell to Spring to load AppProperties first in order to use it in RestTemplateComponent.
By the way problem occurs only on start there is no any problem when I inject it in controller and during request use these objects.
application.properties
integration.url=http://...
#Configuration
#ConstructorBinding
#ConfigurationProperties("integration")
public class AppProperties {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
#Component
public class RestTemplateComponent {
private final AppProperties appProperties;
public RestTemplateComponent(AppProperties appProperties) {
this.appProperties = appProperties;
}
#Bean
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
//Custom authorization
return restTemplate;
}
}
Ok, finally I did it work by adding annotation #PropertySource({"classpath:application.properties"}) within my AppProperties class and then when I try to autowire AppProperties in SpringBootApplication I see in debug that props are initialized.

Use of configuration properties in #Configuration classes

Is it expected that configuration properties classes should be usable within #Configuration classes.
Environment
Spring Boot 2.0.2
java version "10.0.1" 2018-04-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10),
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)
OSX 10.13.4
My Code
application-local.properties:
s3.bucketName=test-bucket
Configuration Properties
#Validated
#ConfigurationProperties(prefix = "s3")
public class S3ConfigurationProperties {
#NotBlank
private String bucketName;
public String getBucketName() {
return bucketName;
}
public void setBucketName(final String bucketName) {
this.bucketName = bucketName;
}
}
Configuration Class
#Configuration
#Profile("local")
#EnableConfigurationProperties(S3ConfigurationProperties.class)
public class LocalS3Configuration {
#Autowired
private S3ConfigurationProperties properties;
#Value("${s3.bucketName}")
private String bucket;
#Bean(destroyMethod = "shutdown")
public AmazonS3 amazonS3(#Value("${local.s3.endpoint}") final String s3Endpoint, #Value("${s3.bucketName}") final String bucketName) {
// use properties...
final String bucketInjectedToMethod = bucketName; // works
final String bucketInjectedViaProperties = properties.getBucketName(); // null
final String bucketInjectedInClass = bucket; // null
}
}
Observed Behaviour
If I inject the S3ConfigurationProperties as a field to the configuration class or an argument to the amazonS3 method the instance is non-null, but the bucketName property within it is null.
Injecting the string to the class via #Value is also null.
The only way I can get it to work is to use the method argument annotated as #Value with a string.
Is this expected behaviour or possibly a bug?
In your case it is not necessary to use #EnableConfigurationProperties. You can put #Configuration in S3ConfigurationProperties:
#Configuration
#ConfigurationProperties(prefix = "s3")
public class S3ConfigurationProperties {
private String bucketName;
//getter and setter
}
So, now you can inject it in LocalS3Configuration:
#Profile("local")
#Configuration
public class LocalS3Configuration {
#Autowired
private S3ConfigurationProperties properties;
#Value(("${s3.bucketName}"))
private String bucketName;
#Bean(destroyMethod = "shutdown")
public AmazonS3 amazonS3() {
final String bucketInjectedToMethod = bucketName;
final String bucketInjectedViaProperties = properties.getBucketName();
...
}
}
The annotation #Configuration registers the class as a bean and allows you to inject it in another bean.

How to load springboot properties?

I'm trying to load properties once in my springboot application.
Actually, I have created a class to do that :
#Configuration
#PropertySource(value = { "classpath:parameters.properties", "classpath:iot.properties" })
public class PropertiesHelper {
#Autowired
protected Environment env;
private static Environment properties;
#PostConstruct
public void init() {
properties = env;
}
public static String getProperty(final String propertyName) {
return properties.getProperty(propertyName);
}
}
This class work fine but this is not clean code (sonar hate my static variable).
So, how can I load in my springboot application all my properties correctly and only once ?
#PropertySource("classpath:parameters.properties")
#PropertySource("classpath:iot.properties")
public class PropertiesHelper {
#Value( "${Value.you.need}" )
private String valueYouNeed;
}
parameters.properties
Value.you.need=12345
Make it something like this, it should be work in your scenario.
An elegant solution
If you just want to eliminate the sonar alarm(sonar hate my static variable)
// application.properties
spring.profiles.active=test1010
// IOC config bean container
#Configuration
#Data
public class PropertiesConfig{
#Value("${spring.profiles.active:preline}")
private String profiles;
}
//Use
#Autowired
private PropertiesConfig propertiesConfig;
#GetMapping("/getPropertiesConfig")
public String getPropertiesConfig(){
return propertiesConfig.getProfiles();
}
I think the above scheme is a more elegant way~,Do you have a better solution?

Spring - should I use #Bean or #Component?

Here is the current code at my work.
Method 1
#Configuration
public class AppConfig {
#Bean
#Autowired(required = false)
public HttpClient createHttpClient() {
// do some connections configuration
return new HttpClient();
}
#Bean
#Autowired
public NameClient nameClient(HttpClient httpClient,
#Value("${ServiceUrl:NotConfigured}")
String serviceUrl) {
return new NameClient(httpClient, serviceUrl);
}
}
And the NameClient is a simple POJO looks like following
public class NameClient {
private HttpClient client;
private String url;
public NameClient(HttpClient client, String url) {
this.client = client;
this.url = url;
}
// other methods
}
Instead of using #Bean to configure, I wanted to follow this pattern:
Method 2
#Configuration
public class AppConfig {
#Bean
#Autowired(required = false)
public HttpClient createHttpClient() {
// do some connections configuration
return new HttpClient();
}
}
And use auto-scanning feature to get the bean
#Service //#Component will work too
public class NameClient {
#Autowired
private HttpClient client;
#Value("${ServiceUrl:NotConfigured}")
private String url;
public NameClient() {}
// other methods
}
Why the first method above is used/preferred? What is the advantage of one over the other? I read about the difference between using #Component and #Bean annotations.
They're equivalent.
You would typically use the second one when you own the NameClient class and can thus add Spring annotations in its source code.
You would use the first one when you don't own the NameClient class, and thus can't annotate it with the appropriate Spring annotations.

Resources