Spring Caching Example with Java and XML Configuration

摘要:

Spring Caching Annotations

In this tutorial we show you how you can leverage the spring framework caching abstraction to improve the performance of your system. At its core, the abstraction applies caching to Java methods, reducing the number of invocations based on the information available in the cache. You can decorate your java methods with Java annotations or XML Configuration thus minimizing the impact on the code.

How the caching works

Each time a targeted method is invoked, the abstraction will apply a caching behavior checking whether the method has been already executed for the given arguments. If it has, then the cached result is returned without having to execute the actual method; if it has not, then method is executed, the result cached and returned to the user so that, the next time the method is invoked the cached result is returned. This way, expensive methods (whether CPU or IO bound) can be executed only once for a given set of parameters and the result reused without having to actually execute the method again. The caching logic is applied transparently without any interference to the invoker.

Supported Caching Providers

The cache abstraction of the Spring Framework does not provide an actual store and relies on one of the following supported caching providers. If you haven’t defined a bean of type CacheManager or a CacheResolver, Spring Boot tries to detect the following providers (in this order).

Enable caching annotations

You can easily enable caching by adding the @EnableCaching annotation to one of your @Configuration classes. You can also completely disable caching by removing this annotation. Meaning, whenever you’re debugging an issue and you want to make sure it’s not a caching issue.. just remove this @EnableCaching annotation.

@Configuration
@EnableCaching
public class CacheConfig {

}

Alternatively for XML configuration use the cache:annotation-driven element:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

    <cache:annotation-driven />

</beans>

@Cacheable annotation

Caching a method in Spring is as simple as annotating a method with the @Cacheable annotation.

@Cacheable("instruments")
public List findAll() { ... }

In the snippet above, the method findAll is associated with the cache named instruments. When the method is executed for the first time, the result is stored into the cache so on subsequent invocations with the same arguments, the value in the cache is returned without invoking the method.

While in most cases one cache is enough, the spring framework supports multiple caches.

@Cacheable(cacheNames = {"instruments", "directory"})
public List findAll() { ... }

In this case, each of the caches will be checked before executing the method – if at least one cache is hit, then the associated value will be returned. All the other caches that do not contain the value will be updated as well.

Conditional Caching

You can optionally use a condition when a method is not suitable for caching all the time. This condition takes a SpEL expression that is evaluated to a boolean condition. If true the result is called.

@Cacheable(condition = "#instrument.equals('trombone')")
public String play(String instrument) { .. }

In addition the condition parameter, the unless parameter can be used to veto the adding of a value to the cache. Unlike condition, unless expressions are evaluated after the method has been called.

@Cacheable(unless = "#result.size() > 25")
public List findAll() { .. }

@CachePut annotation

You can use the @CachePut annotation to populate the cache. The method will always be executed and its result placed into the cache (according to the @CachePut options).

@CachePut(cacheNames="instruments", allEntries=true)
public void save(String instrument) { .. }
Important

Be carefully when mixing @CachePut and @Cacheable annotations on the same method. While the @Cacheable annotation causes the method execution to be skipped by using the cache, the @CachePut annotation forces the execution in order to execute a cache update. This can lead to unexpected behavior.

@CacheEvict annotation

The @CacheEvict is useful for removing stale or unused data from the cache. This method act as a trigger for removing data from the cache.

@CacheEvict
public void delete(String instrument) { .. }

You can optionally pass an extra parameter allEntries=true to wipe alle the entries from the cache.

@CacheEvict(allEntries=true)
public void delete(String instrument) { .. }

You can also pass in a beforeInvocation=true parameter. When true the eviction always occurs before the method is executed.

@CacheEvict(beforeInvocation=true)
public void delete(String instrument) { .. }

@Caching annotation

The @Caching annotation allows multiple nested @Cacheable, @CachePut and @CacheEvict annotations to be used on the same method.

@Caching(cacheable = {
        @Cacheable(condition = "#index == 1"),
        @Cacheable(condition = "#index == 2"),
})
public String findByIndex(int index) { .. }

@CacheConfig annotation

You can annotate your class with the @CacheConfig annotation to specify cache configurations for the entire class.

@CacheConfig(cacheNames = {"directory", "instruments"})
public class MusicServiceIml implements MusicService {..}

