热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

@cacheable是否缓存成功_SpringBoot缓存开发实战

本篇博客的目录一:springBoot开启缓存注解二:常用缓存注解三:使用实例四:总结《Java2019超神之路》《Dubb

本篇博客的目录

  • 一:springBoot开启缓存注解
  • 二:常用缓存注解
  • 三:使用实例
  • 四:总结
  • 《Java 2019 超神之路》
  • 《Dubbo 实现原理与源码解析 —— 精品合集》
  • 《Spring 实现原理与源码解析 —— 精品合集》
  • 《MyBatis 实现原理与源码解析 —— 精品合集》
  • 《Spring MVC 实现原理与源码解析 —— 精品合集》
  • 《Spring Boot 实现原理与源码解析 —— 精品合集》
  • 《数据库实体设计合集》
  • 《Java 面试题 —— 精品合集》
  • 《Java 学习指南 —— 精品合集》

一:springBoot开启注解

1.1:搭建springBoot环境

在idea中,搭建一个springboot是很简单easy的。接下来我简单说一下步骤:

File->new->projiect->Spring Initializer->next->named->web(选中)->Finish->new Window

1.2:开始缓存

@SpringBootApplication
@EnableAutoConfiguration
@EnableCaching
public class SpringbootcacheApplication {public static void main(String[] args) {SpringApplication.run(SpringbootcacheApplication.class, args);}
}

主要是@EnableCaching用于开启缓存注解的驱动,否则后面使用的缓存都是无效的!

二:常用缓存注解

2.1:@CacheConfig

这个注解的的主要作用就是全局配置缓存,比如配置缓存的名字(cacheNames),只需要在类上配置一次,下面的方法就默认以全局配置为主,不需要二次配置,节省了部分代码。

2.2:@Cacheable

这个注解是最重要的,主要实现的功能再进行一个读操作的时候。就是先从缓存中查询,如果查找不到,就会走数据库的执行方法,这是缓存的注解最重要的一个方法,基本上我们的所有缓存实现都要依赖于它。它具有的属性为cacheNames:缓存名字,condtion:缓存的条件,unless:不缓存的条件。可以指定SPEL表达式来实现,也可以指定缓存的key,缓存的内部实现一般都是key,value形式,类似于一个Map(实际上cacheable的缓存的底层实现就是concurrenHashMap),指定了key,那么缓存就会以key作为键,以方法的返回结果作为值进行映射。

2.3:@CacheEvict

这个注解主要是配合@Cacheable一起使用的,它的主要作用就是清除缓存,当方法进行一些更新、删除操作的时候,这个时候就要删除缓存。如果不删除缓存,就会出现读取不到最新缓存的情况,拿到的数据都是过期的。它可以指定缓存的key和conditon,它有一个重要的属性叫做allEntries默认是false,也可以指定为true,主要作用就是清除所有的缓存,而不以指定的key为主。

2.4:@CachePut

这个注解它总是会把数据缓存,而不会去每次做检查它是否存在,相比之下它的使用场景就比较少,毕竟我们希望并不是每次都把所有的数据都给查出来,我们还是希望能找到缓存的数据,直接返回,这样能提升我们的软件效率。

2.5:@cache

这个注解它是上面的注解的综合体,包含上面的三个注解(cacheable、cachePut、CacheEvict),可以使用这一个注解来包含上面的所有的注解,看源码如下

5a28a3781c8f45f409daf8c9bbad7783.png

上面的注解总结如下表格:

5a28a3781c8f45f409daf8c9bbad7783.png

三:使用实例

3.1:建立数据库

我们来新建一个表,含义为文章,下面的示例将会在这张表中进行操作,所使用的框架为SSM+springboot

