Spring: import a module with specified environment - spring

Is there anything that can achieve the equivalent of the below:
<import resource="a.xml">
<prop name="key" value="a"/>
</import>
<import resource="a.xml">
<prop name="key" value="b"/>
</import>
Such that the beans defined in resouce a would see the property key with two different values? The intention would be that this would be used to name the beans in the imports such that resource a.xml would appear:
<bean id="${key}"/>
And hence the application would have two beans named a and b now available with the same definition but as distinct instances. I know about prototype scope; it is not intended for this reason, there will be many objects created with interdepednencies that are not actually prototypes. Currently I am simply copying a.xml, creating b.xml and renaming all the beans using the equivalent of a sed command. I feel there must be a better way.

I suppose that PropertyPlaceholderConfigurers work on a per container basis, so you can't achieve this with xml imports.
Re The application would have two beans named a and b now available with the same definition but as distinct instances
I think you should consider creating additional application contexts(ClassPathXmlApplicationContext for example) manually, using your current application context as the parent application context.
So your many objects created with interdependencies sets will reside in its own container each.
However, in this case you will not be able to reference b-beans from a-container.
update you can postprocess the bean definitions(add new ones) manually by registering a BeanDefinitionRegistryPostProcessor specialized bean, but this solution also does not seem to be easy.
OK, here's my rough attempt to import xml file manually:
disclaimer: I'm very bad java io programmer actually so double check the resource related code :-)
public class CustomXmlImporter implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
private Map<String, String> properties;
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public Map<String, String> getProperties() {
return properties;
}
private void readXml(XmlBeanDefinitionReader reader) {
InputStream inputStream;
try {
inputStream = new ClassPathResource(this.classpathXmlLocation).getInputStream();
} catch (IOException e1) {
throw new AssertionError();
}
try {
Scanner sc = new Scanner(inputStream);
try {
sc.useDelimiter("\\A");
if (!sc.hasNext())
throw new AssertionError();
String entireXml = sc.next();
PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${",
"}", null, false);
Properties props = new Properties();
props.putAll(this.properties);
String newXml = helper.replacePlaceholders(entireXml, props);
reader.loadBeanDefinitions(new ByteArrayResource(newXml.getBytes()));
} finally {
sc.close();
}
} finally {
try {
inputStream.close();
} catch (IOException e) {
throw new AssertionError();
}
}
}
private String classpathXmlLocation;
public void setClassPathXmlLocation(String classpathXmlLocation) {
this.classpathXmlLocation = classpathXmlLocation;
}
public String getClassPathXmlLocation() {
return this.classpathXmlLocation;
}
#Override
public void postProcessBeanDefinitionRegistry(
BeanDefinitionRegistry registry) throws BeansException {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
readXml(reader);
}
}
XML configuration:
<bean class="CustomXmlImporter">
<property name="classPathXmlLocation" value="a.xml" />
<property name="properties">
<map>
<entry key="key" value="a" />
</map>
</property>
</bean>
<bean class="CustomXmlImporter">
<property name="classPathXmlLocation" value="a.xml" />
<property name="properties">
<map>
<entry key="key" value="b" />
</map>
</property>
</bean>
this code loads the resources from classpath. I would think twice before doing something like that, anyway, you can use this as a starting point.

Related

MongoDB-Escape dots '.' in map key]