Spring Caching Example Java and XML Configuration

In the previous section we saw different caching annotations of the spring framework. In the following section we demonstrate how to use them with a simple example configuration.

Maven Dependencies

We use Apache Maven to manage our project dependencies. Start by adding the following dependencies to your project.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.memorynotfound.springboot.caching</groupId>
    <artifactId>caching-annotations</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <url>https://memorynotfound.com</url>
    <name>Spring Boot - ${project.artifactId}</name>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
    </parent>

    <dependencies>
        <!-- Spring Framework Caching Support -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Cashing Service Methods

To demonstrate the spring framework caching abstraction, we created a simple service.

package com.memorynotfound.springboot;

import java.util.List;

public interface MusicService {

    List<String> findAll();

    String findByIndex(int index);

    void save(String instrument);

    void delete(String instrument);

    void deleteAll();

    String play( final String instrument );
}
package com.memorynotfound.springboot;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.*;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Service
@CacheConfig(cacheNames = {"directory", "instruments"})
public class MusicServiceIml implements MusicService {

    private static Logger log = LoggerFactory.getLogger(Application.class);

    private static List<String> instruments = new ArrayList<String>(Arrays.asList("Guitar", "Bass", "Keyboard"));

    @Cacheable(unless = "#result.size() > 25")
    @Override
    public List<String> findAll() {
        log.info("Executing: " + this.getClass().getSimpleName() + ".findAll();");
        return instruments;
    }

    @Cacheable
    @Override
    public String findByIndex(int index) {
        log.info("Executing: " + this.getClass().getSimpleName() + ".findByIndex(\"" + index + "\");");
        return instruments.get(index);
    }

    @CachePut
    @Override
    public void save(String instrument) {
        log.info("Executing: " + this.getClass().getSimpleName() + ".save(\"" + instrument + "\");");
        instruments.add(instrument);
    }

    @CacheEvict
    @Override
    public void delete(String instrument) {
        log.info("Executing: " + this.getClass().getSimpleName() + ".delete(\"" + instrument + "\");");
        instruments.remove(instrument);
    }

    @CacheEvict(allEntries = true)
    @Override
    public void deleteAll() {
        log.info("Executing: " + this.getClass().getSimpleName() + ".deleteAll();");
        instruments.removeAll(instruments);
    }

    @Cacheable(condition = "#instrument.equals('trombone')")
    public String play(String instrument) {
        log.info("Executing: " + this.getClass().getSimpleName() + ".play(\"" + instrument + "\");");
        return "paying " + instrument + "!";
    }

}

Spring Cache Configuration

This example uses a simple ConcurrentMapCache to demonstrate the caching mechanism. The following is the java configuration.

Spring Cache Java Configuration

package com.memorynotfound.springboot;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;

@Configuration
@EnableCaching
public class CacheConfig {

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

}

And here is the equivalent Spring XML Caching Configuration:

@ImportResource("classpath:cache-config.xml")
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
                           http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.2.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <cache:annotation-driven/>
    <context:annotation-config/>

    <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 cache-manager="cacheManager">
        <cache:caching cache="instruments, directory">
            <cache:cacheable method="findAll" unless="#result.size() > 25"/>
            <cache:cacheable method="findByIndex"/>
            <cache:cache-put method="save"/>
            <cache:cache-evict method="delete"/>
            <cache:cache-evict method="deleteAll" all-entries="true"/>
            <cache:cacheable method="play" condition="#instrument.equals('trombone')"/>
        </cache:caching>
    </cache:advice>

</beans>

Bootstrap Spring Application

The following application demonstrates the spring caching by executing a couple of methods annotated with various caching annotations.

