在 Java 开发中,代理模式是一种常用的设计模式,核心思想是在不修改目标类代码的前提下,对目标方法进行增强(如日志记录、权限校验、事务管理等)。根据代理类的生成时机和实现方式,Java 中的代理技术主要分为三类:静态代理(编译期生成)、JDK 动态代理(运行时反射生成)、CGLIB 代理(运行时字节码生成)。

       本文将从原理、实战、对比三个维度,详细拆解这三种代理技术,帮你理清适用场景和核心差异,轻松应对实际开发中的代理需求(如 Spring AOP、框架插件开发等)。

一、代理模式核心概念

在正式讲解三种代理之前,先明确代理模式的核心角色:

  • 目标类(Target):被代理的原始类,包含核心业务逻辑;
  • 代理类(Proxy):包装目标类的类,负责在目标方法执行前后插入增强逻辑;
  • 增强逻辑(Advice):代理类的核心功能,如日志、事务、权限校验等;
  • 客户端(Client):调用方,通过代理类间接调用目标类的方法。

       代理模式的核心价值:解耦核心业务与增强逻辑,让目标类专注于业务本身,增强逻辑可复用、可切换。

二、静态代理:编译期的 “手动代理”

1. 原理剖析

       静态代理是最基础的代理实现,代理类由开发者手动编写,在编译期就生成.class文件。其核心逻辑是:

  1. 代理类与目标类实现相同的接口(或继承同一个父类),保证方法签名一致;
  2. 代理类内部持有目标类的实例(通过构造器注入);
  3. 代理类重写接口方法,在方法中先执行增强逻辑,再调用目标类的原始方法。

特点:无任何框架依赖,纯 Java 基础语法实现;但代码冗余度高,维护成本高。

2. 实战案例:静态代理实现日志增强

       需求:对用户服务(UserService)的添加、删除用户方法,添加 “执行前后日志” 的增强逻辑。

步骤 1:定义公共接口(规范目标类与代理类的方法)
/**
 * 用户服务接口:静态代理需基于接口(或父类)实现
 */
public interface IUserService {
    void addUser(String username); // 添加用户
    void deleteUser(String username); // 删除用户
    String queryUser(String username); // 查询用户
}
步骤 2:实现目标类(核心业务逻辑)
/**
 * 目标类:用户服务核心实现
 */
public class UserServiceImpl implements IUserService {
    @Override
    public void addUser(String username) {
        // 核心业务:模拟添加用户
        System.out.println("【核心业务】添加用户成功:" + username);
    }

    @Override
    public void deleteUser(String username) {
        // 核心业务:模拟删除用户
        System.out.println("【核心业务】删除用户成功:" + username);
    }

    @Override
    public String queryUser(String username) {
        // 核心业务:模拟查询用户
        System.out.println("【核心业务】查询用户:" + username);
        return "用户信息:" + username + "(VIP)";
    }
}
步骤 3:手动编写代理类(注入目标类 + 增强逻辑)
/**
 * 静态代理类:手动编写,与目标类实现同一接口
 */
public class UserServiceStaticProxy implements IUserService {
    // 持有目标类实例(通过构造器注入,解耦)
    private final IUserService target;

    // 构造器:传入目标类对象
    public UserServiceStaticProxy(IUserService target) {
        this.target = target;
    }

    @Override
    public void addUser(String username) {
        // 增强逻辑:方法执行前
        System.out.println("【静态代理-日志】addUser 方法开始执行,参数:" + username);
        try {
            // 调用目标类的核心方法
            target.addUser(username);
            // 增强逻辑:方法执行成功后
            System.out.println("【静态代理-日志】addUser 方法执行成功");
        } catch (Exception e) {
            // 增强逻辑:异常处理
            System.out.println("【静态代理-日志】addUser 方法执行失败,异常:" + e.getMessage());
        } finally {
            // 增强逻辑:方法执行完毕(无论成功失败)
            System.out.println("【静态代理-日志】addUser 方法执行结束\n");
        }
    }

    @Override
    public void deleteUser(String username) {
        // 与addUser逻辑一致,重复编写增强代码
        System.out.println("【静态代理-日志】deleteUser 方法开始执行,参数:" + username);
        try {
            target.deleteUser(username);
            System.out.println("【静态代理-日志】deleteUser 方法执行成功");
        } catch (Exception e) {
            System.out.println("【静态代理-日志】deleteUser 方法执行失败,异常:" + e.getMessage());
        } finally {
            System.out.println("【静态代理-日志】deleteUser 方法执行结束\n");
        }
    }

    @Override
    public String queryUser(String username) {
        System.out.println("【静态代理-日志】queryUser 方法开始执行,参数:" + username);
        String result = null;
        try {
            result = target.queryUser(username);
            System.out.println("【静态代理-日志】queryUser 方法执行成功,结果:" + result);
        } catch (Exception e) {
            System.out.println("【静态代理-日志】queryUser 方法执行失败,异常:" + e.getMessage());
        } finally {
            System.out.println("【静态代理-日志】queryUser 方法执行结束\n");
        }
        return result;
    }
}
步骤 4:测试静态代理
public class StaticProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标类实例
        IUserService target = new UserServiceImpl();
        // 2. 创建代理类实例(注入目标类)
        IUserService proxy = new UserServiceStaticProxy(target);
        
        // 3. 调用代理方法(间接执行目标方法+增强逻辑)
        proxy.addUser("张三");
        proxy.deleteUser("李四");
        proxy.queryUser("王五");
    }
}
执行结果
【静态代理-日志】addUser 方法开始执行,参数:张三
【核心业务】添加用户成功:张三
【静态代理-日志】addUser 方法执行成功
【静态代理-日志】addUser 方法执行结束

【静态代理-日志】deleteUser 方法开始执行,参数:李四
【核心业务】删除用户成功:李四
【静态代理-日志】deleteUser 方法执行成功
【静态代理-日志】deleteUser 方法执行结束

【静态代理-日志】queryUser 方法开始执行,参数:王五
【核心业务】查询用户:王五
【静态代理-日志】queryUser 方法执行成功,结果:用户信息:王五(VIP)
【静态代理-日志】queryUser 方法执行结束

3. 静态代理的优缺点

优点
  • 无依赖:纯 Java 基础实现,无需引入任何框架;
  • 性能好:编译期生成代理类,运行时无反射 / 字节码开销,执行效率高;
  • 逻辑直观:代理类代码手动编写,增强逻辑清晰可见,便于调试。
缺点
  • 代码冗余:目标类有 N 个方法,代理类就要写 N 个方法的增强逻辑,重复代码多;
  • 维护成本高:目标类新增 / 修改方法时,代理类需同步修改,扩展性差;
  • 灵活性低:代理类与目标类强绑定,无法动态切换目标类或增强逻辑。

4. 适用场景

  • 简单增强需求(如固定方法的日志、参数校验);
  • 目标类方法固定,不会频繁修改;
  • 不允许依赖第三方框架,追求极致运行性能。

三、JDK 动态代理:运行时的 “接口代理”

       JDK 动态代理是 Java 原生支持的动态代理技术,无需依赖第三方库,核心是通过java.lang.reflect包下的Proxy类和InvocationHandler接口,在运行时动态生成代理类(接口的实现类)。

1. 原理剖析

JDK 动态代理的核心逻辑的:

  1. 代理类由Proxy.newProxyInstance()方法在运行时动态生成,本质是目标接口的实现类
  2. 开发者需实现InvocationHandler接口(拦截器),将增强逻辑集中在invoke方法中;
  3. 客户端调用代理方法时,会被InvocationHandler拦截,执行增强逻辑后,通过反射调用目标类的原始方法。

关键限制:JDK 动态代理只能代理实现了接口的目标类(代理类是接口的实现类,无法继承目标类)。

