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

mybatis / spring / #1227

14 Sep 2023 05:15PM CUT coverage: 89.622%. Remained the same
#1227

Pull #854

github

web-flow
Update dependency net.bytebuddy:byte-buddy-agent to v1.14.8
Pull Request #854: Update dependency net.bytebuddy:byte-buddy-agent to v1.14.8

829 of 925 relevant lines covered (89.62%)

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

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

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

67
  private boolean addToConfig = true;
1✔
68

69
  private boolean lazyInitialization;
70

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

87
  private String defaultScope;
88

89
  public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
90
    super(registry, false);
1✔
91
    setIncludeAnnotationConfig(!AotDetector.useGeneratedArtifacts());
1✔
92
    setPrintWarnLogIfNotFoundMappers(!NativeDetector.inNativeImage());
1✔
93
  }
1✔
94

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

232
    if (beanDefinitions.isEmpty()) {
1✔
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);
1✔
239
    }
240

241
    return beanDefinitions;
1✔
242
  }
243

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

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

271
      definition.setBeanClass(this.mapperFactoryBeanClass);
1✔
272

273
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
1✔
274

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

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

289
      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
1✔
290
        if (explicitFactoryUsed) {
1✔
291
          LOGGER.warn(
×
292
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
×
293
        }
294
        definition.getPropertyValues().add("sqlSessionTemplate",
1✔
295
            new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
296
        explicitFactoryUsed = true;
1✔
297
      } else if (this.sqlSessionTemplate != null) {
1✔
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) {
1✔
307
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
1✔
308
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
1✔
309
      }
310

311
      definition.setLazyInit(lazyInitialization);
1✔
312

313
      if (scopedProxy) {
1✔
314
        continue;
1✔
315
      }
316

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

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

329
    }
1✔
330
  }
1✔
331

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

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

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