Java中的代理模式

什么是代理

代理就是在访问某个对象时需经过代理对象来进行访问,为一个对象提供代理,能够控制对这个对象的访问。在 Java 中根据代理类生成的时间不同的可以分为静态代理动态代理

静态代理

静态代理就是代理类和委托类实现相同的父类方法或者接口,代理类持有委托类的对象的引用,在实现接口方法的内部调用对应委托类的方法,并可以做些其他的处理。

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
//要实现的接口
public interface Subject {
public void handleTask(String name);
}
//真正要执行任务的类
public class RealSubject implements Subject {
@Override
public void handleTask(String name) {
System.out.println("正在执行任务:" + name);
}
}
//代理类
public class ProxySubject implements Subject {
//代理类持有一个委托类的对象引用
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
//可做些预处理或其他的逻辑调用。
@Override
public void handleTask(String name) {
System.out.println("准备执行任务");
subject.handleTask(name);
System.out.println("执行任务结束");
}
}
public class Client {
public static void main(String[] args) {
Subject proxy = new ProxySubject(new RealSubject());
proxy.handleTask("connect DB");
}
}

这就是静态代理,每个接口对应被代理类和代理类,实际使用的时候只对代理类的对象进行操作,从而可以实现被代理类对象相关方法的调用。

  • 静态代理的好处主要是被代理类可以专心处理自己的业务逻辑,不需要关心外界的变动。而代理类又可以根据需要灵活地扩展其中的方法。
  • 静态代理的缺点也很明显,每个被代理类都有一个代理类要维护,如果他们实现的接口或父类方法比较多的话,代理类的维护就变得很复杂。
    基于静态代理的缺点,下面就来看看动态代理是如何解决这个缺陷的。

##动态代理

动态代理用到了 Java 反射的特性,在运行时动态生成代理类。要用到 InvocationHandler 这个接口以及 Proxy.newProxyInstance()这个静态方法。

InvocationHandler 内部只有一个方法需要实现: invoke(Object proxy,Method method,Object[] args) , proxy 为代理对象,method 指示被调用的方法对象,args 为调用方法的参数,实现这个方法就可以控制代理类调用被代理类的方法以及进行其他的操作。

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
public interface Subject {
void handleTask(String name);
}
//被代理的对象
public class RealSubject implements Subject {
public RealSubject() {
}
public void handleTask(String name) {
System.out.println("handle task" + name);
}
}
public class MyInvocationHandler implements InvocationHandler{
private Object sub;
public MyInvocationHandler(Object sub) {
this.sub = sub;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用方法前");
//开始调用被代理类的方法
method.invoke(sub,args);
System.out.println("调用方法后");
return null;
}
}
public class Client {
public static void main(String[] args) {
RealSubject rs = new RealSubject();
InvocationHandler myInvocationHandler = new MyInvocationHandler(rs);
Class<?> cls = rs.getClass();
//动态生成代理类对象
Subject subject = (Subject) Proxy.newProxyInstance(
cls.getClassLoader(),
cls.getInterfaces(),
myInvocationHandler);
subject.handleTask("connect DB");
}
}

这里可以看到 MyInvocationhandler 都不需要知道对哪个被代理类进行操作,完全通过 invoke 方法内部的method.invoke(sub,args);来对被代理类方法进行调用。

动态代理的好处就是相比于静态代理不用把每个接口方法再写一遍,而是集中处理接口内所有方法的代理过程。而且可以用于代理多个类,如果这些类需要的外围业务逻辑是一样的话。这样就很方便维护了。
动态代理的不足就是只能代理实现了接口的类,而不能代理拥有共同父类的类。
在 Android 开发中,动态代理还出现于 Retrofit 框架中,其内部的 create()方法直接生成实现对应接口的类,解析接口方法中的每个注解方法和参数来封装成一个个 Request 请求,大大简化了开发。虽然动态代理是运行时反射,存在一定的性能损耗,但相比于网络请求的时间可以忽略不计。