Java 动态代理

代理模式

所谓代理模式,就是指「代理对象」(或者我们形象地称呼为中介),通过实现接口类具有了接口的能力,并通过和「实际的生产者」产生联系,将「实际生产者」的能力通过自己中转给「客户(调用者)」

客户不直接通过「实际生产者」获取信息,而是跟「代理对象(中介)」打交道获取信息

我们通过房屋租赁作为例子

静态代理

举个例子,我们有个 IPriceService.java 的接口,用于获取房屋的报价

1
2
3
4

interface IPriceService{
int getPrice();
}

一般在代码里我们还会有一个类去实现这个 IPriceService.java 接口,例如下面的 APriceService.java
代表A房东的报价为 100元

1
2
3
4
5
6
class APriceService implements IPriceService{
override int getPrice(){
return 100;
}
}

我们可能还有一个代理类,PriceServiceProxy,对 APriceService 进行代理,相当于一个「中介」,不让「租客」和「房东」接触,「中介」可以对数据(即价格)进行加工处理后再提供给「租客」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class PriceServiceProxy implements IPriceService{

private IPriceService realPriceService;
public PriceServiceProxy(IPriceService service){
//当然,这里也可以通过外部注入该中介对接的房东
realPriceService = service;
}

override int getPrice(){
//中介获取到了房东的报价
int aPrice = realPriceService.getPrice();
//再在房东报价的基础上加上中介费,返回给租客
return aPrice + 100;
}
}

这就是静态代理,从工程上来说,静态代理是添加一个「委托类」在不需要修改真正的实现类的基础上,做到对需求变动进行灵活修改的目的。

例如有一天需求变为「提供给租客的价格是房东的价格的 9折」,如果没有代理类,而是「租客」和「房东」直接打交道,那么我们需要修改所有实现了 IPriceService 接口的类中的 getPrice()方法,有了代理类,我们只需要修改代理类中的 getPrice() 方法,在房东的报价的基础上 * 0.9 即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class PriceServiceProxy implements IPriceService{

private IPriceService realPriceService;
public PriceServiceProxy(IPriceService service){
//当然,这里也可以通过外部注入该中介对接的房东
realPriceService = service;
}

override int getPrice(){
//中介获取到了房东的报价
int aPrice = realPriceService.getPrice();
//再在房东报价的基础打9折,返回给租客
return (int)(aPrice * 0.9f);
}
}

测试方法:

1
2
3
4
5

public static void main(String[] args) {
IPriceService priceService = new PriceServiceProxy(new APriceService());
System.out.print("租客获取到的房子报价为"+priceService.getPrice());
}

上述的「静态代理」有如下几个特点:

  1. 「租客」只关心报价,而不关心是谁提供了报价;
    即只关心 IPriceService 接口的返回,而不关心实现 IPriceService 接口的实例是谁
  2. 将「租客」和「房东」分离,降低了一定的耦合性
  3. 但也需要创建很多的代理类,使得代码量增加,一旦接口需要变动,代理类和实际目标类都需要进行修改

动态代理

动态代理就是将上述我们的静态代理中的 PriceServiceProxy 类通过代码动态生成,并通过该动态代理对目标对象(房东)进行进一步操作,达到代理的目的

我们可以使用 Java 中的 java.lang.reflect.Proxy#newProxyInstance 生成动态代理对象
其方法签名如下:

1
2
3
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)

第一个参数传入需要代理的接口的 ClassLoader
第二个参数传入需要代理的接口类
第三个参数传入 InvocationHandler 对象,对需要代理的目标对象进行处理

eg.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

private val aPriceService by lazy { APriceService() }


//通过代码动态创建了一个「中介对象」priceServiceProxy
val priceServiceProxy = Proxy.newProxyInstance(
IPriceService::class.java.classLoader,
arrayOf(IPriceService::class.java),
object : InvocationHandler {

override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
Log.d("ProxyDemoActivity","method is ${method?.name}")
if (method?.name?:"" == "getPrice"){
//如果方法是「getPrice」方法,则获取对象返回的数据,然后再加上 100 后在返回
return (method?.invoke(aPriceService,*(args?: arrayOfNulls(0))) as Int) + 100
}
return method?.invoke(aPriceService,*(args?: arrayOfNulls(0)))
}

}
) as IPriceService

