使用Spring Cache设置缓存条件操作

网友投稿 1507 2022-12-08

使用Spring Cache设置缓存条件操作

使用Spring Cache设置缓存条件操作

目录Spring Cache设置缓存条件原理@Cacheable的常用属性及说明Root对象@CachePut的常用属性同@CacheableCache缓存配置1、pom.xml2、Ehcache配置文件3、配置类4、示例

Spring Cache设置缓存条件

原理

从Spring3.1开始,Spring框架提供了对Cache的支持,提供了一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的作用。

提供的主要注解有@Cacheable、@CachePut、@CacheEvict和@Caching,具体见下表:

注解

说明

@Cacheable

可以标注在类或方法上:标注在方法上表示该方法支持数据缓存;标在类上表示该类的所有方法都支持数据缓存。 具体功能:在执行方法体之前,检查缓存中是否有相同key值的缓存存在,如果存在对应的缓存,直接返回缓存中的值;如果不存在对应的缓存,则执行相应的方法体获取数据,并将数据存储到缓存中。

@CachePut

可以标注在类或方法上,表示支持数据缓存。 具体功能:在方法执行前不会检查缓存中是否存在相应的缓存,而是每次都会执行方法体,并将方法执行结果存储到缓存中,如果相应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"),可以不指定值,如果不指定,则缺省按照方法的所有参数进行组合。除了上述使用方法参数作为key之外,Spring还提供了一个root对象用来http://生成key,使用方法如下表所示,其中"#root"可以省略。

Root对象

Root对象

说明

methodName

当前方法名,比如#root.methodName

method

当前方法,比如#root.method.name

target

当前被调用的对象,比如#root.target

targetClass

当前被调用的对象的class,比如#root.targetClass

args

http:// 当前方法参数组成的数组,比如#root.args[0]

caches

当前被调用的方法使用的缓存,比如#root.caches[0].name

@CachePut的常用属性同@Cacheable

@CacheEvict的常用属性如下表所示:

@CacheEvict属性

说明

value

表示要清除的缓存名

key

表示需要清除的缓存key值,

condition

当condition的值为true时才清除缓存

allEntries

表示是否需要清除缓存中的所有元素。默认为false,表示不需要,当指定了allEntries为true时,将忽略指定的key。

beforeInvocation

清除操作默认是在方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当该属性值为true时,会在调用该方法之前清除缓存中的指定元素。

示例:设置当 dname 的长度大于3时才缓存

//条件缓存

@ResponseBody

@GetMapping("/getLocByDname")

@Cacheable(cacheNames = "dept", key = "#dname", condition = "#dname.length()>3")

public String getLocByDname(@RequestParam("dname") String dname) {//key动态参数

QueryWrapper 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 getDeptByDname(@RequestParam("dname") String dname){//key动态参数

QueryWrapper 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

org.springframework.boot

spring-boot-starter-cache

org.springframework.boot

spring-boot-starter-aop

org.reflections

reflections

0.9.11

2、Ehcache配置文件

xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"

updateCheck="false">

eternal="false"

maxElementsInMemory="200000"

overflowToDisk="false"

diskPersistent="false"

timeToIdleSeconds="0"

timeToLiveSeconds="600"

memoryStoreEvictionPolicy="LRU" />

xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"

updateCheck="false">

eternal="false"

maxElementsInMemory="200000"

overflowToDisk="false"

diskPersistent="false"

timeToIdleSeconds="0"

timeToLiveSeconds="600"

memoryStoreEvictionPolicy="LRU" />

eternal="false"

maxElementsInMemory="200000"

overflowToDisk="false"

diskPersistent="false"

timeToIdleSeconds="0"

timeToLiveSeconds="600"

memoryStoreEvictionPolicy="LRU" />

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) {

returnhttp:// 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动态代理可能导致重复缓存

* 注意:返回的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());

// 注意一定要转为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> 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,结合实际业务场景仅扫描@Cacheable即可

*/

final Set 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 implements XXXService {

@CacheEvict(allEntries = true)

public void evictAllEntries() {}

@Override

@Cacheable

public List findById(Long id) {

return this.baseMapper.selectList(new QueryWrapper().lambda()

.eq(XXXEntity::getId, id));

}

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:一小时迅速入门Mybatis之Prepared Statement与符号的使用
下一篇:一小时迅速入门Mybatis之实体类别名与多参数 动态SQL
相关文章

 发表评论

暂时没有评论,来抢沙发吧~