模式组合应用-代理模式

写在前面

Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!


代理模式

定义

结构型设计模式, 为一个对象提供一个替身或占位符, 进而控制对这个对象的访问。

通过引入一个代理对象, 将客户端对真实对象的直接访问进行封装和控制, 从而在不改变真实对象的情况下, 增加额外的功能或限制。

角色

  • 抽象主题: 定义了真实主题和代理主题的共同接口, 使得在任何可以使用真实主题的地方都可以使用代理主题。

  • 真实主题: 定义了代理所代表的真实对象, 它包含了业务逻辑, 是代理模式最终要访问的对象。

  • 代理主题: 持有对真实主题的引用, 并实现与真实主题相同的接口, 代理对象可以在将请求转发给真实主题之前或之后, 执行一些预处理或 后处理的逻辑。

使用时主要思考点

  1. 明确代理目的: 在引入代理模式前, 首先要明确代理的目的是什么, 如控制访问(权限控制、远程代理等)、增强功能(如 日志、缓存、事务), 不同的目的可能导致代理的实现方式和复杂程度不同。

  2. 接口一致性: 代理对象和真实对象必须实现相同的接口。

  3. 性能开销: 引入代理会增加一层间接性, 这可能会带来一定的性能开销, 在对性能要求极高的场景下, 需要仔细评估。

  4. 避免过度设计: 一些简单功能可以通过直接调用实现, 引入代理会使系统变得过于复杂。


JDK 动态代理

JDK动态代理是Java语言自动的代理机制, 基于接口实现。这意味着只有实现了接口的类才能使用JDK动态代理。

核心接口

  • InvocationHandler 接口: 代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。当代理实例上调用方法时, 方法调用将被编码并分派到其调用处理程序的 invoke 方法。

  • Proxy类: 提供了创建动态代理类和实例的静态方法。

代码示例

抽象主题
复制代码
public interface UserService {

    void registerUser(String username, String password);

    void login(String username, String password);

}
真实主题
复制代码
public class UserServiceImpl implements UserService {

    @Override
    public void registerUser(String username, String password) {
        System.out.println("开始执行用户注册业务逻辑: " + username);

        if (username != null && !username.isEmpty() && password != null && !password.isEmpty()) {
            System.out.println("用户 " + username + " 注册成功!");
        }
        else {
            System.out.println("用户注册失败: 用户名或密码不能为空。");
        }

        System.out.println("用户注册业务逻辑执行完毕。");

    }

    @Override
    public void login(String username, String password) {

        System.out.println("开始执行用户登陆业务逻辑: " + username);
        if ("admin".equals(username) && "123".equals(password)) {
            System.out.println("用户 " + username + " 登录成功!");
        }
        else {
            System.out.println("用户 " + username + " 登录失败: 用户名或密码错误。");
        }

        System.out.println("用户登录业务逻辑执行完毕。");
    }

}
动态代理处理器
复制代码
public class PerformanceLogInvocationHandler implements InvocationHandler {

    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        long startTime = System.currentTimeMillis();

        System.out.println("\n--- 动态代理前置处理: 方法 " + method.getName() + "开始执行, 参数: " + Arrays.toString(args) + " ---");
        Object result = null;

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

        } catch (Exception e) {

            System.out.println("动态代理异常处理: 方法 " + method.getName() + " 执行异常: " + e.getMessage());
            throw e.getCause();

        } finally {

            long endTime = System.currentTimeMillis();

            System.out.println("--- 动态代理后置处理: 方法 " + method.getName() + " 执行完毕, 耗时: " + (endTime - startTime) + " 毫秒 ---");
            System.out.println("操作日志: 方法 " + method.getName() + " 在 " + new Date() + " 被调用。");
        }

        return result;

    }
}
测试类
复制代码
public class JdkDynamicProxyTest {

    @Test
    public void test_jdkProxy() {

        System.out.println("--- 使用 JDK 动态代理进行用户服务操作 ---");
        UserService realUserService = new UserServiceImpl();
        Class<? extends UserService> aClass = realUserService.getClass();
        UserService proxyUserService = (UserService) Proxy.newProxyInstance(
                aClass.getClassLoader(), // 类加载器
                aClass.getInterfaces(), // 真实对象实现的接口器
                new PerformanceLogInvocationHandler(realUserService) // 调用处理器
        );

        System.out.println("\n--- 第一次操作: 注册用户 ---");
        proxyUserService.registerUser("赵六", "789012");

        System.out.println("\n--- 第二次操作: 登录用户 ---");
        proxyUserService.login("admin", "123");

        System.out.println("\n--- 第三次操作: 登录失败 ---");
        proxyUserService.login("guest", "wrongpass");

    }

}
运行结果
复制代码
--- 使用 JDK 动态代理进行用户服务操作 ---

