Skip to content

Commit 209f43b

Browse files
committed
BeanFactory supports ObjectFactory as a dependency type for @Autowired and @value (SPR-6079)
1 parent 7448214 commit 209f43b

File tree

5 files changed

+232
-24
lines changed

5 files changed

+232
-24
lines changed

org.springframework.beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616

1717
package org.springframework.beans.factory.config;
1818

19+
import java.io.IOException;
20+
import java.io.ObjectInputStream;
21+
import java.io.Serializable;
1922
import java.lang.annotation.Annotation;
2023
import java.lang.reflect.Field;
24+
import java.lang.reflect.Type;
2125

2226
import org.springframework.core.GenericCollectionTypeResolver;
2327
import org.springframework.core.MethodParameter;
@@ -32,17 +36,27 @@
3236
* @author Juergen Hoeller
3337
* @since 2.5
3438
*/
35-
public class DependencyDescriptor {
39+
public class DependencyDescriptor implements Serializable {
3640

37-
private MethodParameter methodParameter;
41+
private transient MethodParameter methodParameter;
3842

39-
private Field field;
43+
private transient Field field;
44+
45+
private Class declaringClass;
46+
47+
private String methodName;
48+
49+
private Class[] parameterTypes;
50+
51+
private int parameterIndex;
52+
53+
private String fieldName;
4054

4155
private final boolean required;
4256

4357
private final boolean eager;
4458

45-
private Annotation[] fieldAnnotations;
59+
private transient Annotation[] fieldAnnotations;
4660

4761

4862
/**
@@ -65,6 +79,15 @@ public DependencyDescriptor(MethodParameter methodParameter, boolean required) {
6579
public DependencyDescriptor(MethodParameter methodParameter, boolean required, boolean eager) {
6680
Assert.notNull(methodParameter, "MethodParameter must not be null");
6781
this.methodParameter = methodParameter;
82+
this.declaringClass = methodParameter.getDeclaringClass();
83+
if (this.methodParameter.getMethod() != null) {
84+
this.methodName = methodParameter.getMethod().getName();
85+
this.parameterTypes = methodParameter.getMethod().getParameterTypes();
86+
}
87+
else {
88+
this.parameterTypes = methodParameter.getConstructor().getParameterTypes();
89+
}
90+
this.parameterIndex = methodParameter.getParameterIndex();
6891
this.required = required;
6992
this.eager = eager;
7093
}
@@ -89,6 +112,8 @@ public DependencyDescriptor(Field field, boolean required) {
89112
public DependencyDescriptor(Field field, boolean required, boolean eager) {
90113
Assert.notNull(field, "Field must not be null");
91114
this.field = field;
115+
this.declaringClass = field.getDeclaringClass();
116+
this.fieldName = field.getName();
92117
this.required = required;
93118
this.eager = eager;
94119
}
@@ -156,6 +181,14 @@ public Class<?> getDependencyType() {
156181
return (this.field != null ? this.field.getType() : this.methodParameter.getParameterType());
157182
}
158183

184+
/**
185+
* Determine the generic type of the wrapped parameter/field.
186+
* @return the generic type (never <code>null</code>)
187+
*/
188+
public Type getGenericDependencyType() {
189+
return (this.field != null ? this.field.getGenericType() : this.methodParameter.getGenericParameterType());
190+
}
191+
159192
/**
160193
* Determine the generic element type of the wrapped Collection parameter/field, if any.
161194
* @return the generic type, or <code>null</code> if none
@@ -201,4 +234,32 @@ public Annotation[] getAnnotations() {
201234
}
202235
}
203236

237+
238+
//---------------------------------------------------------------------
239+
// Serialization support
240+
//---------------------------------------------------------------------
241+
242+
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
243+
// Rely on default serialization; just initialize state after deserialization.
244+
ois.defaultReadObject();
245+
246+
// Restore reflective handles (which are unfortunately not serializable)
247+
try {
248+
if (this.fieldName != null) {
249+
this.field = this.declaringClass.getDeclaredField(this.fieldName);
250+
}
251+
else if (this.methodName != null) {
252+
this.methodParameter = new MethodParameter(
253+
this.declaringClass.getDeclaredMethod(this.methodName, this.parameterTypes), this.parameterIndex);
254+
}
255+
else {
256+
this.methodParameter = new MethodParameter(
257+
this.declaringClass.getDeclaredConstructor(this.parameterTypes), this.parameterIndex);
258+
}
259+
}
260+
catch (Throwable ex) {
261+
throw new IllegalStateException("Could not find original class structure", ex);
262+
}
263+
}
264+
204265
}

org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.lang.annotation.Annotation;
2222
import java.lang.ref.Reference;
2323
import java.lang.ref.WeakReference;
24+
import java.lang.reflect.ParameterizedType;
25+
import java.lang.reflect.Type;
2426
import java.security.AccessController;
2527
import java.security.PrivilegedAction;
2628
import java.util.ArrayList;
@@ -508,14 +510,13 @@ public void preInstantiateSingletons() throws BeansException {
508510
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
509511
if (isFactoryBean(beanName)) {
510512
final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
511-
boolean isEagerInit = false;
512-
513+
boolean isEagerInit;
513514
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
514515
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
515516
public Boolean run() {
516-
return Boolean.valueOf(((SmartFactoryBean) factory).isEagerInit());
517+
return ((SmartFactoryBean) factory).isEagerInit();
517518
}
518-
}, getAccessControlContext()).booleanValue();
519+
}, getAccessControlContext());
519520
}
520521
else {
521522
isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit();
@@ -634,22 +635,32 @@ protected boolean allowAliasOverriding() {
634635

635636

636637
//---------------------------------------------------------------------
637-
// Implementation of superclass abstract methods
638+
// Dependency resolution functionality
638639
//---------------------------------------------------------------------
639640

640641
public Object resolveDependency(DependencyDescriptor descriptor, String beanName,
641642
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
642643

643644
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
644-
Class<?> type = descriptor.getDependencyType();
645+
if (ObjectFactory.class.equals(descriptor.getDependencyType())) {
646+
return new DependencyObjectFactory(descriptor, beanName);
647+
}
648+
else {
649+
return doResolveDependency(descriptor, descriptor.getDependencyType(), beanName, autowiredBeanNames, typeConverter);
650+
}
651+
}
652+
653+
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
654+
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
645655

646656
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
647657
if (value != null) {
648658
if (value instanceof String) {
649659
String strVal = resolveEmbeddedValue((String) value);
650660
value = evaluateBeanDefinitionString(strVal, getMergedBeanDefinition(beanName));
651661
}
652-
return typeConverter.convertIfNecessary(value, type);
662+
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
663+
return converter.convertIfNecessary(value, type);
653664
}
654665

655666
if (type.isArray()) {
@@ -907,4 +918,38 @@ private Object readResolve() {
907918
}
908919
}
909920

921+
922+
/**
923+
* Serializable ObjectFactory for lazy resolution of a dependency.
924+
*/
925+
private class DependencyObjectFactory implements ObjectFactory, Serializable {
926+
927+
private final DependencyDescriptor descriptor;
928+
929+
private final String beanName;
930+
931+
private final Class type;
932+
933+
public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName) {
934+
this.descriptor = descriptor;
935+
this.beanName = beanName;
936+
this.type = determineObjectFactoryType();
937+
}
938+
939+
private Class determineObjectFactoryType() {
940+
Type type = this.descriptor.getGenericDependencyType();
941+
if (type instanceof ParameterizedType) {
942+
Type arg = ((ParameterizedType) type).getActualTypeArguments()[0];
943+
if (arg instanceof Class) {
944+
return (Class) arg;
945+
}
946+
}
947+
return Object.class;
948+
}
949+
950+
public Object getObject() throws BeansException {
951+
return doResolveDependency(this.descriptor, this.type, this.beanName, null, null);
952+
}
953+
}
954+
910955
}

