spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict

摘要:
@可缓存使用范围:@Cacheable可以标记在方法或类上。您需要通过org.springframework.cache.interceptor.CacheResolver接口实现自己的缓存解析器,并使用此参数@CacheEvictpublicIntegerdelete;@CacheEvictpublicIntegerdelete;@CacheEvictUserdeleteUserById;@缓存使用范围:(同上)功能:可以同时指定多个与Spring Cache相关的注释可用属性:它有三个属性:cacheable、put和exist,分别用于指定@cacheable、@CachePut和@CacheEvect。
 

1.情景展示  

  从3.1开始,Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。Spring Cache是作用在方法上的,其核心思想是这样的:

  当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。

  spring中内部集成了缓存,我们可以拿来直接使用,如何实现对缓存的增、删、改、查操作?

2.@Cacheable、@CachePut、@CacheEvict

  通过这三个注解,完全可以实现对缓存数据库的增删改查。

  @Cacheable

  使用范围:

  @Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。

  特性:初次将数据存入缓存,以后从缓存读取,可以当作读取操作

  对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回(不需要再次执行该方法);

  否则才会执行并将返回结果存入指定的缓存中。

  Spring在缓存方法的返回值时是以键值对进行缓存的;

  值:就是方法的返回结果;

  键:支持两种策略,默认策略和自定义策略。

  自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。

spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict第1张

  注意:当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。

  可用属性:

  • value/cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
  • key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = "#p0"):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档
  • condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3"),表示只有当第一个参数的长度小于3的时候才会被缓存。
  • unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
  • keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的
  • cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
  • cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。
@Cacheable("userCache")
User selectUserById(final Integer id);
@Cacheable(value={"users1", "user2"}, key="caches[1].name")
public User find(User user);  
@Cacheable(value={"userCache"}, key="#user.id", condition="#user.id%2==0")
User getSomeUsers(User user);

  @CachePut

  使用范围:@CachePut也可以标注在类上和方法上;

  特性:一直往缓存中存,可以当作新增或者更新操作

  @CachePut也可以声明一个方法支持缓存功能;

  与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。

  可用属性:(同上)

@CachePut(value = "userCache")
User updateUserById(final Integer id);

  @CacheEvict

  使用范围:(同上)

  当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。

  特性:一直从缓存中删除,可以当作删除操作

  清除缓存

  可用属性:(同上)

  @CacheEvict还有下面两个参数:

  • allEntries:非必需,默认为false。当为true时,清除缓存中的所有元素
  • beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。
@CacheEvict(cacheNames = {"userCache"}, allEntries = true)
public Integer delete(Integer id);  
@CacheEvict(cacheNames = {"userCache"}, beforeInvocation = true)
public Integer delete(Integer id);
@CacheEvict(value = "userCache")
User deleteUserById(final Integer id);  

  @Caching

  使用范围:(同上)

  特性:可同时指定多个Spring Cache相关的注解

  可用属性:

  拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。

@Caching( 
    put = { 
        @CachePut(cacheNames = "userCache", key = "#user.id"), 
        @CachePut(cacheNames = "userCache", key = "#user.username"), 
        @CachePut(cacheNames = "userCache", key = "#user.age") 
   } 
}

  自定义注解

  前提:要想使用spring缓存注解,需要开启spring缓存,否则,一切都是枉然。

  开启方法:@EnableCache

spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict第2张

  在springboot的项目启动类上加上此注解即可。 

3.解决方案

  上面简单的使用方式,这里不在赘述,下面我们来看高级一点的用法。

  方式一:提供接口

  接口:

/**
 * 单位信息缓存
 * @description:
 * @author: Marydon
 * @date: 2020-12-12 9:42
 * @version: 1.0
 * @email: marydon20170307@163.com
 */
public interface IUnitInfoCache {

    UNITINFO insertOrUpdateCache(String unitKey,UNITINFO unitInfo);

    UNITINFO getCache(String unitKey);

    void deleteCache(String unitKey);
}

  实现类:

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.Component;

/**
 * 单位信息表缓存实现类
 * @description:
 * @author: Marydon
 * @date: 2020-12-12 10:05
 * @version: 1.0
 * @email: marydon20170307@163.com
 */
@Component
@CacheConfig(cacheNames = {"unitInfo2"})
public class UnitInfoCacheImpl implements IUnitInfoCache{