--- 第一次操作: 注册用户 ---

--- 动态代理前置处理: 方法 registerUser开始执行, 参数: [赵六, 789012] ---
开始执行用户注册业务逻辑: 赵六
用户 赵六 注册成功!
用户注册业务逻辑执行完毕。
--- 动态代理后置处理: 方法 registerUser 执行完毕, 耗时: 0 毫秒 ---
操作日志: 方法 registerUser 在 Mon Sep 29 10:35:32 CST 2025 被调用。

--- 第二次操作: 登录用户 ---

--- 动态代理前置处理: 方法 login开始执行, 参数: [admin, 123] ---
开始执行用户登陆业务逻辑: admin
用户 admin 登录成功!
用户登录业务逻辑执行完毕。
--- 动态代理后置处理: 方法 login 执行完毕, 耗时: 0 毫秒 ---
操作日志: 方法 login 在 Mon Sep 29 10:35:32 CST 2025 被调用。

--- 第三次操作: 登录失败 ---

--- 动态代理前置处理: 方法 login开始执行, 参数: [guest, wrongpass] ---
开始执行用户登陆业务逻辑: guest
用户 guest 登录失败: 用户名或密码错误。
用户登录业务逻辑执行完毕。
--- 动态代理后置处理: 方法 login 执行完毕, 耗时: 0 毫秒 ---
操作日志: 方法 login 在 Mon Sep 29 10:35:32 CST 2025 被调用。

Process finished with exit code 0

CGLIB 动态代理

CGLIB是一个字节码生成库, 它可以在运行时扩展 Java类和实现 Java 接口。CGLIB通过继承方式实现代理, 它会生成一个真实对象的子类作为代理类, 因此可以代理没有实现接口的类。

核心接口

  • MethodInterceptor : 接口通过唯一的 intercept方法实现对代理对象方法调用的拦截。核心是通过 MethodProxy.invokeSuper()调用目标类的原始方法,并在前后插入自定义逻辑。

注意事项

  • 必须通过 proxy.invokeSuper(obj, args) 调用目标类的原始方法, method.invoke(obj,args) 会使对象再次触发连接器。

代码示例

真实主题
复制代码
public class ConfigurationService {

    private Map<String, String> configCache = new HashMap<>();

    public String getConfig(String key) {

        System.out.println("正在从数据源加载配置项: " + key + "...");

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        String value = "配置值_" + key + "_" + System.currentTimeMillis();
        System.out.println("加载完成, 配置项 " + key + " 的值为: " + value);
        return value;
    }

    public void updateConfig(String key, String value) {
        System.out.println("正在更新配置项: " + key + " 为 " + value + "...");
        System.out.println("配置项 " + key + " 更新成功。");
    }
}
CGLIB 动态代理拦截器
复制代码
public class CacheAndLogMethodInterceptor implements MethodInterceptor {

    private Object target;

    private Map<String, Object> cache = new HashMap<>();

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

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

        System.out.println("\n--- CGLIB 代理前置处理: 方法 " + method.getName() + " 被调用, 参数: " + Arrays.toString(args) + " ---");

        if (method.getName().equals("getConfig") && args.length == 1 && args[0] instanceof String) {
            String key = (String) args[0];
            if (cache.containsKey(key)) {
                System.out.println("CGLIB 代理: 从缓存中获取配置项: " + key);
                return cache.get(key);
            }
        }

        Object result = null;

        try {
            result = proxy.invokeSuper(obj, args);

            if (method.getName().equals("getConfig") && args.length == 1 && args[0] instanceof String && result != null) {
                String key = (String) args[0];
                cache.put(key, result);
                System.out.println("CGLIB 代理: 将配置项 " + key + " 放入缓存。");
            }

        } catch (Exception e) {
            System.out.println("CGLIB 代理异常处理: 方法 " + method.getName() + " 执行异常: " + e.getMessage());

            throw e.getCause();

        } finally {
            System.out.println("--- CGLIB 代理后置处理: 方法 " + method.getName() + " 执行完毕。---");
        }

        return result;
    }

}
测试类
复制代码
public class CglibDynamicProxyTest {

    @Test
    public void test_cglib() {
        System.out.println("--- 使用 CGLIB 动态代理进行配置服务操作 ---");

        ConfigurationService realConfigService = new ConfigurationService();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ConfigurationService.class); // 设置父类 即代理的类
        enhancer.setCallback(new CacheAndLogMethodInterceptor(realConfigService)); // 设置回调函数
        ConfigurationService proxyConfigService = (ConfigurationService) enhancer.create(); // 创建代理对象

