设计模式代理模式

本文最后更新于:2024年3月18日 凌晨

设计模式代理模式

  • 为其他对象提供一种代理以便控制对这个对象的访问。
  • 可以详细控制访问某个类(对象)的方法,在调用这个方法前作的前置处理(统一的流程代码放到代理中处理),调用这个方法后做后置处理。
  • 代理模式分类:
    • 静态代理(静态定义代理类,自定义静态定义的代理类)
    • 动态代理(通过程序动态生成代理类,该代理类不是我们自己定义的,而是由程序自动生成)

静态代理

静态代理角色分析

  • 抽象角色:一般使用接口或者抽象类来实现。
  • 真实角色:被代理的角色。
  • 代理角色:代理真实角色,代理真实角色后,一般会做一些附属的操作。
  • 客户:使用代理角色来进行一些操作。

实例

  • Rent.java 即抽象角色。
1
2
3
4
// 抽象角色:租房。
public interface Rent {
public void rent();
}
  • Host.java 即真实角色。
1
2
3
4
5
6
// 真实角色:房东,房东要出租房子。
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
  • Proxy.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
// 代理角色:中介。
public class Proxy implements Rent {

private Host host;
public Proxy() { }
public Proxy(Host host) {
this.host = host;
}

// 租房。
public void rent(){
seeHouse();
host.rent();
fare();
}
// 看房。
public void seeHouse(){
System.out.println("带房客看房");
}
// 收中介费。
public void fare(){
System.out.println("收中介费");
}
}
  • Client.java 即客户。
1
2
3
4
5
6
7
8
9
10
11
12
// 客户类,一般客户都会去找代理。
public class Client {
public static void main(String[] args) {
// 房东要租房。
Host host = new Host();
// 中介帮助房东。
Proxy proxy = new Proxy(host);

// 客户去找中介。
proxy.rent();
}
}

分析

在这个过程中,客户直接接触的就是中介,就如同现实生活中的样子,客户看不到房东,但是依旧通过代理租到了房东的房子,这就是所谓的代理模式。

  • 静态代理的优点。
    • 可以使得我们的真实角色更加纯粹。不再去关注一些公共的事情。
    • 公共的业务由代理来完成。实现了业务的分工。
    • 公共业务发生扩展时变得更加集中和方便。
  • 静态代理的缺点。
    • 类多了,多了代理类,工作量变大了。开发效率降低。
    • 我们想要静态代理的好处,又不想要静态代理的缺点,所以,就有了动态代理。

静态代理再理解

实例

  1. 创建一个抽象角色,比如平时做的用户业务,抽象起来就是增删改查。
1
2
3
4
5
6
7
// 抽象角色:增删改查业务。
public interface UserService {
void add();
void delete();
void update();
void query();
}
  1. 需要一个真实对象来完成这些增删改查操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 真实对象,完成增删改查操作的人。
public class UserServiceImpl implements UserService {

public void add() {
System.out.println("增加了一个用户");
}

public void delete() {
System.out.println("删除了一个用户");
}

public void update() {
System.out.println("更新了一个用户");
}

public void query() {
System.out.println("查询了一个用户");
}
}
  1. 如果现在需要增加一个日志功能,怎么实现?

    • 思路1 :在实现类上增加代码(需要修改已有的代码)
    • 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能。
  2. 设置一个代理类来处理日志。

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 UserServiceProxy implements UserService {
private UserServiceImpl userService;

public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}

public void add() {
log("add");
userService.add();
}

public void delete() {
log("delete");
userService.delete();
}

public void update() {
log("update");
userService.update();
}

public void query() {
log("query");
userService.query();
}

public void log(String msg){
System.out.println("执行了"+msg+"方法");
}

}
  1. 测试访问类:
1
2
3
4
5
6
7
8
9
10
11
12
public class Client {
public static void main(String[] args) {
// 真实业务。
UserServiceImpl userService = new UserServiceImpl();
// 代理类。
UserServiceProxy proxy = new UserServiceProxy();
// 使用代理类实现日志功能。
proxy.setUserService(userService);

proxy.add();
}
}
  • 在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想。

动态代理

  • AOP的底层机制就是动态代理。
  • 动态代理的角色和静态代理的一样,动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的。
  • 动态代理分类
    • 基于接口的动态代理:JDK动态代理。
    • 基于类的动态代理:cglib
    • 基于Java 字节码实现的动态代理:javasist来生成动态代理。
  • 优点
    • 可以使得我们的真实角色更加纯粹。不再去关注一些公共的事情,公共的业务由代理来完成。实现了业务的分工,公共业务发生扩展时变得更加集中和方便。
    • 一个动态代理一般代理某一类业务,一个动态代理可以代理多个类,代理的是接口。

JDK的动态代理需要了解两个类

InvocationHandler

1
public interface InvocationHandler
  • InvocationHandler是由代理实例的调用处理程序实现的接口。
  • 每个代理实例都有一个关联的调用处理程序,当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invock方法。
1
Object invoke(Object proxy, Method method, Object[] args);

参数

  • proxy:调用该方法的代理实例。
  • method:所述方法对应于调用代理实例上的接口方法的实例,方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
  • args:包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数,原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integerjava.lang.Boolean

Proxy

  • Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
  • 动态代理类(以下简称为代理类)是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为:
    • 代理接口是由代理类实现的接口,代理实例是代理类的一个实例。
    • 每个代理都有一个关联的处理程序对象,它实现了接口InvocationHandler
    • 通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例, java. lang. reflect. Method被调用方法的java. lang. reflect.Method对象以及包含参数的类型Object object的数组。
    • 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。
1
public static Object newProxyInstance( ClassLoader loader, Class<?> interfaces, InvocationHandler h) throws OllegalArgumentException

参数

  • loader:用哪个类加载器去加载代理对象。
  • interfaces:动态代理类需要实现的接口。
  • h:动态代理方法在执行时,会调用h里面的invoke方法去执行。

实例

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
public class Test {
// 接口。
static interface Subject{
void sayHi();
void sayHello();
}

// 默认实现类(可以反射生成)
static class SubjectImpl implements Subject{
@Override
public void sayHi() {
System.out.println("hi");
}
@Override
public void sayHello() {
System.out.println("hello");
}
}

// JDK动态代理。
static class ProxyInvocationHandler implements InvocationHandler{
private Subject target;

public ProxyInvocationHandler(Subject target) {
this.target=target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print("say:");
return method.invoke(target, args);
}
}

public static void main(String[] args) {
Subject subject=new SubjectImpl();
Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
subjectProxy.sayHi();
subjectProxy.sayHello();
}
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!