Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring AOP cannot generate proxy for lambda on Java 16+ #27971

Closed
Ferioney opened this issue Jan 24, 2022 · 5 comments
Closed

Spring AOP cannot generate proxy for lambda on Java 16+ #27971

Ferioney opened this issue Jan 24, 2022 · 5 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@Ferioney
Copy link

Ferioney commented Jan 24, 2022

I'm migrating to Java 17 and found an issue with proxy generation for 'lambda beans'.

  • Spring Boot: 2.6.3
  • Maven: 3.8.4
  • Java:
openjdk version "17.0.2" 2022-01-18 LTS
OpenJDK Runtime Environment Corretto-17.0.2.8.1 (build 17.0.2+8-LTS)
OpenJDK 64-Bit Server VM Corretto-17.0.2.8.1 (build 17.0.2+8-LTS, mixed mode, sharing)

Spring AOP cannot generate a proxy for beans like:

    @Bean
    public Supplier<String> stringSupplier() {
        return () -> "lambda supplier value";
    }

Errors:

Caused by: org.springframework.aop.framework.AopConfigException: 
Could not generate CGLIB subclass of class com.example.issue.aopissue.service.IssueConfiguration$$Lambda$638/0x0000000800e796e0: 
Common causes of this problem include using a final class or a non-visible class; 
nested exception is 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 @5ecddf8f

But if I use class implementation it works fine:

public class TestSupplierService implements Supplier<String> {

    @Override
    public String get() {
        return "class supplier value";
    }
}

I prepared demo project aop-issue to show the issue:

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 24, 2022
@jhoeller jhoeller added the in: core Issues in core modules (aop, beans, core, context, expression) label Jan 24, 2022
@jhoeller jhoeller added this to the Triage Queue milestone Jan 24, 2022
@sbrannen
Copy link
Member

You can make the code work the way you'd expect by adding the following to your pom.xml.

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-surefire-plugin</artifactId>
	<version>3.0.0-M5</version>
	<configuration>
		<argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
	</configuration>
</plugin>

But this is certainly an interesting issue. I'm guessing that the JDK thinks that the com.example.issue.aopissue.service.IssueConfiguration$$Lambda$638/0x0000000800e796e0 type resides in same package as java.util.function.Supplier and therefore in the java.lang module instead of being local to your module, and I'm wondering if that's perhaps a bug in the JDK.

@sbrannen sbrannen added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Feb 1, 2022
@sbrannen sbrannen self-assigned this Feb 1, 2022
@sbrannen sbrannen modified the milestones: Triage Queue, 5.3.16 Feb 1, 2022
@sbrannen sbrannen changed the title Spring AOP could not generate proxy for lambda Java 17 Spring AOP cannot generate proxy for lambda on Java 16+ Feb 1, 2022
@sbrannen
Copy link
Member

sbrannen commented Feb 1, 2022

I'm guessing that the JDK thinks that the com.example.issue.aopissue.service.IssueConfiguration$$Lambda$638/0x0000000800e796e0 type resides in same package as java.util.function.Supplier and therefore in the java.lang module instead of being local to your module, and I'm wondering if that's perhaps a bug in the JDK.

For the sake of clarity, after investigating this issue, there does not appear to be an issue with the JDK. Rather:

  • Spring AOP is attempting to create a CGLIB-based proxy for the lambda expression.
  • You therefore need --add-opens java.base/java.lang=ALL-UNNAMED on Java 16 or higher in order to allow CGLIB to create the class-based proxy for the lambda expression.

Note that --add-opens java.base/java.lang=ALL-UNNAMED is not necessary on Java 9 through Java 15.

For the time being, people can use the --add-opens workaround.

However, the Spring team will investigate a way to avoid the creation of CGLIB-based proxies for lambda expressions. A JDK dynamic proxy should always suffice for a class that can only ever be used via the functional interface it implements. Dynamic proxies should also be used for lambda expressions and method references even if proxyTargetClass has been set to true.

@sbrannen
Copy link
Member

sbrannen commented Feb 4, 2022

Available Workarounds

The easiest way to work around this issue when using Spring Boot is by adding the following to your application.properties (or YAML) file.

spring.aop.proxy-target-class=false

If you're using Spring Framework without Spring Boot, ensure that @EnableAspectJAutoProxy is not declared as @EnableAspectJAutoProxy(proxyTargetClass = true). In other words, keep the proxyTargetClass = false default behavior.


If you do not wish to make either of the above changes to your application configuration, you can run your application (or tests) using --add-opens java.base/java.lang=ALL-UNNAMED as mentioned in #27971 (comment).

@sbrannen
Copy link
Member

sbrannen commented Feb 5, 2022

@Ferioney, I confirmed that the change submitted in 5d7a632 allows your sample application to pass without any changes.

Please add the following to your pom.xml and let us know if you run into any issues.

	<properties>
		<java.version>17</java.version>
		<spring-framework.version>5.3.16-SNAPSHOT</spring-framework.version>
	</properties>

<!-- ... -->

	<repositories>
		<repository>
			<id>repository.spring.snapshot</id>
			<name>Spring Snapshot Repository</name>
			<url>https://repo.spring.io/snapshot</url>
		</repository>
	</repositories>

@Ferioney
Copy link
Author

Ferioney commented Feb 9, 2022

@sbrannen checked and it worked as expected.
Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants