/* * Copyright 2002-2016 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.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.HashSet; import example.scannable.CustomComponent; import example.scannable.CustomStereotype; import example.scannable.DefaultNamedComponent; import example.scannable.FooService; import example.scannable.MessageBean; import example.scannable.ScopedProxyTestBean; import example.scannable_implicitbasepackage.ComponentScanAnnotatedConfigWithImplicitBasePackage; import example.scannable_implicitbasepackage.ConfigurableComponent; import example.scannable_scoped.CustomScopeAnnotationBean; import example.scannable_scoped.MyScope; import org.junit.Test; import org.springframework.aop.support.AopUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.annotation.CustomAutowireConfigurer; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScanParserTests.KustomAnnotationAutowiredBean; import org.springframework.context.annotation.componentscan.simple.ClassWithNestedComponents; import org.springframework.context.annotation.componentscan.simple.SimpleComponent; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; import org.springframework.tests.context.SimpleMapScope; import org.springframework.util.SerializationTestUtils; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*; /** * Integration tests for processing ComponentScan-annotated Configuration classes. * * @author Chris Beams * @author Juergen Hoeller * @author Sam Brannen * @since 3.1 */ @SuppressWarnings("resource") public class ComponentScanAnnotationIntegrationTests { @Test public void controlScan() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan(example.scannable._package.class.getPackage().getName()); ctx.refresh(); assertThat("control scan for example.scannable package failed to register FooServiceImpl bean", ctx.containsBean("fooServiceImpl"), is(true)); } @Test public void viaContextRegistration() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanAnnotatedConfig.class); ctx.refresh(); ctx.getBean(ComponentScanAnnotatedConfig.class); ctx.getBean(TestBean.class); assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig"), is(true)); assertThat("@ComponentScan annotated @Configuration class registered directly against " + "AnnotationConfigApplicationContext did not trigger component scanning as expected", ctx.containsBean("fooServiceImpl"), is(true)); } @Test public void viaContextRegistration_WithValueAttribute() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanAnnotatedConfig_WithValueAttribute.class); ctx.refresh(); ctx.getBean(ComponentScanAnnotatedConfig_WithValueAttribute.class); ctx.getBean(TestBean.class); assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig_WithValueAttribute"), is(true)); assertThat("@ComponentScan annotated @Configuration class registered directly against " + "AnnotationConfigApplicationContext did not trigger component scanning as expected", ctx.containsBean("fooServiceImpl"), is(true)); } @Test public void viaContextRegistration_FromPackageOfConfigClass() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanAnnotatedConfigWithImplicitBasePackage.class); ctx.refresh(); ctx.getBean(ComponentScanAnnotatedConfigWithImplicitBasePackage.class); assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfigWithImplicitBasePackage"), is(true)); assertThat("@ComponentScan annotated @Configuration class registered directly against " + "AnnotationConfigApplicationContext did not trigger component scanning as expected", ctx.containsBean("scannedComponent"), is(true)); assertThat("@Bean method overrides scanned class", ctx.getBean(ConfigurableComponent.class).isFlag(), is(true)); } @Test public void viaContextRegistration_WithComposedAnnotation() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComposedAnnotationConfig.class); ctx.refresh(); ctx.getBean(ComposedAnnotationConfig.class); ctx.getBean(SimpleComponent.class); ctx.getBean(ClassWithNestedComponents.NestedComponent.class); ctx.getBean(ClassWithNestedComponents.OtherNestedComponent.class); assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotationIntegrationTests.ComposedAnnotationConfig"), is(true)); assertThat("@ComponentScan annotated @Configuration class registered directly against " + "AnnotationConfigApplicationContext did not trigger component scanning as expected", ctx.containsBean("simpleComponent"), is(true)); } @Test public void viaBeanRegistration() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.registerBeanDefinition("componentScanAnnotatedConfig", genericBeanDefinition(ComponentScanAnnotatedConfig.class).getBeanDefinition()); bf.registerBeanDefinition("configurationClassPostProcessor", genericBeanDefinition(ConfigurationClassPostProcessor.class).getBeanDefinition()); GenericApplicationContext ctx = new GenericApplicationContext(bf); ctx.refresh(); ctx.getBean(ComponentScanAnnotatedConfig.class); ctx.getBean(TestBean.class); assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig"), is(true)); assertThat("@ComponentScan annotated @Configuration class registered " + "as bean definition did not trigger component scanning as expected", ctx.containsBean("fooServiceImpl"), is(true)); } @Test public void withCustomBeanNameGenerator() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanWithBeanNameGenerator.class); ctx.refresh(); assertThat(ctx.containsBean("custom_fooServiceImpl"), is(true)); assertThat(ctx.containsBean("fooServiceImpl"), is(false)); } @Test public void withScopeResolver() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithScopeResolver.class); // custom scope annotation makes the bean prototype scoped. subsequent calls // to getBean should return distinct instances. assertThat(ctx.getBean(CustomScopeAnnotationBean.class), not(sameInstance(ctx.getBean(CustomScopeAnnotationBean.class)))); assertThat(ctx.containsBean("scannedComponent"), is(false)); } @Test public void multiComponentScan() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MultiComponentScan.class); assertThat(ctx.getBean(CustomScopeAnnotationBean.class), not(sameInstance(ctx.getBean(CustomScopeAnnotationBean.class)))); assertThat(ctx.containsBean("scannedComponent"), is(true)); } @Test public void withCustomTypeFilter() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithCustomTypeFilter.class); assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("componentScanParserTests.KustomAnnotationAutowiredBean")); KustomAnnotationAutowiredBean testBean = ctx.getBean("componentScanParserTests.KustomAnnotationAutowiredBean", KustomAnnotationAutowiredBean.class); assertThat(testBean.getDependency(), notNullValue()); } @Test public void withAwareTypeFilter() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithAwareTypeFilter.class); assertTrue(ctx.getEnvironment().acceptsProfiles("the-filter-ran")); } @Test public void withScopedProxy() throws IOException, ClassNotFoundException { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanWithScopedProxy.class); ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope()); ctx.refresh(); // should cast to the interface FooService bean = (FooService) ctx.getBean("scopedProxyTestBean"); // should be dynamic proxy assertThat(AopUtils.isJdkDynamicProxy(bean), is(true)); // test serializability assertThat(bean.foo(1), equalTo("bar")); FooService deserialized = (FooService) SerializationTestUtils.serializeAndDeserialize(bean); assertThat(deserialized, notNullValue()); assertThat(deserialized.foo(1), equalTo("bar")); } @Test public void withScopedProxyThroughRegex() throws IOException, ClassNotFoundException { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanWithScopedProxyThroughRegex.class); ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope()); ctx.refresh(); // should cast to the interface FooService bean = (FooService) ctx.getBean("scopedProxyTestBean"); // should be dynamic proxy assertThat(AopUtils.isJdkDynamicProxy(bean), is(true)); } @Test public void withScopedProxyThroughAspectJPattern() throws IOException, ClassNotFoundException { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanWithScopedProxyThroughAspectJPattern.class); ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope()); ctx.refresh(); // should cast to the interface FooService bean = (FooService) ctx.getBean("scopedProxyTestBean"); // should be dynamic proxy assertThat(AopUtils.isJdkDynamicProxy(bean), is(true)); } @Test public void withMultipleAnnotationIncludeFilters1() throws IOException, ClassNotFoundException { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanWithMultipleAnnotationIncludeFilters1.class); ctx.refresh(); ctx.getBean(DefaultNamedComponent.class); // @CustomStereotype-annotated ctx.getBean(MessageBean.class); // @CustomComponent-annotated } @Test public void withMultipleAnnotationIncludeFilters2() throws IOException, ClassNotFoundException { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanWithMultipleAnnotationIncludeFilters2.class); ctx.refresh(); ctx.getBean(DefaultNamedComponent.class); // @CustomStereotype-annotated ctx.getBean(MessageBean.class); // @CustomComponent-annotated } @Test public void withBasePackagesAndValueAlias() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanWithBasePackagesAndValueAlias.class); ctx.refresh(); assertThat(ctx.containsBean("fooServiceImpl"), is(true)); } @Configuration @ComponentScan @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ComposedConfiguration { String[] basePackages() default {}; } @ComposedConfiguration(basePackages = "org.springframework.context.annotation.componentscan.simple") public static class ComposedAnnotationConfig { } public static class AwareTypeFilter implements TypeFilter, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware, BeanFactoryAware { private BeanFactory beanFactory; private ClassLoader classLoader; private ResourceLoader resourceLoader; private Environment environment; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) { ((ConfigurableEnvironment) this.environment).addActiveProfile("the-filter-ran"); assertNotNull(this.beanFactory); assertNotNull(this.classLoader); assertNotNull(this.resourceLoader); assertNotNull(this.environment); return false; } } } @Configuration @ComponentScan(basePackageClasses = example.scannable._package.class) class ComponentScanAnnotatedConfig { @Bean public TestBean testBean() { return new TestBean(); } } @Configuration @ComponentScan("example.scannable") class ComponentScanAnnotatedConfig_WithValueAttribute { @Bean public TestBean testBean() { return new TestBean(); } } @Configuration @ComponentScan class ComponentScanWithNoPackagesConfig { } @Configuration @ComponentScan(basePackages = "example.scannable", nameGenerator = MyBeanNameGenerator.class) class ComponentScanWithBeanNameGenerator { } class MyBeanNameGenerator extends AnnotationBeanNameGenerator { @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { return "custom_" + super.generateBeanName(definition, registry); } } @Configuration @ComponentScan(basePackages = "example.scannable_scoped", scopeResolver = MyScopeMetadataResolver.class) class ComponentScanWithScopeResolver { } @Configuration @ComponentScan(basePackages = "example.scannable_scoped", scopeResolver = MyScopeMetadataResolver.class) @ComponentScan(basePackages = "example.scannable_implicitbasepackage") class MultiComponentScan { } class MyScopeMetadataResolver extends AnnotationScopeMetadataResolver { MyScopeMetadataResolver() { this.scopeAnnotationType = MyScope.class; } } @Configuration @ComponentScan( basePackages = "org.springframework.context.annotation", useDefaultFilters = false, includeFilters = @Filter(type = FilterType.CUSTOM, classes = ComponentScanParserTests.CustomTypeFilter.class), // exclude this class from scanning since it's in the scanned package excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ComponentScanWithCustomTypeFilter.class), lazyInit = true) class ComponentScanWithCustomTypeFilter { @Bean @SuppressWarnings({ "rawtypes", "serial", "unchecked" }) public static CustomAutowireConfigurer customAutowireConfigurer() { CustomAutowireConfigurer cac = new CustomAutowireConfigurer(); cac.setCustomQualifierTypes(new HashSet() {{ add(ComponentScanParserTests.CustomAnnotation.class); }}); return cac; } public ComponentScanParserTests.KustomAnnotationAutowiredBean testBean() { return new ComponentScanParserTests.KustomAnnotationAutowiredBean(); } } @Configuration @ComponentScan( basePackages = "org.springframework.context.annotation", useDefaultFilters = false, includeFilters = @Filter(type = FilterType.CUSTOM, classes = ComponentScanAnnotationIntegrationTests.AwareTypeFilter.class), lazyInit = true) class ComponentScanWithAwareTypeFilter {} @Configuration @ComponentScan(basePackages = "example.scannable", scopedProxy = ScopedProxyMode.INTERFACES, useDefaultFilters = false, includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ScopedProxyTestBean.class)) class ComponentScanWithScopedProxy {} @Configuration @ComponentScan(basePackages = "example.scannable", scopedProxy = ScopedProxyMode.INTERFACES, useDefaultFilters = false, includeFilters = @Filter(type=FilterType.REGEX, pattern = "((?:[a-z.]+))ScopedProxyTestBean")) class ComponentScanWithScopedProxyThroughRegex {} @Configuration @ComponentScan(basePackages = "example.scannable", scopedProxy = ScopedProxyMode.INTERFACES, useDefaultFilters = false, includeFilters = @Filter(type=FilterType.ASPECTJ, pattern = "*..ScopedProxyTestBean")) class ComponentScanWithScopedProxyThroughAspectJPattern {} @Configuration @ComponentScan(basePackages = "example.scannable", useDefaultFilters = false, includeFilters = { @Filter(CustomStereotype.class), @Filter(CustomComponent.class) } ) class ComponentScanWithMultipleAnnotationIncludeFilters1 {} @Configuration @ComponentScan(basePackages = "example.scannable", useDefaultFilters = false, includeFilters = @Filter({CustomStereotype.class, CustomComponent.class}) ) class ComponentScanWithMultipleAnnotationIncludeFilters2 {} @Configuration @ComponentScan( value = "example.scannable", basePackages = "example.scannable", basePackageClasses = example.scannable._package.class) class ComponentScanWithBasePackagesAndValueAlias {}