2. 核心组件

  • Proxy:生成代理类的核心类,提供newProxyInstance()方法创建代理对象;
  • InvocationHandler:方法拦截器接口,需实现invoke方法(增强逻辑的核心);
    • invoke(Object proxy, Method method, Object[] args)
      • proxy:动态生成的代理对象;
      • method:被调用的目标方法;
      • args:方法参数;
      • 返回值:目标方法的执行结果。

3. 实战案例:JDK 动态代理实现日志 + 权限增强

需求:在静态代理的基础上,新增 “权限校验” 增强逻辑,且无需为每个方法重复编写增强代码。

步骤 1:复用目标类与接口(同静态代理的IUserServiceUserServiceImpl
步骤 2:实现InvocationHandler(集中管理增强逻辑)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * JDK动态代理拦截器:集中处理所有方法的增强逻辑
 */
public class UserServiceInvocationHandler implements InvocationHandler {
    // 持有目标类实例
    private final Object target;

    // 构造器注入目标类
    public UserServiceInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 拦截代理方法的执行:所有代理方法都会触发该方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        try {
            // 增强逻辑1:日志记录(方法执行前)
            System.out.println("【JDK动态代理-日志】" + method.getName() + " 方法开始执行,参数:" + (args != null ? args[0] : "无"));
            
            // 增强逻辑2:权限校验(仅对add/delete方法生效)
            if (method.getName().equals("addUser") || method.getName().equals("deleteUser")) {
                String username = (String) args[0];
                if (!"admin".equals(username)) {
                    throw new RuntimeException("权限不足:仅admin可执行该操作");
                }
                System.out.println("【JDK动态代理-权限校验】用户 " + username + " 权限通过");
            }

            // 调用目标类的原始方法(通过反射)
            result = method.invoke(target, args);

            // 增强逻辑3:日志记录(方法执行后)
            System.out.println("【JDK动态代理-日志】" + method.getName() + " 方法执行成功,结果:" + result);
        } catch (Exception e) {
            // 增强逻辑4:异常处理
            System.out.println("【JDK动态代理-日志】" + method.getName() + " 方法执行失败,异常:" + e.getMessage());
            throw e; // 抛出异常,让客户端处理
        } finally {
            // 增强逻辑5:资源释放(示例)
            System.out.println("【JDK动态代理-资源】" + method.getName() + " 方法执行完毕,释放资源\n");
        }
        return result;
    }
}
步骤 3:通过Proxy生成代理对象并测试
import java.lang.reflect.Proxy;

public class JdkDynamicProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标类实例
        IUserService target = new UserServiceImpl();
        
        // 2. 创建拦截器实例(注入目标类)
        InvocationHandler handler = new UserServiceInvocationHandler(target);
        
        // 3. 动态生成代理对象
        IUserService proxy = (IUserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 类加载器(与目标类一致)
            target.getClass().getInterfaces(),  // 目标类实现的接口(核心:JDK代理基于接口)
            handler                              // 拦截器(增强逻辑)
        );
        
        // 4. 测试代理方法
        try {
            proxy.addUser("admin"); // 权限通过
            proxy.deleteUser("user"); // 权限不足,抛出异常
        } catch (Exception e) {
            // 捕获异常
        }
        proxy.queryUser("admin"); // 无权限校验
    }
}
执行结果
【JDK动态代理-日志】addUser 方法开始执行,参数:admin
【JDK动态代理-权限校验】用户 admin 权限通过
【核心业务】添加用户成功:admin
【JDK动态代理-日志】addUser 方法执行成功,结果:null
【JDK动态代理-资源】addUser 方法执行完毕,释放资源

【JDK动态代理-日志】deleteUser 方法开始执行,参数:user
【JDK动态代理-权限校验】用户 user 权限不足
【JDK动态代理-日志】deleteUser 方法执行失败,异常:权限不足:仅admin可执行该操作
【JDK动态代理-资源】deleteUser 方法执行完毕,释放资源

【JDK动态代理-日志】queryUser 方法开始执行,参数:admin
【核心业务】查询用户:admin
【JDK动态代理-日志】queryUser 方法执行成功,结果:用户信息:admin(VIP)
【JDK动态代理-资源】queryUser 方法执行完毕,释放资源

4. JDK 动态代理的优缺点

优点
  • 无依赖:JDK 原生支持,无需引入第三方库;
  • 代码简洁:增强逻辑集中在InvocationHandler,无需重复编写代理方法;
  • 灵活性高:可动态切换目标类(只需注入不同目标对象),支持接口扩展。
缺点
  • 只能代理接口:目标类必须实现接口,否则无法生成代理对象;
  • 性能一般:通过反射调用目标方法,JDK8 + 虽有优化,但仍有一定开销;
  • 无法代理私有方法:接口方法默认是 public,无法代理目标类的 private 方法。

5. 适用场景

  • 目标类实现了接口(如 Service 层接口);
  • 追求轻量代理,无需依赖第三方框架;
  • 增强逻辑需统一管理(如所有方法的日志、事务)。

四、CGLIB 代理:运行时的 “子类代理”

       CGLIB(Code Generation Library)是基于 ASM 字节码操作框架的动态代理库,核心能力是通过生成目标类的子类来实现代理,弥补了 JDK 动态代理只能代理接口的缺陷。广泛应用于 Spring、Hibernate 等框架。

1. 原理剖析

CGLIB 代理的核心逻辑:

  1. 通过 ASM 框架在运行时动态生成目标类的子类(代理类);
  2. 代理类重写目标类中所有非 final的方法(final 方法无法被继承重写);
  3. 开发者实现MethodInterceptor接口(方法拦截器),在intercept方法中定义增强逻辑;
  4. 客户端调用代理方法时,会被MethodInterceptor拦截,执行增强逻辑后,通过MethodProxy调用目标类的原始方法。

关键优势:可代理无接口的类,支持代理类的非 final 方法。

2. 核心组件

(1).Enhancer:CGLIB 的核心增强器,用于生成代理类,配置目标类、回调接口等;

(2).MethodInterceptor:方法拦截器接口,需实现intercept方法(增强逻辑核心);

 intercept(Object obj, Method method, Object[] args, MethodProxy proxy)

  • obj:代理对象(目标类的子类实例);
  • method:目标类的方法;
  • args:方法参数;
  • proxy:方法代理对象(推荐用于调用原始方法,性能优于反射);
  • 返回值:目标方法的执行结果。

(3).ASM:CGLIB 底层依赖的字节码操作框架,用于动态生成子类字节码。

3. 实战案例:CGLIB 代理实现日志 + 事务增强

需求:代理一个无接口的目标类,添加日志记录和事务管理(模拟)增强逻辑。

步骤 1:引入 CGLIB 依赖
<!-- Maven依赖 -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version> <!-- 最新稳定版 -->
</dependency>
步骤 2:定义无接口的目标类
/**
 * 无接口的目标类:CGLIB可直接代理类(无需接口)
 */
public class OrderService {
    // 非final方法(可被代理)
    public void createOrder(String orderNo) {
        System.out.println("【核心业务】创建订单成功:" + orderNo);
    }

    // final方法(无法被代理)
    public final void cancelOrder(String orderNo) {
        System.out.println("【核心业务】取消订单成功:" + orderNo);
    }

    // 私有方法(无法被代理)
    private void logOrder(String orderNo) {
        System.out.println("【内部日志】订单操作:" + orderNo);
    }
}
步骤 3:实现MethodInterceptor(增强逻辑)
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * CGLIB方法拦截器:定义增强逻辑
 */
public class OrderServiceMethodInterceptor implements MethodInterceptor {
    /**
     * 拦截代理方法的执行
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object result = null;
        try {
            // 增强逻辑1:日志记录(方法执行前)
            System.out.println("【CGLIB代理-日志】" + method.getName() + " 方法开始执行,参数:" + args[0]);
            
            // 增强逻辑2:事务开始(模拟)
            System.out.println("【CGLIB代理-事务】开启事务");

            // 调用目标类的原始方法(两种方式)
            // 方式1:MethodProxy.invokeSuper(obj, args)(推荐,性能好,避免反射)
            result = proxy.invokeSuper(obj, args);
            
            // 方式2:Method.invoke(target, args)(需持有目标对象,反射调用,性能差)
            // result = method.invoke(target, args);

            // 增强逻辑3:事务提交(模拟)
            System.out.println("【CGLIB代理-事务】提交事务");

            // 增强逻辑4:日志记录(方法执行后)
            System.out.println("【CGLIB代理-日志】" + method.getName() + " 方法执行成功");
        } catch (Exception e) {
            // 增强逻辑5:事务回滚(模拟)
            System.out.println("【CGLIB代理-事务】回滚事务,异常:" + e.getMessage());
            throw e;
        } finally {
            System.out.println("【CGLIB代理-日志】" + method.getName() + " 方法执行结束\n");
        }
        return result;
    }
}
步骤 4:通过Enhancer生成代理对象并测试
import net.sf.cglib.proxy.Enhancer;

public class CglibProxyTest {
    public static void main(String[] args) {
        // 1. 创建增强器(CGLIB核心类)
        Enhancer enhancer = new Enhancer();
        
        // 2. 配置增强器:设置目标类(父类)
        enhancer.setSuperclass(OrderService.class);
        
        // 3. 配置回调:关联方法拦截器
        enhancer.setCallback(new OrderServiceMethodInterceptor());
        
        // 4. 生成代理对象(目标类的子类实例)
        OrderService proxy = (OrderService) enhancer.create();
        
        // 5. 测试代理方法
        try {
            proxy.createOrder("ORDER_001"); // 非final方法,可被代理
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        System.out.println("=== 测试final方法(无法被代理)===");
        proxy.cancelOrder("ORDER_001"); // final方法,增强逻辑不生效
    }
}
执行结果
【CGLIB代理-日志】createOrder 方法开始执行,参数:ORDER_001
【CGLIB代理-事务】开启事务
【核心业务】创建订单成功:ORDER_001
【CGLIB代理-事务】提交事务
【CGLIB代理-日志】createOrder 方法执行成功
【CGLIB代理-日志】createOrder 方法执行结束

=== 测试final方法(无法被代理)===
【核心业务】取消订单成功:ORDER_001

4. CGLIB 代理的优缺点

优点
  • 可代理无接口类:无需目标类实现接口,弥补 JDK 代理的缺陷;
  • 性能较好:运行时通过MethodProxy.invokeSuper调用原始方法,无反射开销(比 JDK 代理运行时性能略优);
  • 支持类方法代理:可代理目标类的非 final 方法(无需接口)。
缺点
  • 依赖第三方库:需引入 CGLIB 依赖(底层依赖 ASM);
  • 不能代理 final 相关:final 类无法被继承,final 方法无法被重写,private/static 方法无法代理;
  • 代理类生成耗时:字节码操作比反射生成代理类耗时更长(首次生成代理类时开销较大)。

5. 适用场景

  • 目标类无接口(如遗留系统中的类);
  • 需代理类的非接口方法;
  • 框架底层增强(如 Spring AOP、Hibernate 延迟加载);
  • 追求运行时高性能(JDK8 + 后差距缩小,但仍有优势)。

五、三种代理技术全面对比

特性静态代理JDK 动态代理CGLIB 代理
代理类生成时机编译期(手动编写)运行时(反射动态生成)运行时(字节码动态生成)
实现方式实现同一接口 / 继承同一父类实现目标接口(代理类 = 接口实现类)继承目标类(代理类 = 目标类子类)
依赖无(纯 Java 基础)JDK 原生(java.lang.reflect)CGLIB 库(底层 ASM)
代理目标限制需实现接口 / 继承父类只能代理实现接口的类可代理类(非 final)、接口
方法限制无(手动控制)只能代理接口的 public 方法不能代理 final/private/static 方法
代码冗余度高(每个方法需重复增强逻辑)低(增强逻辑集中在 InvocationHandler)低(增强逻辑集中在 MethodInterceptor)
灵活性低(固定目标类 + 方法)中(基于接口,动态切换目标类)高(无接口依赖,支持多回调)
运行时性能高(无额外开销)中(反射调用开销)中 - 高(无反射,字节码生成耗时)
调试难度低(代码直观)中(反射调用,需调试 InvocationHandler)高(字节码生成,需理解 ASM)
典型应用简单固定场景(如工具类包装)Spring AOP(目标类有接口时)Spring AOP(目标类无接口时)、Hibernate

核心差异总结

  1. 生成时机:静态代理是 “编译期固定”,动态代理(JDK/CGLIB)是 “运行时动态”;
  2. 依赖关系:静态代理无依赖,JDK 代理依赖 JDK 原生,CGLIB 依赖第三方库;
  3. 代理目标:JDK 代理强依赖接口,CGLIB 代理强依赖继承,静态代理灵活但冗余;
  4. 性能:静态代理 > CGLIB 代理 ≈ JDK 动态代理(JDK8 + 后);
  5. 灵活性:CGLIB 代理 > JDK 动态代理 > 静态代理。

六、实际开发选型建议

  1. 优先选 JDK 动态代理:若目标类实现接口,优先使用 JDK 动态代理(无依赖、轻量、易维护);
  2. 无接口选 CGLIB:若目标类无接口,直接使用 CGLIB 代理(Spring AOP 默认自动切换);
  3. 简单场景选静态代理:若方法固定、增强逻辑简单,且无需扩展,可用静态代理(追求极致性能);
  4. 框架场景选动态代理:开发框架(如插件、AOP)时,优先使用 JDK/CGLIB 动态代理(灵活性高、可复用)。

Spring AOP 中的代理选择逻辑

       Spring AOP 默认采用 “自适应代理” 策略:

  • 若目标类实现接口,则使用 JDK 动态代理;
  • 若目标类无接口,则自动切换为 CGLIB 代理;
  • 若需强制使用 CGLIB 代理(即使目标类有接口),可通过配置proxy-target-class="true"开启。

七、常见问题与避坑指南

1. JDK 动态代理报错:ClassCastException

  • 原因:试图将代理对象强转为目标类(而非接口);
  • 解决:代理对象只能强转为目标类实现的接口(如IUserService proxy = (IUserService) Proxy.newProxyInstance(...))。

2. CGLIB 报错:Cannot subclass final class

  • 原因:目标类是 final 类,CGLIB 无法生成子类;
  • 解决:移除目标类的 final 修饰符,或改用 JDK 动态代理(若目标类可实现接口)。

3. 增强逻辑不生效

  • 静态代理:检查代理类是否重写了目标方法,且调用了目标对象的方法;
  • JDK 代理:检查目标类是否实现接口,InvocationHandler是否正确注入目标对象;
  • CGLIB 代理:检查目标方法是否为 final/private/static,Enhancer是否配置了setSuperclasssetCallback

4. 性能优化建议

  • 静态代理:无优化空间(本身性能最优);
  • JDK 代理:避免在invoke方法中做耗时操作,减少反射调用次数;
  • CGLIB 代理:优先使用MethodProxy.invokeSuper(而非Method.invoke),缓存Enhancer实例(避免频繁生成代理类)。

八、总结

       三种代理技术本质都是 “代理模式” 的实现,核心目标是解耦核心业务与增强逻辑,但适用场景各有侧重:

  • 静态代理:简单直接,适合固定场景,但冗余且不灵活;
  • JDK 动态代理:轻量无依赖,适合有接口的目标类,是日常开发的首选;
  • CGLIB 代理:灵活强大,适合无接口的目标类,是框架底层的常用选择。

       理解三种代理的原理和差异,不仅能帮助你在实际开发中精准选型,还能深入理解 Spring AOP、MyBatis 等框架的底层实现,提升技术深度。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