I am trying to use #Cachable and #CacheEvict for managing redis cache via spring framework. Can one add #CacheEvict on private methods of the class?
#Cacheable is only evaluated when called between beans, even for public methods. Ie for this:
public class MyBean {
#Cacheable
public String getString(int i) {
return Integer.toString(i);
}
public void myOtherMethod() {
String myString = getString(2);
}
}
the caching will not be triggered.
Therefore, it does not make sense to declare a private method with #Cacheable.
Note that the same is true for Aspects (as suggested in the other solution); those are not triggered when calling intra-class methods either.
Method visibility and #Cacheable/#CachePut/#CacheEvict
When using proxies, you should apply the #Cache* annotations only to methods with public visibility. If you do annotate protected, private or package-visible methods with these annotations, no error is raised, but the annotated method does not exhibit the configured caching settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods as it changes the bytecode itself.
Ref : https://docs.spring.io/spring/docs/3.2.0.RC1/reference/html/cache.html
Related
I understand that Spring AOP is very limited in its abilities (it can only cut into public methods of classes that are Spring beans, and only when those methods are called from outside the class). But now I've discovered another baffling limitation when interfaces are involved.
Normally, if a class is subclassed, Spring AOP has no problem cutting into all of their methods (even overridden ones):
public class A {
public void methodA() { } //OK, can cut in
}
public class B extends A {
#Override
public void methodA() { } //OK, can cut in
public void methodB() { } //OK, can cut in
}
But when we add an interface into the mix, things get pretty bad for Spring AOP:
public interface I {
public void methodA();
}
public class A implements I {
#Override
public void methodA() { } //OK, can cut in
public void methodB() { } //Fail, cannot see or cut into this method
}
public class B extends A {
#Override
public void methodA() { } //Fail, cannot see or cut into this method
public void methodC() { } //Fail, cannot see or cut into this method
}
First of all, Spring AOP can only cut into methods that are in the interface, anything else - it cannot see. Second, it can only cut into the method that directly implements the interface's method - A.methodA(). It cannot cut into the same method overridden by B.
I am using a generic pointcut expression "execution(* method*(..))" to cut into all possible methods, so it's not an expression issue.
Is there any way around this limitation? Or should I just forget about Spring AOP and use a different approach?
UPDATE:
Ok, I have found the real cause of the problem. I was actually relying on Intellij IDEA's AOP plugin to test this. It's supposed to link the pointcut to all affected methods. But it was using the 'old', dynamic JDK proxy strategy instead of the new, CGLIB strategy. So it wasn't linking it to all methods, but when I actually ran my program, it would cut into all methods correctly.
I'm using Spring Boot 2, which uses the 'new' CGLIB strategy. But on SB1 it might still use the 'old' dynamic JDK proxy strategy, so it might still not work there.
Spring will use either dynamic proxy or cglib to implement AOP.
Cglib is picked if there is no interface, then it will effectively create a subclass of the target class, and override all methods in the target class. With this way all methods could be cut in, except final and static ones.
In case the target class is with interface, then Spring might use a dynamic proxy using one of the interface, and apprantly this will only affect the methods declared in the interface.
Before spring-boot 2.0, dynamic proxy is the default strategy. Now Cglib is the default strategy after spring-boot 2.0.
It seems to me spring probably take the dynamic proxy approach in your case. You could add spring.aop.proxy-target-class: true in your application.yaml to force use Cglib.
In case you still have issue, it's better to post more complete code snippet showing how the mothods are invoked.
I have the following scenario:
Spring 3.2
EHCache
A superclass that can not be modified (inside a jar), with a structure similar to this:
public abstract class SuperClass<E extends Object> implements SuperIface<E> {
public void insert(E entity) {
}
}
A subclass, (can be modified), with this structure and a little more complex condition in #CacheEvict
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
#Service
#CacheEvict(value = "entityCache", allEntries = true, condition = "'insert'.equals(#root.methodName)")
public class SubClass extends SuperClass<Entity> implements ISubIface {
public void anotherMethod() {
}
}
Is there any option to say Spring to make effective #CacheEvict annotation when insert method of SuperClass is invoked? In other words, Can I remove cache entries when insert method is invoked from a SubClass instance?
Note that I'm looking for the way to do it via configuration. I know that I can override superclass' methods in subclass, but for some circumstances (another logic in superclass) I prefer not to do that.
You can create cache advices on any class you want using xml configuration if you can't change the source.
Having said that, your example does not make much sense to me: the class is abstract so you actually need an implementation to invoke that method. Are you saying that you have multiple implementations and you want all these implementations to have a CacheEvict behaviour? If you only have one, I don't see the problem of having an override that merely call super + the annotation.
This is the code I have:
#Cacheable(value = "configurationCache", key = "#myFile.lastModified()")
private Object foo(File myFile) throws IOException {
System.out.println(myFile.lastModified());
try {
Thread.sleep(6000);
} catch (InterruptedException ignored) {
}
final Object foo = new SomeObjectFromFile(myFile);
return foo;
}
I call this method twice passing file objects that have the same lastmodified value but caching does not work, the method will wait for 6 seconds.
Here is the output I am getting:
1456298573000
1456298573000
What am I doing wrong?
key = "#myFile.lastModified"
did not work either..
I am sure my configuration with ehcache is fine.
Juliens answer is probably the right one assuming you do not use aspectj. its not alone invoking a public method, but invoking a public method of an object where spring had the chance to wrap it's proxies around. So make sure you are injecting the service that you want to have enhanced with cacheable support.
For example
#Service
public SomeService {
#Autowired
private CacheEnhancedService css;
public void doSomething() {
css.getConfig(new File("./file"));
}
}
#Service
public CacheEnhancedService {
#Cacheable(value = "configurationCache", key = "#myFile.lastModified()")
public Object getConfig(File myFile) {
...
}
}
}
The issue lies with the fact that your method is private.
As mentioned in the documentation of the Spring Framework:
Method visibility and cache annotations
When using proxies, you should apply the cache annotations only to
methods with public visibility. If you do annotate protected, private
or package-visible methods with these annotations, no error is raised,
but the annotated method does not exhibit the configured caching
settings. Consider the use of AspectJ (see below) if you need to
annotate non-public methods as it changes the bytecode itself.
[...]
In proxy mode (which is the default), 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
caching at runtime even if the invoked method is marked with
#Cacheable - considering using the aspectj mode in this case. Also,
the proxy must be fully initialized to provide the expected behaviour
so you should not rely on this feature in your initialization code,
i.e. #PostConstruct.
You should either switch to a public method and make and external call or user AspectJ.
Working with ehcache I noticed that #Cacheable annotation could be used on top of a class decleration or on top of a method decleration, such as;
Cacheable class:
#Cacheable
class CacheableClass{
Long l;
Integer i;
String s;
}
Cacheable method:
class ...
#Cacheable
public List<ToBeCached> getCacheableClassList()
{
...
}
If #Cacheable is on top of a class then you cannot give the name of the chache but if you declare on top of a method you can give the name of the cache declared in the configuration xml. I suppose I miss something since using #Cacheable for class declerations seem obsolute to me.
Using ehcache-spring-annotations :
#com.googlecode.ehcache.annotations.Cacheable(cacheName = "test") if we give this at type level then it gives error saying The annotation #Cacheable is disallowed for this location.
According to documents I ever read Annotation Placement :
On a method.
On an interface or
On a public method on a class
Spring recommends that you only annotate methods of concrete classes with the #Cacheable annotation, as opposed to annotating methods of interfaces.
When using proxies, you should apply the #Cacheable annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the #Cacheable annotation, no error is raised, but the annotated method does not exhibit the configured cachable settings.
Using Spring Cache
If you use #org.springframework.cache.annotation.Cacheable(value="test") where value represents the name of the cache. You can specify this at type &/or method level.
You can try this & tell whether you get error or not :-
#com.googlecode.ehcache.annotations.Cacheable(cacheName = "test")
#org.springframework.cache.annotation.Cacheable(value="")
public class PortalDatabaseAdapterImpl{
#com.googlecode.ehcache.annotations.Cacheable(cacheName="test")
#org.springframework.cache.annotation.Cacheable(value="test")
public List<PageControl> getLoginPage() {}
}
If you didn't get error then I have to update myself.
Using #Cacheable at method level means, the results of the method are cached.
Using #Cacheable at interface level is used to define custom annotations as below,
//Custom annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
#Cacheable(value="books", key="#isbn")
public #interface SlowService {
}
The below code
#Cacheable(value="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
can be replaced with
#SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
But I have never seen applying #Cacheable annotation at Class level.
I have previously used Spring DI, and one of the benefits I perceive is that I can test my Spring bean classes without involving Spring (imports omitted for brevity):
public class Foo {
private String field;
public void setField(String field) { this.field = field; }
public String getField() { return field; }
}
public class TestFoo {
#Test
public void test_field_is_set() {
Foo foo = new Foo();
foo.setField("Bar");
assertEquals("Bar", foo.getField());
}
}
Now I am experimenting with JSR-330, which means not explicitly writing setters.
I'm using Hk2 so far, purely because of some anecdotal stuff about Jersey being tied to Hk2, and making it difficult to co-habit with other JSR-330 implementations.
public class Foo {
#Inject
private String field;
}
I half expected some magic to happen, whereby the #Inject annotation caused a setter to become available, but this is not the case:
Foo foo = new Foo();
foo.setField("Bar"); // method setField(String) is undefined for the type Foo
How can I (conveniently) test this kind of annotated class without invoking a framework?
Failing that, how can I invoke a framework in a portable way (i.e. without tightly coupling my test code to Hk2, Guice, etc.)
Failing that, what's a typical, clean way to test classes annotated in this way?
Simplest is to make the fields package-private (instead of private), then in the test, set them directly. (That works if the test is in the same package)
public class Foo {
#Inject
String field;
}
Foo foo = new Foo();
foo.field = "bar";
This has the advantage of avoiding reflection so it's safe for refactoring.
The field injection approach you mentioned is actually the typical Spring style; many programmers don't write setters for private injected fields at all. Spring (with #Autowired or #Inject) and JSR-330 containers usually inject fields using direct field reflection rather than setters.
Because of this, if you don't want to use any DI framework, you could write the necessary reflection code into your unit tests yourself, but this seems like overkill just to avoid a test dependency; after all, the point of using #Inject is that you're coding to an interface, and you don't avoid using the JVM to avoid coupling to it.
The usual approach for testing this sort of class is to set up a test context for whatever container you prefer and run the unit tests in that context. If you're using Spring, you'd put an applicationContext-test.xml file or TestConfig class in your src/test/ directory (or equivalent), and if you're using Guice, you'd write a module to wire up mocks or test datasets.
It turns out that frameworks relying on private/protected field access are not so uncommon. Hibernate, JPA, several JSR-330 implementations, including Spring itself, all do it.
Spring's spring-test package provides a ReflectionTestUtils class containing static methods for accessing these fields.
Using this one can test the class in the question thus:
import static org.springframework.test.util.ReflectionTestUtils.*;
...
#Test
public void testUsingSpringReflectionTestUtils() {
Foo foo = new Foo();
setField(foo, "field", "Bar");
assertEquals("Bar", foo.getField());
}
You need spring-test and spring-core in your test classpath for this to work, but it doesn't add a dependency on Spring for your production code.
(Comments welcome about alternative implementations of the same principle welcome. I don't think it's worth rolling one's own, however simple it would be, given that Spring has a good implementation.)
Give "needle" a try: http://needle.spree.de/overview
needle is an DI-test-framework that only simulates the container behavior, making unit tests real simple.