Skip to content

Commit 3e67f25

Browse files
committed
Optimized process to parse excludeFilters
1 parent 564c108 commit 3e67f25

File tree

13 files changed

+395
-109
lines changed

13 files changed

+395
-109
lines changed

src/main/java/org/mybatis/spring/annotation/MapperScan.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@
201201
* Specifies which types are not eligible for mapper scanning.
202202
*
203203
* @since 3.0.3
204+
*
205+
* @return array of customized mapper excludeFilter
204206
*/
205207
ComponentScan.Filter[] excludeFilters() default {};
206208

src/main/java/org/mybatis/spring/annotation/MapperScannerRegistrar.java

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
import java.lang.annotation.Annotation;
1919
import java.util.ArrayList;
2020
import java.util.Arrays;
21+
import java.util.HashMap;
2122
import java.util.List;
22-
import java.util.regex.Pattern;
23+
import java.util.Map;
2324
import java.util.stream.Collectors;
2425

2526
import org.mybatis.spring.mapper.ClassPathMapperScanner;
@@ -38,9 +39,7 @@
3839
import org.springframework.core.io.ResourceLoader;
3940
import org.springframework.core.type.AnnotationMetadata;
4041
import org.springframework.core.type.filter.AnnotationTypeFilter;
41-
import org.springframework.core.type.filter.AspectJTypeFilter;
4242
import org.springframework.core.type.filter.AssignableTypeFilter;
43-
import org.springframework.core.type.filter.RegexPatternTypeFilter;
4443
import org.springframework.core.type.filter.TypeFilter;
4544
import org.springframework.util.Assert;
4645
import org.springframework.util.ClassUtils;
@@ -136,10 +135,17 @@ void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes a
136135
AnnotationAttributes[] excludeFilterArray = annoAttrs.getAnnotationArray("excludeFilters");
137136
if (excludeFilterArray.length > 0) {
138137
List<TypeFilter> typeFilters = new ArrayList<>();
138+
List<Map<String, String>> rawTypeFilters = new ArrayList<>();
139139
for (AnnotationAttributes excludeFilters : excludeFilterArray) {
140-
typeFilters.addAll(typeFiltersFor(excludeFilters));
140+
if (excludeFilters.getStringArray("pattern").length > 0) {
141+
// in oder to apply placeholder resolver
142+
rawTypeFilters.addAll(parseFiltersHasPatterns(excludeFilters));
143+
} else {
144+
typeFilters.addAll(typeFiltersFor(excludeFilters));
145+
}
141146
}
142147
builder.addPropertyValue("excludeFilters", typeFilters);
148+
builder.addPropertyValue("rawExcludeFilters", rawTypeFilters);
143149
}
144150

145151
String lazyInitialization = annoAttrs.getString("lazyInitialization");
@@ -161,6 +167,44 @@ void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes a
161167

162168
}
163169

170+
/**
171+
* Parse excludeFilters which FilterType is REGEX or ASPECTJ
172+
*
173+
* @param filterAttributes
174+
* AnnotationAttributes of excludeFilters
175+
*
176+
* @since 3.0.3
177+
*/
178+
private List<Map<String, String>> parseFiltersHasPatterns(AnnotationAttributes filterAttributes) {
179+
180+
List<Map<String, String>> rawTypeFilters = new ArrayList<>();
181+
FilterType filterType = filterAttributes.getEnum("type");
182+
String[] expressionArray = filterAttributes.getStringArray("pattern");
183+
for (String expression : expressionArray) {
184+
switch (filterType) {
185+
case REGEX:
186+
case ASPECTJ:
187+
Map<String, String> typeFilter = new HashMap<>(16);
188+
typeFilter.put("type", filterType.name().toLowerCase());
189+
typeFilter.put("expression", expression);
190+
rawTypeFilters.add(typeFilter);
191+
break;
192+
default:
193+
throw new IllegalArgumentException("Cannot specify the 'pattern' attribute if use the " + filterType
194+
+ " FilterType in exclude filter of @MapperScan");
195+
}
196+
}
197+
return rawTypeFilters;
198+
}
199+
200+
/**
201+
* Parse excludeFilters which FilterType is ANNOTATION ASSIGNABLE or CUSTOM
202+
*
203+
* @param filterAttributes
204+
* AnnotationAttributes of excludeFilters
205+
*
206+
* @since 3.0.3
207+
*/
164208
private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
165209

