代理模式-spring关键设计模式,bean的增强,AOP的实现

发布于:2025-04-03 ⋅ 阅读:(14) ⋅ 点赞:(0)

以下是一个结合代理模式解决实际问题的Java实现案例,涵盖远程调用、缓存优化、访问控制等场景,包含逐行中文注释:

场景描述

开发一个跨网络的文件查看器,需实现:
远程文件访问:通过代理访问网络文件
缓存机制:减少重复下载开销
访问控制:限制敏感文件访问
大文件延迟加载:不立即加载大文件
访问日志:记录文件访问记录

完整代码实现

import java.lang.reflect.*;
import java.util.*;

/**
 * 文件服务接口(Subject)
 */
interface FileService {
    byte[] getFile(String filename) throws Exception;
    List<String> listFiles() throws Exception;
}

/**
 * 远程文件服务(RealSubject)
 */
class RemoteFileService implements FileService {
    // 模拟网络延迟
    private void simulateNetworkDelay() throws InterruptedException {
        Thread.sleep(1000);
    }

    @Override
    public byte[] getFile(String filename) throws Exception {
        simulateNetworkDelay();
        System.out.println("下载远程文件:" + filename);
        return ("内容:" + filename).getBytes(); // 模拟文件内容
    }

    @Override
    public List<String> listFiles() throws Exception {
        simulateNetworkDelay();
        return Arrays.asList("file1.txt", "file2.jpg", "secret.doc");
    }
}

/**
 * 智能代理(包含缓存、权限控制、日志)
 */
class FileServiceProxy implements InvocationHandler {
    private final FileService realService;
    private final Map<String, byte[]> cache = new HashMap<>();
    private final Set<String> blockedFiles = Set.of("secret.doc");
    private final User user;

    // 动态代理构造器
    public static FileService createProxy(User user) {
        return (FileService) Proxy.newProxyInstance(
                FileService.class.getClassLoader(),
                new Class[]{FileService.class},
                new FileServiceProxy(new RemoteFileService(), user)
        );
    }

    private FileServiceProxy(FileService realService, User user) {
        this.realService = realService;
        this.user = user;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 访问控制
        if (method.getName().equals("getFile")) {
            String filename = (String) args[0];
            checkAccessPermission(filename);
        }

        // 缓存逻辑
        if (method.getName().equals("getFile")) {
            String filename = (String) args[0];
            if (cache.containsKey(filename)) {
                System.out.println("从缓存获取文件:" + filename);
                return cache.get(filename);
            }
        }

        // 记录日志
        long start = System.currentTimeMillis();
        Object result = method.invoke(realService, args);
        long duration = System.currentTimeMillis() - start;

        // 后处理
        if (method.getName().equals("getFile")) {
            String filename = (String) args[0];
            cache.put(filename, (byte[]) result);
            System.out.printf("文件%s获取耗时:%dms\n", filename, duration);
        }

        return result;
    }

    private void checkAccessPermission(String filename) throws SecurityException {
        if (blockedFiles.contains(filename) && !user.isAdmin()) {
            throw new SecurityException("无权访问敏感文件:" + filename);
        }
    }
}

/**
 * 用户权限类
 */
class User {
    private final String username;
    private final boolean admin;

    public User(String username, boolean admin) {
        this.username = username;
        this.admin = admin;
    }

    public boolean isAdmin() {
        return admin;
    }
}

// ================== 客户端代码 ==================
public class ProxyPatternDemo {
    public static void main(String[] args) {
        // 创建普通用户代理
        User normalUser = new User("Alice", false);
        FileService proxy = FileServiceProxy.createProxy(normalUser);

        try {
            // 第一次获取文件(触发真实下载)
            proxy.getFile("file1.txt");
            
            // 再次获取相同文件(命中缓存)
            proxy.getFile("file1.txt");
            
            // 尝试访问敏感文件
            proxy.getFile("secret.doc");
        } catch (Exception e) {
            System.out.println("访问异常:" + e.getMessage());
        }

        // 管理员访问测试
        User admin = new User("Admin", true);
        FileService adminProxy = FileServiceProxy.createProxy(admin);
        try {
            adminProxy.getFile("secret.doc");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

最佳实践建议

一般的代理有注解式的@Aspect,或者写拦截器进行代理增强,如果系统基于spring,推荐使用spring框架的@Aspect。
如果不关注spring的相关生命周期等等,可以自己使用拦截器增强业务方法进行代理。最好不要自己实现反射去代理,实现复杂,并且不能搭配spring的依赖注入获取代理对象,只能自己管控代理对象生命周期。

一句话总结

代理模式的目的就是为了在不改变原有对象的基础上,对原有对象进行增强。


网站公告

今日签到

点亮在社区的每一天
去签到