        System.out.println("\n--- 第一次获取配置: config_key_A ---");
        proxyConfigService.getConfig("config_key_A");

        System.out.println("\n--- 第二次获取配置: config_key_A(从缓存中获取) ---");
        proxyConfigService.getConfig("config_key_A");

        System.out.println("\n--- 第三次获取配置: config_key_B ---");
        proxyConfigService.getConfig("config_key_B");

        System.out.println("\n--- 第四次更新配置: config_key_A ---");
        proxyConfigService.updateConfig("config_key_A", "new_value_A");

        System.out.println("\n--- 第五次获取配置: config_key_A(更新后应重新加载) ---");
        proxyConfigService.getConfig("config_key_A");

    }

}
运行结果
复制代码
--- 使用 CGLIB 动态代理进行配置服务操作 ---

--- 第一次获取配置: config_key_A ---

--- CGLIB 代理前置处理: 方法 getConfig 被调用, 参数: [config_key_A] ---
正在从数据源加载配置项: config_key_A...
加载完成, 配置项 config_key_A 的值为: 配置值_config_key_A_1759113258603
CGLIB 代理: 将配置项 config_key_A 放入缓存。
--- CGLIB 代理后置处理: 方法 getConfig 执行完毕。---

--- 第二次获取配置: config_key_A(从缓存中获取) ---

--- CGLIB 代理前置处理: 方法 getConfig 被调用, 参数: [config_key_A] ---
CGLIB 代理: 从缓存中获取配置项: config_key_A

--- 第三次获取配置: config_key_B ---

--- CGLIB 代理前置处理: 方法 getConfig 被调用, 参数: [config_key_B] ---
正在从数据源加载配置项: config_key_B...
加载完成, 配置项 config_key_B 的值为: 配置值_config_key_B_1759113258715
CGLIB 代理: 将配置项 config_key_B 放入缓存。
--- CGLIB 代理后置处理: 方法 getConfig 执行完毕。---

--- 第四次更新配置: config_key_A ---

--- CGLIB 代理前置处理: 方法 updateConfig 被调用, 参数: [config_key_A, new_value_A] ---
正在更新配置项: config_key_A 为 new_value_A...
配置项 config_key_A 更新成功。
--- CGLIB 代理后置处理: 方法 updateConfig 执行完毕。---

--- 第五次获取配置: config_key_A(更新后应重新加载) ---

--- CGLIB 代理前置处理: 方法 getConfig 被调用, 参数: [config_key_A] ---
CGLIB 代理: 从缓存中获取配置项: config_key_A

Process finished with exit code 0

代理模式+装饰器模式

装饰器模式

结构型设计模式, 允许在不改变原有对象结构的情况下, 动态地给对象添加新的功能。

通过创建一个包装对象, 来包裹真实的对象, 并在不改变其接口的前提下增强其功能。

案例

假设正在开发一个文件管理系统, 用户可以对文件进行读写操作。对于不同的文件或不同的用户, 我们可能需要动态地组合多种文件操作功能。例如:

  1. 权限控制: 只有特定用户才能访问某些文件。

  2. 数据加密: 敏感文件在写入时需要加密, 读取时需要解密。

  3. 数据压缩: 大文件在传输或存储时需要压缩, 读取时需要解压。

模式职责

  • 代理模式: 作为文件操作的入口, 提供基础的访问控制或预处理, 例如检查文件是否存在、用户是否有权限进行操作等, 确保了文件操作的合法性。

  • 装饰器模式: 动态地添加额外的功能, 如加密、解密、压缩、解压缩等。

代码示例

抽象组件/抽象主题
复制代码
public interface FileOperation {

    void write(String filePath, String content);

    String read(String filePath);

}
  • 定义了文件读写操作的抽象, 是代理和装饰器模式共同的接口, 确保了它们可以相互兼容和组合。
具体组件/真实主题
复制代码
public class RealFileOperation implements FileOperation {

    private String fileContent = "";

    @Override
    public void write(String filePath, String content) {

        System.out.println("\n[真实文件操作] 正在将内容写入文件: " + filePath);
        this.fileContent = content;
        System.out.println("写入成功, 文件内容为: " + content);

    }

    @Override
    public String read(String filePath) {

        System.out.println("\n[真实文件操作] 正在从文件读取内容: " + filePath);
        System.out.println("读取成功, 文件内容为: " + fileContent);

        return fileContent;
    }
}
  • 实现了 FileOperation 接口, 负责模拟实际的文件读写操作, 不包含任何权限、加密或压缩逻辑。