166210
List<TypeFilter> typeFilters = new ArrayList<>();
@@ -188,21 +232,6 @@ private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
188232
+ filterType + " FilterType in exclude filter of @MapperScan");
189233
}
190234
}
191-
192-
String[] expressionArray = filterAttributes.getStringArray("pattern");
193-
for (String expression : expressionArray) {
194-
switch (filterType) {
195-
case REGEX:
196-
typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
197-
break;
198-
case ASPECTJ:
199-
typeFilters.add(new AspectJTypeFilter(expression, this.resourceLoader.getClassLoader()));
200-
break;
201-
default:
202-
throw new IllegalArgumentException("Cannot specify the 'pattern' attribute if use the " + filterType
203-
+ " FilterType in exclude filter of @MapperScan");
204-
}
205-
}
206235
return typeFilters;
207236
}
208237

src/main/java/org/mybatis/spring/config/MapperScannerBeanDefinitionParser.java

Lines changed: 14 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717

1818
import java.lang.annotation.Annotation;
1919
import java.util.ArrayList;
20+
import java.util.HashMap;
2021
import java.util.List;
21-
import java.util.regex.Pattern;
22+
import java.util.Map;
2223

2324
import org.mybatis.spring.mapper.ClassPathMapperScanner;
2425
import org.mybatis.spring.mapper.MapperFactoryBean;
@@ -31,12 +32,6 @@
3132
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
3233
import org.springframework.beans.factory.xml.ParserContext;
3334
import org.springframework.beans.factory.xml.XmlReaderContext;
34-
import org.springframework.core.type.filter.AnnotationTypeFilter;
35-
import org.springframework.core.type.filter.AspectJTypeFilter;
36-
import org.springframework.core.type.filter.AssignableTypeFilter;
37-
import org.springframework.core.type.filter.RegexPatternTypeFilter;
38-
import org.springframework.core.type.filter.TypeFilter;
39-
import org.springframework.lang.Nullable;
4035
import org.springframework.util.ClassUtils;
4136
import org.springframework.util.StringUtils;
4237
import org.w3c.dom.Element;
@@ -111,11 +106,12 @@ protected AbstractBeanDefinition parseInternal(Element element, ParserContext pa
111106
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
112107
}
113108