Map key codeofproduct contains dots but no replacement was configured! Make sure map keys don't contain dots in the first place or configure an appropriate replacement!
org.springframework.data.mapping.model.MappingException: Map key foo.bar.key contains dots but no replacement was configured! Make sure map keys don't contain dots in the first place or configure an appropriate replacement!
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.potentiallyEscapeMapKey(MappingMongoConverter.java:622)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeMapInternal(MappingMongoConverter.java:586)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.createMap(MappingMongoConverter.java:517)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:424)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:386)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:373)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:257)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:373)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:451)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:386)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:373)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:257)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:373)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:451)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:386)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:373)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:257)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:373)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:345)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.write(MappingMongoConverter.java:310)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.write(MappingMongoConverter.java:77)
at org.springframework.data.mongodb.core.MongoTemplate.doSave(MongoTemplate.java:859)
at org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:806)
at org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:794)
When we try to insert value, this happens. How can we solve this?
this is my class
#Configuration
#EnableMongoRepositories("net.ooo.hepsiburada.**.repository")
#Profile(Constants.SPRING_PROFILE_CLOUD)
public class CloudMongoDbConfiguration extends AbstractMongoConfiguration {
private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class);
#Inject
private MongoDbFactory mongoDbFactory;
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public CustomConversions customConversions() {
List<Converter<?, ?>> converterList = new ArrayList<>();;
converterList.add(DateToZonedDateTimeConverter.INSTANCE);
converterList.add(ZonedDateTimeToDateConverter.INSTANCE);
converterList.add(DateToLocalDateConverter.INSTANCE);
converterList.add(LocalDateToDateConverter.INSTANCE);
converterList.add(DateToLocalDateTimeConverter.INSTANCE);
converterList.add(LocalDateTimeToDateConverter.INSTANCE);
return new CustomConversions(converterList);
}
#Override
protected String getDatabaseName() {
return mongoDbFactory.getDb().getName();
}
#Override
public Mongo mongo() throws Exception {
return mongoDbFactory().getDb().getMongo();
}
}
When using Spring Data MongoDB you get an instance of: org.springframework.data.mongodb.core.convert.MappingMongoConverter that has mapKeyDotReplacement set to null by default - that is why you are getting an exception.
You need to either create your own instance of org.springframework.data.mongodb.core.convert.MappingMongoConverter or just modify existing instance using its provider setter method:
/**
* Configure the characters dots potentially contained in a {#link Map} shall be replaced with. By default we don't do
* any translation but rather reject a {#link Map} with keys containing dots causing the conversion for the entire
* object to fail. If further customization of the translation is needed, have a look at
* {#link #potentiallyEscapeMapKey(String)} as well as {#link #potentiallyUnescapeMapKey(String)}.
*
* #param mapKeyDotReplacement the mapKeyDotReplacement to set
*/
public void setMapKeyDotReplacement(String mapKeyDotReplacement) {
this.mapKeyDotReplacement = mapKeyDotReplacement;
}
In MongoDB, dot is always treated as a special character so avoiding it will most likely save you some other headache in the future.
EDIT:
To override default MappingMongoConverter add the following bean declaration:
#Bean
public MappingMongoConverter mongoConverter(MongoDbFactory mongoFactory) throws Exception {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoFactory);
MappingMongoConverter mongoConverter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
mongoConverter.setMapKeyDotReplacement(".");
return mongoConverter;
}
My exception:
org.springframework.data.mapping.MappingException: Map key VAT Registration No. contains dots but no replacement was configured! Make sure map keys don't contain dots in the first place or configure an appropriate replacement!
Field with a dot at the end: VAT Registration No.
This didn't work for me:
mongoConverter.setMapKeyDotReplacement(".");
mongoConverter.setMapKeyDotReplacement("_"); //this broke enum values for example VALUE_1 -> VALUE.1
This works for me:
mongoConverter.setMapKeyDotReplacement("-DOT")
Complete class:
#Configuration
public class MongoConfiguration {
#Bean
public MappingMongoConverter mongoConverter(MongoDbFactory mongoFactory, MongoMappingContext mongoMappingContext) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoFactory);
MappingMongoConverter mongoConverter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
mongoConverter.setMapKeyDotReplacement("-DOT");
return mongoConverter;
}
}
For XML configuration following will be useful.
Note : mongoConverter bean is used for this. It will replace "." in key with "_"
<bean id="mappingContext" class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
<mongo:auditing mapping-context-ref="mappingContext"/>
<mongo:db-factory id="mongoDbFactory" mongo-ref="mongoClient" dbname="${mongo.dbname}"/>
<bean id ="mongoConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
<constructor-arg name="mappingContext" ref="mappingContext"/>
<property name="mapKeyDotReplacement" value="_"></property>
</bean>
<mongo:mongo-client id="mongoClient" credentials="${mongo.credential}" >
<mongo:client-options connections-per-host="50" threads-allowed-to-block-for-connection-multiplier="5000" />
</mongo:mongo-client>
<!-- MongoDB Template -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
<constructor-arg name="mongoConverter" ref="mongoConverter"/>
</bean>

