Java based approach for injecting list of spring beans - spring

I am trying to get rid of my XML beans definition file. I would like to know how can i convert the following XML configuration to Java code.
<bean id="CustomerBean" class="com.java2s.common.Customer">
<property name="lists">
<bean class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="targetListClass">
<value>java.util.ArrayList</value>
</property>
<property name="sourceList">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
</bean>
</property>
</bean>
I am especially interested in knowing how to convert a list, Set, Map and properties XML configurations to Java code.
And if in a list if i have defined the beans in order like
<bean p:order="1000"
How i can manage the same ordering in java code.

A <list> corresponds to java.util.List, <map> corresponds to java.util.Map, <props> corresponds to java.util.Properties and so on.
To set the order, use the org.springframework.core.annotation.Order annotation on your bean or let it implement org.springframework.core.Ordered.
The equivalent of your XML configuration is something like:
#Bean
public Customer CustomerBean() {
Customer customer = new Customer();
List<String> lists = new ArraysList<>();
lists.add("1");
lists.add("2");
lists.add("3");
customer.setLists(lists);
return customer;
}
Note that the name of the method will be the name of the bean.

Related

Null value is returned from spring DI

I am trying to read property file ,but value returned is null.
<bean id="propertylist" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="places">
<list>
<value>file:/myfile/configuration/myproject.properties</value>
</list>
</property>
</bean>
<bean id="callManagement" class="com.callManagement" scope="session">
<property name="googlemapjs" value="${googlemap.js}"/>
</bean>
IF i replace ${googlemap.js} it with hardcode value . IT still gives null
NOTE::if i try to generate setter and getter using source>generate setter and getter it give "resource is not on the build path of java project".So i have to manually define setter and getters
<property name="locations">
<list>
<value>classpath*:*.properties.default</value>
<value>classpath*:${name of properties file}.properties
</value>
</list>
</property>
</bean>
And keep the properties file in src/main/config if you are using maven in your project

How to get key value from properties file at runtime using spring

I want to get the changed key value from properties file at runtime.
test.properties file:
name = Hi
I have made Thread sleep with 5 sec and changed the key value as "Hello" but it is not getting changed.
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:test.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:test</value>
</list>
</property>
<property name="cacheSeconds" value="1" />
</bean>
<bean id="tempBean" name="tempBean1" class="org.sri.spring.temp.Temp"
lazy-init="false" scope="prototype">
<constructor-arg type="String" value="${name}" />
</bean>
The ${name} placeholder inside the XML configuration is resolved using the PropertySourcesPlaceholderConfigurer which, as you may notice, has nothing in common with your reloadable messageSource.
It wouldn't work either way because Spring instantiates the tempBean only once: on application startup, by passing the value of ${name} to the constructor. The bean itself is not aware of where the value came from (and in particular, it doesn't care if the properties file gets edited).
If you really think it's a good idea to do it†, you can inject the entire messageSource into your tempBean, and get the current value in each call, e.g.:
public class Temp {
#Autowired // or wired in XML, constructor, etc.
private MessageSource messages;
public String sayHello() {
return messages.getMessage("name", null, Locale.getDefault());
}
}
† injecting a configuration-related object makes testing more difficult and is arguably bad design (mixing concerns). Have a look at the Spring Cloud Config project as it's likely that this is how the future is going to look like.
I do not think that Spring will update already existing beans when the properties change.
Try to create a new bean (prototype scope)

Why can I get null when I get a bean from spring?

My 'applicationContext.xml' file for spring is:
<bean id="gzipResponseInterceptor" class="my.interceptor.GzipResponseInterceptor"/>
<bean id="addResponseInterceptor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="httpClient"/>
</property>
<property name="targetMethod">
<value>addResponseInterceptor</value>
</property>
<property name="arguments">
<list>
<ref bean="gzipResponseInterceptor"/>
</list>
</property>
</bean>
<bean id="httpClient" class="org.apache.http.impl.client.DefaultHttpClient">
<constructor-arg>
<bean class="org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager" p:defaultMaxPerRoute="100"
p:maxTotal="100"/>
</constructor-arg>
</bean>
Then in my Java code:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(context.getBean("gzipResponseInterceptor"));
System.out.println(context.getBean("addResponseInterceptor"));
System.out.println(context.getBean("httpClient"));
And it prints:
my.interceptor.GzipResponseInterceptor#525f1e4e
null
org.apache.http.impl.client.DefaultHttpClient#75f9eccc
Notice the value of the bean 'addResponseInterceptor' is null! I can't understand why I can get null for a spring bean.
The addResponseInterceptor is a MethodInvokingFactoryBean which sole purpose is, as the name implies, to invoke a method. When doing context.getBean("addResponseInterceptor") what is being returned is the result of the getObject method of the FactoryBean.
The MethodInvokingFactoryBean returns the result of the invoked method.
Judging by the name of the method being invoked, addResponseInterceptor, that is void. void or Void results in a null result to be return from the MethodInvokingFactoryBean.
If you want the actual FactoryBean add a & to the name of the bean you want to retrieve. See last part of section 5.8.3 of the reference guide.

