ComponentScanBeanDefinitionParser supports placeholders for entire base-package specification and for type filter expressions
Issue: SPR-10424 Issue: SPR-10425
This commit is contained in:
parent
5ecdd8ca31
commit
e52f041a78
|
@ -77,7 +77,9 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
|
|||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
|
||||
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
|
||||
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
|
||||
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
|
||||
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||
|
||||
// Actually scan for bean definitions and register them.
|
||||
|
@ -89,17 +91,15 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
|
|||
}
|
||||
|
||||
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
|
||||
XmlReaderContext readerContext = parserContext.getReaderContext();
|
||||
|
||||
boolean useDefaultFilters = true;
|
||||
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
|
||||
useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
|
||||
}
|
||||
|
||||
// Delegate bean definition registration to scanner class.
|
||||
ClassPathBeanDefinitionScanner scanner = createScanner(readerContext, useDefaultFilters);
|
||||
scanner.setResourceLoader(readerContext.getResourceLoader());
|
||||
scanner.setEnvironment(parserContext.getDelegate().getEnvironment());
|
||||
ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
|
||||
scanner.setResourceLoader(parserContext.getReaderContext().getResourceLoader());
|
||||
scanner.setEnvironment(parserContext.getReaderContext().getEnvironment());
|
||||
scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
|
||||
scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
|
||||
|
||||
|
@ -111,17 +111,17 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
|
|||
parseBeanNameGenerator(element, scanner);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
|
||||
parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
|
||||
}
|
||||
|
||||
try {
|
||||
parseScope(element, scanner);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
|
||||
parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
|
||||
}
|
||||
|
||||
parseTypeFilters(element, scanner, readerContext, parserContext);
|
||||
parseTypeFilters(element, scanner, parserContext);
|
||||
|
||||
return scanner;
|
||||
}
|
||||
|
@ -195,9 +195,7 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
|
|||
}
|
||||
}
|
||||
|
||||
protected void parseTypeFilters(
|
||||
Element element, ClassPathBeanDefinitionScanner scanner, XmlReaderContext readerContext, ParserContext parserContext) {
|
||||
|
||||
protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {
|
||||
// Parse exclude and include filter elements.
|
||||
ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
|
||||
NodeList nodeList = element.getChildNodes();
|
||||
|
@ -207,25 +205,27 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
|
|||
String localName = parserContext.getDelegate().getLocalName(node);
|
||||
try {
|
||||
if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
|
||||
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
|
||||
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
|
||||
scanner.addIncludeFilter(typeFilter);
|
||||
}
|
||||
else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
|
||||
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
|
||||
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
|
||||
scanner.addExcludeFilter(typeFilter);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
|
||||
parserContext.getReaderContext().error(
|
||||
ex.getMessage(), parserContext.extractSource(element), ex.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader) {
|
||||
protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader, ParserContext parserContext) {
|
||||
String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
|
||||
String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
|
||||
expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression);
|
||||
try {
|
||||
if ("annotation".equals(filterType)) {
|
||||
return new AnnotationTypeFilter((Class<Annotation>) classLoader.loadClass(expression));
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.springframework.context.support.GenericXmlApplicationContext;
|
|||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
@ -49,8 +48,9 @@ public class ComponentScanParserTests {
|
|||
return new ClassPathXmlApplicationContext(path, getClass());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void aspectJTypeFilter() {
|
||||
public void aspectjTypeFilter() {
|
||||
ClassPathXmlApplicationContext context = loadContext("aspectjTypeFilterTests.xml");
|
||||
assertTrue(context.containsBean("fooServiceImpl"));
|
||||
assertTrue(context.containsBean("stubFooDao"));
|
||||
|
@ -58,6 +58,25 @@ public class ComponentScanParserTests {
|
|||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aspectjTypeFilterWithPlaceholders() {
|
||||
System.setProperty("basePackage", "example.scannable, test");
|
||||
System.setProperty("scanInclude", "example.scannable.FooService+");
|
||||
System.setProperty("scanExclude", "example..Scoped*Test*");
|
||||
try {
|
||||
ClassPathXmlApplicationContext context = loadContext("aspectjTypeFilterTestsWithPlaceholders.xml");
|
||||
assertTrue(context.containsBean("fooServiceImpl"));
|
||||
assertTrue(context.containsBean("stubFooDao"));
|
||||
assertFalse(context.containsBean("scopedProxyTestBean"));
|
||||
context.close();
|
||||
}
|
||||
finally {
|
||||
System.clearProperty("basePackage");
|
||||
System.clearProperty("scanInclude");
|
||||
System.clearProperty("scanExclude");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonMatchingResourcePattern() {
|
||||
ClassPathXmlApplicationContext context = loadContext("nonMatchingResourcePatternTests.xml");
|
||||
|
@ -131,6 +150,7 @@ public class ComponentScanParserTests {
|
|||
public static @interface CustomAnnotation {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Intentionally spelling "custom" with a "k" since there are numerous
|
||||
* classes in this package named *Custom*.
|
||||
|
@ -146,6 +166,7 @@ public class ComponentScanParserTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Intentionally spelling "custom" with a "k" since there are numerous
|
||||
* classes in this package named *Custom*.
|
||||
|
@ -154,6 +175,7 @@ public class ComponentScanParserTests {
|
|||
public static class KustomAnnotationDependencyBean {
|
||||
}
|
||||
|
||||
|
||||
public static class CustomTypeFilter implements TypeFilter {
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<context:component-scan base-package="example.scannable" use-default-filters="false" annotation-config="false">
|
||||
<context:include-filter type="aspectj" expression="example.scannable.Stub*"/>
|
||||
<context:include-filter type="aspectj" expression="example.scannable.FooService+"/>
|
||||
<context:exclude-filter type="aspectj" expression="example..Scoped*Test*" />
|
||||
<context:exclude-filter type="aspectj" expression="example..Scoped*Test*"/>
|
||||
</context:component-scan>
|
||||
|
||||
</beans>
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
|
||||
|
||||
<context:component-scan base-package="${basePackage}" use-default-filters="false" annotation-config="false">
|
||||
<context:include-filter type="aspectj" expression="example.scannable.Stub*"/>
|
||||
<context:include-filter type="aspectj" expression="${scanInclude}"/>
|
||||
<context:exclude-filter type="aspectj" expression="${scanExclude}"/>
|
||||
</context:component-scan>
|
||||
|
||||
</beans>
|
Loading…
Reference in New Issue