该文章基于《Spring源码深度解析》撰写,感谢郝佳老师的奉献
和之前分析的步骤相同,我们先从工厂方法开始分析,对于Mybatis整合的工厂类就是org.mybatis.Spring.SqlSessionFactoryBean
其中有两个值得一提的接口,就是
InitializingBean接口:实现此接口的bean会在初始化时调用器afterPropertiesSet方法来进行bean的逻辑初始化,并通过configuration实例来承载每一步所获取的信息,为创建SqlSessionFactory实例作准备。
FactoryBean接口:将getBean方法转换为getObject方法,所以实际上得到的是通过onfiguration属性和getObject方法返回的初始化后的sqlSessionFactory属性
接下来时另一个配置bean MapperFactoryBean
我们通过区分两种获取XXMapper的方式可以看出一些区别:
1.单独使用Mybatis时:
XXMapper xxmapper = sqlsession.getMapper(XXMapper.class);
2.Spring整合之后
XXMapper xxmapper = (XXMapper)context.getBean(“XXMapperID”);
Mapper的类继承关系如上图所示,可以看到它和SqlSessionFactoryBean一样实现了InitializingBean接口和FactoryBean接口,这两个接口的功能就不再赘述。
InitializingBean接口:
需要注意的是InitializingBean接口的afterPropertiesSet()是由DaoSupport类实现的,在该父类的方法中实现了以下几个功能:
1.父类对于sqlSesion不为空的验证,sqlSession作为根据接口创建映射器代理的接触类一定不可以为空,而sqlSession又是在设定sqlSessionFactory属性时完成的,所以实际上将会检测:
2.映射接口的验证(sqlSession会根据接口动态的创建相应代理类,所以接口必不可少)
3.映射文件存在性验证
FactoryBean接口
源代码
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
很简单,就是将已经注册的MapperInterface返回
如果我们需要指定搜索的包路径我们需要在applicationContext.xml中添加额外的配置
"org.mybatis.Spring.mapper.MapperScannerConfigurer">
"basePackage" value="指定的搜索路径">
这时我们又会引入新的类MapperScannerConfigurer,看一下这个类的类层次结构:
其中出现了我们一直提到的接口InitializingBean接口,但是结果并没有那么顺利,这个接口的作用只是对一堆属性的验证代码。
但是我们还有一个经常出现的接口BeanFactoryPostProcessor及其子类BeanDefinitionRegistryPostProcessor接口,这两个接口都会在Spring初始化的过程中进行调用。
首先看BeanFactoryPostProcessor接口的postProcessBeanFactory方法
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
这是一个空实现,看看BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
this.processPropertyPlaceHolders();//1
}
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();//2
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));//3
}
我们首先关注this.processPropertyPlaceHolders()这个部分,这个函数有一个很重要的说明,就是BeanDefinitionRegistris方法会在应用启动时就开始调用,并且会早于BeanFactoryPostProcessors调用,这就意味着PropertyResourceConfigurers并未被加载,所以所有对属性文件的引用都会失效。所以我们需要在MapperScannerConfigurer的配置文件中显式的配置processPropertyPlaceHolders如下配置所示:
"org.mybatis.Spring.mapper.MapperScannerConfigurer>
" value="true">
//通过显示得配置引导程序进入processPropertyPlaceHolders方法
//从而显示地读取属性文件
接下来我们关注2号注解scanner.registerFilters();//2这个方法主要是根据前一段代码的属性配置生成对应的过滤器,主要处理了:
1.annotationClass属性(根据设置的annotationClass生成能够达到用户效果的过滤器)
2.markerInterface属性(目的同上)
3.全局默认处理(随上面两者的值进行改变,如果都未设定将生成默认过滤器接受所有接口文件)
4.package-info.java处理(排出package-info命名的Java文件)
最后就是注释3scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, “,; \t\n”));//3收尾的工作了,这个方法主要完成了下面的步骤:
1.首先根据传入的包信息生成绝对路径,并且生成相应的bean
2.根据前面第二步注册的过滤器来控制扫描的结果
3.根据不同类型的bean进行不同的操作