/* * Copyright 2002-2017 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.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Arrays; import java.util.List; import javax.annotation.PostConstruct; import org.junit.Before; import org.junit.Test; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.aop.interceptor.SimpleTraceInterceptor; import org.springframework.aop.scope.ScopedObject; import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.FactoryBean; 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.annotation.Lookup; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.ChildBeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.componentscan.simple.SimpleComponent; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.Order; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.DescriptiveResource; import org.springframework.stereotype.Component; import org.springframework.tests.sample.beans.ITestBean; import org.springframework.tests.sample.beans.TestBean; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import static org.junit.Assert.*; /** * @author Chris Beams * @author Juergen Hoeller * @author Sam Brannen */ public class ConfigurationClassPostProcessorTests { private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); @Before public void setup() { QualifierAnnotationAutowireCandidateResolver acr = new QualifierAnnotationAutowireCandidateResolver(); acr.setBeanFactory(this.beanFactory); this.beanFactory.setAutowireCandidateResolver(acr); } /** * Enhanced {@link Configuration} classes are only necessary for respecting * certain bean semantics, like singleton-scoping, scoped proxies, etc. * <p>Technically, {@link ConfigurationClassPostProcessor} could fail to enhance the * registered Configuration classes and many use cases would still work. * Certain cases, however, like inter-bean singleton references would not. * We test for such a case below, and in doing so prove that enhancement is * working. */ @Test public void enhancementIsPresentBecauseSingletonSemanticsAreRespected() { beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); Foo foo = beanFactory.getBean("foo", Foo.class); Bar bar = beanFactory.getBean("bar", Bar.class); assertSame(foo, bar.foo); assertTrue(Arrays.asList(beanFactory.getDependentBeans("foo")).contains("bar")); } @Test public void configurationIntrospectionOfInnerClassesWorksWithDotNameSyntax() { beanFactory.registerBeanDefinition("config", new RootBeanDefinition(getClass().getName() + ".SingletonBeanConfig")); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); Foo foo = beanFactory.getBean("foo", Foo.class); Bar bar = beanFactory.getBean("bar", Bar.class); assertSame(foo, bar.foo); } /** * Tests the fix for SPR-5655, a special workaround that prefers reflection * over ASM if a bean class is already loaded. */ @Test public void alreadyLoadedConfigurationClasses() { beanFactory.registerBeanDefinition("unloadedConfig", new RootBeanDefinition(UnloadedConfig.class.getName())); beanFactory.registerBeanDefinition("loadedConfig", new RootBeanDefinition(LoadedConfig.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); beanFactory.getBean("foo"); beanFactory.getBean("bar"); } /** * Tests whether a bean definition without a specified bean class is handled * correctly. */ @Test public void postProcessorIntrospectsInheritedDefinitionsCorrectly() { beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class)); beanFactory.registerBeanDefinition("parent", new RootBeanDefinition(TestBean.class)); beanFactory.registerBeanDefinition("child", new ChildBeanDefinition("parent")); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); Foo foo = beanFactory.getBean("foo", Foo.class); Bar bar = beanFactory.getBean("bar", Bar.class); assertSame(foo, bar.foo); } @Test public void postProcessorWorksWithComposedConfigurationUsingReflection() { RootBeanDefinition beanDefinition = new RootBeanDefinition(ComposedConfigurationClass.class); assertSupportForComposedAnnotation(beanDefinition); } @Test public void postProcessorWorksWithComposedConfigurationUsingAsm() { RootBeanDefinition beanDefinition = new RootBeanDefinition(ComposedConfigurationClass.class.getName()); assertSupportForComposedAnnotation(beanDefinition); } @Test public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingReflection() { RootBeanDefinition beanDefinition = new RootBeanDefinition( ComposedConfigurationWithAttributeOverrideForBasePackage.class); assertSupportForComposedAnnotation(beanDefinition); } @Test public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingAsm() { RootBeanDefinition beanDefinition = new RootBeanDefinition( ComposedConfigurationWithAttributeOverrideForBasePackage.class.getName()); assertSupportForComposedAnnotation(beanDefinition); } @Test public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingReflection() { RootBeanDefinition beanDefinition = new RootBeanDefinition( ComposedConfigurationWithAttributeOverrideForExcludeFilter.class); assertSupportForComposedAnnotationWithExclude(beanDefinition); } @Test public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingAsm() { RootBeanDefinition beanDefinition = new RootBeanDefinition( ComposedConfigurationWithAttributeOverrideForExcludeFilter.class.getName()); assertSupportForComposedAnnotationWithExclude(beanDefinition); } @Test public void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingReflection() { RootBeanDefinition beanDefinition = new RootBeanDefinition( ComposedComposedConfigurationWithAttributeOverridesClass.class); assertSupportForComposedAnnotation(beanDefinition); } @Test public void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingAsm() { RootBeanDefinition beanDefinition = new RootBeanDefinition( ComposedComposedConfigurationWithAttributeOverridesClass.class.getName()); assertSupportForComposedAnnotation(beanDefinition); } @Test public void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesUsingReflection() { RootBeanDefinition beanDefinition = new RootBeanDefinition( MetaComponentScanConfigurationWithAttributeOverridesClass.class); assertSupportForComposedAnnotation(beanDefinition); } @Test public void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesUsingAsm() { RootBeanDefinition beanDefinition = new RootBeanDefinition( MetaComponentScanConfigurationWithAttributeOverridesClass.class.getName()); assertSupportForComposedAnnotation(beanDefinition); } @Test public void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesSubclassUsingReflection() { RootBeanDefinition beanDefinition = new RootBeanDefinition( SubMetaComponentScanConfigurationWithAttributeOverridesClass.class); assertSupportForComposedAnnotation(beanDefinition); } @Test public void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesSubclassUsingAsm() { RootBeanDefinition beanDefinition = new RootBeanDefinition( SubMetaComponentScanConfigurationWithAttributeOverridesClass.class.getName()); assertSupportForComposedAnnotation(beanDefinition); } private void assertSupportForComposedAnnotation(RootBeanDefinition beanDefinition) { beanFactory.registerBeanDefinition("config", beanDefinition); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.setEnvironment(new StandardEnvironment()); pp.postProcessBeanFactory(beanFactory); SimpleComponent simpleComponent = beanFactory.getBean(SimpleComponent.class); assertNotNull(simpleComponent); } private void assertSupportForComposedAnnotationWithExclude(RootBeanDefinition beanDefinition) { beanFactory.registerBeanDefinition("config", beanDefinition); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.setEnvironment(new StandardEnvironment()); pp.postProcessBeanFactory(beanFactory); try { beanFactory.getBean(SimpleComponent.class); fail("Should have thrown NoSuchBeanDefinitionException"); } catch (NoSuchBeanDefinitionException ex) { // expected } } @Test public void postProcessorOverridesNonApplicationBeanDefinitions() { RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); rbd.setRole(RootBeanDefinition.ROLE_SUPPORT); beanFactory.registerBeanDefinition("bar", rbd); beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); Foo foo = beanFactory.getBean("foo", Foo.class); Bar bar = beanFactory.getBean("bar", Bar.class); assertSame(foo, bar.foo); } @Test public void postProcessorDoesNotOverrideRegularBeanDefinitions() { RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); rbd.setResource(new DescriptiveResource("XML or something")); beanFactory.registerBeanDefinition("bar", rbd); beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); beanFactory.getBean("foo", Foo.class); beanFactory.getBean("bar", TestBean.class); } @Test public void postProcessorDoesNotOverrideRegularBeanDefinitionsEvenWithScopedProxy() { RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); rbd.setResource(new DescriptiveResource("XML or something")); BeanDefinitionHolder proxied = ScopedProxyUtils.createScopedProxy( new BeanDefinitionHolder(rbd, "bar"), beanFactory, true); beanFactory.registerBeanDefinition("bar", proxied.getBeanDefinition()); beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); beanFactory.getBean("foo", Foo.class); beanFactory.getBean("bar", TestBean.class); } @Test public void postProcessorFailsOnImplicitOverrideIfOverridingIsNotAllowed() { RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); rbd.setResource(new DescriptiveResource("XML or something")); beanFactory.registerBeanDefinition("bar", rbd); beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class)); beanFactory.setAllowBeanDefinitionOverriding(false); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); try { pp.postProcessBeanFactory(beanFactory); fail("Should have thrown BeanDefinitionStoreException"); } catch (BeanDefinitionStoreException ex) { assertTrue(ex.getMessage().contains("bar")); assertTrue(ex.getMessage().contains("SingletonBeanConfig")); assertTrue(ex.getMessage().contains(TestBean.class.getName())); } } @Test public void configurationClassesProcessedInCorrectOrder() { beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(OverridingSingletonBeanConfig.class)); beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(SingletonBeanConfig.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); Foo foo = beanFactory.getBean(Foo.class); assertTrue(foo instanceof ExtendedFoo); Bar bar = beanFactory.getBean(Bar.class); assertSame(foo, bar.foo); } @Test public void configurationClassesWithValidOverridingForProgrammaticCall() { beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(OverridingAgainSingletonBeanConfig.class)); beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(OverridingSingletonBeanConfig.class)); beanFactory.registerBeanDefinition("config3", new RootBeanDefinition(SingletonBeanConfig.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); Foo foo = beanFactory.getBean(Foo.class); assertTrue(foo instanceof ExtendedAgainFoo); Bar bar = beanFactory.getBean(Bar.class); assertSame(foo, bar.foo); } @Test public void configurationClassesWithInvalidOverridingForProgrammaticCall() { beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(InvalidOverridingSingletonBeanConfig.class)); beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(OverridingSingletonBeanConfig.class)); beanFactory.registerBeanDefinition("config3", new RootBeanDefinition(SingletonBeanConfig.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); try { beanFactory.getBean(Bar.class); fail("Should have thrown BeanCreationException"); } catch (BeanCreationException ex) { assertTrue(ex.getMessage().contains("OverridingSingletonBeanConfig.foo")); assertTrue(ex.getMessage().contains(ExtendedFoo.class.getName())); assertTrue(ex.getMessage().contains(Foo.class.getName())); assertTrue(ex.getMessage().contains("InvalidOverridingSingletonBeanConfig")); } } @Test public void nestedConfigurationClassesProcessedInCorrectOrder() { beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedNestedClasses.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); Foo foo = beanFactory.getBean(Foo.class); assertTrue(foo instanceof ExtendedFoo); Bar bar = beanFactory.getBean(Bar.class); assertSame(foo, bar.foo); } @Test public void scopedProxyTargetMarkedAsNonAutowireCandidate() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ScopedProxyConfigurationClass.class)); beanFactory.registerBeanDefinition("consumer", new RootBeanDefinition(ScopedProxyConsumer.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); ITestBean injected = beanFactory.getBean("consumer", ScopedProxyConsumer.class).testBean; assertTrue(injected instanceof ScopedObject); assertSame(beanFactory.getBean("scopedClass"), injected); assertSame(beanFactory.getBean(ITestBean.class), injected); } @Test public void processingAllowedOnlyOncePerProcessorRegistryPair() { DefaultListableBeanFactory bf1 = new DefaultListableBeanFactory(); DefaultListableBeanFactory bf2 = new DefaultListableBeanFactory(); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(bf1); // first invocation -- should succeed try { pp.postProcessBeanFactory(bf1); // second invocation for bf1 -- should throw fail("expected exception"); } catch (IllegalStateException ex) { } pp.postProcessBeanFactory(bf2); // first invocation for bf2 -- should succeed try { pp.postProcessBeanFactory(bf2); // second invocation for bf2 -- should throw fail("expected exception"); } catch (IllegalStateException ex) { } } @Test public void genericsBasedInjection() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); RootBeanDefinition bd = new RootBeanDefinition(RepositoryInjectionBean.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); beanFactory.registerBeanDefinition("annotatedBean", bd); beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); RepositoryInjectionBean bean = (RepositoryInjectionBean) beanFactory.getBean("annotatedBean"); assertEquals("Repository<String>", bean.stringRepository.toString()); assertEquals("Repository<Integer>", bean.integerRepository.toString()); } @Test public void genericsBasedInjectionWithScoped() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); RootBeanDefinition bd = new RootBeanDefinition(RepositoryInjectionBean.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); beanFactory.registerBeanDefinition("annotatedBean", bd); beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ScopedRepositoryConfiguration.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); RepositoryInjectionBean bean = (RepositoryInjectionBean) beanFactory.getBean("annotatedBean"); assertEquals("Repository<String>", bean.stringRepository.toString()); assertEquals("Repository<Integer>", bean.integerRepository.toString()); } @Test public void genericsBasedInjectionWithScopedProxy() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); RootBeanDefinition bd = new RootBeanDefinition(RepositoryInjectionBean.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); beanFactory.registerBeanDefinition("annotatedBean", bd); beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ScopedProxyRepositoryConfiguration.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); beanFactory.freezeConfiguration(); RepositoryInjectionBean bean = (RepositoryInjectionBean) beanFactory.getBean("annotatedBean"); assertEquals("Repository<String>", bean.stringRepository.toString()); assertEquals("Repository<Integer>", bean.integerRepository.toString()); assertTrue(AopUtils.isCglibProxy(bean.stringRepository)); assertTrue(AopUtils.isCglibProxy(bean.integerRepository)); } @Test public void genericsBasedInjectionWithScopedProxyUsingAsm() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); RootBeanDefinition bd = new RootBeanDefinition(RepositoryInjectionBean.class.getName()); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); beanFactory.registerBeanDefinition("annotatedBean", bd); beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ScopedProxyRepositoryConfiguration.class.getName())); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); beanFactory.freezeConfiguration(); RepositoryInjectionBean bean = (RepositoryInjectionBean) beanFactory.getBean("annotatedBean"); assertEquals("Repository<String>", bean.stringRepository.toString()); assertEquals("Repository<Integer>", bean.integerRepository.toString()); assertTrue(AopUtils.isCglibProxy(bean.stringRepository)); assertTrue(AopUtils.isCglibProxy(bean.integerRepository)); } @Test public void genericsBasedInjectionWithImplTypeAtInjectionPoint() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); RootBeanDefinition bd = new RootBeanDefinition(SpecificRepositoryInjectionBean.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); beanFactory.registerBeanDefinition("annotatedBean", bd); beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(SpecificRepositoryConfiguration.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); beanFactory.preInstantiateSingletons(); SpecificRepositoryInjectionBean bean = (SpecificRepositoryInjectionBean) beanFactory.getBean("annotatedBean"); assertSame(beanFactory.getBean("genericRepo"), bean.genericRepository); } @Test public void genericsBasedInjectionWithFactoryBean() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); RootBeanDefinition bd = new RootBeanDefinition(RepositoryFactoryBeanInjectionBean.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); beanFactory.registerBeanDefinition("annotatedBean", bd); beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryFactoryBeanConfiguration.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); beanFactory.preInstantiateSingletons(); RepositoryFactoryBeanInjectionBean bean = (RepositoryFactoryBeanInjectionBean) beanFactory.getBean("annotatedBean"); assertSame(beanFactory.getBean("&repoFactoryBean"), bean.repositoryFactoryBean); assertSame(beanFactory.getBean("&repoFactoryBean"), bean.qualifiedRepositoryFactoryBean); assertSame(beanFactory.getBean("&repoFactoryBean"), bean.prefixQualifiedRepositoryFactoryBean); } @Test public void genericsBasedInjectionWithRawMatch() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawMatchingConfiguration.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); assertSame(beanFactory.getBean("rawRepo"), beanFactory.getBean("repoConsumer")); } @Test public void genericsBasedInjectionWithWildcardMatch() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(WildcardMatchingConfiguration.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer")); } @Test public void genericsBasedInjectionWithWildcardWithExtendsMatch() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(WildcardWithExtendsConfiguration.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); assertSame(beanFactory.getBean("stringRepo"), beanFactory.getBean("repoConsumer")); } @Test public void genericsBasedInjectionWithWildcardWithGenericExtendsMatch() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(WildcardWithGenericExtendsConfiguration.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer")); } @Test public void genericsBasedInjectionWithEarlyGenericsMatching() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); String[] beanNames = beanFactory.getBeanNamesForType(Repository.class); assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo")); beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class)); assertEquals(1, beanNames.length); assertEquals("stringRepo", beanNames[0]); } @Test public void genericsBasedInjectionWithLateGenericsMatching() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); beanFactory.preInstantiateSingletons(); String[] beanNames = beanFactory.getBeanNamesForType(Repository.class); assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo")); beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class)); assertEquals(1, beanNames.length); assertEquals("stringRepo", beanNames[0]); } @Test public void genericsBasedInjectionWithEarlyGenericsMatchingOnCglibProxy() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setProxyTargetClass(true); autoProxyCreator.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(autoProxyCreator); beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); String[] beanNames = beanFactory.getBeanNamesForType(Repository.class); assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo")); beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class)); assertEquals(1, beanNames.length); assertEquals("stringRepo", beanNames[0]); assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo"))); } @Test public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxy() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setProxyTargetClass(true); autoProxyCreator.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(autoProxyCreator); beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); beanFactory.preInstantiateSingletons(); String[] beanNames = beanFactory.getBeanNamesForType(Repository.class); assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo")); beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class)); assertEquals(1, beanNames.length); assertEquals("stringRepo", beanNames[0]); assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo"))); } @Test public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setProxyTargetClass(true); autoProxyCreator.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(autoProxyCreator); beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); beanFactory.preInstantiateSingletons(); String[] beanNames = beanFactory.getBeanNamesForType(Repository.class); assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo")); beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class)); assertEquals(1, beanNames.length); assertEquals("stringRepo", beanNames[0]); assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo"))); } @Test public void genericsBasedInjectionWithEarlyGenericsMatchingOnJdkProxy() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(autoProxyCreator); beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class); assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo")); beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class)); assertEquals(1, beanNames.length); assertEquals("stringRepo", beanNames[0]); assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo"))); } @Test public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxy() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(autoProxyCreator); beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); beanFactory.preInstantiateSingletons(); String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class); assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo")); beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class)); assertEquals(1, beanNames.length); assertEquals("stringRepo", beanNames[0]); assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo"))); } @Test public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(autoProxyCreator); beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); beanFactory.preInstantiateSingletons(); String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class); assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo")); beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class)); assertEquals(1, beanNames.length); assertEquals("stringRepo", beanNames[0]); assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo"))); } @Test public void testSelfReferenceExclusionForFactoryMethodOnSameBean() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor()); beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfig.class)); beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); beanFactory.preInstantiateSingletons(); beanFactory.getBean(ServiceBean.class); } @Test public void testConfigWithDefaultMethods() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor()); beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class)); beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); beanFactory.preInstantiateSingletons(); beanFactory.getBean(ServiceBean.class); } @Test public void testConfigWithDefaultMethodsUsingAsm() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor()); beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class.getName())); beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class.getName())); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); beanFactory.preInstantiateSingletons(); beanFactory.getBean(ServiceBean.class); } @Test public void testCircularDependency() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(bpp); beanFactory.registerBeanDefinition("configClass1", new RootBeanDefinition(A.class)); beanFactory.registerBeanDefinition("configClass2", new RootBeanDefinition(AStrich.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); try { beanFactory.preInstantiateSingletons(); fail("Should have thrown BeanCreationException"); } catch (BeanCreationException ex) { assertTrue(ex.getMessage().contains("Circular reference")); } } @Test public void testCircularDependencyWithApplicationContext() { try { new AnnotationConfigApplicationContext(A.class, AStrich.class); fail("Should have thrown BeanCreationException"); } catch (BeanCreationException ex) { assertTrue(ex.getMessage().contains("Circular reference")); } } @Test public void testPrototypeArgumentThroughBeanMethodCall() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithPrototype.class); ctx.getBean(FooFactory.class).createFoo(new BarArgument()); } @Test public void testSingletonArgumentThroughBeanMethodCall() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithSingleton.class); ctx.getBean(FooFactory.class).createFoo(new BarArgument()); } @Test public void testNullArgumentThroughBeanMethodCall() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithNull.class); ctx.getBean("aFoo"); } @Test public void testInjectionPointMatchForNarrowTargetReturnType() { ApplicationContext ctx = new AnnotationConfigApplicationContext(FooBarConfiguration.class); assertSame(ctx.getBean(BarImpl.class), ctx.getBean(FooImpl.class).bar); } @Test public void testCollectionInjectionFromSameConfigurationClass() { ApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionInjectionConfiguration.class); CollectionInjectionConfiguration bean = ctx.getBean(CollectionInjectionConfiguration.class); assertNotNull(bean.testBeans); assertEquals(1, bean.testBeans.size()); assertSame(ctx.getBean(TestBean.class), bean.testBeans.get(0)); } @Test public void testBeanLookupFromSameConfigurationClass() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanLookupConfiguration.class); BeanLookupConfiguration bean = ctx.getBean(BeanLookupConfiguration.class); assertNotNull(bean.getTestBean()); assertSame(ctx.getBean(TestBean.class), bean.getTestBean()); } // ------------------------------------------------------------------------- @Configuration @Order(1) static class SingletonBeanConfig { public @Bean Foo foo() { return new Foo(); } public @Bean Bar bar() { return new Bar(foo()); } } @Configuration @Order(2) static class OverridingSingletonBeanConfig { public @Bean ExtendedFoo foo() { return new ExtendedFoo(); } public @Bean Bar bar() { return new Bar(foo()); } } @Configuration static class OverridingAgainSingletonBeanConfig { public @Bean ExtendedAgainFoo foo() { return new ExtendedAgainFoo(); } } @Configuration static class InvalidOverridingSingletonBeanConfig { public @Bean Foo foo() { return new Foo(); } } @Configuration static class ConfigWithOrderedNestedClasses { @Configuration @Order(1) static class SingletonBeanConfig { public @Bean Foo foo() { return new Foo(); } public @Bean Bar bar() { return new Bar(foo()); } } @Configuration @Order(2) static class OverridingSingletonBeanConfig { public @Bean ExtendedFoo foo() { return new ExtendedFoo(); } public @Bean Bar bar() { return new Bar(foo()); } } } static class Foo { } static class ExtendedFoo extends Foo { } static class ExtendedAgainFoo extends ExtendedFoo { } static class Bar { final Foo foo; public Bar(Foo foo) { this.foo = foo; } } @Configuration static class UnloadedConfig { public @Bean Foo foo() { return new Foo(); } } @Configuration static class LoadedConfig { public @Bean Bar bar() { return new Bar(new Foo()); } } public static class ScopedProxyConsumer { @Autowired public ITestBean testBean; } @Configuration public static class ScopedProxyConfigurationClass { @Bean @Lazy @Scope(proxyMode = ScopedProxyMode.INTERFACES) public ITestBean scopedClass() { return new TestBean(); } } public interface RepositoryInterface<T> { String toString(); } public static class Repository<T> implements RepositoryInterface<T> { } public static class GenericRepository<T> extends Repository<T> { } public static class RepositoryFactoryBean<T> implements FactoryBean<T> { @Override public T getObject() { throw new IllegalStateException(); } @Override public Class<?> getObjectType() { return Object.class; } @Override public boolean isSingleton() { return false; } } public static class RepositoryInjectionBean { @Autowired public Repository<String> stringRepository; @Autowired public Repository<Integer> integerRepository; } @Configuration public static class RepositoryConfiguration { @Bean public Repository<String> stringRepo() { return new Repository<String>() { @Override public String toString() { return "Repository<String>"; } }; } @Bean public Repository<Integer> integerRepo() { return new Repository<Integer>() { @Override public String toString() { return "Repository<Integer>"; } }; } @Bean public Repository<?> genericRepo() { return new Repository<Object>() { @Override public String toString() { return "Repository<Object>"; } }; } } @Configuration public static class RawRepositoryConfiguration { @Bean public Repository stringRepo() { return new Repository<String>() { @Override public String toString() { return "Repository<String>"; } }; } } @Configuration public static class ScopedRepositoryConfiguration { @Bean @Scope("prototype") public Repository<String> stringRepo() { return new Repository<String>() { @Override public String toString() { return "Repository<String>"; } }; } @Bean @Scope("prototype") public Repository<Integer> integerRepo() { return new Repository<Integer>() { @Override public String toString() { return "Repository<Integer>"; } }; } @Bean @Scope("prototype") @SuppressWarnings("rawtypes") public Repository genericRepo() { return new Repository<Object>() { @Override public String toString() { return "Repository<Object>"; } }; } } @Retention(RetentionPolicy.RUNTIME) @Scope(scopeName = "prototype") public @interface PrototypeScoped { ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS; } @Configuration public static class ScopedProxyRepositoryConfiguration { @Bean @Scope(scopeName = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) public Repository<String> stringRepo() { return new Repository<String>() { @Override public String toString() { return "Repository<String>"; } }; } @Bean @PrototypeScoped public Repository<Integer> integerRepo() { return new Repository<Integer>() { @Override public String toString() { return "Repository<Integer>"; } }; } } public static class SpecificRepositoryInjectionBean { @Autowired public GenericRepository<?> genericRepository; } @Configuration public static class SpecificRepositoryConfiguration { @Bean public Repository<Object> genericRepo() { return new GenericRepository<>(); } } public static class RepositoryFactoryBeanInjectionBean { @Autowired public RepositoryFactoryBean<?> repositoryFactoryBean; @Autowired @Qualifier("repoFactoryBean") public RepositoryFactoryBean<?> qualifiedRepositoryFactoryBean; @Autowired @Qualifier("&repoFactoryBean") public RepositoryFactoryBean<?> prefixQualifiedRepositoryFactoryBean; } @Configuration public static class RepositoryFactoryBeanConfiguration { @Bean public RepositoryFactoryBean<Object> repoFactoryBean() { return new RepositoryFactoryBean<>(); } } @Configuration public static class RawMatchingConfiguration { @Bean @SuppressWarnings("rawtypes") public Repository rawRepo() { return new Repository(); } @Bean public Object repoConsumer(Repository<String> repo) { return repo; } } @Configuration public static class WildcardMatchingConfiguration { @Bean @SuppressWarnings("rawtypes") public Repository<?> genericRepo() { return new Repository(); } @Bean public Object repoConsumer(Repository<String> repo) { return repo; } } @Configuration public static class WildcardWithExtendsConfiguration { @Bean public Repository<? extends String> stringRepo() { return new Repository<>(); } @Bean public Repository<? extends Number> numberRepo() { return new Repository<>(); } @Bean public Object repoConsumer(Repository<? extends String> repo) { return repo; } } @Configuration public static class WildcardWithGenericExtendsConfiguration { @Bean public Repository<? extends Object> genericRepo() { return new Repository<String>(); } @Bean public Repository<? extends Number> numberRepo() { return new Repository<>(); } @Bean public Object repoConsumer(Repository<String> repo) { return repo; } } @Configuration @ComponentScan(basePackages = "org.springframework.context.annotation.componentscan.simple") @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ComposedConfiguration { } @ComposedConfiguration public static class ComposedConfigurationClass { } @Configuration @ComponentScan @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ComposedConfigurationWithAttributeOverrides { String[] basePackages() default {}; ComponentScan.Filter[] excludeFilters() default {}; } @ComposedConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple") public static class ComposedConfigurationWithAttributeOverrideForBasePackage { } @ComposedConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple", excludeFilters = @ComponentScan.Filter(Component.class)) public static class ComposedConfigurationWithAttributeOverrideForExcludeFilter { } @ComposedConfigurationWithAttributeOverrides @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ComposedComposedConfigurationWithAttributeOverrides { String[] basePackages() default {}; } @ComposedComposedConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple") public static class ComposedComposedConfigurationWithAttributeOverridesClass { } @ComponentScan @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MetaComponentScan { } @MetaComponentScan @Configuration @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MetaComponentScanConfigurationWithAttributeOverrides { String[] basePackages() default {}; } @MetaComponentScanConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple") public static class MetaComponentScanConfigurationWithAttributeOverridesClass { } @Configuration public static class SubMetaComponentScanConfigurationWithAttributeOverridesClass extends MetaComponentScanConfigurationWithAttributeOverridesClass { } public static class ServiceBean { private final String parameter; public ServiceBean(String parameter) { this.parameter = parameter; } public String getParameter() { return parameter; } } @Configuration public static abstract class AbstractConfig { @Bean public ServiceBean serviceBean() { return provider().getServiceBean(); } @Bean public ServiceBeanProvider provider() { return new ServiceBeanProvider(); } } @Configuration public static class ConcreteConfig extends AbstractConfig { @Autowired private ServiceBeanProvider provider; @Bean @Override public ServiceBeanProvider provider() { return provider; } @PostConstruct public void validate() { Assert.notNull(provider, "No ServiceBeanProvider injected"); } } public interface BaseInterface { ServiceBean serviceBean(); } public interface BaseDefaultMethods extends BaseInterface { @Bean default ServiceBeanProvider provider() { return new ServiceBeanProvider(); } @Bean @Override default ServiceBean serviceBean() { return provider().getServiceBean(); } } public interface DefaultMethodsConfig extends BaseDefaultMethods { } @Configuration public static class ConcreteConfigWithDefaultMethods implements DefaultMethodsConfig { @Autowired private ServiceBeanProvider provider; @Bean @Override public ServiceBeanProvider provider() { return provider; } @PostConstruct public void validate() { Assert.notNull(provider, "No ServiceBeanProvider injected"); } } @Primary public static class ServiceBeanProvider { public ServiceBean getServiceBean() { return new ServiceBean("message"); } } @Configuration public static class A { @Autowired(required = true) Z z; @Bean public B b() { if (z == null) { throw new NullPointerException("z is null"); } return new B(z); } } @Configuration public static class AStrich { @Autowired B b; @Bean public Z z() { return new Z(); } } public static class B { public B(Z z) { } } public static class Z { } @Configuration static class BeanArgumentConfigWithPrototype { @Bean @Scope("prototype") public DependingFoo foo(BarArgument bar) { return new DependingFoo(bar); } @Bean public FooFactory fooFactory() { return new FooFactory() { @Override public DependingFoo createFoo(BarArgument bar) { return foo(bar); } }; } } @Configuration static class BeanArgumentConfigWithSingleton { @Bean @Lazy public DependingFoo foo(BarArgument bar) { return new DependingFoo(bar); } @Bean public FooFactory fooFactory() { return new FooFactory() { @Override public DependingFoo createFoo(BarArgument bar) { return foo(bar); } }; } } @Configuration static class BeanArgumentConfigWithNull { @Bean public DependingFoo aFoo() { return foo(null); } @Bean @Lazy public DependingFoo foo(BarArgument bar) { return new DependingFoo(bar); } @Bean public BarArgument bar() { return new BarArgument(); } } static class BarArgument { } static class DependingFoo { DependingFoo(BarArgument bar) { Assert.notNull(bar, "No BarArgument injected"); } } static abstract class FooFactory { abstract DependingFoo createFoo(BarArgument bar); } interface BarInterface { } static class BarImpl implements BarInterface { } static class FooImpl { @Autowired public BarImpl bar; } @Configuration static class FooBarConfiguration { @Bean public BarInterface bar() { return new BarImpl(); } @Bean public FooImpl foo() { return new FooImpl(); } } @Configuration static class CollectionInjectionConfiguration { @Autowired(required = false) public List<TestBean> testBeans; @Bean public TestBean thing() { return new TestBean(); } } @Configuration static abstract class BeanLookupConfiguration { @Bean public TestBean thing() { return new TestBean(); } @Lookup public abstract TestBean getTestBean(); } }