代理模式(Proxy)
1.Java中常见的有三种代理模式,静态代理、动态代理、CGLIB。
2.静态代理-作为23种设计模式之一,通过代理对象(Proxy)对真正对象(RealSubject)的访问与操作,代理对象充当了中介的作用,这样降低了系统的耦合程度。同时也有可能增加系统的复杂度。另外因为不是直接调用,请求过程处理速度就有可能较慢。看一下类图(网上爬的):
3.举个生活中的例子,好比现在的跑腿业务。周末在家懒的出门想买包烟(外卖居然不可以对烟下单!!),这时候跑腿业务刚好符合。主体我(RealSubject),意图Action(买烟),跑腿小哥(Proxy)。抽象的接口Subject(买烟),试想主体我必须要下单告诉跑腿小哥我需要其帮忙做的事,不然他是不知道我需要干嘛。这个很好理解相当于一个双向的契约。
Subject
1.主体与代理对象共同的契约(买烟-buyCigaret):1
2
3public interface IBuyCigarette {
void buy();
}
RealSubject
1.真是的主体我,目的就是买烟,但是这个动作具体执行肯定不是我自己,因为懒:1
2
3
4
5
6
7public class RealCustomer implements IBuyCigarette {
public void buy() {
System.out.println("Im realCustomer, i just want to buy a pack of cigarettes");
}
}
Proxy
1.跑腿小哥,也就是代理的对象,Proxy同样需要知道我的真实需求,不然他也不知道干嘛:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class Proxy implements IBuyCigarette {
private final IBuyCigarette target;
public Proxy(IBuyCigarette target) {
this.target = target;
}
public void buy() {
if (null == target) {
System.out.println("What do you want?");
return;
}
System.out.println("(proxy)Get the order!");
target.buy();
System.out.println("(proxy)Finish the order!");
}
}
Client
1 | public static void main(String[] args) { |
1.输出的信息:
(proxy)Get the order!
Im realCustomer, i just want to buy a pack of cigarettes
(proxy)Finish the order!
Process finished with exit code 0
动态代理
1.与静态代理不同的是:动态代理类在运行时,利用JDK中API动态生成的,而静态代理类,在编译后便以.class
的形式存在。静态代理需要代理类与RealSubject同时实现相同合约接口。而动态代理则不需要。既然基于反射实现,那么理论上动态代理的性能是低于静态代理的。
2.看看类图关系(也是网上爬的):
3.动态代理,代理类在运行时生成,虽然不需要去实现接口,但是代理的是接口,因此业务类(RealSubject)需要实现相应的接口。通过newProxyInstance得到代理对象。还是以静态代理的例子。
业务需求接口(Interface)
1.动态代理代理的是接口,这里增加一个接口方法,用于测试1
2
3
4public interface IBuyCigarette {
void buy();
long price();
}
RealCustomer
1.RealSubject需要实现的业务接口:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package main.proxy;
public class RealCustomer implements IBuyCigarette {
public void buy() {
System.out.println("Im realCustomer, i just want to buy a pack of cigarettes");
}
public long price() {
System.out.println("The price is 14RMB");
return 1400;
}
}
动态代理类
1.既然使用的是JDK的方法(Proxy.newProxyInstance),看看具体的实现: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
30public class DynamicProxy {
/**
* target object
*/
private final Object target;
public DynamicProxy(Object target) {
this.target = target;
}
public Object getInstance() {
if (null == target) {
return null;
}
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Start the task !!");
Object result = method.invoke(target, args);
//在定义的接口中分别包含了void和long两个方法,void是没有返回值的。
System.out.println("result = " + result);
System.out.println("End the task !!");
return result;
}
});
}
}
查看动态代理类生成的.class文件
1.为了能查看动态生成的.class
文件,引入工具类,将生成的文件保存,通过IDEA的插件,看看反射后的对象是什么: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
29public class Util {
/**
* 根据类信息动态生成的二进制字节码并保存,默认的是clazz目录下
* params: clazz 需要生成动态代理类的类
* proxyName: 为动态生成的代理类的名称
*/
public static void generateClassFile(Class<?> clazz, String className) {
/**根据类信息生成字节码*/
byte[] classFile = ProxyGenerator.generateProxyClass(className, clazz.getInterfaces());
String paths = clazz.getResource(".").getPath();
FileOutputStream out = null;
try {
File file = new File(paths + className);
out = new FileOutputStream(file + ".class");
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != out) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Client
1 | /** |
1.运行打印的信息:1
2
3
4
5
6
7
8
9
10
11Start the task !!
Im realCustomer, i just want to buy a pack of cigarettes
result = null
End the task !!
<------------------------->
Start the task !!
The price is 14RMB
result = 1400
End the task !!
Process finished with exit code 0
2.分析RealCustomer.class文件: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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import main.proxy.IBuyCigarette;
public final class RealCustomer extends Proxy implements IBuyCigarette {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public RealCustomer(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final long price() throws {
try {
return (Long)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void buy() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("main.proxy.IBuyCigarette").getMethod("price");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("main.proxy.IBuyCigarette").getMethod("buy");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
3.可以发现的是,RealCustomer继承了Proxy类,并实现类业务接口IBuyCigarette,由于java语言的单继承特性,因此动态代理,代理的是接口方法,接口是可以多实现的。除了接口方法,同样还实现了Object中的几个方法。类比与静态代理的特点,避免生成过多的代理类。而是通过反射生成继承自Proxy的代理类字节码并生成对象。所以动态代理接口是关键(基于JDK)。
4.在测试的打印信息可以观察到方法完整执行都会打印两句:1
2
3Start the task !!
xxxxxxxxx
End the task !!
那么这是为什么呢?回到.class
文件中:1
2
3
4
5
6
7
8
9public final long price() throws {
try {
return (Long)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
其中super.h
而这个h
即为Proxy类中:1
2
3
4
5/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;
在看我们自定义的动态代理类的参数:1
2
3
4
5
6
7
8
9
10
11
12
13
14return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//InvocationHandler作为接口,这里才是我们自己的实现,现在串起来,那么每次执行的逻辑就很好理解了。这里直接使用的匿名内部类的方式,可能理解上有点绕,如果直接实现InvocationHandler接口会相对清晰。
System.out.println("Start the task !!");
Object result = method.invoke(target, args);
System.out.println("result = " + result);
System.out.println("End the task !!");
return result;
}
});
CGLIB代理
1.通过对JDK中动态代理的分析,是基于接口的,如果没有对应的接口,那么是无法使用动态代理的。CGLIB采用的字节码为一个类创建子类,而拦截父类的方法完成动态代理,因此这种代理的方式首先要对字节码很熟悉,难度较大,其次对于final类还是无法实现代理,不在深入。
Tips
1.静态代理代理对象和真实对象都实现了相同的业务接口,并在代理对象持有真实对象的实例,对外暴露代理对象,起到对真实对象的保护,实现了解耦。
优点:保护实际对象的业务逻辑不对外暴露,安全性高。
缺点:代码冗余,不同的接口需要不同的代理类,当接口中增加新的方法时,导致代理对象与真实对象都需要修改。
2.动态代理基于接口设计,解决了静态代理可能造成的代码冗余。同时使用了反射,动态生成的代理类继承自Proxy,理论上效率是低于静态代理的。
优点:避免静态代理的代码冗余,代理类不实现业务接口(非.class文件)。
缺点:由于是基于动态生成字节码文件,预先是不知道需要代理的是什么业务,不同于静态代理的编译时。
3.CGLIB代理,利用小而快的字节码处理框架ASM生成新的子类(非final类),拦截父类的方法。需要对字节码非常熟悉,难度较大。
优点:解决了JDK动态代理的对象需要实现接口的限制,利用字节码生成新的子类。
缺点:对字节码熟悉程度要求较高,对与final类同样无法动态代理。
Retrofit
1.Retrofit最为人称道的应该就是巧妙的运用了动态代理的实现,上述对代理的分析,现在来看看Retrofit的动态代理的实现。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"unchecked") // Single-interface proxy creation guarded by parameter safety. (
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
2.熟悉的JDK动态代理方式。以Test中的使用为例:1
2
3
4
5
6
7
8
9
10
11
12
13
14public interface Pop {
"robots.txt") (
Call<ResponseBody> robots();
}
OkHttpClient okHttpClient =
new OkHttpClient.Builder().addInterceptor(hostSelectionInterceptor).build();
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl("http://www.github.com/")
.callFactory(okHttpClient)
.build();
Pop pop = retrofit.create(Pop.class);
Response<ResponseBody> response1 = pop.robots().execute();
3.直接看loadServiceMethod(method).invoke(args);
1 | ...省略... |
4.HttpServiceMethod.parseAnnotations
方法比较长,看关键的方法赋值: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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response<T>.
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
} else {
// TODO figure out if type is nullable or not
// Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
// Find the entry for method
// Determine if return type is nullable or not
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
throw methodError(
method,
"'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// TODO support Unit for Kotlin?
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
//关键------------->
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
//逻辑还是比较清晰的,主要就是对Converter、CallAdapter根据我们在创建Retrofit(Builder)时传入参数进行创建新的对象,而关键部分在于-->okhttp3.Call.Factory callFactory = retrofit.callFactory;先看call接口。
5.Call接口(Retrofit中)1
2
3
4
5
6
7
8
9
10
11public interface Call<T> extends Cloneable {
//接口的定义还是比较简单的,Retrofit中的OkHttpCall实现了Call接口,回头看-->okhttp3.Call.Factory callFactory = retrofit.callFactory;,到这里重新创建了请求,并且组装好了参数。OkHttpCall内部持有的okhttp3,真正的请求则进一步下发。
Response<T> execute() throws IOException;
void enqueue(Callback<T> callback);
boolean isExecuted();
void cancel();
boolean isCanceled();
Call<T> clone();
Request request();
Timeout timeout();
}
6.总结一下流程:通过自定义的接口,注解显示的表示请求的方法方式,Retrofit通过动态代理,生成了代理类(.class文件),由于Android中无法调用ProxyGenerator,因此没有生成对应的字节码,但通过分析可以得出,实现了Pop接口。HttpServiceMethod继承自ServiceMethod,实现的抽象方法invoke:1
2
3
4
5
6
7
8
9
10loadServiceMethod(method).invoke(args);
final ReturnT invoke(Object[] args) {
//在这里创建了根据我们接口定义的真正请求。
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
//后续则是对数据的解析工作了
return adapt(call, args);
}
//Retrofit.create-->loadServiceMethod(ServiceMethod)--->parseAnnotations(HttpServiceMethod)--->loadServiceMethod(method).invoke(args)--->return adapt(call, args).也就是我们通用的写法-->(Response<ResponseBody> response1 = pop.robots().execute();)