设计模式:代理模式

回答

“代理模式是一种结构型设计模式,它通过为其他对象提供一个代理来控制对这个对象的访问。代理模式可以让我们在不改变目标对象的情况下,通过代理对象来控制、增强或延迟操作。

代理模式有多种类型,包括静态代理、动态代理、远程代理、虚拟代理和保护代理。以静态代理为例,代理类与目标类实现相同的接口,代理类在调用目标类方法时可以添加额外的逻辑,如日志记录或权限检查。动态代理则是在运行时生成代理类,通常使用JDK的InvocationHandler接口来实现。

代理模式的优点包括控制对目标对象的访问、添加额外的功能、降低客户端与目标对象的耦合度。但它也会增加系统的复杂性,可能带来一定的性能开销。”

设计模式:代理模式

1. 什么是代理模式?

代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供了一种代理,以控制对这个对象的访问。代理模式可以让我们在不改变目标对象的情况下,通过代理对象来控制对目标对象的访问、增强功能或延迟操作等。

在代理模式中,代理对象通常与目标对象实现相同的接口,这样客户端可以通过代理对象来访问目标对象的功能,而无需知道是否正在使用代理。

代理在原有代码乃至原业务流程都不修改的情况下,直接在业务流程中切入新代码,增加新功能, 这也和Spring的(面向切面编程)很相似

image-20240904125743457

2. 代理模式的分类

代理模式根据不同的使用场景,可以分为以下几种常见类型:

  • 静态代理(静态定义代理类)

    • 简单代理模式,是动态代理的理论基础。常见使用在代理模式
  • 动态代理(动态生成代理类,也称为Jdk自带动态代理)

    • 使用反射完成代理。需要有顶层接口才能使用,常见是mybatis的mapper文件是代理
  • Cglib 、javaassist(字节码操作库)

    • 也是使用反射完成代理,可以直接代理类(jdk动态代理不行),使用字节码技术,不能对 final类进行继承。(需要导入jar包)

3. 代理模式的结构

代理模式一般包含以下几个角色:

  • 抽象主题(Subject):定义了代理类和目标类共同的接口,这样客户端可以通过接口来操作具体对象。
  • 具体主题(RealSubject):实现了抽象主题接口,是实际被代理的对象。
  • 代理类(Proxy):同样实现了抽象主题接口,持有对具体主题的引用,负责在客户端和具体主题之间进行交互。

4. 代理模式的实现

下面我们以静态代理和动态代理为例,分别演示如何在Java中实现代理模式。

静态代理

所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

1. 定义接口和目标对象
1
2
3
4
5
6
7
8
9
10
11
12
// 抽象主题接口
public interface Subject {
void request();
}

// 具体主题类
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
2. 创建代理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 代理类
public class Proxy implements Subject {
private RealSubject realSubject;

public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}

@Override
public void request() {
// 代理可以添加额外的逻辑
System.out.println("Proxy: Checking access before firing a real request.");

// 调用真实对象的方法
realSubject.request();

// 代理可以添加额外的逻辑
System.out.println("Proxy: Logging the time of request.");
}
}
3. 使用代理类
1
2
3
4
5
6
7
8
9
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Proxy proxy = new Proxy(realSubject);

// 通过代理类访问真实对象的方法
proxy.request();
}
}
4. 运行结果
1
2
3
Proxy: Checking access before firing a real request.
RealSubject: Handling request.
Proxy: Logging the time of request.

动态代理(使用JDK)

动态代理也叫做,JDK代理、接口代理。

动态代理的对象,是利用JDK的API,动态的在内存中构建代理对象(是根据被代理的接口来动态生成代理类的class文件,并加载运行的过程),这就叫动态代理

1. 定义接口和目标对象
1
2
3
4
5
6
7
8
9
10
public interface Subject {
void request();
}

public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 动态代理处理器
public class DynamicProxyHandler implements InvocationHandler {
private Object realSubject;

public DynamicProxyHandler(Object realSubject) {
this.realSubject = realSubject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理可以添加额外的逻辑
System.out.println("Dynamic Proxy: Before invoking " + method.getName());

// 调用真实对象的方法
Object result = method.invoke(realSubject, args);

// 代理可以添加额外的逻辑
System.out.println("Dynamic Proxy: After invoking " + method.getName());

return result;
}
}
3. 使用动态代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();

// 创建动态代理对象
Subject proxyInstance = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new DynamicProxyHandler(realSubject)
);

// 通过动态代理访问真实对象的方法
proxyInstance.request();
}
}
4. 运行结果
1
2
3
Dynamic Proxy: Before invoking request
RealSubject: Handling request.
Dynamic Proxy: After invoking request

cglib动态代理

指利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理

什么是CGLIB动态代理

CGLIB动态代理和jdk代理一样,使用反射完成代理,不同的是他可以直接代理类(jdk动态代理不 行,他必须目标业务类必须实现接口),CGLIB动态代理底层使用字节码技术,CGLIB动态代理不 能对 final类进行继承。(CGLIB动态代理需要导入jar包)

5.代理模式的优缺点

优点

  • 控制访问:代理模式可以在不修改原有类的情况下,控制对目标对象的访问。
  • 添加功能:可以在代理类中添加额外的功能,如日志记录、权限控制、延迟加载等,而不需要改变目标类。
  • 解耦:通过代理模式,客户端与目标对象之间的耦合度降低,便于后续维护和扩展。

缺点

  • 增加复杂性:使用代理模式会增加系统的复杂性,尤其是当代理类和目标类之间的关系比较复杂时。
  • 性能开销:代理模式引入了额外的代理对象,可能会对系统的性能产生一定的影响,尤其是在频繁调用时。