spring集成Mybatis简单介绍
- 本文mybatis以Mybatis-Spring 1.3.2为主!
- 集成的核心思路:成为spring的Bean。
- 集成的实现方式:基于spring的FactoryBean。
Mybatis-Spring 1.3.2集成的思路
- 通过@MapperScan导入了MapperScannerRegistrar类。
- MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法 。
- 在registerBeanDefinitions方法中定义了一个ClassPathMapperScanner对象,用来扫描mapper。
- 设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中默认是不会扫描接口的。
- 同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component。我们重写所有的接口不加@Component也可以被扫描到。
- 通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition。
- 把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType(用来读取set方法)。
- 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean。
- 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean。
- sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生。
- MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。
- 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性 。
- 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象 。
- 到这里,有了Mybatis的代理对象,并且通过FactoryBean将其放入到spring的Bean中。
Mybatis-Spring 2.0.6集成的思路
- 通过@MapperScan导入了MapperScannerRegistrar类。
- MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法。
- 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition。
- 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法。
- 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描
- 剩余逻辑和1.3.2版本一样。
// 可以通过@Bean的方式注入
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
erScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.zhangwei");
return mapperScannerConfigurer;
}
源码分析:@MapperScan
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class) // 导入了MapperScannerRegistrar类
@Repeatable(MapperScans.class)
public @interface MapperScan {
// 传入的要扫描的包路径
String[] value() default {};
......
}
MapperScannerRegistrar源码分析
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
// spring启动会调用的方法
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 得到MapperScan的注解信息
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
......
// 默认使用MapperFactoryBean
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
......
// 设置被扫描到的都可以成为Bean:IncludeFilter直接返回true
scanner.registerFilters();
// 进行扫描。这里面扫描完成后会设置definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);去保证MapperFactoryBean的set方法可以执行。
// definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());使用构造方法,指定一个参数的!
// definition.setBeanClass(this.mapperFactoryBean.getClass());设置具体的Beanclass
scanner.doScan(StringUtils.toStringArray(basePackages));
}
}
让spring可以扫描接口
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
// 重新方法,让接口也可以成为Bean
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
}
MapperFactoryBean源码分析
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true;
public MapperFactoryBean() {
//intentionally empty
}
// 构造方法传入这个参数
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
......
/**
* Bean的对象为sqlSession.getMapper去获取的Mybatis的代理对象
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
/**
* 返回的类型为前面BeanDefinition传入的类型
*/
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
......
}
MapperFactoryBean继承的SqlSessionDaoSupport源码分析
/**
* MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set 方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是 根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的 bean或者SqlSessionTemplate类型的bean。
*/
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSession sqlSession;
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSession = sqlSessionTemplate;
this.externalSqlSession = true;
}
// 将sqlSession进行依赖注入。之前代码设置为AUTOWIRE_BY_TYPE进行触发。
public SqlSession getSqlSession() {
return this.sqlSession;
}
}
结束语
- 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
- 关注公众号,可以让你对MySQL、并发编程、spring源码有深入的了解!
- 关注公众号,后续持续高效的学习JVM!
- 这个公众号,无广告!!!每日更新!!!