    /*
     * 缓存单位信息
     * @description: 存入缓存
     * @attention: 在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
     *  指定key后,就不再自动将所有入参当成key的组成部分啦
     * @date: 2020年12月12日 0012 9:53
     * @param: unitKey 作为缓存的主键
     * @param: unitInfo 作为缓存的值
     * @return: UNITINFO
     *  在这里添加返回值的目的不是为了调用返回值,因为我们把它作为入参传进来了,所以,看似是画蛇添足;
     *  实际上是因为@CachePut注解会将返回值作为value存入缓存中,所以返回值不能改成void。
     */
    @CachePut(key = "#unitKey", unless = "#result == null")
    @Override
    public UNITINFO insertOrUpdateCache(String unitKey, UNITINFO unitInfo) {
        return unitInfo;
    }

    /*
     * 获取单位信息
     * @description: 从缓存中取
     * @attention: 对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。
     * @date: 2020年12月12日 0012 9:55
     * @param: unitKey
     * @return: UNITINFO
     *  缓存中有就取,没有就执行方法并返回null。
     */
    @Cacheable(key = "#unitKey", unless = "#result == null")
    @Override
    public UNITINFO getCache(String unitKey) {
        // 缓存中没有对应的可以时,会进来
        return null;
    }

    /*
     * 删除单位信息
     * @description: 从缓存中删除
     * @attention:
     * @date: 2020年12月12日 0012 9:56
     * @param: unitKey
     * @return: void
     */
    @CacheEvict(key = "#unitKey")
    @Override
    public void deleteCache(String unitKey) {
        // 不需要内部实现,@CacheEvict会自动将入参作为key进行移除
    }
}

  方式二:提供父类

  父类:

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;

/**
 * 缓存操作父类
 * @description: 使用这种方式虽然可以实现多个子类共享方法,
 * 但是,这些子类需要共用一个cacheName,如果不想共用缓存名称,那就只能重写这些方法
 * @author: Marydon
 * @date: 2020-12-12 11:04
 * @version: 1.0
 * @email: marydon20170307@163.com
 */
@CacheConfig(cacheNames = {"myCache"})
public abstract class CacheAbstractParent<T> {
    /*
     * 存入或更新指定缓存
     * @description:
     * @attention: 在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
     *  指定key后,就不再自动将所有入参当成key的组成部分啦
     * @date: 2020年12月12日 0012 9:53
     * @param: key 作为缓存的主键
     * @param: javaBean 作为缓存的值
     * @return: java对象
     *  在这里添加返回值的目的不是为了调用返回值,因为我们把它作为入参传进来了,所以,看似是画蛇添足;
     *  实际上是因为@CachePut注解会将返回值作为value存入缓存中,所以返回值不能改成void。
     */
    @CachePut(key = "#key", unless = "#result == null")
    public T insertOrUpdateCache(String key, T value) {
        return value;
    }

    /*
     * 获取单位信息
     * @description: 从缓存中取
     * @attention: 对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。
     * @date: 2020年12月12日 0012 9:55
     * @param: key
     * @return: java对象或null
     *  缓存中有就取,没有;就执行方法并返回null。
     */
    @Cacheable(key = "#key", unless = "#result == null")
    public T getCache(String key) {
        // 缓存中没有对应的可以时,会进来
        return null;
    }

    /*
     * 删除单位信息
     * @description: 从缓存中删除
     * @attention:
     * @date: 2020年12月12日 0012 9:56
     * @param: key
     * @return: 无返回值
     */
    @CacheEvict(key = "#key")
    public void deleteCache(String key) {
        // 不需要内部实现,@CacheEvict会自动将入参作为key进行移除
    }
}

  子类:

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Component;

/**
 * 单位信息表缓存
 * @description: 在这里我们只需要定义好缓存的名称,以及要被缓存的对象就可以啦
 * 缓存的操作:增、删、改、查,父类已经实现过了。
 * 这样,我们就可以无限扩展N个缓存实体类
 * @author: Marydon
 * @date: 2020-12-12 11:14
 * @version: 1.0
 * @email: marydon20170307@163.com
 */
@Component
// 这里配置缓存名称无效
// @CacheConfig(cacheNames = {"unitCache"})
public class UnitInfoCache extends CacheAbstractParent<UNITINFO>{
    // 继承了父类所有操作缓存的方法
}

4.调用及测试

  在需要缓存的地方,注入缓存对象即可。比如:

spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict第3张

  调用

spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict第4张

  如果你开启了redis缓存,那我们可以看到具体的存储数据:

spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict第5张

  结果会形如这个样子。 

  如何才能从缓存中取出来呢?   

  第一步:在使用缓存的地方注入缓存管理对象

spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict第6张

  第二步:取值

  一开始,我以为通过这种方式能取到值,事实证明我错了,无法直接通过key拿到value。

spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict第7张

  然后,按照redis的存储方式取值也拿不到

spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict第8张 

  要想获得value,在调用get方法时,需要指定value的返回值类型,或者说用什么样的数据类型来接收返回值。

spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict第9张

  控制台输出结果: 

spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict第10张

  这次,我们就可以拿到value啦。

5.缓存策略  

  如果缓存满了,从缓存中移除数据的策略,常见的有FIFO, LRU 、LFU。

  • FIFO (First in First Out) 先进先出策略,即先放入缓存的数据先被移除
  • LRU (Least Recently Used) 最久未使用策略, 即使用时间距离现在最久的那个数据被移除
  • LFU (Least Frequently Used)  最少使用策略,即一定时间内使用次数(频率)最少的那个数据被移除
  • TTL(Time To Live)存活期,即从缓存中创建时间点开始至到期的一个时间段(不管在这个时间段内有没被访问过都将过期)
  • TTI (Time To Idle)空闲期,即一个数据多久没有被访问就从缓存中移除的时间。

6.缓存管理器

  通过@EnableCaching注解自动化配置合适的缓存管理器(CacheManager),Spring Boot根据下面的顺序去侦测缓存提供者:

  •  Generic
  • JCache (JSR-107)
  •  EhCache 2.x
  •  Hazelcast
  •  Infinispan
  •  Redis
  •  Guava
  •  Simple

  可以通过配置属性spring.cache.type来强制指定,即:

spring.cache.type=Generic

2021-04-14

  关于缓存条件参数的补充

  unless = "#result == null and #result != ''",表示的含义是:当满足该条件时,不进行缓存(当方法返回值为空时,不将key添加到缓存当中);

  condition = "#myKey != null and #myKey != ''",表示的含义是:当满足该条件时,才会进行缓存(当作为缓存主键的参数不为空时,才将该key-value存入缓存)

写在最后

  哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!

 相关推荐:

 

免责声明:文章转载自《spring,springboot 快速完成缓存库的增删改查@Cacheable、@CachePut、@CacheEvict》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Microsoft Web Test Recorder在录制时没有显示WPFProgressBarAndSlider随位置显示Value下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Firebird数据库自动备份脚本

cd C:\Program Files\Firebird\Firebird_2_5\bingbak.exe -USER "sysdba" -PAS "masterkey"  -B  C:\BM.FDB  C:\dbbak\BM%date:~0,4%%date:~5,2%%date:~8,2%-%time:~0,2%%time:~3,2%%time:~6,2...

微信小游戏的本地缓存和清除的使用 (text.js image.js file-util.js)

 参考: 微信小游戏,文件系统 UpdateManager-小游戏 一、Egret提供的本地缓存工具类( 备注:新版本进行了修改,并增加了sound.js等) 在微信小游戏项目中,Egret提供了file-util.js、image.js、text.js来控制微信的50M缓存。 二、尝试缓存一个json文件 现在我们尝试使用text.js来缓存json配...

使用Echarts+Javaweb可视化数据库中数据

这里引用了王正帅同学的图片 地址如下:https://www.cnblogs.com/20183544-wangzhengshuai/p/12409216.html 一、总体感受    首先,说一些我个人感受,这是本人第一次接触图表可视化插件的使用, 说实话,刚开始编代码还是很懵的,而且刚开始的编代码的时候,我有点 心浮气躁了,我直接在网上去找与题目直接相...

CPU高速缓存行与内存关系 及并发MESI 协议

先来一个整体图 一. 大致关系: CPU Cache --> 前端总线 FSB (下图中的Bus) --> Memory 内存 CPU 为了更快的执行代码。于是当从内存中读取数据时,并不是只读自己想要的部分。而是读取足够的字节来填入高速缓存行。根据不同的 CPU ,高速缓存行大小不同。如 X86 是 32BYTES ,而 ALPHA 是 64...

cdn技术浅谈 大风起

CDN,即内容分发网络。cdn,一种网络缓存技术,将网站的内容发布到最接近用户的网络"边缘",使用户可以就近取得所需的内容,解决Internet网络拥塞状况,提高用户访问网站的响应速度。就是当用户访问加入CDN服务的网站时候,域名解析请求将最终交给全局负载均衡DNS处理。解决因分布、带宽、服务器性能带来的访问延迟的问题,适用于站点加速,点播,直播等场景。比如...

架构之CDN缓存

CDN缓存 CDN主要解决将数据缓存到离用户最近的位置,一般缓存静态资源文件(页面,脚本,图片,视频,文件等)。国内网络异常复杂,跨运营商的网络访问会很慢。为了解决跨运营商或各地用户访问问题,可以在重要的城市,部署CDN应用。使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。 CND原理 CDN的基本原理是广泛采用各种缓存服务器,将这些缓...