Java代理模式(Proxy)实现方法详解

 更新时间:2025年04月21日 09:30:46   作者:冰糖心书房  
这篇文章主要介绍了Java代理模式(Proxy)实现的相关资料,代理模式是一种结构型设计模式,通过引入代理对象来控制对目标对象的访问,代理模式的优点包括职责清晰、扩展性好、保护目标对象和增强功能,文中通过代码介绍的非常详细,需要的朋友可以参考下

一、什么是代理模式?

  • 定义: 代理模式是一种结构型设计模式。 它为另一个对象(目标对象/被代理对象)提供一个代理(或占位符),以控制对这个对象的访问。
  • 核心思想: 通过引入一个代理对象,客户端不直接访问目标对象,而是通过代理对象来间接访问目标对象。 代理对象可以控制对目标对象的访问,并可以在访问前后添加额外的操作。
  • 意图: 控制对一个对象的访问,可以延迟加载、访问控制、增强功能等。

二、代理模式的结构

代理模式通常包含以下几个角色:

  • Subject (抽象主题):

    • 定义了 RealSubject 和 Proxy 的共同接口。 客户端通过 Subject 接口访问目标对象。
    • 通常是一个接口或抽象类。
  • RealSubject (真实主题/目标对象/被代理对象):

    • 定义了真正的业务逻辑。
    • 实现了 Subject 接口。
  • Proxy (代理):

    • 持有 RealSubject 对象的引用。
    • 实现了 Subject 接口,与 RealSubject 具有相同的方法。
    • 控制对 RealSubject 对象的访问,并可以在访问前后添加额外的操作。
    • 客户端通过 Proxy 对象间接访问 RealSubject 对象。

UML 类图:

+----------------+       +----------------+       +----------------+
|   <<Subject>>   |       |     Proxy      |       |  RealSubject   |
+----------------+       +----------------+       +----------------+
| +request()     |------>| -realSubject   |------>| +request()     |
+----------------+       | +request()     |       +----------------+
                             +preRequest()
                             +postRequest()

三、代理模式的类型

根据代理的创建时间和功能,可以将代理模式分为以下几种类型:

  • 静态代理 (Static Proxy):

    • 特点: 在编译时就已经确定了代理类和被代理类之间的关系。 代理类和被代理类都需要实现相同的接口。
    • 优点: 实现简单,易于理解。
    • 缺点:
      • 代码冗余: 如果需要代理多个类,就需要创建多个代理类,导致代码冗余。
      • 可维护性差: 如果接口发生变化,代理类和被代理类都需要进行修改。
  • 动态代理 (Dynamic Proxy):

    • 特点: 在运行时动态地生成代理类,无需手动创建代理类。
    • 优点:
      • 灵活性高: 可以代理任何实现了接口的类,无需修改原始代码。
      • 代码复用: 可以使用同一个代理类来代理多个不同的类。
      • 可维护性好: 如果接口发生变化,只需要修改代理逻辑,无需修改被代理类。
    • 缺点:
      • 实现复杂: 动态代理的实现比静态代理复杂。
      • 性能开销: 动态代理需要使用反射机制,性能比静态代理略低。
    • Java 中的动态代理:
      • JDK 动态代理: Java 提供的内置动态代理机制,只能代理实现了接口的类。
      • CGLIB 动态代理: 第三方库提供的动态代理机制,可以代理没有实现接口的类。
  • 其他代理

    • 保护代理 用于控制对敏感对象的访问。
    • 远程代理 用于访问远程对象。
    • 虚拟代理 通过代理延迟创建开销大的对象

