• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / spring / #1512

28 May 2024 08:25PM CUT coverage: 90.232%. Remained the same
#1512

Pull #957

github

web-flow
Update dependency org.mybatis:mybatis-parent to v44
Pull Request #957: Update dependency org.mybatis:mybatis-parent to v44

309 of 370 branches covered (83.51%)

933 of 1034 relevant lines covered (90.23%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.12
/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java
1
/*
2
 * Copyright 2010-2024 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
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
 * A {@link ClassPathBeanDefinitionScanner} that registers Mappers by {@code basePackage}, {@code annotationClass}, or
50
 * {@code markerInterface}. If an {@code annotationClass} and/or {@code markerInterface} is specified, only the
51
 * specified types will be searched (searching for all interfaces will be disabled).
52
 * <p>
53
 * This functionality was previously a private class of {@link MapperScannerConfigurer}, but was broken out in version
54
 * 1.2.0.
55
 *
56
 * @author Hunter Presnall
57
 * @author Eduardo Macarron
58
 *
59
 * @see MapperFactoryBean
60
 *
61
 * @since 1.2.0
62
 */
63
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
64

65
  private static final Logger LOGGER = LoggerFactory.getLogger(ClassPathMapperScanner.class);
1✔
66

67
  // Copy of FactoryBean#OBJECT_TYPE_ATTRIBUTE which was added in Spring 5.2
68
  static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
69

70
  private boolean addToConfig = true;
1✔
71

72
  private boolean lazyInitialization;
73

74
  private boolean printWarnLogIfNotFoundMappers = true;
1✔
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;
1✔
89

90
  private String defaultScope;
91
  private List<TypeFilter> excludeFilters;
92

93
  public ClassPathMapperScanner(BeanDefinitionRegistry registry, Environment environment) {
94
    super(registry, false, environment);
1✔
95
    setIncludeAnnotationConfig(!AotDetector.useGeneratedArtifacts());
1!
96
    setPrintWarnLogIfNotFoundMappers(!NativeDetector.inNativeImage());
1!
97
  }
1✔
98

99
  /**
100
   * @deprecated Please use the {@link #ClassPathMapperScanner(BeanDefinitionRegistry, Environment)}.
101
   */
102
  @Deprecated(since = "3.0.4", forRemoval = true)
103
  public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
104
    super(registry, false);
×
105
    setIncludeAnnotationConfig(!AotDetector.useGeneratedArtifacts());
×
106
    setPrintWarnLogIfNotFoundMappers(!NativeDetector.inNativeImage());
×
107
  }
×
108

109
  public void setAddToConfig(boolean addToConfig) {
110
    this.addToConfig = addToConfig;
1✔
111
  }
1✔
112

113
  public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
114
    this.annotationClass = annotationClass;
1✔
115
  }
1✔
116

117
  /**
118
   * Set whether enable lazy initialization for mapper bean.
119
   * <p>
120
   * Default is {@code false}.
121
   * </p>
122
   *
123
   * @param lazyInitialization
124
   *          Set the @{code true} to enable
125
   *
126
   * @since 2.0.2
127
   */
128
  public void setLazyInitialization(boolean lazyInitialization) {
129
    this.lazyInitialization = lazyInitialization;
1✔
130
  }
1✔
131

132
  /**
133
   * Set whether print warning log if not found mappers that matches conditions.
134
   * <p>
135
   * Default is {@code true}. But {@code false} when running in native image.
136
   * </p>
137
   *
138
   * @param printWarnLogIfNotFoundMappers
139
   *          Set the @{code true} to print
140
   *
141
   * @since 3.0.1
142
   */
143
  public void setPrintWarnLogIfNotFoundMappers(boolean printWarnLogIfNotFoundMappers) {
144
    this.printWarnLogIfNotFoundMappers = printWarnLogIfNotFoundMappers;
1✔
145
  }
1✔
146

147
  public void setMarkerInterface(Class<?> markerInterface) {
148
    this.markerInterface = markerInterface;
1✔
149
  }
1✔
150

