Spring Boot 2系列(十):Spring 缓存体系
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。
1 | import org.springframework.cache.CacheManager; |
声名式缓存注解
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
开启缓存支持。
1 | <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
,需要导入依赖。
开启缓存支持
1
2
3
4
5
6
7
8import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
public class CacheConfig {
}Category实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import com.fasterxml.jackson.annotation.JsonFormat;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Date;
public class Category {
private Long CategoryId;
private String name;
private Date lastUpdate;
//....get/set.......
}Controller
1
2
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
60import 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;
public class CategoryController {
//添加fastJson依赖
private final static Logger logger = LogManager.getLogger(CategoryController.class);
private CategoryService categoryService;
public Category queryById(Long CategoryId){
Category category = categoryService.queryById(CategoryId);
logger.info(JSON.toJSONString(category));
return category;
}
public Category queryByCategoryId(Category category){
category = categoryService.queryByCategoryId(category);
logger.info(JSON.toJSONString(category));
return category;
}
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;
}
public void deleteById(Long categoryId){
categoryService.deleteById(categoryId);
}
public Category queryByName(Category category){
category = categoryService.queryByName(category);
logger.info(JSON.toJSONString(category));
return category;
}
public Category queryByCategoryName(Category category){
category = categoryService.queryByCategoryName(category);
logger.info(JSON.toJSONString(category));
return category;
}
}Service接口
1
2
3
4
5
6
7
8
9
10
11
12
13public 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接口实现
1
2
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
107import 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;
public class CategoryServiceImpl implements CategoryService {
private CategoryRepository categoryRepository;
/**
* Cacheable 先从缓存中查找,没有再找数据库
* @param categoryId
* @return
*/
public Category queryById(Long categoryId) {
return categoryRepository.findById(categoryId).get();
}
/**
* 先查缓存,再查数据库
* @param category
* @return
*/
public Category queryByCategoryId(Category category) {
return categoryRepository.findById(category.getCategoryId()).get();
}
/**
* 保存数据并存入缓存
* @param category
* @return
*/
public Category saveCategory(Category category) {
return categoryRepository.save(category);
}
/**
* 删除数据同时删除缓存
* @param categoryId
*/
public void deleteById(Long categoryId) {
categoryRepository.deleteById(categoryId);
}
/**
* 查数据并存缓存
* @param category
* @return
*/
public Category queryByName(Category category) {
Example<Category> example = new Example<Category>(){
public Category getProbe() {
return category;
}
public ExampleMatcher getMatcher() {
ExampleMatcher matcher = ExampleMatcher.matchingAny();
return matcher;
}
};
return categoryRepository.findOne(example).get();
}
/**
* 先从缓存中查,没有再从库里查
* @param category
* @return
*/
public Category queryByCategoryName(Category category) {
Example<Category> example = new Example<Category>(){
public Category getProbe() {
return category;
}
public ExampleMatcher getMatcher() {
ExampleMatcher matcher = ExampleMatcher.matchingAny();
return matcher;
}
};
return categoryRepository.findOne(example).get();
}
}CategoryRepository
1
2
3
4import org.springframework.data.jpa.repository.JpaRepository;
public interface CategoryRepository extends JpaRepository<Category, Long> {
}
相关参考
Spring Boot 2系列(十):Spring 缓存体系
http://blog.gxitsky.com/2018/05/31/SpringBoot-10-spring-cache/