四、代理模式的实现方式 (Java)

  • 静态代理:

    // 抽象主题
    interface Image {
        void display();
    }
    
    // 真实主题
    class RealImage implements Image {
        private String filename;
    
        public RealImage(String filename) {
            this.filename = filename;
            loadImageFromDisk();
        }
    
        private void loadImageFromDisk() {
            System.out.println("Loading image: " + filename);
        }
    
        @Override
        public void display() {
            System.out.println("Displaying image: " + filename);
        }
    }
    
    // 代理
    class ImageProxy implements Image {
        private RealImage realImage;
        private String filename;
    
        public ImageProxy(String filename) {
            this.filename = filename;
        }
    
        @Override
        public void display() {
            if (realImage == null) {
                realImage = new RealImage(filename);
            }
            realImage.display();
        }
    }
    
    // 客户端代码
    public class StaticProxyExample {
        public static void main(String[] args) {
            Image image = new ImageProxy("test_image.jpg");
    
            // 第一次调用 display() 方法,会加载图片
            image.display();
    
            // 第二次调用 display() 方法,不会再次加载图片
            image.display();
        }
    }
    
  • JDK 动态代理:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    // 抽象主题
    interface Image {
        void display();
    }
    
    // 真实主题
    class RealImage implements Image {
        private String filename;
    
        public RealImage(String filename) {
            this.filename = filename;
            loadImageFromDisk();
        }
    
        private void loadImageFromDisk() {
            System.out.println("Loading image: " + filename);
        }
    
        @Override
        public void display() {
            System.out.println("Displaying image: " + filename);
        }
    }
    
    // 调用处理器
    class ImageInvocationHandler implements InvocationHandler {
        private Object target; // 被代理的对象
    
        public ImageInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 在调用目标方法之前执行的操作
            System.out.println("Before invoking method: " + method.getName());
    
            // 调用目标方法
            Object result = method.invoke(target, args);
    
            // 在调用目标方法之后执行的操作
            System.out.println("After invoking method: " + method.getName());
    
            return result;
        }
    }
    
    // 客户端代码
    public class JDKDynamicProxyExample {
        public static void main(String[] args) {
            // 创建被代理对象
            Image realImage = new RealImage("test_image.jpg");
    
            // 创建调用处理器
            ImageInvocationHandler handler = new ImageInvocationHandler(realImage);
    
            // 创建代理对象
            Image proxy = (Image) Proxy.newProxyInstance(
                    Image.class.getClassLoader(), // 类加载器
                    new Class[] {Image.class}, // 代理类实现的接口列表
                    handler // 调用处理器
            );
    
            // 通过代理对象调用方法
            proxy.display();
        }
    }
    
  • CGLIB 动态代理:

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    // 真实主题 (不需要实现接口)
    class RealImage {
        private String filename;
    
        public RealImage(String filename) {
            this.filename = filename;
            loadImageFromDisk();
        }
        public RealImage(){} //需要一个无参构造函数
    
        private void loadImageFromDisk() {
            System.out.println("Loading image: " + filename);
        }
    
        public void display() {
            System.out.println("Displaying image: " + filename);
        }
    }
    
    // 方法拦截器
    class ImageMethodInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            // 在调用目标方法之前执行的操作
            System.out.println("Before invoking method: " + method.getName());
    
            // 调用目标方法
            Object result = proxy.invokeSuper(obj, args);
    
            // 在调用目标方法之后执行的操作
            System.out.println("After invoking method: " + method.getName());
    
            return result;
        }
    }
    
    // 客户端代码
    public class CGLIBDynamicProxyExample {
        public static void main(String[] args) {
            // 创建 Enhancer 对象
            Enhancer enhancer = new Enhancer();
    
            // 设置超类
            enhancer.setSuperclass(RealImage.class);
    
            // 设置回调
            enhancer.setCallback(new ImageMethodInterceptor());
    
            // 创建代理对象
            RealImage proxy = (RealImage) enhancer.create();
    
            // 通过代理对象调用方法
            proxy.display();
        }
    }
    

五、代理模式的优缺点

优点:

  • 职责清晰: 将客户端与目标对象分离,降低了耦合度。
  • 扩展性好: 可以通过添加新的代理类来扩展系统功能,而无需修改原始代码。
  • 保护目标对象: 代理对象可以控制对目标对象的访问,保护目标对象免受恶意访问。
  • 增强目标对象的功能: 代理对象可以在访问目标对象前后添加额外的操作,例如日志记录、安全检查、延迟加载等。