代理主题
复制代码
public class FileAccessProxy implements FileOperation {

    /**
     * 持有实际操作文件类的引用
     */
    private RealFileOperation realFileOperation;

    private String currentUser;

    public FileAccessProxy(String currentUser) {
        this.realFileOperation = new RealFileOperation();
        this.currentUser = currentUser;
    }

    private boolean checkPermission(String filePath, String operationType) {

        if (filePath.contains("sensitive") && "write".equals(operationType) && !"admin".equals(currentUser)) {
            System.out.println("[文件访问代理] 权限不足: 用户 " + currentUser + " 无权写入敏感文件: " + filePath);
            return false;
        }

        System.out.println("[文件访问代理] 权限检查通过: 用户 " + currentUser + " 可以执行 " + operationType + " 操作。");
        return true;
    }

    @Override
    public void write(String filePath, String content) {
        if (this.checkPermission(filePath, "write")) {
            realFileOperation.write(filePath, content);
        }
    }

    @Override
    public String read(String filePath) {
        if (this.checkPermission(filePath, "read")) {
            return realFileOperation.read(filePath);
        }

        return "";
    }
}
  • 实现了 FileOperation 接口, 并持有一个 RealFileOperation 实例。主要职责是在调用真实文件操作之前, 进行用户权限的检查。如果权限不足则阻止操作;否则,将请求转发给真实文件操作类。
抽象装饰器类
复制代码
public abstract class FileOperationDecorator implements FileOperation {

    protected FileOperation decoratedFileOperation;

    public FileOperationDecorator(FileOperation decoratedFileOperation) {
        this.decoratedFileOperation = decoratedFileOperation;
    }

    @Override
    public void write(String filePath, String content) {
        decoratedFileOperation.write(filePath, content);
    }

    @Override
    public String read(String filePath) {
        return decoratedFileOperation.read(filePath);
    }

}
  • 实现了 FileOperation 接口, 并包含一个 FileOperation 类型的成员变量, 用于引用被装饰的对象, 默认将所有方法调用转发给被装饰的对象。
具体装饰器类
复制代码
public class EncryptionDecorator extends FileOperationDecorator {

    public EncryptionDecorator(FileOperation decoratedFileOperation) {
        super(decoratedFileOperation);
    }

    private String encrypt(String data) {
        System.out.println("[加密装饰器] 正在加密数据...");
        return Base64.getEncoder().encodeToString(data.getBytes(StandardCharsets.UTF_8));
    }

    private String decrypt(String encryptedData) {
        System.out.println("[加密装饰器] 正在解密数据...");
        return new String(Base64.getDecoder().decode(encryptedData), StandardCharsets.UTF_8);
    }

    @Override
    public void write(String filePath, String content) {
        String encryptedContent = this.encrypt(content);
        System.out.println("[加密装饰器] 加入加密后的内容。");
        decoratedFileOperation.write(filePath, encryptedContent);
    }

    @Override
    public String read(String filePath) {
        String encryptedContent = decoratedFileOperation.read(filePath);
        if (encryptedContent != null && !encryptedContent.isEmpty()) {
            System.out.println("[加密装饰器] 读取到加密内容, 正在解密。");
            return decrypt(encryptedContent);
        }

        return "";
    }
}

public class CompressionDecorator extends FileOperationDecorator {

    public CompressionDecorator(FileOperation decoratedFileOperation) {
        super(decoratedFileOperation);
    }

    private byte[] compress(String data) throws IOException {
        System.out.println("[压缩装饰器] 正在压缩数据...");

        byte[] input = data.getBytes(StandardCharsets.UTF_8);
        Deflater deflater = new Deflater();
        deflater.setInput(input);
        deflater.finish();

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(input.length);
        byte[] buffer = new byte[1024];
        while (!deflater.finished()) {
            int count = deflater.deflate(buffer);
            outputStream.write(buffer, 0, count);
        }

        outputStream.close();
        return outputStream.toByteArray();
    }

    private String decompress(byte[] compressedData) throws IOException, DataFormatException {

        System.out.println("[压缩装饰器] 正在解压缩数据...");
        Inflater inflater = new Inflater();
        inflater.setInput(compressedData);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(compressedData.length);
        byte[] buffer = new byte[1024];
        while (!inflater.finished()) {
            int count = inflater.inflate(buffer);
            outputStream.write(buffer, 0, count);
        }

        outputStream.close();
        return new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
    }

