欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-17 22:14:02
目录spring cache设置缓存条件@cacheable的常用属性及说明root对象@cacheput的常用属性同@cacheablecache缓存配置2、ehcache配置文件spring ca...

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对象用来生成key,使用方法如下表所示,其中"#root"可以省略。

root对象

root对象 说明
methodname 当前方法名,比如#root.methodname
method 当前方法,比如#root.method.name
target 当前被调用的对象,比如#root.target
targetclass 当前被调用的对象的class,比如#root.targetclass
args 当前方法参数组成的数组,比如#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<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动态参数
    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>
<!-- 反射工具类用于手动扫描指定包下的注解,根据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:缓存最大个数。
        eternal:对象是否永久有效,一但设置了,timeout将不起作用。
        timetoidleseconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
        timetoliveseconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
        overflowtodisk:当内存中对象数量达到maxelementsinmemory时,ehcache将会对象写到磁盘中。
        diskspoolbuffersizemb:这个参数设置diskstore(磁盘缓存)的缓存区大小。默认是30mb。每个cache都应该有自己的一个缓冲区。
        maxelementsondisk:硬盘最大缓存个数。
        diskpersistent:是否缓存虚拟机重启期数据 whether the disk store persists between restarts of the virtual machine. the default value is false.
        diskexpirythreadintervalseconds:磁盘失效线程运行时间间隔,默认是120秒。
        memorystoreevictionpolicy:当达到maxelementsinmemory限制时,ehcache将会根据指定的策略去清理内存。默认策略是lru(最近最少使用)。你可以设置为fifo(先进先出)或是lfu(较少使用)。
        clearonflush:内存数量最大时是否清除。
    -->
    <!-- 默认缓存 -->
    <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动态代理可能导致重复缓存
 * 注意:返回的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<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,结合实际业务场景仅扫描@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));
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。