缺点:

  • 增加系统复杂性: 引入代理对象会增加系统的复杂性。
  • 可能降低性能: 代理对象需要进行额外的处理,可能会降低程序的性能。

六、代理模式的应用场景

  • 远程代理 (Remote Proxy): 为远程对象提供一个本地代理,隐藏远程对象的具体实现细节。
  • 虚拟代理 (Virtual Proxy): 为创建开销大的对象提供一个代理,延迟对象的创建,直到真正需要使用时才创建。
  • 保护代理 (Protection Proxy): 控制对敏感对象的访问,只允许具有特定权限的客户端访问。
  • 缓存代理 (Cache Proxy): 为访问开销大的对象提供一个缓存,提高访问效率。
  • 智能引用代理 (Smart Reference Proxy): 在访问对象时执行额外的操作,例如引用计数、对象锁定等。
  • AOP (面向切面编程): 使用动态代理来实现 AOP,例如 Spring AOP。
  • 延迟加载 (Lazy Loading): 例如,Hibernate 中的延迟加载机制。
  • 防火墙代理: 控制网络访问,保护内部网络。

七、总结

代理模式是一种非常有用的设计模式,它可以控制对对象的访问,并可以在访问前后添加额外的操作。 在 Java 中,可以使用静态代理、JDK 动态代理和 CGLIB 动态代理来实现代理模式。 选择哪种实现方式取决于具体的应用场景和需求。

到此这篇关于Java代理模式(Proxy)实现方法的文章就介绍到这了,更多相关Java代理模式Proxy内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringCloud feign服务熔断下的异常处理操作

    SpringCloud feign服务熔断下的异常处理操作

    这篇文章主要介绍了SpringCloud feign服务熔断下的异常处理操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • SpringAOP 如何通过JoinPoint获取参数名和值

    SpringAOP 如何通过JoinPoint获取参数名和值

    这篇文章主要介绍了SpringAOP 通过JoinPoint获取参数名和值的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java中内核线程理论及实例详解

    Java中内核线程理论及实例详解

    在本篇文章里小编给大家整理了一篇关于Java中内核线程理论及实例详解内容,有兴趣的朋友们可以学习下。
    2021-03-03
  • 解析Java的Spring框架的BeanPostProcessor发布处理器

    解析Java的Spring框架的BeanPostProcessor发布处理器

    这篇文章主要介绍了Java的Spring框架的BeanPostProcessor发布处理器,Spring是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2015-12-12
  • Java调用用户芝麻信用分

    Java调用用户芝麻信用分

    这篇文章主要为大家详细介绍了Java调用用户芝麻信用分,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-11-11
  • Java Servlet请求重定向的方法

    Java Servlet请求重定向的方法

    这篇文章主要介绍了使用Java Servlet请求重定向的方法,帮助大家更好的理解和学习Java Servlet的相关知识,感兴趣的朋友可以了解下
    2020-11-11
  • Java实现Word/Excel/TXT转PDF的方法

    Java实现Word/Excel/TXT转PDF的方法

    这篇文章主要介绍了Java实现Word/Excel/TXT转PDF的方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01
  • java多线程编程之java线程简介

    java多线程编程之java线程简介

    线程是程序运行的基本执行单元,线程不仅可以共享进程的内存,而且还拥有一个属于自己的内存空间,这段内存空间也叫做线程栈
    2014-01-01
  • 解决Maven静态资源过滤问题

    解决Maven静态资源过滤问题

    在我们使用Maven构建项目的时候,会默认过滤掉静态资源,所以,需要手动来配置,本文就介绍一下Maven静态资源过滤的问题解决,感兴趣的可以了解一下
    2021-06-06
  • Spring Security OAuth2 token权限隔离实例解析

    Spring Security OAuth2 token权限隔离实例解析

    这篇文章主要介绍了Spring Security OAuth2 token权限隔离实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11

最新评论