You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Byte-Buddy创建代理遇IllegalAccessError,如何让超类识别代理?

问题描述

类层级结构

同一包下的类继承关系如下:

abstract class __BaseEntityService<T extends __BaseEntity<U>, U> {
}

abstract class _BaseEntityService<T extends _BaseEntity<U>, U>
        extends __BaseEntityService<T, U> {
}

public class SomeEntityService
        extends _BaseEntityService<Some, Integer> {
}

ByteBuddy动态代理实现

同包测试类中尝试用ByteBuddy创建动态代理:

class SomeServiceTest {

    void proxy() {
        final var instance = new SomeService();
        final var handler = new ValidationInvocationHandler(instance, validator);
        // 创建由handler处理调用逻辑的代理对象
        final var loaded = new ByteBuddy()
                .subclass(serviceClass)
                .method(ElementMatchers.any())
                .intercept(InvocationHandlerAdapter.of(handler))
                .make()
                .load(serviceClass.getClassLoader())
                .getLoaded();
        try {
            return loaded.getConstructor().newInstance();
        } catch (final ReflectiveOperationException roe) {
            throw new RuntimeException("failed to instantiate " + loaded, roe);
        }
    }
}

运行时错误

执行时抛出以下错误:

java.lang.IllegalAccessError: failed to access class ....__BaseEntityService from class ....SomeService$ByteBuddy$3WcNUA2k (....__BaseEntityService is in unnamed module of loader 'app'; ....SomeService$ByteBuddy$3WcNUA2k is in unnamed module of loader net.bytebuddy.dynamic.loading.ByteArrayClassLoader @1c0ec502)
    at ....SomeService$ByteBuddy$3WcNUA2k.<clinit>(Unknown Source)
    at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
    at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1155)
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorFactory.newFieldAccessor(UnsafeFieldAccessorFactory.java:42)
    at java.base/jdk.internal.reflect.ReflectionFactory.newFieldAccessor(ReflectionFactory.java:185)
    at java.base/java.lang.reflect.Field.acquireFieldAccessor(Field.java:1132)
    at java.base/java.lang.reflect.Field.getFieldAccessor(Field.java:1113)
    at java.base/java.lang.reflect.Field.set(Field.java:799)
    at net.bytebuddy.implementation.LoadedTypeInitializer$ForStaticField.onLoad(LoadedTypeInitializer.java:170)
    at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:234)
    at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:103)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6166)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6154)

Javassist可用替代实现

用Javassist实现的可行代码如下:

final Proxy proxy;
{
    final var factory = new ProxyFactory();
    factory.setSuperclass(serviceClass);
    factory.setFilter(m -> !m.getName().equals("finalize"));
    final var c = factory.createClass();
    try {
        proxy = (Proxy) c.getDeclaredConstructor().newInstance();
    } catch (final ReflectiveOperationException roe) {
        throw new RuntimeException("failed to instantiate the proxy class: " + c, roe);
    }
    final MethodHandler mi = (self, thisMethod, proceed, args) -> handler.invoke(instance, thisMethod, args);
    proxy.setHandler(mi);
    proxy.setHandler(mi);
}
return (T) proxy;
}

解决方案

错误根源是Java模块系统的访问限制:ByteBuddy默认用ByteArrayClassLoader加载动态生成的类,而原包级私有父类__BaseEntityService、_BaseEntityService在应用类加载器的未命名模块中,不同类加载器的模块间默认无法访问包级私有成员。

提供三种解决方式:

方式1:修改ByteBuddy类加载策略

让动态类直接注入到原类的类加载器中,确保和原类处于同一模块。修改加载代码时指定INJECTION策略:

final var loaded = new ByteBuddy()
        .subclass(serviceClass)
        .method(ElementMatchers.any())
        .intercept(InvocationHandlerAdapter.of(handler))
        .make()
        .load(serviceClass.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
        .getLoaded();

方式2:调整父类访问权限

将__BaseEntityService和_BaseEntityService的访问权限改为public,这样即使跨类加载器模块也能正常访问父类。

方式3:使用ByteBuddy重定义类(场景受限)

如果无需创建子类,可直接重定义原类实现增强,适合原类未初始化或允许重定义的场景:

final var dynamicType = new ByteBuddy()
        .redefine(serviceClass)
        .method(ElementMatchers.any())
        .intercept(InvocationHandlerAdapter.of(handler))
        .make();
final var loaded = dynamicType.load(serviceClass.getClassLoader()).getLoaded();
return loaded.getConstructor().newInstance();

内容的提问来源于stack exchange,提问作者Jin Kwon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 05:37:22