org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,31 @@
1616

1717
package org.springframework.beans.factory.annotation;
1818

19-
import static org.junit.Assert.*;
20-
19+
import java.io.Serializable;
2120
import java.lang.annotation.ElementType;
2221
import java.lang.annotation.Retention;
2322
import java.lang.annotation.RetentionPolicy;
2423
import java.lang.annotation.Target;
2524
import java.util.List;
2625
import java.util.Map;
2726

27+
import static org.junit.Assert.*;
2828
import org.junit.Test;
29+
import test.beans.ITestBean;
30+
import test.beans.IndexedTestBean;
31+
import test.beans.NestedTestBean;
32+
import test.beans.TestBean;
33+
2934
import org.springframework.beans.factory.BeanCreationException;
3035
import org.springframework.beans.factory.BeanFactory;
3136
import org.springframework.beans.factory.FactoryBean;
37+
import org.springframework.beans.factory.ObjectFactory;
3238
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
39+
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
3340
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
3441
import org.springframework.beans.factory.support.GenericBeanDefinition;
3542
import org.springframework.beans.factory.support.RootBeanDefinition;
36-
37-
import test.beans.ITestBean;
38-
import test.beans.IndexedTestBean;
39-
import test.beans.NestedTestBean;
40-
import test.beans.TestBean;
43+
import org.springframework.util.SerializationTestUtils;
4144