151
  public void setExcludeFilters(List<TypeFilter> excludeFilters) {
152
    this.excludeFilters = excludeFilters;
1✔
153
  }
1✔
154

155
  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
156
    this.sqlSessionFactory = sqlSessionFactory;
1✔
157
  }
1✔
158

159
  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
160
    this.sqlSessionTemplate = sqlSessionTemplate;
1✔
161
  }
1✔
162

163
  public void setSqlSessionTemplateBeanName(String sqlSessionTemplateBeanName) {
164
    this.sqlSessionTemplateBeanName = sqlSessionTemplateBeanName;
1✔
165
  }
1✔
166

167
  public void setSqlSessionFactoryBeanName(String sqlSessionFactoryBeanName) {
168
    this.sqlSessionFactoryBeanName = sqlSessionFactoryBeanName;
1✔
169
  }
1✔
170

171
  /**
172
   * @deprecated Since 2.0.1, Please use the {@link #setMapperFactoryBeanClass(Class)}.
173
   */
174
  @Deprecated
175
  public void setMapperFactoryBean(MapperFactoryBean<?> mapperFactoryBean) {
176
    this.mapperFactoryBeanClass = mapperFactoryBean == null ? MapperFactoryBean.class : mapperFactoryBean.getClass();
×
177
  }
×
178

179
  /**
180
   * Set the {@code MapperFactoryBean} class.
181
   *
182
   * @param mapperFactoryBeanClass
183
   *          the {@code MapperFactoryBean} class
184
   *
185
   * @since 2.0.1
186
   */
187
  public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) {
188
    this.mapperFactoryBeanClass = mapperFactoryBeanClass == null ? MapperFactoryBean.class : mapperFactoryBeanClass;
1✔
189
  }
1✔
190

191
  /**
192
   * Set the default scope of scanned mappers.
193
   * <p>
194
   * Default is {@code null} (equiv to singleton).
195
   * </p>
196
   *
197
   * @param defaultScope
198
   *          the scope
199
   *
200
   * @since 2.0.6
201
   */
202
  public void setDefaultScope(String defaultScope) {
203
    this.defaultScope = defaultScope;
1✔
204
  }
1✔
205

206
  /**
207
   * Configures parent scanner to search for the right interfaces. It can search for all interfaces or just for those
208
   * that extends a markerInterface or/and those annotated with the annotationClass
209
   */
210
  public void registerFilters() {
211
    boolean acceptAllInterfaces = true;
1✔
212

213
    // if specified, use the given annotation and / or marker interface
214
    if (this.annotationClass != null) {
1✔
215
      addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
1✔
216
      acceptAllInterfaces = false;
1✔
217
    }
218

219
    // override AssignableTypeFilter to ignore matches on the actual marker interface
220
    if (this.markerInterface != null) {
1✔
221
      addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
1✔
222
        @Override
223
        protected boolean matchClassName(String className) {
224
          return false;
1✔
225
        }
226
      });
227
      acceptAllInterfaces = false;
1✔
228
    }
229

230
    if (acceptAllInterfaces) {
1✔
231
      // default include filter that accepts all classes
232
      addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
1✔
233
    }
234

235
    // exclude package-info.java
236
    addExcludeFilter((metadataReader, metadataReaderFactory) -> {
1✔
237
      String className = metadataReader.getClassMetadata().getClassName();
1✔
238
      return className.endsWith("package-info");
1✔
239
    });
240

241
    // exclude types declared by MapperScan.excludeFilters
242
    if (excludeFilters != null && excludeFilters.size() > 0) {
1!
243
      for (TypeFilter excludeFilter : excludeFilters) {
1✔
244
        addExcludeFilter(excludeFilter);
1✔
245
      }
1✔
246
    }
247
  }
1✔
248

249
  /**
250
   * Calls the parent search that will search and register all the candidates. Then the registered objects are post
251
   * processed to set them as MapperFactoryBeans
252
   */
253
  @Override
254
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
255
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
1✔
256

