设计模式之代理模式

一,Java 中的代理模式(Proxy Pattern)详解

代理模式(Proxy Pattern) 是一种常见的设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式广泛应用于 AOP(面向切面编程)、延迟加载、权限控制、日志记录等场景。

本文将详细介绍 Java 中三种常见的代理方式:

  1. 静态代理(Static Proxy)
  2. JDK 动态代理(Dynamic Proxy)
  3. 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框架中,比如 SpringHibernate 等。

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
相关推荐
深念Y4 天前
局域网 HTTP 请求转发 502 问题排查记录
网络·网络协议·http·网络安全·代理·转发·中转
ydd1001006 天前
正则表达式匹配
正则表达式·代理模式
LayZhangStrive7 天前
设计模式(二)装饰器模式、代理模式(待更新)
设计模式·代理模式·装饰器模式·后端开发
Lam Tang12 天前
APS 系列文章10
java·代理模式
Lam Tang12 天前
APS 系列文章 08
java·代理模式
wangjialelele13 天前
动态规划DP经典题型总结(Java、C++):路径、子数组、子序列、背包问题详解
java·c++·算法·面试·动态规划·代理模式
聪明蛋子哟13 天前
从MCP到A2A再到Proxy:Agent-工具协议的三种代理模式
代理模式
Lam Tang13 天前
APS 系列文章 09
java·代理模式
Lam Tang13 天前
APS 系列文章 01
java·代理模式
代码AC不AC18 天前
【Linux】NAT与代理
linux·nat·代理