Closed EvgeniGordeev closed 6 months ago
Here's the full stacktrace:
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-2.5.0-SNAPSHOT.jar:2.5.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:779) ~[spring-boot-2.5.0-SNAPSHOT.jar:2.5.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:344) ~[spring-boot-2.5.0-SNAPSHOT.jar:2.5.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1336) ~[spring-boot-2.5.0-SNAPSHOT.jar:2.5.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1325) ~[spring-boot-2.5.0-SNAPSHOT.jar:2.5.0-SNAPSHOT]
at com.example.Application.main(Application.java:18) ~[classes/:na]
Caused by: org.springframework.cglib.core.CodeGenerationException: java.lang.IllegalAccessException-->module java.base does not open java.lang to unnamed module @324e4822
at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:578) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:110) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:108) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54) ~[spring-core-5.3.7.jar:5.3.7]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:134) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.reflect.FastClass$Generator.create(FastClass.java:65) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.proxy.MethodProxy.helper(MethodProxy.java:135) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.proxy.MethodProxy.init(MethodProxy.java:76) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:216) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.3.7.jar:5.3.7]
at com.example.test.TestServiceImpl$$EnhancerBySpringCGLIB$$f4c9a595.toString(<generated>) ~[classes/:na]
at java.base/java.lang.String.valueOf(String.java:3365) ~[na:na]
at java.base/java.io.PrintStream.println(PrintStream.java:1047) ~[na:na]
at com.example.Application.lambda$commandLineRunner$0(Application.java:25) ~[classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795) ~[spring-boot-2.5.0-SNAPSHOT.jar:2.5.0-SNAPSHOT]
... 5 common frames omitted
Caused by: java.lang.IllegalAccessException: module java.base does not open java.lang to unnamed module @324e4822
at java.base/java.lang.invoke.MethodHandles.privateLookupIn(MethodHandles.java:260) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:567) ~[na:na]
at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:574) ~[spring-core-5.3.7.jar:5.3.7]
... 24 common frames omitted
I'm going to transfer this one to the Spring Framework team for assessment. Spring Boot isn't actually involved in the proxy creation, it's a framework concern. This looks similar to https://github.com/spring-projects/spring-framework/issues/26440 but there's no module-info
involved in your sample.
@philwebb It looks like this is yet another case of a ClassLoader mismatch on proxy creation. This warning/exception will appear any time the target ClassLoader
differs from the class loader that the original class has been loaded in. We have a couple of measures in place in the meantime, in particular for Boot's restart class loader, but this still may appear in some scenarios.
There is nothing we can do about it from the framework side anymore as far as we are aware. The only way out is either --add-opens java.base/java.lang=ALL-UNNAMED
or the use of a SmartClassLoader
with a publicDefineClass
implementation (as the main app class loader, not just in the restart class loader) in order to avoid our fallback reflective defineClass
enforcement.
If any such issues come up, I'm happy to help analyzing the specific case. The most important input here is the two ClassLoader
implementations/instances in use: the one that loaded the original class (base class of the CGLIB proxy) and the specified target class loader. At ReflectUtils.defineClass
level, that's contextClass.getClassLoader()
versus the ClassLoader loader
argument.
I'll transfer this back to the Boot issue tracker. Devtools isn't being used with this specific example so I'm not sure how we can fix this one. I don't think we have a good way to consistently change the application classloader being used.
I'm not sure how we can fix this one either. In this case contextClass
is java.lang.Object
so getClassLoader()
returns null
. The loader
argument is the app class loader (jdk.internal.loader.ClassLoaders$AppClassLoader
).
As far as I can tell, the failure is specific to proxying of methods defined in the JDK such as Object#toString
which is the method being called in the sample. Calling testMethod()
on the proxied service doesn't trigger the problem and works fine (once the sample's dependencies have been updated a little so that a transaction manager is auto-configured).
This is essentially the same problem as has just been reported in https://github.com/spring-projects/spring-framework/issues/32671. Given that we don't believe there's anything we can do about it in Boot, I'm going to close this in favor of the Framework issue. I think it's likely that the outcome of that one will be a documentation update that describes the fundamental limitations of JPMS.
Here is a sample project which demonstrates the issue with SB 2.4 and JDK 16 https://github.com/EvgeniGordeev/spring-boot-java-16-aspectj-proxy-error.
The application cannot start due to:
Valid for beans with
@Transactional
and whenSpringBootApplication
combines@EnableAspectJAutoProxy
and@EnableTransactionManagement
.