How to read property as List<String> from [] in springboot - spring

Sometime i see somebody define like this in property file:
spring.autoconfigure.exclude[0]=com.vietnam.AAutoConfiguration
spring.autoconfigure.exclude[1]=com.vietnam.BAutoConfiguration
just question: how to define a property in Spring bean to collect this config as list?
in other place they said to use like this:
in propery:
spring.autoconfigure.exclude=com.vietnam.AAutoConfiguration,com.vietnam.BAutoConfiguration
in spring bean
#Value("#{'${spring.autoconfigure.exclude}'.split(',')}"
But i dont like this way in case the value is long. one config per line would be easier to read and mantain
Thanks

You can read multiple strings like below
in application.properties
app.names=vipul,uncle,bob
in you component class
#Value("${app.names}")
private List<String> names;

i found the way, use #Value will not support case i want.
We need to use #ConfigurationProperties then Spring will collect property as list
#ConfigurationProperties(prefix = "spring.autoconfigure")
public class SpringConfig {
#Setter //lombok
private List<String> exclude;
}
Config will simple like:
spring.autoconfigure.exclude[0]=com.vietnam.AAutoConfiguration
spring.autoconfigure.exclude[1]=com.vietnam.BAutoConfiguration

Lists may also be like this
app.names={'Baeldung','dot','com'}
app.names2={\
'One long text example',\
'Another long text example'\
}
#Value("#{${app.names}}")
private List<String> appNames;
Source https://www.baeldung.com/spring-inject-arrays-lists
Map example:
valuesMap={key1: '1', key2: '2', key3: '3'}
#Value("#{${valuesMap}}")
private Map<String, Integer> valuesMap;
Source https://www.baeldung.com/spring-value-annotation

Related

How define properties names using #ConfigurationProperties in Spring?

