spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

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

Open spring-projects-issues opened this issue 6 years ago • 5 comments

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;

  @Autowired
  ApplicationContext applicationContext;

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

  public CountDownLatch getLatch() {
    return latch;
  }

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

  @Async
  public void function1() {
    meWithAProxy.anotherFunction(123);

    if(latch != null) {
      latch.countDown();
    }
  }

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

}

And I then use it like that:

@Configuration
@EnableCaching
@EnableAsync
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
    myClass.getLatch().await();
    assertThat(cache.get(123)).describedAs("Reentrant call failed").isNull(); // and the value is evicted as expected

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

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

  @Bean
  public MyClass myClass() {
    return new MyClass();
  }

  @Bean
  public TaskExecutor taskExecutor() {
    return new SimpleAsyncTaskExecutor();
  }

  @Bean
  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: https://stackoverflow.com/questions/45938279/cache-not-refreshing-when-being-called-from-a-asynchrounous-function-in-spring/45963494#45963494

Issue Links:

  • #18488 Scheduled method is not invoked via proxy

spring-projects-issues avatar Aug 30 '17 15:08 spring-projects-issues