关键词:
页面缓存是应对高并发的一个比较常见的方案,当请求页面的时候,会先查询redis缓存中是否存在,若存在则直接从缓存中返回页面,否则会通过代码逻辑去渲染页面,并将渲染后的页面缓存到redis中,然后返回。下面通过简单的demo来描述这一过程:
一、准备工作:
1、新建一个springboot工程,命名为novel,添加如下依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.dtouding</groupId> <artifactId>novel</artifactId> <version>0.0.1-SNAPSHOT</version> <name>novel</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--mysql connector--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.9</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <!--jedis--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2、创建一个novel表,并插入几条数据,如下:
DROP TABLE IF EXISTS `t_novel`; CREATE TABLE `t_novel` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘小说ID‘, `novel_name` varchar(16) DEFAULT NULL COMMENT ‘小说名称‘, `novel_category` varchar(64) DEFAULT NULL COMMENT ‘小说类别‘, `novel_img` varchar(64) DEFAULT NULL COMMENT ‘小说图片‘, `novel_summary` longtext COMMENT ‘小说简介‘, `novel_author` varchar(16) DEFAULT NULL COMMENT ‘小说作者‘, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4; INSERT INTO `t_novel` VALUES (‘5‘, ‘诛仙‘, ‘仙侠‘, ‘/img/zhuxian.jpg‘, ‘该小说以“天地不仁,以万物为刍狗”为主题,讲述了青云山下的普通少年张小凡的成长经历以及与两位奇女子凄美的爱情故事,整部小说构思巧妙、气势恢宏,开启了一个独具魅力的东方仙侠传奇架空世界,情节跌宕起伏,人物性格鲜明,将爱情、亲情、友情与波澜壮阔的正邪搏斗、命运交战汇集在一起,文笔优美,故事生动。它与小说《飘邈之旅》、《小兵传奇》并称为“网络三大奇书”,又被称为“后金庸时代的武侠圣经”。‘, ‘萧鼎‘); INSERT INTO `t_novel` VALUES (‘6‘, ‘英雄志‘, ‘武侠‘, ‘/img/yingxiongzhi.jpg‘, ‘《英雄志》为一虚构中国明朝历史的古典小说,借用明英宗土木堡之变为背景,以复辟为舞台,写尽了英雄们与时代间的相互激荡,造反与政变、背叛与殉道……书中无人不可以为英雄,贩夫走卒、市井小民、娼妇与公主、乞丐与皇帝,莫不可以为英雄。孙晓一次又一次建立英雄的面貌,又一次一次拆解英雄的形象。于穷途末路之时的回眸一笑,是孙晓笔下的安慰与沧桑。‘, ‘孙晓‘);
3、在application.yml文件中配置数据库连接信息和redis连接信息:
spring: ##thymeleaf.# thymeleaf: ##默认前缀 prefix: classpath:/templates/ ##默认后缀 suffix: .html cache: false servlet: content-type: text/html enabled: true encoding: UTF-8 mode: HTML5 ##datasource.# datasource: url: jdbc:mysql://localhost:3306/novel?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource tomcat: max-active: 2 max-wait: 60000 initial-size: 1 min-idle: 1 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: select ‘dtouding‘ test-while-idle: true test-on-borrow: false test-on-return: false ##mybatis.# mybatis: type-aliases-package: com.dtouding.novel.domain configuration: map-underscore-to-camel-case: true default-fetch-size: 100 default-statement-timeout: 30 mapper-locations: classpath:com/dtouding/novel/dao/*.xml redis: host: 127.0.0.1 port: 6379 timeout: 3 password: 123456 poolMaxTotal: 10 poolMaxIdle: 10 poolMaxWait: 3
4、做一个简单的查询小说列表的功能,编写dao、service、controller、html:
public class Novel { private Long id; private String novelName; private String novelCategory; private String novelImg; private String novelSummary; private String novelAuthor; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNovelName() { return novelName; } public void setNovelName(String novelName) { this.novelName = novelName; } public String getNovelCategory() { return novelCategory; } public void setNovelCategory(String novelCategory) { this.novelCategory = novelCategory; } public String getNovelImg() { return novelImg; } public void setNovelImg(String novelImg) { this.novelImg = novelImg; } public String getNovelSummary() { return novelSummary; } public void setNovelSummary(String novelSummary) { this.novelSummary = novelSummary; } public String getNovelAuthor() { return novelAuthor; } public void setNovelAuthor(String novelAuthor) { this.novelAuthor = novelAuthor; } }
@Mapper public interface NovelDao { @Select("select * from t_novel") List<Novel> list(); }
@Service public class NovelService { @Resource private NovelDao novelDao; public List<Novel> list() { return novelDao.list(); } }
@Controller @RequestMapping(value = "/novel") public class NovelController { @Autowired private NovelService novelService; @RequestMapping(value = "/list", method = RequestMethod.GET) public String list(Model model) { List<Novel> list = novelService.list(); model.addAttribute("novelList", list); return "novel_list"; } }
<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>小说列表</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <!-- jquery --> <script type="text/javascript" th:src="@{/js/jquery.min.js}"></script> <!-- bootstrap --> <link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}" /> <script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script> </head> <body> <div class="panel panel-default"> <div class="panel-heading">小说列表</div> <table class="table" id="goodslist"> <tr><td>小说名称</td><td>小说图片</td><td>小说类别</td><td>小说作者</td><td>小说简介</td> <tr th:each="novel : ${novelList}"> <td th:text="${novel.novelName}"></td> <td ><img th:src="@{${novel.novelImg}}" width="100" height="100" /></td> <td th:text="${novel.novelCategory}"></td> <td th:text="${novel.novelAuthor}"></td> <td th:text="${novel.novelSummary}"></td> </tr> </table> </div> </body> </html>
5、通过http://localhost:8080/novel/list,可访问。
二、缓存novel_list页面
1、引入redis依赖:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
2、编写redis配置类和通用的redis工具类:
@Component @ConfigurationProperties(prefix = "redis") public class RedisConfig { private String host; private int port; private int timeout;//秒 private String password; private int poolMaxTotal; private int poolMaxIdle; private int poolMaxWait;//秒 public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getPoolMaxTotal() { return poolMaxTotal; } public void setPoolMaxTotal(int poolMaxTotal) { this.poolMaxTotal = poolMaxTotal; } public int getPoolMaxIdle() { return poolMaxIdle; } public void setPoolMaxIdle(int poolMaxIdle) { this.poolMaxIdle = poolMaxIdle; } public int getPoolMaxWait() { return poolMaxWait; } public void setPoolMaxWait(int poolMaxWait) { this.poolMaxWait = poolMaxWait; } @Bean public JedisPool jedisPoolFactory() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(poolMaxIdle); jedisPoolConfig.setMaxTotal(poolMaxTotal); jedisPoolConfig.setMaxWaitMillis(poolMaxWait * 1000); JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout * 1000, password); return jedisPool; } }
@Service public class RedisService { @Autowired private JedisPool jedisPool; /** * 获取存储对象 * @param key * @param clazz * @param <T> * @return */ public <T> T get(String key, Class<T> clazz) { Jedis jedis = null; try { jedis = jedisPool.getResource(); String str = jedis.get(key); T t = stringToBean(str, clazz); return t; } finally { returnToPool(jedis); } } /** * 设置对象 * @param key * @param expireSeconds * @param value * @param <T> * @return */ public <T> boolean set(String key, int expireSeconds, T value) { Jedis jedis = null; try { jedis = jedisPool.getResource(); String str = beanToString(value); if (null == str) { return false; } if (expireSeconds <= 0) { jedis.set(key, str); } else { jedis.setex(key, expireSeconds, str); } return true; } finally { returnToPool(jedis); } } /** * 判断key是否存在 * */ public <T> boolean exists(String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); return jedis.exists(key); }finally { returnToPool(jedis); } } /** * 增加值 * */ public <T> Long incr(String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); return jedis.incr(key); }finally { returnToPool(jedis); } } /** * 减少值 * */ public <T> Long decr(String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); return jedis.decr(key); }finally { returnToPool(jedis); } } private <T> String beanToString(T value) { if (null == value) { return null; } if (value instanceof Integer || value instanceof Long) { return "" + value; } else if (value instanceof String) { return (String) value; } else { return JSON.toJSONString(value); } } private <T> T stringToBean(String str, Class<T> clazz) { if (StringUtils.isEmpty(str) || clazz==null) { return null; } if (clazz==int.class || clazz==Integer.class) { return (T) Integer.valueOf(str); } else if (clazz == String.class) { return (T) str; } else if (clazz==long.class || clazz==Long.class) { return (T)Long.valueOf(str); } else { return JSON.toJavaObject(JSON.parseObject(str), clazz); } } private void returnToPool(Jedis jedis) { if (null != jedis) { jedis.close(); } } }
3、改写NovelController中的list方法,添加页面缓存逻辑,具体包括:
1)、在list方法上添加@ResponseBody注解,并修改返回类型为text/html,可以避免返回的html再次被渲染,因为缓存在redis中的页面是通过代码手工渲染的。
2)、判断redis中是否有novel_list的页面缓存,若有,则直接返回该缓存页面:
String html = redisService.get(NovelRedisKeys.NOVEL_LIST_PAGE, String.class);
if (!StringUtils.isEmpty(html)) {
return html;
}
3、若缓存中没有,则借助ThymeleafViewResolver去渲染html页面:
html = thymeleafViewResolver.getTemplateEngine().process("novel_list", webContext);
4、将渲染后的页面缓存到redis中:
if (!StringUtils.isEmpty(html)) { //将渲染后的页面缓存到redis中 redisService.set(NovelRedisKeys.NOVEL_LIST_PAGE, 60, html); }
4、修改后的完整代码如下:
@Controller
@RequestMapping(value = "/novel")
public class NovelController {
@Autowired
private NovelService novelService;
@Autowired
private RedisService redisService;
@Autowired
private ThymeleafViewResolver thymeleafViewResolver;
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public String list(HttpServletRequest request, HttpServletResponse response, Model model) {
//判断redis是否有缓存
String html = redisService.get(NovelRedisKeys.NOVEL_LIST_PAGE, String.class);
if (!StringUtils.isEmpty(html)) {
return html;
}
List<Novel> list = novelService.list();
model.addAttribute("novelList", list);
WebContext webContext = new WebContext(request,
response,
request.getServletContext(),
request.getLocale(),
model.asMap());
//渲染页面
html = thymeleafViewResolver.getTemplateEngine().process("novel_list", webContext);
if (!StringUtils.isEmpty(html)) {
//将渲染后的页面缓存到redis中
redisService.set(NovelRedisKeys.NOVEL_LIST_PAGE, 60, html);
}
return html;
}
}
springboot整合spring@cache和redis(代码片段)
转载请注明出处:https://www.cnblogs.com/wenjunwei/p/10779450.htmlspring基于注解的缓存对于缓存声明,spring的缓存提供了一组java注解:@Cacheable:触发缓存写入。@CacheEvict:触发缓存清除。@CachePut:更新缓存(不会影响到方法的运行)。@Caching:重新... 查看详情
springboot2.x集成redis缓存(代码片段)
SpringBoot集成Redis缓存在此章,我们将SpringBoot集成Redis缓存,Redis是一个开源的,基于内存的数据结构存储,可以用作数据库、缓存和消息代理,在本章仅讲解缓存集成。准备工作当前项目工具及环境开发工具IDEA2020.3依赖管理MavenS... 查看详情
intellijidea搭建springboot项目配置事务和redis缓存(代码片段)
IntellijIDEA搭建SpringBoot项目–>配置事务和Redis缓存标签(空格分隔):SpringBootJAVA后台一、开始配置事物类似我们前一篇配置spring-dao的时候,我们可以看见在ssm中配置事物的时候是如下配置的//这个不需要我们在进行... 查看详情
springboot项目之登陆缓存session至redis和cookies
一、将获取的openId(详细步骤见卖家扫码登陆获取openId)作为参数传入到SellerUserController中的login登陆方法。注:此处设置token,是为了取出时先从cookie中拿出token,再回redis校验二、将传入的openId去和数据库中的数据进行匹配三... 查看详情
springboot集成redis实现缓存(代码片段)
前言在此章,我们将SpringBoot集成Redis缓存,Redis是一个开源的,基于内存的数据结构存储,可以用作数据库、缓存和消息代理,在本章仅讲解缓存集成。一键获取源码地址准备工作当前项目工具及环境开发工具... 查看详情
springboot集成redis实现缓存(代码片段)
前言在此章,我们将SpringBoot集成Redis缓存,Redis是一个开源的,基于内存的数据结构存储,可以用作数据库、缓存和消息代理,在本章仅讲解缓存集成。一键获取源码地址准备工作当前项目工具及环境开发工具... 查看详情
springboot与redis缓存管理
...篇博客是前面https://www.cnblogs.com/shijinglu2018/p/12258646.html(SpringBoot与MyBatis/Redis整合)的延续。为什么要使用MyBatis?为什么要使用Redis? 用MyBatis是进行java连接数据库的一种封装和简化,方便去调用,过程中不用过... 查看详情
springboot(二十四)整合redis
...介绍了Redis的安装和Redis的常用操作。今天主要介绍介绍springboot整合Redis。v应用场景现 查看详情
springboot下静态资源处理
...于缓存应用静态资源和webjars下面的资源,默认情况下,springboot会配置/webjars/**对应classpath:/META-INF/resources/webjars/的资源映射并进行缓存配置,static-path-pattern也会对应static-locations进行缓存配置。 查看详情
springboot整合springseesion实现redis缓存
参考技术A使用SpringBoot开发项目时我们经常需要存储Session,因为Session中会存一些用户信息或者登录信息。传统的web服务是将session存储在内存中的,一旦服务挂了,session也就消失了,这时候我们就需要将session存储起来,而Redis就... 查看详情
springboot项目之登出删除缓存session至redis和cookies
一、从request中读出cookies集合,然后封装成map,为的是能够直接通过name得到相应的cookie即get方法publicstaticCookieget(HttpServletRequestrequest,Stringname){Map<String,Cookie>cookieMap=readCookieMap(request);if(cookieMap.contains 查看详情
springboot集成redis分布式锁以及redis缓存
https://blog.csdn.net/qq_26525215/article/details/79182687集成Redis首先在pom.xml中加入需要的redis依赖和缓存依赖<!--引入redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifact 查看详情
springboot集成redis来实现缓存技术方案
概述在我们的日常项目开发过程中缓存是无处不在的,因为它可以极大的提高系统的访问速度,关于缓存的框架也种类繁多,今天主要介绍的是使用现在非常流行的NoSQL数据库(Redis)来实现我们的缓存需求。 Redis简介Redis是... 查看详情
springboot
...业所使用,但通常在企业中我们会将其作为缓存来使用。SpringBoot对Redis也提供了自动配置的支持,接下来本小节将讲解如何在SpringBoot项目中使用Redis。添加Redis缓存1.添加Redis起步依赖<dependency 查看详情
springboot整合redis实现缓存
...纲一、缓存的应用场景二、更新缓存的策略三、运行 springboot-mybatis-redis 工程案例四、springboot-mybatis-redis 工程代码配置详解 运行环境:MacOS10.12.xJDK8+Redis3.2.8SpringBoot1.5.1.RELEASE 一、缓存的应用场景什么是缓存?... 查看详情
springboot开启redis缓存(代码片段)
...之前不是说过Redis可以当作缓存用嘛现在我们就配置一下SpringBoot使用Redis的缓存Redis缓存为什么用Redis作缓存用redis做缓存,是因为re 查看详情
springboot下的redis缓存实战
最近在做的一个系统涉及到基础数据的频繁调用,大量的网络开销和数据读写给系统带来了极大的性能压力,我们决定引入缓存机制来缓解系统压力。什么是缓存提起缓存机制,大概10个程序员总有5种不同的解释吧(姑且认为只... 查看详情
springbootsad整合ehcache做缓存处理
...:什么是EhCache?它和redis、membercache比较有什么优势?和SpringBoot怎么整合?实现机制?有哪些坑?EhCache是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认CacheProvider。Ehcache是一种广泛使用的开源Java分布式... 查看详情