设为首页 加入收藏

TOP

深入理解动态代理源码(一)
2019-10-09 19:56:05 】 浏览:301
Tags:深入 理解 动态 代理 源码

前言:  早期学习了动态代理在实际开发中的使用场景和使用方法,我们也知道了最经典的mybatis的mapper就是采用动态代理来实现的,那么动态代理的背后是怎样的原理?为什么能实现动态代理?为什么动态代理只可以代理接口,而无法代理普通类?为什么动态代理需要传入类的classLoder和接口?带着这些疑问,我们来开启本期的主题:探究动态代理的内部原理。


本篇博客的目录


一:动态代理的基本使用方法


二:动态代理的内部运行过程


三:几个相关的问题


四:总结


一:动态代理的基本使用方法


 1.1:简单例子


首先我们来模拟一个简单的动态代理的过程:某歌手去参加一个晚会,需要唱歌,他在演奏的过程中需要别人来报幕:演奏开始、演奏结束,每个歌手都遵循这样的过程,在歌手进行表演的过程,穿插着主持人的开场白和结语,我们来用代码模拟这个场景:


1.2:代理接口


首先我们来定义一个singer接口表示我们将要代理的接口:


 


public interface Singer {
    /**
    * 表演
    * @param soonName
    */
    public void perform(String soonName);
}


 


1.3:接口的具体实现类


 


public class Jay implements Singer {
 
    public void perform(String soonName) {
        System.out.println("接下来我为大家唱一首"+soonName);
    }
}


 


1.4:辅助类,用来模拟注册人的前后台词


 


public class Presenter  {


    public void before(){
        System.out.println("请开始你的表演!");
    }


    public void after(){
        System.out.println("表演结束,大家鼓掌!");
    }
}


 


1.5:具体的代理类


  这里用proxy.newProxyInstance来创建一个代理类,传入原始类的类加载器和接口与接口InvocationHandler,同时插入Presenter类的before与after方法,用于前置和后置处理


 


public class SingerProxy {


  private Presenter presenter;


  public SingerProxy(Presenter presenter){
      this.presenter = presenter;
  }
    /**
    * 获取代理对象
    * @return
    */
    public Singer getProxy(){


        final Singer jay = new Jay();
        Singer singerProxy = (Singer)Proxy.newProxyInstance(jay.getClass().getClassLoader(), jay.getClass().getInterfaces(), new InvocationHandler() {」
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                presenter.before();
                method.invoke(jay, args);
                presenter.after();
                return null;
            }
        });
        return singerProxy;
    }
}


 


1.6:测试类


 


public class Test {
    public static void main(String[] args) {
        SingerProxy singerProxy = new SingerProxy(new Presenter());
        Singer proxy = singerProxy.getProxy();
        proxy.perform("《夜曲》");
    }
}


 


输出:



 二:动态代理的内部探究


从上面的例子可以看出我们首先生成了一个代理类,然后用代理类来调用原始接口的方法,就可以实现我们的预设的逻辑,在原始接口的前后(或者出现异常的时候)插入我们想要的逻辑,那么究竟是为什么呢?


2.1:找到生成的代理类


我们如果需要打开生成的类,首先需要在测试类中添加这行代码,设置系统属性来保存生成的代理类的class文件:


System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");


2.2:singerProxy类


通过动态代理生成的代理类名为:$Proxy0.class然后通过intelj idea反编译之后源代码是这样的,这里主要看到有4个方法,method的m1\m2\m3\m0;分别由反射获取的equals()、toString()、perform()、hashcode()方法,同时代理类继承了proxy并且实现了原始Singer接口,重写了perform()方法,所以这就解释了为什么代理类可以调用perform()方法,在perform方法中,又调用了父类中的InvoationHander的invoke方法,并且传入原始接口中的方法,而invoke方法在我们在创建代理类的时候重写过,所以就会按照我们自定义的逻

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Linux TCP滑动窗口代码简述 下一篇spring加载bean流程解析

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目