Equivalent of #ConfigurationProperties(prefix="foo") over an external Properties object? - java

The CustomProperties object is in an external library, so I cannot add the annotation, but I still would like to feed it from my Spring Boot YAML by means of a prefix.

Found it!
In the #Configuration:
#Bean
#ConfigurationProperties("company.custom")
public CustomProperties customProperties(){
return new CustomProperties();
}

Related

How PropertiesLoaderUtils.loadProperties mapped to Configuration POJO using Spring at Runtime?

I have a service which is which consumes many external services. I am creating properties file for each of them, these are quite few predefined properties such as twil.props, tweet.props, hubpot.props etc
So as to get those properites at runtime I am using PropertiesLoaderUtils like below:
Resource resource = new ClassPathResource("/"+apiname +".properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
I would like to get these properties into POJO just like ConfigurationProperties, I have designed following POJO for that purpose:
public class APIConfig {
private Integer paginationPerPage;
private String paginationKeyword;
private String paginationStyle;
private String countParamKeyword;
private String countKey;
private String offsetKey;
}
I will maintain properties file in such a way so that these can be easily mapped to Config POJO:
Properties for twil.properties
api.paginationPerPage=10
api.paginationKeyword=limit
api.paginationStyle=offset
api.countParamKeyword=count
api.countKey=count
api.offsetKey=offset
So can I get this directly into given POJO by utilizing any of Spring Boot / Spring utility, config etc?
As it is noticed at comments, the only right solution here which includes zero downtime, namely #RefreshScope.
Use spring cloud config dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
Add to your code
#Configuration
class SomeConfiguration {
#Bean
#RefreshScope
#ConfigurationProperties("twil.props")
APIConfig twilConfig() {
return new ApiConfig()
}
#Bean
#RefreshScope
#ConfigurationProperties("tweet.props")
APIConfig tweetConfig() {
return new ApiConfig()
}
}
Usage: #Autowire APIConfig tweetConfig; to any bean
Call /refresh endpoint to refresh beans by new values from property sources
Please make a solution uniform with Spring ecosystem.
If you want to have dynamically to be dependent on, for example, #PathVariable:
private Map<String, AppConfig> sourceToConfig;
#GetMapping("/{source}/foo")
public void baz(#PathVariable source) {
AppConfig revelantConfig = sourceToConfig.get(source);
...
}
Spring provides the opportunity to automatically collect a map of beans where bean key is a bean name.
Rename bean methods above from twilConfig to twil() and call you endpoint like: /twil/foo (twilConfig is a bad path for an endpoint)

Spring boot PropertySourcesPlaceHolderConfigurer with #ProperySource

I have a simple question about PropertySourcesPlaceholderConfigurer and #PropertySource annotation. I have simple class with bean. I would like to inject property from application.properties and some another test.properties wiht #Value annotation. I read in several sources that #PropertySource needs to define staticPropertySourcesPlaceholderConfigurer bean. From documentation:
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.
But for me it works fine without this bean. Is Spring in some way auto configures PropertySourcesPlaceholderConfigurer at application startup? Here is my simple example (first property is from application.properties and second from test.properties):
#Configuration
#PropertySource("classpath:test.properties")
public class AppConfiguration {
#Value("${first.property}")
private String firstProp;
#Value("${second.property}")
private String secondProp;
#Bean
public TestModel getModel() {
TestModel model = new TestModel();
model.setFirstProperty(firstProp);
model.setSecondProperty(secondProp);
return model;
}
}
The static PropertySourcesPlaceholderConfigurer bean is registered automatically when missing by PropertyPlaceholderAutoConfiguration class which appears to be introduced in Spring Boot 1.5.0.RELEASE (as there is no on-line javadoc for it in the previous releases).
Interestingly this new auto configuration is not mentioned in Spring Boot 1.5 Release Notes.

What's the Spring way of accessing properties from an entity?

I'm new to Spring and I'm building an application where some entities (JPA/Hibernate) need access to a property from application.properties. I do have a configuration class in which this is trivial:
#Configuration
public class FactoryBeanAppConfig {
#Value("${aws.accessKeyId}")
private String awsAccessKeyId;
#Value("${aws.secretKey}")
private String awsSecretKey;
}
but since entities do not have and I think they should not have the annotations such as #Configuration or #Component, what's the Spring way for them to access the property?
Now, I know I can create my own class, my own bean, and make it as a simple wrapper around the properties; but is that the Spring way to do it or is there another way?
specify Property file location using #PropertySource
Something like below
#PropertySource("classpath:/application.proerties")
You also need to add below bean in your config
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
There is no "Spring way", since JPA entities and Spring have nothing to do with each other. Most importantly, JPA entities are not Spring beans, so Spring doesn't know or care about them as they're not managed by Spring.
You can try to hack around, trying in vain to access Spring's configuration from code that should not be trying to access it, or you can accept the truth that your design is broken and you're trying to do something that's not meant to be done.
As was proposed several times, use a service class for this. It's managed by Spring, so it can access the Spring config, and it can handle entities, so there's no crossing boundaries.
First create a public static variable in some bean managed by Spring, then make the following use of the #Value annotation.
public static String variable;
#Value("${variable}")
private void setVariable(String value) {
variable = value;
}
It will be set at runtime on startup, now you can access it from entities and everywhere else because it is just a public static var.
You can use #PropertySource to load the properties file as follows
#Configuration
#PropertySource("classpath:/com/organization/config/application.proerties")
public class FactoryBeanAppConfig {
...
}
Entities should not acces environment properties. If you are using your entity through a service, then the service can access the properties to act on the entity.

Using a Spring CacheManager from dependent jar

I have several WAR projects that include a dependency to a certain utility JAR project. I would like to be able to cache certain methods from the utility project by using spring's #Cacheable annotation, so I tried creating a configuration file on the utility project where I could define a CacheManager bean that could handle the caching of the methods. The configuration file looks like this:
(Note that I'm using a Redis caching implementation, but the spring config should be very similar for other caching providers)
#Configuration
#EnableCaching
public class RedisConfig {
#Value("${redis.hostUrl}")
private String hostUrl = null;
#Value("${redis.port}")
private Integer port = null;
#Value("${redis.defaultExpiration}")
private Integer defaultExpiration = null;
#Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory = new JedisConnectionFactory();
// Defaults
redisConnectionFactory.setHostName(hostUrl);
redisConnectionFactory.setPort(port);
return redisConnectionFactory;
}
#Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(cf);
return redisTemplate;
}
#Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
// Number of seconds before expiration. Defaults to unlimited (0)
cacheManager.setDefaultExpiration(defaultExpiration);
cacheManager.setUsePrefix(true);
List<String> cacheNames = new ArrayList<String>();
cacheNames.add("testing");
cacheManager.setCacheNames(cacheNames);
return cacheManager;
}
}
I'm pretty sure the configuration itself is not a problem because I have similar configuration files in other WAR modules and caching works fine on those. However, since this is a JAR that gets loaded by other modules, my guess is that the CacheManager is not being picked up by Spring.
In project A, which has a dependency to the utils project, I have a method like the following (edited for simplicity; ignore the invalid syntax):
#RequestMapping(...)
#ResponseBody
public Dto methodA(...) {
//The relevant part
testCachedMethod(value);
cachedMethodFromProjectB(value);
}
#Cacheable(value="testing")
public String testCachedMethod(String value) {
return value;
}
Project B is another WAR that has its own CacheManager (not tied to utils), and has similar methods cached using #Cacheable. If I hit Project A's methodA, the caches from Project B get stored properly, but the cachedMethod from Project A does not store anything in the cache (and neither do the methods from the utils project annotated with #Cacheable). I also tried the other way around, creating the CacheManager directly on Project A, but it also fails to cache the annotated methods inside the utils project (and I'd prefer declaring the manager on the utils project so it can be reused by other modules).
I know Spring is properly initializing the beans in the utils project because project A fails to run if its context's property placeholder does not find the property files for the #Value annotations from the cache Config file. So I'd suppose the CacheManager bean is there, but somehow it doesn't work. Any ideas on what I'm doing wrong or if it's even possible to use a CacheManager from a dependent JAR (if possible, using Java configuration)? I'm using Spring 3.2.0.RELEASE.
Been trying to figure this out for a couple days now, so any help is greatly appreciated.
Turned out to be an issue with the spring cache abstraction's default proxy mode. All the methods whose caching annotations were not working were being called by other methods within the same object. I can properly manipulate the caches from the dependent jar project using #Autowire on the CacheManager bean and manually performing the caching operations. While it should be possible to use the annotations by enabling the AspectJ weaving mode, I don't want to add more complexity by having to deal with the weaving in the dependent modules.