package com.memorynotfound.springboot;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application implements CommandLineRunner {

    private static Logger log = LoggerFactory.getLogger(Application.class);

    @Autowired
    private MusicService musicService;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        log.info("Spring Boot Conditional Caching and Other Caching Annotations Example");

        String service = MusicServiceIml.class.getSimpleName();
        log.info("Calling: " + service + ".findAll();");
        log.info("Occurrences: " + musicService.findAll());
        log.info("Calling: " + service + ".findAll();");
        log.info("Occurrences: " + musicService.findAll());
        log.info("Calling: " + service + ".findByIndex();");
        musicService.findByIndex(1);
        log.info("Calling: " + service + ".findByIndex();");
        musicService.findByIndex(1);
        log.info("Calling: " + service + ".findByIndex();");
        musicService.findByIndex(0);
        log.info("Calling: " + service + ".findByIndex();");
        musicService.findByIndex(0);
        log.info("Calling: " + service + ".save();");
        musicService.save("Saxophone");
        log.info("Calling: " + service + ".findAll();");
        log.info("Occurrences: " + musicService.findAll());
        log.info("Calling: " + service + ".delete();");
        musicService.delete("Bass");
        log.info("Calling: " + service + ".findAll();");
        log.info("Occurrences: " + musicService.findAll());
        log.info("Calling: " + service + ".deleteAll();");
        musicService.deleteAll();
        log.info("Calling: " + service + ".findAll();");
        log.info("Occurrences: " + musicService.findAll());

        play("trombone");
        play("guitar");
        play("trombone");
        play("bass");
        play("trombone");
    }

    private void play(String instrument){
        log.info("Calling: " + MusicServiceIml.class.getSimpleName() + ".play(\"" + instrument + "\");");
        musicService.play(instrument);
    }
}

Running Spring Application

We can run our application using the following command.

mvn spring-boot:run

Output

The previous application prints the following output to the console.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.4.RELEASE)

DEBUG - Running with Spring Boot v1.5.4.RELEASE, Spring v4.3.9.RELEASE
INFO  - Spring Boot Conditional Caching and Other Caching Annotations Example
INFO  - Calling: MusicServiceIml.findAll();
INFO  - Executing: MusicServiceIml.findAll();
INFO  - Occurrences: [Guitar, Bass, Keyboard]
INFO  - Calling: MusicServiceIml.findAll();
INFO  - Occurrences: [Guitar, Bass, Keyboard]
INFO  - Calling: MusicServiceIml.findByIndex();
INFO  - Executing: MusicServiceIml.findByIndex("1");
INFO  - Calling: MusicServiceIml.findByIndex();
INFO  - Calling: MusicServiceIml.findByIndex();
INFO  - Executing: MusicServiceIml.findByIndex("0");
INFO  - Calling: MusicServiceIml.findByIndex();
INFO  - Executing: MusicServiceIml.findByIndex("0");
INFO  - Calling: MusicServiceIml.save();
INFO  - Executing: MusicServiceIml.save("Saxophone");
INFO  - Calling: MusicServiceIml.findAll();
INFO  - Occurrences: [Guitar, Bass, Keyboard, Saxophone]
INFO  - Calling: MusicServiceIml.delete();
INFO  - Executing: MusicServiceIml.delete("Bass");
INFO  - Calling: MusicServiceIml.findAll();
INFO  - Occurrences: [Guitar, Keyboard, Saxophone]
INFO  - Calling: MusicServiceIml.deleteAll();
INFO  - Executing: MusicServiceIml.deleteAll();
INFO  - Calling: MusicServiceIml.findAll();
INFO  - Executing: MusicServiceIml.findAll();
INFO  - Occurrences: []
INFO  - Calling: MusicServiceIml.play("trombone");
INFO  - Executing: MusicServiceIml.play("trombone");
INFO  - Calling: MusicServiceIml.play("guitar");
INFO  - Executing: MusicServiceIml.play("guitar");
INFO  - Calling: MusicServiceIml.play("trombone");
INFO  - Calling: MusicServiceIml.play("bass");
INFO  - Executing: MusicServiceIml.play("bass");
INFO  - Calling: MusicServiceIml.play("trombone");

Download

上一篇: Spring Boot Caffeine Caching Example Configuration
下一篇: Spring Boot Spring Data JPA with Hibernate and H2 Web Console
 评论 ( What Do You Think )
名称
邮箱
网址
评论
验证
   
 

 


  • 微信公众号

  • 我的微信

站点声明:

1、一号门博客CMS,由Python, MySQL, Nginx, Wsgi 强力驱动

2、部分文章或者资源来源于互联网, 有时候很难判断是否侵权, 若有侵权, 请联系邮箱:summer@yihaomen.com, 同时欢迎大家注册用户,主动发布无版权争议的 文章/资源.

3、鄂ICP备14001754号-3, 鄂公网安备 42280202422812号