I have class:
#ConfigurationProperties(prefix = "user")
public class BarcodeConfig {
private String customName;
private Details customDetails;
And property file with:
user.name=sampleName
user.details.address=sampleAddress
user.details.something=sampleSomething
How map "user.name" to field "customName" and "user.details.*" to "customeDetails" without changing field names?
you can use #AliasFor("name")
You have to provide the property path from your configuration file using the #Value annotation.
For example, if you want to map the user.name to customName:
...
#Value(${user.name})
private String customName;
...
Spring will automatically fetch the user.name property value and assigns it to the customName variable attribute.

Spring RefreshScope with SpEL deferred Evaluation

I'm having trouble knowing how this is possible or a potential work around. I am looking at configuring variables that are in the form of "foo,bar,baz" into a List as separate elements. Currently the code looks like
#RefreshScope
#Configuration
#Getter
public class fakeConfiguration {
#Value("#{PropertySplitter.toSet('${properties:}')}")
private final Set<String> field = new HashSet<>();
}
#Component("PropertySplitter")
#SuppressWarnings("unused")
public class PropertySplitter {
public Set<String> toSet(String property) {
Set<String> set = new HashSet<>();
if(!property.trim().isEmpty()){
Collections.addAll(set, property.split("\\s*,\\s*"));
}
return set;
}
}
This properly evaluates the String list into a Set however the refreshScope never gets triggered properly. If I use
#Value("${properties:}")
private final String fieldAsString;
I can see the field properly refresh, but I'd like to actively convert the value to a list as it is changed. Is this possible?
In newer version of spring-boot below works for application.properties and Application.yml
#Value("#{${props.list}}")
private List<String> fieldList;
If you use Application.yml you can arrange the objects
props:
list:
- val1
- val2
and then use in code
#Value("${props.list}")
private List<String> ymlList
Last, You can try the below as well
#Value("#{'${props.list}'.split(',')}")
private List<String> myList;

Initialize a list of map using ConfigurationProperties in Spring

I am looking for a neat way to initialize a List of Maps using Spring Boot from application.properties.
#ConfigurationProperties(prefix = "mail")
public class ConfigProperties {
private String hostName;
private int port;
private List<Map<String,String>> userList;
// standard getters and setters
}
I want to initialize the List of Maps "userList" from application.properties. How do we give values to mail.userList below in the application.properties file?
#application.properties
mail.hostname=host#mail.com
mail.port=9000
mail.userList=?
Thanks in advance.
You need to define it like this,
mail.userList[0].id=1
mail.userList[0].name=abc
mail.userList[1].id=2
mail.userList[1].name=pqr
mail.userList[2].id=3
mail.userList[2].name=xyz
This will load the in the userlist as list of map. Each entry in the list would contain
id and name.

How to configure Spring Redis Configuration to use Hash instead of string serialization

For a Java POJO, I want to cache it to Redis using Spring's #Cacheable, #CachePut, and #CacheEvict, however I'd prefer to use Redis' Hash capabilities instead of just serializing the POJO into a String. Essentially, I would like to be able to use something like the ObjectHashMapper against the POJO so that POJO properties are automatically saved as Key/Value pairs in a single Redis entry. This behavior can be seen in the RedisRepository functionality which saves POJOs with the ObjectHashMapper, however, I prefer not to define the cache as a Repository, rather I want to use the Cache annotations.
I have successfully created some custom Spring/Redis configuration that helped me get String serialization working propertly. The Spring documentation for customizing the CacheManager and CacheConfiguration are very "light" and I've not found any relevant examples. I'm not sure whether I need a custom serializer, converter, or some other aspect of the CacheConfiguration. It seems like the serializers are more concerned with the individual keys and values, but I don't see where to configure to catch the entire Object and turn it into a Hash first.
Here is my Redis configuration. It is setup for two caches, "v", and "products", plus defaults to a StringRedisSerializer for other caches.
#Slf4j
#RequiredArgsConstructor
#Configuration
#EnableRedisRepositories(enableKeyspaceEvents=RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP)
public class RedisConfig {
private final RedisConnectionFactory connectionFactory;
private static RedisCacheConfiguration createCacheConfiguration(long timeoutInSeconds, RedisSerializationContext.SerializationPair<?> serializationPair) {
logger.info("Creating CacheConfiguration with timeout of {} seconds", timeoutInSeconds);
return RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(serializationPair)
.entryTtl(Duration.ofSeconds(timeoutInSeconds));
}
#Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
logger.info("Creating cache manager");
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
cacheConfigurations.put("v",createCacheConfiguration(1200, RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())));
cacheConfigurations.put("products",createCacheConfiguration(-1,RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer())));
return RedisCacheManager
.builder(connectionFactory)
.cacheDefaults(createCacheConfiguration(-1,RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())))
.withInitialCacheConfigurations(cacheConfigurations)
.build();
}
}
Here is an example of how a POJO is serialized per the configuration above:
#Data
#Builder
#AllArgsConstructor
public class ProductSummary implements Serializable {
#Id
private String id;
private String accountId;
private String name;
private String roles;
private String groups;
}
and the way it is serialized:
\xac\xed\x00\x05sr\x004com.foobar.ProductSummaryh\xb3\x9d
\xd4\x0f\xac\xea\xb3\x02\x00\x05L\x00\taccountIdt\x00
\x12Ljava/lang/String;L\x00\x06groupsq\x00~\x00\x01L\x00
\x02idq\x00~\x00\x01L\x00\x04nameq\x00~\x00\x01L\x00
\x05rolesq\x00~\x00\x01xpt\x00\x19acctFv825MKt\x00
\nimwebuserst\x00\x13prod0lwJAWEYt\x00\x11ProductName/2020t\x00\x00
What I'd like is for it to be (in Redis as a Hash):
>HGETALL KEY
1) "_class"
2) "com.foobar.cache.CheckoutState"
3) "accountId"
4) "ACC000001"
5) "name"
6) "lorem ipsum whatever"
7) "roles"
8) "role1,role2,role3"
9) "groups"
10) "groupA,groupB"
with the hash Key being the id.
Guess this isn't that relevant anymore, but maybe somebody else want to do the same, like me.
It isn't that easy, as deep inside the implementation the DefaultRedisCacheWriter
uses "SET" - what you basically want is connection.hSet. As I see it the only way to achieve this would be to implement an own RedisCacheWriter.
This might be not enough, as the interface defines:
which is called by the redis cache manager:
Long story short, I assume an own "RedisCache" cache class might be the easiest way to go, using just a simple spring data redis "HASH" class or maybe a bit simpler using the RedisTemplate in combination with the spring ObjectHashMapper to created these hashes.

how to access values from application.properties file i dont ant to use #value and #configurationpropertiesin spring boot

I have tried this #Value("${userBucket.path}")
private String userBucketPath;
#ConfigurationProperties( prefix ="response")
but i don't want to use #Value("${userBucket.path}")
private String userBucketPath;
#ConfigurationProperties( prefix ="response")
Because if any new property will come then every time i have to do #value or in #configurtion property i have to create or add new object i want that just i use last part of object name and then i can access whole object for example response.app.abc.aa = aaa
response.app.abc.ab = abb in that i want to use just aa so it should show me aaa or if i used ab it should display abb only one place i need to write i.e. in application.properties and i can get that value but i don't want to use #value and #configurationproperties
Looks like you haven't provided details about how your actually properties look like. Consider this code as an example. I am reading properties with a prefix and grouping them in a Hashmap.
Here is my typical properties file
spring.datasource.username: username
spring.datasource.password: password
here is my #ConfigurationProperties
The ApppProperties has a Map named as datasource and it wont change event if you add a new property like spring.datasource.url
#ConfigurationProperties(prefix="spring")
public class ApppProperties {
private Map<String, String> datasource = new HashMap<>();
public Map<String, String> getDataource() {
return datasource;
}
public void setDatasource(Map<String, String> datasource) {
this.datasource = datasource;
}
}

Resources