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动态代理, ...
随机推荐
- HTML+css基础 p段落标签 a 超链接标签 Src和href有什么区别和关联? target属性 Meta标签
p段落标签: <p></p> 1.他是唯一一个可以不写结束标签的双标签. a 超链接标签: 从一个页面链接到另一个页面.靠的是href属性. Src和href有什么区别和关联 ...
- unityUIMask
Mask: 与Image组件配合工作,根据Image的覆盖区域来定位显示范围,所有该Image的子级UI元素,超出此区域的部分会被隐藏(包括UI的交互事件) 实现原理: Mask会赋予Image一个特 ...
- Navicat MYSQL 建立关联表 保存时遇到 cannot add foreign key constraint
首先建立user表,如下图 然后建立message表,userid用作外键,关联user表的id 点击上面的外键按钮,添加外键如下 结果保存时报错: cannot add foreign key co ...
- sql server取日期各个组成部分的datename()函数
SQL Server中的日期类型datetime的默认格式是yyyy-mm-dd hh:mi:ss:mmm,很多时候我们可能会需要获取日期中的某个组成部分,因此SQL Server提供了一个daten ...
- java中的泛型【T】与通配符【?】概念入门
使用泛型的目的是利用Java编译机制,在编译过程中帮我们检测代码中不规范的有可能导致程序错误的代码.例如,我们都知道List容器可以持有任何类型的数据,所以我们可以把String和Integer等类型 ...
- 机器学习之Artificial Neural Networks
人类通过模仿自然界中的生物,已经发明了很多东西,比如飞机,就是模仿鸟翼,但最终,这些东西会和原来的东西有些许差异,artificial neural networks (ANNs)就是模仿动物大脑的神 ...
- Linux下安装Python3.6.8并安装包
一.问题在Linux下面安装Python3.6.8,由于在Linux中的Python是2.7.x的版本因此,我们需要在Linux中新下载一个Python 二.解决1.python的安装(1)下载包利用 ...
- 【JVM】jmap命令详解----查看JVM内存使用详情
linux获取java进程PID: https://www.cnblogs.com/sxdcgaq8080/p/10734752.html 如果命令使用过程中报错,可能解决你问题的方案: https: ...
- 我是如何一步步编码完成万仓网ERP系统的(十)产品库设计 6.属性项和类别关联
https://www.cnblogs.com/smh188/p/11533668.html(我是如何一步步编码完成万仓网ERP系统的(一)系统架构) https://www.cnblogs.com/ ...
- tensorflow常用函数(二)
一.变量相关的函数 1)tf.train.list_variables(ckpt_dir_or_file) Returns list of all variables in the checkp ...