• 深化代理模式,Mybaits如何做到调用接口


    Mybaits如何做到调用接口?

    毫无疑问,动态代理,本文将透过一些源码深化代理模式

    1.源码篇

    1)生成代理

    先上测试代码,相关包扫描和Mybaits的配置文件都已经写好了

    public class MapperTest {
        @Test
        public void testMapper() throws IOException, MyException {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/spring/applicationContext-dao.xml");
            SqlSessionFactory factory = (SqlSessionFactory) context.getBean("sqlSessionFactory");
            //可DEBUG这一行,这里创建并返回了Mapper的代理
            TestMapper mapper = factory.openSession().getMapper(TbItemMapper.class);
            //发送一个查询请求,这一行揭示了如何做到调用接口
            mapper.selectByExample(new TestMapper ());
        }
    }
    

    这里不必细看,知道在哪进入即可

    public class MapperRegistry {
     public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            //检索Mapper是否注册代理工厂
            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);
                }
            }
        }
            //此处省略其他代码
    }
    

    注意重载方法,该对象已经装配好了配置文件中的接口信息,还配置了一个缓存

    • MapperProxy就是一个InvocationHandler
    • 再用他生成真正的JDK代理对象并返回
      至此动态代理对象已经生成
    public class MapperProxyFactory<T> {
        private final Class<T> mapperInterface;
        private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
    
      protected T newInstance(MapperProxy<T> mapperProxy) {
            //JDK代理
            ***return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);***
        }
       
         //DEBUG进入在这一行
        public T newInstance(SqlSession sqlSession) {
            MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
            return this.newInstance(mapperProxy);
        }
        //此处省略其他代码
    }
    

    2)方法如何调用

    invoke方法接收到方法调用,只做了两件事

    • 创建并缓存MapperMethod类
    • 调用MapperMethod.execute()方法
    //实现了InvocationHandler
    public class MapperProxy<T> implements InvocationHandler, Serializable {
        private static final long serialVersionUID = -6424540398559729838L;
        private final SqlSession sqlSession;
        private final Class<T> mapperInterface;
        private final Map<Method, MapperMethod> methodCache;
    
        public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
            this.sqlSession = sqlSession;
            this.mapperInterface = mapperInterface;
            this.methodCache = methodCache;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //如果调用了Object类的方法
            if (Object.class.equals(method.getDeclaringClass())) {
                try {
                    return method.invoke(this, args);
                } catch (Throwable var5) {
                    throw ExceptionUtil.unwrapThrowable(var5);
                }
            } else {
                //缓存并创建一个MapperMethod对象,KEY method VALUE mapperMethod
                MapperMethod mapperMethod = this.cachedMapperMethod(method);
                //把session和方法参数委托给MapperMethod
                return mapperMethod.execute(this.sqlSession, args);
            }
        }
    
        private MapperMethod cachedMapperMethod(Method method) {
            MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
            if (mapperMethod == null) {
                //用接口信息,方法信息,配置信息构造MapperMethod 
                mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
                this.methodCache.put(method, mapperMethod);
            }
    
            return mapperMethod;
        }
    }
    

    MapperMethod 类做了什么?(指令模式?)

    • 将方法调用信息包装成SqlCommand
    • 将方法xml配置信息包装成MethodSignature
    • 发送SQL语句,获取结果返回
    public class MapperMethod {
     private final MapperMethod.SqlCommand command;
        private final MapperMethod.MethodSignature method;
    
        public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
            this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
            this.method = new MapperMethod.MethodSignature(config, method);
        }
         //进入这个方法,匹配指令执行SQL
        public Object execute(SqlSession sqlSession, Object[] args) {
            Object param;
            Object result;
            if (SqlCommandType.INSERT == this.command.getType()) {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            } else if (SqlCommandType.UPDATE == this.command.getType()) {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            } else if (SqlCommandType.DELETE == this.command.getType()) {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            } else {
                if (SqlCommandType.SELECT != this.command.getType()) {
                    throw new BindingException("Unknown execution method for: " + this.command.getName());
                }
                if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                    this.executeWithResultHandler(sqlSession, args);
                    result = null;
                } else if (this.method.returnsMany()) {
                    result = this.executeForMany(sqlSession, args);
                } else if (this.method.returnsMap()) {
                    result = this.executeForMap(sqlSession, args);
                } else {
                    param = this.method.convertArgsToSqlCommandParam(args);
                    result = sqlSession.selectOne(this.command.getName(), param);
                }
            }
    
            if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
                throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
            } else {
                return result;
            }
        }
    }
    


    自然而然,这个类之后的事就和我们使用JDBC一样,拼装SQL,调用SQL,装配返回结果
    但这里的动态代理和以往我所见过的不同,以往InvocationHandler需要持有代理对象的引用,在这里却没有,因为Mapper本身并不存在实现,就是这一点引发了我的思考


    2.深化代理模式

    代理是什么?我的思想转变(不包括CGLIB)

    阶段一:初识AOP
    这个问题在我初学AOP的时候遇到,当时我的理解跟随着AOP,生成代理的目的就是给方法进行增强,在方法前后添加业务逻辑,接口,所以代理就是一种方法增强的模式,而事务很适合用这一点。毕竟就是在方法前后添加开启事务,提交事务。
    而Proxy.newProxyInstance(classloader,interface,invocationhandler)这个API,拿到手我是看不懂的。

    阶段二:印象强化
    在网上看了一些文章,理解到了静态代理,才感知到动态代理,是Java运用反射机制,沿用代理的思想的一个实现,目的就是为了解耦。
    Proxy.newProxyInstance的参数就很明显了

    • classloader和interface 运行时反射创建代理类
    • invocationhandler 持有代理对象的引用,负责转发的请求处理
      当然我以为这样就结束了

    阶段三:代理模式
    走马观花看了一遍《headfirst设计模式》,才知道代理的玩法太多了,代理更像是一个影武者(傀儡),方法增强不过是冰山一角,比如保护代理,在代理类中编写如果调用了XXX方法就抛出异常等等。

    阶段四:开拓视野
    我看的源码肯定不多,所以Mybaits中的代理着实让我开了眼界,对于没有实现类的接口也可以代理,传递方法和参数,委托自己的类来进行处理,方法信息和SQL语句绑定,调用方法就会执行相关的Statement,原来SQL语句是这么调用的,当然过程比我说的复杂得多。


    3.总结

    1.Mybaits中的Mapper接口使用动态代理来实现方法调用
    2.Mapper的动态代理并不处理方法,而是转发给MapperMethod。
    3.Mapper接口不需要实现类,但需要有相关的XML配置,因为MapperMethod根据方法匹配XML配置

  • 相关阅读:
    Python 递归
    Python 面向过程编程
    Python 协程函数
    Python-第三方库requests详解
    Python 三元表达式
    linux copy
    centos 安装软件
    mysql 权限
    mysql 权限 备份
    android 开发
  • 原文地址:https://www.cnblogs.com/haon/p/11353434.html
Copyright © 2020-2023  润新知