首页 > 解决方案 > OSGi 中的 bytebuddy rebase 与子类和错误名称/NoClassDefFoundError

问题描述

我正在尝试开发一个包含方法实际调用的建议。这就是我声明我的拦截器的方式:

public class SecurityInterceptor() {

    @RuntimeType
    public Object intercept(
        @SuperCall Callable<Object> supercall, 
        @This Object target, 
        @Origin Method method, 
        @AllArguments Object[] args) {  

        // Check args and annotations ...       

        Object obj = supercall.call();

        // use Spring SPEL to post-process obj content ...
    }
}

拦截器注册如下:

byte[] woven = new ByteBuddy().subclass(type)
    .method(ElementMatchers.isAnnotatedWith(Secured.class))
    .intercept(MethodDelegation.to(new SecurityInterceptor()))
    .make().getBytes();

其中编织字节数组通过 WeavingHook/WovenClass OSGi 机制进行管理。

加载编织类后,我会立即收到以下异常:

java.lang.NoClassDefFoundError: com/contoso/users/service/provider/UsersServiceImpl (wrong name: com/contoso/users/service/provider/UsersServiceImpl$ByteBuddy$q3pXZ5KY)
    at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[?:?]
    at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.defineClass(BundleWiringImpl.java:2410) ~[?:?]
    at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.findClass(BundleWiringImpl.java:2194) ~[?:?]
    at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1607) ~[?:?]
    at org.apache.felix.framework.BundleWiringImpl.access$200(BundleWiringImpl.java:80) ~[?:?]
    at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:2053) ~[?:?]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:?]
    at org.apache.felix.framework.Felix.loadBundleClass(Felix.java:1927) ~[?:?]
    at org.apache.felix.framework.BundleImpl.loadClass(BundleImpl.java:978) ~[?:?]

如果我使用rebase方法而不是方法subclass并删除@SuperCall Callable<Object> supercall参数,则会调用拦截器。

此错误仅在我在 OSGi 中应用拦截器时出现:相同的过程在我修改类的 vanilla java junit 测试中工作正常,如下所示:

<T> T loadTestClass(Class<T> clazz) throws Exception {
    return new ByteBuddy()
              .subclass(clazz)
              .method(ElementMatchers.isAnnotatedWith(Secured.class))        
              .intercept(MethodDelegation.to(securityInterceptor))
              .make()
              .load(getClass().getClassLoader())
              .getLoaded()
              .newInstance();
}

关于如何处理此NoClassDefFoundError/错误名称错误的任何想法?

标签: osgibyte-buddy

解决方案


OSGi 类加载器实现通常使用ClassLoader.defineClass将预期类名作为参数的方法:例如https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/ClassLoader .html#defineClass(java.lang.String,byte%5B%5D,int,int)。当提供预期的类名时,类加载器要求被定义的类的名称与预期的类名匹配。这是一个很好的健全性检查。

因此,如果类加载器正在定义类 Foo,则不能为具有不同名称的类(例如子类)提供字节数组。


推荐阅读