动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理
本文只是对原文的梳理总结,以及自行理解。自己总结的比较简单,而且不深入,不如直接看原文。不过自己梳理一遍更有助于理解。
详细可参考原文:http://www.cnblogs.com/CarpenterLee/p/8241042.html (原文很强大,多看几遍,深入理解。)
原文中参考:https://www.jianshu.com/p/e2917b0b9614 (比原文的代码更详细,更有助于理解)
自行总结:
- 两种代理方式,都是提供一个方法调用的中转站用于实现代理:CGLIG中MethodInterceptor和跟JDK代理中的InvocationHandler;
- 都是通过特定类的特定方法得到代理对象:
- Proxy.newProxyInstance() 方法入参中包含:类加载器(被代理对象的)、代理需要实现的接口,可以有多个(被代理对象的)、方法调用的实际处理者(即实际代理对象)
- Enhancer.create() 方法入参中包含:Superclass (被代理的类) 和 Callback (代理类)
- 调用代理对象的对应方法,会自动被代理到 invoke() 方法和 intercept() 方法。可在方法中加入代理的逻辑
- 最终还需要调用实际被代理对象的方法:Method. invoke() 和 MethodProxy.invokeSuper() 。
一、静态代理的实现
- 声明接口;
- 编码接口实现类,需继承对应接口,实现对应方法;
- 编码代理类,需继承对应接口,实现对应方法。代理类内部方法实际调用具体实现类的方法,同时加入额外逻辑。
- Proxy.newProxyInstance() 方法入参中包含:类加载器(被代理对象的)、代理需要实现的接口,可以有多个(被代理对象的)、方法调用的实际处理者(即实际代理对象)
- Enhancer.create() 方法入参中包含:Superclass (被代理的类) 和 Callback (代理类)
- 声明接口;
- 编码接口实现类,需继承对应接口,实现对应方法;
- 编码代理类,需继承对应接口,实现对应方法。代理类内部方法实际调用具体实现类的方法,同时加入额外逻辑。
具体实现可参考原文。
二、JDK原生动态代理:基于接口
1、动态代理:实际就是JDK定义类和方法(Proxy.newProxyInstance()),我们只需传入相关入参,JDK会自行为我们创建代理类,以实现代理功能。
2、使用实现:
// Java Proxy
// 1. 首先实现一个InvocationHandler,方法调用会被转发到该类的invoke()方法。
class LogInvocationHandler implements InvocationHandler{
...
private Hello hello;
public LogInvocationHandler(Hello hello) {
this.hello = hello;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("sayHello".equals(method.getName())) {
logger.info("You said: " + Arrays.toString(args));
}
return method.invoke(hello, args);
}
}
// 2. 然后在需要使用Hello的时候,通过JDK动态代理获取Hello的代理对象。
Hello hello = (Hello)Proxy.newProxyInstance(
getClass().getClassLoader(), // 1. 类加载器(被代理对象的)
new Class<?>[] {Hello.class}, // 2. 代理需要实现的接口,可以有多个(被代理对象的)
new LogInvocationHandler(new HelloImp()));// 3. 方法调用的实际处理者(即实际代理对象)
System.out.println(hello.sayHello("I love you!"));
3、具体使用总结:
- 首先实现一个InvocationHandler,方法调用会被转发到该类的invoke()方法。invoke() 方法就是实际的代理方法。
- 需要使用Hello时,通过JDK动态代理获取Hello的代理对象。
- 调用代理对象的接口声明方法,会被自动转发到invoke()方法上。
4、上述代码理解:
关键是:Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)方法
该方法会根据指定的参数动态创建代理对象。三个参数的意义如下:
loader,指定代理对象的类加载器;
interfaces,代理对象需要实现的接口,可以同时指定多个接口;
handler,方法调用的实际处理者,代理对象的方法调用都会转发到这里(*注意1)。
newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法。
理解上述代码需要对Java反射机制有一定了解。动态代理神奇的地方就是:
-代理对象是在程序运行时产生的,而不是编译期;
-对代理对象的所有接口方法调用都会转发到InvocationHandler.invoke()方法,在invoke()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;之后我们通过某种方式执行真正的方法体,示例中通过反射调用了Hello对象的相应方法,还可以通过RPC调用远程方法。
5、其他理解:参考JDK官方文档即可
- InvocationHandler(接口)和其invoke()方法理解:
接口:
java.lang.reflect.InvocationHandler
方法:在代理实例上处理方法调用并返回结果。
Object invoke(Object proxy, Method method, Object[] args)
参数:
proxy - 在其上调用方法的代理实例(即代理对象)
method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。(即代理方法)
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
返回:
从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。
- Proxy(类)的理解:
类:java.lang.reflect.Proxy
方法:返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回:
一个带有代理类的指定调用处理程序(即入参:h )的代理实例,它由指定的类加载器(即入参:loader)定义,并实现指定的接口(即入参:interfaces)
- 对于不同invoke()方法的理解:
java.lang.reflect.Method.invoke(Object, Object...)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
//即该方法所属对象的该方法调用。
java.lang.reflect.InvocationHandler.invoke(Object, Method, Object[])
三、CGLIB动态代理
大致原理和JDK动态代理一致,方便使用。
优点在于:可以代理没有实现任何接口的类;
1、使用实现:
//需被代理的类
public class HelloConcrete {
public String sayHello(String str) {
return "HelloConcrete: " + str;
}
}
// CGLIB动态代理
// 1. 首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
class MyMethodInterceptor implements MethodInterceptor{
...
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
logger.info("You said: " + Arrays.toString(args));
return proxy.invokeSuper(obj, args);
}
}
// 2. 然后在需要使用HelloConcrete的时候,通过CGLIB动态代理获取代理对象。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloConcrete.class); //被代理的类
enhancer.setCallback(new MyMethodInterceptor());//代理类
HelloConcrete hello = (HelloConcrete)enhancer.create(); //代理对象
System.out.println(hello.sayHello("I love you!"));
2、具体使用总结:
- 首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
- 然后在需要使用HelloConcrete的时候,设置参数,同时通过CGLIB动态代理获取代理对象。
- 调用代理对象的代理方法,然后会自行实现其调用
3、上述代码理解:
1、通过CGLIB的Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,
2、最终通过调用create()方法得到代理对象,对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法,
3、在intercept()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;
4、通过调用MethodProxy.invokeSuper()方法,我们将调用转发给原始对象,具体到本例,就是HelloConcrete的具体方法。
CGLIG中MethodInterceptor的作用跟JDK代理中的InvocationHandler很类似,都是方法调用的中转站。
三、结束。
动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理的更多相关文章
- Java Proxy和CGLIB动态代理原理
动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...
- Java学习笔记--Cglib动态代理
CGLib动态代理 使用JDK创建代理有一个限制,即它只能为接口创建代理实例,这一点可以从Proxy的接口方法newProxyInstance(ClassLoader loader,Class[] i ...
- 代理模式 & Java原生动态代理技术 & CGLib动态代理技术
第一部分.代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常 ...
- Java代理(静态代理、JDK动态代理、CGLIB动态代理)
Java中代理有静态代理和动态代理.静态代理的代理关系在编译时就确定了,而动态代理的代理关系是在运行期确定的.静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性. J ...
- Java代理设计模式(Proxy)的四种具体实现:静态代理和动态代理
面试问题:Java里的代理设计模式(Proxy Design Pattern)一共有几种实现方式?这个题目很像孔乙己问"茴香豆的茴字有哪几种写法?" 所谓代理模式,是指客户端(Cl ...
- AOP的底层实现:JDK动态代理与Cglib动态代理
转载自 https://www.cnblogs.com/ltfxy/p/9872870.html SpringAOP底层的实现原理: JDK动态代理:只能对实现了接口的类产生代理.(实现接口默认JDK ...
- 代理模式 静态代理、JDK动态代理、Cglib动态代理
1 代理模式 使用代理模式时必须让代理类和被代理类实现相同的接口: 客户端通过代理类对象来调用被代理对象方法时,代理类对象会将所有方法的调用分派到被代理对象上进行反射执行: 在分派的过程中还可以添加前 ...
- 从静态代理,jdk动态代理到cglib动态代理-一文搞懂代理模式
从代理模式到动态代理 代理模式是一种理论上非常简单,但是各种地方的实现往往却非常复杂.本文将从代理模式的基本概念出发,探讨代理模式在java领域的应用与实现.读完本文你将get到以下几点: 为什么需要 ...
- JDK动态代理和CGLIB动态代理编码
JDK动态代理[接口]: import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import jav ...
随机推荐
- Js日常笔记之this
在javascript中自己创建构造函数时可以利用this来指向新创建的对象上.这样就可以避免函数中的this指向全局了,如下 var x = 2; function test(){ this.x = ...
- python selenium --层级定位
转自:http://www.cnblogs.com/fnng/p/3193955.html 场景: 假如两个控件,他们长的一模样,还都叫“张三”,唯一的不同是一个在北京,一个在上海,那我们就可以通过, ...
- Hive Group By 常见错误
Expression not in GROUP BY key ‘ xxx’ 遇到这么一个需求,输入数据为一个ID对应多个name,要求输出数据为ID是唯一的,name随便取一个就可以. 执行以下hiv ...
- Request介绍及演示样例 PART1
Request在ServletAPI的规范连接地址http://blog.csdn.net/zghwaicsdn/article/details/51035146 HTTP简介 URL是浏览器寻找信息 ...
- JNI调用实例
1. 环境 Windows7-64Bit VS2010-32Bit JDK1.8-64Bit 2. 步骤 2.1 创建NativePrint类 public class NativePrint { p ...
- nginx vhosts rewrite 独立文件的方式出现
[root@web01 /]# cat /app/server/nginx/conf/nginx.conf user www www; worker_processes ; error_log /ap ...
- EM5-PE6B
1. vocabulay 1.1 scary adj.令人恐怖的 This is a scary thing. you can construct some extremely scary scena ...
- 微信中调起qq
http://wpa.qq.com/msgrd?uin={$qq}&menu=yes
- 8237dma的四种传送方式简介
8237A有四种工作方式:单字节传送.数据块传送.请求传送和多片级联. (1)单字节传送(single mode) 单字节传送方式是每次DMA传送时,仅传送一个字节.传送一个字节之后,当前字节计数器减 ...
- 总结几个关于 jQuery 用法
有关 jquery 用法 目录: $.trim() $.inArray() $.getJSON() 事件委托 on 遍历closest() ajaxSubmit() 拖拽排序 dragsort() 进 ...
动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...
CGLib动态代理 使用JDK创建代理有一个限制,即它只能为接口创建代理实例,这一点可以从Proxy的接口方法newProxyInstance(ClassLoader loader,Class[] i ...
第一部分.代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常 ...
Java中代理有静态代理和动态代理.静态代理的代理关系在编译时就确定了,而动态代理的代理关系是在运行期确定的.静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性. J ...
面试问题:Java里的代理设计模式(Proxy Design Pattern)一共有几种实现方式?这个题目很像孔乙己问"茴香豆的茴字有哪几种写法?" 所谓代理模式,是指客户端(Cl ...
转载自 https://www.cnblogs.com/ltfxy/p/9872870.html SpringAOP底层的实现原理: JDK动态代理:只能对实现了接口的类产生代理.(实现接口默认JDK ...
1 代理模式 使用代理模式时必须让代理类和被代理类实现相同的接口: 客户端通过代理类对象来调用被代理对象方法时,代理类对象会将所有方法的调用分派到被代理对象上进行反射执行: 在分派的过程中还可以添加前 ...
从代理模式到动态代理 代理模式是一种理论上非常简单,但是各种地方的实现往往却非常复杂.本文将从代理模式的基本概念出发,探讨代理模式在java领域的应用与实现.读完本文你将get到以下几点: 为什么需要 ...
JDK动态代理[接口]: import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import jav ...
在javascript中自己创建构造函数时可以利用this来指向新创建的对象上.这样就可以避免函数中的this指向全局了,如下 var x = 2; function test(){ this.x = ...
转自:http://www.cnblogs.com/fnng/p/3193955.html 场景: 假如两个控件,他们长的一模样,还都叫“张三”,唯一的不同是一个在北京,一个在上海,那我们就可以通过, ...
Expression not in GROUP BY key ‘ xxx’ 遇到这么一个需求,输入数据为一个ID对应多个name,要求输出数据为ID是唯一的,name随便取一个就可以. 执行以下hiv ...
Request在ServletAPI的规范连接地址http://blog.csdn.net/zghwaicsdn/article/details/51035146 HTTP简介 URL是浏览器寻找信息 ...
1. 环境 Windows7-64Bit VS2010-32Bit JDK1.8-64Bit 2. 步骤 2.1 创建NativePrint类 public class NativePrint { p ...
[root@web01 /]# cat /app/server/nginx/conf/nginx.conf user www www; worker_processes ; error_log /ap ...
1. vocabulay 1.1 scary adj.令人恐怖的 This is a scary thing. you can construct some extremely scary scena ...
http://wpa.qq.com/msgrd?uin={$qq}&menu=yes
8237A有四种工作方式:单字节传送.数据块传送.请求传送和多片级联. (1)单字节传送(single mode) 单字节传送方式是每次DMA传送时,仅传送一个字节.传送一个字节之后,当前字节计数器减 ...
有关 jquery 用法 目录: $.trim() $.inArray() $.getJSON() 事件委托 on 遍历closest() ajaxSubmit() 拖拽排序 dragsort() 进 ...