幾個重要的概念 & 緩存注解
| Cache |
緩存接口,定義緩存操作。實現有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
| CacheManager |
緩存管理器,管理各種緩存(Cache)組件 |
| @Cacheable |
主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存 |
| @CacheEvict |
清空緩存 |
| @CachePut |
保證方法調用,又能將結果緩存,可以用於刷新緩存 |
| @EnableCaching |
開啟基於注解的緩存 |
| keyGenerator |
緩存數據時key生成策略 |
| serialize |
緩存數據時value序列化的策略 |
使用緩存
首先引入cache組件

@Cacheable
將方法的運行結果進行緩存,以后要相同的數據,直接從緩存中獲取,不用調用方法
- CacheManager管理多個Cache組件的,每一個緩存組件有自己唯一的名字
- 幾個屬性:
- cacheNames/value:指定緩存組件的名字 cacheNames = {"emp"}
- key:緩存數據使用的key,默認使用的是方法的參數、還可以使用SpEL來編寫
- keyGenerator:key的生成器,生成緩存數據的key,keyGenerator跟key只能二選一
- cacheManager:指定緩存管理器,或者cacheResolver獲取解析器
- condition:指定符合條件的情況下才緩存。例:condition="#id>0",id大於0才緩存
- unless:否定緩存,當unless指定的條件為true,方法的返回值就不會被緩存,可以用#result獲取方法的返回值。例: unless = "#result == null"
- sync: 是否使用異步模式
key的取值
| 名字 |
描述 |
示例 |
| methodName |
當前被調用的方法名 |
#root.methodName |
| method |
當前被調用的方法 |
#root.method.name |
| target |
當前被調用的目標對象 |
#root.target |
| targetClass |
當前被調用的目標對象類 |
#root.targetClass |
| args |
當前被調用的方法的參數列表 |
#root.args[0] |
| caches |
當前方法調用使用的緩存列表(如@Cacheable(value={“cache1”, “cache2”})),則有兩個cache |
#root.caches[0].name |
| argument name |
方法參數的名字,可以直接 #參數名,也可以使用 #p0或#a0 的形式,0代表參數的索引 |
#iban、#a0, #p0 |
| result |
方法執行后的返回值(僅當前方法執行之后的判斷有效,如’unless’, ‘cache put’的表達式 ‘cache evict’的表達式beforeInvocation=false) |
#result |
例:
①、首先在主程序類上標注上@EnableCaching注解
@EnableCaching
@SpringBootApplication
public class SpringBootCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootCacheApplication.class, args);
}
}
②、在要緩存的方法上標注即可
@Cacheable(cacheNames = {"emp"})
public Employee getEmp(Integer id){
System.out.println("查詢" + id + "員工號");
Employee emp = employeeMapper.getEmployeeById(id);
return emp;
}
@CachePut
這個注解用於更新緩存
示例:
/**
* @CachePut: 既調用方法,又更新緩存
* 主要用於修改了數據庫的某個數據,同時更新緩存
* 運行時機:先調用目標方法,再將目標方法的結果緩存起來
* 與@Cacheable不同,@Cacheable是先去緩存里面查看是否有值,再執行方法,因而這里在獲取id的時候@CachePut可以通過#result獲取而@Cacleable不行
* 注意:這里的key一定要填要更新的緩存的key,不填就會默認使用第一個參數作為key,很可能就達不到更新緩存的目的而是新創建了一個緩存
*/
@CachePut(value = "emp", key = "#result.id")
public Employee updateEmp(Employee employee){
System.out.println("更新cache");
employeeMapper.updateEmp(employee);
return employee;
}
@CacheEvict
該注解用於刪除一個緩存
示例:
/**
* allEntries:刪除所有的緩存 allEntries = true
* beforeInvocation:緩存的清除是否在方法之前執行,默認是在方法之后執行
* 其他參數與@Cacheable大致一樣
* @param id
*/
@CacheEvict(value = "emp", key = "#id", allEntries = true)
public void deleteEmp(Integer id){
System.out.println("刪除" + id);
}
@Caching
該注解用於存放多個緩存注解,有時候對於一個方法,想放置多個緩存,既想緩存又想更新時使用
示例:
/**
* 可以放多個注解
* @param lastName
* @return
*/
@Caching(
cacheable = {
@Cacheable(value = "emp", key = "#lastName")
},
put = {
@CachePut(value = "emp", key = "#result.id"),
@CachePut(value = "emp", key = "#result.email")
}
)
public Employee getEmpByLastName(String lastName){
System.out.println("查詢");
return employeeMapper.getEmpByLastName(lastName);
}
@CacheConfig
有的時候覺得每次在使用緩存注解的時候都要指定緩存的名字,或者指定緩存的cacheManager之類的,覺得很麻煩。那么就可以在類上使用@CacheConfig統一的配置緩存的名字
@CacheConfig(value = "emp")
@Service
public class EmployeeService {
自定義key的生成策略
對於key,我們可以讓它自動生成,生成的策略可以有我們自己制定,之需要在配置類中將我們的定制規則加入到容器中即可
@Bean
public KeyGenerator keyGenerator(){
return new KeyGenerator(){
@Override
public Object generate(Object o, Method method, Object... objects) {
return method.getName() + "[" + Arrays.asList(objects) + "]";
}
};
}
使用Redis做為緩存
SpringBoot默認使用的是ConcurrentMapCacheManager==ConcurrentMapCache,將數據保存在ConcurrentMap<String, Cache> cacheMap里面
當然我們還可以使用其他的中間件作為緩存
開發中使用的緩存中間件:redis,memcached,ehcache
引入redis的starter
<!-- 引入redis的starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置redis
來到配置文件application.yaml配置redis的主機地址
spring:
redis:
host: 172.17.119.176
使用redis
springboot的starter提供了兩個Template操作redis
StringRedisTemplate跟RedisTemplate,其中StringRedisTemplate的key跟value都是字符串。redisTemplate的key跟value都是Object
/**
* String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)、HyperLogLog
* stringRedisTemplate.opsForValue() 操作String(字符串)
* stringRedisTemplate.opsForList() 操作List(列表)
* stringRedisTemplate.opsForSet() 操作Set(集合)
* stringRedisTemplate.opsForHash() 操作Hash(散列)
* stringRedisTemplate.opsForZSet() 操作ZSet(有序集合)
* stringRedisTemplate.opsForHyperLogLog() 操作HyperLogLog
*/
@Test
public void test01(){
stringRedisTemplate.opsForValue().append("redis", "hello");
stringRedisTemplate.opsForList();
stringRedisTemplate.opsForSet();
stringRedisTemplate.opsForHash();
stringRedisTemplate.opsForZSet();
stringRedisTemplate.opsForHyperLogLog();
}
儲存對象
有的時候需要將對象存進redis(例如一個JavaBean對象),但是如果對象不是可Serializable的,因此需要讓JavaBean對象實現Serializable接口
public class Employee implements Serializable {
序列化機制
如果只是讓JavaBean實現Serializable接口也是可以存儲的,但是並不好看,那么能不能將JavaBean弄成Json的樣式放進redis呢。直接的方式就是自己轉換,但是未免有點麻煩,那就只能修改RedisTemplate的序列化機制了,在配置類中配置上序列化的方法即可
@Bean
public RedisTemplate<Object, Employee> empredisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Employee> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
redisTemplate.setDefaultSerializer(ser);
return redisTemplate;
}
自定義CacheManager
對於自定義CacheManager,SpringBoot 1.x跟SpringBoot 2.x有所不同
SpringBoot 1.x
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
//默認超時時間,單位秒
redisCacheManager.setDefaultExpiration(60);
//緩存超時時間Map,key為cacheName,value為超時,單位是秒
Map<String, Long> expiresMap = new HashMap<>();
//緩存用戶信息的cacheName和超時時間
expiresMap.put("user", 1800L);
//緩存產品信息的cacheName和超時時間
expiresMap.put("product", 600L);
redisCacheManager.setExpires(expiresMap);
return redisCacheManager;
}
SpringBoot 2.x
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)); // 設置緩存有效期一小時
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration).build();
}
編碼的方式使用緩存
前面都是用的注解的方法將注解標注在方法上去緩存方法的結果,但是如果在方法的執行中有一個數據我們想緩存那應該怎么辦呢?既然前面提到過CacheManager是管理Cache的,那我們就可以直接使用CacheManager去緩存了
@Autowired
RedisCacheManager redisCacheManager;
public void test(){
Cache emp = redisCacheManager.getCache("emp");
emp.put("111", "222");
}
