一,Java 中的代理模式(Proxy Pattern)详解
代理模式(Proxy Pattern) 是一种常见的设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式广泛应用于 AOP(面向切面编程)、延迟加载、权限控制、日志记录等场景。
本文将详细介绍 Java 中三种常见的代理方式:
- 静态代理(Static Proxy)
- JDK 动态代理(Dynamic Proxy)
- CGLIB 动态代理
并通过实际代码示例帮助你深入理解其原理和使用场景。
代理模式的核心思想是:通过一个代理类来控制对目标对象的访问。代理类和目标类(真实主题类)实现相同的接口(或继承相同的类),从而对外表现一致,但可以在调用前后添加额外逻辑(如日志、权限检查等)。
代理模式的优点:
- 增强目标对象功能,而无需修改其代码(符合开闭原则)
- 实现关注点分离(如日志、事务、安全等)
- 支持延迟加载、远程访问等高级特性
1.1 静态代理(Static Proxy)
静态代理需要手动编写代理类,代理类和目标类实现同一个接口。
示例场景:用户服务接口
java
package com.toast.proxy.pattern.learn.user;
/**
* @author toast
* @time 2025/8/12
* @remark
*/
public interface UserService {
void addUser();
void deleteUser();
}
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("执行添加用户功能");
}
@Override
public void deleteUser() {
System.out.println("执行删除用户功能");
}
}
java
package com.toast.proxy.pattern.user;
/**
* @author liuwq
* @time 2025/8/12
* @remark 静态代理类
*/
public class UserServiceProxy implements UserService {
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void addUser() {
System.out.println("前置:记录日志开始...");
target.addUser(); // 调用业务
System.out.println("后置:记录日志结束");
}
@Override
public void deleteUser() {
System.out.println("前置:记录日志开始...");
target.deleteUser();
System.out.println("后置:记录日志结束");
}
}
vb
`public class StaticProxyDemo {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserService proxy = new UserServiceProxy(userService);
proxy.addUser();
proxy.deleteUser();
}
}`
输出结果:
java
前置:记录日志开始...
添加用户
后置:记录日志结束
前置:记录日志开始...
删除用户
后置:记录日志结束
在业务系统中,业务需求往往需要日志记录功能,如果直接将打印写在 UserServiceImpl 具体业务方法里面,日志代码侵入到业务功能当中,跟业务代码高度耦合。维护成本增大。
所以进行解耦,通过代理模式 UserServiceProxy 来实现日志的打印,实现日志打印和业务功能互相解耦。符合 开放封闭原则(修改关闭扩展开放)
不过代理模式的实现方式有很多种,其中静态代理类是一种硬编码的方式,并且缺点也很明显,需要为每个接口都需要写一个代理类。
假如我有(User, Dept, Role....)等等50个左右的类需要做统一日志打印打印功能,则需要进行创建50个左右的相关Proxy代理类,会导致项目中类成倍增加,增加维护成本,并且这些代理类基本是"重复"的代码。导致代码冗余。不够灵活,难以应对接口变化。
所以有没有一种可以不用程序手动去创建业务对应的 XxxProxy 代理类,而是交由程序自动创建代理类,实时生成减少维护。有的兄弟有的。
如果是用Java的话,Java是自带动态代理(背后是通过反射机制实现的)。
1.2 JDK 动态代理(Dynamic Proxy)
JDK 动态代理利用 java.lang.reflect.Proxy
类和 InvocationHandler
接口,在运行时动态生成代理对象,要求目标类必须实现接口。
java
package com.toast.proxy.pattern.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author liuwq
* @time 2025/8/12
* @remark
*/
public class JDKProxy implements InvocationHandler {
private Object target; // 真实主题类
public JDKProxy(Object target) {
this.target = target;
}
// 生成代理对象
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 代理业务
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【JDK动态代理】方法调用前:日志记录开始...");
Object result = method.invoke(target, args); // 调用正真的方法
System.out.println("【JDK动态代理】方法调用后:日志记录结束");
return result;
}
}
java
package com.toast.proxy.pattern.dynamic;
import com.toast.proxy.pattern.user.UserService;
import com.toast.proxy.pattern.user.UserServiceImpl;
/**
* @author liuwq
* @time 2025/8/12
* @remark
*/
public class JDKProxyDemo {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) new JDKProxy(userService).getProxy();
proxy.addUser();
proxy.deleteUser();
}
}
输出结果:
java
【JDK动态代理】方法调用前:日志记录开始...
添加用户
【JDK动态代理】方法调用后:日志记录结束
【JDK动态代理】方法调用前:日志记录开始...
删除用户
【JDK动态代理】方法调用后:日志记录结束
动态代理的核心类和方法 Proxy 和 InvocationHandler
java
/**
* @param loader 使用哪个类加载器来加载生成的代理类
* @param interfaces 获取真实主题类的接口类型(要生成代理类的前提要求必须要接口继承)
* @param h InvocationHandler实例,负责处理所有方法调用
*/
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
// 回顾这段代码
public class JDKProxy implements InvocationHandler {
private Object target; // 真实主题类
public JDKProxy(Object target) {
this.target = target;
}
// 生成代理对象
public Object getProxy() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 使用真实主题类的类加载器生成代理类
target.getClass().getInterfaces(), // 获取真实主题类的接口类型
// this 表示 JDKProxy 类,但是它实现接口 InvocationHandler,所以可以传入this
// 而 InvocationHandler 接口,只需要实现invoke() 方法,因为该方法是处理代理业务的。
this
);
}
// 代理业务
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【JDK动态代理】方法调用前:日志记录开始..."); //
Object result = method.invoke(target, args); // 调用正真的业务方法
System.out.println("【JDK动态代理】方法调用后:日志记录结束");
return result;
}
}
1.3 JDK 动态代理(泛型 + 反射)
java
package com.toast.proxy.pattern.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author liuwq
* @time 2025/8/12
* @remark 泛型版 JDK 动态代理,支持面向接口编程
* @param <T> 代理的目标接口类型
*/
public class JDKProxy<T> implements InvocationHandler {
private final T target; // 真实主题类(接口类型)
public JDKProxy(T target) {
this.target = target;
}
/** 生成代理对象,返回泛型接口类型 */
@SuppressWarnings("unchecked")
public T getProxy() {
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
// 生成代理对象,并返回原始接口类型 T
return (T) Proxy.newProxyInstance(classLoader, interfaces, this);
}
/**
* 拦截方法调用
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【JDK代理】方法调用前:日志记录开始...");
// 调用目标对象的真实方法
Object result = method.invoke(target, args);
System.out.println("【JDK代理】方法调用后:日志记录结束");
return result;
}
}
java
package com.toast.proxy.pattern.dynamic;
import com.toast.proxy.pattern.user.UserService;
import com.toast.proxy.pattern.user.UserServiceImpl;
/**
* @author liuwq
* @time 2025/8/12
* @remark
*/
public class JDKProxyDemo {
public static void main(String[] args) {
// 目标对象(实现接口)
UserService userService = new UserServiceImpl();
// 创建代理,自动推断类型,无需要进行强转
UserService proxy = new JDKProxy<UserService>(userService).getProxy();
// 调用方法(自动触发代理逻辑)
proxy.addUser();
proxy.deleteUser();
}
}
输出结果:
java
【JDK代理】方法调用前:日志记录开始...
添加用户
【JDK代理】方法调用后:日志记录结束
【JDK代理】方法调用前:日志记录开始...
删除用户
【JDK代理】方法调用后:日志记录结束
这种方法也很符合"面向接口编程"思想。调用者无需知道实现类
1.4 JDK 动态代理(支持多接口)
1.3 的JDK动态代理方法,尽管不用进行强制转化处理,但是只能处理一个接口的代理。如果是需要有多个接口需要进行代理。则需要不断的new JDKProxy() 代理对象重新创建。伪代码如下:
java
public static void main(String[] args) {
// 目标对象(实现接口)
UserService userService = new UserServiceImpl();
DeptService deptService = new DeptServiceImpl();
// 创建代理,自动推断类型,无需要进行强转
UserService userProxy = new JDKProxy<UserService>(userService).getProxy();
DeptService deptProxy = new JDKProxy<DeptService>(deptService).getProxy();
}
每一个代理对象都需要进行反复创建 JDKProxy 对象类提供所需要的代理类。如果是这样的代码的话。由于每次只能创建一种接口类型的代理对象。也就意味着 JDKProxy 是一次性用品。
所以进行一次改良版,支持多个接口类型的代理对象供给。
java
package com.toast.proxy.pattern.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author liuwq
* @time 2025/8/12
* @remark 泛型版 JDK 动态代理,支持面向接口编程
* @param <T> 代理的目标接口类型
*/
public class JDKProxy<T> implements InvocationHandler {
private final T target; // 真实主题类(接口类型)
public JDKProxy(T target) {
this.target = target;
}
/** 生成代理对象,返回泛型接口类型 */
@SuppressWarnings("unchecked")
public T getProxy() {
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
// 生成代理对象,并返回原始接口类型 T
return (T) Proxy.newProxyInstance(classLoader, interfaces, this);
}
/** 返回指定接口类型的代理对象 */
public <I> I getProxy(Class<I> interfaceType) {
Object instance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
return interfaceType.cast(instance);
}
/**
* 拦截方法调用
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【JDK代理】方法调用前:日志记录开始...");
// 调用目标对象的真实方法
Object result = method.invoke(target, args);
System.out.println("【JDK代理】方法调用后:日志记录结束");
return result;
}
}
java
// UserService 和 DataProvider 是 userService 实现的两个接口
UserService userService = new UserServiceImpl();
JDKProxy proxy = new JDKProxy<>(userService);
UserService proxy1 = proxy.getProxy(UserService.class);
DataProvider proxy2 = proxy.getProxy(DataProvider.class);
这样就支持多个接口类型来提供代理类。
1.5 代理工厂
java
package com.toast.proxy.pattern.factory;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* @author liuwq
* @time 2025/8/12
* @remark 通用代理工厂:根据 Class 创建实例,并自动添加代理(JDK 或 CGLIB)
* 支持面向接口编程
*/
public class ProxyFactory {
/**
* 创建代理对象
* @param interfaceType 接口类型(用于返回值类型)
* @param implClass 实现类(可选,若无参则调用无参构造)
* @param args 构造参数(可选)
* @param <T> 接口泛型
* @return 代理对象
*/
@SafeVarargs
public static <T> T create(Class<T> interfaceType, Class<? extends T> implClass, Object... args) {
try {
// 1. 通过反射创建目标对象
T target;
if (args.length == 0) {
target = implClass.newInstance(); // 默认无参构造
} else {
Class<?>[] argTypes = Arrays.stream(args)
.map(Object::getClass)
.toArray(Class[]::new);
target = implClass.getConstructor(argTypes).newInstance(args);
}
// 2. 判断是否可以使用 JDK 动态代理(是否有接口)
if (interfaceType.isInterface()) {
return createJDKProxy(interfaceType, target);
} else {
// 如果 interfaceType 不是接口,尝试用 CGLIB 代理目标类
return createCGLIBProxy(target);
}
} catch (Exception e) {
throw new RuntimeException("创建代理对象失败", e);
}
}
/**
* 重载:如果只传一个类(可能是具体类),用 CGLIB 代理
*/
public static <T> T create(Class<T> clazz) {
try {
T target = clazz.newInstance();
return createCGLIBProxy(target);
} catch (Exception e) {
throw new RuntimeException("创建 CGLIB 代理失败", e);
}
}
// ==================== 私有方法:JDK 代理 ====================
private static <T> T createJDKProxy(Class<T> interfaceType, T target) {
InvocationHandler handler = new ProxyInvocationHandler<>(target);
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
}
// ==================== 私有方法:CGLIB 代理 ====================
@SuppressWarnings("unchecked")
private static <T> T createCGLIBProxy(T target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new CGLIBMethodInterceptor<>(target));
return (T) enhancer.create();
}
// ==================== 拦截器实现 ====================
/**
* JDK 代理的拦截器
*/
private static class ProxyInvocationHandler<T> implements InvocationHandler {
private final T target;
public ProxyInvocationHandler(T target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String className = method.getDeclaringClass().getSimpleName();
String methodName = method.getName();
// 记录开始时间,用于性能监控
long startTime = System.currentTimeMillis();
// 方法调用前
logBefore(className, methodName, args);
try {
// 执行目标方法
Object result = method.invoke(target, args);
// 方法成功返回后
long cost = System.currentTimeMillis() - startTime;
logAfterReturning(className, methodName, cost, result);
return result; // 正常返回
} catch (InvocationTargetException ex) {
// 注意:method.invoke 抛出的是 InvocationTargetException
// 它的 cause 才是原始异常
long cost = System.currentTimeMillis() - startTime;
Throwable targetException = ex.getTargetException();
logAfterThrowing(className, methodName, cost, targetException);
// 向上抛出原始异常
throw targetException;
} catch (Exception ex) {
// 防御性捕获其他反射异常(一般不会走到这里)
long cost = System.currentTimeMillis() - startTime;
logAfterThrowing(className, methodName, cost, ex);
throw ex;
}
}
private void logBefore(String className, String methodName, Object[] args) {
StringBuilder argStr = new StringBuilder();
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
argStr.append(i > 0 ? ", " : "")
.append(args[i] == null ? "null" : args[i].toString());
}
}
System.out.printf("【JDK代理】%s.%s(%s) - 开始执行%n", className, methodName, argStr);
}
private void logAfterReturning(String className, String methodName, long cost, Object result) {
String resultStr = result == null ? "null" : result.toString();
System.out.printf("【JDK代理】%s.%s() - 执行成功,耗时: %dms,返回值: %s%n",
className, methodName, cost, resultStr);
}
private void logAfterThrowing(String className, String methodName, long cost, Throwable ex) {
System.out.printf("【JDK代理】%s.%s() - 执行异常,耗时: %dms,异常: %s: %s%n",
className, methodName, cost, ex.getClass().getSimpleName(), ex.getMessage());
}
}
/**
* CGLIB 代理的增强拦截器:支持前置、后置、异常、性能监控
*/
private static class CGLIBMethodInterceptor<T> implements MethodInterceptor {
private final T target;
public CGLIBMethodInterceptor(T target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String className = method.getDeclaringClass().getSimpleName();
String methodName = method.getName();
long startTime = System.currentTimeMillis();
// 【前置】方法调用前
logBefore(className, methodName, args);
try {
// 执行目标方法
Object result = proxy.invokeSuper(obj, args);
// 【后置】方法成功返回
long cost = System.currentTimeMillis() - startTime;
logAfterReturning(className, methodName, cost, result);
return result;
} catch (Throwable t) {
// 注意:CGLIB 中直接抛出的是原始异常(不是 InvocationTargetException)
long cost = System.currentTimeMillis() - startTime;
logAfterThrowing(className, methodName, cost, t);
// 向上抛出异常
throw t;
}
}
// ==================== 日志方法 ====================
private void logBefore(String className, String methodName, Object[] args) {
StringBuilder argStr = new StringBuilder();
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
argStr.append(i > 0 ? ", " : "")
.append(args[i] == null ? "null" : args[i].toString());
}
}
System.out.printf("【CGLIB代理】%s.%s(%s) - 开始执行%n", className, methodName, argStr);
}
private void logAfterReturning(String className, String methodName, long cost, Object result) {
String resultStr = result == null ? "null" : result.toString();
System.out.printf("【CGLIB代理】%s.%s() - 执行成功,耗时: %dms,返回值: %s%n",
className, methodName, cost, resultStr);
}
private void logAfterThrowing(String className, String methodName, long cost, Throwable ex) {
System.out.printf("【CGLIB代理】%s.%s() - 执行异常,耗时: %dms,异常: %s: %s%n",
className, methodName, cost, ex.getClass().getSimpleName(), ex.getMessage());
}
}
}
二,CGLIB
2.1 CGLIB 简介
由于Java自动 动态代理 生成的代理类,其真实主题类必须要继承接口才可以。对于没有继承接口的子类,则无法生成代理类,也就意味着需要自己手动创建代理类。
而 CGLIB 可以处理没有继承接口的子类生成代理类。
CGLIB(Code Generation Library)是一个强大的、高性能的Java字节码生成库 ,它可以在运行时动态生成Java类的子类或实现接口,从而实现动态代理 和**AOP(面向切面编程)**等功能。CGLIB广泛应用于许多Java框架中,比如 Spring 、Hibernate 等。
2.2 CGLIB 使用案例
【引入依赖】cglib
java
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
假如我们有一个业务类 OrderService
,其中包含下单、查询等方法。我们希望在不修改原有代码的前提下,自动监控每个方法的执行时间,用于性能分析。
java
package com.toast.proxy.pattern.order;
/**
* @author toast
* @time 2025/8/13
* @remark
*/
public class OrderService {
public void createOrder(String orderId) {
System.out.println("正在创建订单:" + orderId);
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("订单创建完成:" + orderId);
}
public String queryOrderStatus(String orderId) {
System.out.println("正在查询订单状态:" + orderId);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "PAID";
}
public final void finalMethod() {
System.out.println("这是 final 方法,不会被代理!");
}
}
java
package com.toast.proxy.pattern.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author toast
* @time 2025/8/13
* @remark 我们有一个业务类 OrderService,其中包含下单、查询等方法。我们希望在不修改原有代码的前提下,自动监控每个方法的执行时间,用于性能分析。
*/
public class PerformanceMonitorInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("👉 开始执行方法: " + method.getDeclaringClass().getSimpleName() + "." + method.getName());
Object result = null;
try {
result = proxy.invokeSuper(obj, args); // 执行原方法
} finally {
long elapsed = System.currentTimeMillis() - start;
System.out.println("✅ 方法执行耗时: " + elapsed + " ms");
}
return result;
}
}
java
package com.toast.proxy.pattern.cglib;
import com.toast.proxy.pattern.order.OrderService;
import net.sf.cglib.proxy.Enhancer;
/**
* @author toast
* @time 2025/8/13
* @remark
*/
public class CglibPerformanceDemo {
public static void main(String[] args) {
// 创建 Enhancer
Enhancer enhancer = new Enhancer();
// 设置要代理的类
enhancer.setSuperclass(OrderService.class);
// 设置拦截器
enhancer.setCallback(new PerformanceMonitorInterceptor());
// 创建代理对象
OrderService proxy = (OrderService) enhancer.create();
// 调用方法(会被监控)
proxy.createOrder("ORD-1001");
System.out.println();
String status = proxy.queryOrderStatus("ORD-1001");
System.out.println("订单状态: " + status);
System.out.println();
proxy.finalMethod(); // final 方法不会被拦截
}
}
需要注意的是如果你的JDK是9以上,则会有兼容性问题。启动会报错,报错信息如下:
java
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.toast.proxy.pattern.cglib.CglibPerformanceDemo.main(CglibPerformanceDemo.java:15)
Caused by: net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @3b95a09c
at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:464)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
at net.sf.cglib.core.KeyFactory$Generator.create(KeyFactory.java:221)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:174)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:153)
at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:73)
... 1 more
这是因为 JDK 9+ 模块系统(JPMS)引入的强封装性 导致的。CGLIB 使用了反射来访问 ClassLoader.defineClass()
方法,但在 JDK 9 及以上版本中,这个方法被模块系统保护了,默认不允许外部库(如 CGLIB)通过反射访问。
【解决方案】在项目启动的时候,你需要在运行 Java 程序时,显式开放相关模块权限,允许 CGLIB 使用反射。
--add-opens java.base/java.lang=ALL-UNNAMED

