diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
index 2c64c60637b..1a3a2c0bcd8 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
@@ -16,8 +16,12 @@
package org.springframework.beans.factory.config;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
+import java.lang.reflect.Type;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
@@ -32,17 +36,27 @@ import org.springframework.util.Assert;
* @author Juergen Hoeller
* @since 2.5
*/
-public class DependencyDescriptor {
+public class DependencyDescriptor implements Serializable {
- private MethodParameter methodParameter;
+ private transient MethodParameter methodParameter;
- private Field field;
+ private transient Field field;
+
+ private Class declaringClass;
+
+ private String methodName;
+
+ private Class[] parameterTypes;
+
+ private int parameterIndex;
+
+ private String fieldName;
private final boolean required;
private final boolean eager;
- private Annotation[] fieldAnnotations;
+ private transient Annotation[] fieldAnnotations;
/**
@@ -65,6 +79,15 @@ public class DependencyDescriptor {
public DependencyDescriptor(MethodParameter methodParameter, boolean required, boolean eager) {
Assert.notNull(methodParameter, "MethodParameter must not be null");
this.methodParameter = methodParameter;
+ this.declaringClass = methodParameter.getDeclaringClass();
+ if (this.methodParameter.getMethod() != null) {
+ this.methodName = methodParameter.getMethod().getName();
+ this.parameterTypes = methodParameter.getMethod().getParameterTypes();
+ }
+ else {
+ this.parameterTypes = methodParameter.getConstructor().getParameterTypes();
+ }
+ this.parameterIndex = methodParameter.getParameterIndex();
this.required = required;
this.eager = eager;
}
@@ -89,6 +112,8 @@ public class DependencyDescriptor {
public DependencyDescriptor(Field field, boolean required, boolean eager) {
Assert.notNull(field, "Field must not be null");
this.field = field;
+ this.declaringClass = field.getDeclaringClass();
+ this.fieldName = field.getName();
this.required = required;
this.eager = eager;
}
@@ -156,6 +181,14 @@ public class DependencyDescriptor {
return (this.field != null ? this.field.getType() : this.methodParameter.getParameterType());
}
+ /**
+ * Determine the generic type of the wrapped parameter/field.
+ * @return the generic type (never null)
+ */
+ public Type getGenericDependencyType() {
+ return (this.field != null ? this.field.getGenericType() : this.methodParameter.getGenericParameterType());
+ }
+
/**
* Determine the generic element type of the wrapped Collection parameter/field, if any.
* @return the generic type, or null if none
@@ -201,4 +234,32 @@ public class DependencyDescriptor {
}
}
+
+ //---------------------------------------------------------------------
+ // Serialization support
+ //---------------------------------------------------------------------
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ // Rely on default serialization; just initialize state after deserialization.
+ ois.defaultReadObject();
+
+ // Restore reflective handles (which are unfortunately not serializable)
+ try {
+ if (this.fieldName != null) {
+ this.field = this.declaringClass.getDeclaredField(this.fieldName);
+ }
+ else if (this.methodName != null) {
+ this.methodParameter = new MethodParameter(
+ this.declaringClass.getDeclaredMethod(this.methodName, this.parameterTypes), this.parameterIndex);
+ }
+ else {
+ this.methodParameter = new MethodParameter(
+ this.declaringClass.getDeclaredConstructor(this.parameterTypes), this.parameterIndex);
+ }
+ }
+ catch (Throwable ex) {
+ throw new IllegalStateException("Could not find original class structure", ex);
+ }
+ }
+
}
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
index 94b4680ed1d..e5869b0e1c1 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
@@ -21,6 +21,8 @@ import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
@@ -508,14 +510,13 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
- boolean isEagerInit = false;
-
+ boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction() {
public Boolean run() {
- return Boolean.valueOf(((SmartFactoryBean) factory).isEagerInit());
+ return ((SmartFactoryBean) factory).isEagerInit();
}
- }, getAccessControlContext()).booleanValue();
+ }, getAccessControlContext());
}
else {
isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit();
@@ -634,14 +635,23 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
//---------------------------------------------------------------------
- // Implementation of superclass abstract methods
+ // Dependency resolution functionality
//---------------------------------------------------------------------
public Object resolveDependency(DependencyDescriptor descriptor, String beanName,
Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
- Class> type = descriptor.getDependencyType();
+ if (ObjectFactory.class.equals(descriptor.getDependencyType())) {
+ return new DependencyObjectFactory(descriptor, beanName);
+ }
+ else {
+ return doResolveDependency(descriptor, descriptor.getDependencyType(), beanName, autowiredBeanNames, typeConverter);
+ }
+ }
+
+ protected Object doResolveDependency(DependencyDescriptor descriptor, Class> type, String beanName,
+ Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
@@ -649,7 +659,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
String strVal = resolveEmbeddedValue((String) value);
value = evaluateBeanDefinitionString(strVal, getMergedBeanDefinition(beanName));
}
- return typeConverter.convertIfNecessary(value, type);
+ TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
+ return converter.convertIfNecessary(value, type);
}
if (type.isArray()) {
@@ -907,4 +918,38 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
+
+ /**
+ * Serializable ObjectFactory for lazy resolution of a dependency.
+ */
+ private class DependencyObjectFactory implements ObjectFactory, Serializable {
+
+ private final DependencyDescriptor descriptor;
+
+ private final String beanName;
+
+ private final Class type;
+
+ public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName) {
+ this.descriptor = descriptor;
+ this.beanName = beanName;
+ this.type = determineObjectFactoryType();
+ }
+
+ private Class determineObjectFactoryType() {
+ Type type = this.descriptor.getGenericDependencyType();
+ if (type instanceof ParameterizedType) {
+ Type arg = ((ParameterizedType) type).getActualTypeArguments()[0];
+ if (arg instanceof Class) {
+ return (Class) arg;
+ }
+ }
+ return Object.class;
+ }
+
+ public Object getObject() throws BeansException {
+ return doResolveDependency(this.descriptor, this.type, this.beanName, null, null);
+ }
+ }
+
}
diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
index f49d16d5377..4e21315ecdc 100644
--- a/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
+++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
@@ -16,8 +16,7 @@
package org.springframework.beans.factory.annotation;
-import static org.junit.Assert.*;
-
+import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -25,20 +24,24 @@ import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
+import static org.junit.Assert.*;
import org.junit.Test;
-import org.springframework.beans.factory.BeanCreationException;
-import org.springframework.beans.factory.BeanFactory;
-import org.springframework.beans.factory.FactoryBean;
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.beans.factory.support.GenericBeanDefinition;
-import org.springframework.beans.factory.support.RootBeanDefinition;
-
import test.beans.ITestBean;
import test.beans.IndexedTestBean;
import test.beans.NestedTestBean;
import test.beans.TestBean;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.AutowireCandidateQualifier;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.util.SerializationTestUtils;
+
/**
* Unit tests for {@link AutowiredAnnotationBeanPostProcessor}.
*
@@ -553,6 +556,53 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
bf.destroySingletons();
}
+ @Test
+ public void testObjectFactoryInjection() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryInjectionBean.class));
+ bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
+
+ ObjectFactoryInjectionBean bean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
+ assertSame(bf.getBean("testBean"), bean.getTestBean());
+ bf.destroySingletons();
+ }
+
+ @Test
+ public void testObjectFactoryQualifierInjection() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
+ RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
+ bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
+ bf.registerBeanDefinition("testBean", bd);
+
+ ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
+ assertSame(bf.getBean("testBean"), bean.getTestBean());
+ bf.destroySingletons();
+ }
+
+ @Test
+ public void testObjectFactorySerialization() throws Exception {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryInjectionBean.class));
+ bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
+ bf.setSerializationId("test");
+
+ ObjectFactoryInjectionBean bean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
+ assertSame(bf.getBean("testBean"), bean.getTestBean());
+ bean = (ObjectFactoryInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
+ assertSame(bf.getBean("testBean"), bean.getTestBean());
+ bf.destroySingletons();
+ }
+
@Test
public void testCustomAnnotationRequiredFieldResourceInjection() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -1218,6 +1268,29 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
}
+ public static class ObjectFactoryInjectionBean implements Serializable {
+
+ @Autowired
+ private ObjectFactory testBeanFactory;
+
+ public TestBean getTestBean() {
+ return this.testBeanFactory.getObject();
+ }
+ }
+
+
+ public static class ObjectFactoryQualifierInjectionBean {
+
+ @Autowired
+ //@Qualifier("testBean")
+ private ObjectFactory> testBeanFactory;
+
+ public TestBean getTestBean() {
+ return (TestBean) this.testBeanFactory.getObject();
+ }
+ }
+
+
public static class CustomAnnotationRequiredFieldResourceInjectionBean {
@MyAutowired(optional = false)
diff --git a/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java b/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java
index e202ebd7538..43d9fefec53 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java
@@ -17,6 +17,7 @@
package org.springframework.context.expression;
import java.util.Properties;
+import java.io.Serializable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -37,6 +38,7 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.StopWatch;
+import org.springframework.util.SerializationTestUtils;
/**
* @author Juergen Hoeller
@@ -47,7 +49,7 @@ public class ApplicationContextExpressionTests {
private static final Log factoryLog = LogFactory.getLog(DefaultListableBeanFactory.class);
@Test
- public void genericApplicationContext() {
+ public void genericApplicationContext() throws Exception {
GenericApplicationContext ac = new GenericApplicationContext();
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
@@ -139,8 +141,12 @@ public class ApplicationContextExpressionTests {
assertEquals("XXXmyNameYYY42ZZZ", tb3.name);
assertEquals(42, tb3.age);
assertEquals("123 UK", tb3.country);
+ assertEquals("123 UK", tb3.countryFactory.getObject());
assertSame(tb0, tb3.tb);
+ tb3 = (ValueTestBean) SerializationTestUtils.serializeAndDeserialize(tb3);
+ assertEquals("123 UK", tb3.countryFactory.getObject());
+
ConstructorValueTestBean tb4 = ac.getBean("tb4", ConstructorValueTestBean.class);
assertEquals("XXXmyNameYYY42ZZZ", tb4.name);
assertEquals(42, tb4.age);
@@ -198,7 +204,7 @@ public class ApplicationContextExpressionTests {
}
- public static class ValueTestBean {
+ public static class ValueTestBean implements Serializable {
@Autowired @Value("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ")
public String name;
@@ -209,8 +215,11 @@ public class ApplicationContextExpressionTests {
@Value("${code} #{systemProperties.country}")
public String country;
+ @Value("${code} #{systemProperties.country}")
+ public ObjectFactory countryFactory;
+
@Autowired @Qualifier("original")
- public TestBean tb;
+ public transient TestBean tb;
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/MethodParameter.java b/org.springframework.core/src/main/java/org/springframework/core/MethodParameter.java
index ee5bb3e08b9..8f8cfe35673 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/MethodParameter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/MethodParameter.java
@@ -49,7 +49,9 @@ public class MethodParameter {
private final int parameterIndex;
- private Class parameterType;
+ private Class> parameterType;
+
+ private Type genericParameterType;
private Annotation[] parameterAnnotations;
@@ -167,7 +169,7 @@ public class MethodParameter {
/**
* Set a resolved (generic) parameter type.
*/
- void setParameterType(Class parameterType) {
+ void setParameterType(Class> parameterType) {
this.parameterType = parameterType;
}
@@ -175,7 +177,7 @@ public class MethodParameter {
* Return the type of the method/constructor parameter.
* @return the parameter type (never null)
*/
- public Class getParameterType() {
+ public Class> getParameterType() {
if (this.parameterType == null) {
if (this.parameterIndex < 0) {
this.parameterType = (this.method != null ? this.method.getReturnType() : null);
@@ -189,6 +191,24 @@ public class MethodParameter {
return this.parameterType;
}
+ /**
+ * Return the generic type of the method/constructor parameter.
+ * @return the parameter type (never null)
+ */
+ public Type getGenericParameterType() {
+ if (this.genericParameterType == null) {
+ if (this.parameterIndex < 0) {
+ this.genericParameterType = (this.method != null ? this.method.getGenericReturnType() : null);
+ }
+ else {
+ this.genericParameterType = (this.method != null ?
+ this.method.getGenericParameterTypes()[this.parameterIndex] :
+ this.constructor.getGenericParameterTypes()[this.parameterIndex]);
+ }
+ }
+ return this.genericParameterType;
+ }
+
/**
* Return the annotations associated with the target method/constructor itself.
*/