Java代理(静态代理、JDK动态代理、CGLIB动态代理)
Java中代理有静态代理和动态代理。静态代理的代理关系在编译时就确定了,而动态代理的代理关系是在运行期确定的。静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性。
Java中动态代理有JDK原生动态代理和CGLIB动态代理两种。前者本质上是根据定好的接口动态生成静态代理类(该接口的实现类);后者则不需要事先定好接口而是可以直接根据类进行动态代理,其本质是根据指定的类动态生成静态代理类(指定的类的子类)。动态代理中,被代理的对象的所有方法都会被代理,除非在代理逻辑中进行方法筛选。
本质:
JDK原生动态代理:生成被代理对象所实现的接口的实现类;实现类中每个方法调用被代理对象的相应方法,只不过在调用前后加上了额外逻辑;要求被代理对象实现接口
CGLIB动态代理:生成被代理对象的子类;子类中的每个方法调用被代理对象(父类对象)中的相应方法,只不过在调用前后加上了额外处理;要求被代理对象不能被final修饰
动态代理是IOC、AOP等技术的基础。
静态代理和JDK动态代理
Dynamic proxies allow one single class with one single method to service multiple method calls to arbitrary classes with an arbitrary number of methods. A dynamic proxy can be thought of as a kind of Facade, but one that can pretend to be an implementation of any interface. Under the cover, it routes all method invocations to a single handler – the invoke() method. https://www.baeldung.com/java-dynamic-proxies
示例:
package com.marchon.proxytest; public interface IUserService {
public String getUserName();
public Integer getAge(String userName);
}
IUserService
package com.marchon.proxytest; /**
* 被代理对象
*
* @author zsm
*
*/
public class UserServiceImpl implements IUserService { @Override
public String getUserName() {
String res = this.getClass() + ":hello";
System.out.println(res);
return res;
} @Override
public Integer getAge(String userName) {
Integer age = 20;
System.out.println(age);
return age;
} }
UserServiceImpl
package com.marchon.proxytest; /**
* 代理对象(静态代理)<br>
* 缺点:<br>
* 1、代理类和被代理类实现相同的接口,代码重复、得为每个接口都实现相应的实现从而维护成本高 2、代理对象只服务于被代理对象,即每个被代理对象都得实现相应的代理对象
*
* @author zsm
*
*/
class UserServiceStaticProxy implements IUserService {
private IUserService proxiedObj; public UserServiceStaticProxy(IUserService proxiedObj) {
if (proxiedObj instanceof UserServiceStaticProxy) {
throw new RuntimeException("illegal proxiedObj proxied object");
} this.proxiedObj = proxiedObj;
} @Override
public String getUserName() {
System.out.println("before");
String res = proxiedObj.getUserName();
System.out.println("after");
return res; } @Override
public Integer getAge(String userName) {
System.out.println("before");
Integer age = proxiedObj.getAge(userName);
System.out.println("after");
return age;
} } public class Main_StaticProxy {
public static void main(String[] args) {
IUserService proxiedObj = new UserServiceImpl(); // UserServiceStaticProxy proxy = new UserServiceStaticProxy(proxiedObj);
IUserService proxyObject = new UserServiceStaticProxy(proxiedObj);
proxyObject.getUserName();
proxyObject.getAge("zhangsan");
}
}
StaticProxy
package com.marchon.proxytest; import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import sun.misc.ProxyGenerator; /**
* 代理对象(动态代理)<br>
* 动态代理:在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能。动态代理主要分为JDK动态代理和cglib动态代理两大类<br>
* 这里介绍jdk动态代理,其本质上是在运行时动态产生一个实现指定接口的静态代理类,指定接口的所有非final方法(包括继承的非final方法如toString)均会被代理。
*
* @author zsm
*
*/ class JdkDynamicProxyTemplate implements InvocationHandler {
private Object proxiedObj; public JdkDynamicProxyTemplate(Object proxiedObj) {
this.proxiedObj = proxiedObj;
} @Override
public Object invoke(Object proxyObj, Method method, Object[] args) throws Throwable {
// if (method.getName().equals("getUserName")) {//为避免所有非final方法都被代理,可作此判断
//
// } // System.out.println(proxyObj);//stack overflow, why?
// System.out.println(method);// public abstract java.lang.String com.marchon.proxytest.IUserService.getUserName() System.out.println("before");
Object res = method.invoke(proxiedObj, args);
System.out.println("after"); return res;
} } public class Main_JdkDynamicProxy {// 参阅:https://www.jianshu.com/p/269afd0a52e6
public static void main(String[] args) {
IUserService proxiedObj = new UserServiceImpl(); JdkDynamicProxyTemplate proxyTemplate = new JdkDynamicProxyTemplate(proxiedObj); // 第一个参数是指定代理类的类加载器(我们传入当前测试类的类加载器)
// 第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口)
// 第三个参数是invocation handler,用来处理方法的调用。这里传入我们自己实现的handler
IUserService proxyObject = (IUserService) Proxy.newProxyInstance(proxiedObj.getClass().getClassLoader(),
proxiedObj.getClass().getInterfaces(), proxyTemplate);// 创建包含被代理对象各方法的代理对象,显然可知:该代理对象实现了所传接口中(这里为IUserService)定义的各方法、代理对象的各方法实现为直接调用proxyTemplate相应的各方法实现。可以使用sum.misc下的ProxyGenerator生成动态代理类的字节码文件,再反编译出动态代理类源码 proxyObject.getUserName();
proxyObject.getAge("zhangsan");
proxyObject.toString();// 所有非final方法都会被代理,包括从Object继承的等 {
// 获取代理类字节码文件
String path = "$Proxy0.class";
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", proxiedObj.getClass().getInterfaces());
FileOutputStream out = null; try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} // jdk动态代理动态产生的代理类类似于如下静态代理
System.out.println();
System.out.println("equivalent static proxy test:");
new TmpEquivalentStaticProxy(proxyTemplate).getUserName(); }
} /**
*
* 上述生成的动态代理生成的代理类实际是类似于本类
*
* @author zsm
*
*/
class TmpEquivalentStaticProxy implements IUserService {// 真正由jdk动态代理生成的代理类还 extends Proxy
private InvocationHandler invokeHandler; public TmpEquivalentStaticProxy(InvocationHandler invokeHandler) {
this.invokeHandler = invokeHandler;
} @Override
public String getUserName() {
Object[] args = null;
Method getUserNameMethod = IUserService.class.getMethods()[0];// public abstract java.lang.String
// com.marchon.proxytest.IUserService.getUserName()
try {
return (String) invokeHandler.invoke(this, getUserNameMethod, args);
} catch (Throwable e) {
e.printStackTrace();
return null;
} } @Override
public Integer getAge(String userName) {
Object[] args = new Object[] { userName };
try {
Method getAgeMethod = IUserService.class.getMethods()[1];// public abstract java.lang.Integer
// com.marchon.proxytest.IUserService.getAge(java.lang.String)
return (Integer) invokeHandler.invoke(this, getAgeMethod, args);
} catch (Throwable e) {
e.printStackTrace();
return null;
} } }
JdkDynamic
springframework中的一个jdk动态代理示例:
由上可见,静态代理的各个方法具有共性:每个方法内的前后都做额外其他处理、中间调用被代理对象的相应方法。既然有共性,那就可以抽取共性以减少重复,也即将静态代理的方法抽象出一个”模板“,这样就不需要在代理对象中针对被代理对象的每个方法写额外逻辑,这其实就变成了动态代理。
JDK动态代理实际上是自动生成一个静态代理类并创建相应实例。代理类默认继承Porxy类,因为Java中只支持单继承,所以JDK动态代理只能去实现接口;代理类的方法都会去调用InvocationHandler的invoke()方法,故此需重写InvocationHandler的invoke()方法。
JDK动态代理为我们提供了非常灵活的代理机制,但也有不足:
被代理对象的所有非final方法(如从Object继承的toString、equals等)都会被代理(即都会在方法前后做与InvocationHandler的invoke()方法前后同样的处理,当然我们可以通过在invoke里对method name加以判断避免此情况),然而有时候我们并不希望这些方法被代理。
JDK动态代理是基于接口的(生成的动态代理类实际上extends Proxy implements IUserService)。如果要被代理的对象没有实现接口,该如何实现代理呢?可用下面要介绍的CGLIB动态代理。
从上面讨论可得知,静态代理和动态代理在运行时是一样的(都是静态代理了),其差异在运行时之前(即编译期)才存在,差异体现在开发者是否需要分别在每个与被代理方法对应的代理方法的前后写额外的处理逻辑:静态代理需要而动态代理不需要(后者不需要是因为该任务从由开发者负责转交由JVM来负责了)。因此,动态代理相比于静态代理的主要优势是为开发者提供便利,不用像静态代理那写重复的代理方法逻辑。
CGLIB动态代理(springframework)
与JDK动态代理不同,CGLIB(Code Generation Library)动态代理不需要事先定义接口,而是可以直接对类进行动态代理。CGLIB动态代理中被代理对象的所有非final方法默认也会被代理。
示例:
package com.marchon.proxytest; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy; /**
* 代理对象(动态代理)<br>
* 动态代理:在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能。动态代理主要分为JDK动态代理和cglib动态代理两大类<br>
* 这里介绍cglib动态代理,其本质上是在运行时动态产生一个实现继承指定类的的静态代理类,指定类的所有非final方法(包括继承的非final方法如toString)均会被代理。
*
* @author zsm
*
*/ class CglibDynamicProxyTemplate implements MethodInterceptor { @Override
public Object intercept(Object proxyObj, Method proxiedMethod, Object[] args, MethodProxy proxyMethod) throws Throwable {
// if (method.getName().equals("sayHello")) {//为避免所有非final方法都被代理,可作此判断
//
// } // System.out.println(proxyObj);//stack overflow, why?
// System.out.println(proxiedMethod);// public java.lang.String
// com.marchon.proxytest.HelloConcrete.sayHello(java.lang.String)
// System.out.println(proxyMethod);// org.springframework.cglib.proxy.MethodProxy@108c4c35 System.out.println("before");
Object res = proxyMethod.invokeSuper(proxyObj, args);
System.out.println("after");
return res;
} } class HelloConcrete {
public String sayHello(String username) {
return "hello " + username;
} public final String getAddress() {
return "beijing";
}
} public class Main_CglibDynamicProxy {
public static void main(String[] args) {
CglibDynamicProxyTemplate proxyTemplate = new CglibDynamicProxyTemplate(); Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloConcrete.class);
enhancer.setCallback(proxyTemplate); HelloConcrete proxyObject = (HelloConcrete) enhancer.create();
System.out.println(proxyObject.sayHello("zhangsan"));// 会创建包含被代理对象所有非final方法的代理类,实际上代理类是被代理类的子类,故不会代理final方法;代理对象的各方法实现为直接调用proxyTemplate相应的各方法实现
proxyObject.hashCode();// 所有非final方法都会被代理,包括从Object继承的等
proxyObject.getAddress();// final方法不会被代理 // jdk动态代理动态产生的代理类类似于如下静态代理
System.out.println();
System.out.println("equivalent static proxy test:");
new TmpEquivalentStaticProxyOfCglib(proxyTemplate).sayHello("zhangsan");
}
} class TmpEquivalentStaticProxyOfCglib extends HelloConcrete {// 真正由cglib动态代理生成的代理类还 implements Factory
private MethodInterceptor methodInterceptor; public TmpEquivalentStaticProxyOfCglib(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
} @Override
public String sayHello(String username) {
Object[] args = null;
Method proxiedMethod = super.getClass().getMethods()[0];
MethodProxy proxyMethod = null;// this.getClass().getMethods()[0];//不造如何获取MethodProxy对象,故此方法实际上跑不了 try {
return (String) methodInterceptor.intercept(this, proxiedMethod, args, proxyMethod);
} catch (Throwable e) {
e.printStackTrace();
return null;
} } // 无法override getAddress方法
}
CglibDynamicProxy
由上可见,此动态代理本质上是在运行时动态根据指定的类继承实现一个子类(故指定的类不能是final的),在子类中重写方法,方法的实现为调用MethodInterceptor中的intercept方法。
参考资料
https://www.jianshu.com/p/269afd0a52e6
https://www.cnblogs.com/liuyun1995/p/8144628.html
https://www.cnblogs.com/CarpenterLee/p/8241042.html JDK动态代理和CGLIB动态代理
https://blog.csdn.net/difffate/article/details/70552056 CGLIB动态代理
Java代理(静态代理、JDK动态代理、CGLIB动态代理)的更多相关文章
- jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)
代理模式是一种很常见的模式,本文主要分析cglib动态代理的过程 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.c ...
- 动态代理双剑客--JDK Proxy与CGLIB
背景: 研究过设计模式的同胞们都知道代理模式可以有两种实现方案: 1.接口实现(或继承抽象类) 核心代码片段 ProxySubject-->>doOperation() //dosomet ...
- Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj)
一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法. ...
- java的静态代理、jdk动态代理和cglib动态代理
Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...
- Java代理:静态代理、JDK动态代理和CGLIB动态代理
代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式.所谓的代理者是指一个类别可以作为其它东西的接口.代理者可以作任何东西的接口:网络连接.存储器中的大对象.文件或其它昂贵或无法复制 ...
- Spring 静态代理+JDK动态代理和CGLIB动态代理
代理分为两种:静态代理 动态代理 静态代理:本质上会在硬盘上创建一个真正的物理类 动态代理:本质上是在内存中构建出一个类. 如果多个类需要进行方法增强,静态代理则需要创建多个物理类,占用磁盘空间.而动 ...
- Spring AOP JDK动态代理与CGLib动态代理区别
静态代理与动态代理 静态代理 代理模式 (1)代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是指静态代理,也就是在代码中显式指定的代理. (2)静态代理由 业务实现类.业务代理类 两部分 ...
- Spring AOP详解 、 JDK动态代理、CGLib动态代理
AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...
- 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
Spring AOP详解 . JDK动态代理.CGLib动态代理 原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...
- Spring AOP中的JDK和CGLib动态代理哪个效率更高?
一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...
随机推荐
- 【转】常用PLC通讯协议
三菱FX系列PLC通讯测试 发送帧(Hex): 起始(STX) 02 命令(CMD) 30 首地址(ADDRESS) 30 30 41 30 字节数(BYTES) 30 31 终止(ETX) 03 校 ...
- java 获取真实ip地址
/** * 获取真实ip地址 * @param request * @return */ public static String getIpAddress(HttpServletRequest re ...
- 像修改本机代码一样修改远端服务器的PHP网站
映射vps目录到自己电脑的摸索记录 最近拿discuz做了一个网站,需要修改模板文件之类的,还需要调试微信和QQ登陆的接口.都是些位置零散的小修改,但是有些需要在线上才能调试(QQ微信的oauth登陆 ...
- .NET创建Windows定时任务
创建Windows定时任务教程 1.创建一个控制台应用程序,保证程序正常运行. 2.右键点击我的电脑->点击管理. 3.在计算机管理弹出框->展开计算机管理(本地)->展开系统工具- ...
- Web API 2 的操作结果
这是msdn原文文档!明天用,留存. Web API 控制器操作可以返回以下任何内容: void HttpResponseMessage IHttpActionResult 其他类型 根据返回的这种情 ...
- window.postMessage()实现跨域消息传递
window.postMessage() 方法可以安全地实现跨源通信.通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https), 端口号(443为https的默认值), ...
- MVC里模型常用的一些操作
学习也是做买卖,归根到底还是学习成本的问题. 下面把微软集合类型的增删改查稍微罗列一下,大家看看它能带来的便利,和你要学的新东西,还有风险(纯粹的数据操作,不用框架,风险其实不大)相比,是否值得.来决 ...
- 维护中常用的k8s和docker命令
kubernet命令 查看所有pod的信息: kubectl get pod --all-namespaces -o wide 查看命名为kube-system的pod kubectl get pod ...
- 京东js加密 nloginpwd 破解
京东登录,有一个参数nloginpwd,是加密字段. 第一步:浏览器抓包 第二部:搜索加密字段 js 代码 第三部: 下断点 2. js代码: var navigator = {}; var wind ...
- texlive相关问题
1.The font "WenQuanYi Micro Hei" cannot be found. 解决方法:下载WenQuanYi Micro Hei的字体(以ttf结尾),粘贴 ...