Mybatis整合到spring
SqlSessionTemplate整合
spring提供SqlSessionTemplate类,用于在spring中注入该类实例进行sql操作。该类实现了SqlSession接口,直接调用sqlSession的方法用于执行sql,该类持有SqlSessionFactory类,每次执行方法都会创建新的sqlSession。
- SqlSessionTemplate类结构如下图。其中SqlSessionInterceptor是内部动态代理类,sqlSessionProxy代理对象执行的是内部类SqlSessionInterceptor的invoke方法。
- sqlSessionProxy是实现了SqlSession接口的动态代理类,SqlSessionTemplate的selectOne等方法执行的实际是代理类的invoke方法
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
- 代理类SqlSessionInterceptor每次执行invoke时都会生成新的SqlSession对象。就是通过这里保证每次sqlSession线程安全的。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//每次获取新的sqlSession
SqlSession sqlSession =
SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
...
}
- 在使用时,需要每次都注入SqlSessionTemplate,为了方便,可以通过继承SqlSessionDaoSupport类。该类持有SqlSessionTemplate实例。
MapperScanConfiguration自动注入整合
通过上述SqlSessionTemplate方式还是不方便,可以通过定义Mapper接口,直接用@Autowired
方式注入后直接使用。通过这个方式是怎么得到代理对象,怎么执行对应的sql的呢?
- MapperScanConfiguration类实现了BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,在方法中扫描了Mapper类,封装成BeanDefinition,得到所有Mapper的BeanDefinition后执行BeanDefinition的setBeanClass方法,把beanClass修改为MapperFacotryBean.class,所以最终注册在BeanDefinition容器中的是MapperFacotryBean类。每个MapperFactoryBean还持有对应的Mapper类。这一步可以把无法实例化的Mapper接口变为可以实例化的MapperFactoryBean,也把对应的Mapper与后续创建的代理对象关联起来了。
//执行路径postProcessBeanDefinitionRegistry#ClassPathMapperScanner#doScan#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
//把获得的对应的Mapper接口类的BeanDefinition的类类型修改为MapperFactoryBean类型。
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
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);
}
}
- MapperFactoryBean实现了FactoryBean接口,所以在spring容器实例化阶段,实例化的bean是从getObject方法返回的对象。MapperFactoryBean继承了SqlSessionDaoSupport,可以拿到SqlSession,参看前面的Mybatis执行流程可以得到Mapper的代理对象,所以注册到IoC容器的其实是MapperProxy生成的Mapper代理对象。
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
- 在使用时,spring会注入对应的Mapper的代理对象,执行MapperProxy的invoke方法对sq进行操作。