Use of configuration properties in #Configuration classes - spring-boot

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.

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, inject properties in a Bean instance based one of that Bean's field value, is it possible?

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();
}
}

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?

#Autowired Environment is always null

I am having a simple RestController:
#RestController
#PropertySource("classpath:application.properties")
public class Word2VecRestController {
private final static Logger LOGGER = Logger.getLogger(Word2VecRestController.class);
// #Resource is not working as well
#Autowired
Environment env;
// This is working for some reason
// but it's null inside the constructor
#Value("${test}")
String test;
public Word2VecRestController() {
LOGGER.info(env.getProperty("test"));
System.out.println("");
}
#GetMapping("/dl4j/getWordVector")
public ResponseEntity<List<Double[]>> getWordVector(String word) {
return null;
}
}
The problem is, that env is always null. I've seen somewhere that I could try to use #Resource instead of #Autowired but that didn't help.
application.properties:
test=helloworld
I've tried to use
#Value("${test}")
String test;
but the problem here is that these are null during construction of the object where I need it.
Field injection is done by Spring after the constructor is called. This is why Environment is null in the Word2VecRestController constructor. You can try constructor injection if you need it in the constructor:
#RestController
#PropertySource("classpath:application.properties")
public class Word2VecRestController {
private final static Logger LOGGER = Logger.getLogger(Word2VecRestController.class);
#Autowired
public Word2VecRestController(Environment env, #Value("${test}") String test) {
LOGGER.info(env.getProperty("test"));
System.out.println("");
}
#GetMapping("/dl4j/getWordVector")
public ResponseEntity<List<Double[]>> getWordVector(String word) {
return null;
}
}
PS: if you use Spring Boot, you do not need the #PropertySource("classpath:application.properties"), this is automatically done for you.
Add
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
to enable the PropertySourcesPlaceholderConfigurer to your configuration. Important this must been a static method!
For example:
#Configuration
#PropertySource("classpath:application.properties")
public class SpringConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}

spring boot values not substituted by gradle during Integration test

I don't know if this is a spring boot or gradle issue
I am using spring boot 1.4 to build Restful Services.
I have a StatusController class which gives status of service. This includes service name,buildtime,environmenet and version.
Here is my Controller
#RestController
public class StatusController
{
#Value("${service.version: not configured}")
private String version;
#Value("${service.name: not configured}")
private String appName;
#Value("${service.env: not configured}")
private String env;
#Value("${service.timestamp: not configured}")
private String buildDate;
#RequestMapping(value = "/status", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public String getStatus() throws JsonProcessingException {
Map<String,String> map = new HashMap<>();
map.put("version", version);
map.put("appName", appName);
map.put("env", env);
map.put("buildDate", buildDate);
return new ObjectMapper().writeValueAsString(map);
}
}
src/main/resources/application.properties
service.name=${name}
service.version=${version}
service.timestamp=${timestamp}
build.gradle (only relevant section)
processResources {
expand project.properties
}
def buildTime() {
def today = new Date()
def formattedDate = today.format('MM-dd-yyyy HH:mm:ss z')
return formattedDate
}
ext.timestamp=buildTime()
I have version defined in gradle.properties
version=1.0.0-SNAPSHOT.
when I start my service (gradlew bootRun) and I hit /status, I see all the values injected (not the default values of "not configured". However, in my tests, see the values not being substituted but default values injected.
Here is my test class
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApplicationTests {
#Test
public void contextLoads() {
System.out.println("test passed");
}
#Value("${service.version: not configured}")
private String version;
#Value("${service.name: not configured}")
private String appName;
#Value("${service.env: not configured}")
private String env;
#Value("${service.timestamp: not configured}")
private String buildDate;
#Autowired
private TestRestTemplate restTemplate;
#Test
public void exampleTest() throws Exception {
String lifeCheck = this.restTemplate.getForObject("/status", String.class);
System.out.print(lifeCheck);
}
}
using debugger, I confirmed the values are not substituted in the test class.
but also not substituted in controller
Here is the output from running above test
{"appName":" not configured","buildDate":" not configured","env":" not configured","version":" not configured"}
Don't forget that there's a separate task in Gradle called processTestResources
Place the application.properties in your src/test/resources/
The test classes cannot see your main resources. The values will be substituted if you place it in your test resources folder. :)

Resources