9.动态代理
9.1 介绍
- JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。动态代理是动态地在内存中构建代理对象,从而实现对目标对象的代理功能,接口增加方法时代理对象不受影响,但性能会降一个数量级
- 框架核心底层反射机制
- 动态代理和静态代理角色一样
- 动态代理相当于无侵入式拓展了功能
9.2 分类
- 动态代理分为2类:基于接口的动态代理和基于类的动态代理
基于接口:jdk的动态代理。
JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口
基于类:cglib的动态代理
cglib是一个强大的高性能的代码生成包、类库,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP,为他们提供方法的interception(拦截),Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。
关于java字节码的处理,有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。
基于字节码:javassist
开源分析,编辑,创建java字节码的类库为JBoss实现动态AOP框架
不需要跟虚拟机指令打交道直接使用java编码形式动态改变类结构和生成类
9.3 JDK的动态代理实现
-
Proxy : 提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
-
InvocationHandler:由代理实例的调用处理程序实现的接口 。 每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
-
在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)
9.4 举例理解(JDK的动态代理)
- 中介抽象类
//中介抽象类
public interface Rent {
public void rentFunction();
}
- 真实对象
//代理对象
//要代理的真实对象,车主去实现租借接口
public class Owner implements Rent {
@Override
public void rentFunction() {
System.out.println("车主出租车");
}
}
- 调用处理程序-动态代理工具类模板。
- **执行机制:**跟静态代理一样,目标对象和代理类都要实现目标类的抽象借口,注意ObjInvocationHandler是调用处理程序,由它去创建代理类,通过Proxy.newProxyInstance方法创建一个实现了 target.getClass().getInterfaces()目标对象接口的代理类
/**
* 代理实例关联的“调用处理程序”,实现的InvocationHandler接口,重写 invoke方法
* 调用处理程序生成代理
* 本质使用反射机制,返回代理类和代理方法
*/
public class ObjInvocationHandler implements InvocationHandler {
/**
* 被代理的对象类或接口
*/
private Object target;
public ObjInvocationHandler(Object target) {
this.target = target;
}
//获得代理类
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
/**
* 处理代理实例上的方法调用并返回结果。
* 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法调用前!");
beforeFunction(method.getName());
//反射对象的方法是privite,则需要invoke来激活
//操作反射对象的方法是privite属性,需要关闭程序的安全检查,属性或方法的setAccessible(true)
//利用反射机制,实现目标对象的方法调用
Object result = method.invoke(target, args);
System.out.println("方法调用后!");
afterFunction();
return result;
}
public static void beforeFunction(String msg) {
System.out.println("租借前行为,执行了"+msg+"方法");
}
public static void afterFunction() {
System.out.println("租借后行为");
}
}
- 测试
@Test
public void testProxy01(){
//真实角色
Rent target = new Owner();
//代理角色
//方式1:动态生成代理实例
Rent proxyInstance = (Rent)Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new ObjInvocationHandler(target));
//执行方法
proxyInstance.rentFunction();
System.out.println("方式1=======================方式2");
ObjInvocationHandler objInvocationHandler = new ObjInvocationHandler(target);
//方式2:动态生成代理实例(将产生代理实例封装为一个方法)
Rent proxyInstance1 = (Rent)objInvocationHandler.getProxyInstance();
proxyInstance1.rentFunction();
}
- 结果
方法调用前!
租借前行为,执行了rentFunction方法
车主出租车
方法调用后!
租借后行为
方式1=======================方式2
方法调用前!
租借前行为,执行了rentFunction方法
车主出租车
方法调用后!
租借后行为
Process finished with exit code 0
9.5 举例理解(cglib的动态代理)
- JDK动态代理还是需要被代理对象实现特定的接口,为了解决这一缺陷,可使用cglib动态代理的方式解决,Cglib是继承了被代理对象。
- 倒入依赖cglib、asm
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.2</version>
</dependency>
- 目标对象
//目标对象
//要代理的真实对象,车主去实现租借接口
public class Owner {
public void rentFunction() {
System.out.println("车主出租车");
}
}
- Cglib动态代理工具类
public class ObjInterceptor implements MethodInterceptor {
/**获取代理对象,参数为目标对象的Class类对象*/
public Object getProxyInstance(Class target){
//实现方式1:
/* Object result = Enhancer.create(target, this);
return result;*/
//实现方式2
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target);
enhancer.setCallback(this);
return enhancer.create();
}
/**参数分别为代理对象,目标对象方法,方法参数,代理对象方法*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeFunction(methodProxy.getSuperName());
//利用反射机制,使用代理对象方法,传入的是代理对象和方法参数
Object invoke = methodProxy.invokeSuper(o, objects);
return invoke;
}
public static void beforeFunction(String msg) {
System.out.println("租借前行为,执行了"+msg+"方法");
}
public static void afterFunction() {
System.out.println("租借后行为");
}
}
- 测试
@Test
public void testProxyCglib(){
Owner owner = new Owner();
//调用方式1:
System.out.println("方式1");
Owner proxyInstance1 = (Owner)Enhancer.create(Owner.class, new ObjInterceptor());
proxyInstance1.rentFunction();
//调用方式2
System.out.println("方式2");
ObjInterceptor objInterceptor = new ObjInterceptor();
Owner proxyInstance2 = (Owner)objInterceptor.getProxyInstance(owner.getClass());
proxyInstance2.rentFunction();
}
}
- 结果
方式1
租借前行为,执行了CGLIB$rentFunction$0方法
车主出租车
方式2
租借前行为,执行了CGLIB$rentFunction$0方法
车主出租车
- 注意:上面工具类invokeSuper()是使用代理对象方法,传入的是代理对象和方法参数 。若想利用反射机制,实现目标对象的方法调用invoke()方法需要变动一下代理工具类和调用方式
public class ObjInterceptor2 implements MethodInterceptor {
/**
* 被代理的对象类或接口
*/
private Object target;
public ObjInterceptor2(Object target) {
this.target = target;
}
/**获取代理对象,参数为目标对象的Class类对象*/
public Object getProxyInstance(){
//实现方式1:
/* Object result = Enhancer.create(target, this);
return result;*/
//实现方式2
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
/**参数分别为代理对象,目标对象方法,方法参数,代理对象方法*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeFunction(methodProxy.getSuperName());
//方式3.利用反射机制,实现目标对象的方法调用
//实现和JDK形式一样
Object result = method.invoke(target, objects);
return result;
}
public static void beforeFunction(String msg) {
System.out.println("租借前行为,执行了"+msg+"方法");
}
public static void afterFunction() {
System.out.println("租借后行为");
}
}
- 调用方式变化
@Test
public void testProxyCglib(){
Owner owner = new Owner();
//实现目标对象的方法调用
System.out.println("实现方式3");
ObjInterceptor2 objInterceptor2 = new ObjInterceptor2(owner);
Owner proxyInstance3 = (Owner)objInterceptor2.getProxyInstance();
proxyInstance3.rentFunction();
}
- 结果
实现方式3
租借前行为,执行了CGLIB$rentFunction$0方法
车主出租车
9.6 JDK和Cglib动态代理区别
JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
9.7 优缺点
- 优点
可以使真实角色的操作更加纯粹,不去关注一些公共的业务
公共的业务交给代理角色,实现了业务分工,降低耦合性
代理角色扩展的公共业务,更容易集中管理
一个动态代理类代理的是一个接口,一般是对应的一类业务
一个动态代理类可以代理多个类,只要是实现了同一个接口即可
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/123926.html