设计模式笔记_结构型_代理模式

1. 代理模式介绍

代理模式是一种结构型设计模式,它允许你提供一个代理对象来控制对另一个对象的访问。代理对象通常在客户端和目标对象之间起到中介作用,能够在不改变目标对象的前提下增加额外的功能操作,比如延迟初始化、访问控制、日志记录等。

通俗来说,代理模式就像是你在现实生活中使用中介服务一样。比如租房时,你可能不会直接联系房东,而是通过房屋中介来完成租赁过程。中介(代理)会帮你处理一些细节问题,如看房、谈判价格等,最终让你顺利租到房子。

代理模式有四种常见实现方式:

实现方式 核心机制 依赖要求 限制条件 性能 灵活性 代码侵入性
基于接口的静态代理 手动创建代理类,实现相同接口 依赖接口 必须实现接口 ★★★★☆ (高) ★★☆☆☆ (低)
基于继承的静态代理 代理类继承目标类并重写方法 不需要接口 不能代理final类/方法 ★★★★☆ (高) ★★☆☆☆ (低)
基于JDK的动态代理 Proxy+InvocationHandler反射生成字节码 依赖接口 JDK原生支持,目标类要求有接口 ★★★☆☆ (中等) ★★★★★ (高)
基于CGLIB的动态代理 继承目标类+MethodInterceptor修改字节码 不需要接口 不能代理final类/方法;需第三方库 ★★★★☆ (较高) ★★★★★ (高)

2.代码演示

2.1 基于接口的静态代理

实现原理

  • 代理类与目标类实现相同接口
  • 代理类持有目标对象的引用
  • 手动在接口方法中添加增强逻辑

示例场景

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

//被代理的目标类
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

//"基于接口"的静态代理类
public class ProxySubject implements Subject {
    private RealSubject realSubject;

    @Override
    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        System.out.println("ProxySubject: Pre-processing before handling request.");
        realSubject.request();
        System.out.println("ProxySubject: Post-processing after handling request.");
    }
}

使用方式:

java 复制代码
public class Demo {
    public static void main(String[] args) {
        System.out.println("Using RealSubject directly:");
        Subject realSubject = new RealSubject();
        realSubject.request();

        System.out.println("\nUsing ProxySubject:");
        Subject proxySubject = new ProxySubject();
        proxySubject.request();
    }
}

2.2 基于继承的静态代理

实现原理

  • 代理类继承目标类
  • 重写方法并添加增强逻辑
  • 通过super调用父类方法

示例场景

java 复制代码
//被代理的目标类
public class RealSubject {
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

//"基于继承"的静态代理方式
public class ProxySubjectByInheritance extends RealSubject {
    @Override
    public void request() {
        System.out.println("ProxySubjectByInheritance: Pre-processing before handling request.");
        super.request();
        System.out.println("ProxySubjectByInheritance: Post-processing after handling request.");
    }
}

使用方式:

java 复制代码
public class Demo {
    public static void main(String[] args) {
        System.out.println("Using RealSubject directly:");
        Subject realSubject = new RealSubject();
        realSubject.request();

        System.out.println("\nUsing ProxySubjectByInheritance:");
        Subject proxySubjectByInheritance = new ProxySubjectByInheritance();
        proxySubjectByInheritance.request();
    }
}

2.3 基于JDK的动态代理

实现原理

  • 底层使用Java的反射机制
  • 运行时通过java.lang.reflect.Proxy.newProxyInstance()动态生成代理对象
  • 通过InvocationHandler统一处理所有方法调用

示例场景

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * "基于JDK"的动态代理(可以看作是"基于接口"的动态代理)
 * 要求: 代理的目标对象必须实现接口
 * 提供者: JDK,使用JDK官方的Proxy类创建代理对象
 * 代理类: 生成的代理类和被代理类基于相同的接口,只能使用用接口里的public方法
 *
 */
public class JdkProxySubject implements InvocationHandler {
    private Object target;

    /**
     * 创建代理对象的方法
     *
     * @param target 被代理的原始对象; 被代理对象的类必须实现接口
     * @return 代理对象; 与被代理对象实现相同接口
     */
    public Object getInstance(Object target) {
        this.target = target;
        // 创建对象的代理对象
        // 参数1:类加载器; 参数2:被代理对象的接口; 参数3:代理类
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /**
     * 拦截所有目标类的方法
     *
     * @param proxy  目标类对象
     * @param method 目标类的方法
     * @param args   目标类的方法参数
     * @return 目标类方法的返回值
     * @throws Throwable 目标类方法抛出的异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JdkProxySubject: Pre-processing before handling request.");
        Object result = method.invoke(target, args);
        System.out.println("JdkProxySubject: Post-processing after handling request.");
        return result;
    }
}

使用方式:

java 复制代码
public class Demo {
    public static void main(String[] args) {
        System.out.println("Using RealSubject directly:");
        Subject realSubject = new RealSubject();
        realSubject.request();

        System.out.println("\nUsing JdkProxySubject:");
        JdkProxySubject jdkProxySubject = new JdkProxySubject();
        Subject realSubjectProxy = (Subject) jdkProxySubject.getInstance(new RealSubject());
        realSubjectProxy.request();
    }
}

2.4 基于CGLIB的动态代理

实现原理

  • 使用CGLIB库来生成代理类,这个库允许我们在运行时动态地创建目标类的子类
  • 采用方法拦截(MethodInterceptor)机制
  • 通过FastClass机制避免反射调用

示例场景

java 复制代码
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * "基于CGLB"的动态代理(可以看作"基于继承"的动态代理)
 * 提供者: 第三方 CGLib,使用CGLib的Enhancer类创建代理对象
 * 代理类: 生成的代理类是被代理类的子类,可以使用被代理类中的 public 和 protected 方法
 */
public class CglibProxySubject implements MethodInterceptor {
    private Object target;

    /**
     * 使用CGLib创建动态代理对象
     *
     * @param target 被代理的原始对象; 被代理对象不能用final修饰(final修饰后无法继承了)
     * @return 代理对象; 是被代理对象的子类
     */
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    /**
     * 拦截所有目标类的方法
     *
     * @param obj    目标类的实例
     * @param method 目标类的方法
     * @param args   目标类方法的参数
     * @param proxy  目标类方法的代理
     * @return 目标类方法的返回值
     * @throws Throwable 目标类方法抛出的异常
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CglibProxySubject: Pre-processing before handling request.");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("CglibProxySubject: Post-processing after handling request.");
        return result;
    }
}

使用方式:

java 复制代码
public class Demo {
    public static void main(String[] args) {
        System.out.println("Using RealSubject directly:");
        Subject realSubject = new RealSubject();
        realSubject.request();

        System.out.println("\nUsing CglibProxySubject:");
        CglibProxySubject cglibProxySubject = new CglibProxySubject();
        RealSubject realSubjectCglibProxy = (RealSubject) cglibProxySubject.getInstance(new RealSubject());
        realSubjectCglibProxy.request();
    }
}

3.四种代理模式比较

以下是四种代理模式的对比分析及使用场景总结:

代理方式 优点 缺点 适用场景
基于接口的静态代理 1.结构清晰,易于理解和维护 2.性能最优(无反射开销),类型安全 1.需要为每个被代理类编写代理类,代码冗余 2.类方法变更需同步修改代理类 1.接口方法数量少且稳定 2.需要极致性能的场景(如高频调用)或者需要精确控制代理逻辑的场景 3.简单项目或教学演示
基于继承的静态代理 1.可以代理没有接口的类 1.同样需要编写大量代码; 2.不能代理final类/方法 3.类方法变更需修改代理类 1.遗留系统改造(无接口的类) 2.需要代理第三方类库(拿不到三方类库的接口) 3.不涉及final修饰的类/方法
JDK动态代理 1.无需编写代理类(动态生成),自动适配所有接口方法,减少代码量; 2. 支持统一拦截逻辑 1.只能代理实现了接口的类; 2.反射调用性能略低 1.Spring AOP默认实现 2.需要代理接口多个方法的场景 3.代理逻辑复杂且需复用的系统(如日志/事务统一管理)
CGLIB动态代理 1.可以代理没有接口的类; 2.性能通常优于JDK动态代理,接近静态代理(FastClass机制) 1.不能代理final类或final方法; 2.需引入第三方库(如Spring-core已内置) 3.生成字节码可能增加内存开销 1.代理没有接口的类(Spring对无接口Bean的代理) 2.需要高性能代理的场景(如Hibernate延迟加载) 3.代理非final的第三方类库
相关推荐
丝斯201111 小时前
AI学习笔记整理(42)——NLP之大规模预训练模型Transformer
人工智能·笔记·学习
凉、介13 小时前
深入 QEMU Guest Agent:虚拟机内外通信的隐形纽带
c语言·笔记·学习·嵌入式·虚拟化
GISer_Jing14 小时前
AI Agent 目标设定与异常处理
人工智能·设计模式·aigc
njsgcs14 小时前
SIMA2 论文阅读 Google 任务设定器、智能体、奖励模型
人工智能·笔记
蔺太微14 小时前
组合模式(Composite Pattern)
设计模式·组合模式
云半S一15 小时前
pytest的学习过程
经验分享·笔记·学习·pytest
AI视觉网奇15 小时前
ue5.7 配置 audio2face
笔记·ue5
鱼跃鹰飞16 小时前
DDD中的防腐层
java·设计模式·架构
会员果汁17 小时前
15.设计模式-组合模式
设计模式·组合模式
崎岖Qiu17 小时前
【OS笔记35】:文件系统的使用、实现与管理
笔记·操作系统·存储管理·文件系统·os