Spring和Mybatis整合
江粒 人气:0前言
最近读完了Spring的IOC部分的源码,受益匪浅,这篇文章讲解一下MyBatis是如何做到与Spring整合的。MyBatis是如何做到干扰Spring的生命周期,把Mapper一个个的注册到Spring容器中的将在这里揭秘。
简单猜想
因为阅读过Spring源码后对他有了一定的认识,这里可以简单盲猜一下,使用的是什么方式,在上一篇文章揭秘Autowired注解中有介绍到。我们只是向xml中写入了一行<context:annotation-config/>
配置。Spring就像BeanFatory中写入了很多的BeanPostProcessor,这里我觉得采用的功能类似。
通过定义spring.handlers文件。然后mybatis定义各种处理标签的Handler和Paser。随后通过读取配置文件中的mappers标签,去像register中注册BeanDefinition,这是其一。
第二种方法就是类似AutowiredAnnotationBeanPostProcessor的实现方式,通过Xml注册一个Bean,这个Bean继承自MergedBeanDefinitionPostProcessor,由于继承自MegedBeanDefinitionPostProcessor所以他会优先Bean运行,在此时可以像Bean工厂中添加BeanDefinition。
其实我们的目标很明确,只要我们能在Spring调用InitiazionBean方法之前去把mapper的BeanDefinition添加进Spring容器,都可以实现当前的目的。
那么接下来我们就看看MyBatis本身究竟是如何实现的吧。
案例搭建
源码地址:MyBatis整合Spring有两种方式,第一种是通过Xml,第二种是通过Mapper接口的扫描,具体的整合方法我这里就不演示了,直接看配置文件吧。其实就是application.xml有一点改动。
通过扫描接口
这里搭建一个最简单的整合方式:
正式开始
通过上方的配置文件,可以看见一个配置了一个叫scannerConfigurer,这里先去看一下这个类。
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
这个类继承了几个接口:
- BeanDefinitionRegistryPostProcessor
- 这个接口追进去,发现该接口继承自BeanFactoryPostProcessor,也就相当于spring在refresh方法中有一个方法专门去执行这类的接口。
- InitializingBean
- 在createBean的生命周期中会调用该接口的afterProperties方法。
- ApplicationContextAware
- Spring在创建该Bean时会调用setapplicationContext方法注入上下文
- BeanNameAware
- 创建是调用setBeanName方法
按照这样的一个接口被执行的顺序是,setBeanName -> setApplicationContext -> afterproperties -> postProcessBeanDefinitionRegistry
setBeanName
@Override public void setBeanName(String name) { this.beanName = name; }
这个方法很明显不是。
setApplicationContext
@Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; }
这个方法很明显,也不是。
afterProperties
@Override public void afterPropertiesSet() throws Exception { notNull(this.basePackage, "Property 'basePackage' is required"); }
这个方法是用来校验的,判断了basePackage是否为空,如果为空就throw Exception。
postProcessBeanDefinitionRegistry
@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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass); if (StringUtils.hasText(lazyInitialization)) { scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization)); } if (StringUtils.hasText(defaultScope)) { scanner.setDefaultScope(defaultScope); } scanner.registerFilters(); scanner.scan( StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }
那这里就很明显了,这里创建ClassPathMapperScanner。随后对scanner的一些配置做了一些设置。
然后就调用了registerFilters方法,字面意思也就是注册过滤器,这里就跳过吧,无非是设置一些属性,然后在后面解析的时候判断过滤条件,在循环时continue。
主要是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; }
这里这个扫描类其实是Spring中的类,ClassPathBeanDefinitionScanner,当中的scan方法。所以这里可能会有点眼熟,类似于创建Bean时,先获取一下创建之前的Bean总数,然后再获取创建之后的Bean总数,返回时减一下就知道这次创建了多少。
总结
其实到这里呢,我们就算是结束了,因为后续的包扫描,在严格意义上来讲是Spring来实现的,我后续开篇文章来讲解这个东西。
这里总结一下,正如我猜想的一样,myBatis只要在finishBeanFactoryInitialization方法之前,把Mapper的BeanDefinition塞进Spring容器中,在最后的finishBeanFactoryInitialization方法,Spring自然就会根据BeanDefinition去创建Bean了。
这里使用的方法是,注册一个BeanFactoryPostProcessor,所以这个方法会在finishBeanFactoryInitialization方法之前运行,所以这里是成功的。
加载全部内容