详解java动态代理机制以及使用场景
- 详解java动态代理机制以及使用场景
- https://blog.csdn.net/u011784767/article/details/78281384
深入理解java动态代理的实现机制
https://blog.51cto.com/4247649/2109130
- (1)什么是代理?
- 大道理上讲代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 商户---->明星经纪人(代理)---->明星这种模式。我们可以不通过直接与明星对话的情况下,而通过明星经纪人(代理)与其产生间接对话。
- (2)什么情况下使用代理?
- (1)设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。
- (2)我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。
- (3)Spring的AOP机制就是采用动态代理的机制来实现切面编程。
- (3)静态代理和动态代理
- 我们根据加载被代理类的时机不同,将代理分为静态代理和动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,比如RPC框架和Spring AOP机制。
- (4)静态代理
- 我们先创建一个接口,遗憾的是java api代理机制求被代理类必须要实现某个接口,对于静态代理方式代理类也要实现和被代理类相同的接口;对于动态代理代理类则不需要显示的实现被代理类所实现的接口。
- /**
- * 顶层接口
- * @author yujie.wang
- *
- */
- public interface Person {
- public void sayHello(String content, int age);
- public void sayGoodBye(boolean seeAgin, double time);
- }
- /**
- * 需要被代理的类 实现了一个接口Person
- * @author yujie.wang
- *
- */
- public class Student implements Person{
- @Override
- public void sayHello(String content, int age) {
- // TODO Auto-generated method stub
- System.out.println("student say hello" + content + " "+ age);
- }
- @Override
- public void sayGoodBye(boolean seeAgin, double time) {
- // TODO Auto-generated method stub
- System.out.println("student sayGoodBye " + time + " "+ seeAgin);
- }
- }
- /**
- * 静态代理,这个代理类也必须要实现和被代理类相同的Person接口
- * @author yujie.wang
- *
- */
- public class ProxyTest implements Person{
- private Person o;
- public ProxyTest(Person o){
- this.o = o;
- }
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- //s为被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问
- Student s = new Student();
- //创建代理类对象
- ProxyTest proxy = new ProxyTest(s);
- //调用代理类对象的方法
- proxy.sayHello("welcome to java", 20);
- System.out.println("******");
- //调用代理类对象的方法
- proxy.sayGoodBye(true, 100);
- }
- @Override
- public void sayHello(String content, int age) {
- // TODO Auto-generated method stub
- System.out.println("ProxyTest sayHello begin");
- //在代理类的方法中 间接访问被代理对象的方法
- o.sayHello(content, age);
- System.out.println("ProxyTest sayHello end");
- }
- @Override
- public void sayGoodBye(boolean seeAgin, double time) {
- // TODO Auto-generated method stub
- System.out.println("ProxyTest sayHello begin");
- //在代理类的方法中 间接访问被代理对象的方法
- o.sayGoodBye(seeAgin, time);
- System.out.println("ProxyTest sayHello end");
- }
- }
- 测试代码输出:
- ProxyTest sayHello begin
- student say hellowelcome to java 20
- ProxyTest sayHello end
- ******
- ProxyTest sayHello begin
- student sayGoodBye 100.0 true
- ProxyTest sayHello end
- 静态代理看起来是比较简单的,没有什么问题只不过是在代理类中引入了被代理类的对象而已。
- 那么接下来我们看看动态代理。
- (5)动态代理
- 我们先直接上动态代理的代码,之后再分析代码的行为,上面的Person接口和Student被代理类保持不变。
- /**
- * 动态代理,动态代理类不要显示的实现被代理类所实现的接口
- * @author yujie.wang
- *
- */
- public class MyInvocationHandler implements InvocationHandler{
- private Object object;
- public MyInvocationHandler(Object object){
- this.object = object;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- // TODO Auto-generated method stub
- System.out.println("MyInvocationHandler invoke begin");
- System.out.println("proxy: "+ proxy.getClass().getName());
- System.out.println("method: "+ method.getName());
- for(Object o : args){
- System.out.println("arg: "+ o);
- }
- //通过反射调用 被代理类的方法
- method.invoke(object, args);
- System.out.println("MyInvocationHandler invoke end");
- return null;
- }
- public static void main(String [] args){
- //创建需要被代理的类
- Student s = new Student();
- //这一句是生成代理类的class文件,前提是你需要在工程根目录下创建com/sun/proxy目录,不然会报找不到路径的io异常
- System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
- //获得加载被代理类的 类加载器
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- //指明被代理类实现的接口
- Class<?>[] interfaces = s.getClass().getInterfaces();
- // 创建被代理类的委托类,之后想要调用被代理类的方法时,都会委托给这个类的invoke(Object proxy, Method method, Object[] args)方法
- MyInvocationHandler h = new MyInvocationHandler(s);
- //生成代理类
- Person proxy = (Person)Proxy.newProxyInstance(loader, interfaces, h);
- //通过代理类调用 被代理类的方法
- proxy.sayHello("yujie.wang", 20);
- proxy.sayGoodBye(true, 100);
- System.out.println("end");
- }
- }
- 运行测试代码输出如下结果:
- MyInvocationHandler invoke begin
- proxy: com.sun.proxy.$Proxy0
- method: sayHello
- arg: yujie.wang
- arg: 20
- student say helloyujie.wang 20
- MyInvocationHandler invoke end
- MyInvocationHandler invoke begin
- proxy: com.sun.proxy.$Proxy0
- method: sayGoodBye
- arg: true
- arg: 100.0
- student sayGoodBye 100.0 true
- MyInvocationHandler invoke end
- end
- 仔细分析上面的动态代理实现代码,我们看到这里涉及到java反射包下的一个接口InvocationHandler和一个类Proxy。
- package java.lang.reflect;
- public interface InvocationHandler {
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable;
- }
- 这个接口只有一个invoke方法,我们在通过代理类调用被代理类的方法时,最终都会委托给这个invoke方法执行,
- //通过代理类调用 被代理类的方法
- proxy.sayHello("yujie.wang", 20);
- proxy.sayGoodBye(true, 100);
- 所以我们就可以在这个invoke方法中对被代理类进行增强或做一些其他操作。
- Proxy类的public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法内部通过拼接字节码的方式来创建代理类,后面我会反编译出它所创建的代理类看看内容。
- 我们看这个方法的三个参数:
- ClassLoader loader:指定一个动态加载代理类的类加载器
- Class<?>[] interfaces:指明被代理类实现的接口,之后我们通过拼接字节码生成的类才能知道调用哪些方法。
- InvocationHandler h:这是一个方法委托类,我们通过代理调用被代理类的方法时,就可以将方法名和方法参数都委托给这个委托类。
- 你看我们现在有了类加载器、类实现的接口、要调用方法的方法名和参数,那么我们就可以做很多事情了。
- (6)反编译Proxy.newProxyInstance所创建的代理类
- //这一句是生成代理类的class文件,前提是你需要在工程根目录下创建com/sun/proxy目录,不然会报找不到路径的io异常
- System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
- 我们在代码中加入上述代码,代码就会保存生成的代理类,名称为$Proxy0.class
- 通过jd-gui反编译代码如下,其中注释是我加上去的:
- package com.sun.proxy;
- import com.yujie.proxy.dynamic.Person;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.lang.reflect.UndeclaredThrowableException;
- /**
- *代理类也实现了Person接口,看起来和静态代理的方式也会一样的
- *同时代理类也继承了Proxy类
- */
- public final class $Proxy0 extends Proxy implements Person{
- private static Method m4;
- private static Method m1;
- private static Method m0;
- private static Method m3;
- private static Method m2;
- public $Proxy0(InvocationHandler paramInvocationHandler)
- throws
- {
- super(paramInvocationHandler);
- }
- //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用
- public final void sayGoodBye(boolean paramBoolean, double paramDouble)
- throws
- {
- try
- {
- // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
- // m4为代理类通过反射获得的Method
- this.h.invoke(this, m4, new Object[] { Boolean.valueOf(paramBoolean), Double.valueOf(paramDouble) });
- return;
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- public final boolean equals(Object paramObject)
- throws
- {
- try
- {
- return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- public final int hashCode()
- throws
- {
- try
- {
- return ((Integer)this.h.invoke(this, m0, null)).intValue();
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用
- public final void sayHello(String paramString, int paramInt)
- throws
- {
- try
- {
- // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
- // m4为代理类通过反射获得的Method
- this.h.invoke(this, m3, new Object[] { paramString, Integer.valueOf(paramInt) });
- return;
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- public final String toString()
- throws
- {
- try
- {
- return (String)this.h.invoke(this, m2, null);
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- static
- {
- try
- {//代理类通过反射 获得的接口方法Method
- m4 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayGoodBye", new Class[] { Boolean.TYPE, Double.TYPE });
- m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
- m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
- //代理类通过反射 获得的接口方法Method
- m3 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayHello", new Class[] { Class.forName("java.lang.String"), Integer.TYPE });
- m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
- return;
- }
- catch (NoSuchMethodException localNoSuchMethodException)
- {
- throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
- }
- catch (ClassNotFoundException localClassNotFoundException)
- {
- throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
- }
- }
- }
- 总结一下:
- jdk的代理让我们在不直接访问某些对象的情况下,通过代理机制也可以访问被代理对象的方法,这种技术可以应用在很多地方比如RPC框架,Spring AOP机制,但是我们看到jdk的代理机制必须要求被代理类实现某个方法,这样在生成代理类的时候才能知道重新那些方法。这样一个没有实现任何接口的类就无法通过jdk的代理机制进行代理,当然解决方法是使用cglib的代理机制进行代理。
详解java动态代理机制以及使用场景的更多相关文章
- 详解Java动态代理机制
之前介绍的反射和注解都是Java中的动态特性,还有即将介绍的动态代理也是Java中的一个动态特性.这些动态特性使得我们的程序很灵活.动态代理是面向AOP编程的基础.通过动态代理,我们可以在运行时动态创 ...
- 详解Java动态代理机制(二)----cglib实现动态代理
上篇文章的结尾我们介绍了普通的jdk实现动态代理的主要不足在于:它只能代理实现了接口的类,如果一个类没有继承于任何的接口,那么就不能代理该类,原因是我们动态生成的所有代理类都必须继承Proxy这个类, ...
- Java 动态代理机制详解
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
- 大厂高级工程师面试必问系列:Java动态代理机制和实现原理详解
代理模式 Java动态代理运用了设计模式中常用的代理模式 代理模式: 目的就是为其他对象提供一个代理用来控制对某个真实对象的访问 代理类的作用: 为委托类预处理消息 过滤消息并转发消息 进行消息被委托 ...
- Java 动态代理机制分析及扩展
Java 动态代理机制分析及扩展,第 1 部分 王 忠平, 软件工程师, IBM 何 平, 软件工程师, IBM 简介: 本文通过分析 Java 动态代理的机制和特点,解读动态代理类的源代码,并且模拟 ...
- [转]Java 动态代理机制分析及扩展
引言 Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执 ...
- 异常处理器详解 Java多线程异常处理机制 多线程中篇(四)
在Thread中有异常处理器相关的方法 在ThreadGroup中也有相关的异常处理方法 示例 未检查异常 对于未检查异常,将会直接宕掉,主线程则继续运行,程序会继续运行 在主线程中能不能捕获呢? 我 ...
- java动态代理机制
首先了解代理设计模式,其思想是为其他对象提供一种代理以控制对这个对象的访问. java动态代理就是遵循这种思想,spring中的AOP实现原理就是java的动态代理. 在java的动态代理机制中,有两 ...
- Java 动态代理机制分析及扩展--转
http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/#icomments http://www.ibm.com/developerworks/c ...
随机推荐
- Windows(win2016、win2019、win10)在IIS下添加.NET Framework 3.5 NetFx3 失败 (状态为:0x800f0950)的解决办法
今天一个客户自己的电脑安装了一个windows server 2016 想装一个IIS,程序一个C+写的ERP,NET是必然,NET4.7可以安装了,但就是3.5,如何也装不上,错误(状态为:0x80 ...
- In Vitro model验证 | Harnessing single-cell genomics to improve the physiological fidelity of organoid-derived cell types
Transcriptional benchmarking of in vitro cells to in vivo with single-cell rna-seq - 简介 Harnessing s ...
- TFS变更地址
本文链接:https://blog.csdn.net/qq_31117007/article/details/78044381 1: 今天公司服务器换了IP地址,然后发现tfs的服务器删除不了,也添加 ...
- 避免git clone和push时每次都需要输入用户名和密码
有三种方式解决git clone时每次都需要输入用户名和密码, 1. SSH免密方式 使用git bash ssh-keygen或puttygen.exe生成公钥. 2. 配置全局开机存储认证信息 下 ...
- mybatis ResultHandler vs ResultSetHandler及自定义扩展
ResultSetHandler是mybatis的关键类之一,用于对jdbc返回的ResultSet进行映射处理,其中包括列前缀处理,逻辑分页,鉴别器(Discriminator,基于值实现动态映射列 ...
- 安全漏洞XSS、CSRF、SQL注入以及DDOS攻击
随着互联网的普及,网络安全变得越来越重要,程序员需要掌握最基本的web安全防范,下面列举一些常见的安全漏洞和对应的防御措施. 0x01: XSS漏洞 1.XSS简介 跨站脚本(cross site s ...
- Manifest中meta-data扩展元素数据的配置与获取
简介-meta是什么 在AndroidManifest.xml清单文件中 我们有时会看到如下类似的<meta-data ... >元素开始的配置内容: <meta-data andr ...
- pd.ExcelWriter(to_excel)保存结果到已存在的excel文件中
网易云课堂该课程链接地址 https://study.163.com/course/courseMain.htm?share=2&shareId=400000000398149&cou ...
- Spring-boot +Shiro 导致事务无效
今天在开发过程中,遇到一个情况,就是事务事务,同项目的别的service都在事务中,可以就是有一个事务失效. 排除了各种情况 1.检查数据库的引擎是否是innoDB 2.方法是否为public 3.这 ...
- JS的base64编码解码
Unicode问题解法 有个小坑是它只支持ASCII. 如果你调用btoa("中文")会报错: Uncaught DOMException: Failed to execute ' ...