Skip to content

Commit 7a54ff2

Browse files
committed
Extract public TypeFilterUtils from ComponentScanAnnotationParser
Prior to this commit, third parties using @componentscan's @filter annotation had to implement their own parsing for @filter AnnotationAttributes as well as instantiation of the corresponding TypeFilters. In such cases the various *Aware callbacks (BeanFactoryAware, EnvironmentAware, etc.) should also be supported. This commit therefore extracts a new public TypeFilterUtils class from ComponentScanAnnotationParser so that third parties can benefit from consistent TypeFilter creation from @componentscan @filter annotations. Closes gh-27553
1 parent ec3f857 commit 7a54ff2

File tree

2 files changed

+127
-57
lines changed

2 files changed

+127
-57
lines changed

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

Lines changed: 8 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,10 @@
1616

1717
package org.springframework.context.annotation;
1818

19-
import java.lang.annotation.Annotation;
20-
import java.util.ArrayList;
2119
import java.util.Collections;
2220
import java.util.LinkedHashSet;
2321
import java.util.List;
2422
import java.util.Set;
25-
import java.util.regex.Pattern;
2623

2724
import org.springframework.beans.BeanUtils;
2825
import org.springframework.beans.factory.config.BeanDefinitionHolder;
@@ -33,12 +30,7 @@
3330
import org.springframework.core.env.Environment;
3431
import org.springframework.core.io.ResourceLoader;
3532
import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter;
36-
import org.springframework.core.type.filter.AnnotationTypeFilter;
37-
import org.springframework.core.type.filter.AspectJTypeFilter;
38-
import org.springframework.core.type.filter.AssignableTypeFilter;
39-
import org.springframework.core.type.filter.RegexPatternTypeFilter;
4033
import org.springframework.core.type.filter.TypeFilter;
41-
import org.springframework.util.Assert;
4234
import org.springframework.util.ClassUtils;
4335
import org.springframework.util.StringUtils;
4436

@@ -93,13 +85,17 @@ public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final
9385

9486
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
9587

96-
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
97-
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
88+
for (AnnotationAttributes includeFilterAttributes : componentScan.getAnnotationArray("includeFilters")) {
89+
List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(includeFilterAttributes, this.environment,
90+
this.resourceLoader, this.registry);
91+
for (TypeFilter typeFilter : typeFilters) {
9892
scanner.addIncludeFilter(typeFilter);
9993
}
10094
}
101-
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
102-
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
95+
for (AnnotationAttributes excludeFilterAttributes : componentScan.getAnnotationArray("excludeFilters")) {
96+
List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(excludeFilterAttributes, this.environment,
97+
this.resourceLoader, this.registry);
98+
for (TypeFilter typeFilter : typeFilters) {
10399
scanner.addExcludeFilter(typeFilter);
104100
}
105101
}
@@ -132,49 +128,4 @@ protected boolean matchClassName(String className) {
132128
return scanner.doScan(StringUtils.toStringArray(basePackages));
133129
}
134130

