代理模式(Proxy)

目录

一、定义与原理

二、优点

三、缺点

四、实现方式

五、应用场景

六、举例

静态代理

JDK动态代理

CGLIB动态代理


一、定义与原理

  • 定义:代理模式为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户和目标对象之间起到中介的作用。
  • 原理:代理模式涉及三个角色------抽象角色(定义了代理和被代理对象的共同接口)、代理角色(代理对象持有被代理对象的引用,在代理对象上实现客户请求的方法,并通过调用被代理对象的方法来实现客户请求)、真实角色(被代理的对象,负责执行具体的业务逻辑)。

二、优点

  1. 可扩展目标对象的功能:通过代理对象可以在不修改目标对象的前提下,增加额外的功能。
  2. 降低系统耦合度:客户端通过代理对象与目标对象进行交互,降低了客户端与目标对象之间的直接依赖,从而提高了系统的扩展性和灵活性。
  3. 保护目标对象:代理对象可以对客户端的请求进行过滤、验证等处理,从而保护目标对象不受非法请求的影响。

三、缺点

  1. 类的数量增加:使用代理模式会增加系统中的类数量,从而增加了系统的复杂度。
  2. 请求处理速度变慢:由于客户端的请求需要经过代理对象处理后再转发给目标对象,因此可能会增加请求处理的时间。
  3. 增加了系统的复杂度:代理模式的实现需要额外的类和接口,增加了系统的理解和维护成本。

四、实现方式

代理模式可以分为静态代理和动态代理两种实现方式:

  • 静态代理:由程序员创建或工具生成代理类的源码,再编译代理类。静态代理在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
  • 动态代理:在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。动态代理的实现方式主要有JDK原生动态代理和CGLIB动态代理。JDK原生动态代理基于接口实现,而CGLIB动态代理基于继承实现。

五、应用场景

代理模式在软件开发中有着广泛的应用,包括但不限于以下几个方面:

  • 远程代理:为位于不同地址空间的对象提供局部代表,从而隐藏远程调用的细节。
  • 虚拟代理:根据需要创建开销很大的对象,通过它来存放实例化对象的引用。
  • 安全代理:控制对原始对象的访问权限,增加访问控制的安全性。
  • 智能指引:调用者通过代理引用间接调用目标对象的方法,代理引用可以在将调用请求提交给目标对象前后执行一些附加操作。

总之,代理模式是一种非常有用的设计模式,它可以在不修改目标对象的前提下,增加额外的功能、降低系统耦合度、保护目标对象等。在实际开发中,我们可以根据具体需求选择合适的代理模式实现方式。

六、举例

静态代理

静态代理通常是在编译时就已经确定代理类的具体实现。这种方式需要程序员手动编写代理类的代码。

假设我们有一个接口Subject和一个实现了该接口的类RealSubject,我们想要为RealSubject创建一个代理类StaticProxy

Subject.java

java 复制代码
public interface Subject {  
    void request();  
}

RealSubject.java

java 复制代码
public class RealSubject implements Subject {  
    @Override  
    public void request() {  
        System.out.println("Executing real request.");  
    }  
}

StaticProxy.java

java 复制代码
public class StaticProxy implements Subject {  
    private RealSubject realSubject;  
  
    public StaticProxy(RealSubject realSubject) {  
        this.realSubject = realSubject;  
    }  
  
    @Override  
    public void request() {  
        // 在调用真实对象的方法之前,可以添加一些自定义操作  
        beforeRequest();  
        realSubject.request();  
        // 在调用真实对象的方法之后,可以添加一些自定义操作  
        afterRequest();  
    }  
  
    private void beforeRequest() {  
        System.out.println("Before real request.");  
    }  
  
    private void afterRequest() {  
        System.out.println("After real request.");  
    }  
}

Client.java

java 复制代码
public class Client {  
    public static void main(String[] args) {  
        RealSubject realSubject = new RealSubject();  
        Subject proxy = new StaticProxy(realSubject);  
        proxy.request();  
    }  
}
JDK动态代理

动态代理是在运行时动态生成代理类的字节码,并加载到JVM中。Java提供了两种主要的动态代理机制:基于接口的JDK动态代理和基于类的CGLIB动态代理。这里以JDK动态代理为例。

Subject.java(与静态代理示例相同)

InvocationHandler.java (实现InvocationHandler接口)

java 复制代码
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
  
public class InvocationHandlerImpl implements InvocationHandler {  
    private Object target;  
  
    public InvocationHandlerImpl(Object target) {  
        this.target = target;  
    }  
  
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        // 在调用真实对象的方法之前,可以添加一些自定义操作  
        beforeRequest();  
        // 调用真实对象的方法  
        Object result = method.invoke(target, args);  
        // 在调用真实对象的方法之后,可以添加一些自定义操作  
        afterRequest();  
        return result;  
    }  
  
    private void beforeRequest() {  
        System.out.println("Before method invocation.");  
    }  
  
    private void afterRequest() {  
        System.out.println("After method invocation.");  
    }  
}

