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

Reconsider log level for final methods on CGLIB proxy classes #33939

Closed
taqqanori opened this issue Nov 22, 2024 · 7 comments
Closed

Reconsider log level for final methods on CGLIB proxy classes #33939

taqqanori opened this issue Nov 22, 2024 · 7 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@taqqanori
Copy link

taqqanori commented Nov 22, 2024

When a method is @Async, @Value variable becomes null in any other final methods (and, the @Async method itself if it's final) of the class.

Is this unavoidable due to Java Reflection spec?

Reproduction code here: https://github.com/taqqanori/spring-async-bug.

Reproduction code in short

@Service
public class AsyncService {

	@Value("${spring.application.name}")
	private String value;

	@Async
	public void async() {
		// just declared, never called
	}

	public final void _final() {
		// becomes null
		System.out.println("async final: " + value);
	}

	public void nonFinal() {
		// becomes "demo"
		System.out.println("async non-final: " + value);
	}

}
@Service
public class NonAsyncService {

	@Value("${spring.application.name}")
	private String value;

	public final void _final() {
		// becomes "demo"
		System.out.println("non-async final: " + value);
	}

	public void nonFinal() {
		// becomes "demo"
		System.out.println("non-async non-final: " + value);
	}

}
spring.application.name=demo

Output

async final: null
async non-final: demo
non-async final: demo
non-async non-final: demo

Environment

  • OS: Ubuntu-20.04 on WSL2 on Windows11
  • Java: openjdk-21
  • Spring: Spring Boot 3.4.0 initialized from https://start.spring.io/ with "Spring Web" dependency

Thanks.

@taqqanori
Copy link
Author

I found @Retryable has the same problem spring-projects/spring-retry#478

@ZLATAN628
Copy link
Contributor

Hi @taqqanori,

Here’s my understanding: When you use the @async annotation, Spring creates a proxy object for the bean of the target class. Since the proxy is created using CGLIB, it generates a subclass of the target class and overrides the non-final methods to enable interception. As a result, when you invoke a final method, the call is handled directly by the proxy object itself, rather than being delegated to the original (target) object. Additionally, the proxy object does not copy the field values from the original target object.

@taqqanori
Copy link
Author

taqqanori commented Nov 25, 2024

Hi @ZLATAN628

Thank you for the detailed explanation. Very interesting.
If this issue is unavoidable or too hard to fix, I'd like Spring framework to raise an Exception or warn in log, when @Async/@Retryable and @Value and final is used together in a class (or its ascendant and descendant).
I wasted hours wondering why my value is null...

@lucky8987
Copy link
Contributor

@taqqanori Generating proxy objects through cglib will skip the final modified class or method, but the log level is: trace, so you may not be able to see it. The corresponding code location is: org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#enhanceFactoryBean
image

@taqqanori
Copy link
Author

@lucky8987
Thank you for investigation.
I wanted to see that log at default log level, so that I could have noticed that final was doing something, before messing around with my code.

@jhoeller
Copy link
Contributor

We log at debug level for such scenarios in CglibAopProxy.doValidateClass. Maybe we should raise this to info level at least.

@jhoeller jhoeller added in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Mar 19, 2025
@jhoeller jhoeller self-assigned this Mar 19, 2025
@jhoeller jhoeller added this to the 7.0.x milestone Mar 19, 2025
@jhoeller jhoeller changed the title @Async spoils references to @Value variables from final methods of the class Reconsider log level for final methods on CGLIB proxy classes Mar 19, 2025
@jhoeller
Copy link
Contributor

As of 7.0, we log a public final method at warn level for the non-interface case as well now. If this turns out to cause too many spurious warnings for cases like final-marked setter methods on common classes in the Spring ecosystem, we need to either adapt those affected classes to drop final or fine-tune our warn condition.

@jhoeller jhoeller modified the milestones: 7.0.x, 7.0.0-M4 Mar 21, 2025
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

5 participants