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

AdvisedSupport.MethodCacheKey should check for logical equality as well as identity #33915

Closed
PleaseGiveMeTheCoke opened this issue Nov 19, 2024 · 2 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@PleaseGiveMeTheCoke
Copy link

PleaseGiveMeTheCoke commented Nov 19, 2024

In the production environment, I discovered that AdvisedSupport#methodCache contains two "identical" keys, both corresponding to the same method object.

Experimentally, I found that the methodCache established during bean initialization is practically useless. During actual method invocation, the interceptor chain retrieved via methodCache.get(cacheKey) turns out to be empty. Thus, the interceptor chain is fetched again using advisorChainFactory and then placed into the methodCache. This means that the initialization logic related to getInterceptorsAndDynamicInterceptionAdvice will not only execute during bean initialization but also during the first method invocation.

Returning to the issue itself, since each call to getClass().getDeclaredMethods()[0] results in a Method object that, when compared using ==, returns false (even if it's the same method), I believe the equals method of MethodCacheKey is poorly designed.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Nov 19, 2024
@bclozel bclozel added the in: core Issues in core modules (aop, beans, core, context, expression) label Nov 20, 2024
@PleaseGiveMeTheCoke
Copy link
Author

@bclozel Hello, the code and README in this repository provide a more detailed description of the issue. I would be very grateful if you could help confirm this problem.
https://github.com/PleaseGiveMeTheCoke/spring-aop-methodCache

@sbrannen sbrannen changed the title In org.springframework.aop.framework.AdvisedSupport.MethodCacheKey#equals, using "==" to determine object equality may lead to issues In AdvisedSupport.MethodCacheKey#equals, using == to determine object equality may lead to issues Dec 1, 2024
@sbrannen sbrannen self-assigned this Dec 1, 2024
@sbrannen sbrannen changed the title In AdvisedSupport.MethodCacheKey#equals, using == to determine object equality may lead to issues AdvisedSupport.MethodCacheKey#equals should check for equality as well as identity Dec 1, 2024
sbrannen added a commit that referenced this issue Dec 1, 2024
This commit introduces a test which verifies how many times the
matches() method of a StaticMethodMatcherPointcut is invoked during
ApplicationContext startup as well as during actual method invocations
via the advice chain, which also indirectly tests the behavior of the
equals() implementation in AdvisedSupport.MethodCacheKey.

In addition, this commit revises BeanFactoryTransactionTests to assert
that a transaction is started for the setAge() method.

See gh-33915
sbrannen added a commit to sbrannen/spring-framework that referenced this issue Dec 1, 2024
Prior to this commit, the equals() implementation in AdvisedSupport's
MethodCacheKey only considered methods to be equal based on an identity
comparison (`==`). However, an identity comparison is not always
sufficient due to the following.

- Class#getDeclaredMethods() always returns "child copies" of the
  underlying Method instances -- which means that `equals()` should be
  used instead of (or in addition to) `==` whenever the compared Method
  instances can come from different sources.

With this commit, the equals() implementation in MethodCacheKey now
considers methods equal based on identity or logical equality, giving
preference to the quicker identity check.

See spring-projectsgh-32586
Closes spring-projectsgh-33915
@sbrannen sbrannen changed the title AdvisedSupport.MethodCacheKey#equals should check for equality as well as identity AdvisedSupport.MethodCacheKey should check for logical equality as well as identity Dec 1, 2024
@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 Dec 1, 2024
@sbrannen sbrannen added this to the 6.2.1 milestone Dec 1, 2024
@sbrannen
Copy link
Member

sbrannen commented Dec 1, 2024

Hi @PleaseGiveMeTheCoke,

Congratulations on submitting your first issue for the Spring Framework! 👍

And thanks for the detailed explanation and sample project.

In commit 320831b, I introduced DefaultAdvisorAutoProxyCreatorTests which is a simplified version of your sample project that reproduces the issue. The second assertion verifies that the invocation count for the matches() method isEqualTo(3); whereas, I believe that that should rather be 2 for a static pointcut (i.e., remain unchanged).

In addition, I pushed a proposed enhancement for MethodCacheKey to the following feature branch, which I'll discuss with @jhoeller before deciding if/where to merge it.

main...sbrannen:spring-framework:issues/gh-33915-AdvisedSupport-MethodCacheKey

As a side note, this is also related to:

Regards,

Sam

sbrannen added a commit to sbrannen/spring-framework that referenced this issue Dec 2, 2024
Prior to this commit, the equals() implementation in AdvisedSupport's
MethodCacheKey only considered methods to be equal based on an identity
comparison (`==`). However, an identity comparison is not always
sufficient due to the following.

- Class#getDeclaredMethods() always returns "child copies" of the
  underlying Method instances -- which means that `equals()` should be
  used instead of (or in addition to) `==` whenever the compared Method
  instances can come from different sources.

With this commit, the equals() implementation in MethodCacheKey now
considers methods equal based on identity or logical equality, giving
preference to the quicker identity check.

See spring-projectsgh-32586
Closes spring-projectsgh-33915
sbrannen added a commit that referenced this issue Dec 4, 2024
Prior to this commit, the equals() implementation in AdvisedSupport's
MethodCacheKey only considered methods to be equal based on an identity
comparison (`==`), which led to duplicate entries in the method cache
for the same logical method.

This is caused by the fact that AdvisedSupport's
getInterceptorsAndDynamicInterceptionAdvice() method is invoked at
various stages with different Method instances for the same method:

1) when creating the proxy
2) when invoking the method via the proxy

The reason the Method instances are different is due to the following.

- Methods such as Class#getDeclaredMethods() and
  Class#getDeclaredMethod() always returns "child copies" of the
  underlying Method instances -- which means that `equals()` should be
  used instead of (or in addition to) `==` whenever the compared Method
  instances can come from different sources.

With this commit, the equals() implementation in MethodCacheKey now
considers methods equal based on identity or logical equality, giving
preference to the quicker identity check.

See gh-32586
Closes gh-33915
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