Mybatis(7)- 源码解析(5)*Mapper.java接口生成代理对象
我们在使用mybatis时只定义了类似 ActorMapper 的接口,并没有编写实现这个接口的类,实际上mybatis在初始化时生成了这个接口的代理对象,这篇文章分析下mybatis在何时完成的这件事。
通过 org.apache.ibatis.session.SqlSession.getMapper 方法我们可以获得对象接口的代理类,下面分析下这段代码:
ActorMapper actorMapper = session.getMapper(ActorMapper.class);
org.apache.ibatis.session.defaults.DefaultSqlSession getMapper 方法,从以下代码可知,实际执行的是 configuration 的 getMapper 方法:
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
org.apache.ibatis.session.Configuration getMapper 方法,如下 configuration调用的是 configuration 属性 MapperRegistry mapperRegistry 的方法,mapperRegistry 是在 org.apache.ibatis.builder.xml.XMLConfigBuilder 构造方法中初始化 Configuration过程中初始化的
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
org.apache.ibatis.binding.MapperRegistry
解析完 *Mapper.xml 后,MapperRegistry 中 knownMappers 属性已经填充好了对应 接口和 MapperProxyFactory 的键值对
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
// 生成 Mapper 的代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
}
org.apache.ibatis.binding.MapperProxyFactory
package org.apache.ibatis.binding;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.ibatis.binding.MapperProxy.MapperMethodInvoker;
import org.apache.ibatis.session.SqlSession;
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return this.methodCache;
}
protected T newInstance(MapperProxy<T> mapperProxy) {
// 通过JDK动态代理的方式生成代理对象
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
// 生成代理对象
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
org.apache.ibatis.binding.MapperProxy
如下:MapperProxy 实现了 InvocationHandler 接口
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -4724728412955527868L;
private static final int ALLOWED_MODES = 15;
private static final Constructor<Lookup> lookupConstructor;
private static final Method privateLookupInMethod;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperProxy.MapperMethodInvoker> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperProxy.MapperMethodInvoker> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
}
当执行 selectOneActor 方法时实际调用 MapperProxy invoke 方法,最终从 configuration 中获取 SQL 并替换参数执行
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}