related to the commit in spring framework https://github.com/spring-projects/spring-framework/commit/5aefcc802ef05abc51bbfbeb4a78b3032ff9eee3
the initialisation is set to a later stage from afterPropertiesSet() to afterSingletonsInstantiated()
In short: This prevents the caching to work when using it in a @PostConstruct use case.
Longer version: This prevents the use case where you would
create serviceB with @Cacheable on a methodB
create serviceA with @PostConstruct calling serviceB.methodB
@Component
public class ServiceA{
@Autowired
private ServiceB serviceB;
@PostConstruct
public void init() {
List<String> list = serviceB.loadSomething();
}
This results in org.springframework.cache.interceptor.CacheAspectSupport not being initialised now and thus not caching the result.
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
// check whether aspect is enabled
// to cope with cases where the AJ is pulled in automatically
if (this.initialized) {
//>>>>>>>>>>>> NOT Being called
Class<?> targetClass = getTargetClass(target);
Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass);
if (!CollectionUtils.isEmpty(operations)) {
return execute(invoker, new CacheOperationContexts(operations, method, args, target, targetClass));
}
}
//>>>>>>>>>>>> Being called
return invoker.invoke();
}
My workaround is to manually call the initialisation method:
@Configuration
public class SomeConfigClass{
@Inject
private CacheInterceptor cacheInterceptor;
@PostConstruct
public void init() {
cacheInterceptor.afterSingletonsInstantiated();
}
This of course fixes my issue but does it have side effects other that just being called 2 times (1 manual and 1 by the framework as intended)
My question is: "Is this a safe workaround to do as the initial commiter seemed to have an issue with just using the afterPropertiesSet()"
As Marten said already, you are not supposed to use any of those services in the PostConstruct
phase because you have no guarantee that the proxy interceptor has fully started at this point.
Your best shot at pre-loading your cache is to listen for ContextRefreshedEvent
(more support coming in 4.2) and do the work there. That being said, I understand that it may not be clear that such usage is forbidden so I've created SPR-12700 to improve the documentation. I am not sure what javadoc you were referring to.
To answer your question: no it's not a safe workaround. What you were using before worked by "side-effect" (i.e. it wasn't supposed to work, if your bean was initialized before the CacheInterceptor
you would have the same problem with an older version of the framework). Don't call such low-level infrastructure in your own code.
Just had the exact same problem as OP and listening to ContextRefreshedEvent
was causing my initialization method to be called twice. Listening to ApplicationReadyEvent
worked best for me.
Here is the code I used
@Component
public class MyInitializer implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
//doing things
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With