一, 背景
最近在工作之余, 把开 mybatis 的源码看了下, 决定自己手写个简单版的. 实现核心的功能即可. 写完之后, 执行了一下, 正巧在 mybatis 对 Mapper 接口的动态代理这个核心代码这边发现一个问题. 正好再回头看了下 jdk 的动态代理发现问题所在.
二, 问题
问题所在, 当我用 SqlSession.getMapper() 方法来获取 Mapper 的代理类的时候, 发现这个代理对象所展示的 toString() 是个 null. 如下图
而 debug 了一下 mybatis 的源码发, 发现是有值, 并且的确是 new 的 MapperProxy 类型的对象
三, 回头看 jdk 动态代理
当我在排查问题的时候, 无意中发现, 在执行 (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); 方法时, 竟然会抛出 mapperProxy 类的 invoke 方法中的异常, 但是这个时候程序还没有走到任何代理的方法.
回头找了下之前学着写的动态代理的例子, 并在 invoke 方法里面输出了 method. 如下:
启动 main 方法, 发现打印的内容:
这时候才发现, 在实例化代理类的时候, 会调用一次 invoke() 方法, 并且此时调用方法的 method 参数是 Object.toString() . 看到这发现了两点
invoke 方法在实例化代理的时候会调用一次, 如果这个方法有对全局的公共变量做修改的话, 会存在隐患的问题
代理类的 toString 最后出来的类的名称就是调用这个方法得到的. 将代码中 result 输出就是对应的代理类.
四, 发现并解决问题
回到自己的手写的 mybatis 源码中, 自然就定位到了如下这个地方:
对比一下 mybatis 的源码
正是红框中的代码, 自己手写的时候认为代理的方法的声明类都是定义的 mapper 接口, 这边的判断其实没有必要, 也走不到. 然而从第三点中可以知道, 在 new 代理类的时候传入的 Method 为 toString 方法正好进了这个分支, 并且调用了当前 MapperProxy 实例的 toString 方法, 返回了代理类的名称, 正式之前所缺失的. 把这段代码加上就 OK 了
来源: https://www.cnblogs.com/yanbincn/p/8779606.html