博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
转:jdk动态代理实现
阅读量:6582 次
发布时间:2019-06-24

本文共 5152 字,大约阅读时间需要 17 分钟。

原文链接: 

注:文章中用常用的流程实现 动态代理,流程逻辑比较清晰。文章后面对 “为什么要使用接口” 原理分析还未细看。

jdk的动态代理为什么用接口,内部是什么原理呢?看了几篇文章貌似都没讲的清楚明白,因此来解释一下。

先通过一个简单例子实现功能:

1 //接口 2 public interface SayService { 3   4     void say(String name); 5 } 6 //实现类 7 public class SayServiceImpl implements SayService{ 8   9     @Override10     public void say(String name) {11         System.out.println(name);12     }13 }

然后再自定义一个增强类, 实现InvocationHandler接口:

1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Method; 3   4 public class WavingInvocationHandler  implements InvocationHandler{ 5   6     private Object target; 7   8     public void setTarget(Object target) { 9         this.target = target;10     }11  12     @Override13     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {14         System.out.println("方法执行之前!");15         Object obj = method.invoke(target, args);16         System.out.println("方法执行之后!");17         return obj;18     }19 }

编写测试方法:

1 import sun.misc.ProxyGenerator; 2   3 import java.io.File; 4 import java.io.FileNotFoundException; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.lang.reflect.Proxy; 8   9 public class Test {10  11     public static void main(String[] args) {12  13         //接口14         SayService sayService = new SayServiceImpl();15         //织入类16         WavingInvocationHandler handler = new WavingInvocationHandler();17         handler.setTarget(sayService);18         //代理类--增强的对象19         SayService s = (SayService) Proxy.newProxyInstance(20                 sayService.getClass().getClassLoader(),21                 sayService.getClass().getInterfaces(), handler);22  23         s.say("say()");//执行代理对象完成业务24         /**25          方法执行之前!26          say()27          方法执行之后!28          */29  30         //将jdk中生成代理类输出到本地.Class文件,之后可以通过反编译软件打开查看31         createProxyClassFile("test12345",sayService.getClass().getInterfaces());32  33     }34  35     private static void createProxyClassFile(String name,Class
[] interfaces){36 byte[] data = ProxyGenerator.generateProxyClass(name,interfaces);//该方法为jdk中生成代理类的核心方法37 FileOutputStream out =null;38 try {39 out = new FileOutputStream(name+".class");40 System.out.println((new File(name)).getAbsolutePath());41 out.write(data);42 } catch (FileNotFoundException e) {43 e.printStackTrace();44 } catch (IOException e) {45 e.printStackTrace();46 }finally {47 if(null!=out) try {48 out.close();49 } catch (IOException e) {50 e.printStackTrace();51 }52 }53 }54 55 }

好奇心强的小伙伴一定看过newProxyInstance方法看过了,里面的getProxyClass方法创建代理类:

1 /*2  * Look up or generate the designated proxy class.3  */4 Class
cl = getProxyClass0(loader, intfs);

具体是里面的哪个方法生成的,小伙伴们不用找半天了,慢慢debug后会发现,就是上面提到的

1 ProxyGenerator.generateProxyClass()

通过该方法生成的代理类如下:通过反编译查看源码,一看便知接口的作用

JDK的动态代理是靠多态和反射来实现的,它生成的代理类需要实现你传入的接口,并通过反射来得到接口的方法对象(下文中的m3),并将此方法对象传参给增强类(上文中的WavingInvocationHandler类)的invoke方法去执行,从而实现了代理功能,故接口是jdk动态代理的核心实现方式,没有它就无法通过反射找到方法,所以这也是必须有接口的原因。不知道大家明白否

1 public final class test12345 extends Proxy implements SayService { 2     //1、该类实现你传入的接口并实现方法  3     // 2、通过构造方法传入你定义的增强类对象  4     // 3、通过反射该接口,得到接口里的Method对象并传参给增强类,然后执行Invoke实现功能 5     private static Method m1; 6     private static Method m2; 7     private static Method m3; 8     private static Method m0; 9  10     public test12345(InvocationHandler paramInvocationHandler) {11         super(paramInvocationHandler);//2.此处的super指向Proxy中的构造方法并赋值(下文的h就是此处的增强类对象),12     }13  14     //略去无关的hashcode和equals方法15     public final void say(String paramString) {16         try {17             this.h.invoke(this, m3, new Object[]{paramString});//3.此处的h就是InvocationHandler对象,invoke就是你增强类里的方法,传入接口的方法和参数并执行你增强类里的invoke方法,即完成了整个操作!18         } catch (Error | RuntimeException localError) {19             throw localError;20         } catch (Throwable localThrowable) {21             throw new UndeclaredThrowableException(localThrowable);22         }23     }24  25     static {
//1.静态代码块给属性赋值,初始化接口中的方法对象(主要是下面的m3)26 try {27 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});28 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);29 m3 = Class.forName("com.chenrui.core.jdk.SayService").getMethod("say", new Class[]{Class.forName("java.lang.String")});30 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);31 } catch (NoSuchMethodException localNoSuchMethodException) {32 throw new NoSuchMethodError(localNoSuchMethodException.getMessage());33 } catch (ClassNotFoundException localClassNotFoundException) {34 throw new NoClassDefFoundError(localClassNotFoundException.getMessage());35 }36 }37 }

 

---------------------
作者:锐锐
来源:CSDN
原文:https://blog.csdn.net/ray890206/article/details/70146029
版权声明:本文为博主原创文章,转载请附上博文链接!

转载于:https://www.cnblogs.com/mumu122GIS/p/10037971.html

你可能感兴趣的文章
maven 添加阿里云maven镜像
查看>>
mac上安装consolas字体
查看>>
对向量、矩阵求导
查看>>
各版本linux下载地址大全
查看>>
CentOS 6.X 关闭不需要的 TTY 方法
查看>>
我的友情链接
查看>>
分区技术学习一
查看>>
Juniper 高级选项
查看>>
编程能力的四种境界
查看>>
编译安装mysql
查看>>
在windows上秒开应用程序
查看>>
【20180611】MySQL OOM
查看>>
Python面向对象编程(一)
查看>>
决心书
查看>>
如何把图片上的文字转换成word?
查看>>
7z命令行
查看>>
C语言编程实现 输入一个非负整数,返回组成它的数字之和(递归方法)
查看>>
c3p0
查看>>
我的友情链接
查看>>
引号-下划线,连接多个变量
查看>>