本篇文章是Spring整合MyBatis原理的第三篇文章,上一篇文章在这里,我们啦继续学习Spring整合MyBatis原理
Mapper接口代理对象的产生以及调用
Mapper接口代理对象的产生
我们知道在MyBatis使用中,有如下步骤
public static void main(String[] args) throws Exception {
Reader reader = Resources.getResourceAsReader('resource/mybatis-config.xml');
// 获取 SqlSessionFactory 对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 获取 SqlSession 对象
SqlSession sqlSession = sessionFactory.openSession();
PersonDao personDao = sqlSession.getMapper(PersonDao.class);
Person person = new Person('11', 'Fighter168');
personDao.save(person);
// ......省略
}
由sqlSessiogetMapper()可知,它必定会产生代理对象,那么就开始分析它的背后逻辑在Spring整合MyBatis中,由于SqlSession是线程不安全的,所以Spring提供了一个SqlSessionTemplate来代替SqlSession,SqlSessionTemplate类实现了SqlSession接口SqlSessionTemplate中的getMapper()方法如下
@Override
public T getMapper(Class type) {
return getConfiguration().getMapper(type, this);
}
@Override
public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();
}
我们继续跟近Configuration类的getMapper()方法看下里面做了什么
public T getMapper(Class type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
继续跟进MapperRegistry类中的getMapper()
@SuppressWarnings('unchecked')
public T getMapper(Class type, SqlSession sqlSession) {
// 获取已经解析完成的 Mapper 接口信息
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException('Type ' + type + ' is not known to the MapperRegistry.');
}
try {
// 实例化对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException('Error getting mapper instance. Cause: ' + e, e);
}
}
knownMappers.get()方法:获取已经解析完成了的Mapper接口信息
显然,应该跟进mapperProxyFactory.newInstance()方法
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
// 调用下面的 newInstance()
return newInstance(mapperProxy);
}
@SuppressWarnings('unchecked')
protected T newInstance(MapperProxy mapperProxy) {
// 返回代理对象(jdk动态代理)
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
从上面Proxy.newProxyInstance()的方法中,可以看出几点
这里最终返回的是一个Mapper接口对应的一个代理对象,是由MapperFactoryBean这个类来完成的,根本原因是MapperFactoryBean这个类实现了FactoryBean接口因为第二个参数只能传接口数组,所以mapperInterface是接口,也就是说数据映射器只能是接口MapperProxy类实现了InvocationHandler接口,具体的代理逻辑在它重写的invoke()方法中
Mapper接口方法的调用
Mapper接口的调用
之所以Mapper中的接口可以被调用,因为MapperProxy类实现了InvocationHandler接口,所以会调用到MapperProxy的invoke()方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果方法是Object类的方法,则直接反射执行
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 1.创建MapperMethodInvoker
// 2.将method -> MapperMethodInvoker放到methodCache缓存
// 3.调用 MapperMethodInvoker 的 invoke() 方法
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
// MapperProxy.java
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// 1.放到methodCache缓存,key为method,value为MapperMethodInvoker
return methodCache.computeIfAbsent(method, m -> {
if (m.isDefault()) {
// 2.方法为默认方法,Java8之后,接口允许有默认方法
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 {
// 3.正常接口会走这边,使用mapperInterface、method、configuration
// 构建一个MapperMethod,封装成PlainMethodInvoker
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
继续跟进MapperProxy类中的invoke()方法
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
// MapperMethod.java
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 根据命令类型执行来进行相应操作
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
// 关键部分 sqlSession.insert()
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
// 关键部分 sqlSession.update()
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
// 关键部分 sqlSession.delete()
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);
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;
}
根据不同的操作类型执行相应的操作,以sqlSessioinsert()为例跟进
sqlSessioinsert()为例跟进
进入到SqlSessionTemplate类中的insert()方法
@Override
public int insert(String statement, Object parameter) {
// 继续跟进 insert() 方法如下 2.3.
return this.sqlSessionProxy.insert(statement, parameter);
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
// 很明显,关键部分
Object result = method.invoke(sqlSession, args);
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) {
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);
}
}
}
}
继续跟进methoinvoke()方法,来到DefaultSqlSession类中
增删改查
DefaultSqlSession类中增删改查如下
@Override
public int insert(String statement, Object parameter) {
// 调用下面的 update() 方法
return update(statement, parameter);
}
@Override
public int update(String statement, Object parameter) {
try {
dirty = true;
// 从 mappedStatements 缓存拿到对应的 MappedStatement 对象,执行更新操作
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException('Error updating database. Cause: ' + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public int delete(String statement, Object parameter) {
// 调用上面的 update() 方法
return update(statement, parameter);
}
// select,以 executeForMany 为例
private Object executeForMany(SqlSession sqlSession, Object[] args) {
List result;
// 1.参数转换成sql命令参数
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
// selectList() 方法在最下面
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
// 2.执行查询操作,selectList() 方法在最下面
result = sqlSession.selectList(command.getName(), param);
}
// 3.处理返回结果
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
@Override
public List selectList(String statement, Object parameter) {
// selectList() 方法在最下面
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 从 mappedStatements 缓存中拿到对应的 MappedStatement 对象,执行查询操作
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException('Error querying database. Cause: ' + e, e);
} finally {
ErrorContext.instance().reset();
}
}
可以看出,最终都是从mappedStatements缓存中拿到对应的MappedStatement对象,执行相应的操作
Spring整合MyBatis原理流程步骤
扫描注册basePackage包下的所有的mapper接口类,将mapper接口类封装成为BeanDefinition对象,注册到spring容器中,同时会将basePackage包下的所有bean进行一些特殊处理:beanClass设置为MapperFactoryBean、bean的真正接口类作为构造函数参数传入MapperFactoryBean解析mapperLocations配置的mapper文件,将mapper文件中的每个SQL封装成MappedStatement,放到mappedStatements缓存中,key为id,例如:id为:coatguigu.mapper.ProductInfoMapper.selectByPrimaryKey,value为MappedStatement。并且将解析过的mapper文件的namespace放到knownMappers缓存中,key为namespace对应的class,value为MapperProxyFactory创建DAO的bean时,通过mapperInterface从knownMappers缓存中获取到MapperProxyFactory对象,通过JDK动态代理创建mapper接口代理对象,实现了InvocationHandler接口的类为MapperProxyDAO中的接口被调用时,通过mapper接口代理对象,调用MapperProxy的invoke方法,执行相应的增删改查操作
文章为作者独立观点,不代表观点