设计模式 代理模式

words: 2.1k    views:    time: 9min

代理是一种常用的设计模式,给某个对象提供一个代理,并由代理对象来控制对真实对象的访问。它的方式就是让代理类持有目标类的实例,并在对目标类进行操作的前后加入一层代理操作。

1. 静态代理

静态代理类在编译时就实现好了,它在Java编译完成后就是一个确定的class文件

  • Proxy:代理主题,用来代理和封装真实主题;
  • Subject:抽象主题,定义代理类和真实主题的公共对外方法,即需要代理的方法。
  • RealSubject:真实主题,真正实现业务逻辑的类;

客户端直接面对的是代理类,通过代理类对目标类进行访问。这样当需要在访问过程中进行一些操作时,就可以不用修改已有的实现类,只需将操作加到代理类中。

示例

Subject
1
2
3
4
public interface Handler {

void handle(String arg);
}
RealSubject
1
2
3
4
5
6
7
public class HandlerImpl implements Handler {

@Override
public void handle(String arg) {
System.out.println("handle[" + arg + "]");
}
}
Proxy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HandlerProxy implements Handler {

private Handler handler;

public HandlerProxy(Handler target){
this.handler = target;
}

@Override
public void handle(String arg) {
System.out.println("before...");
handler.handle(arg);
System.out.println("after...");
}
}
Client
1
2
3
4
5
6
7
public class Client {
public static void main(String[] args) {
Handler handler = new HandlerImpl();
HandlerProxy handlerProxy = new HandlerProxy(handler);
handlerProxy.handle("test");
}
}

通过代理类可以避免对目标类的修改,并增加了期望的行为,同时保证了调用者所调用的类型不变。但是这种代理方式显得不够灵活,如果接口发生了改变,必须同时改变目标类和代理类。

假设现在有一个明确的代理操作,比如打印方法的执行耗时,那么如果目标接口的方法非常多,代理类就会显得很臃肿。如果希望将这个代理操作应用到更多不同的接口实现类上,那就更麻烦了,要给每个接口都定制一个代理类,可能你会想到用工厂模式来让代码结构更新清晰一点,但如此臃肿又麻烦的方式,显然早已违背了设计原则的初衷。

2. 动态代理

2.1. JDK动态代理

对于上面代理模式存在的问题,JDK肯定也想到了,于是提供了一种动态的方式来创建代理类,利用反射在运行时进行创建。

它提供了InvocationHandler让你来定义期望的代理行为

java.lang.reflect.InvocationHandler
1
2
3
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

并提供了一个工厂方法,给你来创建代理类,这样你就可以将代理行为应用到任何接口上了

java.lang.reflect.Proxy
1
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
示例
  • 定义代理行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ProxyHandler implements InvocationHandler {

Object target;

public ProxyHandler(Object target){
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before(method, args);
Object result = method.invoke(target, args);
after(method, args);
return result;
}

private void before(Method method, Object[] args) {
System.out.println("before " + method.getName());
}

private void after(Method method, Object[] args) {
System.out.println("after " + method.getName());
}
}
  • 创建代理类
1
2
3
4
5
6
7
8
9
10
public class ProxyFactory {

public static <T> T createProxy(T target){
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();

ProxyHandler proxyHandler = new ProxyHandler(target);
return (T) Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);
}
}
  • 调用
1
2
3
4
5
6
7
8
9
public class Client {
public static void main(String[] args) {
//保存动态生成的代理类的class
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

Handler proxy = ProxyFactory.createProxy(new HandlerImpl());
proxy.handle("xxx");
}
}

所以,利用JDK动态代理,只要在InvocationHandler中定义好代理行为,然后在创建代理类再决定将代理行为添加到哪些接口上。只要代理行为是确定的,那么不管要对多少目标类进行代理,以及目标类如何变化,都可以轻松应付了,我们还可以在定义代理行为时根据method来进行判断取舍

实现
  • 代理类的实现

通过工具jad -sjava $Proxy0.class可以看一下生成的代理类class:

它实现了目标类的所有接口,并持有一个InvocationHandler的实例。而InvocationHandler中又持有了目标类的实例,并定义了代理行为。这样代理类最终将操作全部委托给了InvocationHandler,也就不直接依赖于目标类了,实现了解耦。

