springboot整合Redis


1、新建项目,并添加所需要的jar包:

分析:

(1)在springboot中已经继承了Redis,就是说一旦我们启动项目,它是可以根据properties配置文件中的配置信息自动创建Jedis对象的

(2)我们想要的是自己定义配置信息的名称,再根据自己定义的配置信息配置一个有关配置信息的类,最后根据这个类来创建一个我们想要的Jedis对象。

(3)springboot项目在加载的时候,由于springboot无法从配置文件中获取想要的信息,所以不会自动创建属于springboot的Jedis对象,而是根据我们自定义

的配置信息进行创建一个Jedis对象

(4)在使用Jedis对象的时候,我们给我们的Jedis对象加上了@Bean注解,在要使用Jedis的类中使用@Autowired注解,所以spring中的BeanFactory工厂自动创建对象,

,且是单例的;此处直接在@Autowire的注解下注入的是(3)中的对象,也就是根据我们自定义信息创建的对象

springboot-parent
spring-boot-web-start
mysql驱动包
druid(德鲁伊)连接池
springboot和mybatis的整合包
thymeleaf模板jar包
thymeleaf忽略语法的jar包

springboot和redis的整合包
jedis的包:
jedis就是通过Java实现对redis集群的增删改查,就是操作redis数据库
redis最终只支持5种数据格式:
String类型是最常用的
如果需要把User对象存入到redis中===>需要把User对象转换为json字符串===>再把字符串存入到redis中
json的jar包
configuration-processer包(为了使用其中的@ConfigurationProperties()注解,在下文中的RedisProperties类中所使用)


 1 <!-- springboot的jar -->
 2 <parent>
 3     <groupId>org.springframework.boot</groupId>
 4     <artifactId>spring-boot-starter-parent</artifactId>
 5     <version>1.5.10.RELEASE</version>
 6 </parent>
 7 
 8 <dependencies>
 9     <!--
10         springboot-starter-web
11     -->
12     <dependency>
13         <groupId>org.springframework.boot</groupId>
14         <artifactId>spring-boot-starter-web</artifactId>
15     </dependency>
16     <!--
17         springboot-mybatis整合包
18     -->
19     <dependency>
20         <groupId>org.mybatis.spring.boot</groupId>
21         <artifactId>mybatis-spring-boot-starter</artifactId>
22         <version>1.3.0</version>
23     </dependency>
24     <!--
25         mysql的驱动包
26     -->
27     <dependency>
28         <groupId>mysql</groupId>
29         <artifactId>mysql-connector-java</artifactId>
30         <version>5.1.38</version>
31     </dependency>
32     <!--
33         druid连接池
34     -->
35     <dependency>
36         <groupId>com.alibaba</groupId>
37         <artifactId>druid</artifactId>
38         <version>1.1.10</version>
39     </dependency>
40     <!--
41         html的thymeleaf模板
42     -->
43     <dependency>
44         <groupId>org.springframework.boot</groupId>
45         <artifactId>spring-boot-starter-thymeleaf</artifactId>
46     </dependency>
47     <!--
48         redis的jar包以及jedis的jar包
49     -->
50     <dependency>
51         <groupId>redis.clients</groupId>
52         <artifactId>jedis</artifactId>
53         <version>2.9.0</version>
54     </dependency>
55     <!--
56         redis和springboot的整合包
57     -->
58     <dependency>
59         <groupId>org.springframework.data</groupId>
60         <artifactId>spring-data-redis</artifactId>
61     </dependency>
62     <!--
63         fastjson包
64     -->
65     <dependency>
66         <groupId>com.fasterxml.jackson.core</groupId>
67         <artifactId>jackson-databind</artifactId>
68         <version>2.8.1</version>
69     </dependency>
70     <!--
71         添加springboot的进程jar包
72             里面包含了properties文件的读取(其实就是包含了@ConfigurationProperties()注解)
73     -->
74     <dependency>
75         <groupId>org.springframework.boot</groupId>
76         <artifactId>spring-boot-configuration-processor</artifactId>
77         <optional>true</optional>
78     </dependency>
79 
80     <dependency>
81         <groupId>net.sourceforge.nekohtml</groupId>
82         <artifactId>nekohtml</artifactId>
83         <version>1.9.21</version>
84     </dependency>
85 </dependencies>
2、在springboot框架中,已经整合了Redis,可以直接使用。
  此处根据springboot整合Redis的源码来实现自己的Redis,原因:springboot自带的Redis已经非常完善,一旦出错,很难找到错误的地方
  例如:当服务器的IP变动,端口号的问题等,可以通过在自己的配置类中打断点等方法判断错误的地方

