前言

在软件开发中,代理模式是一种常用的结构型设计模式,其核心思想是通过代理对象控制对目标对象的访问,并可以在目标方法执行前后添加额外的增强逻辑。本文将详细讲解代理模式的三种实现方式:静态代理、JDK 动态代理和 CGLIB 动态代理,并结合「周杰伦演出」的场景进行代码演示,帮助大家理解不同代理方式的特点和适用场景。

一、代理模式核心概念

1. 作用

通过代理对象包裹目标对象,实现对目标方法的前置处理(如参数校验、权限控制)、后置处理(如日志记录、数据统计),核心是控制访问 + 增强功能。

2. 核心角色

角色说明
抽象角色通常是接口,定义目标对象和代理对象的公共方法,规范核心业务逻辑
真实角色实现抽象角色的具体类,包含真正的业务逻辑(如周杰伦本人唱歌)
代理角色实现 / 继承抽象角色,持有真实角色的引用,调用真实角色方法并附加增强逻辑(如经纪人)

二、静态代理

静态代理是最基础的代理方式,代理类需要手动编写,且与目标类实现相同的接口。

2.1 代码实现

步骤 1:定义抽象角色(Star 接口)
package com.hg.proxy.StaticProxy;

public interface Star {
    /** 面谈 */
    void confer();
    /** 签合同 */
    void signContract();
    /** 订票 */
    void bookTicket();
    /** 唱歌 */
    void sing();
    /** 收钱 */
    void collectMoney();
}
步骤 2:定义真实角色(RealStar - 周杰伦)
package com.hg.proxy.StaticProxy;

// 真实角色:周杰伦(只负责核心业务——唱歌)
public class RealStar implements Star {
    @Override
    public void confer() {}

    @Override
    public void signContract() {}

    @Override
    public void bookTicket() {}

    @Override
    public void sing() {
        System.out.println("RealStar(周杰伦本人).sing() —— 七里香~");
    }

    @Override
    public void collectMoney() {}
}
步骤 3:定义代理角色(ProxyStar - 经纪人)
package com.hg.proxy.StaticProxy;

// 代理角色:经纪人(处理非核心业务,调用周杰伦的核心方法)
public class ProxyStar implements Star {
    
    // 持有真实角色的引用
    private Star star;

    public ProxyStar(Star star) {
        this.star = star;
    }

    @Override
    public void confer() {
        System.out.println("ProxyStar.confer() —— 经纪人面谈");
    }

    @Override
    public void signContract() {
        System.out.println("ProxyStar.signContract() —— 经纪人签合同");
    }

    @Override
    public void bookTicket() {
        System.out.println("ProxyStar.bookTicket() —— 经纪人订机票");
    }

    @Override
    public void sing() {
        // 调用真实角色的核心方法
        star.sing();
    }

    @Override
    public void collectMoney() {
        System.out.println("ProxyStar.collectMoney() —— 经纪人收钱");
    }
}
步骤 4:测试静态代理
package com.hg.proxy.StaticProxy;

public class Client {
    public static void main(String[] args) {
        // 创建真实角色
        Star realStar = new RealStar();
        // 创建代理角色,传入真实角色
        Star proxy = new ProxyStar(realStar);
        
        // 调用代理方法(经纪人处理前置/后置,周杰伦只负责唱歌)
        proxy.confer();
        proxy.signContract();
        proxy.bookTicket();
        proxy.sing();
        proxy.collectMoney();
    }
}
输出结果
ProxyStar.confer() —— 经纪人面谈
ProxyStar.signContract() —— 经纪人签合同
ProxyStar.bookTicket() —— 经纪人订机票
RealStar(周杰伦本人).sing() —— 七里香~
ProxyStar.collectMoney() —— 经纪人收钱

2.2 静态代理缺点

  1. 代码冗余:代理类和目标类实现相同接口,接口方法越多,重复代码越多;
  2. 扩展性差:代理类仅服务于一种类型的目标对象,若需代理其他类(如 DeptService、UserService),需重新编写代理类;
  3. 维护成本高:接口新增方法时,所有实现类(目标类 + 代理类)都需修改。

三、JDK 动态代理

JDK 动态代理是基于反射机制实现的动态代理,无需手动编写代理类,核心是 java.lang.reflect.Proxy 和 InvocationHandler 接口。

核心特点

  • 目标类必须实现至少一个接口;
  • 代理类由 JVM 动态生成(类名格式:com.sun.proxy.$Proxy0);
  • 可代理任意实现了接口的目标类,扩展性强。

3.1 代码实现

步骤 1:定义抽象角色(Star 接口)
package com.hg.JdkProxy;

public interface Star {
    /** 唱歌(核心业务) */
    void sing();
}
步骤 2:定义真实角色(RealStar)
package com.hg.JdkProxy;

// 真实角色:周杰伦(仅实现核心业务)
public class RealStar implements Star {
    @Override
    public void sing() {
        System.out.println("周杰伦:快使用双截棍,哼哼哈嘿....");
    }
}
步骤 3:定义动态代理工厂
package com.hg.JdkProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 代理工厂:动态生成代理对象
public class ProxyFactory {

    // 目标对象(可接收任意类型的实现类)
    private Object realObj;

    public ProxyFactory(Object realObj) {
        this.realObj = realObj;
    }

    // 获取动态代理对象
    public Object getProxyObject() {
        return Proxy.newProxyInstance(
                // 1. 目标类的类加载器
                realObj.getClass().getClassLoader(),
                // 2. 目标类实现的所有接口(代理类会实现这些接口)
                realObj.getClass().getInterfaces(),
                // 3. 调用处理器:定义增强逻辑
                new InvocationHandler() {
                    /**
                     * 代理方法的核心逻辑
                     * @param proxy  代理对象(一般不用)
                     * @param method 目标方法(如sing())
                     * @param args   目标方法的参数
                     * @return       目标方法的返回值
                     * @throws Throwable 异常
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 前置增强:经纪人处理非核心业务
                        System.out.println("真正的方法执行前!");
                        System.out.println("面谈,签合同,预付款,订机票");

                        // 调用目标对象的核心方法
                        Object result = method.invoke(realObj, args);

                        // 后置增强:经纪人收尾
                        System.out.println("真正的方法执行后!");
                        System.out.println("收尾款");

                        return result;
                    }
                }
        );
    }
}
步骤 4:测试 JDK 动态代理
package com.hg.JdkProxy;

public class Client {
    public static void main(String[] args) {
        // 1. 创建目标对象
        RealStar realStar = new RealStar();
        // 2. 创建代理工厂,传入目标对象
        ProxyFactory proxyFactory = new ProxyFactory(realStar);
        // 3. 获取动态代理对象
        Star proxyObject = (Star) proxyFactory.getProxyObject();
        
        // 打印代理类类型(com.sun.proxy.$Proxy0)
        System.out.println(proxyObject.getClass());
        // 4. 调用代理方法
        proxyObject.sing();
    }
}
输出结果
class com.sun.proxy.$Proxy0
真正的方法执行前!
面谈,签合同,预付款,订机票
周杰伦:快使用双截棍,哼哼哈嘿....
真正的方法执行后!
收尾款

四、CGLIB 动态代理

CGLIB(Code Generation Library)是基于字节码生成的动态代理,无需目标类实现接口,通过继承目标类生成子类作为代理类。

核心特点

  • 目标类无需实现接口;
  • 基于 ASM 框架生成目标类的子类,重写目标方法;
  • Spring 核心包已集成 CGLIB,无需额外引入(若单独使用需引入依赖)。

4.1 依赖引入(可选)

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

4.2 代码实现

步骤 1:定义真实角色(RealStar - 无需实现接口)
package com.hg.proxy.CglibProxy;

// 真实角色:周杰伦(无接口)
public class RealStar {
    public void sing() {
        System.out.println("RealStar(周杰伦本人).sing() —— 青花瓷~");
    }
}
步骤 2:定义 CGLIB 代理工厂
package com.hg.proxy.CglibProxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// CGLIB代理工厂:生成目标类的子类作为代理类
public class ProxyFactory implements MethodInterceptor {

    // 目标对象
    private Object realObj;

    public ProxyFactory(Object realObj) {
        this.realObj = realObj;
    }

    // 获取代理对象(子类)
    public Object getProxyObject() {
        // 1. 创建CGLIB增强器
        Enhancer en = new Enhancer();
        // 2. 设置父类(目标类)
        en.setSuperclass(realObj.getClass());
        // 3. 设置回调函数(当前类实现MethodInterceptor)
        en.setCallback(this);
        // 4. 生成子类(代理类)并返回
        return en.create();
    }

    /**
     * 增强逻辑入口
     * @param obj          代理对象(子类)
     * @param method       目标方法(父类)
     * @param args         方法参数
     * @param methodProxy  代理方法(子类)
     * @return             目标方法返回值
     * @throws Throwable   异常
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 前置增强
        System.out.println("真正的方法执行前!");
        System.out.println("面谈,签合同,预付款,订机票");

        // 调用目标对象的方法
        Object result = method.invoke(realObj, args);

        // 后置增强
        System.out.println("真正的方法执行后!");
        System.out.println("收尾款");

        return result;
    }
}
步骤 3:测试 CGLIB 动态代理
package com.hg.proxy.CglibProxy;

public class Client {
    public static void main(String[] args) {
        // 1. 创建目标对象
        RealStar realStar = new RealStar();
        // 2. 创建代理工厂
        ProxyFactory proxyFactory = new ProxyFactory(realStar);
        // 3. 获取代理对象(子类)
        RealStar proxyObject = (RealStar) proxyFactory.getProxyObject();
        // 4. 调用代理方法
        proxyObject.sing();
    }
}
输出结果
真正的方法执行前!
面谈,签合同,预付款,订机票
RealStar(周杰伦本人).sing() —— 青花瓷~
真正的方法执行后!
收尾款

五、三种代理方式对比

特性静态代理JDK 动态代理CGLIB 动态代理
目标类要求实现接口必须实现接口无需实现接口(继承)
实现方式手动编写代理类反射机制字节码生成(ASM)
代理类生成编译期固定运行期动态生成运行期动态生成子类
性能无额外开销反射有轻微开销字节码生成开销略低
扩展性差(仅支持单一目标类)好(支持任意接口实现类)好(支持任意类)
适用场景简单场景、固定目标类目标类实现接口的场景目标类无接口的场景

六、总结

  1. 静态代理:适合简单场景,代码直观但扩展性差,适用于目标类和方法固定的场景;
  2. JDK 动态代理:Spring AOP 默认实现方式,依赖接口,通过反射动态生成代理类,适合大多数接口化的业务场景;
  3. CGLIB 动态代理:弥补 JDK 动态代理的接口限制,通过继承生成子类,适合无接口的目标类(如 Spring 中的@Configuration类代理)。

实际开发中,Spring 框架已封装了两种动态代理的实现(自动选择:有接口用 JDK,无接口用 CGLIB),掌握代理模式的核心思想,能帮助我们更好地理解 Spring AOP、事务管理等核心功能的底层原理。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