设计模式之代理模式

一,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
相关推荐
##学无止境##7 小时前
从青铜到王者:Java设计模式——代理模式
java·设计模式·代理模式
越来越无动于衷7 小时前
代理模式深度解析:从静态代理到 Spring AOP 实现
java·spring·代理模式
秃了也弱了。2 天前
Redisson3.14.1及之后连接阿里云redis代理模式,使用分布式锁:ERR unknown command ‘WAIT‘
redis·阿里云·代理模式
我家大宝最可爱2 天前
动态规划:入门思考篇
算法·动态规划·代理模式
Zyy~6 天前
《设计模式》代理模式
设计模式·代理模式
melonbo8 天前
代理模式C++
c++·设计模式·系统安全·代理模式
啊阿狸不会拉杆11 天前
《算法导论》第 15 章 - 动态规划
数据结构·c++·算法·排序算法·动态规划·代理模式
王彬泽14 天前
【设计模式】代理模式
设计模式·代理模式
是店小二呀18 天前
【动态规划 | 子序列问题】子序列问题的最优解:动态规划方法详解
算法·动态规划·代理模式