Mybatis MapperScannerConfigurer Automatic Scanning Mapper Interface Generating Agent Injection to Spring

  • 2021-07-03 00:14:07
  • OfStack

Preface

Mybatis MapperScannerConfigurer Auto Scan injects Mapper interface generation proxy into Spring Mybatis can configure MapperFactoryBean to generate Mapper interface proxy when integrated with Spring.

For example:


<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
 <property name="mapperInterface" value="com.bijian.study.dao" />
 <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

The proxy class created by MapperFactoryBean implements the UserMapper interface and is injected into the application. Because the proxy is created in the runtime environment (Runtime), the mapper specified must be an interface, not a concrete implementation class.

The above configuration has a big disadvantage, that is, when the system has a lot of configuration files, all of them need to be written manually, so the above method is already very useful.

It is not necessary to register all mappers in the XML configuration file of Spring. Instead, you can use an MapperScannerConfigurer, which will look up the mappers under the ClassPath and automatically create them as MapperFactoryBean.

To create MapperScannerConfigurer, add the following code to the configuration of Spring:


<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
 <property name="mapperInterface" value="com.bijian.study.dao" />
 <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

The basePackage property lets you set the basic package path for the mapper interface file. You can use semicolons or commas as separators to set more than 1 package path. Each mapper will be searched recursively in the specified package path.

Note that it is not necessary to specify SqlSessionFactory or SqlSessionTemplate, because MapperScannerConfigurer will create MapperFactoryBean and then assemble it automatically. However, if you use more than one DataSource, the automatic assembly may fail. In this case, you can use the sqlSessionFactoryBeanName or sqlSessionTemplateBeanName properties to set the correct bean name to use. This is how it is configured, noting that the name of bean is required, not a reference to bean, so the value attribute replaces the usual ref here.


<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />

The overall configuration is as follows:


<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource" />
	<property name="mapperLocations" value="classpath*:**/*Dao*.xml"></property>
</bean>

<!-- dao Configure  -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="com.bijian.study.dao" />
	<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>

MapperScannerConfigurer supports filtering mappers created by specified creation interfaces or annotations. The annotationClass attribute specifies the name of the annotation to look for. The markerInterface attribute specifies the parent interface to look for. If both are specified, the mapper added to the interface will match both criteria. By default, both properties are null, so all interfaces given in the base package can be loaded as mappers.

Discovered mappers will be named using the Spring default naming strategy for auto-detection components (see Spring manual 3.14. 4). That is, if no annotations are found, it uses the mapper's non-uppercase, non-fully qualified class name. But if it finds the @ Named annotation for @ Component or JSR-330, it gets the name. Note that you can configure it into org. springframework. stereotype. Component, javax. inject. Named (if you use JSE 6) or your own annotations (definitely self-annotations), so that annotations will be used as generators and name providers.

Next, let's look at the source code of MapperScannerConfigurer class to see how it is automatically scanned.


/** 
 * {@inheritDoc} 
 * 
 * @since 1.0.2 
 */ 
@Override 
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { 
 if (this.processPropertyPlaceHolders) { 
 processPropertyPlaceHolders(); 
 } 
 
 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); 
 scanner.setAddToConfig(this.addToConfig); 
 scanner.setAnnotationClass(this.annotationClass); 
 scanner.setMarkerInterface(this.markerInterface); 
 scanner.setSqlSessionFactory(this.sqlSessionFactory); 
 scanner.setSqlSessionTemplate(this.sqlSessionTemplate); 
 scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); 
 scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); 
 scanner.setResourceLoader(this.applicationContext); 
 scanner.setBeanNameGenerator(this.nameGenerator); 
 scanner.registerFilters(); 
 scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); 
} 

The Mapper interface into MapperFactoryBean code in scanner. scan method, let's follow in to see 1.


/** 
 * Perform a scan within the specified base packages. 
 * @param basePackages the packages to check for annotated classes 
 * @return number of beans registered 
 */ 
public int scan(String... basePackages) { 
 int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); 
 
 doScan(basePackages); 
 
 // Register annotation config processors, if necessary. 
 if (this.includeAnnotationConfig) { 
 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); 
 } 
 
 return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); 
} 

/** 
 * Perform a scan within the specified base packages, 
 * returning the registered bean definitions. 
 * <p>This method does <i>not</i> register an annotation config processor 
 * but rather leaves this up to the caller. 
 * @param basePackages the packages to check for annotated classes 
 * @return set of beans registered if any for tooling registration purposes (never {@code null}) 
 */ 
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { 
 Assert.notEmpty(basePackages, "At least one base package must be specified"); 
 Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>(); 
 for (String basePackage : basePackages) { 
 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); 
 for (BeanDefinition candidate : candidates) { 
  ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); 
  candidate.setScope(scopeMetadata.getScopeName()); 
  String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); 
  if (candidate instanceof AbstractBeanDefinition) { 
  postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); 
  } 
  if (candidate instanceof AnnotatedBeanDefinition) { 
  AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); 
  } 
  if (checkCandidate(beanName, candidate)) { 
  BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); 
  definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); 
  beanDefinitions.add(definitionHolder); 
  registerBeanDefinition(definitionHolder, this.registry); 
  } 
 } 
 } 
 return beanDefinitions; 
} 

Summarize


Related articles: