Stefano,
Give the following a try. I took your code snippets and added in some of Andreas' comments to create an initial implementation. If anyone finds this handy, please let me know.
First, two new interfaces and the FactoryBean implementation. Note that the FactoryBean allows you to either supply a custom init-method name or you can implement DynamicInitializingBean in the bean that the factory creates:
Code:
package com.cbconstantini.core.spring;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
/**
* An ObjectFactory that allows you to supply bean property values at
* dynamically at runtime (ObjectFactory only allows you to supply bean property
* values at config time).
* @author jkelly
*/
public interface DynamicObjectFactory extends ObjectFactory
{
/**
* Sets the dynamicPropertyValues that this factory will set on the bean it
* creates.
* @param dynamicPropertyValues a property-name-to-property-value Map
*/
void setDynamicPropertyValues(Map dynamicPropertyValues);
}
Code:
package com.cbconstantini.core.spring;
/**
* Interface to be implemented by beans that need to react once all their
* dynamic properties have been set by a DynamicObjectFactory. For example, to
* perform custom initialization, or merely to check that all mandatory dynamic
* properties have been set.
* @author jkelly
*/
public interface DynamicInitializingBean
{
/**
* Invoked by a DynamicObjectFactory after it has set all dynamic bean
* properties supplied.
* <p>
* This method allows the bean instance to perform initialization only
* possible when all dynamic bean properties have been set and to throw an
* exception in the event of misconfiguration.
* @throws Exception in the event of misconfiguration (such as failure to set
* an essential property) or if initialization fails.
*/
void afterDynamicPropertiesSet() throws Exception;
}
Code:
package com.cbconstantini.core.spring;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean;
import org.springframework.util.StringUtils;
/**
* An ObjectFactoryCreatingFactoryBean that creates DynamicObjectFactory instances.
* @author jkelly
*/
public class DynamicObjectFactoryCreatingFactoryBean extends
ObjectFactoryCreatingFactoryBean
{
private String dynamicInitMethodName;
/**
* Sets the name of the initialization method that this factory will call
* after it sets the dynamicPropertyValues.
* @param dynamicInitMethodName
*/
public void setDynamicInitMethodName(String dynamicInitMethodName)
{
this.dynamicInitMethodName = dynamicInitMethodName;
}
/**
* Overrides ObjectFactoryCreatingFactoryBean.createInstance() to return a
* DynamicObjectFactory instead of an ObjectFactory.
*/
protected Object createInstance()
{
return new DynamicObjectFactory()
{
private Map dynamicPropertyValues;
private ObjectFactory wrappedObjectFactory = (ObjectFactory)
DynamicObjectFactoryCreatingFactoryBean.super.createInstance();
public void setDynamicPropertyValues(Map dynamicPropertyValues)
{
this.dynamicPropertyValues = dynamicPropertyValues;
}
public Object getObject() throws BeansException
{
// get the bean with only config time property values set
Object bean = wrappedObjectFactory.getObject();
// now set also dynamic property values
BeanWrapper bwBean = new BeanWrapperImpl(bean);
bwBean.setPropertyValues(dynamicPropertyValues);
// now call either custom dynamic init-method or afterDynamicPropertiesSet()
if (StringUtils.hasText(dynamicInitMethodName))
{
Method initMethod = BeanUtils.findMethod(bean.getClass(),
dynamicInitMethodName, null);
if (initMethod == null)
{
throw new BeanInitializationException(
"Couldn't find an init method named '" + dynamicInitMethodName
+ "' on bean of type '" + bean.getClass().getName() + "'");
}
if (!Modifier.isPublic(initMethod.getModifiers()))
{
initMethod.setAccessible(true);
}
try
{
initMethod.invoke(bean, (Object[]) null);
}
catch (InvocationTargetException ite)
{
throw new BeanInitializationException(
"An error occurred in afterDynamicPropertiesSet()", ite.getTargetException());
}
catch (Exception e)
{
throw new BeanInitializationException(
"An error occurred in afterDynamicPropertiesSet()", e);
}
}
else if (bean instanceof DynamicInitializingBean)
{
try
{
((DynamicInitializingBean) bean).afterDynamicPropertiesSet();
}
catch (Exception e)
{
throw new BeanInitializationException(
"An error occurred in afterDynamicPropertiesSet()", e);
}
}
return bean;
}
};
}
}
Now a TestCase (with accompanying application context file and helper classes) that shows sample usage:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
Test data for DynamicObjectFactoryCreatingFactoryBeanTest
-->
<beans>
<bean id="prototypeTarget" class="com.cbconstantini.core.spring.MyBean" singleton="false">
<property name="propertyKnownAtConfigTime">
<value>foo</value>
</property>
</bean>
<bean id="prototype" class="com.cbconstantini.core.spring.DynamicObjectFactoryCreatingFactoryBean">
<property name="targetBeanName"><idref local="prototypeTarget"/></property>
</bean>
<bean id="testBean" class="com.cbconstantini.core.spring.DynamicObjectFactoryCreatingFactoryBeanTest$TestBean">
<property name="dynamicObjectFactory"><ref local="prototype"/></property>
</bean>
<bean id="prototypeTargetWithCustomInit" class="com.cbconstantini.core.spring.MyBeanWithCustomInit" singleton="false">
<property name="propertyKnownAtConfigTime">
<value>foo</value>
</property>
</bean>
<bean id="prototypeWithCustomInit" class="com.cbconstantini.core.spring.DynamicObjectFactoryCreatingFactoryBean">
<property name="targetBeanName"><idref local="prototypeTargetWithCustomInit"/></property>
<property name="dynamicInitMethodName">
<value>myCustomInitMethod</value>
</property>
</bean>
<bean id="testBeanWithCustomInit" class="com.cbconstantini.core.spring.DynamicObjectFactoryCreatingFactoryBeanTest$TestBean">
<property name="dynamicObjectFactory"><ref local="prototypeWithCustomInit"/></property>
</bean>
</beans>
Code:
package com.cbconstantini.core.spring;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import junit.framework.TestCase;
/**
* TestCase for DynamicObjectFactoryCreatingFactoryBean.
* @author jkelly
*/
public class DynamicObjectFactoryCreatingFactoryBeanTest extends TestCase
{
private BeanFactory beanFactory;
protected void setUp() throws Exception
{
super.setUp();
this.beanFactory = new XmlBeanFactory(new ClassPathResource(
"DynamicObjectFactoryCreatingFactoryBeanTest.xml", getClass()));
}
public void testGetObject()
{
// get DynamicObjectFactory
TestBean testBean = (TestBean) beanFactory.getBean("testBean");
DynamicObjectFactory dynamicObjectFactory = testBean.getDynamicObjectFactory();
// set dynamicProperties
Map dynamicProperties = new HashMap();
dynamicProperties.put("propertyKnownAtRunTime", "bar");
dynamicObjectFactory.setDynamicPropertyValues(dynamicProperties);
// create a bean with the factory
Object obj = dynamicObjectFactory.getObject();
DynamicObjectFactoryTestBean bean = (DynamicObjectFactoryTestBean) dynamicObjectFactory.getObject();
// do assertions
assertEquals("getConcatenated()", "foobar", bean.getConcatenated());
}
public void testGetObjectWithCustomInit()
{
// get DynamicObjectFactory
TestBean testBean = (TestBean) beanFactory.getBean("testBeanWithCustomInit");
DynamicObjectFactory dynamicObjectFactory = testBean.getDynamicObjectFactory();
// set dynamicProperties
Map dynamicProperties = new HashMap();
dynamicProperties.put("propertyKnownAtRunTime", "bar");
dynamicObjectFactory.setDynamicPropertyValues(dynamicProperties);
// create a bean with the factory
Object obj = dynamicObjectFactory.getObject();
DynamicObjectFactoryTestBeanWithCustomInit bean = (DynamicObjectFactoryTestBeanWithCustomInit) dynamicObjectFactory.getObject();
// do assertions
assertEquals("getConcatenated()", "barfoo", bean.getConcatenated());
}
public static class TestBean {
public DynamicObjectFactory dynamicObjectFactory;
public DynamicObjectFactory getDynamicObjectFactory()
{
return dynamicObjectFactory;
}
public void setDynamicObjectFactory(
DynamicObjectFactory dynamicObjectFactory)
{
this.dynamicObjectFactory = dynamicObjectFactory;
}
}
}
Code:
package com.cbconstantini.core.spring;
/**
* Test class to be instantiated by a DynamicObjectFactory in
* DynamicObjectFactoryCreatingFactoryBeanTest.
* @author jkelly
*/
public class MyBean implements DynamicInitializingBean
{
private String propertyKnownAtConfigTime;
private String propertyKnownAtRunTime;
private String concatenated;
public void setPropertyKnownAtConfigTime(String propertyKnownAtConfigTime)
{
this.propertyKnownAtConfigTime = propertyKnownAtConfigTime;
}
public void setPropertyKnownAtRunTime(String propertyKnownAtRunTime)
{
this.propertyKnownAtRunTime = propertyKnownAtRunTime;
}
public String getConcatenated()
{
return concatenated;
}
/* (non-Javadoc)
* @see com.cbconstantini.core.spring.DynamicInitializingBean#afterDynamicPropertiesSet()
*/
public void afterDynamicPropertiesSet() throws Exception
{
concatenated = propertyKnownAtConfigTime + propertyKnownAtRunTime;
}
}
Code:
package com.cbconstantini.core.spring;
/**
* Test class to be instantiated by a DynamicObjectFactory in
* DynamicObjectFactoryCreatingFactoryBeanTest. This class has a custom
* init-method called myCustomInitMethod().
* @author jkelly
*/
public class MyBeanWithCustomInit
{
private String propertyKnownAtConfigTime;
private String propertyKnownAtRunTime;
private String concatenated;
public void setPropertyKnownAtConfigTime(String propertyKnownAtConfigTime)
{
this.propertyKnownAtConfigTime = propertyKnownAtConfigTime;
}
public void setPropertyKnownAtRunTime(String propertyKnownAtRunTime)
{
this.propertyKnownAtRunTime = propertyKnownAtRunTime;
}
public String getConcatenated()
{
return concatenated;
}
public void myCustomInitMethod()
{
concatenated = propertyKnownAtRunTime + propertyKnownAtConfigTime;
}
}