深入剖析 mybatis 原理(三)如何整合Spring 没有他的参考,我不可能做出这篇理解。
@MapperScan.jpg
注册的那些MapperFactoryBean(是一种FactoryBean)会在spring初始化时调用其getObject
方法生成具体Bean。(关于FactoryBean,可参考Spring源码学习--FactoryBean实现原理)
mapperFactory初始化.jpg
但在MapperFactoryBean实例化后,调用getObject
之前,由于MapperFactoryBean本身也是个Bean,spring框架会调用setSqlSessionFactory和setSqlSessionTemplate设置其属性。以便之后的getObject
使用。
// SqlSessionDaoSupport.java
// MapperFactoryBean继承了SqlSessionDaoSupport
// 设置sqlSessionFactory, 顺便自动设置sqlSessionTemplate。所以如果没设定sqlSessionTemplate也没关系。
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
}
// 可以手动设置
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
在之后的getObject
中:
getSqlSession
就用到了刚才设置的sqlSessionTemplate。getMapper(this.mapperInterface)
在深入剖析 mybatis 原理(二)已经分析过了
,该方法最终会返回Mapper代理类对象。// MapperFactoryBean.java
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
// SqlSessionDaoSupport.java
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
你可能会问,那这个MapperFactoryBean.getObject何时会调用呢?
还记得@mapperScan注册MapperFactoryBean时的ClassPathMapperScanner.processBeanDefinitions
方法吗:
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + beanClassName + "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBeanClass);
// ...
}
}
}
用途有两个:
xx.xx.xxMapper
,但实际的Bean是MapperFactoryBean。
在springboot初始化的过程中,会对每个Bean调用getBean
进行实例化,因此MapperFactoryBean.getObject也会被调用。xx.xx.xxMapper
找Bean,然后调用beanFactory.getBean(beanName);
。但根据这个名字找到的其实是刚才注册的MapperFactoryBean。由于它是FactoryBean,对其调用getBean
只会触发getObject
,从而返回代理类对象。
要验证@Autowired在Mapper上的作用,请自行调试,可参考后文。AutowiredAnnotationBeanPostProcessor.inject
中的Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
打断点调试。beanName
名字是包含了Mapper的Service或Controller时,进入该方法。
然后跟随进入result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
->instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
->beanFactory.getBean(beanName);
。进入调试。熟悉BeanFactory.getBean的人应该不用调试就知道,这个名字对应的Bean是MapperBeanFactory,也就是一个BeanFactory,所以最终该方法beanFactory.getBean(beanName);
返回的是MapperFactoryBean的getObject的返回值。这个getObject就是刚才分析的用途啦!getSqlSession().getMapper(this.mapperInterface);
了。@Autowired SomeMapper mapper;
上注入了一个Mapper代理类,该代理类会将所有数据库请求都移交给底层的SqlSession操作。getSqlSession
方法,其内部在需要的时候调用session = sessionFactory.openSession(executorType);
获取新的session,其实也就在开启新的连接。
也就是说SqlSessionTemplate的数据库操作会被SqlSessionInterceptor.invoke所拦截,每次操作前都要获取到SqlSession(实际类型是DefaultSqlSession),这个SqlSession: