前言
cache可以说是后端提高响应速度、承载能力的标准套路了
spring boot中提供spring boot starter cache 组件 配合spring boot starter redis 或者其他缓存组件 可以很简单的使用缓存
spring cache 介绍
一套基于spring aop的方式 为函数添加缓存的 框架
支持的缓存类型
- Generic
- JCache (JSR-107)
- EhCache 2.x
- Hazelcast
- Infinispan
- Redis
- Guava
- Simple
如果不满足上述的缓存方案 可以自实现 cacheManager
注解介绍
- @Cacheable
获取缓存 如果有缓存 直接返回
属性 |
类型 |
功能 |
value |
String[] |
缓存的名称 和cacheNames功能一样 |
cacheNames |
String[] |
缓存的名称和value功能一样 |
key |
String |
缓存key的值、默认是以所有的参数作为key、也可以直接配置keyGenerator |
keyGenerator |
String |
缓存key的生成器 |
cacheManager |
String |
配置使用那个缓存管理器、和cacheResolver排斥 |
cacheResolver |
String |
定义使用那个拦截器、和cacheManager互斥 |
condition |
String |
根据spel表达式来可以配置什么条件下进行缓存 默认全部缓存 |
unless |
String |
和condition相反 |
sync |
boolean |
是否开启同步功能、默认不开启 |
- @CachePut
执行并且更新缓存相关 不管如何 肯定会执行方法 然后返回 这样可以更新缓存的内容
属性 |
类型 |
功能 |
value |
String[] |
缓存的名称 和cacheNames功能一样 |
cacheNames |
String[] |
缓存的名称和value功能一样 |
key |
String |
缓存key的值、默认是以所有的参数作为key、也可以直接配置keyGenerator |
keyGenerator |
String |
缓存key的生成器 |
cacheManager |
String |
配置使用那个缓存管理器、和cacheResolver排斥 |
cacheResolver |
String |
定义使用那个拦截器、和cacheManager互斥 |
condition |
String |
根据spel表达式来可以配置什么条件下进行缓存 默认全部缓存 |
unless |
String |
和condition相反 |
属性 |
类型 |
功能 |
value |
String[] |
缓存的名称 和cacheNames功能一样 |
cacheNames |
String[] |
缓存的名称和value功能一样 |
key |
String |
缓存key的值、默认是以所有的参数作为key、也可以直接配置keyGenerator |
keyGenerator |
String |
缓存key的生成器 |
cacheManager |
String |
配置使用那个缓存管理器、和cacheResolver排斥 |
cacheResolver |
String |
定义使用那个拦截器、和cacheManager互斥 |
condition |
String |
根据spel表达式来可以配置什么条件下进行缓存 默认全部缓存 |
allEntries |
boolean |
是否删除所有键的缓存 默认不删除 |
beforeInvocation |
boolean |
是否在调用此方法前 删除缓存 |
- @CacheConfig
在类级别统一的配置缓存公共配置
属性 |
类型 |
功能 |
cacheNames |
String[] |
缓存的名称和value功能一样 |
keyGenerator |
String |
缓存key的生成器 |
cacheManager |
String |
配置使用那个缓存管理器、和cacheResolver排斥 |
cacheResolver |
String |
定义使用那个拦截器、和cacheManager互斥 |
- @EnableCaching
开启缓存以及缓存的全局配置
属性 |
类型 |
功能 |
proxyTargetClass |
boolean |
是否要基于cglib生成代理去实现缓存 |
mode |
AdviceMode |
配置那种模式去实现缓存、默认是AdviceMode.PROXY 可以切换为 AdviceMode#ASPECTJ |
order |
int |
设置缓存管理器执行的顺序 |
属性 |
类型 |
功能 |
cacheable |
Cacheable |
配置获取缓存相关的配置 |
put |
CachePut |
配置如何更新缓存的相关配置 |
evict |
CacheEvict |
配置如何删除缓存的相关配置 |
实战
gradle 依赖
1 2 3 4
| compile('org.springframework.boot:spring-boot-starter-web') compile('org.springframework.boot:spring-boot-starter-data-redis') compile('org.springframework.boot:spring-boot-starter-cache') testCompile('org.springframework.boot:spring-boot-starter-test')
|
启动引导开启缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.ming;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication //开启缓存 @EnableCaching public class Start {
public static void main(String[] args) { SpringApplication.run(Start.class,args); } }
|
配置
1 2 3 4 5 6 7 8
| spring: redis: host: <ip> port: <port> password: <password> cache: # spring cache 缓存类型为redis 也可以是其他的实现 type: redis
|
使用cache
模拟带缓存的service
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
| package com.ming;
import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service;
@Service //公共配置 可以在类上注释 注释本类的 缓存相关公共配置 //@CacheConfig(cacheNames = TestCacheService.CACHE_KEY) public class TestCacheService {
public static final String CACHE_KEY = "test-cache";
/** * 获取信息 第二次访问会取缓存 * * @author ming * @date 2018-07-11 17:41:47 */ @Cacheable(cacheNames = CACHE_KEY) public String testCache(String id) { return getString(id); }
/** * 更新信息 更新缓存 * * @author ming * @date 2018-07-12 09:50:53 */ @CachePut(cacheNames = CACHE_KEY) public String testCachePut(String id) { return getString(id + "update"); }
/** * 清除缓存 * * @author ming * @date 2018-07-12 09:51:22 */ @CacheEvict(cacheNames = CACHE_KEY) public void removeCache(String id) { System.out.println("删除缓存 "); }
/** * 获取string 模拟调用方法 * * @author ming * @date 2018-07-11 17:41:58 */ private String getString(String id) { try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } return id + "load"; }
}
|
测试用例
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
| package com.ming;
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = Start.class) public class TestCache {
@Autowired private TestCacheService testCacheService;
@Test public void test() { String id = "ming"; System.out.println("第一次访问没有缓存--------"); long oneNow = System.currentTimeMillis(); System.out.println(testCacheService.testCache(id)); System.out.println("耗时:" + (System.currentTimeMillis() - oneNow) + "ms");
System.out.println("第二次访问有缓存--------"); long twoNow = System.currentTimeMillis(); System.out.println(testCacheService.testCache(id)); System.out.println("耗时:" + (System.currentTimeMillis() - twoNow) + "ms");
System.out.println("更新缓存信息--------"); long threeNow = System.currentTimeMillis(); System.out.println(testCacheService.testCachePut(id)); System.out.println("耗时:" + (System.currentTimeMillis() - threeNow) + "ms");
System.out.println("获取更新后的缓存信息-------"); long fourNow = System.currentTimeMillis(); System.out.println(testCacheService.testCache(id)); System.out.println("耗时:" + (System.currentTimeMillis() - fourNow) + "ms");
System.out.println("移除缓存------并且调用testCache方法"); testCacheService.removeCache(id); long fiveNow = System.currentTimeMillis(); System.out.println(testCacheService.testCache(id)); System.out.println("耗时:" + (System.currentTimeMillis() - fiveNow) + "ms"); } }
|
坑
- @Cacheable 、@CachePut、@CacheEvict 必须要有 cacheNames
- 注解必须放在public修饰的方法上
- 如果只是获取缓存使用@Cacheable即可 如果要更新数据库并且更新缓存一定要使用@CachePut 否则@Cacheable会出现脏读
总结
spring cache 为缓存提供了一套简单快捷的方案 可以在旧的功能上很快速添加上缓存
具体缓存的实现 也有更多的选择 也可以自己实现spring cache的缓存管理器皿 来实现自定义的缓存
本身提供的有很多 例如Generic、JCache (JSR-107)、EhCache 2.x、Hazelcast、Infinispan、Redis、Guava、Simple
按需选择 如果没有 可以自己实现cacheManager去做