输出结果:
👉 开始执行方法: OrderService.createOrder
正在创建订单:ORD-1001
订单创建完成:ORD-1001
✅ 方法执行耗时: 1084 ms
👉 开始执行方法: OrderService.queryOrderStatus
正在查询订单状态:ORD-1001
✅ 方法执行耗时: 505 ms
订单状态: PAID
这是 final 方法,不会被代理!
CGLIB 虽然经典,但已基本停止维护。现代 Java 项目推荐使用 ByteBuddy,它对 JDK 9+ 模块系统支持更好。
【CGLIB的限制】
- ❌ 不能代理
final
类(无法继承)。 - ❌ 不能代理
final
方法(无法重写)。 - ❌ 静态方法无法被拦截(CGLIB 拦截的是实例方法)。
- ⚠️ 生成类会占用永久代/元空间,注意内存泄漏(尤其在频繁生成代理时)。
三,ByteBuddy
3.1 ByteBuddy 与 CGLIB 的核心区别
|---------------|-----------------------------|--------------------------------|
| | | |
| 维护状态 | ✅ 活跃维护(2024年仍在更新) | ⚠️ 基本停止维护(最后一次发布是 2019年12月) |
| 底层技术 | 基于 ASM,直接操作字节码 | 基于 ASM,但封装较老 |
| API 设计 | 现代化、流式 API(Fluent API),易读易用 | 老旧 API,配置繁琐 |
| JDK 兼容性 | ✅ 完美支持 JDK 9+ 模块系统 | ❌ 需要--add-opens
才能在 JDK 9+ 使用 |
| 代理方式 | 可生成子类、重写方法、甚至创建全新类 | 仅支持继承目标类生成子类 |
| 功能丰富性 | 支持注解、条件匹配、类型校验、Agent 注入等 | 功能较单一,仅基本代理 |
| 性能 | 更优(生成代码更高效) | 良好,但略逊于 ByteBuddy |
| 学习成本 | 中等偏高(功能强大) | 较低(简单场景容易上手) |
| Spring 使用 | Spring 6+ 内部部分模块开始倾向使用 | Spring 5.x 主要使用 CGLIB |
💡 总结:ByteBuddy 是 CGLIB 的现代替代品,更适合新项目。
3.2 使用 ByteBuddy 实现方法拦截案例
🎯 场景:监控 OrderService
方法执行时间(与之前 CGLIB 案例一致)
引入依赖
<!-- https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy -->
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.17.6</version>
</dependency>
java
package com.toast.proxy.pattern.bytebuddy;
import net.bytebuddy.implementation.bind.annotation.*;
import java.util.concurrent.Callable;
/**
* @author toast
* @time 2025/8/13
* @remark
*/
public class SimpleInterceptor {
/**
* ✅ 通用、稳定的 Byte Buddy 方法拦截器
* - @RuntimeType:允许返回值类型在运行时适配
* - @SuperCall:提供调用原始方法的能力
*/
@RuntimeType
public static Object intercept(@SuperCall Callable<?> zuper) throws Exception {
System.out.println("【拦截】方法开始执行...");
long start = System.currentTimeMillis();
try {
// 调用原方法
Object result = zuper.call();
long elapsed = System.currentTimeMillis() - start;
System.out.println("【拦截】方法执行完成,耗时: " + elapsed + " ms");
return result;
} catch (Exception e) {
long elapsed = System.currentTimeMillis() - start;
System.out.println("【拦截】方法执行异常,耗时: " + elapsed + " ms, 异常: " + e.getMessage());
throw e;
}
}
}
java
package com.toast.proxy.pattern.bytebuddy;
import com.toast.proxy.pattern.order.OrderService;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
/**
* @author toast
* @time 2025/8/13
* @remark
*/
public class PlainByteBuddyDemo {
public static void main(String[] args) throws Exception {
OrderService proxy = new ByteBuddy()
.subclass(OrderService.class)
.method(
ElementMatchers.isPublic()
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class)))
)
.intercept(MethodDelegation.to(SimpleInterceptor.class))
.make()
.load(PlainByteBuddyDemo.class.getClassLoader())
.getLoaded()
.getDeclaredConstructor().newInstance();
// 调用被拦截的方法
System.out.println("=== 测试 createOrder ===");
proxy.createOrder("ORD-3001");
System.out.println();
System.out.println("=== 测试 queryOrderStatus ===");
String status = proxy.queryOrderStatus("ORD-3001");
System.out.println("订单状态: " + status);
System.out.println();
// 测试 Object 方法(不会被拦截,也不会报错)
System.out.println("=== 测试 toString (不会被拦截) ===");
System.out.println("代理对象: " + proxy);
System.out.println("=== 测试 hashCode ===");
System.out.println("hashCode: " + proxy.hashCode());
System.out.println("=== 测试 equals ===");
System.out.println("equals itself: " + proxy.equals(proxy));
}
}
输出结果:
java
=== 测试 createOrder ===
【拦截】方法开始执行...
正在创建订单:ORD-3001
订单创建完成:ORD-3001
【拦截】方法执行完成,耗时: 1007 ms
=== 测试 queryOrderStatus ===
【拦截】方法开始执行...
正在查询订单状态:ORD-3001
【拦截】方法执行完成,耗时: 502 ms
订单状态: PAID
=== 测试 toString (不会被拦截) ===
代理对象: com.toast.proxy.pattern.order.OrderService$ByteBuddy$DQcZjlYw@5609159b
=== 测试 hashCode ===
hashCode: 1443435931
=== 测试 equals ===
equals itself: true