    @Override
    public void write(String filePath, String content) {

        try {
            byte[] compressedContent = this.compress(content);
            String encodedCompressedContent = Base64.getEncoder().encodeToString(compressedContent);
            System.out.println("[压缩装饰器] 写入压缩并编码后的内容。");
            decoratedFileOperation.write(filePath, encodedCompressedContent);
        } catch (IOException e) {
            System.out.println("压缩写入失败: " + e.getMessage());
        }

    }

    @Override
    public String read(String filePath) {

        String encodedCompressedContent = decoratedFileOperation.read(filePath);

        if (encodedCompressedContent != null && !encodedCompressedContent.isEmpty()) {

            try {
                // 将 Base64 字符串解码为字节数组
                byte[] compressedContent = Base64.getDecoder().decode(encodedCompressedContent);
                System.out.println("[压缩装饰器] 读取到压缩内容, 正在解压缩。");
                return this.decompress(compressedContent);

            } catch (IOException | DataFormatException e) {

                System.out.println("解压缩读取失败: " + e.getMessage());

            }

        }

        return "";
    }

}
  • EncryptionDecorator 具体的加密装饰器, 继承 FileOperationDecorator, 在 write 方法中对内容进行加密后再写入, 在 read 方法中读取加密内容后再解密。

  • CompressionDecorator 具体的压缩装饰器, 继承 FileOperationDecorator, 在 write 方法中对内容进行压缩后再写入, 在 read 方法中读取压缩内容后再解压缩。

测试类
复制代码
public class ProxyDecoratorText {

    @Test
    public void test_proxyAndDecorator() {

        System.out.println("\n--- 场景1: 普通用户写入普通文件 ---");
        FileOperation normalUserFileOp = new FileAccessProxy("user1");
        normalUserFileOp.write("normal_file.txt", "这是一段普通文本内容。");
        String readContent1 = normalUserFileOp.read("normal_file.txt");
        System.out.println("最终读取内容: " + readContent1);

        System.out.println("\n--- 场景2: 普通用户尝试写入敏感文件 ---");
        normalUserFileOp.write("sensitive_data.txt", "这是敏感数据。");

        System.out.println("\n--- 场景3: 管理员写入加密的敏感文件 ---");
        FileOperation adminFileOp = new FileAccessProxy("admin");
        EncryptionDecorator encryptionAdminFileOp = new EncryptionDecorator(adminFileOp);
        encryptionAdminFileOp.write("sensitive_encrypted_file.txt", "这是一段需要加密的敏感信息。");
        String readContent2 = encryptionAdminFileOp.read("sensitive_encrypted_file.txt");
        System.out.println("最终读取内容: " + readContent2);

        System.out.println("\n--- 场景4: 管理员写入压缩并加密的大文件 ---");
        String largeContent = "这是一段非常非常长的大文本内容,需要进行压缩和加密处理,以节省存储空间并保证数据安全。";
        CompressionDecorator compressionEncryptedAdminFileOp = new CompressionDecorator(new EncryptionDecorator(adminFileOp));
        compressionEncryptedAdminFileOp.write("large_compressed_encrypted_file.txt", largeContent);
        String readContent3 = compressionEncryptedAdminFileOp.read("large_compressed_encrypted_file.txt");
        System.out.println("最终读取内容: " + readContent3);

    }

}
运行结果
复制代码
--- 场景1: 普通用户写入普通文件 ---
[文件访问代理] 权限检查通过: 用户 user1 可以执行 write 操作。

[真实文件操作] 正在将内容写入文件: normal_file.txt
写入成功, 文件内容为: 这是一段普通文本内容。
[文件访问代理] 权限检查通过: 用户 user1 可以执行 read 操作。

[真实文件操作] 正在从文件读取内容: normal_file.txt
读取成功, 文件内容为: 这是一段普通文本内容。
最终读取内容: 这是一段普通文本内容。

--- 场景2: 普通用户尝试写入敏感文件 ---
[文件访问代理] 权限不足: 用户 user1 无权写入敏感文件: sensitive_data.txt

--- 场景3: 管理员写入加密的敏感文件 ---
[加密装饰器] 正在加密数据...
[加密装饰器] 加入加密后的内容。
[文件访问代理] 权限检查通过: 用户 admin 可以执行 write 操作。

[真实文件操作] 正在将内容写入文件: sensitive_encrypted_file.txt
写入成功, 文件内容为: 6L+Z5piv5LiA5q616ZyA6KaB5Yqg5a+G55qE5pWP5oSf5L+h5oGv44CC
[文件访问代理] 权限检查通过: 用户 admin 可以执行 read 操作。

