一、mybatis流程
这里是Mybatis自己的流程,不包含spring
1、重要标签
mybatis-config.xml有3个重要标签transactionManager、mappers、dataSource标签
- dataSource存的是数据库的连接信息
- transactionManager存的是事务管理模式。事务回滚就是通过这个来选择事务类型的。
- mappers标签,主要是把配置DAO.xml关联进来,DAO.xml有个namespace标签表示这个DAO.xml所对应的DAO.java类型。
2、解析mybatis-config.xml配置文件
把mybatis-config.xml配置文件转成Java对象Configuration,DAO.xml的namespace和Mapper类绑定,在把这个Mapper类转成MapperProxyFactory对象,并存到Configuration的属性Map里面去。因为后面会通过getMapper(Type)来拿到这个MapperProxyFactory。下面看下MapperProxyFactory里面有啥
//MapperProxyFactory结构
public class MapperProxyFactory<T> {
//接口类型,也就是Mapper对应的class类
private final Class<T> mapperInterface;
//通过sqlSession,得到Mapper的代理对象
public T newInstance(SqlSession sqlSession) {
//创建一个Mapperxy对象,这个方法实现了JDK动态代理中的InvocationHandler接口
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
//产生Mapper对应的代理对象
protected T newInstance(MapperProxy<T> mapperProxy) {
//mapperInterface,说明Mapper接口被代理了,这样返回的对象就是Mapper接口的子类,
//Mapper接口里面方法被调用时,会被mapperProxy里面的invoke()方法拦截。
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
}
3、创建SqlSessionFactory
通过Configuration创建DefaultSqlSessionFactory对象(DefaultSqlSessionFactory是SqlSessionFactory实现类),DefaultSqlSessionFactory对象里面有个属性Configuration,所以DefaultSqlSessionFactory没什么就一个Configuration属性,其他的不是很重要。SqlSessionFactory内部会做一系列的事情。
1、创建JdbcTransactionFactory:通过Configuration得到标签transactionManager,把transactionManager转成TransactionFactory,TransactionFactory的实现类是JdbcTransactionFactory。
2、创建JdbcTransaction:从Configuration得到标签dataSource的属性存到JdbcTransactionFactory里面。在通过JdbcTransactionFactory创建JdbcTransaction对象,用到的条件就是标签dataSource里面的属性。
3、创建执行器CachingExecutor:通过上一步得到的JdbcTransaction对象,转成执行器,也就是CachingExecutor
4、创建SqlSession
通过Configuration + 执行器CachingExecutor来得到SqlSession,这里SqlSession的实现类用的是DefaultSqlSession。
5、通过MapperProxyFactory生成Mapper类的代理对象
通过DefaultSqlSession+Mapper.class得到该Mapper对应的MapperProxyFactory,内部是从Configuration里面的map来得到MapperProxyFactory,在通过MapperProxyFactory的newInstance方法来动态代理生成代理对象。也就是Mapper代理对象。MapperProxyFactory的newInstance方法里面会生成一个MapperProxy对象,这个很重要,Mappe接口方法执行拦截都是通过他来实现的。
6、生成MapperMethod类
拿到Mapper生成的代理类,执行目标方法,此时会被invoke方法拦截,invoke里面会生成MapperMethod对象
public class MapperMethod {
//里面存的是Mapper.xml方法和sql类型,比如getById,select类型
private final SqlCommand command;
//这个是用来生成sql的参数
private final MethodSignature method;
。。。。。
}
二、Spring整合mybatis流程
1、SqlSessionFactoryBean->SqlSessionFactory(实现类:DefaultSqlSessionFactory)
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(..);
return sqlSessionFactoryBean.getObject();
}
//至此SqlSessionFactory就是这样产生了
2、Mapper->MapperFactoryBean->反射生成MapperFactoryBean实例->初始化属性->sqlSession(实现类:SqlSessionTemplate)
注意:初始化属性包含处理@AutoWrite注解、@Value注解,像setSqlSessionTemplate方法就带了@AutoWrite注解,就会创建上面说的sqlSession
SqlSession sqlSession = new SqlSessionTemplate(sqlSessionFactory);
//SqlSessionTemplate是Mybatis-spring jar包里面的
SqlSessionTemplate属性:
1、sqlSessionFactory = sqlSessionFactory;
2、executorType = executorType
3、SqlSession sqlSessionProxy //创建SqlSession代理对象,注意这里和上面都是sqlSession但是实现类不一样,上面的SqlSession的实现类是SqlSessionTemplate。这里的SqlSession对象是代理出来的
sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
//SqlSessionInterceptor实现了了invoke方法
//当执行SqlSession的任何方法,都会走到回调方法invoke里面,这就是SqlSessionInterceptor的作用
代理对象1:被代理对象SqlSession,回调方法在SqlSessionInterceptor类里面
问题1、那么上面的SqlSession对象何时被使用?
问题2、为什么还需要SqlSessionTemplate?
重点
1、完善了MapperFactoryBean,填充好了属性,通过实例调用MapperFactoryBean.getObject()方法得到Mapper实例。
Spring把Mapper转成了MapperFactoryBean,通过MapperFactoryBean来生产Mapper的实例,就是通过getObject方法来得到Mapper的实例。下面看下getObject方法怎么做的
问题3、Mapper的真正实例是什么?
this.getSqlSession().getMapper(this.mapperInterface);
//getSqlSession就得到上面我们创建的SqlSessionTemplate对象
看下SqlSessionTemplate的getMapper方法
public <T> T getMapper(Class<T> type) {
return this.getConfiguration().getMapper(type, this);
}
public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();//拿到Mybatis的Configuration
}
最终还是通过Mybatis的Configuration调用getMapper方法
注意有个细节:在调用getMapper方法的时候传参数,第二个参数是this也就是SqlSessionTemplate对象
看下getMapper方法是怎么执行的
追踪mapperRegistry的getMapper方法
看下newInstance方法执行流程
看下MapperProxy
上面通过创建MapperProxy对象,然后再将MapperProxy对象作为参数调用下面方法创建mapperInterface代理对象
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
也就是UserInfoMapper的代理对象返回,走了这么长的流程,就是为了得到Mapper的代理对象,原来是在这里产生的。
代理对象2:被代理对象 “自定义Mapper” ,回调方法在MapperProxy类里面
回到上面的问题3,Mapper真正的实例对象,是动态代理生成的对象,被代理对象就是我们自定义的Mapper
最后把Maper的实例存到Spring容器中下面看下,当我们执行查询的时候,流程是什么?
查询:因为Mapper的实例是代理对象,当我我们调用就会进到MapperProxy的invoke方法里面
进入execute
selectOne方法
当执行sqlSessionProxy的任何方法,都会进入到SqlSessionInterceptor的invoke里面去,看下invoke里面怎么做的
包括method.invoke执行的是selectOne里面的逻辑,这些都是mybatis自己实现的
整个流程涉及的2个代理对象
1、第一个是sqlSessionTemplate里面创建的一个SqlSessionProxy
2、第二个是Mapper的代理对象
第一个代理对象为了在执行sqlSession的selectOne的方法时,为了能走到SqlSessionInterceptor的invoke方法,然后在Invoke方法里面通过调用mybatis的sqlSession去执行selectOne
第二个代理对象是为注入Spring容器中,当我们service调用Mapper里面的方法的时候,能进到代理对象的invoke方法,而这个invoke方法里面会调用sqlSessionTemplate的逻辑方法,来触发进到第一个代理对象的Invoke方法
下面介绍关于Spring为什么要加sqlSessionTemplate,最主要的是实现事务
当我们在Service方法加了@Transactional注解的时候
这个时候sqlSessionTemplate的作用就提现出来了
关于上面的代码执行了分两步,第一个是目标方法,第二个是invoke方法
先进入代理类的invoke方法
在执行目标方法
当没哟开启事务时,Spring是默认不提交,如果未开启是会自动提交的。