开启生长之旅!这是我参与「日新计划 12 月更文挑战」的第1天
1.前语
咱们知道不论静态署理仍是动态署理,实际上都是关于源方针的一种操控
,经过署理方针来直接拜访方针方针。可是静态署理有以下两个问题
静态署理存在的问题是:
1)一旦接口新增或许修正,那么署理方针和被署理方针就得去适配修正
2)静态署理是在代码编写时,去生成的,class文件必然会形成类爆破的风险
有没有计划处理以上的问题呢?显然是有的,那便是动态署理。动态署理,顾名思义,便是在运行时去动态生成署理,咱们下面分甭说两种完成办法,分别为JDK和Cglib。
2.两种完成计划
2.1 完成计划–JDK动态署理
如上面所说,一般被署理的方针会完成某个接口,咱们是否能够借用署理形式的这个特点做点文章呢?
JDK给咱们提供了一些API,能够完成动态署理,InvocationHandler和Proxy.newProxyInstance来完成。
动态署理类:
package com.itbird.design.proxy.demo.dynamic.v1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 署理方针
* Created by itbird on 2022/7/4
*/
public class ProxyObject implements InvocationHandler {
Object proxy;
public ProxyObject(Object proxy) {
this.proxy = proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(proxy, args);
}
}
代码很简单,便是完成InvocationHandler接口,重写invoke办法,这里有一个要害的构造函数,便是要入参源方针。所以咱们看一下调用。
package com.itbird.design.proxy.demo.dynamic.v1;
import java.lang.reflect.Proxy;
/**
* Created by itbird on 2022/7/4
*/
public class Client {
public void main() {
SourceObject sourceObject = new SourceObject();
// 回来的是 IBank 的一个实例方针,这个方针是由 Java 给咱们创立的 ,调用的是 jni,经过反射+classloader加载
IObject proxy = (IObject) Proxy.newProxyInstance(IObject.class.getClassLoader(), // ClassLoader
new Class[]{IObject.class}, //方针接口
new ProxyObject(sourceObject)); //替换署理
proxy.methodA();
}
}
是否很简单,假如这时接口新增或许修正,我们发现没有,不再需要去修正署理类。因为其本质是,经过反射+classloader去完成的,所以不依赖于方针。
可是依然有一个问题,便是Proxy.newProxyInstance
运用上有一个弊端,便是必须面向接口,假如源方针也便是被署理方针,假如没有完成某个接口,这时不就GG了。该怎么做呢?不要着急,Cglib能够帮咱们处理这个问题。
2.2 完成计划–Cglib
咱们知道一个方针在内存中存储,一般便是在栈或许堆上,那是否有一种办法或许组件,能够直接从内存copy这个方针,完成深克隆
,也便是在内存中在copy一个出来。
Cglib署理形式的根本介绍
1) JDK动态署理形式要求方针方针是完成一个接口,可是有时分方针方针只是一个单独的方针,并没有完成任何的接口,这个时分可运用方针方针子类来完成署理-这便是Cglib署理
2)Cglib署理也叫作子类署理,它是在内存中构建一个子类方针从而完成对方针方针功能扩展 3)Cglib是一个强壮的高性能的代码生成包,它能够在运行期扩展java类与完成java接口.它广泛的被许多AOP的结构运用,例如Spring AOP,完成办法阻拦
4)Cglib包的底层是经过运用字节码处理结构ASM来转换字节码并生成新的类
5)Cglib由于是根据字节码的,显然这时android就不能运用了,因为android是dex文件,这怎么办?不要紧,有 dexmaker 和 cglib-for-android 库。
Cglib署理怎么完成呢?
第一步:引进cglib的android jar文件
界说源方针,能够看到此时的被署理方针,并未完成任何接口
package com.itbird.design.proxy.demo.dynamic.v2;
/**
* 原始方针,预对其拜访,加以操控的方针,没有完成接口
* Created by itbird on 2022/7/4
*/
public class SourceObject {
public void methodA() {
//TODO 详细的完成
}
public void methodB() {
//TODO 详细的完成
}
public void methodC() {
//TODO 详细的完成
}
public void methodD() {
//TODO 详细的完成
}
public void methodE() {
//TODO 详细的完成
}
}
第二步:代码完成,首先运用Cglib完成办法阻拦
package com.itbird.design.proxy.demo.dynamic.v2;
import leo.android.cglib.proxy.MethodInterceptor;
import leo.android.cglib.proxy.MethodProxy;
/**
* 署理方针
* Created by itbird on 2022/7/4
*/
public class ProxyMethodInterceptor implements MethodInterceptor {
/**
* 重写办法阻拦在办法前和办法后参加事务
* Object obj为方针方针
* Method method为方针办法
* Object[] params 为参数,
* MethodProxy proxy CGlib办法署理方针
*/
@Override
public Object intercept(Object o, Object[] objects, MethodProxy methodProxy) throws Exception {
//参数:Object为由CGLib动态生成的署理类实例,Method为上文中实体类所调用的被署理的办法引证,Object[]为参数值列表,MethodProxy为生成的署理类对办法的署理引证。
//回来:从署理实例的办法调用回来的值。
//其间,proxy.invokeSuper(obj,arg) 调用署理类实例上的proxy办法的父类办法(即实体类TargetObject中对应的办法)
System.out.println("调用前");
Object result = methodProxy.invokeSuper(o,objects);
System.out.println(" 调用后"+result);
return result;
}
}
第三步,调用
package com.itbird.design.proxy.demo.dynamic.v2;
import android.content.Context;
import leo.android.cglib.proxy.Enhancer;
/**
* Created by itbird on 2022/7/4
*/
public class Client {
public void main(Context context) {
//Enhancer类是CGLib中的一个字节码增强器
Enhancer enhancer = new Enhancer(context);
//将被署理类TargetObject设置成父类,然后设置阻拦器TargetInterceptor
enhancer.setSuperclass(SourceObject.class);
enhancer.setInterceptor(new ProxyMethodInterceptor());
//履行enhancer.create()动态生成一个署理类,并从Object强制转型成父类型TargetObject
SourceObject object = (SourceObject) enhancer.create();
//在署理类上调用办法
object.methodA();
}
}
我们发现,此时完成了源方针的办法调用。