Mybatis:Mapper接口编程原理分析(四)

在上一篇文章Mybatis:Mapper接口编程原理分析(三)中,已经获取到了 mapper 接口的代理,而且也知道它使用的 JDK 动态代理。而实现 InvocationHandler 接口的类是 MapperProxy,因此接下来分析 MapperProxy。还是老规矩,通过查看它的源码。

  • MapperProxy
public class MapperProxy implements InvocationHandler {
    private final SqlSession sqlSession;
    // 被代理接口的类型对象
    private final Class mapperInterface;
    // 被代理接口中方法的缓存,由MapperProxyFactory传递过来
    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 类中声明的方法不需要额外处理。如 hashCode 、toString
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
            
            // java8 支持在接口中定义 default 方法
            if (this.isDefaultMethod(method)) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
        // 对方法进行缓存
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        // 核心的地方就在这,此时才是真正对 SqlSession 进行的包装调用
        return mapperMethod.execute(this.sqlSession, args);
    }

    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
            this.methodCache.put(method, mapperMethod);
        }

        return mapperMethod;
    }

    @UsesJava7
    private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
        Constructor constructor = Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
        if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
        }

        Class declaringClass = method.getDeclaringClass();
        return ((Lookup)constructor.newInstance(declaringClass, Integer.valueOf(15))).unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
    }

    private boolean isDefaultMethod(Method method) {
        return (method.getModifiers() & 1033) == 1 && method.getDeclaringClass().isInterface();
    }
}

你可能感兴趣的:(Mybatis:Mapper接口编程原理分析(四))