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

发布于:2025-07-13 ⋅ 阅读:(15) ⋅ 点赞:(0)

1. 代理模式介绍

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

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

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

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

2.代码演示

2.1 基于接口的静态代理

实现原理

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

示例场景

//接口
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.");
    }
}

使用方式:

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调用父类方法

示例场景

//被代理的目标类
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.");
    }
}

使用方式:

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统一处理所有方法调用

示例场景

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;
    }
}

使用方式:

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机制避免反射调用

示例场景

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;
    }
}

使用方式:

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的第三方类库


网站公告

今日签到

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