在上一篇《Spring整合MyBatis原理-@MapperScan》中,我们知道mybatis-spring
框架的@MapperScan
会将扫描包下的所有接口定义成一个MapperFactoryBean
类型的BeanDefinition
注册到IoC容器中。那么今天我们就来研究一下MapperFactoryBean
基本结构
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
}
从类图中可以看到,
MapperFactoryBean
是一个FactoryBean
,因此其主要逻辑就在getObject
中。
MapperFactoryBean#getObject()
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
getSqlSession()
:这个方法在其父类SqlSessionDaoSupport
中实现,返回一个SqlSessionTemplate
调用 SqlSessionTemplate#getMapper
SqlSessionDaoSupport#getSqlSession
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
这个方法没多大意义,直接返回属性,但是这个属性何时被赋值呢?
属性何时被赋值的问题,我们要回到MapperFactoryBean
如何定义的代码看一下了
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));
definition.setBeanClass(this.mapperFactoryBeanClass);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory",new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate",new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}
}
上面这段代码是
ClassPathMapperScanner
扫描完所有的接口,然后将接口的BeanDefinition
注册 可以看到在注册MapperFactoryBean
时,会设置SqlSessionFactory
和SqlSessionTemplate
若都没设置,也会通过byType的自动装配 那大家有疑问了?如果我只设置SqlSessionFactory
,SqlSessionTemplate
还有值吗?答案是有的,即使只设置SqlSessionFactory
,SqlSessionTemplate
也会在设置SqlSessionFactory
时被自动创建。 下面我们一起看下这段代码
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
}
}
大家需要清楚一点,通过
BeanDefinition
中的PropertyValues
配置属性和byType的自动装配。Spring在填充属性时会走其setXxx方法,那么我们看下setSqlSessionFactory
如何实现 它会调用createSqlSessionTemplate
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
大家看到这里是不是略懂了,若只设置
SqlSessionFactory
,SqlSessionTemplate
也会自动的被创建
SqlSessionTemplate#getMapper
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
}
调用
SqlSessionTemplate#getMapper
方法,其入参是mapperInterface
就是XxxxMapper.class接口类型
public class SqlSessionTemplate implements SqlSession, DisposableBean {
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
@Override
public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();
}
}
最终从
SqlSessionFactory
的Configuration
中获取Mapper
. 还要注意一点获取的Mapper
传的SqlSession
是this,即SqlSessionTemplate
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
小总结
MyBatis会将Mapper接口生成代理对象存放到mapperRegistry,这段逻辑属于MyBatis框架原理的内容。 通过上文中的代码解读,我们通过Spring框架获取的Mapper接口,内部都拥有一个
SqlSessionTemplate
mybatis-spring
通过SqlSessionTemplate
获取SqlSessionFactory
,从SqlSessionFactory
获取Configuration
,从Configuration
中的mapperRegistry
获取到Mapper代理对象
。Mapper代理对象
中再设置SqlSessionTemplate
我们注入的Mapper接口,就是Mapper接口的代理对象
(是由MyBatis框架生产的代理对象)
总结
这篇文章简单说了一下MapperFactoryBean
的工作原理。主要记住几个关键点
-
MapperFactoryBean
会自动用SqlSessionFactory
创建SqlSessionTemplate
-
MapperFactoryBean#getObject
返回Mapper代理对象
-
Mapper代理对象
中拥有SqlSessionTemplate
-
我们注入Mapper接口时,注入的就是接口对应的 Mapper代理对象
原文始发于微信公众号(溪溪技术笔记):Spring整合MyBatis原理-MapperFactoryBean
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/207123.html