Description
Petar Tahchiev opened SPR-16738 and commented
In my spring-boot application I need to be able to turn on/off the caching of the app at runtime. I found a very neat way but to make it work I have introduced some changes in the AbstractTransactionSupportingCacheManager
(PR will follow in a minute).
First of all in my spring-boot application I define a custom CacheManagerCustomizer
:
@Bean
public CacheManagerCustomizer defaultSpringCacheManagerCustomizer() {
return cacheManager -> {
CacheDecorator cacheDecorator = ((AbstractCacheManager) cacheManager).getCacheDecorator();
if (cacheDecorator != null) {
((AbstractCacheManager) cacheManager).setCacheDecorator(new NemesisCacheKeyAwareCacheDecorator(cacheDecorator));
} else {
((AbstractCacheManager) cacheManager).setCacheDecorator(new NemesisCacheKeyAwareCacheDecorator());
}
};
}
and this works fine - now I plug-in a custom NemesisCacheKeyAwareCacheDecorator
which checks at runtime if a given flag is on and so returns the underlying Cache
or if it is off and so it returns null
denoting that no such cache was found.
However as you can see the AbstractCacheManager
now has an attribute cacheDecorator
of type CacheDecorator
:
public interface CacheDecorator {
/**
* Decorate the given cache if necessary.
*
* @param cache the cache to decorate
* @return the decorated cache
*/
Cache decorateCache(Cache cache);
boolean shouldDecorate();
}
and so the AbstractCacheManager
's decorate
method becomes:
protected Cache decorateCache(Cache cache) {
return this.cacheDecorator != null && this.cacheDecorator.shouldDecorate() ? this.cacheDecorator.decorateCache(cache) : cache;
}
Then the AbstractTransactionSupportingCacheManager
becomes much simpler as the logic of transactionAware
is now delegated to a new TransactionAwareCacheDecorator
public class TransactionAwareCacheDecorator implements CacheDecorator {
/**
* Whether this CacheDecorator is transaction aware and should expose transaction-aware Cache objects.
* <p>Default is "false". Set this to "true" to synchronize cache put/evict
* operations with ongoing Spring-managed transactions, performing the actual cache
* put/evict operation only in the after-commit phase of a successful transaction.
*/
private boolean transactionAware;
public TransactionAwareCacheDecorator(boolean transactionAware) {
this.transactionAware = transactionAware;
}
@Override
public Cache decorateCache(Cache cache) {
return new TransactionAwareCache(cache);
}
@Override
public boolean shouldDecorate() {
return this.transactionAware;
}
}
and the AbstractTransactionSupportingCacheManager
becomes:
public abstract class AbstractTransactionSupportingCacheManager extends AbstractCacheManager {
/**
* Constructor that creates CacheManager with transactionAware flag set to false.
*/
public AbstractTransactionSupportingCacheManager() {
this(false);
}
/**
* Constructor that creates CacheManager with the given transactionAware flag.
*
* @param transactionAware Set whether this CacheManager should expose transaction-aware Cache objects.
* <p>Default is "false". Set this to "true" to synchronize cache put/evict
* operations with ongoing Spring-managed transactions, performing the actual cache
* put/evict operation only in the after-commit phase of a successful transaction.
*/
public AbstractTransactionSupportingCacheManager(boolean transactionAware) {
super(new TransactionAwareCacheDecorator(transactionAware));
}
}
Of course JCacheCacheManager
and EhCacheCacheManager
needs new constructors but I think this is much better than relying on setter methods and manually invoking afterPropertiesSet
.
As a result I think with these chages the caching API becomes much more powerful and with absolutely NO changes in spring-boot I am able to fulfil my requirements. I even have a custom actuator endpoint to start/stop the cacheManager at runtime!
2 votes, 1 watchers