[真实文件操作] 正在从文件读取内容: sensitive_encrypted_file.txt
读取成功, 文件内容为: 6L+Z5piv5LiA5q616ZyA6KaB5Yqg5a+G55qE5pWP5oSf5L+h5oGv44CC
[加密装饰器] 读取到加密内容, 正在解密。
[加密装饰器] 正在解密数据...
最终读取内容: 这是一段需要加密的敏感信息。

--- 场景4: 管理员写入压缩并加密的大文件 ---
[压缩装饰器] 正在压缩数据...
[压缩装饰器] 写入压缩并编码后的内容。
[加密装饰器] 正在加密数据...
[加密装饰器] 加入加密后的内容。
[文件访问代理] 权限检查通过: 用户 admin 可以执行 write 操作。

[真实文件操作] 正在将内容写入文件: large_compressed_encrypted_file.txt
写入成功, 文件内容为: ZUp3dHlzOE93VEFBQitCWGI1ZXRZa3BFTW9aT1NJWXRtZFlCNlZqU2g5RmYvNXk4QWdlWDcvUUZzM2Fsc3BvNGVZL1ZEbHIvTFl6ZnBLalBiamx5b2dQTElQdlB3S01nNFVTRDJZWUR4MnppaHhZTGpud1B4VkNuZnM1K3g3Nk9JVSs4b0xpVVNCcmZQdVBxaHY1aFRSVVVkY1hWVFNYa0dGbnpKc2tYa2JCYit3PT0=
[文件访问代理] 权限检查通过: 用户 admin 可以执行 read 操作。

[真实文件操作] 正在从文件读取内容: large_compressed_encrypted_file.txt
读取成功, 文件内容为: ZUp3dHlzOE93VEFBQitCWGI1ZXRZa3BFTW9aT1NJWXRtZFlCNlZqU2g5RmYvNXk4QWdlWDcvUUZzM2Fsc3BvNGVZL1ZEbHIvTFl6ZnBLalBiamx5b2dQTElQdlB3S01nNFVTRDJZWUR4MnppaHhZTGpud1B4VkNuZnM1K3g3Nk9JVSs4b0xpVVNCcmZQdVBxaHY1aFRSVVVkY1hWVFNYa0dGbnpKc2tYa2JCYit3PT0=
[加密装饰器] 读取到加密内容, 正在解密。
[加密装饰器] 正在解密数据...
[压缩装饰器] 读取到压缩内容, 正在解压缩。
[压缩装饰器] 正在解压缩数据...
最终读取内容: 这是一段非常非常长的大文本内容,需要进行压缩和加密处理,以节省存储空间并保证数据安全。

Process finished with exit code 0

组合优势

  1. 代理模式负责基础的访问控制, 而装饰器模式负责功能增强, 使得每个类的职责更加单一。

  2. 装饰器模式允许在运行时动态地组合多种功能, 代理模式作为功能链的起点, 确保所有操作都经过必要的访问控制, 为后续的功能增强提供了安全的基础。

代理模式+策略模式

策略模式

行为型设计模式, 定义一系列算法, 将每个算法封装并使它们可以互相替换。

案例

在电商支付系统中, 用户可以选择多种支付方式(如信用卡支付、支付宝支付、微信支付), 每种支付方式的实现细节不同, 但它们都提供了一个统一的 支付接口, 同时, 为确保系统的安全性和合规性, 所有支付操作都需要进行严格的安全审计。

模式职责

  • 代理模式: 作为支付操作的统一入口, 负责在实际支付操作执行前后进行安全审计、日志记录、身份验证等非核心业务逻辑, 确保了所有支付请求都经过必要的安全检查。

  • 策略模式: 封装不同的支付算法, 使得这些支付方式可以相互替换, 上下文根据用户的选择, 动态地使用不同的支付策略来完成支付。

代码示例

抽象策略
复制代码
public interface PaymentStrategy {

    void pay(double amount);

}
  • 定义了具体支付方式必须实现的 pay(double amount) 方法。
具体策略
复制代码
public class CreditCardPaymentStrategy implements PaymentStrategy {

    private String cardNumber;

    private String expiryDate;

    public CreditCardPaymentStrategy(String cardNumber, String expiryDate) {
        this.cardNumber = cardNumber;
        this.expiryDate = expiryDate;
    }

    @Override
    public void pay(double amount) {

        System.out.println("[信用卡支付策略] 正在使用信用卡 " + cardNumber + " (有效期: " + expiryDate + ") 支付" + amount + " 元。");
        System.out.println("[信用卡支付策略] 支付成功。");

    }

}

public class AlipayPaymentStrategy implements PaymentStrategy {

    private String userId;

