cglib Java升级后,报错java.lang.reflect.InaccessibleObjectException/java.lang.ClassFormatError

问题描述: 当我们项目将Java8升级至Java21后,使用cglib报错,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 @5a10411

结论: 如果你未在Spring环境下运行,我认为可以放弃解决此问题,换成其他代理工具来替换。cglib官方已经不维护并建议其他代理工具了。
这里也给出一个方案,但建议切换代理轮子:
增加JVM参数: --add-opens java.base/java.lang=ALL-UNNAMED

cglib github: https://github.com/cglib/cglib

IMPORTANT NOTE: cglib is unmaintained and does not work well (or possibly at all?) in newer JDKs, particularly JDK17+. If you need to support newer JDKs, we will accept well-tested well-thought-out patches… but you’ll probably have better luck migrating to something like ByteBuddy.

解决方案: 言归正传,解决方式其实就是使用Spring改造后的cglib来进行,但使用其改造后的cglib也会有一点问题,需要稍微排除Object类方法的代理。 下面给一个最终的示例。

package netty.rocky.main.cglib;


import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.NoOp;

public class Main2 {
    
    public static void main(String[] arg) {
        final int NO_PROXY_METHOD = 1;
        final int PROXY_METHOD = 0;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CglibOriginClassImpl.class);
        enhancer.setCallbacks(new Callback[]{
                (MethodInterceptor) (obj, method, args, proxy) -> proxy.invokeSuper(obj, args),
                NoOp.INSTANCE
        });
        enhancer.setCallbackFilter(method -> {
            if (method.getDeclaringClass().isAssignableFrom(Object.class)) {
                // resolve module(java.base) reflection problem:
                // module java.base does not open java.lang to unnamed module
                return NO_PROXY_METHOD;
            }
            return PROXY_METHOD;
        });
        CglibOriginClass proxyObject = (CglibOriginClass) enhancer.create();
        proxyObject.test();
        // if not set callback filter, proxy class call Object.method() will throw error.
        System.out.println(proxyObject.hashCode());
    }

    private interface CglibOriginClass {
        void test();
    }

    public static class CglibOriginClassImpl implements CglibOriginClass {
        @Override
        public void test() {
            System.out.println("target class test.");
        }
    }
}

注意:enhancer.setCallbackFilter中将Object的method排除代理了,不然会报错,比如修改成这样。

public static void main(String[] arg) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CglibOriginClassImpl.class);
        enhancer.setCallback(
                (MethodInterceptor) (obj, method, args, proxy) -> proxy.invokeSuper(obj, args)
        );
        CglibOriginClass proxyObject = (CglibOriginClass) enhancer.create();
        proxyObject.test();
        System.out.println(proxyObject.hashCode());
    }

会抛出此异常:

/Users/worthy/Library/Java/JavaVirtualMachines/corretto-21.0.3/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=50854:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath /Users/worthy/Workspace/Mine/Code/netty-rocky/netty-rocky/target/classes:/Users/worthy/.m2/repository/cglib/cglib/3.3.0/cglib-3.3.0.jar:/Users/worthy/.m2/repository/org/ow2/asm/asm/7.1/asm-7.1.jar:/Users/worthy/.m2/repository/io/netty/netty-all/4.1.109.Final/netty-all-4.1.109.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-buffer/4.1.104.Final/netty-buffer-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec/4.1.104.Final/netty-codec-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-dns/4.1.104.Final/netty-codec-dns-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-haproxy/4.1.104.Final/netty-codec-haproxy-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-http/4.1.104.Final/netty-codec-http-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-http2/4.1.104.Final/netty-codec-http2-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-memcache/4.1.104.Final/netty-codec-memcache-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-mqtt/4.1.104.Final/netty-codec-mqtt-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-redis/4.1.104.Final/netty-codec-redis-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-smtp/4.1.104.Final/netty-codec-smtp-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-socks/4.1.104.Final/netty-codec-socks-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-stomp/4.1.104.Final/netty-codec-stomp-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-codec-xml/4.1.104.Final/netty-codec-xml-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-common/4.1.104.Final/netty-common-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-handler/4.1.104.Final/netty-handler-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-native-unix-common/4.1.104.Final/netty-transport-native-unix-common-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-handler-proxy/4.1.104.Final/netty-handler-proxy-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-handler-ssl-ocsp/4.1.104.Final/netty-handler-ssl-ocsp-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-resolver/4.1.104.Final/netty-resolver-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-resolver-dns/4.1.104.Final/netty-resolver-dns-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-transport/4.1.104.Final/netty-transport-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-rxtx/4.1.104.Final/netty-transport-rxtx-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-sctp/4.1.104.Final/netty-transport-sctp-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-udt/4.1.104.Final/netty-transport-udt-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-classes-epoll/4.1.104.Final/netty-transport-classes-epoll-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-classes-kqueue/4.1.104.Final/netty-transport-classes-kqueue-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-resolver-dns-classes-macos/4.1.104.Final/netty-resolver-dns-classes-macos-4.1.104.Final.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-native-epoll/4.1.104.Final/netty-transport-native-epoll-4.1.104.Final-linux-x86_64.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-native-epoll/4.1.104.Final/netty-transport-native-epoll-4.1.104.Final-linux-aarch_64.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-native-epoll/4.1.104.Final/netty-transport-native-epoll-4.1.104.Final-linux-riscv64.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-native-kqueue/4.1.104.Final/netty-transport-native-kqueue-4.1.104.Final-osx-x86_64.jar:/Users/worthy/.m2/repository/io/netty/netty-transport-native-kqueue/4.1.104.Final/netty-transport-native-kqueue-4.1.104.Final-osx-aarch_64.jar:/Users/worthy/.m2/repository/io/netty/netty-resolver-dns-native-macos/4.1.104.Final/netty-resolver-dns-native-macos-4.1.104.Final-osx-x86_64.jar:/Users/worthy/.m2/repository/io/netty/netty-resolver-dns-native-macos/4.1.104.Final/netty-resolver-dns-native-macos-4.1.104.Final-osx-aarch_64.jar:/Users/worthy/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.41/tomcat-embed-core-9.0.41.jar:/Users/worthy/.m2/repository/org/apache/tomcat/tomcat-annotations-api/10.1.17/tomcat-annotations-api-10.1.17.jar:/Users/worthy/.m2/repository/org/springframework/boot/spring-boot-starter/3.2.1/spring-boot-starter-3.2.1.jar:/Users/worthy/.m2/repository/org/springframework/boot/spring-boot/3.2.1/spring-boot-3.2.1.jar:/Users/worthy/.m2/repository/org/springframework/spring-context/6.1.2/spring-context-6.1.2.jar:/Users/worthy/.m2/repository/org/springframework/spring-expression/6.1.2/spring-expression-6.1.2.jar:/Users/worthy/.m2/repository/io/micrometer/micrometer-observation/1.12.1/micrometer-observation-1.12.1.jar:/Users/worthy/.m2/repository/io/micrometer/micrometer-commons/1.12.1/micrometer-commons-1.12.1.jar:/Users/worthy/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/3.2.1/spring-boot-autoconfigure-3.2.1.jar:/Users/worthy/.m2/repository/org/springframework/boot/spring-boot-starter-logging/3.2.1/spring-boot-starter-logging-3.2.1.jar:/Users/worthy/.m2/repository/ch/qos/logback/logback-classic/1.4.14/logback-classic-1.4.14.jar:/Users/worthy/.m2/repository/ch/qos/logback/logback-core/1.4.14/logback-core-1.4.14.jar:/Users/worthy/.m2/repository/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.jar:/Users/worthy/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.21.1/log4j-to-slf4j-2.21.1.jar:/Users/worthy/.m2/repository/org/apache/logging/log4j/log4j-api/2.21.1/log4j-api-2.21.1.jar:/Users/worthy/.m2/repository/org/slf4j/jul-to-slf4j/2.0.9/jul-to-slf4j-2.0.9.jar:/Users/worthy/.m2/repository/jakarta/annotation/jakarta.annotation-api/2.1.1/jakarta.annotation-api-2.1.1.jar:/Users/worthy/.m2/repository/org/springframework/spring-core/6.1.2/spring-core-6.1.2.jar:/Users/worthy/.m2/repository/org/springframework/spring-jcl/6.1.2/spring-jcl-6.1.2.jar:/Users/worthy/.m2/repository/org/yaml/snakeyaml/2.2/snakeyaml-2.2.jar:/Users/worthy/.m2/repository/org/springframework/boot/spring-boot-starter-aop/3.2.1/spring-boot-starter-aop-3.2.1.jar:/Users/worthy/.m2/repository/org/springframework/spring-aop/6.1.2/spring-aop-6.1.2.jar:/Users/worthy/.m2/repository/org/springframework/spring-beans/6.1.2/spring-beans-6.1.2.jar:/Users/worthy/.m2/repository/org/aspectj/aspectjweaver/1.9.21/aspectjweaver-1.9.21.jar netty.rocky.main.cglib.Main2
target class test.
Exception in thread "main" org.springframework.cglib.core.ReflectUtils$1: ClassLoader mismatch for [java.lang.Object]: JVM should be started with --add-opens=java.base/java.lang=ALL-UNNAMED for ClassLoader.defineClass to be accessible on jdk.internal.loader.ClassLoaders$AppClassLoader; consider co-locating the affected class in that target ClassLoader instead.
	at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:530)
	at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:371)
	at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.lambda$new$1(AbstractClassGenerator.java:107)
	at org.springframework.cglib.core.internal.LoadingCache.lambda$createEntry$1(LoadingCache.java:52)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
	at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:57)
	at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
	at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:130)
	at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:317)
	at org.springframework.cglib.reflect.FastClass$Generator.create(FastClass.java:70)
	at org.springframework.cglib.proxy.MethodProxy.helper(MethodProxy.java:148)
	at org.springframework.cglib.proxy.MethodProxy.init(MethodProxy.java:89)
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:256)
	at netty.rocky.main.cglib.Main2.lambda$main$0(Main2.java:15)
	at netty.rocky.main.cglib.Main2$CglibOriginClassImpl$$EnhancerByCGLIB$$8553989f.hashCode(<generated>)
	at netty.rocky.main.cglib.Main2.main(Main2.java:19)
Caused by: java.lang.IllegalAccessException: module java.base does not open java.lang to unnamed module @7dc36524
	at java.base/java.lang.invoke.MethodHandles.privateLookupIn(MethodHandles.java:287)
	at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:526)
	... 15 more

Process finished with exit code 1

所以总结:
方案1: 增加JVM参数: --add-opens java.base/java.lang=ALL-UNNAMED
方案2: 使用spring-cglib,并排除Object代理

PS:至于为什么会出这个问题,是Java9之后增加module模块化,对权限进行了更多的限制。Reflection.getCalerClass() 这里会放不到java.base(module)。不过看官网并没有说java9有问题,按道理应该java9之后就会有异常,我没有试过,大家可以试一下。
Reflection.getCalerClass() 据说已经不建议使用这种方式了,而是建议这样(如下),我还没去研究这块,大家自行研究吧。

Optional<Class<?>> callerClass = StackWalker.getInstance()
                .walk(frames -> frames
                        .skip(1)
                        .findFirst()
                        .map(StackFrame::getDeclaringClass));

        return callerClass.orElse(null);
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值