3、在config中配置application.properties配置信息:
 1 server.port=8081
 2 server.context-path=/
 3 
 4 spring.datasource.driver-class-name=com.mysql.jdbc.Driver
 5 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?userSSL=false
 6 spring.datasource.username=root
 7 spring.datasource.password=123456
 8 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
 9 
10 mybatis.type-aliases-package=com.aaa.liu.redis.model
11 mybatis.mapper-locations=classpath:mapper/*Mapper.xml
12 
13 #开始自定义配置
14 # 配置redis集群的端口号和IP
15 spring.redis.nodes=192.168.134.130:6380,192.168.134.130:6381,192.168.134.130:6382,192.168.134.130:6383,192.168.134.130:6384,192.168.134.130:6385
16 # 配置了redistribution的最大连接数
17 spring.redis.maxAttempts=1000
18 # 配置了redis最大超时时间
19 spring.redis.commandTimeout=500000
20 # 配置了redis的失效时间
21 spring.redis.expire=1000
22 
23 # 定义图书的key值
24 book_key=bookKey1
25 
26 # 配置thymeleaf模板(不配置也可以,直接使用)
27 # 配置thymeleaf缓存:默认值true,需要手动修改为false
28 spring.thymeleaf.cache=false
29 # 配置不严谨的html
30 spring.thymeleaf.mode=LEGACYHTML5

4、在Java中新建一个config包,在包中新建RedisPro类,将刚刚application.properties的配置信息引入此类中:
 1 package com.aaa.liu.redis.config;
 2 
 3 import org.springframework.boot.context.properties.ConfigurationProperties;
 4 import org.springframework.stereotype.Component;
 5 
 6 /**
 7  * @Author 刘其佳
 8  * @DateTime 2019/8/28 19:49
 9  * @Project_Name SpringBootRedis
10  *
11  *  1、@Component:把RedisProperties作为spring的一个组件
12  *  作用是 把Redis所需要配置和连接信息封装进RedisProperties类中
13  *   该组件可拆分
14  *   配置该组件类的作用是从application.properties文件中读取自定义的信息
15  *  2、@ConfigurationPrperties:作用是把application.properties中的属性读进RedisProperties类中
16  *      当使用该注解的时候必须要使用spring-boot-configuration-processor的jar包
17  *   prefx="spring.redis";作用是选中在application.properties文件中的 属性名 进行对用(必须一致)
18  *  3、本来按理说在此类中定义属性过后,只有get方法,不应该有set方法,因为不能别其他类修改
19  *      但若是真的不放入set方法的话,在使用此类时,无法传递进来参数(属性是私有的)
20  *      !!!所以最终还是要有set方法,只要注意其他的类在调用RedisProperties类中的属性的时候,
21  *              一定不能修改!!!
22  *
23  */
24 
25 @Component
26 /**
27  * 所有以spring.redis开头的配置信息
28  */
29 @ConfigurationProperties(prefix = "spring.redis")
30 public class RedisProperties {
31 
32     /**
33      * 下列属性对应application.properties文件中的自定义的配置:
34      * redis集群的节点
35      * 最大连接数
36      * 最大超时时间
37      * 失效时间
38      */
39     private String nodes;
40     private String maxAttempts;
41     private String commandTimeout;
42     private String expire;
43 
44     public String getNodes() {
45         return nodes;
46     }
47 
48     public void setNodes(String nodes) {
49         this.nodes = nodes;
50     }
51 
52     public String getMaxAttempts() {
53         return maxAttempts;
54     }
55 
56     public void setMaxAttempts(String maxAttempts) {
57         this.maxAttempts = maxAttempts;
58     }
59 
60     public String getCommandTimeout() {
61         return commandTimeout;
62     }
63 
64     public void setCommandTimeout(String commandTimeout) {
65         this.commandTimeout = commandTimeout;
66     }
67 
68     public String getExpire() {
69         return expire;
70     }
71 
72     public void setExpire(String expire) {
73         this.expire = expire;
74     }
75 }

