springboot和redis处理页面缓存

news/2024/7/4 19:31:25

      页面缓存是应对高并发的一个比较常见的方案,当请求页面的时候,会先查询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>
pom.xml

           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', '《英雄志》为一虚构中国明朝历史的古典小说,借用明英宗土木堡之变为背景,以复辟为舞台,写尽了英雄们与时代间的相互激荡,造反与政变、背叛与殉道……书中无人不可以为英雄,贩夫走卒、市井小民、娼妇与公主、乞丐与皇帝,莫不可以为英雄。孙晓一次又一次建立英雄的面貌,又一次一次拆解英雄的形象。于穷途末路之时的回眸一笑,是孙晓笔下的安慰与沧桑。', '孙晓');
t_novel

           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
application.yml

           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;
    }
}
Novel
@Mapper
public interface NovelDao {
    
    @Select("select * from t_novel")
    List<Novel> list();
    
}
NovelDao
@Service
public class NovelService {
    
    @Resource
    private NovelDao novelDao;
    
    public List<Novel> list() {
        return novelDao.list();
    }
}
NovelService
@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";
    }
}
NovelController
<!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>
novel_list

           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;
    }
}
RedisConfig
@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();
        }
    }
}
RedisService

         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;
}
}

 

转载于:https://www.cnblogs.com/gujianzhe/p/10229210.html


http://www.niftyadmin.cn/n/4821463.html

相关文章

Ribbon 框架简介及搭建

Ribbon简介1. 负载均衡框架&#xff0c;支持可插拔式的负载均衡规则2. 支持多种协议&#xff0c;如HTTP、UDP等3. 提供负载均衡客户端Ribbon子模块1. ribbon-core&#xff08;ribbon的核心&#xff0c;主要包含负载均衡器、负载均衡接口、客户端接口、内置负载均衡实现API&…

硬盘分区表详解

硬盘主引导扇区 硬盘主引导记录&#xff08;MBR&#xff09; 硬盘分区表&#xff08;DPT&#xff09; -------------------------------------------------------------- 物理位置&#xff1a;0面0道1扇区&#xff08;clindyer 0, side 0, sector 1) 大小&#xff1a; 512字节…

(原創) 數學就是loose coupling的極致表現 (OO)

Abstract我們已經在STL中看到loose coupling的威力了&#xff0c;但我今天發現&#xff0c;數學更是loose coupling最佳的詮釋。Introduction從小學到大學&#xff0c;哪些科目用處最大?英文和數學&#xff0c;英文不難理解&#xff0c;因為他是語言&#xff0c;任何知識都以英…

Visual Studio 2010 and the .NET Framework 4.0!

下一站:Visual Studio 2010 和 .NET Framework 4.0 REDMOND, Wash. — Sept. 29, 2008 — Microsoft Corp. today provided the first look at the next version of its developer tools and platform, which will be named Visual Studio 2010 and the .NET Framework 4.0. M…

CentOS基本的命令与快捷建

由于我的计算机在安装linux系统时&#xff0c;计算机出现了问题&#xff0c;并没有安装ubuntu而是安装的centos。虽然两者属于linux的不同版本&#xff0c;但是在具体的操作上大同小异。在学习linux的各种指令和快捷键的时候&#xff0c;难免会遇到各种各样的问题&#xff0c;以…

Delphi使用zlib来压缩文件

使用时&#xff0c;需要Zlib.pas和 Zlibconst.pas两个单元文件&#xff0c;这两个文件保存在 Delphi 5.0安装光盘上 InfoExtrasZlib目录下&#xff0c;此外&#xff0c;在 InfoExtrasZlibObj目录中还保存了 Zlib.pas单元引用的 Obj文件&#xff0c;把这个目录拷贝到delphi的lib…

Linq 动态查询库

【原文地址】Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library) 【原文发表日期】 Monday, January 07, 2008 11:02 PM LINQ &#xff08;语言级集成查询&#xff09;是VS 2008 和 .NET 3.5中提供的一个新特性。LINQ使得数据查询的概念成为.NET中的一等编程概念&a…

CentOS下screen 命令详解

一、背景 系统管理员经常需要SSH 或者telent 远程登录到Linux 服务器&#xff0c;经常运行一些需要很长时间才能完成的任务&#xff0c;比如系统备份、ftp 传输等等。通常情况下我们都是为每一个这样的任务开一个远程终端窗口&#xff0c;因为它们执行的时间太长了。必须等待它…