这样的好处是很明显的,你只要定义好了代理行为,并给定接口,就能创建代理类实例进行执行,这也是Mybatis、Feign等能够实现接口注解的基础。

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
public final class $Proxy0 extends Proxy implements Handler{

public $Proxy0(InvocationHandler invocationhandler){
super(invocationhandler);
}

public final boolean equals(Object obj){
try{
return ((Boolean)super.h.invoke(this, m1, new Object[] {obj})).booleanValue();
}catch(Error _ex) {

}catch(Throwable throwable){
throw new UndeclaredThrowableException(throwable);
}
}

public final void query(String s){
try{
super.h.invoke(this, m4, new Object[] {s});
return;
}catch(Error _ex) {

}catch(Throwable throwable){
throw new UndeclaredThrowableException(throwable);
}
}

public final String toString(){
try{
return (String)super.h.invoke(this, m2, null);
}catch(Error _ex) {

}catch(Throwable throwable){
throw new UndeclaredThrowableException(throwable);
}
}

public final void delete(String s){
try{
super.h.invoke(this, m3, new Object[] {s});
return;
}catch(Error _ex) {

}catch(Throwable throwable){
throw new UndeclaredThrowableException(throwable);
}
}

public final int hashCode(){
try{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}catch(Error _ex) {

}catch(Throwable throwable){
throw new UndeclaredThrowableException(throwable);
}
}

private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0;

//确保所有的接口都已经正确加载
static {
try{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m4 = Class.forName("org.eto.essay.Handler").getMethod("query", new Class[] {
Class.forName("java.lang.String")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("org.eto.essay.Handler").getMethod("delete", new Class[] {
Class.forName("java.lang.String")
});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}catch(NoSuchMethodException nosuchmethodexception){
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}catch(ClassNotFoundException classnotfoundexception){
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
  • 代理实例的创建

首先检查调用者的访问权限,然后尝试从缓存中检查代理类Class是否创建过,如果没有就新建一个,最后反射调用Class的构造器创建实例

java.lang.reflect.Proxy
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
public class Proxy implements java.io.Serializable {

//...

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException{
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone();

//检查调用者是否有接口访问权限
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}

/*
* Look up or generate the designated proxy class.
* 从缓存中获取代理类的Class,如果没有就创建
*/
Class<?> cl = getProxyClass0(loader, intfs);

/*
* Invoke its constructor with the designated invocation handler.
* 调用构造器创建实例
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}

final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}

//...
}

2.2. CGLIB动态代理

CGLIB是Spring提供的一种动态代理创建方式,与JDK动态代理类似,它也提供了MethodInterceptor用来定义代理行为

org.springframework.cglib.proxy.MethodInterceptor
1
2
3
public interface MethodInterceptor extends Callback {
Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable;
}

也提供了一个工厂,给你来创建代理类

org.springframework.cglib.proxy.Enhancer
1
2
3
4
5
6
7
8
9
10
public class Enhancer extends AbstractClassGenerator {

private Class superclass;

private Callback[] callbacks;

// ...

public Object create();
}

不同的是CGLIB不再要求你提供目标类实现的接口了,因为它是通过继承的方式来进行代理,所以要代理的类或方法不能声明为final

示例
  • 定义代理行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ProxyInterceptor implements MethodInterceptor {

private void before(Object object, Method method, Object[] objects) {
System.out.println("before " + method.getName());
}

private void after(Object object, Method method, Object[] objects) {
System.out.println("after " + method.getName());
}

@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
before(object, method, args);
// 注意这里是调用invokeSuper,而不是invoke,否则死循环。invokesuper执行目标类方法,invoke执行的是子类方法
Object result = methodProxy.invokeSuper(object, args);
after(object, method, args);
return result;
}
}
  • 创建代理类
1
2
3
4
5
6
7
8
9
public class ProxyFactory {

public static <T> T createProxy(Class<T> clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new ProxyInterceptor());
return (T)enhancer.create();
}
}
  • 调用
1
2
3
4
5
6
7
8
9
public class Client {
public static void main(String[] args) {
//保存动态生成的代理类的class
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/shanhuiming/");

Handler proxy = ProxyFactory.createProxy(Handler.class);
proxy.handle("xxx");
}
}