Introduced support for @Lazy on injection points
This turned into a rather huge affair since it led to the introduction of a new AutowireCandidateResolver implementation in the spring-context module. That ACR impl is now being set through AnnotationConfigUtils; GenericApplicationContext and co do not set a default QualifierAnnotationAutowireCandidateResolver anymore (which has always been a smell anyway). At the same time, dependency ordering has moved from AutowiredAnnotationBeanPostProcessor to DefaultListableBeanFactory itself through a "dependencyComparator" strategy, applying to constructor dependencies and lazy resolution proxies as well. Issue: SPR-10353
This commit is contained in:
@@ -57,7 +57,6 @@ import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -436,10 +435,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
private Object resolvedCachedArgument(String beanName, Object cachedArgument) {
|
||||
if (cachedArgument instanceof DependencyDescriptor) {
|
||||
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
|
||||
TypeConverter typeConverter = this.beanFactory.getTypeConverter();
|
||||
Object value = this.beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
|
||||
AnnotationAwareOrderComparator.sortIfNecessary(value);
|
||||
return value;
|
||||
return this.beanFactory.resolveDependency(descriptor, beanName, null, null);
|
||||
}
|
||||
else if (cachedArgument instanceof RuntimeBeanReference) {
|
||||
return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
|
||||
@@ -479,7 +475,6 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||
AnnotationAwareOrderComparator.sortIfNecessary(value);
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (value != null || this.required) {
|
||||
@@ -557,7 +552,6 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
arguments = null;
|
||||
break;
|
||||
}
|
||||
AnnotationAwareOrderComparator.sortIfNecessary(arg);
|
||||
arguments[i] = arg;
|
||||
}
|
||||
synchronized (this) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -131,6 +131,10 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
protected final BeanFactory getBeanFactory() {
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether the provided bean definition is an autowire candidate.
|
||||
@@ -336,4 +340,14 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation always returns {@code null},
|
||||
* leaving lazy resolution support up to subclasses.
|
||||
*/
|
||||
@Override
|
||||
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,8 +23,8 @@ import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
* Strategy interface for determining whether a specific bean definition
|
||||
* qualifies as an autowire candidate for a specific dependency.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @since 2.5
|
||||
*/
|
||||
public interface AutowireCandidateResolver {
|
||||
@@ -47,4 +47,15 @@ public interface AutowireCandidateResolver {
|
||||
*/
|
||||
Object getSuggestedValue(DependencyDescriptor descriptor);
|
||||
|
||||
/**
|
||||
* Build a proxy for lazy resolution of the actual dependency target,
|
||||
* if demanded by the injection point.
|
||||
* @param descriptor the descriptor for the target method parameter or field
|
||||
* @param beanName the name of the bean that contains the injection point
|
||||
* @return the lazy resolution proxy for the actual dependency target,
|
||||
* or {@code null} if straight resolution is to be performed
|
||||
* @since 4.0
|
||||
*/
|
||||
Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName);
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -125,6 +127,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
/** Whether to allow eager class loading even for lazy-init beans */
|
||||
private boolean allowEagerClassLoading = true;
|
||||
|
||||
/** Optional OrderComparator for dependency Lists and arrays */
|
||||
private Comparator dependencyComparator;
|
||||
|
||||
/** Resolver to use for checking if a bean definition is an autowire candidate */
|
||||
private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
|
||||
|
||||
@@ -205,6 +210,22 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
this.allowEagerClassLoading = allowEagerClassLoading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a {@link java.util.Comparator} for dependency Lists and arrays.
|
||||
* @see org.springframework.core.OrderComparator
|
||||
* @see org.springframework.core.annotation.AnnotationAwareOrderComparator
|
||||
*/
|
||||
public void setDependencyComparator(Comparator dependencyComparator) {
|
||||
this.dependencyComparator = dependencyComparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dependency comparator for this BeanFactory (may be {@code null}.
|
||||
*/
|
||||
public Comparator getDependencyComparator() {
|
||||
return this.dependencyComparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom autowire candidate resolver for this BeanFactory to use
|
||||
* when deciding whether a bean definition should be considered as a
|
||||
@@ -786,13 +807,18 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName);
|
||||
}
|
||||
else {
|
||||
return doResolveDependency(descriptor, descriptor.getDependencyType(), beanName, autowiredBeanNames, typeConverter);
|
||||
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, beanName);
|
||||
if (result == null) {
|
||||
result = doResolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
|
||||
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
|
||||
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
|
||||
|
||||
Class<?> type = descriptor.getDependencyType();
|
||||
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
|
||||
if (value != null) {
|
||||
if (value instanceof String) {
|
||||
@@ -819,7 +845,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
autowiredBeanNames.addAll(matchingBeans.keySet());
|
||||
}
|
||||
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
|
||||
return converter.convertIfNecessary(matchingBeans.values(), type);
|
||||
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
|
||||
if (this.dependencyComparator != null && result instanceof Object[]) {
|
||||
Arrays.sort((Object[]) result, this.dependencyComparator);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
|
||||
Class<?> elementType = descriptor.getCollectionType();
|
||||
@@ -840,7 +870,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
autowiredBeanNames.addAll(matchingBeans.keySet());
|
||||
}
|
||||
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
|
||||
return converter.convertIfNecessary(matchingBeans.values(), type);
|
||||
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
|
||||
if (this.dependencyComparator != null && result instanceof List) {
|
||||
Collections.sort((List) result, this.dependencyComparator);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
|
||||
Class<?> keyType = descriptor.getMapKeyType();
|
||||
@@ -1091,7 +1125,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
|
||||
@Override
|
||||
public Object getObject() throws BeansException {
|
||||
return doResolveDependency(this.descriptor, this.descriptor.getDependencyType(), this.beanName, null, null);
|
||||
return doResolveDependency(this.descriptor, this.beanName, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,4 +46,9 @@ public class SimpleAutowireCandidateResolver implements AutowireCandidateResolve
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.tests.sample.beans.ITestBean;
|
||||
import org.springframework.tests.sample.beans.IndexedTestBean;
|
||||
@@ -46,16 +47,13 @@ import org.springframework.util.SerializationTestUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AutowiredAnnotationBeanPostProcessor}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @author Sam Brannen
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||
public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
public void testIncompleteBeanDefinition() {
|
||||
@@ -348,6 +346,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||
@Test
|
||||
public void testOrderedResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
@@ -381,6 +380,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||
@Test
|
||||
public void testAnnotationOrderedResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
@@ -412,6 +412,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||
@Test
|
||||
public void testOrderedCollectionResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
@@ -452,6 +453,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||
@Test
|
||||
public void testAnnotationOrderedCollectionResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
@@ -566,6 +568,55 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithMultipleOrderedCandidates() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ConstructorsResourceInjectionBean.class));
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
ConstructorsResourceInjectionBean bean = (ConstructorsResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertNull(bean.getTestBean3());
|
||||
assertSame(tb, bean.getTestBean4());
|
||||
assertEquals(2, bean.getNestedTestBeans().length);
|
||||
assertSame(ntb2, bean.getNestedTestBeans()[0]);
|
||||
assertSame(ntb1, bean.getNestedTestBeans()[1]);
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithMultipleCandidatesAsOrderedCollection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(
|
||||
ConstructorsCollectionResourceInjectionBean.class));
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
ConstructorsCollectionResourceInjectionBean bean = (ConstructorsCollectionResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertNull(bean.getTestBean3());
|
||||
assertSame(tb, bean.getTestBean4());
|
||||
assertEquals(2, bean.getNestedTestBeans().size());
|
||||
assertSame(ntb2, bean.getNestedTestBeans().get(0));
|
||||
assertSame(ntb1, bean.getNestedTestBeans().get(1));
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
@@ -1102,7 +1153,6 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||
|
||||
private TestBean testBean2;
|
||||
|
||||
|
||||
@Autowired
|
||||
public void setTestBean2(TestBean testBean2) {
|
||||
if (this.testBean2 != null) {
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
|
||||
package org.springframework.beans.factory.annotation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.tests.TestResourceUtils.qualifiedResource;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.support.AutowireCandidateResolver;
|
||||
@@ -28,6 +26,9 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.tests.TestResourceUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link CustomAutowireConfigurer}.
|
||||
*
|
||||
@@ -87,6 +88,11 @@ public final class CustomAutowireConfigurerTests {
|
||||
public Object getSuggestedValue(DependencyDescriptor descriptor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user