1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.spring.mapper;
17
18 import java.lang.annotation.Annotation;
19 import java.util.Arrays;
20 import java.util.List;
21 import java.util.Optional;
22 import java.util.Set;
23
24 import org.apache.ibatis.io.Resources;
25 import org.apache.ibatis.session.SqlSessionFactory;
26 import org.mybatis.logging.Logger;
27 import org.mybatis.logging.LoggerFactory;
28 import org.mybatis.spring.SqlSessionTemplate;
29 import org.springframework.aop.scope.ScopedProxyFactoryBean;
30 import org.springframework.aop.scope.ScopedProxyUtils;
31 import org.springframework.aot.AotDetector;
32 import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
33 import org.springframework.beans.factory.config.BeanDefinition;
34 import org.springframework.beans.factory.config.BeanDefinitionHolder;
35 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
36 import org.springframework.beans.factory.config.ConstructorArgumentValues;
37 import org.springframework.beans.factory.config.RuntimeBeanReference;
38 import org.springframework.beans.factory.support.AbstractBeanDefinition;
39 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
40 import org.springframework.beans.factory.support.RootBeanDefinition;
41 import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
42 import org.springframework.core.NativeDetector;
43 import org.springframework.core.env.Environment;
44 import org.springframework.core.type.filter.AnnotationTypeFilter;
45 import org.springframework.core.type.filter.AssignableTypeFilter;
46 import org.springframework.core.type.filter.TypeFilter;
47 import org.springframework.util.StringUtils;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
65
66 private static final Logger LOGGER = LoggerFactory.getLogger(ClassPathMapperScanner.class);
67
68
69 static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
70
71 private boolean addToConfig = true;
72
73 private boolean lazyInitialization;
74
75 private boolean printWarnLogIfNotFoundMappers = true;
76
77 private SqlSessionFactory sqlSessionFactory;
78
79 private SqlSessionTemplate sqlSessionTemplate;
80
81 private String sqlSessionTemplateBeanName;
82
83 private String sqlSessionFactoryBeanName;
84
85 private Class<? extends Annotation> annotationClass;
86
87 private Class<?> markerInterface;
88
89 private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
90
91 private String defaultScope;
92 private List<TypeFilter> excludeFilters;
93
94
95
96
97
98
99
100
101
102 public ClassPathMapperScanner(BeanDefinitionRegistry registry, Environment environment) {
103 super(registry, false, environment);
104 setIncludeAnnotationConfig(!AotDetector.useGeneratedArtifacts());
105 setPrintWarnLogIfNotFoundMappers(!NativeDetector.inNativeImage());
106 }
107
108
109
110
111
112
113
114
115
116 @Deprecated(since = "3.0.4", forRemoval = true)
117 public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
118 super(registry, false);
119 setIncludeAnnotationConfig(!AotDetector.useGeneratedArtifacts());
120 setPrintWarnLogIfNotFoundMappers(!NativeDetector.inNativeImage());
121 }
122
123
124
125
126
127
128
129 public void setAddToConfig(boolean addToConfig) {
130 this.addToConfig = addToConfig;
131 }
132
133
134
135
136
137
138
139 public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
140 this.annotationClass = annotationClass;
141 }
142
143
144
145
146
147
148
149
150
151
152
153 public void setLazyInitialization(boolean lazyInitialization) {
154 this.lazyInitialization = lazyInitialization;
155 }
156
157
158
159
160
161
162
163
164
165
166
167 public void setPrintWarnLogIfNotFoundMappers(boolean printWarnLogIfNotFoundMappers) {
168 this.printWarnLogIfNotFoundMappers = printWarnLogIfNotFoundMappers;
169 }
170
171
172
173
174
175
176
177 public void setMarkerInterface(Class<?> markerInterface) {
178 this.markerInterface = markerInterface;
179 }
180
181
182
183
184
185
186
187 public void setExcludeFilters(List<TypeFilter> excludeFilters) {
188 this.excludeFilters = excludeFilters;
189 }
190
191
192
193
194
195
196
197 public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
198 this.sqlSessionFactory = sqlSessionFactory;
199 }
200
201
202
203
204
205
206
207 public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
208 this.sqlSessionTemplate = sqlSessionTemplate;
209 }
210
211
212
213
214
215
216
217 public void setSqlSessionTemplateBeanName(String sqlSessionTemplateBeanName) {
218 this.sqlSessionTemplateBeanName = sqlSessionTemplateBeanName;
219 }
220
221
222
223
224
225
226
227 public void setSqlSessionFactoryBeanName(String sqlSessionFactoryBeanName) {
228 this.sqlSessionFactoryBeanName = sqlSessionFactoryBeanName;
229 }
230
231
232
233
234
235
236
237
238
239 @Deprecated(since = "2.0.1", forRemoval = true)
240 public void setMapperFactoryBean(MapperFactoryBean<?> mapperFactoryBean) {
241 this.mapperFactoryBeanClass = mapperFactoryBean == null ? MapperFactoryBean.class : mapperFactoryBean.getClass();
242 }
243
244
245
246
247
248
249
250
251
252 public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) {
253 this.mapperFactoryBeanClass = mapperFactoryBeanClass == null ? MapperFactoryBean.class : mapperFactoryBeanClass;
254 }
255
256
257
258
259
260
261
262
263
264
265
266 public void setDefaultScope(String defaultScope) {
267 this.defaultScope = defaultScope;
268 }
269
270
271
272
273
274 public void registerFilters() {
275 var acceptAllInterfaces = true;
276
277
278 if (this.annotationClass != null) {
279 addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
280 acceptAllInterfaces = false;
281 }
282
283
284 if (this.markerInterface != null) {
285 addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
286 @Override
287 protected boolean matchClassName(String className) {
288 return false;
289 }
290 });
291 acceptAllInterfaces = false;
292 }
293
294 if (acceptAllInterfaces) {
295
296 addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
297 }
298
299
300 addExcludeFilter((metadataReader, metadataReaderFactory) -> {
301 var className = metadataReader.getClassMetadata().getClassName();
302 return className.endsWith("package-info");
303 });
304
305
306 if (excludeFilters != null && excludeFilters.size() > 0) {
307 for (TypeFilter excludeFilter : excludeFilters) {
308 addExcludeFilter(excludeFilter);
309 }
310 }
311 }
312
313
314
315
316
317 @Override
318 public Set<BeanDefinitionHolder> doScan(String... basePackages) {
319 var beanDefinitions = super.doScan(basePackages);
320
321 if (beanDefinitions.isEmpty()) {
322 if (printWarnLogIfNotFoundMappers) {
323 LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
324 + "' package. Please check your configuration.");
325 }
326 } else {
327 processBeanDefinitions(beanDefinitions);
328 }
329
330 return beanDefinitions;
331 }
332
333 private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
334 AbstractBeanDefinition definition;
335 var registry = getRegistry();
336 for (BeanDefinitionHolder holder : beanDefinitions) {
337 definition = (AbstractBeanDefinition) holder.getBeanDefinition();
338 var scopedProxy = false;
339 if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
340 definition = (AbstractBeanDefinition) Optional
341 .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
342 .map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
343 "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
344 scopedProxy = true;
345 }
346 var beanClassName = definition.getBeanClassName();
347 LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
348 + "' mapperInterface");
349
350
351
352 var constructorArgument = new ConstructorArgumentValues.ValueHolder(beanClassName);
353 definition.getConstructorArgumentValues().addGenericArgumentValue(constructorArgument);
354 try {
355 Class<?> beanClass = Resources.classForName(beanClassName);
356 if (shouldUseClassConstructorArgument(this.mapperFactoryBeanClass)) {
357 constructorArgument.setValue(beanClass);
358 }
359
360
361 definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClass);
362
363 definition.getPropertyValues().add("mapperInterface", beanClass);
364 } catch (ClassNotFoundException ignore) {
365
366 }
367
368 definition.setBeanClass(this.mapperFactoryBeanClass);
369
370 definition.getPropertyValues().add("addToConfig", this.addToConfig);
371
372 var explicitFactoryUsed = false;
373 if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
374 definition.getPropertyValues().add("sqlSessionFactory",
375 new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
376 explicitFactoryUsed = true;
377 } else if (this.sqlSessionFactory != null) {
378 definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
379 explicitFactoryUsed = true;
380 }
381
382 if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
383 if (explicitFactoryUsed) {
384 LOGGER.warn(
385 () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
386 }
387 definition.getPropertyValues().add("sqlSessionTemplate",
388 new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
389 explicitFactoryUsed = true;
390 } else if (this.sqlSessionTemplate != null) {
391 if (explicitFactoryUsed) {
392 LOGGER.warn(
393 () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
394 }
395 definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
396 explicitFactoryUsed = true;
397 }
398
399 if (!explicitFactoryUsed) {
400 LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
401 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
402 }
403
404 definition.setLazyInit(lazyInitialization);
405
406 if (scopedProxy) {
407 continue;
408 }
409
410 if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
411 definition.setScope(defaultScope);
412 }
413
414 if (!definition.isSingleton()) {
415 var proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
416 if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
417 registry.removeBeanDefinition(proxyHolder.getBeanName());
418 }
419 registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
420 }
421
422 }
423 }
424
425 @Override
426 protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
427 var metadata = beanDefinition.getMetadata();
428
429 return metadata.isInterface() && metadata.isIndependent() && !metadata.isAnnotation();
430 }
431
432 private boolean shouldUseClassConstructorArgument(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) {
433 return hasSingleArgumentConstructorAccepting(mapperFactoryBeanClass, Class.class)
434 && !hasSingleArgumentConstructorAccepting(mapperFactoryBeanClass, String.class);
435 }
436
437 private boolean hasSingleArgumentConstructorAccepting(Class<? extends MapperFactoryBean> mapperFactoryBeanClass,
438 Class<?> argumentType) {
439 return Arrays.stream(mapperFactoryBeanClass.getDeclaredConstructors())
440 .anyMatch(constructor -> constructor.getParameterCount() == 1
441 && constructor.getParameterTypes()[0].isAssignableFrom(argumentType));
442 }
443
444 @Override
445 protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) {
446 if (super.checkCandidate(beanName, beanDefinition)) {
447 return true;
448 }
449 LOGGER.warn(() -> "Skipping MapperFactoryBean with name '" + beanName + "' and '"
450 + beanDefinition.getBeanClassName() + "' mapperInterface" + ". Bean already defined with the same name!");
451 return false;
452 }
453
454 }