How to modify beans defined in a spring container - java

I have two xml files defining beans for the springframework (version 2.5.x):
containerBase.xml:
<beans>
<bean id="codebase" class="com.example.CodeBase">
<property name="sourceCodeLocations">
<list>
<value>src/handmade/productive</value>
</list>
</property>
</bean>
</beans>
... and
containerSpecial.xml:
<beans>
<import resource="containerBase.xml" />
</beans>
Now I want to adjust the property sourceCodeLocations of bean codebase within containerSpecial.xml. I need to add a second value src/generated/productive.
A simple approach is to override the definition of codebase in containerSpecial.xml and add both values, the one from containerBase.xml and the new one:
containerSpecial.xml:
<beans>
<import resource="containerBase.xml" />
<bean id="codebase" class="com.example.CodeBase">
<property name="sourceCodeLocations">
<list>
<value>src/handmade/productive</value>
<value>src/generated/productive</value>
</list>
</property>
</bean>
</beans>
Is there a way to extend the list without redefining the bean?
EDIT 2009-10-06:
The purpose of this is to have a shared standard container containerBase that is used by a lot of different projects. Each project can override/extend some properties that are special for that project in its own containerSpecial. If the project doesn't override, it's using the defaults defined in containerBase.

You could use a BeanFactoryPostProcessor to change the bean's metadata before the Spring container instantiates the CodeBase bean. For example:
public class CodebaseOverrider implements BeanFactoryPostProcessor {
private List<String> sourceCodeLocations;
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
CodeBase codebase = (CodeBase)beanFactory.getBean("codebase");
if (sourceCodeLocations != null)
{
codebase.setSourceCodeLocations(sourceCodeLocations);
}
}
public void setSourceCodeLocations(List<String> sourceCodeLocations) {
this.sourceCodeLocations = sourceCodeLocations;
}
}
Then in contextSpecial.xml:
<beans>
<import resource="context1.xml" />
<bean class="com.example.CodebaseOverrider">
<property name="sourceCodeLocations">
<list>
<value>src/handmade/productive</value>
<value>src/generated/productive</value>
</list>
</property>
</bean>
</beans>

Yes. A bean definition can have a "parent" attribute that references a parent bean definition. The new "child" definition inherits most of the properties of the parent and any of those properties can be overridden.
See Bean Definition Inheritance
Also you can use Collection Merging to merge the list property definition from the parent and child bean definitions. This way you can specify some list items in the parent bean definition and add more items to it in the child bean definition.

Is there a way to define the list in a properties or other configuration before hand?
It seems like the app configuration and wiring are tightly coupled. From my experience, if it is hard to do something in Spring, likely there is a different easier way to do it.

3 approaches:
Simple: have two lists defaultSourceCodeLocations and additionalSourceCodeLocations and have your accessor methods check both of these (or combine them). I've seen this done in some frameworks - a default list of handlers is populated then additional user created ones are added...
More complicated but keeps the original class clean: You could then create a CodeBaseModifier class. This would have a init-method to alter an injected instance of the bean.
<bean id="codebaseModifier" class="com.example.CodeBase" init-method="populateCodeBase">
<property name="sourceCodeLocations" ref="codebase"/>
<property name="additionalSourceCodeLocations">
<list>
<value>src/handmade/productive</value>
</list>
</property>
</bean>
If you wanted to make this really generic you could make a bean modifier that would do this by reflection. Be careful of the ordering if use this approach. Dependent beans of CodeBase would have to make sure this class was instantiated first (with depends on)
3 A variation on 2... Instead of directly creating a CodeBase class instead create a factory that returns a populated bean. This factory could then be configured with Spring in a similar fashion to 2. Have a defaultSourceCodeLocations and additionalSourceCodeLocations
Unless you need a lot of extensible properties I would go with option 1.

In Spring 3.0, you can specify merge="true" on the 'list' tag. See http://forum.springsource.org/archive/index.php/t-97501.html for details.

Related

Suggestion to implement custom bean initialization/creation logic in Spring

