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