4245
/**
4346
* Unit tests for {@link AutowiredAnnotationBeanPostProcessor}.
@@ -553,6 +556,53 @@ public void testMethodInjectionWithMapAndNoMatches() {
553556
bf.destroySingletons();
554557
}
555558

559+
@Test
560+
public void testObjectFactoryInjection() {
561+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
562+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
563+
bpp.setBeanFactory(bf);
564+
bf.addBeanPostProcessor(bpp);
565+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryInjectionBean.class));
566+
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
567+
568+
ObjectFactoryInjectionBean bean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
569+
assertSame(bf.getBean("testBean"), bean.getTestBean());
570+
bf.destroySingletons();
571+
}
572+
573+
@Test
574+
public void testObjectFactoryQualifierInjection() {
575+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
576+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
577+
bpp.setBeanFactory(bf);
578+
bf.addBeanPostProcessor(bpp);
579+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
580+
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
581+
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
582+
bf.registerBeanDefinition("testBean", bd);
583+
584+
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
585+
assertSame(bf.getBean("testBean"), bean.getTestBean());
586+
bf.destroySingletons();
587+
}
588+
589+
@Test
590+
public void testObjectFactorySerialization() throws Exception {
591+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
592+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
593+
bpp.setBeanFactory(bf);
594+
bf.addBeanPostProcessor(bpp);
595+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryInjectionBean.class));
596+
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
597+
bf.setSerializationId("test");
598+
599+
ObjectFactoryInjectionBean bean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
600+
assertSame(bf.getBean("testBean"), bean.getTestBean());
601+
bean = (ObjectFactoryInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
602+
assertSame(bf.getBean("testBean"), bean.getTestBean());
603+
bf.destroySingletons();
604+
}
605+
556606
@Test
557607
public void testCustomAnnotationRequiredFieldResourceInjection() {
558608
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -1218,6 +1268,29 @@ public Map<String, TestBean> getTestBeanMap() {
12181268
}
12191269

12201270

1271+
public static class ObjectFactoryInjectionBean implements Serializable {
1272+
1273+
@Autowired
1274+
private ObjectFactory<TestBean> testBeanFactory;
1275+
1276+
public TestBean getTestBean() {
1277+
return this.testBeanFactory.getObject();
1278+
}
1279+
}
1280+
1281+
1282+
public static class ObjectFactoryQualifierInjectionBean {
1283+
1284+
@Autowired
1285+
//@Qualifier("testBean")
1286+
private ObjectFactory<?> testBeanFactory;
1287+
1288+
public TestBean getTestBean() {
1289+
return (TestBean) this.testBeanFactory.getObject();
1290+
}
1291+
}
1292+
1293+
12211294
public static class CustomAnnotationRequiredFieldResourceInjectionBean {
12221295

12231296
@MyAutowired(optional = false)

0 commit comments

Comments
 (0)