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

mybatis / spring / #1071

pending completion
#1071

push

github

web-flow
Merge pull request #786 from mybatis/renovate/spring-batch

Update spring batch to v5.0.1

809 of 905 relevant lines covered (89.39%)

0.89 hits per line

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

87.27
/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java
1
/*
2
 * Copyright 2010-2022 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.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.beans.factory.annotation.AnnotatedBeanDefinition;
31
import org.springframework.beans.factory.config.BeanDefinition;
32
import org.springframework.beans.factory.config.BeanDefinitionHolder;
33
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
34
import org.springframework.beans.factory.config.RuntimeBeanReference;
35
import org.springframework.beans.factory.support.AbstractBeanDefinition;
36
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
37
import org.springframework.beans.factory.support.RootBeanDefinition;
38
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
39
import org.springframework.core.NativeDetector;
40
import org.springframework.core.type.filter.AnnotationTypeFilter;
41
import org.springframework.core.type.filter.AssignableTypeFilter;
42
import org.springframework.util.StringUtils;
43

44
/**
45
 * A {@link ClassPathBeanDefinitionScanner} that registers Mappers by {@code basePackage}, {@code annotationClass}, or
46
 * {@code markerInterface}. If an {@code annotationClass} and/or {@code markerInterface} is specified, only the
47
 * specified types will be searched (searching for all interfaces will be disabled).
48
 * <p>
49
 * This functionality was previously a private class of {@link MapperScannerConfigurer}, but was broken out in version
50
 * 1.2.0.
51
 *
52
 * @author Hunter Presnall
53
 * @author Eduardo Macarron
54
 *
55
 * @see MapperFactoryBean
56
 *
57
 * @since 1.2.0
58
 */
59
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
60

61
  private static final Logger LOGGER = LoggerFactory.getLogger(ClassPathMapperScanner.class);
62

63
  // Copy of FactoryBean#OBJECT_TYPE_ATTRIBUTE which was added in Spring 5.2
64
  static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
65

66
  private boolean addToConfig = true;
67

68
  private boolean lazyInitialization;
69

70
  private boolean printWarnLogIfNotFoundMappers = true;
71

72
  private SqlSessionFactory sqlSessionFactory;
73

74
  private SqlSessionTemplate sqlSessionTemplate;
75

76
  private String sqlSessionTemplateBeanName;
77

78
  private String sqlSessionFactoryBeanName;
79

80
  private Class<? extends Annotation> annotationClass;
81

82
  private Class<?> markerInterface;
83

84
  private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
85

86
  private String defaultScope;
87

88
  public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
89
    super(registry, false);
90
    setIncludeAnnotationConfig(!NativeDetector.inNativeImage());
91
    setPrintWarnLogIfNotFoundMappers(!NativeDetector.inNativeImage());
92
  }
93

94
  public void setAddToConfig(boolean addToConfig) {
95
    this.addToConfig = addToConfig;
96
  }
97

98
  public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
99
    this.annotationClass = annotationClass;
100
  }
101

102
  /**
103
   * Set whether enable lazy initialization for mapper bean.
104
   * <p>
105
   * Default is {@code false}.
106
   * </p>
107
   *
108
   * @param lazyInitialization
109
   *          Set the @{code true} to enable
110
   *
111
   * @since 2.0.2
112
   */
113
  public void setLazyInitialization(boolean lazyInitialization) {
114
    this.lazyInitialization = lazyInitialization;
115
  }
116

117
  /**
118
   * Set whether print warning log if not found mappers that matches conditions.
119
   * <p>
120
   * Default is {@code true}. But {@code false} when running in native image.
121
   * </p>
122
   *
123
   * @param printWarnLogIfNotFoundMappers
124
   *          Set the @{code true} to print
125
   *
126
   * @since 3.0.1
127
   */
128
  public void setPrintWarnLogIfNotFoundMappers(boolean printWarnLogIfNotFoundMappers) {
129
    this.printWarnLogIfNotFoundMappers = printWarnLogIfNotFoundMappers;
130
  }
131

132
  public void setMarkerInterface(Class<?> markerInterface) {
133
    this.markerInterface = markerInterface;
134
  }
135

136
  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
137
    this.sqlSessionFactory = sqlSessionFactory;
138
  }
139

140
  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
141
    this.sqlSessionTemplate = sqlSessionTemplate;
142
  }
143

144
  public void setSqlSessionTemplateBeanName(String sqlSessionTemplateBeanName) {
145
    this.sqlSessionTemplateBeanName = sqlSessionTemplateBeanName;
146
  }
147

148
  public void setSqlSessionFactoryBeanName(String sqlSessionFactoryBeanName) {
149
    this.sqlSessionFactoryBeanName = sqlSessionFactoryBeanName;
150
  }
151

152
  /**
153
   * @deprecated Since 2.0.1, Please use the {@link #setMapperFactoryBeanClass(Class)}.
154
   */
155
  @Deprecated
156
  public void setMapperFactoryBean(MapperFactoryBean<?> mapperFactoryBean) {
157
    this.mapperFactoryBeanClass = mapperFactoryBean == null ? MapperFactoryBean.class : mapperFactoryBean.getClass();
158
  }
159

160
  /**
161
   * Set the {@code MapperFactoryBean} class.
162
   *
163
   * @param mapperFactoryBeanClass
164
   *          the {@code MapperFactoryBean} class
165
   *
166
   * @since 2.0.1
167
   */
