MyBatis Spring 整合,mapper介面@repository有時候卻不用寫的原因(MyBatis Spring 整合原始碼解析)
之前看了Spring原始碼之後,對Spring+MyBatis專案有個疑問,Dao層的mapper介面上@repository有時候卻不用寫,難道mapper包掃描生成代理類發生在service層的依賴注入之前嗎?要不然service層的類@Autowired mapper介面時會找不到例項啊?
後面讀了MyBatis Spring 整合原始碼MapperScannerConfigurer後解決了疑問。
先看一下MapperScannerConfigurer
Mybatis MapperScannerConfigurer 自動掃描 將Mapper介面生成代理注入到Spring
MapperFactoryBean的出現為了代替手工使用SqlSessionDaoSupport或SqlSessionTemplate編寫資料訪問物件(DAO)的程式碼,使用動態代理實現。
比如下面這個官方文件中的配置:
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
org.mybatis.spring.sample.mapper.UserMapper是一個介面,我們建立一個MapperFactoryBean例項,然後注入這個介面和sqlSessionFactory(mybatis中提供的SqlSessionFactory介面,MapperFactoryBean會使用SqlSessionFactory建立SqlSession)這兩個屬性。
之後想使用這個UserMapper介面的話,直接通過spring注入這個bean,然後就可以直接使用了,spring內部會建立一個這個介面的動態代理。
當發現要使用多個MapperFactoryBean的時候,一個一個定義肯定非常麻煩,於是mybatis-spring提供了MapperScannerConfigurer這個類,它將會查詢類路徑下的對映器並自動將它們建立成MapperFactoryBean。
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="org.mybatis.spring.sample.mapper" /> </bean>
這段配置會掃描org.mybatis.spring.sample.mapper下的所有介面,然後建立各自介面的動態代理類。
與Spring整合可以分為3個步驟.
1. 把Java類對應的Mapper介面類納入Spring中的IOC容器管理。
2. 把Java類對應的XML名稱空間新增到Mybatis中的Configuration類中的mapperRegistry(用於管理Mybatis的Mapper).
3. 使用Spring中的IOC容器擴充套件FactoryBean獲取到Mapper的例項。(第一步納入Spring只是介面)
下面看一下MapperScannerConfigurer原始碼:
先看一下類的繼承關係
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor,
InitializingBean, ApplicationContextAware, BeanNameAware
實現了BeanDefinitionRegistryPostProcessor介面
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}
BeanDefinitionRegistryPostProcessor介面繼承了BeanFactoryPostProcessor
BeanDefinitionRegistryPostProcessor spring官方解釋是:允許在正常的BeanFactoryPostProcessor檢測開始之前註冊更多的自定義bean。特別是,BeanDefinitionRegistryPostProcessor可以註冊更多的bean定義,然後定義BeanFactoryPostProcessor例項。也就是說可以藉此方法實現自定義的bean。
BeanDefinitionRegistryPostProcessor繼承了BeanFactoryPostProcessor, 也就是說想實現自定義的bean 可以實現BeanDefinitionRegistryPostProcessor或者BeanFactoryPostProcessor中的方法。
下面看一下MapperScannerConfigurer中實現BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (this.processPropertyPlaceHolders) {
this.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, ",; \t\n"));
}
scan方法呼叫的是類ClassPathBeanDefinitionScanner的scan方法
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
this.doScan(basePackages);
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
String[] var3 = basePackages;
int var4 = basePackages.length;
for(int var5 = 0; var5 < var4; ++var5) {
String basePackage = var3[var5];
Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
Iterator var8 = candidates.iterator();
while(var8.hasNext()) {
BeanDefinition candidate = (BeanDefinition)var8.next();
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
}
if (this.checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
this.registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
第三行this.doScan(basePackages)呼叫的並不是本類的doScan方法,而是ClassPathMapperScanner的doScan方法
兩個類的關係:
class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
Iterator i$ = beanDefinitions.iterator();
while(i$.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)i$.next();
GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
definition.setBeanClass(MapperFactoryBean.class);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(2);
}
}
}
return beanDefinitions;
}
第二行Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);呼叫的又是類ClassPathBeanDefinitionScanner的doScan方法