Browse Source

Support *Aware for @ComponentScan custom filters

Support a limited set of *Aware interfaces for TypeFilters created
via the @ComponentScan annotation.

Issue: SPR-14009
pull/1002/head
Phillip Webb 9 years ago
parent
commit
094cf6cafb
  1. 9
      spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java
  2. 36
      spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java
  3. 72
      spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java

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

@ -189,6 +189,15 @@ public @interface ComponentScan {
* </table> * </table>
* <p>When multiple classes are specified, <em>OR</em> logic is applied * <p>When multiple classes are specified, <em>OR</em> logic is applied
* &mdash; for example, "include types annotated with {@code @Foo} OR {@code @Bar}". * &mdash; for example, "include types annotated with {@code @Foo} OR {@code @Bar}".
* <p>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}:
* <ul>
* <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
* </ul>
* <p>Specifying zero classes is permitted but will have no effect on component * <p>Specifying zero classes is permitted but will have no effect on component
* scanning. * scanning.
* @since 4.2 * @since 4.2

36
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"); * 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.
@ -25,10 +25,17 @@ import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.springframework.beans.BeanUtils; 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.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ConfigurableApplicationContext; 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.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
@ -156,7 +163,9 @@ class ComponentScanAnnotationParser {
case CUSTOM: case CUSTOM:
Assert.isAssignable(TypeFilter.class, filterClass, Assert.isAssignable(TypeFilter.class, filterClass,
"An error occured while processing a @ComponentScan CUSTOM type filter: "); "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; break;
default: default:
throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType); throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
@ -179,4 +188,27 @@ class ComponentScanAnnotationParser {
return typeFilters; 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);
}
}
}
} }

72
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"); * 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.
@ -36,15 +36,28 @@ import example.scannable_scoped.MyScope;
import org.junit.Test; import org.junit.Test;
import org.springframework.aop.support.AopUtils; 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.annotation.CustomAutowireConfigurer;
import org.springframework.beans.factory.config.BeanDefinition; 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.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; 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.ComponentScan.Filter;
import org.springframework.context.annotation.ComponentScanParserTests.KustomAnnotationAutowiredBean; import org.springframework.context.annotation.ComponentScanParserTests.KustomAnnotationAutowiredBean;
import org.springframework.context.annotation.componentscan.simple.ClassWithNestedComponents; import org.springframework.context.annotation.componentscan.simple.ClassWithNestedComponents;
import org.springframework.context.annotation.componentscan.simple.SimpleComponent; import org.springframework.context.annotation.componentscan.simple.SimpleComponent;
import org.springframework.context.support.GenericApplicationContext; 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.tests.context.SimpleMapScope;
import org.springframework.util.SerializationTestUtils; import org.springframework.util.SerializationTestUtils;
@ -177,6 +190,12 @@ public class ComponentScanAnnotationIntegrationTests {
assertThat(testBean.getDependency(), notNullValue()); assertThat(testBean.getDependency(), notNullValue());
} }
@Test
public void withAwareTypeFilter() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithAwareTypeFilter.class);
assertTrue(ctx.getEnvironment().acceptsProfiles("the-filter-ran"));
}
@Test @Test
public void withScopedProxy() throws IOException, ClassNotFoundException { public void withScopedProxy() throws IOException, ClassNotFoundException {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@ -258,6 +277,47 @@ public class ComponentScanAnnotationIntegrationTests {
public static class ComposedAnnotationConfig { 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 @Configuration
@ComponentScan(basePackages = "example.scannable", @ComponentScan(basePackages = "example.scannable",
scopedProxy = ScopedProxyMode.INTERFACES, scopedProxy = ScopedProxyMode.INTERFACES,
@ -384,3 +452,5 @@ class ComponentScanWithMultipleAnnotationIncludeFilters2 {}
basePackages = "example.scannable", basePackages = "example.scannable",
basePackageClasses = example.scannable._package.class) basePackageClasses = example.scannable._package.class)
class ComponentScanWithBasePackagesAndValueAlias {} class ComponentScanWithBasePackagesAndValueAlias {}

Loading…
Cancel
Save