5、仍然在config包中新建类:RedisConfiguration,将RedisProperties类注入进来:
 1 package com.aaa.liu.redis.config;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 6 import org.springframework.context.annotation.Bean;
 7 import org.springframework.context.annotation.Configuration;
 8 import redis.clients.jedis.HostAndPort;
 9 import redis.clients.jedis.JedisCluster;
10 
11 import java.util.HashSet;
12 import java.util.Set;
13 
14 /**
15  * @Author 刘其佳
16  * @DateTime 2019/8/28 20:34
17  * @Project_Name SpringBootRedis
18  */
19 
20 /**
21  * 此处加上注解@Configuration或者@SpringBootApplication
22  *
23  * 因为在SpringBootApplication是一个组合注解
24  * 进入其中可以看到@SpringBootConfiguration  @EnableAutoConfiguration  @ComponentScan
25  * 再次进入@SpringBootConfiguration可以看到@Configuration
26  * 因此SpringBootConfiguration是EnableAutoConfiguration、ComponentScan和Configuration的组合注解
27  *
28  * 在较低版本的SpringBoot中:
29  * @Configuration是启用基于Java的配置
30  * @ComponentScan是启用组件扫描。
31  * @EnableAutoConfiguration是启用SpringBoot的自动配置功能
32  * 引用自:https://blog.csdn.net/qq_37939251/article/details/83058065
33  */
34 
35 /**
36  * 通过SpringBootApplication/Configuration注解将RedisConfiguration类标识为springboot的配置类
37  *
38  * 使用Jedis的jar包操作Redis集群数据库
39  *  1、Redis:标识了Redis是单节点模式(只有一台Redis)
40  *      源码中:protected static class RedisConfiguration {}
41  *  2、RedisCluster:标识了Redis的Redis集群模式
42  *      源码中:protected final RedisClusterConfiguration getClusterConfiguration() {}
43  *  本次只配置集群版的Redis
44  *
45  * @Bean:  相当于applicationContext.xml中的<bean id=""> class="JedisCluster.class"</bean>
46  *
47  * @EnableConfirgurationProperties({RedisProperties.class})是源码中使用的注解
48  * 在本类中我们使用@Autowired来代替掉
49  */
50 @SpringBootApplication
51 public class RedisConfiguration {
52     @Autowired
53     private RedisProperties redisProperties;
54 
55     @Bean
56     public JedisCluster getJedisCluster(){
57         //1、获取到application.properties文件中的Redis集群的节点信息
58         String nodes=redisProperties.getNodes();
59         //2、使用split来将多个node进行拆分
60         String[] nodeArray = nodes.split(",");
61         //3、创建一个set集合,以HostAndPort对象作为泛型
62         Set<HostAndPort> hostAndPortSet=new HashSet<HostAndPort>();
63         //4、遍历刚刚得到的节点数组
64         for (String node : nodeArray) {
65             //5、继续使用split将node拆分成IP和端口号(host、port)
66             String[] hostAndPortArray = node.split(":");
67             //6、创建HostAndPort对象,并将得到的IP和端口号作为构造方法的参数传入
68             HostAndPort hostAndPort=new HostAndPort(hostAndPortArray[0],Integer.parseInt(hostAndPortArray[1]));
69             //7、把每一个HostAndPort对象装进Set集合中
70             hostAndPortSet.add(hostAndPort);
71         }
72         //8、返回一个JedisCluster对象
73         return new JedisCluster(hostAndPortSet,Integer.parseInt(redisProperties.getCommandTimeout()),Integer.parseInt(redisProperties.getMaxAttempts()));
74     }
75 }