135-
private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
136-
List<TypeFilter> typeFilters = new ArrayList<>();
137-
FilterType filterType = filterAttributes.getEnum("type");
138-
139-
for (Class<?> filterClass : filterAttributes.getClassArray("classes")) {
140-
switch (filterType) {
141-
case ANNOTATION:
142-
Assert.isAssignable(Annotation.class, filterClass,
143-
"@ComponentScan ANNOTATION type filter requires an annotation type");
144-
@SuppressWarnings("unchecked")
145-
Class<Annotation> annotationType = (Class<Annotation>) filterClass;
146-
typeFilters.add(new AnnotationTypeFilter(annotationType));
147-
break;
148-
case ASSIGNABLE_TYPE:
149-
typeFilters.add(new AssignableTypeFilter(filterClass));
150-
break;
151-
case CUSTOM:
152-
Assert.isAssignable(TypeFilter.class, filterClass,
153-
"@ComponentScan CUSTOM type filter requires a TypeFilter implementation");
154-
155-
TypeFilter filter = ParserStrategyUtils.instantiateClass(filterClass, TypeFilter.class,
156-
this.environment, this.resourceLoader, this.registry);
157-
typeFilters.add(filter);
158-
break;
159-
default:
160-
throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
161-
}
162-
}
163-
164-
for (String expression : filterAttributes.getStringArray("pattern")) {
165-
switch (filterType) {
166-
case ASPECTJ:
167-
typeFilters.add(new AspectJTypeFilter(expression, this.resourceLoader.getClassLoader()));
168-
break;
169-
case REGEX:
170-
typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
171-
break;
172-
default:
173-
throw new IllegalArgumentException("Filter type not supported with String pattern: " + filterType);
174-
}
175-
}
176-
177-
return typeFilters;
178-
}
179-
180131
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation;
18+
19+
import java.lang.annotation.Annotation;
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.regex.Pattern;
23+
24+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
25+
import org.springframework.core.annotation.AnnotationAttributes;
26+
import org.springframework.core.env.Environment;
27+
import org.springframework.core.io.ResourceLoader;
28+
import org.springframework.core.type.filter.AnnotationTypeFilter;
29+
import org.springframework.core.type.filter.AspectJTypeFilter;
30+
import org.springframework.core.type.filter.AssignableTypeFilter;
31+
import org.springframework.core.type.filter.RegexPatternTypeFilter;
32+
import org.springframework.core.type.filter.TypeFilter;
33+
import org.springframework.util.Assert;
34+
35+
/**
36+
* Collection of utilities for working with {@link ComponentScan @ComponentScan}
37+
* {@linkplain ComponentScan.Filter type filters}.
38+
*
39+
* @author Chris Beams
40+
* @author Juergen Hoeller
41+
* @author Sam Brannen
42+
* @since 5.3.13
43+
* @see ComponentScan.Filter
44+
* @see org.springframework.core.type.filter.TypeFilter
45+
*/
46+
public abstract class TypeFilterUtils {
47+
48+
/**
49+
* Create {@linkplain TypeFilter type filters} from the supplied
50+
* {@link AnnotationAttributes}, such as those sourced from
51+
* {@link ComponentScan#includeFilters()} or {@link ComponentScan#excludeFilters()}.
52+
* <p>Each {@link TypeFilter} will be instantiated using an appropriate
53+
* constructor, with {@code BeanClassLoaderAware}, {@code BeanFactoryAware},
54+
* {@code EnvironmentAware}, and {@code ResourceLoaderAware} contracts
55+
* invoked if they are implemented by the type filter.
56+
* @param filterAttributes {@code AnnotationAttributes} for a
57+
* {@link ComponentScan.Filter @Filter} declaration
58+
* @param environment the {@code Environment} to make available to filters
59+
* @param resourceLoader the {@code ResourceLoader} to make available to filters
60+
* @param registry the {@code BeanDefinitionRegistry} to make available to filters
61+
* as a {@link org.springframework.beans.factory.BeanFactory} if applicable
62+
* @return a list of instantiated and configured type filters
63+
* @see TypeFilter
64+
* @see AnnotationTypeFilter
65+
* @see AssignableTypeFilter
66+
* @see AspectJTypeFilter
67+
* @see RegexPatternTypeFilter
68+
* @see org.springframework.beans.factory.BeanClassLoaderAware
69+
* @see org.springframework.beans.factory.BeanFactoryAware
70+
* @see org.springframework.context.EnvironmentAware
71+
* @see org.springframework.context.ResourceLoaderAware
72+
*/
73+
public static List<TypeFilter> createTypeFiltersFor(AnnotationAttributes filterAttributes, Environment environment,
74+
ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {
75+
76+
List<TypeFilter> typeFilters = new ArrayList<>();
77+
FilterType filterType = filterAttributes.getEnum("type");
78+
79+
for (Class<?> filterClass : filterAttributes.getClassArray("classes")) {
80+
switch (filterType) {
81+
case ANNOTATION:
82+
Assert.isAssignable(Annotation.class, filterClass,
83+
"@ComponentScan ANNOTATION type filter requires an annotation type");
84+
@SuppressWarnings("unchecked")
85+
Class<Annotation> annotationType = (Class<Annotation>) filterClass;
86+
typeFilters.add(new AnnotationTypeFilter(annotationType));
87+
break;
88+
case ASSIGNABLE_TYPE:
89+
typeFilters.add(new AssignableTypeFilter(filterClass));
90+
break;
91+
case CUSTOM:
92+
Assert.isAssignable(TypeFilter.class, filterClass,
93+
"@ComponentScan CUSTOM type filter requires a TypeFilter implementation");
94+
TypeFilter filter = ParserStrategyUtils.instantiateClass(filterClass, TypeFilter.class,
95+
environment, resourceLoader, registry);
96+
typeFilters.add(filter);
97+
break;
98+
default:
99+
throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
100+
}
101+
}
102+
103+
for (String expression : filterAttributes.getStringArray("pattern")) {
104+
switch (filterType) {
105+
case ASPECTJ:
106+
typeFilters.add(new AspectJTypeFilter(expression, resourceLoader.getClassLoader()));
107+
break;
108+
case REGEX:
109+
typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
110+
break;
111+
default:
112+
throw new IllegalArgumentException("Filter type not supported with String pattern: " + filterType);
113+
}
114+
}
115+
116+
return typeFilters;
117+
}
118+
119+
}

0 commit comments

Comments
 (0)