I have a method as following:
#Cacheable(value = "SAMPLE")
public List<SomeObj> find() {
// Method that initiates and returns the List<SomeObj> and takes around 2-3 seconds, does some logging too
}
And I am enabling caching in one of my configuration classes:
#EnableCaching
#Configuration
public SomeConf extends CachingConfigurerSupport {
// Here I also initialize my classes with #Cacheable annotation
#Bean
#Override
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Collections.singletonList((new ConcurrentMapCache("SAMPLE"))));
return cacheManager;
}
#Bean
#Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager());
}
#Bean
#Override
public KeyGenerator keyGenerator() {
return new SimpleKeyGenerator();
}
}
I have the following in my pom.xml:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>1.5.14.RELEASE</version>
</dependency>
I am declaring a CacheManager as follows:
#Bean
public CacheManager cacheManager(){
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Collections.singletonList((new ConcurrentMapCache("SAMPLE"))));
return cacheManager;
}
When I get an #Autowired CacheManager instance into one of my #Services I can see that there exists a cache with name "SAMPLE", but its entries are always empty. I call again and again the method find(), but it doesn't seem to populate the cache.
I have tried to put an argument (say int a) to the find() method and put it as key = "#a" to #Cacheable, but nothing changed.
When I try to recreate the issue in an isolated environment, I can see that it works properly. But when I add my dependencies (non open-source company libraries, which include an EhCache configuration as well) it doesn't work. How can I debug this, what am I doing wrong?
Update:
I have also tried to use cacheManager = myCacheManager in #Cacheable as well. No luck.
Update 2:
I am using AspectJ and Spring AOP. I think that it may have something to do with it. I have tried #EnableCaching(mode = AdviceMode.ASPECTJ) with #EnableLoadTimeWeaving but same thing.
Update 3:
I was finally able to reproduce the issue, here it is: client-api-cache
Basically when you run the application and telnet localhost 9000 after you send any line to it, it should print NOT CACHED once even though the method is called twice in CachedController (Second coming from the cache). But it prints twice.
The root cause is that you are misusing "afterPropertiesSet". So, what you are doing is endless loop and never pass control back to the Spring pipeline, so Spring isn't able to init caching facility properly.
Check out code which fixes our problem: https://dumpz.org/cbx8h28KeAss
As Andrews S noted, this does sound like colliding caches in the auto configuration.
#EnableCaching's javadoc has some useful notes regarding how the cacheManager is selected and, specifically, an idea on how to proceed. What you'd do in this case is establish a CachingConfigurer to select your cache - perhaps you can just extend CachingConfigurerSupport (as in example below) and be done.
A bean of type CacheManager must be registered, as there is no reasonable default that the framework can use as a convention. And whereas the <cache:annotation-driven> element assumes a bean named "cacheManager", #EnableCaching searches for a cache manager bean by type. Therefore, naming of the cache manager bean method is not significant.
For those that wish to establish a more direct relationship between #EnableCaching and the exact cache manager bean to be used, the CachingConfigurer callback interface may be implemented. Notice the #Override-annotated methods below:
#Configuration
#EnableCaching
public class AppConfig extends CachingConfigurerSupport {
#Bean
public MyService myService() {
// configure and return a class having #Cacheable methods
return new MyService();
}
#Bean
#Override
public CacheManager cacheManager() {
// configure and return an implementation of Spring's CacheManager SPI
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
return cacheManager;
}
#Bean
#Override
public KeyGenerator keyGenerator() {
// configure and return an implementation of Spring's KeyGenerator SPI
return new MyKeyGenerator();
}
}
This approach may be desirable simply because it is more explicit, or it may be necessary in order to distinguish between two CacheManager beans present in the same container.
Notice also the keyGenerator method in the example above. This allows for customizing the strategy for cache key generation, per Spring's KeyGenerator SPI. Normally, #EnableCaching will configure Spring's SimpleKeyGenerator for this purpose, but when implementing CachingConfigurer, a key generator must be provided explicitly. Return null or new SimpleKeyGenerator() from this method if no customization is necessary.
CachingConfigurer offers additional customization options: it is recommended to extend from CachingConfigurerSupport that provides a default implementation for all methods which can be useful if you do not need to customize everything. See CachingConfigurer Javadoc for further details.
You may also find this thread useful: How to have multiple cache manager configuration in spring cache java
Edit:
So thankfully I have a project using caching and wrote this little bit:
#Bean
#Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager()) {
#Override
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
Collection<String> toReturn = super.getCacheNames(context);
toReturn.forEach(System.out::println);
return toReturn;
}
#Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
System.out.println(Arrays.toString(context.getArgs()));
System.out.println(context.getClass());
System.out.println(context.getMethod());
System.out.println(context.getOperation());
return super.resolveCaches(context);
}
};
}
Aside from seeing my established cache names pop out, I do note that context is outputting:
[]
class org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext
public abstract ...transferobjects.CacheContainer ...service.LookupService.getCacheSelections()
Builder[public ...transferobjects.CacheContainer ...dao.LookupFacade.getCacheSelections()] caches=[cacheselections] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='true'
The context.getClass() output is of interest, given your Aspect question. That said I have a very similar logging/timing aspect in my own code that isn't causing any confusion for the rest of the caching. Try my resolver out and see if the output is instructive on what calls are being made against the cache code.
Edit #2:
The TLDR issue appears to be that we're expecting #Cacheable to work in places where it can't - mainly due to the fact that the framework hasn't fully brought itself up yet. You are using InitializingBean and I tried replacing that functionality with #PostConstruct which both fail.
Spring cache using #Cacheable during #PostConstruct does not work
I got your github code. I initially ran into the blocking issue you reported in the other thread but, in the interest of pursuing one thing at a time, I just commented out that blocking code and called service.cachedMethod() directly.
I ran your jar file with --trace and noted that the cacheManager was scanned and created, but it wasn't initially obvious to me when it was being invoked. So, no big deal, I just had the bean method throw a RuntimeException. Upon doing that I noted that both your NOT CACHED lines print prior to that invocation - another hint that things aren't set up as expected.
Finally, I added System.out.println(service.toString()); in your Controller's afterPropertiesSet() method and saw com.company.client.api.domain.CachedServiceImpl#2bbfb8b - your object, not a proxied object. Thus no caching.
So, in general, you'll need to rework how you're trying to enable this functionality.
Make sure you are setting your cache into cachemanager as below.
#EnableCaching
#Configuration
public SomeConf {
#Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("find():List<SomeObj>")));
return cacheManager;
}
}
Related
I have two micro services and I need one of them to read the cache, but never update it.
I tried to put
#Cacheable(cacheNames = "test-stack", key=..., unless="true")
and
#Cacheable(cacheNames = "test-stack", key=..., condition="false")
but it didn't work.
How can I achieve this?
By default, NO.
There is/are no annotation(s) in Spring, or even JCache's API (more generally speaking, here, or specifically here) that enables read-only caching behavior.
Fortunately, not all is lost in this case. One of the many benefits of Spring is the ability to extend the current APIs to customize or alter the behavior, for example, caching.
In Spring's Cache Abstraction, the 2 primary interfaces of the SPI are the Cache and CacheManager interfaces in the org.springframework.cache package (Javadoc).
By way of example, and to present 1 possible solution to this problem (off the top of my head) let's assume you are using Redis for caching. As you no doubt already know, without Spring Boot, your application configuration to enable caching would look something like this:
#Conifguration
#EnableCaching
class CachingConfiguration {
#Bean
RedisCacheManager cacheManager() {
// ...
]
}
Here, you would need to 1) EnableCaching and 2) declare a CacheManager bean (named "cacheManager") for the caching provider you want to use. Again, in this example we are using Redis.
Now, we are going to introduce a 2nd configuration:
#Configuration
#Profile("read-only-microservice")
class ReadOnlyCachingConfiguration {
#Bean
BeanPostProcessor readOnlyCachingBeanPostProcessor() {
return new BeanPostProcessor() {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean instanceof CacheManager
? new ReadOnlyCacheManager((CacheManager) bean)
: bean;
}
}
}
}
NOTE: If your application is using multiple caching providers (e.g. Redis and Hazelcast), then the instanceof check may need to be altered here depending on your requirements.
Notice 2 things here.
First, notice the use of the Spring #Profile annotation. If your microservice is essentially the same application codebase deployed twice with slight configuration differences, then the first microservice can be configured to write to the cache when this profile is disabled (or rather not enabled). However, in the second deployment, you can enable this profile (e.g. as a JVM System property -Dspring.profiles.active=read-only-microservice) to disable the microservice from being able to write to the cache.
But, how do you prevent the second deployment of the microservice from writing to the cache?
Well, the second thing to notice here is the "wrapping" (decorating) of the CacheManager bean (for example, the RedisCacheManager), with the ReadOnlyCacheManager.
The ReadOnlyCacheManager still implements the CacheManager interface, but it essentially wraps and decorates an existing CacheManager, such as the RedisCacheManager.
The sole purpose of the CacheManager is to find/lookup and serve Cache objects by "name" when caching operations, such as #Cacheable annotated application service methods are called.
So, our implementation would be:
class ReadOnlyCacheManager {
private final CacheManager source;
ReadOnlyCacheManager(#NonNull CacheManager cacheManager) {
Assert.notNull(cacheManager, "CacheManager is required");
this.source = cacheManager;
}
#Override
public Cache getCache(String name) {
Cache cache = this.source.getCache(name);
return cache != null
? new ReadOnlyCache(cache);
: null;
}
#Override
public Collection<String> getCacheNames() {
return this.source.getCacheNames();
}
}
Pretty straight forward.
Now, you only need to implement the ReadOnlyCache implementation that 1) implements the Spring Cache interface, 2) wraps the existing Cache implementation (e.g. RedisCache) and then 3) delegates to the source Cache object for the cache operations, except for the write operations (e.g. put(key, value)).
For example (partial implementation):
class ReadOnlyCache {
private final Cache source;
ReadOnlyCache(#NonNull Cache cache) {
Assert.notNull(cache, "Cache is required");
this.source = cache;
}
#Override
public <T> T get(Object key, Class<T> type) {
return this.source.get(key, type);
}
#Override
public void put(Object key, Object value) {
// Do Nothing; the Cache is read-only!
}
}
You would override each cache operation appropriately.
You could possible solve this problem other ways as well, such as by injecting your own AOP Aspect into the caching infrastructure. However, you must be careful to order the Aspects correctly.
I prefer the approach I demonstrated above because 1) AOP is a form of "decoration" (or the Decorator pattern) and 2) this approach is very reliable and will even work with Spring Boot's auto-configuration of caching. With Spring Boot, you do not need to explicitly declare a CacheManager bean when you have a caching provider, such as Redis, or 1 of the other supported caching providers on the classpath and you have enabled caching (i.e. #EnableCaching).
If your microservice applications are different (e.g. 2 separate codebases) then you might not need the Profile I demonstrated above.
Anyway, I hope this gives you more ideas on how you can customize the framework to your needs.
I did not explicitly write a test or create an example this time (in my repository, and specifically here) since I felt this was pretty straight forward. However, I have demonstrated this approach before in other cases. You might find this repository insightful, especially here. The AbstractCacheDelegate and AbstractCacheManagerDelegate help in the process of implementing Spring's Cache and CacheManager interfaces.
Good luck!
I'm trying to save a step-related state, that would be accessible from processor. For this purpose I made a class and a bean for it. My configuration file looks like this:
#Slf4j
#Configuration
#EnableScheduling
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class MyConfiguration
{
// Job, reader and writer beans
#Bean("myStep")
Step myStep(#Qualifier("myReader") ItemReader<InputEntity> reader,
#Qualifier("myProcessor") ItemProcessor<InputEntity, OutputEntity> processor,
#Qualifier("myWriter") ItemWriter<OutputEntity> writer)
{
return stepBuilderFactory
.get("myStep")
.<InputEntity, OutputEntity> chunk(100)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
#StepScope
#Bean("myProcessor")
public MyProcessor processingStep(StateService s)
{
var processor = new MyProcessor();
processor.setStateService(s);
return processor;
}
#Scope(value = "step", proxyMode = ScopedProxyMode.NO)
#Bean
public StateService stateService()
{
return new StateService();
}
}
Idea behind is to create a state service for each new step execution (the class is empty at the moment and doesn't have #Component annotation). However, I get in trouble with Spring proxies:
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'MyProcessor' is expected to be of type 'very.long.package.name.steps.MyProcessor' but was actually of type 'com.sun.proxy.$Proxy265'
Gathering already answered questions and dozens of guides I tried following:
All possible proxy modes of stateService bean;
Injecting this bean directly into MyProcessor via #Autowired variable
Annotating configuration with #EnableBatchProcessing
Calling stateService() bean directly: processor.setStateService(stateService());
Injecting bean into step Step bean. In this case I have to change the method signature, so the method accepts MyProcessor instead of ItemProcessor<InputEntity, OutputEntity> to expose the variable
Nothing helped, I still get this exception. What am I misunderstanding in concept of #StepScope? How can I store some state for particular step execution?
I read this, this and even this, but neither helped me to understand it.
What am I misunderstanding in concept of #StepScope?
The step scope is designed for beans that should be lazily instantiated at runtime when the step is executed. By default, for interface based beans, Spring will create JDK dynamic proxies, and for classes it will use CGLib.
In the code you shared, you used proxyMode = ScopedProxyMode.NO, so you have explicitly told Spring to not create a proxy for that bean, hence your error. And that's why when you change it to proxyMode = ScopedProxyMode.INTERFACES it works.
How can I store some state for particular step execution?
The execution context is designed for this very use case (among other use cases). So you can store that state in the ExecutionContext of the step and retrieve afterwards in the same job or when restarting a failed job.
Well, adding an interface helps in this case:
#Scope(value = "step", proxyMode = ScopedProxyMode.INTERFACES)
#Bean
public IStateService stateService()
{
return new StateService();
}
I also had to add new interface IStateService, which I actually don't need. However, it still doesn't answer the question WHY, and doesn't bring any understanding either.
What I want to achieve
My objective is to have a transactional bean, instantiated twice (or more), and each time w/ a different TransactionManager (i.e. a different DB).
#Transactional
class MyReusableService {
...
}
#Configuration
class MyConfig {
// here I want to use `transactionManagerForDb1`
#Bean
public MyReusableService myReusableServiceForDb1() { ... }
// here I want to use `transactionManagerForDb2`
#Bean
public MyReusableService myReusableServiceForDb2() { ... }
}
What I observed
TransactionInterceptor is the piece that delegates to a TransactionManager (TM). I found it used in 2 ways:
Via #EnableJpaRepositories(... transactionManagerRef="..."). This mechanism relates a bit w/ what I want. But it doesn't use #Transactional at all. It finds all the repos. And it instantiates one TransactionInterceptor per repo.
Via #Transactional. Here, there is a single instance of TransactionInterceptor (TI). And at each invocation, it decides which TM to use by looking at the annotations (using caching of course). This TI is defined ProxyTransactionManagementConfiguration.
What I did
I defined my own TI:
#Configuration
public class MyConfiguration {
#SuppressWarnings("serial")
public static class MyTransactionInterceptor extends TransactionInterceptor {
...
}
#Bean
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new MyTransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
return interceptor;
}
}
This seem to work. The TI defined by Spring is no more instantiated. Mine is. And it is used.
My questions
Why does the above work? How did Spring know that I wanted to override it's TI? I was expecting to have 2 TIs, and fight to use the right one, maybe using #Primary. E.g. like here.
Is there maybe a cleaner way to achieve my objective? I cannot say I'm entirely satisfied, because I find the solution a bit intrusive. MyReusableService resides actually in a lib. And forcing the user to override the TI is not self contained.
I am using Redis Cache in my Spring Boot application to store data of multiple rest API's.
I am clearing Redis cache on a regular interval using Spring Cron Jobs. The method is getting called at required time-slots.
I have verified the logs but the cache is not getting clear and hence it is showing the stale data.
The code where I'm trying to clear the cache.
public class CustomerDerivation {
#Autowired
#Qualifier("redisCacheMngr")
CacheManager redisCacheMngr;
#Scheduled(cron = "${redis.api.update.interval}")
#CacheEvict(value = "redis-cache", allEntries = true, cacheNames = {"redis-cache"})
protected void cacheEvict() {
redisCacheMngr.getCache("redis-cache").clear();
logger.info("Evicting ModelCache");
}
}
Custom cache configuration code.
#Configuration
#Profile("cloud")
public class CacheConfig extends AbstractCloudConfig {
#Autowired
Environment env;
#Bean
public RedisConnectionFactory brRedisFactory() {
return connectionFactory().redisConnectionFactory(env.getProperty("model_cache_name"));
}
#Bean
public RedisTemplate<String, Object> brRedisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(brRedisFactory());
return redisTemplate;
}
#Bean(name = "redisCacheMngr")
public CacheManager cacheManager() {
RedisCacheManager cacheManager = new RedisCacheManager(brRedisTemplate());
cacheManager.setUsePrefix(true);
cacheManager.setTransactionAware(true);
return cacheManager;
}
}
How to fix the code to clear the redis cache ?
1) Have you enabled the cache using #EnableCaching annotation?
2) why are you using #CacheEvict and clear() in the same method? Both serves the same purpose. Use either one of them.
Check the following:
App is started with the according profile (ie. "cloud")
#EnabledCaching on your configuration
Avoid to mix #CacheEvict and the CacheManager bean, you have to choose one way for eviction
Extract the #Scheduled method in another "CronJobs" bean to avoid multiple annotations and AOP inside class issues.
Regards.
Spring use Spring AOP (Aspect Oriented Programming) to implement caching which means you must use public access level on your cacheEvict() method so it can be intercepted by AOP proxy. Otherwise it is like you never annotate your method with #CacheEvict
We are using spring boot frame work and choose SimpleCacheManager to be the cache manager, scanned some references, there are also CompositeCacheManager and NoOpCacheManager, I thought that NoOpCacheManager was designed to make tests convenient, but what is the scene to use CompositeCacheManager and what is it different from SimpleCacheManager?
In the next part, I'll show some codes to explain how we use SimpleCacheManager, if there is better strategy to use SimpleCacheManager?
To make the frame work, we need to add #EnableCaching at the entrance and add #Cacheable of the aim service method, cache manager implemented at the entrance.
entrance:
#Configuration
#EnableCaching
public class Application {
SimpleCacheManager cacheManager = new SimpleCacheManager();
GuavaCache cache = new GuavaCache("ivan_cache",
CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build());
cacheManager.setCaches(ImmutableList.of(cache));
return cacheManager();
}
Service module:
#Cacheable(value = { "ivan_cache" })
public void cacheTest() {
log.info("Cache not effects.");
}