114-
// parse exclude-filter
115-
List<TypeFilter> typeFilters = parseTypeFilters(element, parserContext, classLoader);
116-
if (!typeFilters.isEmpty()) {
117-
builder.addPropertyValue("excludeFilters", typeFilters);
109+
// parse raw exclude-filter in <mybatis:scan>
110+
List<Map<String, String>> rawExcludeFilters = parseScanTypeFilters(element, parserContext);
111+
if (!rawExcludeFilters.isEmpty()) {
112+
builder.addPropertyValue("rawExcludeFilters", rawExcludeFilters);
118113
}
114+
119115
} catch (Exception ex) {
120116
XmlReaderContext readerContext = parserContext.getReaderContext();
121117
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
@@ -133,62 +129,24 @@ protected AbstractBeanDefinition parseInternal(Element element, ParserContext pa
133129
return builder.getBeanDefinition();
134130
}
135131

136-
private List<TypeFilter> parseTypeFilters(Element element, ParserContext parserContext, ClassLoader classLoader) {
137-
// Parse exclude filter elements.
138-
List<TypeFilter> typeFilters = new ArrayList<>();
132+
private List<Map<String, String>> parseScanTypeFilters(Element element, ParserContext parserContext) {
133+
List<Map<String, String>> typeFilters = new ArrayList<>();
139134
NodeList nodeList = element.getChildNodes();
140135
for (int i = 0; i < nodeList.getLength(); i++) {
141136
Node node = nodeList.item(i);
142137
if (Node.ELEMENT_NODE == node.getNodeType()) {
143138
String localName = parserContext.getDelegate().getLocalName(node);
144-
try {
145-
if (ATTRIBUTE_EXCLUDE_FILTER.equals(localName)) {
146-
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
147-
typeFilters.add(typeFilter);
148-
}
149-
} catch (ClassNotFoundException ex) {
150-
parserContext.getReaderContext().warning("Ignoring non-present type filter class: " + ex,
151-
parserContext.extractSource(element));
152-
} catch (Exception ex) {
153-
parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
139+
if (ATTRIBUTE_EXCLUDE_FILTER.equals(localName)) {
140+
Map<String, String> filter = new HashMap<>(16);
141+
filter.put("type", ((Element) node).getAttribute("type"));
142+
filter.put("expression", ((Element) node).getAttribute("expression"));
143+
typeFilters.add(filter);
154144
}
155145
}
156146
}
157147
return typeFilters;
158148
}
159149

160-
@SuppressWarnings("unchecked")
161-
private TypeFilter createTypeFilter(Element element, @Nullable ClassLoader classLoader, ParserContext parserContext)
162-
throws ClassNotFoundException {
163-
String filterType = element.getAttribute("type");
164-
String expression = element.getAttribute("expression");
165-
expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression);
166-
switch (filterType) {
167-
case "annotation":
168-
Class<?> filterAnno = ClassUtils.forName(expression, classLoader);
169-
if(!Annotation.class.isAssignableFrom(filterAnno)){
170-
throw new IllegalArgumentException(
171-
"Class is not assignable to [" + Annotation.class.getName() + "]: " + expression);
172-
}
173-
return new AnnotationTypeFilter((Class<Annotation>) filterAnno);
174-
case "custom":
175-
Class<?> filterClass = ClassUtils.forName(expression, classLoader);
176-
if (!TypeFilter.class.isAssignableFrom(filterClass)) {
177-
throw new IllegalArgumentException(
178-
"Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
179-
}
180-
return (TypeFilter) BeanUtils.instantiateClass(filterClass);
181-
case "assignable":
182-
return new AssignableTypeFilter(ClassUtils.forName(expression, classLoader));
183-
case "regex":
184-
return new RegexPatternTypeFilter(Pattern.compile(expression));
185-
case "aspectj":
186-
return new AspectJTypeFilter(expression, classLoader);
187-
default:
188-
throw new IllegalArgumentException("Unsupported filter type: " + filterType);
189-
}
190-
}
191-
192150
/**
193151
* {@inheritDoc}
194152
*

src/main/java/org/mybatis/spring/mapper/MapperScannerConfigurer.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
import static org.springframework.util.Assert.notNull;
1919

2020
import java.lang.annotation.Annotation;
21+
import java.util.ArrayList;
2122
import java.util.List;
2223
import java.util.Map;
2324
import java.util.Optional;
25+
import java.util.regex.Pattern;
2426

2527
import org.apache.ibatis.session.SqlSessionFactory;
2628
import org.mybatis.spring.SqlSessionTemplate;
29+
import org.springframework.beans.BeanUtils;
2730
import org.springframework.beans.PropertyValue;
2831
import org.springframework.beans.PropertyValues;
2932
import org.springframework.beans.factory.BeanNameAware;
@@ -40,7 +43,13 @@
4043
import org.springframework.context.ApplicationContextAware;
4144
import org.springframework.context.ConfigurableApplicationContext;
4245
import org.springframework.core.env.Environment;
46+
import org.springframework.core.type.filter.AnnotationTypeFilter;
47+
import org.springframework.core.type.filter.AspectJTypeFilter;
48+
import org.springframework.core.type.filter.AssignableTypeFilter;
49+
import org.springframework.core.type.filter.RegexPatternTypeFilter;
4350
import org.springframework.core.type.filter.TypeFilter;
51+
import org.springframework.lang.Nullable;
52+
import org.springframework.util.ClassUtils;
4453
import org.springframework.util.StringUtils;
4554

4655
/**
@@ -113,6 +122,8 @@ public class MapperScannerConfigurer
113122

114123
private List<TypeFilter> excludeFilters;
115124

125+
private List<Map<String, String>> rawExcludeFilters;
126+
116127
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass;
117128

118129
private ApplicationContext applicationContext;
@@ -209,6 +220,20 @@ public void setExcludeFilters(List<TypeFilter> excludeFilters) {
209220
this.excludeFilters = excludeFilters;
210221
}
211222

223+
/**
224+
* In order to support process PropertyPlaceHolders.
225+
* <p>
226+
* After parsed, it will be added to excludeFilters.
227+
*
228+
* @since 3.0.3
229+
*
230+
* @param rawExcludeFilters
231+
* list of rawExcludeFilter
232+
*/
233+
public void setRawExcludeFilters(List<Map<String, String>> rawExcludeFilters) {
234+
this.rawExcludeFilters = rawExcludeFilters;
235+
}
236+
212237
/**
213238
* Specifies which {@code SqlSessionTemplate} to use in the case that there is more than one in the spring context.
214239
* Usually this is only needed when you have more than one datasource.
@@ -432,6 +457,7 @@ private void processPropertyPlaceHolders() {
432457
this.sqlSessionTemplateBeanName = getPropertyValue("sqlSessionTemplateBeanName", values);
433458
this.lazyInitialization = getPropertyValue("lazyInitialization", values);
434459
this.defaultScope = getPropertyValue("defaultScope", values);
460+
this.rawExcludeFilters = getPropertyValueForTypeFilter("rawExcludeFilters", values);
435461
}
436462
this.basePackage = Optional.ofNullable(this.basePackage).map(getEnvironment()::resolvePlaceholders).orElse(null);
437463
this.sqlSessionFactoryBeanName = Optional.ofNullable(this.sqlSessionFactoryBeanName)
@@ -441,6 +467,7 @@ private void processPropertyPlaceHolders() {
441467
this.lazyInitialization = Optional.ofNullable(this.lazyInitialization).map(getEnvironment()::resolvePlaceholders)
442468
.orElse(null);
443469
this.defaultScope = Optional.ofNullable(this.defaultScope).map(getEnvironment()::resolvePlaceholders).orElse(null);
470+
this.excludeFilters = mergeExcludeFilters();
444471
}
445472

446473
private Environment getEnvironment() {
@@ -467,4 +494,66 @@ private String getPropertyValue(String propertyName, PropertyValues values) {
467494
}
468495
}
469496

497+
@SuppressWarnings("unchecked")
498+
private List<Map<String, String>> getPropertyValueForTypeFilter(String propertyName, PropertyValues values) {
499+
PropertyValue property = values.getPropertyValue(propertyName);
500+
Object value;
501+
if (property == null || (value = property.getValue()) == null || !(value instanceof List<?>)) {
502+
return null;
503+
}
504+
return (List<Map<String, String>>) value;
505+
}
506+
507+
private List<TypeFilter> mergeExcludeFilters() {
508+
List<TypeFilter> typeFilters = new ArrayList<>();
509+
if (this.rawExcludeFilters == null || this.rawExcludeFilters.isEmpty()) {
510+
return this.excludeFilters;
511+
}
512+
if (this.excludeFilters != null && !this.excludeFilters.isEmpty()) {
513+
typeFilters.addAll(this.excludeFilters);
514+
}
515+
try {
516+
for (Map<String, String> typeFilter : this.rawExcludeFilters) {
517+
typeFilters.add(
518+
createTypeFilter(typeFilter.get("type"), typeFilter.get("expression"), this.getClass().getClassLoader()));
519+
}
520+
} catch (ClassNotFoundException exception) {
521+
throw new RuntimeException("ClassNotFoundException occur when to load the Specified excludeFilter classes.",
522+
exception);
523+
}
524+
return typeFilters;
525+
}
526+
527+
@SuppressWarnings("unchecked")
528+
private TypeFilter createTypeFilter(String filterType, String expression, @Nullable ClassLoader classLoader)
529+
throws ClassNotFoundException {
530+
531+
expression = this.getEnvironment().resolvePlaceholders(expression);
532+
533+
switch (filterType) {
534+
case "annotation":
535+
Class<?> filterAnno = ClassUtils.forName(expression, classLoader);
536+
if (!Annotation.class.isAssignableFrom(filterAnno)) {
537+
throw new IllegalArgumentException(
538+
"Class is not assignable to [" + Annotation.class.getName() + "]: " + expression);
539+
}
540+
return new AnnotationTypeFilter((Class<Annotation>) filterAnno);
541+
case "custom":
542+
Class<?> filterClass = ClassUtils.forName(expression, classLoader);
543+
if (!TypeFilter.class.isAssignableFrom(filterClass)) {
544+
throw new IllegalArgumentException(
545+
"Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
546+
}
547+
return (TypeFilter) BeanUtils.instantiateClass(filterClass);
548+
case "assignable":
549+
return new AssignableTypeFilter(ClassUtils.forName(expression, classLoader));
550+
case "regex":
551+
return new RegexPatternTypeFilter(Pattern.compile(expression));
552+
case "aspectj":
553+
return new AspectJTypeFilter(expression, classLoader);
554+
default:
555+
throw new IllegalArgumentException("Unsupported filter type: " + filterType);
556+
}
557+
}
558+
470559
}

src/main/resources/org/mybatis/spring/config/mybatis-spring.xsd

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,10 @@
177177
<xsd:documentation><![CDATA[
178178
Controls the type of filtering to apply to the expression.
179179
180-
"annotation" indicates an annotation to be present or meta-present at the type level in target components;
181-
"assignable" indicates a class (or interface) that the target components are assignable to (extend/implement);
182-
"aspectj" indicates an AspectJ type pattern expression to be matched by the target components;
183-
"regex" indicates a regex pattern to be matched by the target components' class names;
180+
"annotation" indicates an annotation to be present or meta-present at the type level in target mappers;
181+
"assignable" indicates a class (or interface) that the target mappers are assignable to (extend/implement);
182+
"aspectj" indicates an AspectJ type pattern expression to be matched by the target mappers;
183+
"regex" indicates a regex pattern to be matched by the target mappers' class names;
184184
"custom" indicates a custom implementation of the org.springframework.core.type.TypeFilter interface.
185185
186186
Note: This attribute will not be inherited by child bean definitions.

0 commit comments

Comments
 (0)