Spring Cache @Cacheable - Not Working While Calling from Another Method of the Same Bean

Spring Cache @Cacheable - not working while calling from another method of the same bean

I believe this is how it works. From what I remember reading, there is a proxy class generated that intercepts all requests and responds with the cached value, but 'internal' calls within the same class will not get the cached value.

From https://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheable

Only external method calls coming in through the proxy are
intercepted. This means that self-invocation, in effect, a method
within the target object calling another method of the target object,
will not lead to an actual cache interception at runtime even if the
invoked method is marked with @Cacheable.

Spring's @Cacheable not working when called from the controller in Spring Boot

Like Andrew said, the right question should be "when called in the same class".

But as the answers to the suggested post are really outdated and there are better approaches that are useful with newer versions of Spring, i would like to share what I think is the best approach:

  • Autowire the controller and use to call the method it instead of using the class context this.

The updated code would look like:

@Controller
public class TestController {

@Autowired TestController self;

@RequestMapping("/test")
public String testView(){
self.expensiveMethod();
return "test";
}

@Cacheable("ones")
public void expensiveMethod(){
System.out.println("Cache is now being used");
}

}

Spring cache @Cacheable method ignored when called from within the same class

This is because of the way proxies are created for handling caching, transaction related functionality in Spring. This is a very good reference of how Spring handles it - Transactions, Caching and AOP: understanding proxy usage in Spring

In short, a self call bypasses the dynamic proxy and any cross cutting concern like caching, transaction etc which is part of the dynamic proxies logic is also bypassed.

The fix is to use AspectJ compile time or load time weaving.

Spring Boot @Cacheable stopped working after I moved method to the same class

When having a look into Cacheable source, doc says:

Annotation indicating that the result of invoking a method (or all methods in a class) 
can be cached.
Each time an advised method is invoked, caching behavior will be applied, checking
whether the method has been already invoked for the given arguments.

The most relevant part of this snippet for this question, is advised method. In order to make AOP work, you'll need to organize the code as you did before.

Explanation of why private methods cannot be used with AOP:

Because private methods are not inherited by subclasses, i.e. there is no way to 
intercept a private method and then delegate to it because the subclass cannot even
call that method. This is a normal Java limitation and has nothing to do with AOP specifically.

https://stackoverflow.com/a/59961404/14072498

The link provided by Pablo explains why calling method cannot reside in same class as method annotated with Cacheable.

Spring @Cacheable not caching

Imagine you go to the Zoo. You go through the entrance once and pay your entry. Afterwards you can visit the Lions, the Tigers, and so on... You don't have to pay everytime because you did it when you entered. If you get bored and want to go to another Zoo, you have to go out, go to the next one, and pay again.

Your Class is the Zoo, your Methods are the Animals, and the Cache Proxy is the Entrance. When someone calls your class, it goes through the Cache once. When she is in, and calls another methods of the same class, it doesn't go through the Cache again. Only when you go out and in again, you go through the Cache.

There is a nasty trick you can use to override this called inject yourself:

public class YourClass {
@Autowired
private YourClass instance;

@Cacheable
public String method1() {
// now you go through the cache again
return instance.method2();
}

@Cacheable
public String method2() {
return "2";
}
}

@Cacheable not working, still calling the caching method

I think your @Cacheable annotation is not working because you have not specified Interface for class. This is because of proxy created for caching by Spring. Spring has specified below in its documentation . I thing you have not specified proxy-target-class, it means it will be default to false. If it is false it will use JDK interface based proxies. But in your case you class i.e. RollMappingService is not implementing interface. Create interface RollMappingService with method getAllRoles and implement it, will sole your problem.

Controls what type of caching proxies are created for classes annotated with the @Cacheable or @CacheEvict annotations. If the proxy-target-class attribute is set to true, then class-based proxies are created. If proxy-target-class is false or if the attribute is omitted, then standard JDK interface-based proxies are created. (See Section 9.6, “Proxying mechanisms” for a detailed examination of the different proxy types.)

Also modify your test class to create Spring bean for RoleMappingService in following ways and inject mock of AdminClient into it

 @Mock
private AdminClient mockedAdminClient;

@InjectMocks
@Autowired
private RoleMappingService roleMappingService

@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(roleMappingService,
"adminClient",
mockedAdminClient);
}


Related Topics



Leave a reply



Submit