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