美文网首页
mybatis运行原理03-mapper代理对象的获取

mybatis运行原理03-mapper代理对象的获取

作者: 布拉德老瓜 | 来源:发表于2021-03-07 14:05 被阅读0次

    上一篇文章中讲述了DefaultSqlSession的创建过程。它可以利用executor来完成crud操作、管理数据库连接和事务,也可以根据mapper类型来获取mapper代理对象。sql的执行放到后面讲,本文先记录一下mapper代理对象是如何生成的

        MemberDao mapper = sqlSession.getMapper(MemberDao.class);
    

    当我们写下getMapper(mapper.class)的时候,框架在后台做了些什么事?实际上,sqlSession本身是对mapper无感知的,所有关于mapper的信息,都在configuration属性中。所以getMapper先交给了configuration来完成。

        public <T> T getMapper(Class<T> type) {
            return this.configuration.getMapper(type, this);
        }
    

    在第一篇文章中说过,对mapper的解析完成后,以type:MapperProxyFactory(type)的形式将mapper的类型和产生对应代理对象的工厂存放到了mapperRegistry中的knowMapppers内,并将mapper的parameterMap、resultMap、statement等属性以namespace: val的形式存到了对应的HashMap里。然后在这里configuration.getMapper就将工作交给了mapperRegistry.同时,为了完成mapper与sqlSession的绑定,还将sqlSession作为参数传递了进来。

        //委派给mapperRegistry完成
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            return this.mapperRegistry.getMapper(type, sqlSession);
        }
    

    看看mapperRegistry是如何获取到mapper代理对象的吧。

    // mapperRegistry.getMapper(...)
    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 {
                    return mapperProxyFactory.newInstance(sqlSession);
                } catch (Exception var5) {
                    throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
                }
            }
        }
    

    实际上,mapperRegistry并不承担创建代理对象的职责,它的责任就是完成mapper的注册。创建代理对象交给mapperProxyFactory来完成。mapperProxyFactory内部mapperInterface用于封装mapper的类型对象,methodCache则存放方法缓存。

    public class MapperProxyFactory<T> {
        private final Class<T> mapperInterface;
        private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();
        // ······
        protected T newInstance(MapperProxy<T> mapperProxy) {
            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);
        }
    

    重点关注代理对象的创建,在这里首先调用的是 newInstance(SqlSession sqlSession)方法,然后调newInstance(MapperProxy<T> mapperProxy),最后由Proxy.newProxyInstance()生成代理对象。至于这中间做了什么,我们想想动态代理Proxy.newProxyInstance(...)是如何创建代理对象的吧。

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
    

    Proxy.newProxyInstance(loader, interfaces, ih)需要三个参数: 被代理对象的类加载器loader、被代理对象的接口类型数组Class<?> [] interfaces、定义了方法调用逻辑的InvocationHandler h.

    先思考一下:假设现在我们有一个类对象,需要创建一个能够对它的方法进行增强的代理实例对象,该对象进行方法调用的逻辑在invocationHandler中,该如何创建这个代理对象呢?
    咱也不知道哇,咱只知道动态代理...这里的invocationHandler不就有方法的实现逻辑吗?我们只需要一个对象,把该对象方法的调用关联给这个invocationHandler去做不就行了吗?
    //todo : 动态代理ref:https://zhuanlan.zhihu.com/p/60805342

    //invocationHandler.invoke(proxy, method, args)
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    }
    

    所以Proxy类就实现了这么一个功能:动态创建一个代理对象proxyObj,将对象与InvocationHandler ih关联。然后当我们在程序中使用proxyObj.method(args)的时候,实际上就交给了ih.invoke(proxyObj, method, args)去处理。

    我们现在有了前两个参数,但是当方法被调用时怎么进行处理的逻辑还没有啊。因此需要先创建一个InvocationHandler对象来,这个对象就是mapperProxy.他的构造方法很简单,就是将sqlSession, 接口和方法缓存关联进来。没什么好讲的

    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);
            }
        }
      //......
    }
    

    在newInstance(SqlSession sqlSession)时创建了mapperProxy,然后再使用Proxy.newInstance(interface.getClassLoader, new Class[]{interface}, mapperProxy)创建了一个对象,并将该对象与mapperProxy进行了关联,最后返回代理对象。如下。

    public class MapperProxyFactory<T> {
        // ······
        protected T newInstance(MapperProxy<T> mapperProxy) {
            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);
        }
    

    至此,xxxMapper = getMapper(xxxMapper.class)就结束了。我们拿到了代理对象,接下来的问题就是如何使用这个代理对象了,也就是当我们调用xxxMapper .xxxMethod()时,框架在后台做了些什么。
    to be continued.

    相关文章

      网友评论

          本文标题:mybatis运行原理03-mapper代理对象的获取

          本文链接:https://www.haomeiwen.com/subject/xmtyqltx.html