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:
parent
01b8d9327d
commit
4447248a83
|
|
@ -57,7 +57,6 @@ import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.PriorityOrdered;
|
import org.springframework.core.PriorityOrdered;
|
||||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
import org.springframework.core.annotation.AnnotationAttributes;
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
@ -436,10 +435,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
private Object resolvedCachedArgument(String beanName, Object cachedArgument) {
|
private Object resolvedCachedArgument(String beanName, Object cachedArgument) {
|
||||||
if (cachedArgument instanceof DependencyDescriptor) {
|
if (cachedArgument instanceof DependencyDescriptor) {
|
||||||
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
|
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
|
||||||
TypeConverter typeConverter = this.beanFactory.getTypeConverter();
|
return this.beanFactory.resolveDependency(descriptor, beanName, null, null);
|
||||||
Object value = this.beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
|
|
||||||
AnnotationAwareOrderComparator.sortIfNecessary(value);
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
else if (cachedArgument instanceof RuntimeBeanReference) {
|
else if (cachedArgument instanceof RuntimeBeanReference) {
|
||||||
return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
|
return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
|
||||||
|
|
@ -479,7 +475,6 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
|
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
|
||||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||||
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||||
AnnotationAwareOrderComparator.sortIfNecessary(value);
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!this.cached) {
|
if (!this.cached) {
|
||||||
if (value != null || this.required) {
|
if (value != null || this.required) {
|
||||||
|
|
@ -557,7 +552,6 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
arguments = null;
|
arguments = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
AnnotationAwareOrderComparator.sortIfNecessary(arg);
|
|
||||||
arguments[i] = arg;
|
arguments[i] = arg;
|
||||||
}
|
}
|
||||||
synchronized (this) {
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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;
|
this.beanFactory = beanFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final BeanFactory getBeanFactory() {
|
||||||
|
return this.beanFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the provided bean definition is an autowire candidate.
|
* Determine whether the provided bean definition is an autowire candidate.
|
||||||
|
|
@ -336,4 +340,14 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
|
||||||
return value;
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* Strategy interface for determining whether a specific bean definition
|
||||||
* qualifies as an autowire candidate for a specific dependency.
|
* qualifies as an autowire candidate for a specific dependency.
|
||||||
*
|
*
|
||||||
* @author Mark Fisher
|
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Mark Fisher
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
public interface AutowireCandidateResolver {
|
public interface AutowireCandidateResolver {
|
||||||
|
|
@ -47,4 +47,15 @@ public interface AutowireCandidateResolver {
|
||||||
*/
|
*/
|
||||||
Object getSuggestedValue(DependencyDescriptor descriptor);
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -125,6 +127,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
/** Whether to allow eager class loading even for lazy-init beans */
|
/** Whether to allow eager class loading even for lazy-init beans */
|
||||||
private boolean allowEagerClassLoading = true;
|
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 */
|
/** Resolver to use for checking if a bean definition is an autowire candidate */
|
||||||
private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
|
private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
|
||||||
|
|
||||||
|
|
@ -205,6 +210,22 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
this.allowEagerClassLoading = allowEagerClassLoading;
|
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
|
* Set a custom autowire candidate resolver for this BeanFactory to use
|
||||||
* when deciding whether a bean definition should be considered as a
|
* 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);
|
return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName);
|
||||||
}
|
}
|
||||||
else {
|
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 {
|
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
|
||||||
|
|
||||||
|
Class<?> type = descriptor.getDependencyType();
|
||||||
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
|
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
|
|
@ -819,7 +845,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
autowiredBeanNames.addAll(matchingBeans.keySet());
|
autowiredBeanNames.addAll(matchingBeans.keySet());
|
||||||
}
|
}
|
||||||
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
|
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()) {
|
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
|
||||||
Class<?> elementType = descriptor.getCollectionType();
|
Class<?> elementType = descriptor.getCollectionType();
|
||||||
|
|
@ -840,7 +870,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
autowiredBeanNames.addAll(matchingBeans.keySet());
|
autowiredBeanNames.addAll(matchingBeans.keySet());
|
||||||
}
|
}
|
||||||
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
|
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()) {
|
else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
|
||||||
Class<?> keyType = descriptor.getMapKeyType();
|
Class<?> keyType = descriptor.getMapKeyType();
|
||||||
|
|
@ -1091,7 +1125,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getObject() throws BeansException {
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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;
|
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.GenericBeanDefinition;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.tests.sample.beans.ITestBean;
|
import org.springframework.tests.sample.beans.ITestBean;
|
||||||
import org.springframework.tests.sample.beans.IndexedTestBean;
|
import org.springframework.tests.sample.beans.IndexedTestBean;
|
||||||
|
|
@ -46,16 +47,13 @@ import org.springframework.util.SerializationTestUtils;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link AutowiredAnnotationBeanPostProcessor}.
|
|
||||||
*
|
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
public final class AutowiredAnnotationBeanPostProcessorTests {
|
public class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIncompleteBeanDefinition() {
|
public void testIncompleteBeanDefinition() {
|
||||||
|
|
@ -348,6 +346,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
@Test
|
@Test
|
||||||
public void testOrderedResourceInjection() {
|
public void testOrderedResourceInjection() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
bpp.setBeanFactory(bf);
|
bpp.setBeanFactory(bf);
|
||||||
bf.addBeanPostProcessor(bpp);
|
bf.addBeanPostProcessor(bpp);
|
||||||
|
|
@ -381,6 +380,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
@Test
|
@Test
|
||||||
public void testAnnotationOrderedResourceInjection() {
|
public void testAnnotationOrderedResourceInjection() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
bpp.setBeanFactory(bf);
|
bpp.setBeanFactory(bf);
|
||||||
bf.addBeanPostProcessor(bpp);
|
bf.addBeanPostProcessor(bpp);
|
||||||
|
|
@ -412,6 +412,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
@Test
|
@Test
|
||||||
public void testOrderedCollectionResourceInjection() {
|
public void testOrderedCollectionResourceInjection() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
bpp.setBeanFactory(bf);
|
bpp.setBeanFactory(bf);
|
||||||
bf.addBeanPostProcessor(bpp);
|
bf.addBeanPostProcessor(bpp);
|
||||||
|
|
@ -452,6 +453,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
@Test
|
@Test
|
||||||
public void testAnnotationOrderedCollectionResourceInjection() {
|
public void testAnnotationOrderedCollectionResourceInjection() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
bpp.setBeanFactory(bf);
|
bpp.setBeanFactory(bf);
|
||||||
bf.addBeanPostProcessor(bpp);
|
bf.addBeanPostProcessor(bpp);
|
||||||
|
|
@ -566,6 +568,55 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
bf.destroySingletons();
|
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
|
@Test
|
||||||
public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() {
|
public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
|
@ -1102,7 +1153,6 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
|
|
||||||
private TestBean testBean2;
|
private TestBean testBean2;
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public void setTestBean2(TestBean testBean2) {
|
public void setTestBean2(TestBean testBean2) {
|
||||||
if (this.testBean2 != null) {
|
if (this.testBean2 != null) {
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.beans.factory.annotation;
|
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.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||||
import org.springframework.beans.factory.support.AutowireCandidateResolver;
|
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.beans.factory.xml.XmlBeanDefinitionReader;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.springframework.tests.TestResourceUtils.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link CustomAutowireConfigurer}.
|
* Unit tests for {@link CustomAutowireConfigurer}.
|
||||||
*
|
*
|
||||||
|
|
@ -87,6 +88,11 @@ public final class CustomAutowireConfigurerTests {
|
||||||
public Object getSuggestedValue(DependencyDescriptor descriptor) {
|
public Object getSuggestedValue(DependencyDescriptor descriptor) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,11 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.context.support.GenericApplicationContext;
|
||||||
import org.springframework.core.annotation.AnnotationAttributes;
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
|
@ -35,14 +38,16 @@ import org.springframework.util.ClassUtils;
|
||||||
* Utility class that allows for convenient registration of common
|
* Utility class that allows for convenient registration of common
|
||||||
* {@link org.springframework.beans.factory.config.BeanPostProcessor} and
|
* {@link org.springframework.beans.factory.config.BeanPostProcessor} and
|
||||||
* {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor}
|
* {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor}
|
||||||
* definitions for annotation-based configuration.
|
* definitions for annotation-based configuration. Also registers a common
|
||||||
|
* {@link org.springframework.beans.factory.support.AutowireCandidateResolver}.
|
||||||
*
|
*
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
|
* @see ContextAnnotationAutowireCandidateResolver
|
||||||
* @see CommonAnnotationBeanPostProcessor
|
* @see CommonAnnotationBeanPostProcessor
|
||||||
* @see org.springframework.context.annotation.ConfigurationClassPostProcessor
|
* @see ConfigurationClassPostProcessor
|
||||||
* @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
|
* @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
|
||||||
* @see org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
|
* @see org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
|
||||||
* @see org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor
|
* @see org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor
|
||||||
|
|
@ -176,6 +181,16 @@ public class AnnotationConfigUtils {
|
||||||
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
|
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
|
||||||
BeanDefinitionRegistry registry, Object source) {
|
BeanDefinitionRegistry registry, Object source) {
|
||||||
|
|
||||||
|
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
|
||||||
|
if (beanFactory != null) {
|
||||||
|
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
|
||||||
|
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||||
|
}
|
||||||
|
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
|
||||||
|
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
|
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
|
||||||
|
|
||||||
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||||
|
|
@ -229,6 +244,18 @@ public class AnnotationConfigUtils {
|
||||||
return new BeanDefinitionHolder(definition, beanName);
|
return new BeanDefinitionHolder(definition, beanName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static DefaultListableBeanFactory unwrapDefaultListableBeanFactory(BeanDefinitionRegistry registry) {
|
||||||
|
if (registry instanceof DefaultListableBeanFactory) {
|
||||||
|
return (DefaultListableBeanFactory) registry;
|
||||||
|
}
|
||||||
|
else if (registry instanceof GenericApplicationContext) {
|
||||||
|
return ((GenericApplicationContext) registry).getDefaultListableBeanFactory();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
|
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
|
||||||
processCommonDefinitionAnnotations(abd, abd.getMetadata());
|
processCommonDefinitionAnnotations(abd, abd.getMetadata());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.springframework.aop.TargetSource;
|
||||||
|
import org.springframework.aop.framework.ProxyFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
|
||||||
|
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete implementation of the {@link org.springframework.beans.factory.support.AutowireCandidateResolver}
|
||||||
|
* strategy interface, providing support for qualifier annotations as well as for lazy resolution driven
|
||||||
|
* by the {@link Lazy} annotation in the {@code context.annotation} package.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
|
||||||
|
return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isLazy(DependencyDescriptor descriptor) {
|
||||||
|
for (Annotation ann : descriptor.getAnnotations()) {
|
||||||
|
Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
|
||||||
|
if (lazy != null && lazy.value()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MethodParameter methodParam = descriptor.getMethodParameter();
|
||||||
|
if (methodParam != null) {
|
||||||
|
Method method = methodParam.getMethod();
|
||||||
|
if (method == null || void.class.equals(method.getReturnType())) {
|
||||||
|
Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
|
||||||
|
if (lazy != null && lazy.value()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final String beanName) {
|
||||||
|
Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
|
||||||
|
"BeanFactory needs to be a DefaultListableBeanFactory");
|
||||||
|
final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
|
||||||
|
TargetSource ts = new TargetSource() {
|
||||||
|
@Override
|
||||||
|
public Class<?> getTargetClass() {
|
||||||
|
return descriptor.getDependencyType();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isStatic() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Object getTarget() {
|
||||||
|
return beanFactory.doResolveDependency(descriptor, beanName, null, null);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void releaseTarget(Object target) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ProxyFactory pf = new ProxyFactory();
|
||||||
|
pf.setTargetSource(ts);
|
||||||
|
Class<?> dependencyType = descriptor.getDependencyType();
|
||||||
|
if (dependencyType.isInterface()) {
|
||||||
|
pf.addInterface(dependencyType);
|
||||||
|
}
|
||||||
|
return pf.getProxy(beanFactory.getBeanClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -42,14 +42,21 @@ import java.lang.annotation.Target;
|
||||||
* method within a {@code @Lazy}-annotated {@code @Configuration} class, this indicates
|
* method within a {@code @Lazy}-annotated {@code @Configuration} class, this indicates
|
||||||
* overriding the 'default lazy' behavior and that the bean should be eagerly initialized.
|
* overriding the 'default lazy' behavior and that the bean should be eagerly initialized.
|
||||||
*
|
*
|
||||||
|
* <p>In addition to its role for component initialization, this annotation may also be placed
|
||||||
|
* on injection points marked with {@link org.springframework.beans.factory.annotation.Autowired}
|
||||||
|
* or {@link javax.inject.Inject}: In that context, it leads to the creation of a
|
||||||
|
* lazy-resolution proxy for all affected dependencies, as an alternative to using
|
||||||
|
* {@link org.springframework.beans.factory.ObjectFactory} or {@link javax.inject.Provider}.
|
||||||
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see Primary
|
* @see Primary
|
||||||
* @see Bean
|
* @see Bean
|
||||||
* @see Configuration
|
* @see Configuration
|
||||||
* @see org.springframework.stereotype.Component
|
* @see org.springframework.stereotype.Component
|
||||||
*/
|
*/
|
||||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface Lazy {
|
public @interface Lazy {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package org.springframework.context.support;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
|
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
@ -222,7 +221,6 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
|
||||||
if (this.allowCircularReferences != null) {
|
if (this.allowCircularReferences != null) {
|
||||||
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
|
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
|
||||||
}
|
}
|
||||||
beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -21,7 +21,6 @@ import java.io.IOException;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
|
|
@ -100,7 +99,6 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
|
||||||
*/
|
*/
|
||||||
public GenericApplicationContext() {
|
public GenericApplicationContext() {
|
||||||
this.beanFactory = new DefaultListableBeanFactory();
|
this.beanFactory = new DefaultListableBeanFactory();
|
||||||
this.beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,254 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
public class LazyAutowiredAnnotationBeanPostProcessorTests {
|
||||||
|
|
||||||
|
private void doTestLazyResourceInjection(Class<? extends TestBeanHolder> annotatedBeanClass) {
|
||||||
|
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
|
||||||
|
RootBeanDefinition abd = new RootBeanDefinition(annotatedBeanClass);
|
||||||
|
abd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||||
|
ac.registerBeanDefinition("annotatedBean", abd);
|
||||||
|
RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class);
|
||||||
|
tbd.setLazyInit(true);
|
||||||
|
ac.registerBeanDefinition("testBean", tbd);
|
||||||
|
ac.refresh();
|
||||||
|
|
||||||
|
TestBeanHolder bean = ac.getBean("annotatedBean", TestBeanHolder.class);
|
||||||
|
assertFalse(ac.getBeanFactory().containsSingleton("testBean"));
|
||||||
|
assertNotNull(bean.getTestBean());
|
||||||
|
assertNull(bean.getTestBean().getName());
|
||||||
|
assertTrue(ac.getBeanFactory().containsSingleton("testBean"));
|
||||||
|
TestBean tb = (TestBean) ac.getBean("testBean");
|
||||||
|
tb.setName("tb");
|
||||||
|
assertSame("tb", bean.getTestBean().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLazyResourceInjectionWithField() {
|
||||||
|
doTestLazyResourceInjection(FieldResourceInjectionBean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLazyResourceInjectionWithFieldAndCustomAnnotation() {
|
||||||
|
doTestLazyResourceInjection(FieldResourceInjectionBeanWithCompositeAnnotation.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLazyResourceInjectionWithMethod() {
|
||||||
|
doTestLazyResourceInjection(MethodResourceInjectionBean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLazyResourceInjectionWithMethodLevelLazy() {
|
||||||
|
doTestLazyResourceInjection(MethodResourceInjectionBeanWithMethodLevelLazy.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLazyResourceInjectionWithMethodAndCustomAnnotation() {
|
||||||
|
doTestLazyResourceInjection(MethodResourceInjectionBeanWithCompositeAnnotation.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLazyResourceInjectionWithConstructor() {
|
||||||
|
doTestLazyResourceInjection(ConstructorResourceInjectionBean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLazyResourceInjectionWithConstructorLevelLazy() {
|
||||||
|
doTestLazyResourceInjection(ConstructorResourceInjectionBeanWithConstructorLevelLazy.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLazyResourceInjectionWithConstructorAndCustomAnnotation() {
|
||||||
|
doTestLazyResourceInjection(ConstructorResourceInjectionBeanWithCompositeAnnotation.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLazyResourceInjectionWithNonExistingTarget() {
|
||||||
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
bf.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
|
||||||
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
|
bpp.setBeanFactory(bf);
|
||||||
|
bf.addBeanPostProcessor(bpp);
|
||||||
|
RootBeanDefinition bd = new RootBeanDefinition(FieldResourceInjectionBean.class);
|
||||||
|
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||||
|
bf.registerBeanDefinition("annotatedBean", bd);
|
||||||
|
|
||||||
|
FieldResourceInjectionBean bean = (FieldResourceInjectionBean) bf.getBean("annotatedBean");
|
||||||
|
assertNotNull(bean.getTestBean());
|
||||||
|
try {
|
||||||
|
bean.getTestBean().getName();
|
||||||
|
fail("Should have thrown NoSuchBeanDefinitionException");
|
||||||
|
}
|
||||||
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
|
// expected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface TestBeanHolder {
|
||||||
|
|
||||||
|
TestBean getTestBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class FieldResourceInjectionBean implements TestBeanHolder {
|
||||||
|
|
||||||
|
@Autowired @Lazy
|
||||||
|
private TestBean testBean;
|
||||||
|
|
||||||
|
public TestBean getTestBean() {
|
||||||
|
return this.testBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class FieldResourceInjectionBeanWithCompositeAnnotation implements TestBeanHolder {
|
||||||
|
|
||||||
|
@LazyInject
|
||||||
|
private TestBean testBean;
|
||||||
|
|
||||||
|
public TestBean getTestBean() {
|
||||||
|
return this.testBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class MethodResourceInjectionBean implements TestBeanHolder {
|
||||||
|
|
||||||
|
private TestBean testBean;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setTestBean(@Lazy TestBean testBean) {
|
||||||
|
if (this.testBean != null) {
|
||||||
|
throw new IllegalStateException("Already called");
|
||||||
|
}
|
||||||
|
this.testBean = testBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestBean getTestBean() {
|
||||||
|
return this.testBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class MethodResourceInjectionBeanWithMethodLevelLazy implements TestBeanHolder {
|
||||||
|
|
||||||
|
private TestBean testBean;
|
||||||
|
|
||||||
|
@Autowired @Lazy
|
||||||
|
public void setTestBean(TestBean testBean) {
|
||||||
|
if (this.testBean != null) {
|
||||||
|
throw new IllegalStateException("Already called");
|
||||||
|
}
|
||||||
|
this.testBean = testBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestBean getTestBean() {
|
||||||
|
return this.testBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class MethodResourceInjectionBeanWithCompositeAnnotation implements TestBeanHolder {
|
||||||
|
|
||||||
|
private TestBean testBean;
|
||||||
|
|
||||||
|
@LazyInject
|
||||||
|
public void setTestBean(TestBean testBean) {
|
||||||
|
if (this.testBean != null) {
|
||||||
|
throw new IllegalStateException("Already called");
|
||||||
|
}
|
||||||
|
this.testBean = testBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestBean getTestBean() {
|
||||||
|
return this.testBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ConstructorResourceInjectionBean implements TestBeanHolder {
|
||||||
|
|
||||||
|
private final TestBean testBean;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ConstructorResourceInjectionBean(@Lazy TestBean testBean) {
|
||||||
|
this.testBean = testBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestBean getTestBean() {
|
||||||
|
return this.testBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ConstructorResourceInjectionBeanWithConstructorLevelLazy implements TestBeanHolder {
|
||||||
|
|
||||||
|
private final TestBean testBean;
|
||||||
|
|
||||||
|
@Autowired @Lazy
|
||||||
|
public ConstructorResourceInjectionBeanWithConstructorLevelLazy(TestBean testBean) {
|
||||||
|
this.testBean = testBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestBean getTestBean() {
|
||||||
|
return this.testBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ConstructorResourceInjectionBeanWithCompositeAnnotation implements TestBeanHolder {
|
||||||
|
|
||||||
|
private final TestBean testBean;
|
||||||
|
|
||||||
|
@LazyInject
|
||||||
|
public ConstructorResourceInjectionBeanWithCompositeAnnotation(TestBean testBean) {
|
||||||
|
this.testBean = testBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestBean getTestBean() {
|
||||||
|
return this.testBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired @Lazy
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface LazyInject {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -164,7 +164,7 @@ public class MethodParameter {
|
||||||
* Returns the wrapped member.
|
* Returns the wrapped member.
|
||||||
* @return the Method or Constructor as Member
|
* @return the Method or Constructor as Member
|
||||||
*/
|
*/
|
||||||
private Member getMember() {
|
public Member getMember() {
|
||||||
// NOTE: no ternary expression to retain JDK <8 compatibility even when using
|
// NOTE: no ternary expression to retain JDK <8 compatibility even when using
|
||||||
// the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
|
// the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
|
||||||
// as common type, with that new base class not available on older JDKs)
|
// as common type, with that new base class not available on older JDKs)
|
||||||
|
|
@ -180,7 +180,7 @@ public class MethodParameter {
|
||||||
* Returns the wrapped annotated element.
|
* Returns the wrapped annotated element.
|
||||||
* @return the Method or Constructor as AnnotatedElement
|
* @return the Method or Constructor as AnnotatedElement
|
||||||
*/
|
*/
|
||||||
private AnnotatedElement getAnnotatedElement() {
|
public AnnotatedElement getAnnotatedElement() {
|
||||||
// NOTE: no ternary expression to retain JDK <8 compatibility even when using
|
// NOTE: no ternary expression to retain JDK <8 compatibility even when using
|
||||||
// the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
|
// the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
|
||||||
// as common type, with that new base class not available on older JDKs)
|
// as common type, with that new base class not available on older JDKs)
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,22 @@ public abstract class AnnotationUtils {
|
||||||
private static final Map<Class<?>, Boolean> annotatedInterfaceCache = new WeakHashMap<Class<?>, Boolean>();
|
private static final Map<Class<?>, Boolean> annotatedInterfaceCache = new WeakHashMap<Class<?>, Boolean>();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single {@link Annotation} of {@code annotationType} from the supplied
|
||||||
|
* annotation: either the given annotation itself or a meta-annotation thereof.
|
||||||
|
* @param ann the Annotation to check
|
||||||
|
* @param annotationType the annotation class to look for, both locally and as a meta-annotation
|
||||||
|
* @return the matching annotation or {@code null} if not found
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends Annotation> T getAnnotation(Annotation ann, Class<T> annotationType) {
|
||||||
|
if (annotationType.isInstance(ann)) {
|
||||||
|
return (T) ann;
|
||||||
|
}
|
||||||
|
return ann.annotationType().getAnnotation(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a single {@link Annotation} of {@code annotationType} from the supplied
|
* Get a single {@link Annotation} of {@code annotationType} from the supplied
|
||||||
* Method, Constructor or Field. Meta-annotations will be searched if the annotation
|
* Method, Constructor or Field. Meta-annotations will be searched if the annotation
|
||||||
|
|
@ -98,16 +114,7 @@ public abstract class AnnotationUtils {
|
||||||
*/
|
*/
|
||||||
public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
|
public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
|
||||||
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||||
A ann = resolvedMethod.getAnnotation(annotationType);
|
return getAnnotation((AnnotatedElement) resolvedMethod, annotationType);
|
||||||
if (ann == null) {
|
|
||||||
for (Annotation metaAnn : resolvedMethod.getAnnotations()) {
|
|
||||||
ann = metaAnn.annotationType().getAnnotation(annotationType);
|
|
||||||
if (ann != null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ann;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,13 @@ package org.springframework.web.context.support;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.tests.sample.beans.ITestBean;
|
|
||||||
import org.springframework.beans.MutablePropertyValues;
|
import org.springframework.beans.MutablePropertyValues;
|
||||||
import org.springframework.tests.sample.beans.TestBean;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||||
import org.springframework.mock.web.test.MockServletContext;
|
import org.springframework.mock.web.test.MockServletContext;
|
||||||
|
import org.springframework.tests.sample.beans.ITestBean;
|
||||||
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
@ -35,14 +36,18 @@ public class SpringBeanAutowiringSupportTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcessInjectionBasedOnServletContext() {
|
public void testProcessInjectionBasedOnServletContext() {
|
||||||
MockServletContext sc = new MockServletContext();
|
|
||||||
StaticWebApplicationContext wac = new StaticWebApplicationContext();
|
StaticWebApplicationContext wac = new StaticWebApplicationContext();
|
||||||
|
AnnotationConfigUtils.registerAnnotationConfigProcessors(wac);
|
||||||
|
|
||||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||||
pvs.add("name", "tb");
|
pvs.add("name", "tb");
|
||||||
wac.registerSingleton("testBean", TestBean.class, pvs);
|
wac.registerSingleton("testBean", TestBean.class, pvs);
|
||||||
|
|
||||||
|
MockServletContext sc = new MockServletContext();
|
||||||
wac.setServletContext(sc);
|
wac.setServletContext(sc);
|
||||||
wac.refresh();
|
wac.refresh();
|
||||||
sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
|
sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
|
||||||
|
|
||||||
InjectionTarget target = new InjectionTarget();
|
InjectionTarget target = new InjectionTarget();
|
||||||
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(target, sc);
|
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(target, sc);
|
||||||
assertTrue(target.testBean instanceof TestBean);
|
assertTrue(target.testBean instanceof TestBean);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue