If you have a few years of experience in the Java ecosystem, and you're interested in sharing that experience with the community (and getting paid for your work of course), have a look at the "Write for Us" page. Cheers. Eugen

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. The Cache Abstraction?

In this article, we’re going to show how to use the Caching Abstraction in Spring – and generally, improve the performance of your system.

We’ll enable simple caching for some real-world method examples and we’ll discuss how we can practically improve the performance of these calls through smart cache management.

2. Enable Caching

To enable caching, Spring makes good use of annotations, much like enabling any other configuration level feature in the framework.

The caching feature can be declaratively enabled by simply adding the @EnableCaching annotation to any of the configuration classes:

@Configuration
@EnableCaching
public class CachingConfig {

    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("addresses");
    }
}

You can, of course, enable cache management with XML configuration as well:

<beans>
    <cache:annotation-driven />

    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean 
                  class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" 
                  name="addresses"/>
            </set>
        </property>
    </bean>
</beans>

Note: After we enable caching – for the minimal setup – we must register a cacheManager.

Using XML does enable more flexible options to configure caching – you can specify your own Cache-Manager, Cache-Resolver, Error-Handler and generally use more advanced customization options (Refer the Javadoc for more details)

3. Use Caching With Annotations

Once you’ve enabled caching, the next step is to bind the caching behavior to the methods with declarative annotations.

3.1. @Cacheable

The simplest way to enable caching behavior for a method is to demarcate it with @Cacheable and parameterize it with the name of the cache where the results would be stored:

@Cacheable("addresses")
public String getAddress(Customer customer) {...}

The getAddress() call will first check the cache addresses before actually invoking the method and then caching the result.

While in most cases, one cache is enough, the Spring framework also supports multiple caches to be passed as parameters:

@Cacheable({"addresses", "directory"})
public String getAddress(Customer customer) {...}

In this case, if any of the caches contains the required result, the result is returned and the method is not invoked.

3.2. @CacheEvict

Now, what would be the problem with making all methods @Cacheable?

The problem is size – we don’t want to populate the cache with values that we don’t need often. Caches can grow quite large, quite fast, and we could be holding on to a lot of stale or unused data.

The @CacheEvict annotation is used to indicate the removal of one or more/all values – so that fresh values can be loaded into the cache again:

@CacheEvict(value="addresses", allEntries=true)
public String getAddress(Customer customer) {...}

Here, we’re using the additional parameter allEntries in conjunction with the cache to be emptied – to clear all the entries in the cache addresses and prepare it for new data.

3.3. @CachePut

While @CacheEvict reduces the overhead of looking up entries in a large cache by removing stale and unused entries, ideally, you want to avoid evicting too much data out of the cache.

Instead, you’d want to selectively and intelligently update the entries whenever they’re altered.

With the @CachePut annotation, you can update the content of the cache without interfering the method execution. That is, the method would always be executed and the result cached.

@CachePut(value="addresses")
public String getAddress(Customer customer) {...}

The difference between @Cacheable and @CachePut is that @Cacheable will skip running the method, whereas @CachePut will actually run the method and then put its results in the cache.

3.4. @Caching

What if you want to use multiple annotations of the same type for caching a method. Look at the incorrect example below:

@CacheEvict("addresses")
@CacheEvict(value="directory", key=customer.name)
public String getAddress(Customer customer) {...}

The above code would fail to compile since Java does not allow multiple annotations of the same type to be declared for a given method.

The workaround to the above issue would be:

@Caching(evict = { 
  @CacheEvict("addresses"), 
  @CacheEvict(value="directory", key="#customer.name") })
public String getAddress(Customer customer) {...}

As shown in the code snippet above, you can group multiple caching annotations with @Caching, and use it to implement your own customized caching logic.

3.5 @CacheConfig

With the @CacheConfig annotation, you can streamline some of the cache configuration into a single place – at the class level – so that you don’t have to declare things multiple times:

@CacheConfig(cacheNames={"addresses"})
public class CustomerDataService {

    @Cacheable
    public String getAddress(Customer customer) {...}

4. Conditional Caching

Sometimes, caching might not work well for a method in all situations.

For example – reusing our example from the @CachePut annotation – this will both execute the method as well as cache the results each and every time:

@CachePut(value="addresses")
public String getAddress(Customer customer) {...}

4.1 Condition Parameter

Now – if we want more control over when the annotation is active – @CachePut can be parametrized with a condition parameter that takes a SpEL expression to ensure that the results are cached based on evaluating that expression:

@CachePut(value="addresses", condition="#customer.name=='Tom'")
public String getAddress(Customer customer) {...}

4.2 Unless Parameter

We can also control the caching based on the output of the method rather than the input – via the unless parameter:

@CachePut(value="addresses", unless="#result.length()<64")
public String getAddress(Customer customer) {...}

The above annotation would cache addresses unless they are shorter than 64 characters.

It’s important to know that the condition and unless parameters can be used in conjunction with all the caching annotations.

This kind of conditional caching can prove quite useful for managing large results and customizing behavior based on input parameters instead of enforcing a generic behavior to all operations.

5. Declarative XML-based Caching

In case you don’t have access to your application’s source code or want to inject the caching behavior externally, you can also use declarative XML- based caching.

Here is our XML configuration:

<!-- the service that you wish to make cacheable -->
<bean id="customerDataService" 
  class="com.your.app.namespace.service.CustomerDataService"/>

<bean id="cacheManager" 
  class="org.springframework.cache.support.SimpleCacheManager"> 
    <property name="caches"> 
        <set> 
            <bean 
              class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" 
              name="directory"/> 
            <bean 
              class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" 
              name="addresses"/> 
        </set> 
    </property> 
</bean>
<!-- define caching behavior -->
<cache:advice id="cachingBehavior" cache-manager="cacheManager">
    <cache:caching cache="addresses">
        <cache:cacheable method="getAddress" key="#customer.name"/>
    </cache:caching>
</cache:advice>

<!-- apply the behavior to all the implementations of CustomerDataService interface->
<aop:config>
    <aop:advisor advice-ref="cachingBehavior"
      pointcut="execution(* com.your.app.namespace.service.CustomerDataService.*(..))"/>
</aop:config>

6. The Java-based Caching

And here is the equivalent Java Configuration:

@Configuration
@EnableCaching
public class CachingConfig {
 
    @Bean
    public CustomerDataService customerDataService() {
        return new CustomerDataService();
    }

    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(Arrays.asList(
          new ConcurrentMapCache("directory"), 
          new ConcurrentMapCache("addresses")));
        return cacheManager;
    }
}

And here is our CustomerDataService:

@Component
public class CustomerDataService {
 
    @Cacheable(value = "addresses", key = "#customer.name")
    public String getAddress(Customer customer) {
        return customer.getAddress();
    }
}

7. Summary

In this article, we discussed the basics of Caching in Spring and how to make good use of that abstraction with annotations.

The full implementation of this article can be found in the GitHub project – this is an Eclipse based project, so it should be easy to import and run as it is.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS

newest oldest most voted
Notify of
Avinash Ga
Guest
Avinash Ga

Hi,

I want to place @Cachable annotation on abstract class and @CacheConfig at the subclass level so that the cache names are different for the different subclasses. But i am getting no cache can be resolved exception. How can i do this?

Regards,

Avinash

Eugen Paraschiv
Guest

Hey Avinash – that’s an interesting usecase. The best way to debug through something like that is for you to post a StackOverflow question with an actual working example – and then follow up with me with the link to the question here or over email. I’ll have a look at that and figure out what’s happening. Cheers,
Eugen.

Avinash Ga
Guest
Avinash Ga

Thanks Eugen. I will do that soon.

Regards,

Avinash

Matthias Dietl
Guest
Matthias Dietl

Hey Eugen – I’ve got the exact same problem as Avinash. Could you find a solution in the meanwhile?
As far as I can see, Spring parses the abstract class in search of cacheable methods, finds them and then fails to find a cachename in the abstract class (because the cache config annotation is in a subclass). Cheers, Matthias

Eugen Paraschiv
Guest

Hey Matthias – that may be by design, but it’s going to be much simpler to look at it on an actual usecase. So, as I suggested earlier – a working example is the way to go. And the reason I suggested StackOverflow is that they have clear guidelines to what makes a simple, working example.

Matthias Dietl
Guest
Matthias Dietl

Hey Eugen, I’ve posted the question on Stack Overflow:

http://stackoverflow.com/questions/36977643/spring-cache-not-working-for-abstract-classes

Maybe you have an idea?

Eugen Paraschiv
Guest

Hey Matthias – I gave it a try with my own examples (see the github project linked at the end of the article) – and everything seems to work fine both when the annotation is in the base class as well as in the concrete class.

Now – you may have a slightly different usecase (and since your SO example wasn’t copy-paste runnable I couldn’t reproduce the issue) – but you can give it a try with the CustomerServiceWithParent service I set up (submit a PR with the failing test).
Cheers,
Eugen.

Matthias Dietl
Guest
Matthias Dietl

Hey Eugen, thanks for you help! I’ve added a restcontroller and a resource scan in order to match the example with my setup, now I’m getting a “java.lang.IllegalStateException: No cache could be resolved for” Exception. I’ve submitted a PR (#418).
Thanks,
Matthias

Eugen Paraschiv
Guest

Sounds good – I’ll follow up there.

Mayank
Guest
Mayank

Hi, Great Article, easy to follow. One question though, Is it possible to instantiate 1 CacheManager with two different intervals for different Cache. @Bean public CacheManager cacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCacheBuilder( CacheBuilder.newBuilder() .expireAfterWrite(HOURS_12, TimeUnit.HOURS) //.expireAfterWrite(HOURS_12, TimeUnit.MINUTES) .maximumSize(CACHE_MAX_SIZE)); cacheManager.setCaches(Arrays.asList( new ConcurrentMapCache(“directory”), new ConcurrentMapCache(“addresses”))); return cacheManager; }

Eugen Paraschiv
Guest

First – let me see if I clearly understand what you’re asking. If you set 2 expiration times, how would that work exactly? Wouldn’t it simply expire based on the lowest of the two?
Second – since we are in the comments (as opposed to – for instance – a github issue) – having a lot of code there makes it a bit hard to read – so, if you can, please edit it and clean up (or remove) some of that code.
Thanks,
Eugen.

Eduardo Medeiros Branquinho
Guest
Eduardo Medeiros Branquinho

I dont get it. If i use @Cacheable, the result is cached and the application do not hit my database anymore. Ok, but if i put another entity in db, lets say, a user, the method with @Cacheable will show the old result, without my new entity, because it doesnt hit the database, so, it doesnt now that i have a new recorde. If i use @CachePut it will always show me current database situation, but, it will always hit the database, so… what i gain with it? I would like to have a @Cacheable notation, that holds my result… Read more »

Eduardo Medeiros Branquinho
Guest
Eduardo Medeiros Branquinho

Okay, i’m sorry. I just have to use
@CacheEvict(value=”addresses”, allEntries=true) in my save method. I somehow didnt pay enought attention in this annotation.

Grzegorz Piwowarek
Guest
Grzegorz Piwowarek

Yeah, exactly 🙂 when you are performing action that changes something, you need to mark it properly and cache will be evicted.

Ramesh Babu Y
Guest
Ramesh Babu Y

can we integrate , memcached with spring ? can you please provide some help in integrate memcached with spring please , i checked that that their is no cachemanager implementation for the memcached cache

Grzegorz Piwowarek
Guest
Grzegorz Piwowarek

Ramesh, Spring does not support Memcached out of the box but there are solutions for this. Check the following library, should be helpful.

https://github.com/ragnor/simple-spring-memcached

akuma8
Guest
akuma8

Hi Eugen, nice job as usual. Just a question, how can I measure the efficiency of this solution in a Spring MVC application? I have to make several database operations and using this kind of solution could really help. Unfortunately I don’t know all the tools which could help me to make an efficient web application. Another one, did you do a serie of tutorials that could help making a user friendly application? Like your serie of “Registration process”? Or could you advice me where can I find what could help me? Many thanks and please keep on sharing your… Read more »

Grzegorz Piwowarek
Guest
Grzegorz Piwowarek

Do you mean simple comparison of execution times with and without caching?

akuma8
Guest
akuma8

Yes