168
  public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) {
169
    this.mapperFactoryBeanClass = mapperFactoryBeanClass == null ? MapperFactoryBean.class : mapperFactoryBeanClass;
170
  }
171

172
  /**
173
   * Set the default scope of scanned mappers.
174
   * <p>
175
   * Default is {@code null} (equiv to singleton).
176
   * </p>
177
   *
178
   * @param defaultScope
179
   *          the scope
180
   *
181
   * @since 2.0.6
182
   */
183
  public void setDefaultScope(String defaultScope) {
184
    this.defaultScope = defaultScope;
185
  }
186

187
  /**
188
   * Configures parent scanner to search for the right interfaces. It can search for all interfaces or just for those
189
   * that extends a markerInterface or/and those annotated with the annotationClass
190
   */
191
  public void registerFilters() {
192
    boolean acceptAllInterfaces = true;
193

194
    // if specified, use the given annotation and / or marker interface
195
    if (this.annotationClass != null) {
196
      addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
197
      acceptAllInterfaces = false;
198
    }
199

200
    // override AssignableTypeFilter to ignore matches on the actual marker interface
201
    if (this.markerInterface != null) {
202
      addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
203
        @Override
204
        protected boolean matchClassName(String className) {
205
          return false;
206
        }
207
      });
208
      acceptAllInterfaces = false;
209
    }
210

211
    if (acceptAllInterfaces) {
212
      // default include filter that accepts all classes
213
      addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
214
    }
215

216
    // exclude package-info.java
217
    addExcludeFilter((metadataReader, metadataReaderFactory) -> {
218
      String className = metadataReader.getClassMetadata().getClassName();
219
      return className.endsWith("package-info");
220
    });
221
  }
222

223
  /**
224
   * Calls the parent search that will search and register all the candidates. Then the registered objects are post
225
   * processed to set them as MapperFactoryBeans
226
   */
227
  @Override
228
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
229
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
230

231
    if (beanDefinitions.isEmpty()) {
232
      if (printWarnLogIfNotFoundMappers) {
233
        LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
234
            + "' package. Please check your configuration.");
235
      }
236
    } else {
237
      processBeanDefinitions(beanDefinitions);
238
    }
239

240
    return beanDefinitions;
241
  }
242

243
  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
244
    AbstractBeanDefinition definition;
245
    BeanDefinitionRegistry registry = getRegistry();
246
    for (BeanDefinitionHolder holder : beanDefinitions) {
247
      definition = (AbstractBeanDefinition) holder.getBeanDefinition();
248
      boolean scopedProxy = false;
249
      if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
250
        definition = (AbstractBeanDefinition) Optional
251
            .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
252
            .map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
253
                "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
254
        scopedProxy = true;
255
      }
256
      String beanClassName = definition.getBeanClassName();
257
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
258
          + "' mapperInterface");
259

260
      // the mapper interface is the original class of the bean
261
      // but, the actual class of the bean is MapperFactoryBean
262
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
263
      try {
264
        // for spring-native
265
        definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));
266
      } catch (ClassNotFoundException ignore) {
267
        // ignore
268
      }
269

270
      definition.setBeanClass(this.mapperFactoryBeanClass);
271

272
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
273

274
      // Attribute for MockitoPostProcessor
275
      // https://github.com/mybatis/spring-boot-starter/issues/475
276
      definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClassName);
277

278
      boolean explicitFactoryUsed = false;
279
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
280
        definition.getPropertyValues().add("sqlSessionFactory",
281
            new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
282
        explicitFactoryUsed = true;
283
      } else if (this.sqlSessionFactory != null) {
284
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
285
        explicitFactoryUsed = true;
286
      }
287

288
      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
289
        if (explicitFactoryUsed) {
290
          LOGGER.warn(
291
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
292
        }
293
        definition.getPropertyValues().add("sqlSessionTemplate",
294
            new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
295
        explicitFactoryUsed = true;
296
      } else if (this.sqlSessionTemplate != null) {
297
        if (explicitFactoryUsed) {
298
          LOGGER.warn(
299
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
300
        }
301
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
302
        explicitFactoryUsed = true;
303
      }
304

305
      if (!explicitFactoryUsed) {
306
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
307
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
308
      }
309

310
      definition.setLazyInit(lazyInitialization);
311

312
      if (scopedProxy) {
313
        continue;
314
      }
315

316
      if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
317
        definition.setScope(defaultScope);
318
      }
319

320
      if (!definition.isSingleton()) {
321
        BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
322
        if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
323
          registry.removeBeanDefinition(proxyHolder.getBeanName());
324
        }
325
        registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
326
      }
327

328
    }
329
  }
330

331
  /**
332
   * {@inheritDoc}
333
   */
334
  @Override
335
  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
336
    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
337
  }
338

339
  /**
340
   * {@inheritDoc}
341
   */
342
  @Override
343
  protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) {
344
    if (super.checkCandidate(beanName, beanDefinition)) {
345
      return true;
346
    } else {
347
      LOGGER.warn(() -> "Skipping MapperFactoryBean with name '" + beanName + "' and '"
348
          + beanDefinition.getBeanClassName() + "' mapperInterface" + ". Bean already defined with the same name!");
349
      return false;
350
    }
351
  }
352

353
}
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