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

动态代理

有一段时间没有写文章了, 主要是回想起这两年多的时间,多多少少,每个知识点差不多都有写到了, 一时也想不起什么新鲜的知识分享给大家.
今天写动态代理,主要是在看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

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


image

进入正文

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

代理类

/**
 * @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

全文的重点

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

源码中的应用

主要是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

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容