Customised MultiResourceItemReader: assign different mappers/writers for each file inside an archive

I have a requirement to read/process an archive which contains several flat files, each file should have it's own mapping and writer.
How do I go about assigning different FieldSetmappers and Writers for each file
using the bean configuration.
I have started with extending the MultiResourceItemReader and overriding the open method
as shown here:
#Override
public void open(ExecutionContext executionContext)
throws ItemStreamException {
ZipFile zipFile;
List<Resource> resources = new ArrayList<Resource>();
try {
zipFile = new ZipFile(pathtozipfile);
Enumeration zippedFile = zipFile.entries();
while (zippedFile.hasMoreElements()) {
ZipEntry zipEntry = (ZipEntry) zippedFile.nextElement();
resources.add(new InputStreamResource(zipFile
.getInputStream(zipEntry), zipEntry.getName()));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.setResources(resources.toArray(new Resource[resources.size()]));
super.open(executionContext);
}
and the bean declaration as follows:
<bean id="itemReader" class="com.proc.spring.ZipMResourceItemReader" scope="step">
<property name="pathtozipfile" value="#{jobParameters['input.pathtozipfile']}" />
<property name="delegate" ref="delegateReader" /> </bean>
<bean id="delegateReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="lineMapper1">
<bean class="org.springframework.batch.item.file.mapping.." />
</property>
</bean>
I know programmatically, I could use the getCurrentResource() during read but I expect spring must support the ability to assign different FieldSetMappers based on current resource of the MultiResourceItemReader.
I guess perhaps extracting the files as the first step then assign each file/resource to a step: FlatFileItemReader would be solution but would prefer to use delegate method if its possible to differentiate the mappers/writers based on the resource name.
First of all: extract all files in a previous step is the best pratice.
A solution can involve a custom ResourceAwareItemReaderItemStream used to dispatch to correct reader looking at current resource name; you can do dispatch manually or using a Classifier. This custom reader is used as delegate of your multiresource item reader.
class ReaderDispatcher implements ResourceAwareItemReaderItemStream<Object> {
private ItemReader<Object> delegate1;
private ItemReader<Object> delegate2;
private ItemReader<Object> delegate1;
private ItemReader<Object> currentDelegate;
private Resource resource;
public setResource(org.springframework.core.io.Resource resource) {
this.resource = resource;
currentDelegate = getDelegateFromResource();
}
public Object read() {
return currentDelegate.read();
}
// Others interfaces implementation (or extends from one of abstract reader/stream implementations)
private ItemReader getDelegateFromResource() {
// here code to detect right reader from delegateN
}
}
(sorry for untested/incomplete code; I am unable to check it, but I hope you can see the idea behind).
You can pre-configure your readers with its own fieldSetMapper or any other customization you want.
Check ResourceAwareItemWriterItemStream for writer counterpart of reader interface.

Getting ClassCastException error

I have two classes ClientLogic1 and WelcomeBean1 as follows
public class ClientLogic1 {
public static void main(String[] args)
{
Resource res = new ClassPathResource("spconfig.xml");
BeanFactory factory = new XmlBeanFactory(res);
Object o = factory.getBean("id1");
WelcomeBean1 wb = (WelcomeBean1)o;
wb.show();
}
}
2nd class
public class WelcomeBean1 {
private Map data;
public void setData(Map data) {
this.data = data;
}
public void show()
{
Set s=data.entrySet();
Iterator it = s.iterator();
while(it.hasNext())
{
Map.Entry me = (Map.Entry)it.next();
System.out.println(me.getKey()+ " - "+me.getValue());
}
}
}
I have a xml file as
<beans>
<bean id="id1" class="WelcomeBean1">
<property name="data">
<map>
<entry key="k1">
<vlaue>1323</value>
</entry>
<entry key="k2">
<value>feed</value>
</entry>
</map>
</property>
</bean>
</bean>
I have given the right path.It's just when i run this program i get the following error
Exception in thread "main" java.lang.ClassCastException: WelcomeBean cannot be
cast to mapexmpl.WelcomeBean1 at mapexmpl.ClientLogic1.main(ClientLogic1.java:15)
I am not sure where i am going wrong.Can someone help me plz...
make sure there is no duplicate bean id in spring configuration file. for instance you might have bean WelcomeBean with id id1
change to full package name <bean id="id1" class="mapexmpl.WelcomeBean1">
Actually it is saying as WelcomeBean cannot be ..............But your code is showing all as WelcomeBean1.
You used WelcomeBean some where .Please check it once.
I think before you used WelComeBean.Then changed it to WelComeBean1.Please build agian with clean.

Can a Spring BeanFactoryPostProcessor find nested beans with no name?

I am trying to write a Spring BeanFactoryPostProcessor that can find any bean which defines an init-method. I am having luck finding beans that have names, but not nested nameless beans as in the target bean in following example:
<bean id="aclDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target">
<bean class="com.vidsys.dao.impl.acl.ACLDaoImpl" init-method="init">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
When I list the beans in my BeanFactoryPostProcessor I only seem to get the ones with names as in the following code:
public class BeanInitializationFinder implements BeanFactoryPostProcessor, Ordered {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
//String[] beanDefs = BeanFactoryUtils.beanNamesIncludingAncestors(beanFactory);
String[] beanDefs = beanFactory.getBeanDefinitionNames();
for(String defName : beanDefs) {
BeanDefinition def = beanFactory.getBeanDefinition(defName);
if(null == def.getBeanClassName() || !(def instanceof AbstractBeanDefinition))
return;
}
AbstractBeanDefinition abd = (AbstractBeanDefinition) def;
try {
if(abd.getFactoryMethodName() == null && abd.getFactoryBeanName() == null)
Class<?> beanClass = Class.forName(abd.getBeanClassName());
if(InitializingBean.class.isAssignableFrom(beanClass) || null != abd.getInitMethodName()) {
beansWithInits.add(defName);
}
}
}
catch(Exception e) {
throw new BeanIntrospectionException("Failed to instrospect bean defs", e);
}
}
}
}
I would like to get all the bean that have an init-method, including nested beans that are nameless. Can I do that?
You can retrieve nested BeanDefinitions, but not via beanFactory.getBeanDefinition. The only way to get to nested bean definitions is through the PropertyValues of the parent BeanDefinition - you need to walk the graph.
By way of example (and missing any null-checking):
BeanDefinition parentDef = beanFactory.getBeanDefinition(defName);
for (PropertyValue property : parentDef.getPropertyValues().getPropertyValues()) {
Object value = property.getValue();
if (value instanceof BeanDefinitionHolder) {
BeanDefinition nestedDef = ((BeanDefinitionHolder)value).getBeanDefinition();
}
}
Given that graph traversal works well with the Visitor pattern, you can subclass org.springframework.beans.factory.config.BeanDefinitionVisitor to do this in a more concise fashion.