257
    if (beanDefinitions.isEmpty()) {
1!
258
      if (printWarnLogIfNotFoundMappers) {
×
259
        LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
×
260
            + "' package. Please check your configuration.");
261
      }
262
    } else {
263
      processBeanDefinitions(beanDefinitions);
1✔
264
    }
265

266
    return beanDefinitions;
1✔
267
  }
268

269
  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
270
    AbstractBeanDefinition definition;
271
    BeanDefinitionRegistry registry = getRegistry();
1✔
272
    for (BeanDefinitionHolder holder : beanDefinitions) {
1✔
273
      definition = (AbstractBeanDefinition) holder.getBeanDefinition();
1✔
274
      boolean scopedProxy = false;
1✔
275
      if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
1✔
276
        definition = (AbstractBeanDefinition) Optional
1✔
277
            .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
1✔
278
            .map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
1✔
279
                "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
280
        scopedProxy = true;
1✔
281
      }
282
      String beanClassName = definition.getBeanClassName();
1✔
283
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
1✔
284
          + "' mapperInterface");
285

286
      // the mapper interface is the original class of the bean
287
      // but, the actual class of the bean is MapperFactoryBean
288
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
1✔
289
      try {
290
        Class<?> beanClass = Resources.classForName(beanClassName);
1✔
291
        // Attribute for MockitoPostProcessor
292
        // https://github.com/mybatis/spring-boot-starter/issues/475
293
        definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClass);
1✔
294
        // for spring-native
295
        definition.getPropertyValues().add("mapperInterface", beanClass);
1✔
296
      } catch (ClassNotFoundException ignore) {
×
297
        // ignore
298
      }
1✔
299

300
      definition.setBeanClass(this.mapperFactoryBeanClass);
1✔
301

302
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
1✔
303

304
      boolean explicitFactoryUsed = false;
1✔
305
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
1✔
306
        definition.getPropertyValues().add("sqlSessionFactory",
1✔
307
            new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
308
        explicitFactoryUsed = true;
1✔
309
      } else if (this.sqlSessionFactory != null) {
1!
310
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
×
311
        explicitFactoryUsed = true;
×
312
      }
313

314
      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
1✔
315
        if (explicitFactoryUsed) {
1!
316
          LOGGER.warn(
×
317
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
×
318
        }
319
        definition.getPropertyValues().add("sqlSessionTemplate",
1✔
320
            new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
321
        explicitFactoryUsed = true;
1✔
322
      } else if (this.sqlSessionTemplate != null) {
1!
323
        if (explicitFactoryUsed) {
×
324
          LOGGER.warn(
×
325
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
×
326
        }
327
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
×
328
        explicitFactoryUsed = true;
×
329
      }
330

331
      if (!explicitFactoryUsed) {
1✔
332
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
1✔
333
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
1✔
334
      }
335

336
      definition.setLazyInit(lazyInitialization);
1✔
337

338
      if (scopedProxy) {
1✔
339
        continue;
1✔
340
      }
341

342
      if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
1✔
343
        definition.setScope(defaultScope);
1✔
344
      }
345

346
      if (!definition.isSingleton()) {
1✔
347
        BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
1✔
348
        if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
1!
349
          registry.removeBeanDefinition(proxyHolder.getBeanName());
1✔
350
        }
351
        registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
1✔
352
      }
353

354
    }
1✔
355
  }
1✔
356

357
  /**
358
   * {@inheritDoc}
359
   */
360
  @Override
361
  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
362
    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
1!
363
  }
364

365
  /**
366
   * {@inheritDoc}
367
   */
368
  @Override
369
  protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) {
370
    if (super.checkCandidate(beanName, beanDefinition)) {
1✔
371
      return true;
1✔
372
    } else {
373
      LOGGER.warn(() -> "Skipping MapperFactoryBean with name '" + beanName + "' and '"
1✔
374
          + beanDefinition.getBeanClassName() + "' mapperInterface" + ". Bean already defined with the same name!");
1✔
375
      return false;
1✔
376
    }
377
  }
378

379
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc