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

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

Comments

@spring-projects-issues
Copy link
Collaborator

@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;

  @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
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.

@spring-projects-issues
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
Labels
in: core type: bug
Projects
None yet
Development

No branches or pull requests

1 participant