Mybatis 原始碼解析二、Mapper介面的代理實現過程 MapperScannerConfigurer 解析
阿新 • • 發佈:2019-02-13
一、使用包掃描的配置方式生成Mapper的動態代理物件
上一篇文章中介紹了SqlSessionFactoryBean 通過Mybatis主配置檔案生成Mapper介面的代理物件,並將其儲存到Configuration中,但是一般在開發中我們並不是採用這種方式配置Mapper介面,因為這種方式是針對單個介面進行配置的,如果有大量的Mapper介面,這樣配置就很麻煩。開發中我們通常使用包掃描的方式進行配置。例如下面的配置:
1.1 MapperScannerConfigurer 類 該類的主要作用是初始化我們配置的引數,並呼叫方法掃描配置的包。
1.2 ClassPathMapperScanner
ClassPathMapperScanner類繼承了Spring提供的ClassPathBeanDefinitionScanner類。Scan方法的實現在父類中。
其中basePackage就是我們配置的Mapper檔案所在的包,我們追蹤下程式碼來看一下,MapperScannerConfigurer是如何通過包掃描的方式將我們的Mapper儲存到Configuration。
<!-- mybatis操作介面掃描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name
="annotationClass" value="javax.annotation.Resource"/><property name="basePackage" value="com.xxx.md.xxx.xxx.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
1.1 MapperScannerConfigurer 類 該類的主要作用是初始化我們配置的引數,並呼叫方法掃描配置的包。
從上面程式碼我們可以看到真正執行掃描的是ClassPathMapperScanner類的Scan方法。
publicvoid postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)throwsBeansException{
if(this.processPropertyPlaceHolders){
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner =newClassPathMapperScanner
(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));
}
1.2 ClassPathMapperScanner
ClassPathMapperScanner類繼承了Spring提供的ClassPathBeanDefinitionScanner類。Scan方法的實現在父類中。
在執行scan()方法時,會執行doScan()方法,這個方法在ClassPathMapperScanner類中有具體實現。
publicint 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);
}
該方法首先會執行父類中的doScan()方法,父類中的doScan()方法也即Spring的包掃描,根據註解生成物件的方法,從原始碼中我們也可以看到在MapperScannerConfigure中配置的annotationClass是如何起到過濾作用的,如果不配置這個引數,會根據Spring中的預設註解生成物件,配置這個引數,我們可以指定我們自定義的註解。簡單來說Spring在生成物件的時候會有一個註解過濾器進行過濾,使用特定註解的類,才能由Spring生成物件,這也解釋了我們常用的註解起作用的過程。
publicSet<BeanDefinitionHolder> doScan(String... basePackages){
Set<BeanDefinitionHolder> beanDefinitions =super.doScan(basePackages);
if(beanDefinitions.isEmpty()){
logger.warn("No MyBatis mapper was found in '"+Arrays.toString(basePackages)+"' package. Please check your configuration.");
}else{
for(BeanDefinitionHolder holder : beanDefinitions){
GenericBeanDefinition definition =(GenericBeanDefinition) holder.getBeanDefinition();
if(logger.isDebugEnabled()){
logger.debug("Creating MapperFactoryBean with name '"+ holder.getBeanName()
+"' and '"+ definition.getBeanClassName()+"' mapperInterface");
}
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
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",newRuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed =true;
}elseif(this.sqlSessionFactory !=null){
definition.getPropertyValues().add("sqlSessionFactory",this.sqlSessionFactory);
explicitFactoryUsed =true;
}
if(StringUtils.hasText(this.sqlSessionTemplateBeanName)){
if(explicitFactoryUsed){
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate",newRuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed =true;
}elseif(this.sqlSessionTemplate !=null){
if(explicitFactoryUsed){
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate",this.sqlSessionTemplate);
explicitFactoryUsed =true;
}
if(!explicitFactoryUsed){
if(logger.isDebugEnabled()){
logger.debug("Enabling autowire by type for MapperFactoryBean with name '"+ holder.getBeanName()+"'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
return beanDefinitions;
}
我們關鍵看一下這三行程式碼,很明顯由Spring包掃描的Mapper介面新增到了MapperFactoryBean中。 1.3 MapperFactoryBean類 MapperFactoryBean類實現了FactroyBean類,其中主要起作用的方法是cheakDaoConfig()。
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
definition.setBeanClass(MapperFactoryBean.class);
- definition.getPropertyValues().add("addToConfig",this.addToConfig);
@Override