    public AlipayPaymentStrategy(String userId) {
        this.userId = userId;
    }

    @Override
    public void pay(double amount) {

        System.out.println("[支付宝支付策略] 正在使用支付宝账户 " + userId + " 支付 " + amount + " 元。");
        System.out.println("[支付宝支付策略] 支付成功。");

    }
}

public class WechatPaymentStrategy implements PaymentStrategy {

    private String openId;

    public WechatPaymentStrategy(String openId) {
        this.openId = openId;
    }

    @Override
    public void pay(double amount) {
        System.out.println("[微信支付策略] 正在使用微信账户 " + openId + " 支付 " + amount + " 元。");
        System.out.println("[微信支付策略] 支付成功。");
    }
}
  • CreditCardPaymentStrategyAlipayPaymentStrategyWechatPaymentStrategy 分别实现了 PaymentStrategy 接口, 并封装了各自支付方式的详细逻辑, 具体策略类之间可以相互替换。
抽象主题/策略上下文接口
复制代码
public interface PaymentService {

    void processPayment(double amount, PaymentStrategy strategy);

}
  • 定义了 processPayment(double amount, PaymentStrategy strategy) 方法, 接受支付金额和 PaymentStrategy 作为参数。
真实主题
复制代码
public class RealPaymentService implements PaymentService {

    @Override
    public void processPayment(double amount, PaymentStrategy strategy) {
        System.out.println("\n[真实支付服务] 开始处理支付请求, 金额: " + amount + " 元。");

        if (strategy == null) {
            System.out.println("[真实支付服务] 错误: 未指定支付策略。");
            return;
        }

        strategy.pay(amount);
        System.out.println("[真实支付服务] 支付请求处理完毕。");
    }
}
  • 持有一个 PaymentStrategy 的引用, 并在 processPayment 方法中调用该策略的 pay 方法来完成实际支付。
代理主题
复制代码
public class SecurityAuditPaymentProxy implements PaymentService {

    private RealPaymentService realPaymentService;

    private String currentUserId;

    public SecurityAuditPaymentProxy(RealPaymentService realPaymentService, String currentUserId) {
        this.realPaymentService = realPaymentService;
        this.currentUserId = currentUserId;
    }

    private boolean preAudit(double amount, PaymentStrategy strategy) {
        System.out.println("\n[安全审计代理] 前置审计: 用户 " + currentUserId + " 尝试支付 " + amount + " 元, 使用策略: " + strategy.getClass().getSimpleName() + " 在 " + new Date());

        if ("blockedUser".equals(currentUserId)) {
            System.out.println("[安全审计代理] 审计失败: 用户 " + currentUserId + "被阻止支付。");
            return false;
        }

        System.out.println("[安全审计代理] 前置审计通过。");
        return true;
    }

    private void postAudit(double amount, PaymentStrategy strategy) {
        System.out.println("[安全审计代理] 后置审计: 用户 " + currentUserId + " 支付 " + amount + " 元操作完成, 使用策略: " + strategy.getClass().getSimpleName() + "..");
    }

    @Override
    public void processPayment(double amount, PaymentStrategy strategy) {
        if (this.preAudit(amount, strategy)) {
            realPaymentService.processPayment(amount, strategy);
            this.postAudit(amount, strategy);
        }
        else {
            System.out.println("[安全审计代理] 支付请求被拒绝。");
        }
    }
}
  • 实现了 PaymentService 接口, 并持有一个 RealPaymentService 的引用。在 processPayment 方法中, 在调用 realPaymentService.processPayment 之前 执行 preAudit(前置审计), 在之后执行 postAudit
测试类
复制代码
public class ProxyStrategyTest {

    @Test
    public void test_proxyStrategy() {
        System.out.println("\n--- 用户1(正常用户)使用信用卡支付 ---");
        PaymentService user1PaymentService = new SecurityAuditPaymentProxy(new RealPaymentService(), "user_A");
        PaymentStrategy creditCardStrategy = new CreditCardPaymentStrategy("1234-5678-9012", "12/25");
        user1PaymentService.processPayment(100.00, creditCardStrategy);

        System.out.println("\n--- 用户2(正常用户)使用支付宝支付 ---");
        PaymentService user2PaymentService = new SecurityAuditPaymentProxy(new RealPaymentService(), "user_B");
        PaymentStrategy alipayStrategy = new AlipayPaymentStrategy("alipay_user_B");
        user2PaymentService.processPayment(50.50, alipayStrategy);

        System.out.println("\n--- 用户3(被阻止用户)尝试使用微信支付 ---");
        PaymentService user3PaymentService = new SecurityAuditPaymentProxy(new RealPaymentService(), "blockedUser");
        PaymentStrategy wechatStrategy = new WechatPaymentStrategy("wechat_open_id_C");
        user3PaymentService.processPayment(200.00, wechatStrategy);

        System.out.println("\n--- 用户4(正常用户)再次使用支付宝支付 ---");
        PaymentService user4PaymentService = new SecurityAuditPaymentProxy(new RealPaymentService(), "user_D");
        PaymentStrategy anotherAlipayStrategy = new AlipayPaymentStrategy("alipay_user_D");
        user4PaymentService.processPayment(75.00, anotherAlipayStrategy);
    }

}
执行结果
复制代码
--- 用户1(正常用户)使用信用卡支付 ---

[安全审计代理] 前置审计: 用户 user_A 尝试支付 100.0 元, 使用策略: CreditCardPaymentStrategy 在 Mon Sep 29 10:28:28 CST 2025
[安全审计代理] 前置审计通过。

[真实支付服务] 开始处理支付请求, 金额: 100.0 元。
[信用卡支付策略] 正在使用信用卡 1234-5678-9012 (有效期: 12/25) 支付100.0 元。
[信用卡支付策略] 支付成功。
[真实支付服务] 支付请求处理完毕。
[安全审计代理] 后置审计: 用户 user_A 支付 100.0 元操作完成, 使用策略: CreditCardPaymentStrategy..

--- 用户2(正常用户)使用支付宝支付 ---

[安全审计代理] 前置审计: 用户 user_B 尝试支付 50.5 元, 使用策略: AlipayPaymentStrategy 在 Mon Sep 29 10:28:28 CST 2025
[安全审计代理] 前置审计通过。

[真实支付服务] 开始处理支付请求, 金额: 50.5 元。
[支付宝支付策略] 正在使用支付宝账户 alipay_user_B 支付 50.5 元。
[支付宝支付策略] 支付成功。
[真实支付服务] 支付请求处理完毕。
[安全审计代理] 后置审计: 用户 user_B 支付 50.5 元操作完成, 使用策略: AlipayPaymentStrategy..

--- 用户3(被阻止用户)尝试使用微信支付 ---

[安全审计代理] 前置审计: 用户 blockedUser 尝试支付 200.0 元, 使用策略: WechatPaymentStrategy 在 Mon Sep 29 10:28:28 CST 2025
[安全审计代理] 审计失败: 用户 blockedUser被阻止支付。
[安全审计代理] 支付请求被拒绝。

--- 用户4(正常用户)再次使用支付宝支付 ---

[安全审计代理] 前置审计: 用户 user_D 尝试支付 75.0 元, 使用策略: AlipayPaymentStrategy 在 Mon Sep 29 10:28:28 CST 2025
[安全审计代理] 前置审计通过。

[真实支付服务] 开始处理支付请求, 金额: 75.0 元。
[支付宝支付策略] 正在使用支付宝账户 alipay_user_D 支付 75.0 元。
[支付宝支付策略] 支付成功。
[真实支付服务] 支付请求处理完毕。
[安全审计代理] 后置审计: 用户 user_D 支付 75.0 元操作完成, 使用策略: AlipayPaymentStrategy..

Process finished with exit code 0

组合优势

  1. 当需要增加新的支付方式时, 只需添加新的具体策略类; 当安全审计需求变化时, 只需修改代理类。

  2. 代理模式专注于安全审计和访问控制, 而策略模式专注于封装不同的支付算法, 两者职责明确, 互不干扰。

相关推荐
用户962377954484 小时前
DVWA 靶场实验报告 (High Level)
安全
数据智能老司机7 小时前
用于进攻性网络安全的智能体 AI——在 n8n 中构建你的第一个 AI 工作流
人工智能·安全·agent
数据智能老司机7 小时前
用于进攻性网络安全的智能体 AI——智能体 AI 入门
人工智能·安全·agent
用户962377954488 小时前
DVWA 靶场实验报告 (Medium Level)
安全
red1giant_star8 小时前
S2-067 漏洞复现:Struts2 S2-067 文件上传路径穿越漏洞
安全
用户9623779544812 小时前
DVWA Weak Session IDs High 的 Cookie dvwaSession 为什么刷新不出来?
安全
cipher2 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
一次旅行5 天前
网络安全总结
安全·web安全
red1giant_star5 天前
手把手教你用Vulhub复现ecshop collection_list-sqli漏洞(附完整POC)
安全
ZeroNews内网穿透5 天前
谷歌封杀OpenClaw背后:本地部署或是出路
运维·服务器·数据库·安全