使用Spring Cache設置緩存條件操作
Spring Cache設置緩存條件
原理
從Spring3.1開始,Spring框架提供了對Cache的支持,提供了一個對緩存使用的抽象,通過在既有代碼中添加少量它定義的各種 annotation,即能夠達到緩存方法的返回對象的作用。
提供的主要注解有@Cacheable、@CachePut、@CacheEvict和@Caching,具體見下表:
| 注解 | 說明 |
|---|---|
| @Cacheable | 可以標注在類或方法上:標注在方法上表示該方法支持數(shù)據(jù)緩存;標在類上表示該類的所有方法都支持數(shù)據(jù)緩存。 具體功能:在執(zhí)行方法體之前,檢查緩存中是否有相同key值的緩存存在,如果存在對應的緩存,直接返回緩存中的值;如果不存在對應的緩存,則執(zhí)行相應的方法體獲取數(shù)據(jù),并將數(shù)據(jù)存儲到緩存中。 |
| @CachePut | 可以標注在類或方法上,表示支持數(shù)據(jù)緩存。 具體功能:在方法執(zhí)行前不會檢查緩存中是否存在相應的緩存,而是每次都會執(zhí)行方法體,并將方法執(zhí)行結(jié)果存儲到緩存中,如果相應key值的緩存存在,則更新key對應的value值。 |
| @CacheEvict | 可以標注在類或方法上,用于清除相應key值的緩存。 |
| @Caching | 可以標注在類或方法上,它有三個屬性cacheable、put、evict分別用于指定@Cacheable、@CachePut和@CacheEvict |
當需要在類上或方法上同時使用多個注解時,可以使用@Caching,如:
@Caching(cacheable=@Cacheable("User"), evict = {@CacheEvict("Member"), @CacheEvict(value = "Customer", allEntries = true)})
@Cacheable的常用屬性及說明
如下表所示:
| @Cacheable屬性 | 說明 |
|---|---|
| key | 表示緩存的名稱,必須指定且至少要有一個值,比如:@Cacheable(value=“Dept”)或@Cacheable(value={“Dept”,“Depts”}) |
| condition | 表示是否需要緩存,默認為空,表示所有情況都會緩存。通過SpEL表達式來指定,若condition的值為true則會緩存,若為false則不會緩存,如@Cacheable(value=“Dept”,key="‘deptno_'+# deptno “,condition=”#deptno<=40") |
| value | 表示緩存的key,支持SpEL表達式,如@Cacheable(value=“Dept”,key="‘deptno_' +#deptno"),可以不指定值,如果不指定,則缺省按照方法的所有參數(shù)進行組合。除了上述使用方法參數(shù)作為key之外,Spring還提供了一個root對象用來生成key,使用方法如下表所示,其中"#root"可以省略。 |
Root對象
| Root對象 | 說明 |
|---|---|
| methodName | 當前方法名,比如#root.methodName |
| method | 當前方法,比如#root.method.name |
| target | 當前被調(diào)用的對象,比如#root.target |
| targetClass | 當前被調(diào)用的對象的class,比如#root.targetClass |
| args | 當前方法參數(shù)組成的數(shù)組,比如#root.args[0] |
| caches | 當前被調(diào)用的方法使用的緩存,比如#root.caches[0].name |
@CachePut的常用屬性同@Cacheable
@CacheEvict的常用屬性如下表所示:
| @CacheEvict屬性 | 說明 |
|---|---|
| value | 表示要清除的緩存名 |
| key | 表示需要清除的緩存key值, |
| condition | 當condition的值為true時才清除緩存 |
| allEntries | 表示是否需要清除緩存中的所有元素。默認為false,表示不需要,當指定了allEntries為true時,將忽略指定的key。 |
| beforeInvocation | 清除操作默認是在方法成功執(zhí)行之后觸發(fā)的,即方法如果因為拋出異常而未能成功返回時不會觸發(fā)清除操作。使用beforeInvocation可以改變觸發(fā)清除操作的時間,當該屬性值為true時,會在調(diào)用該方法之前清除緩存中的指定元素。 |
示例:設置當 dname 的長度大于3時才緩存
//條件緩存
@ResponseBody
@GetMapping("/getLocByDname")
@Cacheable(cacheNames = "dept", key = "#dname", condition = "#dname.length()>3")
public String getLocByDname(@RequestParam("dname") String dname) {//key動態(tài)參數(shù)
QueryWrapper<Dept> queryMapper = new QueryWrapper<>();
queryMapper.eq("dname", dname);
Dept dept = deptService.getOne(queryMapper);
return dept.getLoc();
}
示例:unless 即條件不成立時緩存
#result 代表返回值,意思是當返回碼不等于 200 時不緩存,也就是等于 200 時才緩存。
@ResponseBody
@GetMapping("/getDeptByDname")
@Cacheable(cacheNames = "dept", key = "#dname", unless = "#result.code != 200")
public Result<Dept> getDeptByDname(@RequestParam("dname") String dname){//key動態(tài)參數(shù)
QueryWrapper<Dept> queryMapper = new QueryWrapper<>();
queryMapper.eq("dname", dname);
Dept dept = deptService.getOne(queryMapper);
if (dept == null)
return ResultUtil.error(120, "dept is null");
else
return ResultUtil.success(dept);
}
Cache緩存配置
1、pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 反射工具類用于手動掃描指定包下的注解,根據(jù)defaultCache模塊增加ehcache緩存域(非Spring Cache必須)-->
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
2、Ehcache配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!-- 磁盤緩存位置 -->
<diskStore path="java.io.tmpdir" />
<!--
name:緩存名稱。
maxElementsInMemory:緩存最大個數(shù)。
eternal:對象是否永久有效,一但設置了,timeout將不起作用。
timeToIdleSeconds:設置對象在失效前的允許閑置時間(單位:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閑置時間無窮大。
timeToLiveSeconds:設置對象在失效前允許存活時間(單位:秒)。最大時間介于創(chuàng)建時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。
overflowToDisk:當內(nèi)存中對象數(shù)量達到maxElementsInMemory時,Ehcache將會對象寫到磁盤中。
diskSpoolBufferSizeMB:這個參數(shù)設置DiskStore(磁盤緩存)的緩存區(qū)大小。默認是30MB。每個Cache都應該有自己的一個緩沖區(qū)。
maxElementsOnDisk:硬盤最大緩存?zhèn)€數(shù)。
diskPersistent:是否緩存虛擬機重啟期數(shù)據(jù) Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。
memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據(jù)指定的策略去清理內(nèi)存。默認策略是LRU(最近最少使用)。你可以設置為FIFO(先進先出)或是LFU(較少使用)。
clearOnFlush:內(nèi)存數(shù)量最大時是否清除。
-->
<!-- 默認緩存 -->
<defaultCache
eternal="false"
maxElementsInMemory="200000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
3、配置類
@Configuration
@EnableCaching
public class CustomConfiguration {
/**
* @see org.springframework.cache.interceptor.SimpleKeyGenerator
* Generate a key based on the specified parameters.
*/
public static Object generateKey(Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
}
if (params.length == 1) {
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
return new SimpleKey(params);
}
/**
* 若將target作為key的一部分時,CGLIB動態(tài)代理可能導致重復緩存
* 注意:返回的key一定要重寫hashCode()和toString(),防止key對象不一致導致的緩存無法命中
* 例如:ehcache 底層存儲net.sf.ehcache.store.chm.SelectableConcurrentHashMap#containsKey
*/
@Bean
public KeyGenerator customKeyGenerator(){
return (target, method, params) -> {
final Object key = generateKey(params);
StringBuffer buffer = new StringBuffer();
buffer.append(method.getName());
buffer.append("::");
buffer.append(key.toString());
// 注意一定要轉(zhuǎn)為String,否則ehcache key對象可能不一樣,導致緩存無法命中
return buffer.toString();
};
}
/**
* redis緩存管理器
*/
@Bean
@ConditionalOnBean(RedisConfiguration.class)
@ConditionalOnProperty(prefix = "spring.cache", name = "type", havingValue = "redis",
matchIfMissing = false)
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofMinutes(10));
return RedisCacheManager
.builder(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(config).build();
}
/**
* ehcache緩存管理器(默認)
* default XML files {@link net.sf.ehcache.config.ConfigurationFactory#parseConfiguration()}
*/
@Bean
@ConditionalOnProperty(prefix = "spring.cache", name = "type", havingValue = "ehcache",
matchIfMissing = true)
public CacheManager ehcacheCacheManager() {
net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.create();
/**
* 包掃描查找指定注解并將cacheNames添加到net.sf.ehcache.CacheManager(單例)
*/
Reflections reflections = new Reflections("com.example.demo.service", new TypeAnnotationsScanner()
, new SubTypesScanner(), new MethodAnnotationsScanner());
Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(CacheConfig.class);
for (Class<?> aClass : classesList) {
final CacheConfig config = AnnotationUtils.findAnnotation(aClass, CacheConfig.class);
if (config.cacheNames() != null && config.cacheNames().length > 0) {
for (String cacheName : config.cacheNames()) {
cacheManager.addCacheIfAbsent(cacheName);
}
}
}
/**
* 方法級別的注解 @Caching、@CacheEvict、@Cacheable、@CachePut,結(jié)合實際業(yè)務場景僅掃描@Cacheable即可
*/
final Set<Method> methods = reflections.getMethodsAnnotatedWith(Cacheable.class);
for (Method method : methods) {
final Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class);
if (cacheable.cacheNames() != null && cacheable.cacheNames().length > 0) {
for (String cacheName : cacheable.cacheNames()) {
cacheManager.addCacheIfAbsent(cacheName);
}
}
}
EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager();
ehCacheCacheManager.setCacheManager(cacheManager);
return ehCacheCacheManager;
}
}
4、示例
@Component
@CacheConfig(cacheNames = "XXXServiceImpl", keyGenerator = "customKeyGenerator")
public class XXXServiceImpl extends ServiceImpl<XXXMapper, XXXEntity> implements XXXService {
@CacheEvict(allEntries = true)
public void evictAllEntries() {}
@Override
@Cacheable
public List<XXXEntity> findById(Long id) {
return this.baseMapper.selectList(new QueryWrapper<XXXEntity>().lambda()
.eq(XXXEntity::getId, id));
}
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot實現(xiàn)人臉識別與WebSocket長連接的實現(xiàn)代碼
這篇文章主要介紹了Springboot實現(xiàn)人臉識別與WebSocket長連接的實現(xiàn),本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-11-11
沒有編輯器的環(huán)境下是如何創(chuàng)建Servlet(Tomcat+Java)項目的?
今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識,文章圍繞著在沒有編輯器的環(huán)境下如何創(chuàng)建Servlet(Tomcat+Java)項目展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下2021-06-06
SpringBoot集成Redis及SpringCache緩存管理示例詳解
本文介紹了如何在SpringBoot中集成Redis并使用SpringCache進行緩存管理,詳解了Redis的配置、使用以及SpringCache的注解,還闡述了SpringCache的工作原理,包括其AOP實現(xiàn)和與各種緩存框架的集成,使得開發(fā)者可以輕松實現(xiàn)緩存功能,以提高應用性能2024-09-09