CREATE TABLE Artile (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` varchar(30) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL ,
`author` varchar(30) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL ,
`content` mediumtext CHARACTER SET gbk COLLATE gbk_chinese_ci NULL ,
`file_name` varchar(30) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL ,
`state` smallint(2) NULL DEFAULT 1 COMMENT '状态' ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=gbk COLLATE=gbk_chinese_ci
AUTO_INCREMENT=11
ROW_FORMAT=COMPACT
;

3.2:Mapper层

主要就是对Article进行增删改查的业务操作,映射到具体的xml的sql里,然后用service去调用

public interface ArticleMapper {/*** 插入一篇文章* @param title* @param author* @param content* @param fileName* @return*/public Integer addArticle(@Param("title") String title,@Param("author")String author,@Param("content")String content,@Param("fileName")String fileName);/*** 根据id获取文章* @param id* @return*/public Article getArticleById(@Param("id") Integer id);/*** 更新content* @param content*/public Integer updateContentById(@Param("content")String content,@Param("id")Integer id);/*** 根据id删除文章* @param id* @return*/public Integer removeArticleById(@Param("id")Integer id);/*** 获得上一次插入的id* @return*/public Integer getLastInertId();}

3.3:service层

主要需要注意的是我们上述讲述的缓存注解都是基于service层(不能放在contoller和dao层),首先我们在类上配置一个CacheConfig,然后配置一个cacheNames,那么下面的方法都是以这个缓存名字作为默认值,他们的缓存名字都是这个,不必进行额外的配置。当进行select查询方法的时候,我们配置上@Cacheable,并指定key,这样除了第一次之外,我们都会把结果缓存起来,以后的结果都会把这个缓存直接返回。而当进行更新数据(删除或者更新操作)的时候,使用@CacheEvict来清除缓存,防止调用@Cacheabel的时候没有更新缓存

@Service
@CacheConfig(cacheNames = "articleCache")
public class ArticleService {private AtomicInteger count =new AtomicInteger(0);@Autowiredprivate ArticleMapper articleMapper;/*** 增加一篇文章 每次就进行缓存* @return*/@CachePutpublic Integer addArticle(Article article){Integer result = articleMapper.addArticle(article.getTitle(), article.getAuthor(), article.getContent(), article.getFileName());if (result>0) {Integer lastInertId = articleMapper.getLastInertId();System.out.println("--执行增加操作--id:" + lastInertId);}return result;}/*** 获取文章 以传入的id为键,当state为0的时候不进行缓存* @param id 文章id* @return*/@Cacheable(key = "#id",unless = "#result.state==0")public Article getArticle(Integer id) {try {//模拟耗时操作Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}final Article artcile = articleMapper.getArticleById(id);System.out.println("--执行数据库查询操作"+count.incrementAndGet()+"次"+"id:"+id);return artcile;}/*** 通过id更新内容 清除以id作为键的缓存** @param id* @return*/@CacheEvict(key = "#id")public Integer updateContentById(String contetnt, Integer id) {Integer result = articleMapper.updateContentById(contetnt, id);System.out.println("--执行更新操作id:--"+id);return result;}/*** 通过id移除文章* @param id 清除以id作为键的缓存* @return*/@CacheEvict(key = "#id")public Integer removeArticleById(Integer id){final Integer result = articleMapper.removeArticleById(id);System.out.println("执行删除操作,id:"+id);return result;}}

3.4:controller层

主要是接受客户端的请求,我们配置了@RestController表示它是一个rest风格的应用程序,在收到add请求会增加一条数据,get请求会查询一条数据,resh会更新一条数据,rem会删除一条数据

@RestController
@ComponentScan(basePackages = {"com.wyq.controller", "com.wyq.service"})
@MapperScan(basePackages = {"com.wyq.dao"})
public class ArticleController {@Autowiredprivate ArticleService articleService;@AutowiredArticleMapper articleMapper;@PostMapping("/add")public ResultVo addArticle(@RequestBody Article article) {System.out.println(article.toString());Integer result = articleService.addArticle(article);if (result >= 0) {return ResultVo.success(result);}return ResultVo.fail();}@GetMapping("/get")public ResultVo getArticle(@RequestParam("id") Integer id) {Long start = System.currentTimeMillis();Article article = articleService.getArticle(id);Long end = System.currentTimeMillis();System.out.println("耗时:"+(end-start));if (null != article)return ResultVo.success(article);return ResultVo.fail();}/*** 更新一篇文章** @param contetnt* @param id* @return*/@GetMapping("/resh")public ResultVo update(@RequestParam("content") String contetnt, @RequestParam("id") Integer id) {final Integer result = articleService.updateContentById(contetnt, id);if (result > 0) {return ResultVo.success(result);} else {return ResultVo.fail();}}/*** 删除一篇文章** @param id* @return*/@GetMapping("/rem")public ResultVo remove(@RequestParam("id") Integer id) {final Integer result = articleService.removeArticleById(id);if (result > 0) {return ResultVo.success(result);} else {return ResultVo.fail();}}}

3.5:测试

这里使用postman模拟接口请求

3.5.1:首先我们来增加一篇文章:请求add接口:

a584309ee39bfb436511a181c9372be2.png

后台返回表示成功:

0bedfefdd08e5fba3ec6eacd69966bba.png

我看到后台数据库已经插入了数据,它的id是11

a1937d2e8cf535848692c9a90451d453.png

3.5.2:执行查询操作

在查询操作中,getArticle,我使用线程睡眠的方式,模拟了5秒的时间来处理耗时性业务,第一次请求肯定会查询数据库,理论上第二次请求,将会走缓存,我们来测试一下:首先执行查询操作

b0b95776f0dd333861bc7709ac1a9b73.png

接口响应成功,再看一下后台打印:表示执行了一次查询操作,耗时5078秒

20b965a2886b03fb6cf43105fbfab5bc.png

好,重点来了,我们再次请求接口看看会返回什么?理论上,将不会走数据库执行操作,并且耗时会大大减少:与上面的比对,这次没有打印执行数据库查询操作,证明没有走数据库,并且耗时只有5ms,成功了!缓存发挥作用,从5078秒减小到5秒!大大提升了响应速度,哈哈!

d8e5cf7b5d02cafc71c711ea20f14587.png

后台控制台打印:

--执行更新操作id:--11

趁热打铁,我们再次请求三次查询接口,看看会返回什么?每次都会返回这样的结果,但是我的直观感受就是第一次最慢,第二次、第三次返回都很快

c89bb6668b89f887ba60d17dad70379c.png

再看看后台打印了什么?执行id为11的数据库查询操作,这是因为缓存被清空了,所以它又走数据库了(获得最新数据),然后后面的查询都会走缓存!很明显,实验成功!

f6968df85bd96dbc620985399ede22ef.png

3.5.4:删除操作

同理,在删除操作中,执行了一次删除,那么缓存也会被清空,查询的时候会再次走数据库,这里就不给具体实验效果了,如果需要的同学,可以把代码下载下来,自己测试一下就知道了。

四:总结

本篇博客介绍了springBoot中缓存的一些使用方法,如何在开发中使用缓存?怎样合理的使用都是值得我们学习的地方,缓存能大大提升程序的响应速度,提升用户体验,不过它适用的场景也是读多写少的业务场景,如果数据频繁修改,缓存将会失去意义,每次还是执行的数据库操作!如何使用好它,还有更高效的方式,比如使用redismemoryCache等专业组件,本篇博客只是探讨的spring的注解缓存,相对来说比较简单。希望起到抛砖引玉的作用,在以后博客中,我将介绍redis如何搭建集群来实现缓存!



推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文介绍了brain的意思、读音、翻译、用法、发音、词组、同反义词等内容,以及脑新东方在线英语词典的相关信息。还包括了brain的词汇搭配、形容词和名词的用法,以及与brain相关的短语和词组。此外,还介绍了与brain相关的医学术语和智囊团等相关内容。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • 本文介绍了lintcode(12)题目的要求和解题思路,以及给出了相应的代码实现。题目要求在给定的字符串source中找到包括所有目标字符串字母的最短子串,并且时间复杂度为O(n)。解题思路是使用滑动窗口的方法,通过维护一个unordered_map来记录目标字符串中每个字符的出现次数,并使用双指针来寻找最小子串。代码实现部分给出了具体的实现代码。 ... [详细]
author-avatar
sEn_森森森森森
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有