上文中我们初略的了解了mybatis源码工作流程 但是它是如何集成到spring中来工作的 先看示例代码:
applicationMybatis.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
<!-- 配置spring-mybatis.xml -->
<context:property-placeholder location="classpath:config/application.properties"/>
<!-- 配置数据库连接池 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置MyBatis的SessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- <property name="mapperLocations" value="classpath:mapper/*.xml"/> -->
</bean>
<bean id="myMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.lgj.mybatis.xml.dao.MyMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
</beans>
dao:
public interface MyMapper {
@Select("select now() ")
String getDate();
@Insert("insert into test values ('我输入的测试啊啊啊')")
void insertTest();
}
启动main方法:
public class MybatisTest {
@SuppressWarnings("resource")
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationMybatis.xml");
MyMapper bean = applicationContext.getBean(MyMapper.class);
System.out.println(bean.getDate());
bean.insertTest();
}
}
spring的启动流程我们暂不关注
我们看到MyMapper的bean配置在xml中
class是org.mybatis.spring.mapper.MapperFactoryBean 是一个fatorybean
factorybean在spring启动时候会创建两个实例 一个是它本身 一个是getObject方法 创建出来的示例 直接看getObject源码
public T getObject() throws Exception {
//1、getSqlSession 拿到sqlSession对象
//2、getMapper 生成mapper的代理类
return getSqlSession().getMapper(this.mapperInterface);//mapperInterface在xml中传入
}
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;//其实就是传入的sqlSessionTemplate对象 也就是我们可以传入sqlSessionTemplate对象 但xml中我们传入的是sqlSessionFactory 查看父类
}
org.mybatis.spring.support.SqlSessionDaoSupport.setSqlSessionFactory(SqlSessionFactory)
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);//通过sqlSessionFactory 创建了sqlSessionTemplate 对象
}
}
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);//创建了SqlSessionTemplate实例
}
也就是getSqlSession拿到了SqlSessionTemplate对象 getMapper也是它的方法
org.mybatis.spring.SqlSessionTemplate.getMapper(Class<T>)
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);//type就是interface this是SqlSessionTemplate对象
}
public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();//也就是sqlSessionFactory的Configuration对象 这个对象哪里创建的 后面会说
}
org.apache.ibatis.session.Configuration.getMapper(Class<T>, SqlSession)
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);//通过mapperRegistry 拿到mapper sqlSession是SqlSessionTemplate
}
org.apache.ibatis.binding.MapperRegistry.getMapper(Class<T>, SqlSession)
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);//通过mapperProxyFactory实例化 sqlSession是SqlSessionTemplate
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
org.apache.ibatis.binding.MapperProxyFactory.newInstance(SqlSession)
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);//创建代理类MapperProxy
return newInstance(mapperProxy);//实例化方法
}
org.apache.ibatis.binding.MapperProxyFactory.newInstance(MapperProxy<T>)
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);//我们可以查看mapperProxy的增强方法 invoke方法 mapperProxy就是MapperProxy对象
}
org.apache.ibatis.binding.MapperProxy.invoke(Object, Method, Object[])
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {//方法是Object的方法
return method.invoke(this, args);
} else {//走else
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
org.apache.ibatis.binding.MapperProxy.cachedInvoker(Method)
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return MapUtil.computeIfAbsent(methodCache, method, m -> {
if (m.isDefault()) {//java8的default方法 走else
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));//创建PlainMethodInvoker对象 查看他的invoke方法
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
org.apache.ibatis.binding.MapperProxy.PlainMethodInvoker.invoke(Object, Method, Object[], SqlSession)
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);//执行execute方法
}
org.apache.ibatis.binding.MapperMethod.execute(SqlSession, Object[])
public Object execute(SqlSession sqlSession, Object[] args) {//我们以select为例
Object result;
switch (command.getType()) {
case INSERT: {//insert
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {//update
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {//delete
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);//最终调用了sqlSession的selectOne 但是这个sqlSession是sqlSessionTemplate对象 我们要看其中的selectOne方法
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
最终的查询方法调用了org.mybatis.spring.SqlSessionTemplate.selectOne(String, Object)方法
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.selectOne(statement, parameter);//sqlSessionProxy在构造函数中被创建
}
org.mybatis.spring.SqlSessionTemplate.SqlSessionTemplate(SqlSessionFactory)
org.mybatis.spring.SqlSessionTemplate.SqlSessionTemplate(SqlSessionFactory, ExecutorType)
org.mybatis.spring.SqlSessionTemplate.SqlSessionTemplate(SqlSessionFactory, ExecutorType, PersistenceExceptionTranslator)
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());//sqlSessionProxy 是一个代理类 增强方法位于SqlSessionInterceptor中 查看它的invoke方法
}
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor.invoke(Object, Method, Object[])
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);//拿到Mybatis真正的sqlSession对象 DefaultSqlSession
try {
Object result = method.invoke(sqlSession, args);//method是selectOne 反射调用DefaultSqlSession的selectOne方法
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {//事务判断
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;//返回结果
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
总结一下:
1、MapperFactoryBean作用
1.1、创建了sqlSession对象:SqlSessionTemplate
1.2、实现了InitializingBean afterPropertiesSet中 往configuration中添加了mapper,内部创建MapperProxyFactory 后续getMapper来创建代理类
1.3、实现了FactoryBean ,getObject方法生成了代理类的bean:MapperProxy
2、SqlSessionTemplate作用
2.1、代理DAO MapperProxy在执行方法时候 通过command类型找到SqlSessionTemplate中对应的方法
2.2、SqlSessionTemplate创建了代理对象sqlSessionProxy,其中的增强器SqlSessionInterceptor的invoke才是真正的执行方法
2.3、SqlSessionInterceptor中 拿到MyBatis的DefaultSqlSession对象 通过反射调用其中的方法
2.4、通过反射调用mybatis中的DefaultSqlSession对应方法
3、SqlSessionFactoryBean对象
3.1、实现了FactoryBean getObject创建了SqlSessionFactory对象
3.2、实现了InitializingBean afterPropertiesSet 创建sqlSessionFactory
3.3、创建了Configuration对象
3.4、设置了传入的dataSource
3.5、解析所有的mybatis的xml文件装载到Configuration当中
3.6、创建DefaultSqlSessionFactory工厂 来创建DefaultSqlSession