findViewById<Button>(R.id.btnProxyDemo).setOnClickListener {
//通过代理对象获取价格
Log.d("ProxyDemoActivity","price is "+priceServiceProxy.getPrice())
}

可见,我们通过代码 Proxy.newProxyInstance 生成了一个动态代理对象,对原本的 aPriceService 对象进行代理,从中做一些「偷梁换柱」的方法

那这到底有什么用呢,静态代理不也能做到吗,何必多此一举呢。

下面我们看个例子,假设我们有个需求,需要对 Android Framework 中的 startActivity() 方法做一些拦截处理

然而这个方法的代码是我们无法编辑的,也无法通过静态代理的方式生成一个 ActivityManagerProxy 对象进行代理,这时我们就可以考虑使用动态代理的方式,生成一个动态代理对象,对 activityManager 对象进行代理,在 startActivity 方法前后进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
try {
Class<?> activityManagerNativeClass = null;
Field defaultFiled = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
activityManagerNativeClass = Class.forName("android.app.ActivityManager");
defaultFiled = activityManagerNativeClass.getDeclaredField("IActivityManagerSingleton");
} else {
activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
defaultFiled = activityManagerNativeClass.getDeclaredField("gDefault");
}

defaultFiled.setAccessible(true);
Object defaultValue = defaultFiled.get(null);
//反射SingleTon
Class<?> SingletonClass = Class.forName("android.util.Singleton");
Field mInstance = SingletonClass.getDeclaredField("mInstance");
mInstance.setAccessible(true);
//到这里已经拿到ActivityManager对象
Object iActivityManagerObject = mInstance.get(defaultValue);

//开始动态代理,用代理对象替换掉真实的ActivityManager,瞒天过海
Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");

AmsInvocationHandler handler = new AmsInvocationHandler(iActivityManagerObject);

Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{IActivityManagerIntercept}, handler);

//现在替换掉这个对象
mInstance.set(defaultValue, proxy);

} catch (Exception e) {
Log.e("vivid","hook 异常");
e.printStackTrace();
}

class AmsInvocationHandler implements InvocationHandler {

private Object iActivityManagerObject;

private AmsInvocationHandler(Object iActivityManagerObject) {
this.iActivityManagerObject = iActivityManagerObject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".contains(method.getName())) {
//我要在这里搞点事情
}
return method.invoke(iActivityManagerObject, args);
}
}

上述代码则是通过反射获取到 ActivityManager 对象,然后通过动态代理构建其一个代理类,并在 invoke 方法中对 startActivity 方法进行判断处理,达到我们的目的

Retrofit 中的动态代理

下文基于 "com.squareup.retrofit2:retrofit:2.7.1" 分析

我们知道在 Retrofit 中,api 方法的定义是定义在一个 interface 中的,例如

1
2
3
4
interface IPodCastBusiness {
@GET
fun getRss(@Url url: String): Flowable<RssResponse>
}

而在使用该方法进行网络请求时,是使用以下的方法进行请求的

1
2
3
4
5
6
7
8
Retrofit.Builder()
.baseUrl("xxxx")
.client(OkHttpClient())
.addConverterFactory(SimpleXmlConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
.create(IPodCastBusiness::class.java)
.getRss(rssLink)

我们重点看一下这个 create 方法,看 Retrofit 是如何将一个 IPodCastBusiness 的接口转化为一个实例的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  public <T> T create(final Class<T> service) {
//第一步校验 service 是否是一个接口,不是我们现在的重点,跳过不表
validateServiceInterface(service);
//这里又见到了我们的老朋友了,在这里,通过 Proxy.newProxyInstance() 方法
//将我们传入的 service 接口构建了一个动态代理对象并返回
//外部则可以使用这个动态代理的实例进行下一步操作了
//至于调用了接口中定义的 HTTP Request 方法是如何转化成返回类型的,则需要看 invoke 方法中的内容
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];

@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
//如果这个方法是 Object 里的方法,则正常调用,不处理
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}

到这里,我们就知道了在 Retrofit 中是如何将一个 Interface 转化成一个实例对象了的
其远离就是通过 Proxy.newProxyInstance() 方法生成其代理对象返回

作者

PPTing

发布于

2021-11-21

更新于

2022-02-12

许可协议

评论