使用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
相关产品推荐
相关产品推荐

