Mybatis我相信大家都接触过,就不进行介绍了。 它目前是企业中最受欢迎的持久层框架,在用的过程中我们也产生很多疑问?最大的疑问莫过于mapper接口的动态代理了,这就需要我们从源码的角度去分析。
我们想使用Mybatis对数据库进行操作,必须要拿到SqlSession对象。
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession接口提供了大量对数据库进行操作的抽象方法,其中这个方法,根据传入的mapper接口的Class对象,返回其代理对象。
T getMapper(Class var1);
SqlSession使用的默认实现是DefaultSqlSession。DefaultSqlSession中持有Configuration的引用,会调用Configuration提供的getMapper方法。
DefaultSqlSession类中
public Configuration getConfiguration() {
return this.configuration;
}
public T getMapper(Class type) {
return this.configuration.getMapper(type, this);
}
我们进入Configuration中,发现会调用MapperRegistry中的getMapper方法。
public T getMapper(Class type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry的getMapper方法会使用MapperProxyFactory最终生成代理类。
public T getMapper(Class type, SqlSession sqlSession) {
MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if(mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
分析
MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
在配置阶段Configuration中会调用addMapper添加mapper接口的Class对象,最终调用MapperRegistry中的addMapper方法。放入Map中,key:Class>,value:MapperProxyFactory>。
private final Map, MapperProxyFactory>> knownMappers = new HashMap();
根据Class对象,取到MapperProxyFactory实例,调用newInstance(SqlSession sqlSession)方法。
public class MapperProxyFactory {
//mapper接口的Class对象
private final Class mapperInterface;
//key:mapper接口中的抽象方法 value:代理对象所要调用的对象
private final Map methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class getMapperInterface() {
return this.mapperInterface;
}
public Map getMethodCache() {
return this.methodCache;
}
//使用jdk动态代理
protected T newInstance(MapperProxy mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
主要分析的方法
public T newInstance(SqlSession sqlSession) {
MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
MapperProxy实现了java.lang.reflect包下的InvocationHandler接口,我的理解是此接口是一个介于代理对象与被代理对象的统一管理者。
public class MapperProxy implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class mapperInterface;
private final Map methodCache;
public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//判断方法是否继承自Object,如toString、equals等方法
if(Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
//针对jdk1.8的默认实现方法
if(this.isDefaultMethod(method)) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
//获取MapperMethod
MapperMethod mapperMethod = this.cachedMapperMethod(method);
//执行相关的SQL语句操作
return mapperMethod.execute(this.sqlSession, args);
}
//若key对应的value为空,会将第二个参数的返回值存入并返回
private MapperMethod cachedMapperMethod(Method method) {
return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> {
//创建MapperMethod对象
return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
});
}
其它方法忽略.....
}
最终调用其生成代理对象。
protected T newInstance(MapperProxy mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
通过jdk提供的ProxyGenerator可以生成代理类的字节码文件。反编译如下:
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space
import java.lang.reflect.*;
import java.util.List;
import mybaits.mappers.userDao;
public final class UserDaoProxy extends Proxy
implements userDao
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public UserDaoProxy(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final List select()
{
try
{
return (List)super.h.invoke(this, m3, null);
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("mybaits.mappers.userDao").getMethod("select", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch (NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch (ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
java反编译工具