问题描述: 当我们项目将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);