SpringMethodInvocationBean: Argument is an Interface

<bean id="arrayDescriptor"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<value>oracle.sql.ArrayDescriptor</value>
</property>
<property name="targetMethod">
<value>createDescriptor</value>
</property>
<property name="arguments">
<list>
<value>UDB_NAME</value>
<value>#{database.getConnection()}</value>
</list>
</property>
</bean>
I can't seem to make the method work because it needs an argument of (String, java.sql.Connection) instead its getting (String, org.basic.datasource.PoolableConnection).
Is it possible to typecast on EL? I have research its not possible.
Can I use another spring api to run a method and an argument?
Is it possible to instantiate in Spring at interface? I have research and it is not possible.
First of all the ArrayDescriptor.createDescriptor() requires DataSource, not a connection. I guess database represents DataSource, try:
<value>#{database}</value>
or even:
<ref bean="database"/>
What about using Java configuration and avoiding this XML hell?
#Configuration
public class Cfg {
#Resource
private DataSource database;
#Bean
public ArrayDescriptor arrayDescriptor() {
return ArrayDescriptor.createDescriptor("UDB_NAME", database);
}
}

Is it possible to alias bean class names in Spring?

I have a string property which looks similar to the following example:
<property name="mappingData">
<list>
<bean class="com.company.product.longNamingStandard.migration.extractor.FieldMapping">
<property name="elementName" value="entitlement.user"/>
<property name="mapping" value="DocUsers"/>
</bean>
<bean class="com.company.product.longNamingStandard.migration.extractor.FieldMapping">
<property name="elementName" value="entitlement.contributor"/>
<property name="mapping" value="DocContributors"/>
</bean>
</list>
</property>
The long class name(s) effect readability & also create a refactoring overhead.
Is it possible to alias the class name and use a short name to declare the beans? Or is there an alternate best practice I'm missing?
Probably a bit late for you, but hopefully useful for others:
You can use parent beans to accomplish this.
First declare a parent bean as a template:
<bean id="FieldMapping" class="com.company.product.longNamingStandard.migration.extractor.FieldMapping"/>
Then use it elsewhere, using the parent attribute.
<property name="mappingData">
<list>
<bean parent="FieldMapping">
<property name="elementName" value="entitlement.user"/>
<property name="mapping" value="DocUsers"/>
</bean>
<bean parent="FieldMapping">
<property name="elementName" value="entitlement.contributor"/>
<property name="mapping" value="DocContributors"/>
</bean>
</list>
</property>
Please note my convention here is to use upper case id's here for the parent template beans.
each <bean/> comes with an attribute of name and id to help you reference those beans later in your configuration.
I would suggest using the id for declaring the bean.
your config could look like:
<bean id="fooBean" class="com.example.foo"/>
<bean id="barBean" class="com.example.bar"/>
<list>
<ref>fooBean</ref>
<ref>barBean</ref>
</list>
You may try to represent your mapping in some short form, and then convert it to the list of FieldMappings. For example, mappings from your snippet may be represented as a map.
As a theoretic exercise in Spring 3 you can do this with Spring Expression Language (if FieldMapping has the apropriate constructor):
<util:map id = "m">
<entry name = "entitlement.user" value = "DocUsers" />
<entry name = "entitlement.contributor" value = "DocContributors" />
</util:map>
...
<property name = "mappingData"
value = "#{m.![new com.company.product.longNamingStandard.migration.extractor.FieldMapping(key, value)]}" />
If this expression is too obscure, you may implement a FactoryBean to take a short form of your mapping data (for example, a map, as in this example) and return a configured list of FieldMappings:
<property name = "mappingData">
<bean class = "FieldMappingListFactoryBean">
<property name = "mappings">
<map>
<entry name = "entitlement.user" value = "DocUsers" />
<entry name = "entitlement.contributor" value = "DocContributors" />
</map>
</property>
</bean>
</property>
However, if your field mappings are some kind of reusable DSL, you may try to think about implementing a namespace extension.
I found a way to simulate an effect similar to a "import com.Foo;" in java code. The best option I could find was to use a PropertyPlaceholderConfigurer with local properties defined. Using your example, here's the configuration that you would put at the top of your spring config file to define a "class_FieldMapping" property:
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<description>Define properties equivalent to "import foo;" in java source</description>
<property name="properties">
<props>
<prop key="class_FieldMapping">com.company.product.longNamingStandard.migration.extractor.FieldMapping</prop>
</props>
</property>
</bean>
Then, you can use that property within your beans:
<property name="mappingData">
<list>
<bean class="${class_FieldMapping}">
...
</bean>
<bean class="${class_FieldMapping}">
...
</bean>
</list>
</property>
This has the benefit that use can also use it for things where you actually need the class name, and can't reference an instance of an object:
<util:constant static-field="${class_FieldMapping}.MYSTATICVAR" />
Why not declare those inner beans as separate top-level beans with their own names, and then reference them in the list ?
If I use PropertyPlaceholderConfigurer it leads to several exceptions in debug log. It works, but it seems it doesn't work on the first try.

Resources