JDK 动态代理一定要有代理对象吗? 请结合 Mybatis 回答

本贴最后更新于 1825 天前,其中的信息可能已经物是人非

动态代理

有一段时间没有写文章了, 主要是回想起这两年多的时间,多多少少,每个知识点差不多都有写到了, 一时也想不起什么新鲜的知识分享给大家.
今天写动态代理,主要是在看 Mybatis 源码时,发现真得是把动态代理用的是太 6 了, 感叹之余,有一些心得,和大家分享一下.

我所理解的动态代理

其实网上对动态代理的解释有很多了,我就不赘述那些概念了, 于小刀看来, 目的只有一个,那就是可以自定义逻辑,可以添加逻辑. 在本文中,我想写的是可以自定义逻辑, 在此之前,我们先看一下通常的动态代理的代码

动态代理代码

接口

/** * @author lixiang * @date 2020/6/16 **/ public interface Greet { /** * 加油的接口定义 */ public void cheer(); }

实现类

/** * @author lixiang * @date 2020/6/16 **/ public class GreetImpl implements Greet{ @Override public void cheer() { System.out.println("加油, 为了美好的明天!"); } }

代理类

/** * @author lixiang * @date 2020/6/16 **/ public class GreetProxy implements InvocationHandler { /** 被代理的对象,真正的逻辑,还是要请求这个 */ private final Greet greet; public GreetProxy(Greet greet) { this.greet = greet; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("在真正调用之前"); greet.cheer(); System.out.println("在真正调用之后"); return null; } }

Main 函数

/** * @author lixiang * @date 2020/6/16 **/ public class ProxyMain { public static void main(String[] args) { // 真正的对象 Greet greet = new GreetImpl(); // 代理对象 InvocationHandler handler = new GreetProxy(greet); // 使用的jdk代理生成代理类 Greet proxy = (Greet)Proxy.newProxyInstance(ProxyMain.class.getClassLoader(), new Class[]{Greet.class}, handler); proxy.cheer(); } }

我们在运行的时候打个断点,可以看到:
image.png

如上图所示,我们虽然把 jdk 生成的代理对象强转成了 Greet,但实际上是 Proxy 类型,运行结果如下图所示:
image.png

进入正文

上面这些代码, 是平常的增加逻辑的用法,但,今天小刀想和大家聊的是: 自定义逻辑.先看代码
接口不变,

代理类

/** * @author lixiang * @date 2020/6/16 **/ public class GreetCustomProxy implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("所谓自定义就是,这个代理类里面并没有Greet的真正实现类"); System.out.println("全都是加强的逻辑"); return null; } }

main 函数

/** * @author lixiang * @date 2020/6/16 **/ public class ProxyCustomMain { public static void main(String[] args) { InvocationHandler handler = new GreetCustomProxy(); Greet proxy = (Greet) Proxy.newProxyInstance(ProxyMain.class.getClassLoader(), new Class[]{Greet.class}, handler); proxy.cheer(); } }

运行结果如下:
image.png

全文的重点

是可以正常运行的, 这里会打破大家一个思维定式,就是代理类里面并不一定需要真正的处理对象. 可能全部都是自定义的逻辑.

源码中的应用

主要是 mybatis , 我们想一下, 在写 sql 时, 我们经常 DAO 里面都是接口和定义的方法, 然后 mapper 的 xml 里面写 SQL, 那么这两者是怎么对应起来的呢? 今天先不细讲, 只是看看动态代理的使用,要出场的是 MapperProxy
MapperProxyFactory:

protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }

我们可以看到,传入的 InvocationHandler 实际上是 mapperProxy

@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // 处理Object相关的方法 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else { // 我们的重点关注对象 return cachedInvoker(method).invoke(proxy, method, args, sqlSession); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }

cachedInvoker 通过源码,我们可以跟踪到的代码:

// 如果是接口中的default方法,则执行 if (m.isDefault()) { try { if (privateLookupInMethod == null) { return new DefaultMethodInvoker(getMethodHandleJava8(method)); } else { return new DefaultMethodInvoker(getMethodHandleJava9(method)); } } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } } else { // 其他的,就是sql语句之类的 return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); }

最终我们可以看到:

@Override public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { return mapperMethod.execute(sqlSession, args); }

然后使用 sqlSession 去执行 Sql

总结

如上 mybatis 中对动态代理的使用,并没有实现类,真是在 invoke 方法中,直接调用了 sqlSession 去执行 SQL , 刚开始看到这块时, 不是很好理解 , 要破开思维, 为什么动态代理一定要有代理对象呢? 我们也完全可以自己模拟逻辑.

来一起学习吧

欢迎各位大佬关注我的众号: java 技术大本营, 也可以加我微信, 进群一起讨论,小弟微信: best396975802

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3201 引用 • 8216 回帖
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    173 引用 • 414 回帖 • 365 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...