diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java index 340348cc01..4d0d946496 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java @@ -189,6 +189,15 @@ public @interface ComponentScan { * *
When multiple classes are specified, OR logic is applied * — for example, "include types annotated with {@code @Foo} OR {@code @Bar}". + *
Custom {@link TypeFilter TypeFilters} may optionally implement any of the + * following {@link org.springframework.beans.factory.Aware Aware} interfaces, and + * their respective methods will be called prior to {@link TypeFilter#match match}: + *
Specifying zero classes is permitted but will have no effect on component * scanning. * @since 4.2 diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java index 1119012b5b..1827731271 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * 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. @@ -25,10 +25,17 @@ import java.util.Set; import java.util.regex.Pattern; import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.Aware; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.ResourceLoaderAware; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; @@ -156,7 +163,9 @@ class ComponentScanAnnotationParser { case CUSTOM: Assert.isAssignable(TypeFilter.class, filterClass, "An error occured while processing a @ComponentScan CUSTOM type filter: "); - typeFilters.add(BeanUtils.instantiateClass(filterClass, TypeFilter.class)); + TypeFilter filter = BeanUtils.instantiateClass(filterClass, TypeFilter.class); + invokeAwareMethods(filter); + typeFilters.add(filter); break; default: throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType); @@ -179,4 +188,27 @@ class ComponentScanAnnotationParser { return typeFilters; } + /** + * Invoke {@link ResourceLoaderAware}, {@link BeanClassLoaderAware} and + * {@link BeanFactoryAware} contracts if implemented by the given {@code filter}. + */ + private void invokeAwareMethods(TypeFilter filter) { + if (filter instanceof Aware) { + if (filter instanceof EnvironmentAware) { + ((EnvironmentAware) filter).setEnvironment(this.environment); + } + if (filter instanceof ResourceLoaderAware) { + ((ResourceLoaderAware) filter).setResourceLoader(this.resourceLoader); + } + if (filter instanceof BeanClassLoaderAware) { + ClassLoader classLoader = (this.registry instanceof ConfigurableBeanFactory ? + ((ConfigurableBeanFactory) this.registry).getBeanClassLoader() : + this.resourceLoader.getClassLoader()); + ((BeanClassLoaderAware) filter).setBeanClassLoader(classLoader); + } + if (filter instanceof BeanFactoryAware && this.registry instanceof BeanFactory) { + ((BeanFactoryAware) filter).setBeanFactory((BeanFactory) this.registry); + } + } + } } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java index 1a5983b10d..985401a7a5 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * 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. @@ -36,15 +36,28 @@ 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.config.ConfigurableBeanFactory; 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; @@ -177,6 +190,12 @@ public class ComponentScanAnnotationIntegrationTests { 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(); @@ -258,6 +277,47 @@ public class ComponentScanAnnotationIntegrationTests { 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; + } + + } + + } @@ -340,6 +400,14 @@ class ComponentScanWithCustomTypeFilter { } } +@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, @@ -384,3 +452,5 @@ class ComponentScanWithMultipleAnnotationIncludeFilters2 {} basePackages = "example.scannable", basePackageClasses = example.scannable._package.class) class ComponentScanWithBasePackagesAndValueAlias {} + +