• 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

90.32
/src/main/java/org/mybatis/spring/mapper/MapperScannerConfigurer.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 static org.springframework.util.Assert.notNull;
19

20
import java.lang.annotation.Annotation;
21
import java.util.Map;
22
import java.util.Optional;
23

24
import org.apache.ibatis.session.SqlSessionFactory;
25
import org.mybatis.spring.SqlSessionTemplate;
26
import org.springframework.beans.PropertyValue;
27
import org.springframework.beans.PropertyValues;
28
import org.springframework.beans.factory.BeanNameAware;
29
import org.springframework.beans.factory.InitializingBean;
30
import org.springframework.beans.factory.config.BeanDefinition;
31
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
32
import org.springframework.beans.factory.config.PropertyResourceConfigurer;
33
import org.springframework.beans.factory.config.TypedStringValue;
34
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
35
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
36
import org.springframework.beans.factory.support.BeanNameGenerator;
37
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
38
import org.springframework.context.ApplicationContext;
39
import org.springframework.context.ApplicationContextAware;
40
import org.springframework.context.ConfigurableApplicationContext;
41
import org.springframework.core.env.Environment;
42
import org.springframework.util.StringUtils;
43

44
/**
45
 * BeanDefinitionRegistryPostProcessor that searches recursively starting from a base package for interfaces and
46
 * registers them as {@code MapperFactoryBean}. Note that only interfaces with at least one method will be registered;
47
 * concrete classes will be ignored.
48
 * <p>
49
 * This class was a {code BeanFactoryPostProcessor} until 1.0.1 version. It changed to
50
 * {@code BeanDefinitionRegistryPostProcessor} in 1.0.2. See https://jira.springsource.org/browse/SPR-8269 for the
51
 * details.
52
 * <p>
53
 * The {@code basePackage} property can contain more than one package name, separated by either commas or semicolons.
54
 * <p>
55
 * This class supports filtering the mappers created by either specifying a marker interface or an annotation. The
56
 * {@code annotationClass} property specifies an annotation to search for. The {@code markerInterface} property
57
 * specifies a parent interface to search for. If both properties are specified, mappers are added for interfaces that
58
 * match <em>either</em> criteria. By default, these two properties are null, so all interfaces in the given
59
 * {@code basePackage} are added as mappers.
60
 * <p>
61
 * This configurer enables autowire for all the beans that it creates so that they are automatically autowired with the
62
 * proper {@code SqlSessionFactory} or {@code SqlSessionTemplate}. If there is more than one {@code SqlSessionFactory}
63
 * in the application, however, autowiring cannot be used. In this case you must explicitly specify either an
64
 * {@code SqlSessionFactory} or an {@code SqlSessionTemplate} to use via the <em>bean name</em> properties. Bean names
65
 * are used rather than actual objects because Spring does not initialize property placeholders until after this class
66
 * is processed.
67
 * <p>
68
 * Passing in an actual object which may require placeholders (i.e. DB user password) will fail. Using bean names defers
69
 * actual object creation until later in the startup process, after all placeholder substitution is completed. However,
70
 * note that this configurer does support property placeholders of its <em>own</em> properties. The
71
 * <code>basePackage</code> and bean name properties all support <code>${property}</code> style substitution.
72
 * <p>
73
 * Configuration sample:
74
 *
75
 * <pre class="code">
76
 * {@code
77
 *   <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
78
 *       <property name="basePackage" value="org.mybatis.spring.sample.mapper" />
79
 *       <!-- optional unless there are multiple session factories defined -->
80
 *       <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
81
 *   </bean>
82
 * }
83
 * </pre>
84
 *
85
 * @author Hunter Presnall
86
 * @author Eduardo Macarron
87
 *
88
 * @see MapperFactoryBean
89
 * @see ClassPathMapperScanner
90
 */
91
public class MapperScannerConfigurer
1✔
92
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
93

94
  private String basePackage;
95

96
  private boolean addToConfig = true;
1✔
97

98
  private String lazyInitialization;
99

100
  private SqlSessionFactory sqlSessionFactory;
101

102
  private SqlSessionTemplate sqlSessionTemplate;
103

104
  private String sqlSessionFactoryBeanName;
105

106
  private String sqlSessionTemplateBeanName;
107

108
  private Class<? extends Annotation> annotationClass;
109

110
  private Class<?> markerInterface;
111

112
  private Class<? extends MapperFactoryBean> mapperFactoryBeanClass;
113

114
  private ApplicationContext applicationContext;
115

116
  private String beanName;
117

118
  private boolean processPropertyPlaceHolders;
119

120
  private BeanNameGenerator nameGenerator;
121

122
  private String defaultScope;
123

124
  /**
125
   * This property lets you set the base package for your mapper interface files.
126
   * <p>
127
   * You can set more than one package by using a semicolon or comma as a separator.
128
   * <p>
129
   * Mappers will be searched for recursively starting in the specified package(s).
130
   *
131
   * @param basePackage
132
   *          base package name
133
   */
134
  public void setBasePackage(String basePackage) {
135
    this.basePackage = basePackage;
1✔
136
  }
1✔
137

138
  /**
139
   * Same as {@code MapperFactoryBean#setAddToConfig(boolean)}.
140
   *
141
   * @param addToConfig
142
   *          a flag that whether add mapper to MyBatis or not
143
   *
144
   * @see MapperFactoryBean#setAddToConfig(boolean)
145
   */
146
  public void setAddToConfig(boolean addToConfig) {
147
    this.addToConfig = addToConfig;
×
148
  }
×
149

150
  /**
151
   * Set whether enable lazy initialization for mapper bean.
152
   * <p>
153
   * Default is {@code false}.
154
   * </p>
155
   *
156
   * @param lazyInitialization
157
   *          Set the @{code true} to enable
158
   *
159
   * @since 2.0.2
160
   */
161
  public void setLazyInitialization(String lazyInitialization) {
162
    this.lazyInitialization = lazyInitialization;
1✔
163
  }
1✔
164

165
  /**
166
   * This property specifies the annotation that the scanner will search for.
167
   * <p>
168
   * The scanner will register all interfaces in the base package that also have the specified annotation.
169
   * <p>
170
   * Note this can be combined with markerInterface.
171
   *
172
   * @param annotationClass
173
   *          annotation class
174
   */
175
  public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
176
    this.annotationClass = annotationClass;
1✔
177
  }
1✔
178

179
  /**
180
   * This property specifies the parent that the scanner will search for.
181
   * <p>
182
   * The scanner will register all interfaces in the base package that also have the specified interface class as a
183
   * parent.
184
   * <p>
185
   * Note this can be combined with annotationClass.
186
   *
187
   * @param superClass
188
   *          parent class
189
   */
190
  public void setMarkerInterface(Class<?> superClass) {
191
    this.markerInterface = superClass;
1✔
192
  }
1✔
193

194
  /**
195
   * Specifies which {@code SqlSessionTemplate} to use in the case that there is more than one in the spring context.
196
   * Usually this is only needed when you have more than one datasource.
197
   * <p>
198
   *
199
   * @deprecated Use {@link #setSqlSessionTemplateBeanName(String)} instead
200
   *
201
   * @param sqlSessionTemplate
202
   *          a template of SqlSession
203
   */
204
  @Deprecated
205
  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
206
    this.sqlSessionTemplate = sqlSessionTemplate;
×
207
  }
×
208

209
  /**
210
   * Specifies which {@code SqlSessionTemplate} to use in the case that there is more than one in the spring context.
211
   * Usually this is only needed when you have more than one datasource.
212
   * <p>
213
   * Note bean names are used, not bean references. This is because the scanner loads early during the start process and
214
   * it is too early to build mybatis object instances.
215
   *
216
   * @since 1.1.0
217
   *
218
   * @param sqlSessionTemplateName
219
   *          Bean name of the {@code SqlSessionTemplate}
220
   */
221
  public void setSqlSessionTemplateBeanName(String sqlSessionTemplateName) {
222
    this.sqlSessionTemplateBeanName = sqlSessionTemplateName;
1✔
223
  }
1✔
224

225
  /**
226
   * Specifies which {@code SqlSessionFactory} to use in the case that there is more than one in the spring context.
227
   * Usually this is only needed when you have more than one datasource.
228
   * <p>
229
   *
230
   * @deprecated Use {@link #setSqlSessionFactoryBeanName(String)} instead.
231
   *
232
   * @param sqlSessionFactory
233
   *          a factory of SqlSession
234
   */
235
  @Deprecated
236
  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
237
    this.sqlSessionFactory = sqlSessionFactory;
×
238
  }
×
239

240
  /**
241
   * Specifies which {@code SqlSessionFactory} to use in the case that there is more than one in the spring context.
242
   * Usually this is only needed when you have more than one datasource.
243
   * <p>
244
   * Note bean names are used, not bean references. This is because the scanner loads early during the start process and
245
   * it is too early to build mybatis object instances.
246
   *
247
   * @since 1.1.0
248
   *
249
   * @param sqlSessionFactoryName
250
   *          Bean name of the {@code SqlSessionFactory}
251
   */
252
  public void setSqlSessionFactoryBeanName(String sqlSessionFactoryName) {
253
    this.sqlSessionFactoryBeanName = sqlSessionFactoryName;
1✔
254
  }
1✔
255

256
  /**
257
   * Specifies a flag that whether execute a property placeholder processing or not.
258
   * <p>
259
   * The default is {@literal false}. This means that a property placeholder processing does not execute.
260
   *
261
   * @since 1.1.1
262
   *
263
   * @param processPropertyPlaceHolders
264
   *          a flag that whether execute a property placeholder processing or not
265
   */
266
  public void setProcessPropertyPlaceHolders(boolean processPropertyPlaceHolders) {
267
    this.processPropertyPlaceHolders = processPropertyPlaceHolders;
1✔
268
  }
1✔
269

270
  /**
271
   * The class of the {@link MapperFactoryBean} to return a mybatis proxy as spring bean.
272
   *
273
   * @param mapperFactoryBeanClass
274
   *          The class of the MapperFactoryBean
275
   *
276
   * @since 2.0.1
277
   */
278
  public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) {
279
    this.mapperFactoryBeanClass = mapperFactoryBeanClass;
1✔
280
  }
1✔
281

282
  /**
283
   * {@inheritDoc}
284
   */
285
  @Override
286
  public void setApplicationContext(ApplicationContext applicationContext) {
287
    this.applicationContext = applicationContext;
1✔
288
  }
1✔
289

290
  /**
291
   * {@inheritDoc}
292
   */
293
  @Override
294
  public void setBeanName(String name) {
295
    this.beanName = name;
1✔
296
  }
1✔
297

298
  /**
299
   * Gets beanNameGenerator to be used while running the scanner.
300
   *
301
   * @return the beanNameGenerator BeanNameGenerator that has been configured
302
   *
303
   * @since 1.2.0
304
   */
305
  public BeanNameGenerator getNameGenerator() {
306
    return nameGenerator;
×
307
  }
308

309
  /**
310
   * Sets beanNameGenerator to be used while running the scanner.
311
   *
312
   * @param nameGenerator
313
   *          the beanNameGenerator to set
314
   *
315
   * @since 1.2.0
316
   */
317
  public void setNameGenerator(BeanNameGenerator nameGenerator) {
318
    this.nameGenerator = nameGenerator;
1✔
319
  }
1✔
320

321
  /**
322
   * Sets the default scope of scanned mappers.
323
   * <p>
324
   * Default is {@code null} (equiv to singleton).
325
   * </p>
326
   *
327
   * @param defaultScope
328
   *          the default scope
329
   *
330
   * @since 2.0.6
331
   */
332
  public void setDefaultScope(String defaultScope) {
333
    this.defaultScope = defaultScope;
1✔
334
  }
1✔
335

336
  /**
337
   * {@inheritDoc}
338
   */
339
  @Override
340
  public void afterPropertiesSet() throws Exception {
341
    notNull(this.basePackage, "Property 'basePackage' is required");
1✔
342
  }
1✔
343

344
  /**
345
   * {@inheritDoc}
346
   */
347
  @Override
348
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
349
    // left intentionally blank
350
  }
1✔
351

352
  /**
353
   * {@inheritDoc}
354
   *
355
   * @since 1.0.2
356
   */
357
  @Override
358
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
359
    if (this.processPropertyPlaceHolders) {
1✔
360
      processPropertyPlaceHolders();
1✔
361
    }
362

363
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
1✔
364
    scanner.setAddToConfig(this.addToConfig);
1✔
365
    scanner.setAnnotationClass(this.annotationClass);
1✔
366
    scanner.setMarkerInterface(this.markerInterface);
1✔
367
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
1✔
368
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
1✔
369
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
1✔
370
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
1✔
371
    scanner.setResourceLoader(this.applicationContext);
1✔
372
    scanner.setBeanNameGenerator(this.nameGenerator);
1✔
373
    scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
1✔
374
    if (StringUtils.hasText(lazyInitialization)) {
1✔
375
      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
1✔
376
    }
377
    if (StringUtils.hasText(defaultScope)) {
1✔
378
      scanner.setDefaultScope(defaultScope);
1✔
379
    }
380
    scanner.registerFilters();
1✔
381
    scanner.scan(
1✔
382
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
1✔
383
  }
1✔
384

385
  /*
386
   * BeanDefinitionRegistries are called early in application startup, before BeanFactoryPostProcessors. This means that
387
   * PropertyResourceConfigurers will not have been loaded and any property substitution of this class' properties will
388
   * fail. To avoid this, find any PropertyResourceConfigurers defined in the context and run them on this class' bean
389
   * definition. Then update the values.
390
   */
391
  private void processPropertyPlaceHolders() {
392
    Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class,
1✔
393
        false, false);
394

395
    if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) {
1✔
396
      BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext).getBeanFactory()
1✔
397
          .getBeanDefinition(beanName);
1✔
398

399
      // PropertyResourceConfigurer does not expose any methods to explicitly perform
400
      // property placeholder substitution. Instead, create a BeanFactory that just
401
      // contains this mapper scanner and post process the factory.
402
      DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
1✔
403
      factory.registerBeanDefinition(beanName, mapperScannerBean);
1✔
404

405
      for (PropertyResourceConfigurer prc : prcs.values()) {
1✔
406
        prc.postProcessBeanFactory(factory);
1✔
407
      }
1✔
408

409
      PropertyValues values = mapperScannerBean.getPropertyValues();
1✔
410

411
      this.basePackage = getPropertyValue("basePackage", values);
1✔
412
      this.sqlSessionFactoryBeanName = getPropertyValue("sqlSessionFactoryBeanName", values);
1✔
413
      this.sqlSessionTemplateBeanName = getPropertyValue("sqlSessionTemplateBeanName", values);
1✔
414
      this.lazyInitialization = getPropertyValue("lazyInitialization", values);
1✔
415
      this.defaultScope = getPropertyValue("defaultScope", values);
1✔
416
    }
417
    this.basePackage = Optional.ofNullable(this.basePackage).map(getEnvironment()::resolvePlaceholders).orElse(null);
1✔
418
    this.sqlSessionFactoryBeanName = Optional.ofNullable(this.sqlSessionFactoryBeanName)
1✔
419
        .map(getEnvironment()::resolvePlaceholders).orElse(null);
1✔
420
    this.sqlSessionTemplateBeanName = Optional.ofNullable(this.sqlSessionTemplateBeanName)
1✔
421
        .map(getEnvironment()::resolvePlaceholders).orElse(null);
1✔
422
    this.lazyInitialization = Optional.ofNullable(this.lazyInitialization).map(getEnvironment()::resolvePlaceholders)
1✔
423
        .orElse(null);
1✔
424
    this.defaultScope = Optional.ofNullable(this.defaultScope).map(getEnvironment()::resolvePlaceholders).orElse(null);
1✔
425
  }
1✔
426

427
  private Environment getEnvironment() {
428
    return this.applicationContext.getEnvironment();
1✔
429
  }
430

431
  private String getPropertyValue(String propertyName, PropertyValues values) {
432
    PropertyValue property = values.getPropertyValue(propertyName);
1✔
433

434
    if (property == null) {
1✔
435
      return null;
1✔
436
    }
437

438
    Object value = property.getValue();
1✔
439

440
    if (value == null) {
1✔
441
      return null;
×
442
    } else if (value instanceof String) {
1✔
443
      return value.toString();
1✔
444
    } else if (value instanceof TypedStringValue) {
1✔
445
      return ((TypedStringValue) value).getValue();
1✔
446
    } else {
447
      return null;
×
448
    }
449
  }
450

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