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

EnableAsync prevents EnableCaching from working in self-injection scenarios [SPR-15915] #20469

spring-projects-issues opened this issue Aug 30, 2017 · 2 comments
in: core type: bug


Copy link

@spring-projects-issues spring-projects-issues commented Aug 30, 2017

Henri Tremblay opened SPR-15915 and commented

If I use @Async and @CacheEvict on the same class, calling the method with @CacheEvict directly won't evict anymore.

Let's say I have the following class.

public class MyClass {

  private CountDownLatch latch;
  private MyClass meWithAProxy;

  ApplicationContext applicationContext;

  public void init() {
    meWithAProxy = applicationContext.getBean(MyClass.class);

  public CountDownLatch getLatch() {
    return latch;

  public void setLatch(CountDownLatch latch) {
    this.latch = latch;

  public void function1() {

    if(latch != null) {

  @CacheEvict(cacheNames = "cache", key = "#testId")
  public List<Integer> anotherFunction(int testId) {
    return Collections.emptyList();


And I then use it like that:

public class App {

  public static void main(String[] args) throws InterruptedException {
    ApplicationContext context = new AnnotationConfigApplicationContext(App.class);
    CacheManager cacheManager = context.getBean(CacheManager.class);
    Cache cache = cacheManager.getCache("cache");

    MyClass myClass = context.getBean(MyClass.class);

    cache.put(123, "test"); // value to evict

    myClass.setLatch(new CountDownLatch(1));
    myClass.function1(); // this is correctly called asynchronously
    assertThat(cache.get(123)).describedAs("Reentrant call failed").isNull(); // and the value is evicted as expected

    cache.put(1, "test"); // new value to evict

    myClass.anotherFunction(1); // direct call
    assertThat(cache.get(1)).describedAs("Direct call failed").isNull(); // fails!

  public MyClass myClass() {
    return new MyClass();

  public TaskExecutor taskExecutor() {
    return new SimpleAsyncTaskExecutor();

  public CacheManager cacheManager() {
    javax.cache.CacheManager cacheManager = Caching.getCachingProvider().getCacheManager();
    cacheManager.createCache("cache", new MutableConfiguration<>().setStoreByValue(false));
    return new JCacheCacheManager(cacheManager);

For some reason, the cache interceptor is not there. It seems that the Advisors with the cache are replaced by the async ones but I don't know why.

Affects: 4.3.10

Reference URL:

Issue Links:

  • #18488 Scheduled method is not invoked via proxy
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Sep 6, 2017

Juergen Hoeller commented

This is strongly related to self-injection: That self-injected early bean reference gets flagged as such in AbstractAutoProxyCreator, expecting to see a pre-proxied instance in postProcessAfterInitialization which it doesn't have to add any interceptors to anymore. While this is an accurate assumption in most cases, it can lead to accidental exposure of an incomplete proxy in self-injection scenarios.

Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Sep 19, 2017

Juergen Hoeller commented

It's generally a good idea to break such a self-reference initialization cycle with an @Lazy reference (or corresponding ObjectFactory/Provider declaration), in this case for the meWithAProxy reference. We'll see what we can do to make it work without that as well but probably only in 5.x.

@spring-projects-issues spring-projects-issues added type: bug in: core labels Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
in: core type: bug
None yet

No branches or pull requests

1 participant