Skip to content

Commit 094cf6c

Browse files
committed
Support *Aware for @componentscan custom filters
Support a limited set of *Aware interfaces for TypeFilters created via the @componentscan annotation. Issue: SPR-14009
1 parent beb01e6 commit 094cf6c

File tree

3 files changed

+114
-3
lines changed

3 files changed

+114
-3
lines changed

spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java

+9
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,15 @@
189189
* </table>
190190
* <p>When multiple classes are specified, <em>OR</em> logic is applied
191191
* &mdash; for example, "include types annotated with {@code @Foo} OR {@code @Bar}".
192+
* <p>Custom {@link TypeFilter TypeFilters} may optionally implement any of the
193+
* following {@link org.springframework.beans.factory.Aware Aware} interfaces, and
194+
* their respective methods will be called prior to {@link TypeFilter#match match}:
195+
* <ul>
196+
* <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
197+
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
198+
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
199+
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
200+
* </ul>
192201
* <p>Specifying zero classes is permitted but will have no effect on component
193202
* scanning.
194203
* @since 4.2

spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java

+34-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,10 +25,17 @@
2525
import java.util.regex.Pattern;
2626

2727
import org.springframework.beans.BeanUtils;
28+
import org.springframework.beans.factory.Aware;
29+
import org.springframework.beans.factory.BeanClassLoaderAware;
30+
import org.springframework.beans.factory.BeanFactory;
31+
import org.springframework.beans.factory.BeanFactoryAware;
2832
import org.springframework.beans.factory.config.BeanDefinitionHolder;
33+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
2934
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
3035
import org.springframework.beans.factory.support.BeanNameGenerator;
3136
import org.springframework.context.ConfigurableApplicationContext;
37+
import org.springframework.context.EnvironmentAware;
38+
import org.springframework.context.ResourceLoaderAware;
3239
import org.springframework.core.annotation.AnnotationAttributes;
3340
import org.springframework.core.env.Environment;
3441
import org.springframework.core.io.ResourceLoader;
@@ -156,7 +163,9 @@ private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
156163
case CUSTOM:
157164
Assert.isAssignable(TypeFilter.class, filterClass,
158165
"An error occured while processing a @ComponentScan CUSTOM type filter: ");
159-
typeFilters.add(BeanUtils.instantiateClass(filterClass, TypeFilter.class));
166+
TypeFilter filter = BeanUtils.instantiateClass(filterClass, TypeFilter.class);
167+
invokeAwareMethods(filter);
168+
typeFilters.add(filter);
160169
break;
161170
default:
162171
throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
@@ -179,4 +188,27 @@ private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
179188
return typeFilters;
180189
}
181190

191+
/**
192+
* Invoke {@link ResourceLoaderAware}, {@link BeanClassLoaderAware} and
193+
* {@link BeanFactoryAware} contracts if implemented by the given {@code filter}.
194+
*/
195+
private void invokeAwareMethods(TypeFilter filter) {
196+
if (filter instanceof Aware) {
197+
if (filter instanceof EnvironmentAware) {
198+
((EnvironmentAware) filter).setEnvironment(this.environment);
199+
}
200+
if (filter instanceof ResourceLoaderAware) {
201+
((ResourceLoaderAware) filter).setResourceLoader(this.resourceLoader);
202+
}
203+
if (filter instanceof BeanClassLoaderAware) {
204+
ClassLoader classLoader = (this.registry instanceof ConfigurableBeanFactory ?
205+
((ConfigurableBeanFactory) this.registry).getBeanClassLoader() :
206+
this.resourceLoader.getClassLoader());
207+
((BeanClassLoaderAware) filter).setBeanClassLoader(classLoader);
208+
}
209+
if (filter instanceof BeanFactoryAware && this.registry instanceof BeanFactory) {
210+
((BeanFactoryAware) filter).setBeanFactory((BeanFactory) this.registry);
211+
}
212+
}
213+
}
182214
}

spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java

+71-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,15 +36,28 @@
3636
import org.junit.Test;
3737

3838
import org.springframework.aop.support.AopUtils;
39+
import org.springframework.beans.BeansException;
40+
import org.springframework.beans.factory.BeanClassLoaderAware;
41+
import org.springframework.beans.factory.BeanFactory;
42+
import org.springframework.beans.factory.BeanFactoryAware;
3943
import org.springframework.beans.factory.annotation.CustomAutowireConfigurer;
4044
import org.springframework.beans.factory.config.BeanDefinition;
45+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
4146
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
4247
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
48+
import org.springframework.context.EnvironmentAware;
49+
import org.springframework.context.ResourceLoaderAware;
4350
import org.springframework.context.annotation.ComponentScan.Filter;
4451
import org.springframework.context.annotation.ComponentScanParserTests.KustomAnnotationAutowiredBean;
4552
import org.springframework.context.annotation.componentscan.simple.ClassWithNestedComponents;
4653
import org.springframework.context.annotation.componentscan.simple.SimpleComponent;
4754
import org.springframework.context.support.GenericApplicationContext;
55+
import org.springframework.core.env.ConfigurableEnvironment;
56+
import org.springframework.core.env.Environment;
57+
import org.springframework.core.io.ResourceLoader;
58+
import org.springframework.core.type.classreading.MetadataReader;
59+
import org.springframework.core.type.classreading.MetadataReaderFactory;
60+
import org.springframework.core.type.filter.TypeFilter;
4861
import org.springframework.tests.context.SimpleMapScope;
4962
import org.springframework.util.SerializationTestUtils;
5063

@@ -177,6 +190,12 @@ public void withCustomTypeFilter() {
177190
assertThat(testBean.getDependency(), notNullValue());
178191
}
179192

193+
@Test
194+
public void withAwareTypeFilter() {
195+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithAwareTypeFilter.class);
196+
assertTrue(ctx.getEnvironment().acceptsProfiles("the-filter-ran"));
197+
}
198+
180199
@Test
181200
public void withScopedProxy() throws IOException, ClassNotFoundException {
182201
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@@ -258,6 +277,47 @@ public void withBasePackagesAndValueAlias() {
258277
public static class ComposedAnnotationConfig {
259278
}
260279

280+
public static class AwareTypeFilter implements TypeFilter, EnvironmentAware,
281+
ResourceLoaderAware, BeanClassLoaderAware, BeanFactoryAware {
282+
283+
private BeanFactory beanFactory;
284+
private ClassLoader classLoader;
285+
private ResourceLoader resourceLoader;
286+
private Environment environment;
287+
288+
@Override
289+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
290+
this.beanFactory = beanFactory;
291+
}
292+
293+
@Override
294+
public void setBeanClassLoader(ClassLoader classLoader) {
295+
this.classLoader = classLoader;
296+
}
297+
298+
@Override
299+
public void setResourceLoader(ResourceLoader resourceLoader) {
300+
this.resourceLoader = resourceLoader;
301+
}
302+
303+
@Override
304+
public void setEnvironment(Environment environment) {
305+
this.environment = environment;
306+
}
307+
308+
@Override
309+
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
310+
((ConfigurableEnvironment) this.environment).addActiveProfile("the-filter-ran");
311+
assertNotNull(this.beanFactory);
312+
assertNotNull(this.classLoader);
313+
assertNotNull(this.resourceLoader);
314+
assertNotNull(this.environment);
315+
return false;
316+
}
317+
318+
}
319+
320+
261321
}
262322

263323

@@ -340,6 +400,14 @@ public ComponentScanParserTests.KustomAnnotationAutowiredBean testBean() {
340400
}
341401
}
342402

403+
@Configuration
404+
@ComponentScan(
405+
basePackages = "org.springframework.context.annotation",
406+
useDefaultFilters = false,
407+
includeFilters = @Filter(type = FilterType.CUSTOM, classes = ComponentScanAnnotationIntegrationTests.AwareTypeFilter.class),
408+
lazyInit = true)
409+
class ComponentScanWithAwareTypeFilter {}
410+
343411
@Configuration
344412
@ComponentScan(basePackages = "example.scannable",
345413
scopedProxy = ScopedProxyMode.INTERFACES,
@@ -384,3 +452,5 @@ class ComponentScanWithMultipleAnnotationIncludeFilters2 {}
384452
basePackages = "example.scannable",
385453
basePackageClasses = example.scannable._package.class)
386454
class ComponentScanWithBasePackagesAndValueAlias {}
455+
456+

0 commit comments

Comments
 (0)