Spring 对各种缓存技术抽象成了统一的接口和常用的操作方法,对不同的缓存技术,如 redis, ehcache 等透明地添加缓存的支持。
  只要使用了缓存注解@EnableCaching,Spring Boot就会自动配置缓存基本设置。
Spring 缓存支持
Spring 提供了 org.springframework.cache.CacheManager 接口来统一管理缓存技术,org.springframework.cache.Cache接口包含缓存常用的CRUD操作方法,但一般不会使用此接口。
CacheManager类型
| Cache | CacheManager | 描述 | 
| Generic | 通用缓存 | 如果定义了Cache Bean,则所有该Bean类型的 CacheManager都会被创建 | 
| JCache (JSR-107) | JCacheCacheManager | 支持JSR-107标准的实现作为缓存技术,如 EhCache 3, Hazelcast, Infinispan等 | 
| EhCache 2.x | EhCacheCacheManager | 使用EhCache 2.x作用存储缓存 | 
| Couchbase | CouchbaseCacheManager | 使用分布式Couchbase作为存储缓存 | 
| Redis | RedisCacheManager | 使用Redis作为存储缓存 | 
| Caffeine | CaffeineCacheManager | Caffeine是对Guava缓存的Java 8重写,取代了对Guava的支持。Caffeine 2.1 or higher | 
| Simple | SimpleCacheManager | 使用给定的collection来作为存储缓存,常用于测试或作简单声明,如果没有提供缓存,则使用ConcurrentHashMap 来作为缓存的简单实现 | 
CacheManager实现
使用 CacheManager 时,需要注册该实现该接口的 Bean。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.cache.ehcache.EhCacheCacheManager;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
 @EnableCaching
 public class AppConfig {
 
 @Bean
 public EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager){
 return new EhCacheCacheManager();
 }
 }
 
 | 
声名式缓存注解
Spring 提供了 4个注解来声明缓存规则(使用AOP实现)。
- @Cacheable
 方法的结果可以被缓存。查询时会先从缓存中取,如果没有再执行方法返回数据并缓存。
- key 属性可以是 SpEL 表达式.
- keyGenerator 属性是指定生成键的 Bean 的名称。
- @CachePut
 总是执行方法返回数据并缓存,不会跳过方法的执行。
- @CacheEvit
 如果存在缓存数据,则清除所有。
- @Caching
 可通过该注解来组合多个注解(上面三个注解),元注解作为该注解的属性来使用。
Spring Boot的支持
Spring Boot自动配置了多个 CacheManager 的实现,自动配置在 org.springframework.boot.autoconfigure.cache 包下。

缓存的属性类是 CacheProperties,以spring.cache为前缀的属性来配置缓存。
Spring Boot环境下,使用缓存只需要导入相应的缓存依赖包,并在配置类上使用@EnableCaching开启缓存支持。
| 12
 3
 4
 
 | <dependency><groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-cache</artifactId>
 </dependency>
 
 | 
备注:查看 spring-boot-starter-cache 的依赖,依赖了 spring-boot-starter, context, context-support包,这些包也被包含在在 Sping Boot其它组件里,没有独有的依赖包,所以如果项目已存在这三个依赖是可以不添加spring-boot-starter-cache也能使用默认缓存。如果要使用默认的 ConcurrentHashMap 来做缓存,就不能添加第三方缓存依赖(ehcache,redis),否则会启用第三方缓存技术。
Simple
示例依赖了JPA,需要导入spring-boot-starter-data-jpa;使用 Log4j2和fastJson,需要导入依赖。
- 开启缓存支持
| 12
 3
 4
 5
 6
 7
 8
 
 | import org.springframework.cache.annotation.EnableCaching;import org.springframework.context.annotation.Configuration;
 
 @Configuration
 @EnableCaching
 public class CacheConfig {
 
 }
 
 | 
- Category实体类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | import com.fasterxml.jackson.annotation.JsonFormat;
 import javax.persistence.Entity;
 import javax.persistence.Id;
 import java.util.Date;
 
 @Entity
 public class Category {
 
 @Id
 private Long CategoryId;
 private String name;
 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
 private Date lastUpdate;
 
 
 
 }
 
 | 
- Controller
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 
 | import com.alibaba.fastjson.JSON;import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.Date;
 
 @RestController
 @RequestMapping(value = "/cache/category")
 public class CategoryController {
 
 private final static Logger logger = LogManager.getLogger(CategoryController.class);
 
 @Autowired
 private CategoryService categoryService;
 
 @RequestMapping("/queryById")
 public Category queryById(Long CategoryId){
 Category category = categoryService.queryById(CategoryId);
 logger.info(JSON.toJSONString(category));
 return category;
 }
 
 @RequestMapping("/queryByCategoryId")
 public Category queryByCategoryId(Category category){
 category = categoryService.queryByCategoryId(category);
 logger.info(JSON.toJSONString(category));
 return category;
 }
 
 @RequestMapping("/saveCategory")
 public Category saveCategory(){
 Category category = new Category().setCategoryId(18L).setLastUpdate(new Date()).setName("Hello Kitty");
 category = categoryService.saveCategory(category);
 logger.info(JSON.toJSONString(category));
 return category;
 }
 
 @RequestMapping("/deleteById")
 public void deleteById(Long categoryId){
 categoryService.deleteById(categoryId);
 }
 
 @RequestMapping("/queryByName")
 public Category queryByName(Category category){
 category = categoryService.queryByName(category);
 logger.info(JSON.toJSONString(category));
 return category;
 }
 
 @RequestMapping("/queryByCategoryName")
 public Category queryByCategoryName(Category category){
 category = categoryService.queryByCategoryName(category);
 logger.info(JSON.toJSONString(category));
 return category;
 }
 
 }
 
 | 
- Service接口
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | public interface CategoryService {Category queryById(Long categoryId);
 
 Category queryByCategoryId(Category category);
 
 Category queryByName(Category category);
 
 Category queryByCategoryName(Category category);
 
 Category saveCategory(Category category);
 
 void deleteById(Long categoryId);
 }
 
 | 
- ServiceImpl接口实现
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 
 | import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.CachePut;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.data.domain.Example;
 import org.springframework.data.domain.ExampleMatcher;
 import org.springframework.stereotype.Service;
 
 @Service
 public class CategoryServiceImpl implements CategoryService {
 
 @Autowired
 private CategoryRepository categoryRepository;
 
 
 
 
 
 
 @Override
 @Cacheable(key = "#categoryId", value = "category")
 public Category queryById(Long categoryId) {
 return categoryRepository.findById(categoryId).get();
 }
 
 
 
 
 
 
 @Override
 @Cacheable(key = "#category.CategoryId", value = "category")
 public Category queryByCategoryId(Category category) {
 return categoryRepository.findById(category.getCategoryId()).get();
 }
 
 
 
 
 
 
 @Override
 @Cacheable(key = "#category.CategoryId", value = "category")
 public Category saveCategory(Category category) {
 return categoryRepository.save(category);
 }
 
 
 
 
 
 
 @Override
 @CacheEvict(value = "category")
 public void deleteById(Long categoryId) {
 categoryRepository.deleteById(categoryId);
 }
 
 
 
 
 
 
 @Override
 @CachePut(key = "#category.name", value = "category")
 public Category queryByName(Category category) {
 Example<Category> example = new Example<Category>(){
 
 @Override
 public Category getProbe() {
 return category;
 }
 
 @Override
 public ExampleMatcher getMatcher() {
 ExampleMatcher matcher = ExampleMatcher.matchingAny();
 return matcher;
 }
 };
 
 return categoryRepository.findOne(example).get();
 }
 
 
 
 
 
 
 @Override
 @Cacheable(key = "#category.name", value = "category")
 public Category queryByCategoryName(Category category) {
 Example<Category> example = new Example<Category>(){
 
 @Override
 public Category getProbe() {
 return category;
 }
 
 @Override
 public ExampleMatcher getMatcher() {
 ExampleMatcher matcher = ExampleMatcher.matchingAny();
 return matcher;
 }
 };
 return categoryRepository.findOne(example).get();
 }
 }
 
 | 
- CategoryRepository
| 12
 3
 4
 
 | import org.springframework.data.jpa.repository.JpaRepository;
 public interface CategoryRepository extends JpaRepository<Category, Long> {
 }
 
 | 
相关参考
- Spring Cache 抽象 官网
- Spring Boot Caching 官网
- Caffeine 缓存 官网
- Cache抽象详解
- Spring Boot 缓存使用