6、在Service层中新建RedisService
 1 package com.aaa.liu.redis.service;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.stereotype.Service;
 5 import redis.clients.jedis.JedisCluster;
 6 
 7 /**
 8  * @Author 刘其佳
 9  * @DateTime 2019/8/28 21:39
10  * @Project_Name SpringBootRedis
11  * 在以前的UserService中注入UserMapper,对数据库进行操作
12  * 现在的RedisService中注入JedisCluster,对Redis数据库进行操作
13  */
14 @Service
15 public class RedisService {
16 
17     @Autowired
18     private JedisCluster jedisCluster;
19 
20     /**
21      * 在Redis数据库中的增删查(没有修改)分别为:
22      * set del get
23      * 在下面分别定义
24      */
25 
26     /**
27     * @author 刘其佳
28     * @description
29      *      向Redis数据库中存入String类型的数据
30      *      set的时候返回值是"ok"
31     * @param * param *:key
32     * @date 2019/8/28
33     * @return java.lang.String
34     * @throws
35     */
36     public String set(String key,String value){
37         return jedisCluster.set(key,value);
38     }
39 
40     /**
41     * @author 刘其佳
42     * @description
43      *      从Redis数据库中通过key取出数据
44     * @param * param *:key
45     * @date 2019/8/28
46     * @return java.lang.String
47     * @throws
48     */
49     public String get(String key){
50         return jedisCluster.get(key);
51     }
52 
53     /**
54     * @author 刘其佳
55     * @description
56      *      1、在此处的参数key是一个可变参
57      *      2、在Redis中删除的返回值是Integer类型
58      *          在Java中被转成了Long类型
59     * @param * param *:key
60     * @date 2019/8/28
61     * @return java.lang.Long
62     * @throws
63     */
64     public Long del(String key){
65         return jedisCluster.del(key);
66     }
67 
68     /**
69     * @author 刘其佳
70     * @description
71      *      通过key设置失效时间(单位是秒)
72     * @param * param *:key
73     * param *:seconds
74     * @date 2019/8/28
75     * @return java.lang.Long
76     * @throws
77     */
78     public Long expire(String key,Integer seconds){
79         return jedisCluster.expire(key,seconds);
80     }
81 }
查询图书信息的service:
 1 /**
 2     * @author 刘其佳
 3     * @description
 4      *  注意:service层不允许注入service
 5      *  因为在applicationContext.xml中配置事务后,事务都是在service中的
 6      *  当service注入另一个事务时,会造成事务串线(如果线程是并行的没有问题)
 7      *  applicationContext.xml配置文件中进行注解扫描:
 8      *  controller:必须要在springmvc中进行扫描
 9      *  service:必须要在spring中进行扫描
10      *
11     * @param * param *:null
12     * @date 2019/8/28
13     * @return
14     * @throws
15     */
16     public Map<String ,Object> selectAllBooks(RedisService redisService){
17         /**
18          * 1、从Redis中查询所有图书信息
19          * 2、判断从Redis中是否查询出数据
20          * 3、没有查询到的话再从数据库中查询数据
21          * 4、判断是否从数据库中查询到数据
22          * 5、若查询到数据,则返回结果,并且将数据放入Redis中
23          * 6、利用try...catch判断是否存入Redis中
24          * 7、若没有存入Redis中,要做出处理,此处为再次往Redis中存放一次
25          */
26         Map<String,Object> resultMap=new HashMap<String, Object>();
27         //1、
28         String bookString=redisService.get(bookKey);
29         //2、
30         if(null==bookString||"".equals(bookString)){
31             //3、
32             List<Book> bookList = bookMapper.selectAll();
33             //4、
34             if(bookList.size()>0){
35                 //6、
36                 try {
37                     //5、
38                     redisService.set(bookKey, JSONUtil.toJsonString(bookList));
39                     //将从数据库中查询到的数据存入resultMap中
40                     resultMap.put(StatusEnum.SUCCESS.getCodeName(), StatusEnum.SUCCESS.getCode());
41                     resultMap.put(StatusEnum.SUCCESS.getResultName(),bookList);
42                 } catch (Exception e) {
43                     //7、
44                     redisService.set(bookKey, JSONUtil.toJsonString(bookList));
45                     //如果还是没有将数据放入Redis中,此时我们将resultMap里面的code换成404,但是用户还是能够查询到数据
46                     resultMap.put(StatusEnum.FAILED.getCodeName(),StatusEnum.FAILED.getCode());
47                     resultMap.put(StatusEnum.FAILED.getResultName(),bookList);
48                     e.printStackTrace();
49                     return resultMap;
50                 }
51             }else {
52                 //说明数据库中没有数据,放入500
53                 resultMap.put(StatusEnum.ERROR.getCodeName(),StatusEnum.ERROR.getCode());
54             }
55         }else {
56             //!!!可能会存在Redis中的数据与数据库中的数据不一致的情况
57             //所以此处从Redis中获取到数据后与从数据库中查询到的数据做出一个判断(长度是否一致)
58             List<Book> bookList = JSONUtil.toList(bookString, Book.class);
59             List<Book> bookList1 = bookMapper.selectAll();
60             if(bookList.size()==bookList1.size()){
61                 //将查询从Redis中查询到的数据存入到resultMap
62                 //因为是从Redis中查询到的数据,此处给转回正常状态下的List集合类型的数据,
63                 //正好从数据库中查询到的数据也是List,保持一致
64                 resultMap.put(StatusEnum.SUCCESS.getCodeName(), StatusEnum.SUCCESS.getCode());
65                 resultMap.put(StatusEnum.SUCCESS.getResultName(),JSONUtil.toList(bookString,Book.class));
66         }else {
67                 //发现数据不一致时,丢弃Redis中的数据,并将数据库中的数据存入Redis中
68                 redisService.set(bookKey,JSONUtil.toJsonString(bookList));
69                 resultMap.put(StatusEnum.SUCCESS.getCodeName(), StatusEnum.SUCCESS.getCode());
70                 resultMap.put(StatusEnum.SUCCESS.getResultName(),bookList1);
71             }
72 
73     }
74         return resultMap;
75     }
7、控制层的BookController:
 1 /**
 2  * @Author 刘其佳
 3  * @DateTime 2019/8/28 22:25
 4  * @Project_Name SpringBootRedis
 5  */
 6 @RestController
 7 public class BookController {
 8 
 9     @Autowired
10     private BookService bookService;
11     @Autowired
12     private RedisService redisService;
13 
14     @RequestMapping("/")
15     public List<Book> selectAllBooks(){
16         Map<String, Object> resultMap = bookService.selectAllBooks(redisService);
17         List<Book> bookList=null;
18         //如果查询到的resultMap中的code值为200,说明直接从Redis中查到数据了
19         if(StatusEnum.SUCCESS.getCode().equals((resultMap.get(StatusEnum.SUCCESS.getCodeName())))){
20             bookList= (List<Book>) resultMap.get(StatusEnum.SUCCESS.getResultName());
21             System.out.println("everything is ok!");
22         }else {
23             //如果code值不为200,则有两种情况:404即Redis错误; 500即mysql错误
24             if(StatusEnum.ERROR.getCode().equals((resultMap.get(StatusEnum.SUCCESS.getCodeName())))){
25                 //1、result到最后也没存进去值,数据库都出现了问题
26                 System.out.println("数据库出现了问题");
27             }else{
28                 //2、result有值,Redis出现了问题,但从数据库查询了数据
29                 bookList= (List<Book>) resultMap.get(StatusEnum.SUCCESS.getResultName());
30                 System.out.println("redis出现了问题");
31             }
32         }
33         return bookList;
34     }
在本次项目中为了避免魔法值问题,使用了枚举:
新建一个包statuscode ,在其中新建类:StatusEnum
 1 package com.aaa.liu.redis.statuscode;
 2 
 3 /**
 4  * @Author 刘其佳
 5  * @DateTime 2019/8/29 20:08
 6  * @Project_Name SpringBootRedis
 7  */
 8 public enum StatusEnum {
 9     SUCCESS(200,"操作失败","code","result"),
10     FAILED(404,"操作成功","code","result"),
11     ERROR(500,"操作失败","code","result");
12 
13     private Integer code;
14     private String msg;
15     private String codeName;
16     private String resultName;
17 
18     /**
19      * 给枚举赋值
20      * @param code
21      * @param msg
22      * @param codeName
23      */
24      StatusEnum(Integer code, String msg, String codeName,String resultName) {
25         this.code = code;
26         this.msg = msg;
27         this.codeName = codeName;
28         this.resultName=resultName;
29     }
30 
31     public Integer getCode() {
32         return code;
33     }
34 
35     public void setCode(Integer code) {
36         this.code = code;
37     }
38 
39     public String getMsg() {
40         return msg;
41     }
42 
43     public void setMsg(String msg) {
44         this.msg = msg;
45     }
46 
47     public String getCodeName() {
48         return codeName;
49     }
50 
51     public void setCodeName(String codeName) {
52         this.codeName = codeName;
53     }
54 
55     public String getResultName() {
56         return resultName;
57     }
58 
59     public void setResultName(String resultName) {
60         this.resultName = resultName;
61     }
62 }

mapper中的BookMapper查询所有图书:
 1 /**
 2  * @Author 刘其佳
 3  * @DateTime 2019/8/27 20:19
 4  * @Project_Name springbootshiro
 5  */
 6 @Mapper
 7 public interface BookMapper {
 8     //查询所有书籍
 9     @Select("select id,book_name bookName,book_price bookPrice from book ")
10     List<Book> selectAll();
11 }
其中的注意事项已在代码中写出!



此处插入一个关于ajax的用法:
 1 <!DOCTYPE html>
 2 <html xmlns="http://www.w3.org/1999/xhtml"
 3       xmlns:th="http://www.thymeleaf.org"
 4       xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
 5 <head>
 6     <meta charset="UTF-8">
 7     <title>Index</title>
 8     <script type="text/javascript" src="/jquery-3.2.1.min.js"></script>
 9 </head>
10 
11 <script>
12     /**
13      * 1.页面自动加载完成后,函数自动执行,查询出数据并拼接处table表单,将数据放入其中
14      * */
15     $(document).ready( selectBookTest());
16 
17     function selectBookTest(){
18         $.ajax({
19             type:"POST",
20             url:"selectBooks",
21             dataType:"JSON",
22             success:function (msg) {
23                 var tr="";
24                 for(var i=0;i<msg.length;i++){
25                     tr+="<tr><td>"+msg[i].id+"</td>";
26                     tr+="<td>"+msg[i].bookName+"</td>";
27                     tr+="<td>"+msg[i].bookPrice+"</td></tr>";
28                     //不能直接使用下面的方式,否则会只有一行数据;要使用+=
29                     // tr="<tr><td>"+msg[i].id+"</td><td>"+msg[i].bookName+"</td><td>"+msg[i].bookPrice+"</td></tr>"
30                 }
31                 tr+="<tr><td onclick='testBookAdd()'>添加</td></tr>";
32                 $("#test_Book").append("<table border='1px solid black'>" +
33                     "<thead>" +
34                     "<tr >" +
35                     "<th>图书编号</th>" +
36                     "<th>图书名称</th>" +
37                     "<th>图书价格</th>" +
38                     "</tr>" +
39                     tr +
40                     "</thead>" +
41                     "</table>")
42             }
43 
44         })
45     }
46 
47     /**
48      * 2.点击添加按钮,将查询所得的数据清空,并拼接出新的form表单用以输入要添加的数据
49      */
50     function testBookAdd() {
51         $("#test_Book").empty();
52         $("#test_Book").append("<form id='formBook'><table border='1px solid red'>" +
53             "<tr><td>图书名称:</td><td><input type='text' name='bookName'/></td></tr>" +
54             "<tr><td>图书价格:</td><td><input type='text' name='bookPrice'/></td></tr>"+
55             "<tr><td><input type='button' value='submit' onclick='bookAdd()'/></td></tr>"+
56             "</table></form>")
57     }
58 
59     /**
60      * 3.输入完数据,使用ajax调用添加方法添加
61      */
62     function bookAdd() {
63         $.ajax({
64             type:"POST",
65             url:"testAddBook",
66             data:$("#formBook").serialize(),
67             dataType:"json",
68             success:function (msg) {
69                 if(null==msg){
70                     alert("添加失败")
71                 }else{
72                     $("#test_Book").empty();
73                     selectBookTest();
74                 }
75             }
76         })
77     }
78 
79 </script>
80 <body>
81 
82 <div id="test_Book"></div>
83 </body>
84 
85 
86 </html>





















乾坤未定,你我皆是黑马
原文地址:https://www.cnblogs.com/liuqijia/p/11433542.html