In my Java/spring project there are lots of beans configured in an xml like,
<beans>..
<bean id="beanOne" class=...>
<property name="x" value="1"/>
<property name="y" value="something"/>
<property name="z" value="something else"/>
</bean>
</beans>
"beanOne"'s properties(x,y,z) values keep changing for different needs/machines. I can create different xml files having different values, one for each need/machine. But I want to write some custom bean creation logic so that I can define different properties in a single properties (or) json file and the custom class takes care of creating the beans with appropriate values from that single properties (or) json file for all needs/machines.
So how to can I do that in spring? - Extend AbstractFactoryBean, implement FactoryBean, implement BeanFactory, or something else? I just need the logic/skeleton of how to do that using spring.
Follow these steps to do the same,
create the object for your required bean.
Set the required properties for that object, you can fetch it from anywhere you want.
Register that object in beanFactory as a bean.
Reference : https://wordpress.com/post/anilagrawal038.wordpress.com/3

Single JavaConfig class for multiple beans of same type with dependencies

Would it be possible to convert this config to a single #Configuration class? I need to pick the values for Car from property files
<bean name="VW" class="com.app.car">
<property name="cost" value="${vw.cost}"/>
<property name="power" value="${vw.power}"/>
</bean>
<bean name="Merc" class="com.app.car">
<property name="cost" value="${merc.cost}"/>
<property name="power" value="${merc.power}"/>
</bean>
<bean name="FirstCar" class="com.app.cart">
<property name="car" ref="VW"/>
</bean>
<bean name="SecondCar" class="com.app.cart">
<property name="car" ref="Merc"/>
</bean>
I know we can define different classes fro VW and Marc and then refer #Autowire them to a parent #Configuration class. Wondering if there is a solution involving defining all these beans in a single class. I tried using #Value for parameters for devAppConfig as below
vw(#Value("vw.cost") String cost, #Value("vw.power") String power)
merc(#Value("merc.cost") String merc, #Value("merc.power") String power)
But these methods have input parameters. Having 2 different objects of the same type that need to be instantiated with different property values and injected as dependencies is the goal
You can use Spring Profiles, so you can have a property file o bean for each enviroment.
Spring Profiles provide a way to segregate parts of your application
configuration and make it only available in certain environments. Any
#Component or #Configuration can be marked with #Profile to limit when
it is loaded You can see more here
http://www.baeldung.com/spring-profiles
http://www.mkyong.com/spring/spring-profiles-example/
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html

Auto-wiring Spring constructor arguments from properties file in XML

I'm using Spring to auto-wire beans for configuration. Some parameters come from a properties file:
<bean id="mydb" class="myproject.mydb" autowire="constructor">
<constructor-arg name="host" value="${mydb.host}" />
<constructor-arg name="db" value="${mydb.db}" />
<constructor-arg name="user" value="${mydb.user}" />
<constructor-arg name="password" value="${mydb.password}" />
</bean>
Is there a way to auto-wire these properties based on the bean id so that I would just have to write the following?
<bean id="mydb" class="myproject.mydb" autowire="constructor" />
Edit: The point of this is to not have to explicity specify the non-bean constructor arguments. I want Spring to automatically check the properties for beanId.constructorArgName
To achieve exactly what you want, I think you'd need to implement a BeanPostProcessor and provide your custom wiring logic (where you read the .properties file) in postProcessBeforeInitialization. The bean name is available to that method, but there are multiple problems with this. The first is that argument names are not necessarily available at runtime, so indexes might be a better option. The second is that you already have an instantiated bean (so a default constructor would need to exists), and you'd instantiate another, throwing the first one away which is wasteful. To use the instance that already exists, you'd need to wire it by properties, not constructor, which violates encapsulation and is not what you asked. The third is that it's not at all obvious what is going on. So, all in all, you are probably better off avoiding this completely.
In your class myproject.mydb
#Autowired
public mydb(#Value("mydb.host") String host, ...){...}
As per your question, the only way Property values can be injected to the constructor is through the XMLfile as done above or using the #Value("${some.property}") annotation.
Refer this for more info
Use #Value("property key") annotation. look at eg.: http://java.dzone.com/articles/autowiring-property-values

Nesting abstract beans in Spring and init-method does not seem to work

I have the following construct Spring XML (Spring 3.1):
<bean id="taskRunner" abstract="true" init-method="startThreads"
class="my.class.TaskRunner" />
...
<bean id="taskRunnerA" parent="taskRunner">
<constructor-arg name="foo">...</constructor-arg>
<property name="bar">...</property>
</bean>
And I am trying to separate out the init method into a higher level abstract bean:
<bean id="taskRunnerLauncher" abstract="true" init-method="startThreads" />
<bean id="taskRunner" abstract="true" depends-on="taskRunnerLauncher"
class="my.class.TaskRunner" />
...
<bean id="taskRunnerA" parent="taskRunner">
<constructor-arg name="foo">...</constructor-arg>
<property name="bar">...</property>
</bean>
Somehow this does not work, i.e. startThreads() is never invoked in the second case. Does anybody know why? Does Spring support nested abstract beans?
My idea for doing this is so I can override "taskRunnerLauncher" in unit tests and set it to "mock" or "java.lang.Object" and suppress startThreads() call (which starts new thread and making it a pain to test).
Does anybody know why?
The taskRunnerLauncher bean is set to be abstract. This means it will only act as a template for other beans. Spring will not actually create a bean for it. Therefore there won't be any invocation of startThreads because there is nothing to invoke it on.
Found the problem. I mistakenly used depends-on instead of parent attribute on taskRunner bean.

The Spring AOP Proxy that isn't

I have two Spring proxies set up:
<bean id="simpleBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref local="simpleBeanTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>cacheInterceptor</value>
</list>
</property>
</bean>
<bean id="springDao" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="springDaoTarget"/>
<property name="interceptorNames">
<list>
<value>daoInterceptor</value>
</list>
</property>
</bean>
simpleBean works just fine -- springDao does not.
The SpringDao class looks like:
public class SpringDao extends JdbcDaoSupport {
private SimpleJdbcTemplate simpleJdbcTemplate;
public SimpleJdbcTemplate getSimpleJdbcTemplate() {
if (simpleJdbcTemplate==null) {
simpleJdbcTemplate= new SimpleJdbcTemplate(getDataSource());
}
return simpleJdbcTemplate;
}
...
And I have my unit test autowired like this:
#Autowired
#Qualifier("springDao")
protected SpringDao springDao;
And the first indication something is wrong is I get this error:
Could not autowire field: . . . nested
exception is
java.lang.IllegalArgumentException
If I comment out the #Qualifier annotation and run my unit test again, I get this:
No unique bean of type ... expected
single matching bean but found 2:
[springDaoTarget, springDao]
That is what I expected.
So I changed my autowiring to
#Autowired
#Qualifier("springDaoTarget")
protected SpringCustomerCapacityDao springDao;
And added the following to my unit test:
Object proxy = applicationContext.getBean("springDao");
Assert.assertNotNull(proxy);
Assert.assertTrue(proxy instanceof SpringDao);
And the instanceof test failed, which (to me) means that my proxy is not really my proxy.
So I'm confused. What's going on? How can I fix this?
Edit Here is the requested springDaoTarget definition, which will disappoint many people:
<bean id="springDaoTarget" class="com.company.SpringDao">
If the target of your proxy implements at least one interface then Spring's default behavior is to create a JDK Proxy that implements all the interfaces of the target. This means it will not be a subclass of the target class. You can override this by forcing the creation of CGLIB proxies instead which are dynamic subclasses of the target.
As a general rule, if you are going to use AOP but only use interfaces in a limited fashion you'll want to force CGLIB. Otherwise you will have lots of JDK Proxies in your container which are not of the same type as the bean implementations you loaded.
See Cliff Meyers blog: Spring AOP: CGLIB or JDK Dynamic Proxies?
It was easy to fix, once I figured it out. SpringDao no longer inherits from JdbcDaoSupport and now it works.

Categories

Resources