Using placeholders in Annotations on Spring Java Configurations

I'm a little bit lost in Spring's Property Replacement mechanism. Lets say I have this Java Config
#Configuration
#ComponentScan(basePackageClasses = Application.class)
#PropertySources({
#PropertySource("classpath:/config/default.properties")
})
public class ApplicationConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
return pspc;
}
Now I want to add a Spring-Data Annotation #EnableMongoRepositories and define a custom basepackage for scanning, using a custom placeholder like follows
#EnableMongoRepositories("${my.custom.repobasepackage}"). The Placeholder is defined in my default.properties.
However, this property cannot be resolved here. When diving deeper into Spring's property replacement, I can see that it tries to resolve the property, so it is possible to do so.
However, the underlying Environment class which is used to replace the placeholder does not know about my PropertyPlaceholderConfigurer, but only know about my SystemProperties and my VM-Props. :-(
I can see that in org.springframework.context.annotation.ClassPathBeanDefinitionScanner#getOrCreateEnvironment.java#339 (I'm using Spring 4.0.1) my "PropertyPlaceholder" is already in place, so its not a question of ordering in initialization, but is not used, because the used BeanDefinitionRegistry does not implement the Interface EnvironmentCapable. Here, my understanding of the Spring App-Context Bootstrapping is at the end.
Can anybody help me out here? Is there a BeanDefinitionRegistry out there which is capable of providing the Environment Instance which uses my Property Placeholder?
Any help is highly appreciated!! I got cookies for you! :-))
Cheers, Stefan
I imagine that the #PropertySources (you only have one by the way so you don't need the wrapper) are added to the Environment after the config classes are processed, so it will be too late to resolve on one of the annotations of those classes themselves. You can verify this by setting my.custom.repobasepackage as a System property.
As an alternative I encourage you to try out Spring Boot (where application.properties is added to the Environment before any configuration is processed).

Categories