前言:缓存在开发中是一个必不可少的优化点,近期在公司的项目重构中,关于缓存优化了很多点,比如在加载一些数据比较多的场景中,会大量使用缓存机制提高接口响应速度,简介提升用户体验。关于缓存,很多人对它都是既爱又恨,爱它的是:它能大幅提升响应效率,恨的是它如果处理不好,没有用好比如LRU这种策略,没有及时更新数据库的数据就会导致数据产生滞后,进而产生用户的误读,或者疑惑。这是很严重的一个问题,比如我在公司和某家公司(国内的一线旅游开发公司)的对接的时候,线上总是出现我们推送接口数据但是网站的数据产生滞后的现象,询问对方的技术人员,告诉我们是缓存的问题,只要删除缓存就没事了,我只能无奈...所以如何处理好缓存,对我们开发人员来说是一个很棘手的问题。不过关于这一切,springboot已经提供给我们很便捷的开发工具!本篇博客就来探索springBoot的缓存注解如何使用!

本篇博客的目录

一:springBoot开启缓存注解

二:常用缓存注解

三:使用实例

四:总结

一: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.3:@CachePut

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

2.4:@cache

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

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

三:使用实例

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); @Autowired
private ArticleMapper articleMapper; /**
* 增加一篇文章 每次就进行缓存
* @return
*/
@CachePut
public 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 { @Autowired
private ArticleService articleService; @Autowired
ArticleMapper 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接口:

后台返回表示成功:

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

3.5.2:执行查询操作

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

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

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

3.5.3:更新操作

当我们进行修改操作的时候,我们希望缓存的数据被清空:看接口返回值成功了,再看数据库

后台控制台打印:

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

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

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

3.5.4:删除操作

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

 四:总结

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

本篇博客的代码示例下载地址(适用于intel idea):链接:https://pan.baidu.com/s/1CkRCFTlzfbKyg1R15Er6tw 密码:nda4(如果文件失效请及时联系我,补链)

springboot缓存开发的更多相关文章

  1. 转载-springboot缓存开发

    转载:https://www.cnblogs.com/wyq178/p/9840985.html   前言:缓存在开发中是一个必不可少的优化点,近期在公司的项目重构中,关于缓存优化了很多点,比如在加载 ...

  2. Spring Boot + Mybatis + Redis二级缓存开发指南

    Spring Boot + Mybatis + Redis二级缓存开发指南 背景 Spring-Boot因其提供了各种开箱即用的插件,使得它成为了当今最为主流的Java Web开发框架之一.Mybat ...

  3. SpringBoot Web开发(5) 开发页面国际化+登录拦截

    SpringBoot Web开发(5) 开发页面国际化+登录拦截 一.页面国际化 页面国际化目的:根据浏览器语言设置的信息对页面信息进行切换,或者用户点击链接自行对页面语言信息进行切换. **效果演示 ...

  4. SpringBoot整合开发

    1.SpringBoot分模块 分模块就是将一个项目分成多个模块,即maven项目. 1)首先创建一个springboot的项目: 第一步:选择springboot的项目 第二步:填写项目的相关信息, ...

  5. SpringBoot缓存管理(二) 整合Redis缓存实现

    SpringBoot支持的缓存组件 在SpringBoot中,数据的缓存管理存储依赖于Spring框架中cache相关的org.springframework.cache.Cache和org.spri ...

  6. SpringBoot缓存之redis--最简单的使用方式

    第一步:配置redis 这里使用的是yml类型的配置文件 mybatis: mapper-locations: classpath:mapping/*.xml spring: datasource: ...

  7. springboot区分开发、测试、生产多环境的应用配置(二)

    转:https://www.jb51.net/article/139119.htm springboot区分开发.测试.生产多环境的应用配置(二) 这篇文章主要给大家介绍了关于maven profil ...

  8. SpringBoot Web开发(4) Thymeleaf模板与freemaker

    SpringBoot Web开发(4) Thymeleaf模板与freemaker 一.模板引擎 常用得模板引擎有JSP.Velocity.Freemarker.Thymeleaf SpringBoo ...

  9. springboot区分开发、测试、生产多环境的应用配置

    转:https://blog.csdn.net/daguairen/article/details/79236885 springboot区分开发.测试.生产多环境的应用配置(一) Spring可使用 ...

随机推荐

  1. 面向英特尔® x86 平台的 Unity* 优化指南: 第 1 部分

    原文地址 目录 工具 Unity 分析器 GPA 系统分析器 GPA 帧分析器 如要充分发挥 x86 平台的作用,您可以在项目中进行多种性能优化,以最大限度地提升性能. 在本指南中,我们将展示 Uni ...

  2. vue 跳转到外部 后回跳

    微信  vue 跳转到外部 后回跳  ,比如登陆 授权操作 .需要 路由 先跳转到一个中间页面 后再跳转到授权服务器!而不能跳转前的页面与回跳后的页面相同 不然回跳可能会出现空白 路由不解析.

  3. 【Shell 开发】Shell 目录

    目录 [第一章]Shell 概述 [第二章]Shell 变量 [第三章]Shell 变量的数值计算 [第四章]Shell 条件测试表达式 [shell 练习1]编写Shell条件句练习 [shell ...

  4. 算法笔记(c++)--求一个数的所有质数因子

    算法笔记(c++)--求一个数的所有质数因子 先贴题目: 这题不难,恶心在理解上面.最后看评论知道了怎么回事: 2*2*3*3*5=180 按照这逻辑的话应该输入的数由一系列质数相乘出来,所以每次找到 ...

  5. 20181016-4 Alpha阶段第2周/共2周 Scrum立会报告+燃尽图 05

    作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2288 Scrum master:王硕 一.小组介绍 组长:王一可 组员:范 ...

  6. Alpha阶段中间产物——GUI Prototype、WBS及PSP

    作业地址:https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/1224 内容: GUI Prototype 我的书架 我的书架→添加图书 ...

  7. 欢迎来怼——第四次Scrum会议

    一.小组信息 队名:欢迎来怼小组成员队长:田继平成员:李圆圆,葛美义,王伟东,姜珊,邵朔,冉华小组照片 二.开会信息 时间:2017/10/16 17:15~17:40,总计25min.地点:东北师范 ...

  8. uc浏览器的用户体验

    用户界面: 我认为,uc浏览器的用户界面还是很招人喜欢的,可以很容易让用户找到自己想看的网页.简单快捷. 记住用户的选择: uc在每次用户访问完网站之后都会记住用户访问的高频网站,以便下次用户可以更好 ...

  9. c# 加载图片 正在被占用问题

    问题情境:图片文件加载到pdf中,程序没有退出,再次加载该图片文件,提示被占用. 解决办法: 1.加载文件会锁定该文件,fromfile方法会导致占用内存较大,不使用该方法. FileStream f ...

  10. StringBuilder、StringBuffer和String三者的联系和区别

    String 类    String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间.    String a = "a ...