本文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 MapperFactoryBeanMapperFactoryBean继承的SqlSessionDaoSupport源码分析extends SqlSessionDaoSupport implements FactoryBean { private Class mapperInterface; private boolean addToConfig = true; public MapperFactoryBean() { //intentionally empty } // 构造方法传入这个参数 public MapperFactoryBean(Class mapperInterface) { this.mapperInterface = mapperInterface; } ...... @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } @Override public Class getObjectType() { return this.mapperInterface; } ...... }
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!这个公众号,无广告!!!每日更新!!!



