Java 动态代理 2022-3-17
代理模式:一种结构型设计模式;指的是给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问
分类:根据字节码的创建时机来分类,可以分为 静态代理 和 动态代理
静态代理:在程序运行前就已经存在代理类的字节码文件,代理类 和 真正实现业务逻辑的类 的关系在运行前就确定了
动态代理:字节码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件
静态代理实现:代理对象和目标对象实现共同的接口,并且代理对象持有目标对象的引用
虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来
静态代理的缺点
1、 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
- 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
- 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
2、 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。
CGLIB 创建动态代理类的模式是:
1、查找目标类上的所有非final 的public类型的方法定义;
2、将这些方法的定义转换成字节码;
3、将组成的字节码转换成相应的代理的class对象;
4、实现 MethodInterceptor接口,用来处理对代理类上所有方法的请求
面试题
来源于网上,用于帮助理解和掌握,欢迎补充
描述动态代理的几种实现方式?分别说出相应的优缺点
代理可以分为 "静态代理" 和 "动态代理",动态代理又分为 "JDK动态代理" 和 "CGLIB动态代理" 实现。
静态代理:代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是实例对象
优点:可以很好的保护实际对象的业务逻辑对外暴露,从而提高安全性
缺点:不同的接口要有不同的代理类实现,或者一个代理类实现多个接口,冗余
JDK 动态代理:
为了解决静态代理中,生成大量的代理类造成的冗余;
JDK 动态代理只需要实现 InvocationHandler 接口,重写 invoke 方法便可以完成代理的实现,
jdk的代理是利用反射生成代理类 Proxyxx.class 代理类字节码,并生成对象
jdk动态代理之所以只能代理接口是因为代理类本身已经extends了Proxy,而java是不允许多重继承的,但是允许实现多个接口
优点:解决了静态代理中冗余的代理实现类问题
缺点:JDK 动态代理是基于接口设计实现的,如果没有接口,会抛异常。
CGLIB 代理:
由于 JDK 动态代理限制了只能基于接口设计,而对于没有接口的情况,JDK方式解决不了;
CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。
实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。
但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。
同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。
优点:没有接口也能实现动态代理,而且采用字节码增强技术,性能也不错。
缺点:技术实现相对难理解些。
CGlib 对接口实现代理?
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import proxy.UserService;
import java.lang.reflect.Method;
/**
-
创建代理类的工厂 该类要实现 MethodInterceptor 接口。
-
该类中完成三样工作:
-
(1)声明目标类的成员变量,并创建以目标类对象为参数的构造器。用于接收目标对象
-
(2)定义代理的生成方法,用于创建代理对象。方法名是任意的。代理对象即目标类的子类
-
(3)定义回调接口方法。对目标类的增强这在这里完成
*/
public class CGLibFactory implements MethodInterceptor {
// 声明目标类的成员变量
private UserService target;public CGLibFactory(UserService target) {
this.target = target;
}
// 定义代理的生成方法,用于创建代理对象
public UserService myCGLibCreator() {
Enhancer enhancer = new Enhancer();
// 为代理对象设置父类,即指定目标类
enhancer.setSuperclass(UserService.class);
/**
* 设置回调接口对象 注意,只所以在setCallback()方法中可以写上this,
* 是因为MethodIntecepter接口继承自Callback,是其子接口
*/
enhancer.setCallback(this);
return (UserService) enhancer.create();// create用以生成CGLib代理对象
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("start invoke " + method.getName());
Object result = method.invoke(target, args);
System.out.println("end invoke " + method.getName());
return result;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本