How to get values from property-file into Map using Spring framework?

For now I could inject values from property-files:
#Value("${aaa.prop}")
public String someProp;
But I want something more...
For example I have some property file:
aaa.props=p1,p2,p3
aaa.props.p1=qwe
aaa.props.p2=asd
aaa.props.p3=zxc
I know for sure, that it contains property aaa.props, and know nothing about other properties. And I want to get this properties in to map using code like this:
#Value ("${aaa.props}")
public Map<String, String> someProps;
Resulting someProps: {p1=qwe,p2=asd,p3=zxc}
Well I built a generic approach for you: a factory bean that creates a map by filtering another map (properties are a kind of map, after all).
Here's the factory bean:
public class FilteredMapFactoryBean<V> extends
AbstractFactoryBean<Map<String, V>>{
private Map<String, V> input;
/**
* Set the input map.
*/
public void setInput(final Map<String, V> input){
this.input = input;
}
/**
* Set the string by which key prefixes will be filtered.
*/
public void setKeyFilterPrefix(final String keyFilterPrefix){
this.entryFilter = new EntryFilter<String, V>(){
#Override
public boolean accept(final Entry<String, V> entry){
return entry.getKey().startsWith(keyFilterPrefix);
}
};
}
public static interface EntryFilter<EK, EV> {
boolean accept(Map.Entry<EK, EV> entry);
}
/**
* If a prefix is not enough, you can supply a custom filter.
*/
public void setEntryFilter(final EntryFilter<String, V> entryFilter){
this.entryFilter = entryFilter;
}
private EntryFilter<String, V> entryFilter;
/**
* {#inheritDoc}
*/
#Override
public Class<?> getObjectType(){
return Map.class;
}
/**
* {#inheritDoc}
*/
#Override
protected Map<String, V> createInstance() throws Exception{
final Map<String, V> map = new LinkedHashMap<String, V>();
for(final Entry<String, V> entry : this.input.entrySet()){
if(this.entryFilter == null || this.entryFilter.accept(entry)){
map.put(entry.getKey(), entry.getValue());
}
}
return map;
}
}
Here is a spring bean definition file with some sample usage:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- use System.getProperties() as input -->
<bean class="spring.test.FilteredMapFactoryBean" id="javaMap">
<property name="keyFilterPrefix" value="java." />
<property name="input" value="#{T(java.lang.System).getProperties()}" />
</bean>
<!-- use custom properties as input -->
<bean class="spring.test.FilteredMapFactoryBean" id="customMap">
<property name="keyFilterPrefix" value="hello" />
<property name="input">
<props>
<prop key="hello">Is it me you're looking for?</prop>
<prop key="hello.again">Just called to say: hello.</prop>
<prop key="hello.goodby">You say goodbye and I say hello</prop>
<prop key="goodbye.blue.sky">Did-did-did-did-you hear the falling bombs?</prop>
<prop key="goodbye.ruby.tuesday">Who could hang a name on you?</prop>
</props>
</property>
</bean>
</beans>
And here is a test class:
public class Tester{
#SuppressWarnings("unchecked")
public static void main(final String[] args){
final ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:spring/test/mapFactorybean.xml");
final Map<String, String> javaMap =
(Map<String, String>) context.getBean("javaMap");
print("java.", javaMap);
final Map<String, String> customMap =
(Map<String, String>) context.getBean("customMap");
print("hello.", customMap);
}
private static void print(final String prefix, final Map<String, String> map){
System.out.println("Map of items starting with " + prefix);
for(final Entry<String, String> entry : map.entrySet()){
System.out.println("\t" + entry.getKey() + ":" + entry.getValue());
}
System.out.println("");
}
}
The output is as expected:
Map of items starting with java.
java.runtime.name:Java(TM) SE Runtime Environment
java.vm.version:14.2-b01
java.vm.vendor:Sun Microsystems Inc.
java.vendor.url:http://java.sun.com/
java.vm.name:Java HotSpot(TM) Client VM
java.vm.specification.name:Java Virtual Machine Specification
java.runtime.version:1.6.0_16-b01
java.awt.graphicsenv:sun.awt.Win32GraphicsEnvironment
[... etc]
Map of items starting with hello.
hello.goodby:You say goodbye and I say hello
hello:Is it me you're looking for?
hello.again:Just called to say: hello.
I'm afraid you can't, directly. But you can
implement ApplicationContextAware and set the ApplicationContext as a field in your bean.
in a #PostConstruct method call context.getBean("${aaa.props}")
parse the result manually and set it to the desired fields
you can use #Value.
Properties file:
aaa.props={p1:'qwe',p2:'asd',p3:'zxc'}
Java code:
#Value("#{${aaa.props}}")
private Map<String,String> someProps;
You could do something like this:
Maven dependency
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.2</version>
</dependency>
Add the import.
import javax.annotation.Resource;
...
#Resource (name="propertiesMapName")
public Properties someProps;
In your spring xml application context:
<util:properties id="propertiesMapName" location="classpath:yourFile.properties"/>
You will need this namespace
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd
The solution it's well defined on https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
#ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}

Resources