Mybatis集成Spring原理
Mybatis-Spring集成原理
- 通过定义BD来最终集成到spring中
- 由于mapper文件时接口 所以没法直接通过class名来定义BD,需要通过动态代理生成代理类 所以可以考虑用FactoryBean来自定义Bean的具体实现
- 为了保证FactoryBean的通用性,不需要为每一个Mapper编写一个factoryBean 所以可以通过泛型来解决
- 考虑到应该动态添加BD到spring中 所以需要用到Import技术来动态添加DB
- 最后需要用到scan技术来扫描指定包下的所有接口,生成代理对象,定义为BD,最终通过ImportBeanDefinitionRegistrar 注册到Spring中
引入依赖
org.mybatis mybatis-spring 1.2.2 Spring启动
ApplicationContext ctx = new ClassPathXmlApplicationContext(“bean.xml”);Spring配置文件
- 创建MapperScannerConfigurer
这个扫描器继承了spring的ClassPathBeanDefinitionScanner
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
判断包扫描路径下的Interface
sbd.getPropertyValues().add(“mapperInterface”, definition.getBeanClassName());
sbd.setBeanClass(MapperFactoryBean.class);
sbd.getPropertyValues().add(“sqlSessionFactory”, this.sqlSessionFactory);
将bd注入到beanfactory
再看看SqlSessionFactoryBean
两次赋值:
setDataSource
setMapperLocations
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.dataSource, “Property ‘dataSource’ is required”);
Assert.notNull(this.sqlSessionFactoryBuilder, “Property ‘sqlSessionFactoryBuilder’ is required”);
this.sqlSessionFactory = this.buildSqlSessionFactory();
}
执行afterPropertiesSet
buildSqlSessionFactory()
首先通过一个关键字new创建了对象Configuration,这个对象是mybatis框架的一个核心类,在这里我们不做详细介绍,以后再剖析。
接着又创建了new SpringManagedTransactionFactory(),后面介绍这个类的作用,此处略过。
接着继续创建new Environment(this.environment, this.transactionFactory, this.dataSource),这个Environment类中持有事物工厂和数据源的引用。
接下来就是创建XMLMapperBuilder对象,并且调用了xmlMapperBuilder.parse()方法,这个方法的详细,不在此分析,也不是我们这篇文章要记录的重点,否则会偏离我们的主题,
parse()这个方法就是在解析mapperLocation变量所代表的就是mybatis的一个xml配置文件
xmlMapperBuilder.parse()方法执行完成之后,调用this.sqlSessionFactoryBuilder.build(configuration),这个sqlSessionFactoryBuilder 构造器在哪儿创建的呢?
其他它就是SqlSessionFactoryBean的一个私有类变量,初始化SqlSessionFactoryBean的时候,就实例化了这个sqlSessionFactoryBuilder。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
总结起来,就是创建了几个对象,依次是mybatis的核心类Configuration、spring和mybatis集成的事物工厂类
SpringManagedTransactionFactory、mybatis的Environment类、mybatis的DefaultSqlSessionFactory类,同时还完成了对mybatis的xml文件解析,并将解析结果封装在Configuration类中。
执行onApplicationEvent方法
调用完上面的afterPropertiesSet方法之后,第二个被调用的就是onApplicationEvent方法,这个方法的调用时机是,spring容器初始化完成之后,该方法是接口ApplicationListener
public void onApplicationEvent(ApplicationEvent event) {
if (this.failFast && event instanceof ContextRefreshedEvent) {
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
}
}
调试发现this.failFast这个变量的值是false,所以这个方法不会执行,在此就不做重点分析了。
上面两个标签的解析过程分析完成,现在有两个问题没有答案,第一个是basePackage基础包下面扫描出来的mapper接口怎么实例化的?第二个是这两个标签创建出来的对象怎么配合使用的?
@Service
public class AuthUserServiceImpl implements IAuthUserService {
@Resource
private AuthUserMapper authUserMapper;
public AuthUserServiceImpl(){
logger.info("创建 com.test.bean.AuthUserServiceImpl");
}
}
首先根据这个mapper的名字从spring的BeanFactory中获取它的BeanDefinition,再从BeanDefinition中获取BeanClass,AuthUserMapper对应的BeanClass就是MapperFactoryBean,
这是为什么呢?在上面分析的内容中提到过,也就是在创建MapperScannerConfigurer对象的时候设置的。
MapperFactoryBean对象的属性设置完成之后,就调用它的getObject()方法,来获取authUserMapper对应的实现类,从上面图中可以看出来,最后返回的就是一个代理类,这个代理类使用jdk的动态代理创建出来的。
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
这个MapperProxy类就是InvocationHandler的实现类:
public class MapperProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
}
程序在调用authUserMapper对象的某个方法的时候,就会调用到MapperProxy对象的invoke()方法,去完成对数据库的操作。