Spring

@Cachable usage and gotchas

Recently I was working with a colleague to implement caching in an application. Initially it looks fairly straight forward to integrate it, however, because of the way the code was being called we came across a number of problems that I want to share.

To see the code examples of where @Cachable works and where it doesn’t work clone the code from https://github.com/marcthomas2013/ehcache

Dependencies required

This example code is based on Spring Boot 1.2.5, however, when using Spring Boot and ehcache prior to Spring Boot 1.3 you will need to pull in the following dependency to be able to get the ehcacheCacheManager. I struggled to find the dependency that I needed for org.springframework.cache.ehcache.EhCacheCacheManager so, here it is.

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
   <version>4.2.0.RELEASE</version>
</dependency>

Cachable annotation

Once you have the ehcacheCacheManager set up and an ehcache.xml config file in your resources folder then you are ready to start adding the @Cachable annotation to your public methods.

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
         monitoring="autodetect" dynamicConfig="true">

    <!-- <diskStore path="java.io.tmpdir" /> -->
    <diskStore path="~/cache" />

    <cache name="demoCache"
           maxEntriesLocalHeap="10000"
           maxEntriesLocalDisk="1000"
           eternal="false"
           diskSpoolBufferSizeMB="20"
           timeToIdleSeconds="300" timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap" />
    </cache>
</ehcache>

In the example code we have a cache setup in ehcache.xml with the name “demoCache” this name needs to go into the value parameter. Then the key that you want to use for the cache is set in the “key” field. This key field can be defined as something more than just a parameter name, see Spring’s documentation for more detail.

@Cacheable(value="demoCache", key="#url")
public String getURLResponse(String url) {
    System.out.println("This should only happen once!");
    return "Success";
}

 

Different ways of invoking the cache method

Some ways work and some don’t. In the example code referred to above there is a class MyController that invokes all the cache calls.

In the MyService class the getURLResponse method contains a log line that says “This should only happen once!” this is to show when the method is invoked. If the caching is working then you should only ever see this line once because the cache should be returning the value, however, there are a number of demonstration method calls that show when this doesn’t work and this article explains why.

callServiceDirect explanation

The first call callServiceDirect in MyController is probably the most likely scenario. A controller calling an autowired service method. You’ll see in the log that the target method is called once, and then the response is cached. The second call return value is returned straight from the cache.

callFromObjectWithDependencyPassedIn explanation

The next call callFromObjectWithDependencyPassedIn shows the scenario if you have another class that needs to invoke the service method and you need to pass that dependency in. This approach works and is the way you should do it, but then we’ll compare it with the next method callFromObjectCreatedByTheService that doesn’t work.

callFromObjectCreatedByTheService explanation

There is a method called callFromObjectCreatedByTheService that is very quirky but is used to highlight a Spring Aspect Oriented Programming (AOP) proxy issue that people could get caught out with. When the code is run you will notice that the log shows that the methods @Cachable annotation isn’t invoked. To explain why this is we need to look into the createObjectThatCallsService method in MyService.

What this method is doing is creating another object and passing itself into the object as a form of dependency injection. You might think that this is fine however, it doesn’t work and it is because Spring needs to Proxy the objects that it manages. The Spring Documentation describes this in detail but essentially proxying means that outside of the MyService class references to the object are in fact a Spring Proxy to the MyService object, whereas when you are running code within MyService the object’s this will be the MyService and not the Spring Proxy.

For AOP based annotations like @Cachable to work they need to be invoked using the proxied object and not the directly the object where the method exists. Effectively under the hood when a method with @Cachable is called using a proxied object Spring can then invoke any annotation based calls prior to the actual method being invoked. This is not possible if you are calling MyService direct. This is why this approach doesn’t work.

callingPrivateCacheMethodFromWithinService explanation

callingPrivateCacheMethodFromWithinService is another example of a call to an @Cachable method that doesn’t work and is for the same reason as explained above but for a different use case. The Spring Documentation on caching annotations explains on a tip box on the right with the title “Method visibility and @Cacheable/@CachePut/@CacheEvict” that this won’t work because of a limitation of Spring AOP and it suggests if you want to be able to call private methods internally with AOP based annotations then you will need to setup AspectJ instead. Sadly, I haven’t been able to find a good tutorial on setting up AspectJ instead of Spring AOP.

callingCacheMethodFromWithinService explanation

callingCacheMethodFromWithinService is another example of a call to an @Cachable method that doesn’t work and is for the same reason as explained above but for a different use case. You will notice that this is the is calling the same public method that works for all calls that are external to MyService and as long as they are invoked using the Spring Proxied object they will work, however, this example is calling the @Cachable method inside the MyService which therefore won’t call the method via the proxy object.

How to create a Spring Boot Remote Shell Command

So today I’ve been looking at the actuator and remote functionality in Spring Boot and thought I’d have a go and getting a custom command working in my simple Spring Boot app.

One challenge from the documentation is it only talks about how to setup the command using Groovy, so what if you are using Java? There are a few pitfalls that catch people out so I thought I’d document them here in one article.

So, the documentation on the remote shell does say that the command will be looked for on the class path at /commands or /crash/commands the subtle point about this is the command should be put in your project so that they are built because the remote shell command are compiled on first use so they need to be in your /resources folder.

So, you should put the source code for any remote shell commands in /src/main/resources/commands/ the downside with this is now that it is in the resources folder the IDE won’t highlight any syntax issues. My tip would be to write the command in your java source tree and then when you are happy with it, drag it into the resources folder. One reason for this is any compilation issues will be ignored when invoking the command.

Dependency required:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-remote-shell</artifactId>
</dependency>

Two annotations you’ll need on your command are:

@Command

@Usage(“My Test Command”)

These two annotations indicate that this is a shell command and that the command description is “My Test Command”.

The name of your class becomes the command name.

See my example source code below, that was in my Spring Boot Project at /src/main/resources/commands/

 

package commands;
import org.crsh.cli.Command;
import org.crsh.cli.Usage;
import org.crsh.command.BaseCommand;
public class mycommand extends BaseCommand {
    @Command
    @Usage("My Test Command")
    public String main() {
        return "test command invoked";
    }
}

Spring Boot NoSuchBeanDefinitionException

At the moment I’m in the process of getting to grips with Spring 4, and Spring Boot. I had created a Service PeopleService class that implements an interface IPeopleService. However, when I added the following code to my main method in my Spring Boot application


@Bean
public CommandLineRunner init(
	final PeopleService peopleService) {

	return new CommandLineRunner() {
		@Override
		public void run(String... strings) throws Exception {
			peopleService.createTestData();
		}
	};
}

I get the following error:


Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [rota.services.impl.PeopleService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

The reason for this is Spring Autowires to the Interface and not the implementation. So, what I needed to do was use the IPeopleService in my init() method instead.