Client.java(使用JDK动态代理)

java 复制代码
import java.lang.reflect.Proxy;  
  
public class Client {  
    public static void main(String[] args) {  
        RealSubject realSubject = new RealSubject();  
        // 创建InvocationHandler实例  
        InvocationHandler handler = new InvocationHandlerImpl(realSubject);  
        // 获取与RealSubject同接口的代理实例  
        Subject proxy = (Subject) Proxy.newProxyInstance(  
                RealSubject.class.getClassLoader(),  
                new Class[]{Subject.class},  
                handler);  
        // 通过代理实例调用方法  
        proxy.request();  
    }  
}

在这个动态代理的例子中,我们并没有显式地编写代理类,而是通过Proxy.newProxyInstance()方法在运行时动态地创建了代理类的实例。这个代理实例实现了与RealSubject相同的接口,并在调用接口方法时,通过InvocationHandlerinvoke方法添加了额外的逻辑。

CGLIB动态代理

CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,用于在运行时扩展Java类和实现接口。它通常用于实现基于类的动态代理,而不需要目标类实现任何接口。以下是一个使用CGLIB动态代理的例子。

首先,确保你的项目中包含了CGLIB的依赖。如果你使用Maven,可以在pom.xml中添加如下依赖:

java 复制代码
<dependency>  
    <groupId>cglib</groupId>  
    <artifactId>cglib</artifactId>  
    <version>3.3.0</version>  
</dependency>

接下来,我们定义一个不需要实现任何接口的类RealSubject,然后创建一个实现了MethodInterceptor接口的CGLIB代理拦截器,最后通过CGLIB的Enhancer类来创建代理对象。

RealSubject.java

java 复制代码
public class RealSubject {  
    public void someMethod() {  
        System.out.println("Executing real method.");  
    }  
}

MethodInterceptorImpl.java

java 复制代码
import net.sf.cglib.proxy.MethodInterceptor;  
import net.sf.cglib.proxy.MethodProxy;  
  
import java.lang.reflect.Method;  
  
public class MethodInterceptorImpl implements MethodInterceptor {  
    @Override  
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {  
        // 在调用真实对象的方法之前,可以添加一些自定义操作  
        beforeMethod();  
        // 调用真实对象的方法  
        Object result = proxy.invokeSuper(obj, args);  
        // 在调用真实对象的方法之后,可以添加一些自定义操作  
        afterMethod();  
        return result;  
    }  
  
    private void beforeMethod() {  
        System.out.println("Before method execution.");  
    }  
  
    private void afterMethod() {  
        System.out.println("After method execution.");  
    }  
}

Client.java

java 复制代码
import net.sf.cglib.proxy.Enhancer;  
  
public class Client {  
    public static void main(String[] args) {  
        // 创建目标对象  
        RealSubject realSubject = new RealSubject();  
          
        // 创建Enhancer对象,并设置其父类(即目标类)  
        Enhancer enhancer = new Enhancer();  
        enhancer.setSuperclass(RealSubject.class);  
          
        // 设置回调(即拦截器)  
        enhancer.setCallback(new MethodInterceptorImpl());  
          
        // 创建代理对象  
        RealSubject proxy = (RealSubject) enhancer.create();  
          
        // 通过代理对象调用方法  
        proxy.someMethod();  
    }  
}

在这个例子中,RealSubject类没有实现任何接口,但我们仍然能够通过CGLIB为它创建一个代理对象。我们通过实现MethodInterceptor接口并定义intercept方法来定义在调用目标方法前后要执行的操作。然后,我们使用Enhancer类来生成代理对象,并指定目标类和拦截器。最后,我们通过代理对象调用方法,intercept方法中的逻辑就会在调用真实方法之前和之后被执行。

相关推荐
无限大.2 天前
动态规划与贪心算法:核心区别与实例分析
贪心算法·动态规划·代理模式
丶Darling.4 天前
Day42 | 动态规划 :选或不选 打家劫舍&&打家劫舍II
算法·动态规划·代理模式
OkeyProxy4 天前
伺服器代理查找失敗的原因和解決方案
代理模式·ip·代理服务器·海外ip代理·proxypattern
无敌岩雀5 天前
C++设计模式结构型模式———代理模式
c++·设计模式·代理模式
南城花随雪。6 天前
Spring框架之代理模式 (Proxy Pattern)
java·spring·代理模式
肘击鸣的百k路8 天前
Java 代理模式详解
java·开发语言·代理模式
OkeyProxy8 天前
怎麼解決IP地址衝突的問題?
代理模式·proxy模式·ip地址·代理服务器·海外ip代理
G皮T11 天前
【设计模式】结构型模式(二):代理模式
java·设计模式·编程·代理模式·proxy pattern·结构型模式
zzzhpzhpzzz12 天前
设计模式——代理模式
设计模式·系统安全·代理模式
羽愿12 天前
技术总结(十九)
代理模式