Skip to content

Commit e52f041

Browse files
committed
ComponentScanBeanDefinitionParser supports placeholders for entire base-package specification and for type filter expressions
Issue: SPR-10424 Issue: SPR-10425
1 parent 5ecdd8c commit e52f041

File tree

4 files changed

+55
-19
lines changed

4 files changed

+55
-19
lines changed

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
7777

7878
@Override
7979
public BeanDefinition parse(Element element, ParserContext parserContext) {
80-
String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
80+
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
81+
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
82+
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
8183
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
8284

8385
// Actually scan for bean definitions and register them.
@@ -89,17 +91,15 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
8991
}
9092

9193
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
92-
XmlReaderContext readerContext = parserContext.getReaderContext();
93-
9494
boolean useDefaultFilters = true;
9595
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
9696
useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
9797
}
9898

9999
// Delegate bean definition registration to scanner class.
100-
ClassPathBeanDefinitionScanner scanner = createScanner(readerContext, useDefaultFilters);
101-
scanner.setResourceLoader(readerContext.getResourceLoader());
102-
scanner.setEnvironment(parserContext.getDelegate().getEnvironment());
100+
ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
101+
scanner.setResourceLoader(parserContext.getReaderContext().getResourceLoader());
102+
scanner.setEnvironment(parserContext.getReaderContext().getEnvironment());
103103
scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
104104
scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
105105

@@ -111,17 +111,17 @@ protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserCo
111111
parseBeanNameGenerator(element, scanner);
112112
}
113113
catch (Exception ex) {
114-
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
114+
parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
115115
}
116116

117117
try {
118118
parseScope(element, scanner);
119119
}
120120
catch (Exception ex) {
121-
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
121+
parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
122122
}
123123

124-
parseTypeFilters(element, scanner, readerContext, parserContext);
124+
parseTypeFilters(element, scanner, parserContext);
125125

126126
return scanner;
127127
}
@@ -195,9 +195,7 @@ else if ("no".equals(mode)) {
195195
}
196196
}
197197

198-
protected void parseTypeFilters(
199-
Element element, ClassPathBeanDefinitionScanner scanner, XmlReaderContext readerContext, ParserContext parserContext) {
200-
198+
protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {
201199
// Parse exclude and include filter elements.
202200
ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
203201
NodeList nodeList = element.getChildNodes();
@@ -207,25 +205,27 @@ protected void parseTypeFilters(
207205
String localName = parserContext.getDelegate().getLocalName(node);
208206
try {
209207
if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
210-
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
208+
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
211209
scanner.addIncludeFilter(typeFilter);
212210
}
213211
else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
214-
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
212+
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
215213
scanner.addExcludeFilter(typeFilter);
216214
}
217215
}
218216
catch (Exception ex) {
219-
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
217+
parserContext.getReaderContext().error(
218+
ex.getMessage(), parserContext.extractSource(element), ex.getCause());
220219
}
221220
}
222221
}
223222
}
224223

225224
@SuppressWarnings("unchecked")
226-
protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader) {
225+
protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader, ParserContext parserContext) {
227226
String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
228227
String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
228+
expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression);
229229
try {
230230
if ("annotation".equals(filterType)) {
231231
return new AnnotationTypeFilter((Class<Annotation>) classLoader.loadClass(expression));

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.core.type.classreading.MetadataReader;
3333
import org.springframework.core.type.classreading.MetadataReaderFactory;
3434
import org.springframework.core.type.filter.TypeFilter;
35-
import org.springframework.stereotype.Component;
3635

3736
import static org.hamcrest.CoreMatchers.*;
3837
import static org.junit.Assert.*;
@@ -49,15 +48,35 @@ private ClassPathXmlApplicationContext loadContext(String path) {
4948
return new ClassPathXmlApplicationContext(path, getClass());
5049
}
5150

51+
5252
@Test
53-
public void aspectJTypeFilter() {
53+
public void aspectjTypeFilter() {
5454
ClassPathXmlApplicationContext context = loadContext("aspectjTypeFilterTests.xml");
5555
assertTrue(context.containsBean("fooServiceImpl"));
5656
assertTrue(context.containsBean("stubFooDao"));
5757
assertFalse(context.containsBean("scopedProxyTestBean"));
5858
context.close();
5959
}
6060

61+
@Test
62+
public void aspectjTypeFilterWithPlaceholders() {
63+
System.setProperty("basePackage", "example.scannable, test");
64+
System.setProperty("scanInclude", "example.scannable.FooService+");
65+
System.setProperty("scanExclude", "example..Scoped*Test*");
66+
try {
67+
ClassPathXmlApplicationContext context = loadContext("aspectjTypeFilterTestsWithPlaceholders.xml");
68+
assertTrue(context.containsBean("fooServiceImpl"));
69+
assertTrue(context.containsBean("stubFooDao"));
70+
assertFalse(context.containsBean("scopedProxyTestBean"));
71+
context.close();
72+
}
73+
finally {
74+
System.clearProperty("basePackage");
75+
System.clearProperty("scanInclude");
76+
System.clearProperty("scanExclude");
77+
}
78+
}
79+
6180
@Test
6281
public void nonMatchingResourcePattern() {
6382
ClassPathXmlApplicationContext context = loadContext("nonMatchingResourcePatternTests.xml");
@@ -131,6 +150,7 @@ public void componentScanRespectsProfileAnnotation() {
131150
public static @interface CustomAnnotation {
132151
}
133152

153+
134154
/**
135155
* Intentionally spelling "custom" with a "k" since there are numerous
136156
* classes in this package named *Custom*.
@@ -146,6 +166,7 @@ public KustomAnnotationDependencyBean getDependency() {
146166
}
147167
}
148168

169+
149170
/**
150171
* Intentionally spelling "custom" with a "k" since there are numerous
151172
* classes in this package named *Custom*.
@@ -154,6 +175,7 @@ public KustomAnnotationDependencyBean getDependency() {
154175
public static class KustomAnnotationDependencyBean {
155176
}
156177

178+
157179
public static class CustomTypeFilter implements TypeFilter {
158180

159181
/**

spring-context/src/test/java/org/springframework/context/annotation/aspectjTypeFilterTests.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<context:component-scan base-package="example.scannable" use-default-filters="false" annotation-config="false">
99
<context:include-filter type="aspectj" expression="example.scannable.Stub*"/>
1010
<context:include-filter type="aspectj" expression="example.scannable.FooService+"/>
11-
<context:exclude-filter type="aspectj" expression="example..Scoped*Test*" />
11+
<context:exclude-filter type="aspectj" expression="example..Scoped*Test*"/>
1212
</context:component-scan>
1313

1414
</beans>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<beans xmlns="http://www.springframework.org/schema/beans"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:context="http://www.springframework.org/schema/context"
5+
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
6+
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
7+
8+
<context:component-scan base-package="${basePackage}" use-default-filters="false" annotation-config="false">
9+
<context:include-filter type="aspectj" expression="example.scannable.Stub*"/>
10+
<context:include-filter type="aspectj" expression="${scanInclude}"/>
11+
<context:exclude-filter type="aspectj" expression="${scanExclude}"/>
12+
</context:component-scan>
13+
14+
</beans>

0 commit comments

Comments
 (0)