介绍

代理模式(Proxy Pattern)又叫委托模式,是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理对象在客户端和目标对象之间起到中介作用,代理模式属于结构性设计模式。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象(执行目标方法前后都可以做额外的处理)。

分类

  • 静态代理:由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
  • 动态代理:动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件(字节码在内存中动态生成的),代理类和委托类的关系是在程序运行时确定。

静态代理

类图

image-20220218103937691

类图说明

Subject:抽象主题角色:可以是抽象类,也可以是接口。抽象主题是一个普通的业务类型,无特殊要求。

RealSubject: 具体主题角色:也叫做被委托角色或被代理角色,是业务逻辑的具体执行者。

Proxy:代理主题角色:也叫做委托类或代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在具体主题角色处理完毕。

示例代码

抽象主题Subject

1
2
3
4
5
public interface Subject {

void request();

}

具体主题角色RealSubject

1
2
3
4
5
6
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("目标方法调用...");
}
}

代理主题角色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
public class Proxy implements Subject{

//需要被代理的对象的引用
private Subject subject;

//通过构造器完成被代理的对象的引用赋值
public Proxy(Subject subject){
this.subject = subject;
}

@Override
public void request() {
//调用目标方法之前的处理
before();
//目标方法调用
subject.request();
//目标方法调用之后的处理
after();
}

public void before(){
System.out.println("目标方法调用之前...");
}

public void after(){
System.out.println("目标方法调用之后...");
}

}

测试代码结果

image-20220218175927057

从示例代码可以看出:

  1. 如果我们的接口新增一个方法,那么所有的实现类都需要实现这个方法,所有的代理类也需要实现这个方法。
  2. 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。
  3. 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

因为以上的一些瑕疵,所以才出现了动态代理。

JDK动态代理

类图

image-20220219145403250

代码

抽象主题Subject

1
2
3
4
5
public interface Subject {

void request();

}

具体主题角色RealSubject

1
2
3
4
5
6
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("目标方法调用...");
}
}

JDK动态代理类

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 class JdkProxy implements InvocationHandler {

//需要被代理的对象的引用
private Object object;

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目标方法执行之前的处理
before();
//目标方法执行
Object result = method.invoke(object, args);
//目标方法执行之后的处理
after();
return result;
}

public void before(){
System.out.println("目标方法执行之前...");
}

public void after(){
System.out.println("目标方法执行之后...");
}

//返回代理对象
public Object getProxyObject(Object object){
this.object = object;
Class<?> clazz = object.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}

}

测试截图
image-20220219151333001

由此我们可以看出:

  1. 动态代理类和被代理类必须继承同一个接口。
  2. 动态代理只能对接口中声明的方法进行代理。
  3. 每一个动态代理实例都有一个关联的InvocationHandler
  4. 通过代理实例调用方法,方法调用请求会被转发给InvocationHandlerinvoke方法。
  5. 只能代理声明在接口中的方法。

CGLIB动态代理

类图

image-20220219154824466

代码

具体主题角色RealSubject

1
2
3
4
5
6
7
public class RealSubject{

public void request() {
System.out.println("目标方法调用...");
}

}

CGLIB动态代理类

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
public class CglibProxy implements MethodInterceptor {

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//执行目标方法之前的处理
before();
//执行目标方法
Object obj = methodProxy.invokeSuper(o, objects);
//执行目标方法之后的处理
after();
//返回目标方法的返回值
return obj;
}

//获取代理对象
public Object getProxyObject(Object object){
Class<?> clazz = object.getClass();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
Object proxyObject = enhancer.create();
return proxyObject;
}

public void before(){
System.out.println("目标方法执行之前...");
}

public void after(){
System.out.println("目标方法执行之后...");
}

}

测试截图

image-20220219152106611

我们发现cglib动态代理不需要代理对象实现任何接口就能实现对对象的代理,这是和cglib的不同之一

静态代理和动态代理的区别

  1. 静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。
  2. 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
  3. 若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。

代理模式和装饰器模式的区别

  1. 代理模式通常在一个代理类中创建一个被代理类的对象,而装饰器模式通常的做法是将原始对象作为一个参数传给装饰者的构造器。
  2. 代理模式关注于控制对对象的访问,然而饰器器模式关注于在一个对象上动态的添加方法。
  3. 代理模式注重的是对对象的某一功能的流程把控和辅助,它可以控制对象做某些事,重心是为了借用对象的功能完成某一流程,而非对象功能如何;装饰器模式注重的是对对象功能的扩展,不关心外界如何调用,只注重对对象功能加强,装饰后还是对象本身。

本章节主要讲解代理模式,至于jdk动态代理和cglib动态代理的原理本章无需深入过多,以后会专门讲解jdk动态代理和cglib动态代理的原理。