arguably a duplicate of #24922 but since that wasn't fixed and actually causes real errors when using JPMS. It's not a warning. From the looks of it you're seeing if you can proxy java.lang
/home/xeno/.asdf/installs/java/temurin-17.0.8+7/bin/java -XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -Dmanagement.endpoints.jmx.exposure.include=* -javaagent:/home/xeno/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/lib/idea_rt.jar=41197:/home/xeno/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/bin -Dfile.encoding=UTF-8 -classpath /home/xeno/IdeaProjects/bug-spring-security-jpms/build/resources/main:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework/spring-context/6.1.5/735d1bd7372d7c53e7b31b4a9c980ce2e0b26424/spring-context-6.1.5.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/6.1.5/6dae1b06ffacbb9abab636be2dbc6acd3b6e5d68/spring-core-6.1.5.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework/spring-aop/6.1.5/a4f596bd3c55b6cec93f0e2e7245dd0bab8afec3/spring-aop-6.1.5.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework/spring-beans/6.1.5/9ae967f467281c9bb977585ef4d5ea7351704d60/spring-beans-6.1.5.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework/spring-expression/6.1.5/7e21cb1c6bbef1509e12d485b75ffc61278d9fa7/spring-expression-6.1.5.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/io.micrometer/micrometer-observation/1.12.4/492deebbd9b8ab23f588428f66578e21af266e01/micrometer-observation-1.12.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework/spring-jcl/6.1.5/896ae3519327731589c6e77521656b50ae32d5b3/spring-jcl-6.1.5.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/io.micrometer/micrometer-commons/1.12.4/a57f10c78956b38087f97beae66cf14cb8b08d34/micrometer-commons-1.12.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-web/3.2.4/a74df12b71060da7c8e87f9a8c2ef4ea43fc8017/spring-boot-starter-web-3.2.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-json/3.2.4/ef3f72369ce7f6f7a7b02c0b23e60ef5bdf581b1/spring-boot-starter-json-3.2.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter/3.2.4/842cf7f0ed2ecfef3011f3191fc53c59ceed752/spring-boot-starter-3.2.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-tomcat/3.2.4/ffa632eeaaf1a4e807ec4bbcc1938f7d43096472/spring-boot-starter-tomcat-3.2.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework/spring-webmvc/6.1.5/92809fce136e0b662dc9325529443386ba5ec2c6/spring-webmvc-6.1.5.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework/spring-web/6.1.5/4f4e92cc52ee33260f1ee0cdc7b7a2f22d49708c/spring-web-6.1.5.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.4/7de629770a4559db57128d35ccae7d2fddd35db3/jackson-datatype-jsr310-2.15.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.module/jackson-module-parameter-names/2.15.4/e654497a08359db2521b69b5f710e00836915d8c/jackson-module-parameter-names-2.15.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.datatype/jackson-datatype-jdk8/2.15.4/694777f182334a21bf1aeab1b04cc4398c801f3f/jackson-datatype-jdk8-2.15.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.15.4/560309fc381f77d4d15c4a4cdaa0db5025c4fd13/jackson-databind-2.15.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-logging/3.2.4/32616f4a33ec0fda0c54aaa67ab10dc78df3fd78/spring-boot-starter-logging-3.2.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/jakarta.annotation/jakarta.annotation-api/2.1.1/48b9bda22b091b1f48b13af03fe36db3be6e1ae3/jakarta.annotation-api-2.1.1.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/2.2/3af797a25458550a16bf89acc8e4ab2b7f2bfce0/snakeyaml-2.2.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.19/adf4710fac2471236f8a466ca678cdf7e6a8257c/tomcat-embed-websocket-10.1.19.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.apache.tomcat.embed/tomcat-embed-core/10.1.19/3dbbca8acbd4dd6a137c3d6f934a2931512b42ce/tomcat-embed-core-10.1.19.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.apache.tomcat.embed/tomcat-embed-el/10.1.19/c61a582c391aca130884a5421deedfe1a96c7415/tomcat-embed-el-10.1.19.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-annotations/2.15.4/5223ea5a9bf52cdc9c5e537a0e52f2432eaf208b/jackson-annotations-2.15.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.15.4/aebe84b45360debad94f692a4074c6aceb535fa0/jackson-core-2.15.4.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.4.14/d98bc162275134cdf1518774da4a2a17ef6fb94d/logback-classic-1.4.14.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-to-slf4j/2.21.1/d77b2ba81711ed596cd797cc2b5b5bd7409d841c/log4j-to-slf4j-2.21.1.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.slf4j/jul-to-slf4j/2.0.12/eb5f48f782b41cc881b0bf1fb4d88ae2ff6d5b93/jul-to-slf4j-2.0.12.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-core/1.4.14/4d3c2248219ac0effeb380ed4c5280a80bf395e8/logback-core-1.4.14.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/2.0.12/48f109a2a6d8f446c794f3e3fa0d86df0cdfa312/slf4j-api-2.0.12.jar:/home/xeno/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.21.1/74c65e87b9ce1694a01524e192d7be989ba70486/log4j-api-2.21.1.jar -p /home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/3.2.4/b3f481aff8f0775f44d78399c804a8c52d75b971/spring-boot-autoconfigure-3.2.4.jar:/home/xeno/IdeaProjects/bug-spring-security-jpms/build/classes/java/main:/home/xeno/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/3.2.4/ccb7cbb30dcf1d91dbbf20a3219a457eead46601/spring-boot-3.2.4.jar -m bug.spring.security.jpms.main/org.example.bugspringsecurityjpms.BugSpringSecurityJpmsApplication
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.4)
2024-04-18T15:38:49.330-04:00 INFO 1910319 --- [bug-spring-security-jpms] [ main] o.e.b.BugSpringSecurityJpmsApplication : Starting BugSpringSecurityJpmsApplication using Java 17.0.8 with PID 1910319 (/home/xeno/IdeaProjects/bug-spring-security-jpms/build/classes/java/main started by xeno in /home/xeno/IdeaProjects/bug-spring-security-jpms)
2024-04-18T15:38:49.333-04:00 INFO 1910319 --- [bug-spring-security-jpms] [ main] o.e.b.BugSpringSecurityJpmsApplication : No active profile set, falling back to 1 default profile: "default"
2024-04-18T15:38:49.730-04:00 WARN 1910319 --- [bug-spring-security-jpms] [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.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 @5a4041cc
2024-04-18T15:38:49.738-04:00 INFO 1910319 --- [bug-spring-security-jpms] [ main] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-04-18T15:38:49.755-04:00 ERROR 1910319 --- [bug-spring-security-jpms] [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.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 @5a4041cc
at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:547) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:371) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:575) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.lambda$new$1(AbstractClassGenerator.java:107) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.cglib.core.internal.LoadingCache.lambda$createEntry$1(LoadingCache.java:52) ~[spring-core-6.1.5.jar:6.1.5]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:57) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:130) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:317) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:562) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:407) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.context.annotation.ConfigurationClassEnhancer.createClass(ConfigurationClassEnhancer.java:138) ~[spring-context-6.1.5.jar:6.1.5]
at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:109) ~[spring-context-6.1.5.jar:6.1.5]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:533) ~[spring-context-6.1.5.jar:6.1.5]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:310) ~[spring-context-6.1.5.jar:6.1.5]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:363) ~[spring-context-6.1.5.jar:6.1.5]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:153) ~[spring-context-6.1.5.jar:6.1.5]
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:788) ~[spring-context-6.1.5.jar:6.1.5]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:606) ~[spring-context-6.1.5.jar:6.1.5]
at spring.boot@3.2.4/org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.4.jar:na]
at spring.boot@3.2.4/org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.4.jar:na]
at spring.boot@3.2.4/org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.4.jar:na]
at spring.boot@3.2.4/org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[spring-boot-3.2.4.jar:na]
at spring.boot@3.2.4/org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-3.2.4.jar:na]
at spring.boot@3.2.4/org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-3.2.4.jar:na]
at bug.spring.security.jpms.main/org.example.bugspringsecurityjpms.BugSpringSecurityJpmsApplication.main(BugSpringSecurityJpmsApplication.java:10) ~[main/:na]
Caused by: 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 @5a4041cc
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[na:na]
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[na:na]
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199) ~[na:na]
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193) ~[na:na]
at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:505) ~[spring-core-6.1.5.jar:6.1.5]
... 26 common frames omitted
Process finished with exit code 1
------------------------------------------------------------
Gradle 8.7
------------------------------------------------------------
Build time: 2024-03-22 15:52:46 UTC
Revision: 650af14d7653aa949fce5e886e685efc9cf97c10
Kotlin: 1.9.22
Groovy: 3.0.17
Ant: Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM: 17.0.10 (Eclipse Adoptium 17.0.10+7)
OS: Linux 6.6.26-1-MANJARO amd64
bug-spring-framework-jpms-32671.tar.gz
blocks #18079
side comment: can we please stop the pattern of closing with "it's just a warning"? especially if it's a warning from java which means it's someone's error.
Comment From: philwebb
Some debugging shows that the classloader used when trying to enhance org.example.bugspringsecurityjpms.BugSpringSecurityJpmsApplication
is the AppClassLoader
, despite the RestartClassLoader
being set here.
Comment From: philwebb
@xenoterracide If you're not directly calling bean methods in your @Configuration
class you can try doing @SpringBootApplication(proxyBeanMethods = false)
so that the proxy isn't created.
Comment From: jhoeller
Please understand that JPMS simply does not allow for defining new classes in unrelated ClassLoaders: There is intentionally no Java platform API that lets us do this. This is not a CGLIB incompatibility or a legacy warning that we don't care about, it is rather a fundamental consequence of the module system being designed to prevent such runtime definitions in distinct ClassLoaders.
As for that fallback code path in ReflectUtils
, we just retain that for compatibility with --add-opens=java.base/java.lang=ALL-UNNAMED
. This will always fail in a strict module system setup but only after having tried the JDK 9+ Lookup.defineClass
API first. Unfortunately Lookup.defineClass
only works for the original ClassLoader, not for a separate ClassLoader that we may want to define the proxy class in. This prevents certain kinds of use cases that used to work fine in a classpath setup, e.g. proxies for certain core Java types.
There is one escape hatch in a Spring setup: SmartClassLoader.publicDefineClass
which the Boot RestartClassLoader
implements. Such explicit support for externally provided class definitions is the only way to make a separate ClassLoader work in a JPMS setup at all. If https://github.com/spring-projects/spring-boot/issues/40434 reveals anything concrete that can be improved for that escape hatch, I'm happy to consider it. Beyond that, I'm afraid we are not able to do anything about the fundamental limitations in the platform module system.
Comment From: wilkinsona
@jhoeller this isn't a Devtools problem as the attached sample isn't using Devtools and RestartClassLoader
isn't involved. Unfortunately, that means that there's no opportunity in Boot for us to do anything that requires a custom class loader.
I think this is the same as or very similar to the problem reported in https://github.com/spring-projects/spring-boot/issues/26578. I've just closed that issue as there's nothing we can do in Boot as we don't have any control over the class loader.
I think the proxyBeanMethods = false
workaround, either on @SpringBootApplication
or @Configuration
, is the best we can offer at the moment for this particular case. It may be that this just needs to be documented as a possible escape hatch for the fundamental limitations of the platform module system. For other Framework features that rely on proxying, such as the use of AOP seen in https://github.com/spring-projects/spring-boot/issues/26578, there may be no workaround.
Comment From: jhoeller
@wilkinsona thanks for the update, I'll turn this issue into a documentation ticket then. There is indeed nothing we can do in scenarios where we don't control the ClassLoader, and I'm not expecting any Devtools-driven refinement on the core side either.
Comment From: philwebb
FWIW the sample in https://github.com/spring-projects/spring-boot/issues/26578 does use devtools but for some reason the RestartClassLoader
isn't being used when the enhancer runs.
Comment From: xenoterracide
questions:
1. Has anyone whined at Java about this? I'd do it but I A don't know what I'm talking about, and B it'd be a lot more meaningful coming from Spring which is huge.
2. Is it possible to auto-magically detect being run on the module path and either disable the feature auto-magically or in addition to documentation throw a better error pointing to the documentation/fix?
3. Tangent, I notice the error itself is around java.lang classes, whenever I've defined a @Bean
of type String (etc), that's had problems where I've had to disable the proxy on it anyways... from the discussion I'm certain that avoiding that wouldn't fix anything but maybe it could be avoided?
Comment From: jhoeller
I've added a note to the reference documentation as well as corresponding hints to the exceptions thrown from CGLIB's ReflectUtils
and specifically from ConfigurationClassEnhancer
. I hope this improves the guidance when running into this.
There is no chance of this getting revised at the Java platform level. This is part of the encapsulation design, preventing certain internal access to core Java classes to begin with. While there is an escape hatch on the classpath, there is intentionally none for the module path.
Comment From: xenoterracide
long term then, it might be a good idea to extract the "cglib" package to another jar and not make it available in spring boot by default (like v4). just my opinion, so it can be more of an auto-detected feature... or see if there are other strategies to do this that could be employed.
Honestly, I don't think I'll be missing anything? time will tell I guess... never really liked some of that proxy magic in configuration classes myself.
Thanks for the documentation!
Comment From: xenoterracide
it would appear that these problem also disappear with exports package.name to spring.beans, spring.context;
which is probably the more correct solution than disabling the proxies. Note: I have not checked devtools with this at this time, I suspect that might still be problematic, but this does seem to work for tests, and devtools does not.