目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

SpringBoot整合Redis

Redis 远程连接配置

1[root@localhost ~]# vim /etc/redis/6379.conf 
2bind 0.0.0.0 
3port 6379 
4daemonize yes
5protected-mode no
6requirepass 123

SpringBoot 整合 Redis

引入依赖

spring-boot-starter-data-redis 包含了 jedis,封装了一类 SpringBoot 与 Redis 的连接工具。

1<dependency
2		<groupId>org.springframework.boot</groupId>
3		<artifactId>spring-boot-starter-data-redis</artifactId>
4	</dependency>

编写 redisTemplate 的配置类,设置 redisConnectFactory

 1import org.springframework.data.redis.connection.RedisConnectionFactory;
 2import org.springframework.data.redis.core.RedisTemplate;
 3
 4@Configuration
 5public class RedisConfig {
 6    @Bean
 7    public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory factory){
 8        RedisTemplate<String,String> redisTemplate = new RedisTemplate<>();
 9        redisTemplate.setConnectionFactory(factory);
10        return redisTemplate;
11    }
12}

引入配置文件 application.properties

1spring.redis.database=0
2spring.redis.host=192.168.31.220
3spring.redis.port=6379
4spring.redis.password=

编写测试类

 1import org.springframework.data.redis.core.RedisTemplate;
 2import org.springframework.web.bind.annotation.RequestMapping;
 3import org.springframework.web.bind.annotation.ResponseBody;
 4import org.springframework.web.bind.annotation.RestController;
 5import javax.annotation.Resource;
 6
 7@RestController
 8public class RedisController {
 9    @Resource
10    private RedisTemplate redisTemplate;
11
12    @RequestMapping("/redis/setAndGet")
13    @ResponseBody
14    public String setAndGetValue(String name,String value){
15	//使用Redis设置键值对
16        redisTemplate.opsForValue().set(name,value);
17	//从Redis中获取指定的键值对
18        return (String) redisTemplate.opsForValue().get(name);
19    }
20}

测试结果

1http://localhost:8080/redis/setAndGet?name=soulboy&value=123
2123

封装 redisTemplate API

  • opsForValue 操作 String,Key,Value,包含过期 key,setBit 位操作等
  • opsForSet 操作 set
  • opsForHash 操作 hash
  • opsForZset 操作 SortSet
  • opsForList 操作 list 队列

封装 redisTemplate

  1package com.xdclass.mobile.xdclassmobileredis;
  2
  3import org.springframework.beans.factory.annotation.Autowired;
  4import org.springframework.data.redis.core.*;
  5import org.springframework.stereotype.Service;
  6
  7import java.io.Serializable;
  8import java.util.List;
  9import java.util.Set;
 10import java.util.concurrent.TimeUnit;
 11
 12/**
 13 * Created by Administrator on 2018/10/6.
 14 */
 15@Service
 16public class RedisService {
 17
 18    @Autowired
 19    private RedisTemplate redisTemplate;
 20
 21    private static double size = Math.pow(2, 32);
 22
 23
 24    /**
 25     * 写入缓存
 26     *
 27     * @param key
 28     * @param offset   位 8Bit=1Byte
 29     * @return
 30     */
 31    public boolean setBit(String key, long offset, boolean isShow) {
 32        boolean result = false;
 33        try {
 34            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
 35            operations.setBit(key, offset, isShow);
 36            result = true;
 37        } catch (Exception e) {
 38            e.printStackTrace();
 39        }
 40        return result;
 41    }
 42
 43    /**
 44     * 写入缓存
 45     *
 46     * @param key
 47     * @param offset
 48     * @return
 49     */
 50    public boolean getBit(String key, long offset) {
 51        boolean result = false;
 52        try {
 53            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
 54            result = operations.getBit(key, offset);
 55        } catch (Exception e) {
 56            e.printStackTrace();
 57        }
 58        return result;
 59    }
 60
 61
 62    /**
 63     * 写入缓存
 64     *
 65     * @param key
 66     * @param value
 67     * @return
 68     */
 69    public boolean set(final String key, Object value) {
 70        boolean result = false;
 71        try {
 72            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
 73            operations.set(key, value);
 74            result = true;
 75        } catch (Exception e) {
 76            e.printStackTrace();
 77        }
 78        return result;
 79    }
 80
 81    /**
 82     * 写入缓存设置时效时间
 83     *
 84     * @param key
 85     * @param value
 86     * @return
 87     */
 88    public boolean set(final String key, Object value, Long expireTime) {
 89        boolean result = false;
 90        try {
 91            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
 92            operations.set(key, value);
 93            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
 94            result = true;
 95        } catch (Exception e) {
 96            e.printStackTrace();
 97        }
 98        return result;
 99    }
100
101    /**
102     * 批量删除对应的value
103     *
104     * @param keys
105     */
106    public void remove(final String... keys) {
107        for (String key : keys) {
108            remove(key);
109        }
110    }
111
112
113    /**
114     * 删除对应的value
115     *
116     * @param key
117     */
118    public void remove(final String key) {
119        if (exists(key)) {
120            redisTemplate.delete(key);
121        }
122    }
123
124    /**
125     * 判断缓存中是否有对应的value
126     *
127     * @param key
128     * @return
129     */
130    public boolean exists(final String key) {
131        return redisTemplate.hasKey(key);
132    }
133
134    /**
135     * 读取缓存
136     *
137     * @param key
138     * @return
139     */
140    public Object get(final String key) {
141        Object result = null;
142        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
143        result = operations.get(key);
144        return result;
145    }
146
147    /**
148     * 哈希 添加
149     *
150     * @param key
151     * @param hashKey
152     * @param value
153     */
154    public void hmSet(String key, Object hashKey, Object value) {
155        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
156        hash.put(key, hashKey, value);
157    }
158
159    /**
160     * 哈希获取数据
161     *
162     * @param key
163     * @param hashKey
164     * @return
165     */
166    public Object hmGet(String key, Object hashKey) {
167        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
168        return hash.get(key, hashKey);
169    }
170
171    /**
172     * 列表添加
173     *
174     * @param k
175     * @param v
176     */
177    public void lPush(String k, Object v) {
178        ListOperations<String, Object> list = redisTemplate.opsForList();
179        list.rightPush(k, v);
180    }
181
182    /**
183     * 列表获取
184     *
185     * @param k
186     * @param l
187     * @param l1
188     * @return
189     */
190    public List<Object> lRange(String k, long l, long l1) {
191        ListOperations<String, Object> list = redisTemplate.opsForList();
192        return list.range(k, l, l1);
193    }
194
195    /**
196     * 集合添加
197     *
198     * @param key
199     * @param value
200     */
201    public void add(String key, Object value) {
202        SetOperations<String, Object> set = redisTemplate.opsForSet();
203        set.add(key, value);
204    }
205
206    /**
207     * 集合获取
208     *
209     * @param key
210     * @return
211     */
212    public Set<Object> setMembers(String key) {
213        SetOperations<String, Object> set = redisTemplate.opsForSet();
214        return set.members(key);
215    }
216
217    /**
218     * 有序集合添加
219     *
220     * @param key
221     * @param value
222     * @param scoure
223     */
224    public void zAdd(String key, Object value, double scoure) {
225        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
226        zset.add(key, value, scoure);
227    }
228
229    /**
230     * 有序集合获取
231     *
232     * @param key
233     * @param scoure
234     * @param scoure1
235     * @return
236     */
237    public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
238        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
239        redisTemplate.opsForValue();
240        return zset.rangeByScore(key, scoure, scoure1);
241    }
242
243
244    //第一次加载的时候将数据加载到redis中
245    public void saveDataToRedis(String name) {
246        double index = Math.abs(name.hashCode() % size);
247        long indexLong = new Double(index).longValue();
248        boolean availableUsers = setBit("availableUsers", indexLong, true);
249    }
250
251    //第一次加载的时候将数据加载到redis中
252    public boolean getDataToRedis(String name) {
253
254        double index = Math.abs(name.hashCode() % size);
255        long indexLong = new Double(index).longValue();
256        return getBit("availableUsers", indexLong);
257    }
258
259    /**
260     * 有序集合获取排名
261     *
262     * @param key 集合名称
263     * @param value 值
264     */
265    public Long zRank(String key, Object value) {
266        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
267        return zset.rank(key,value);
268    }
269
270
271    /**
272     * 有序集合获取排名
273     *
274     * @param key
275     */
276    public Set<ZSetOperations.TypedTuple<Object>> zRankWithScore(String key, long start,long end) {
277        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
278        Set<ZSetOperations.TypedTuple<Object>> ret = zset.rangeWithScores(key,start,end);
279        return ret;
280    }
281
282    /**
283     * 有序集合添加
284     *
285     * @param key
286     * @param value
287     */
288    public Double zSetScore(String key, Object value) {
289        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
290        return zset.score(key,value);
291    }
292
293
294    /**
295     * 有序集合添加分数
296     *
297     * @param key
298     * @param value
299     * @param scoure
300     */
301    public void incrementScore(String key, Object value, double scoure) {
302        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
303        zset.incrementScore(key, value, scoure);
304    }
305
306
307    /**
308     * 有序集合获取排名
309     *
310     * @param key
311     */
312    public Set<ZSetOperations.TypedTuple<Object>> reverseZRankWithScore(String key, long start,long end) {
313        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
314        Set<ZSetOperations.TypedTuple<Object>> ret = zset.reverseRangeByScoreWithScores(key,start,end);
315        return ret;
316    }
317
318    /**
319     * 有序集合获取排名
320     *
321     * @param key
322     */
323    public Set<ZSetOperations.TypedTuple<Object>> reverseZRankWithRank(String key, long start, long end) {
324        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
325        Set<ZSetOperations.TypedTuple<Object>> ret = zset.reverseRangeWithScores(key, start, end);
326        return ret;
327    }
328
329}

测试 redisTemplate 工具类

 1@RestController
 2public class RedisController {
 3    @Resource
 4    private RedisService service;
 5
 6    @RequestMapping("/redis/setAndGet1")
 7    @ResponseBody
 8    public String setAndGetValueV2(String name,String value){
 9        service.set(name,value);
10        return service.get(name).toString();
11    }
12}

SpringBoot 整合 MyBatis

引入依赖

 1<dependency>
 2     <groupId>mysql</groupId>
 3     <artifactId>mysql-connector-java</artifactId>
 4  </dependency>
 5  <dependency>
 6      <groupId>org.mybatis.spring.boot</groupId>
 7      <artifactId>mybatis-spring-boot-starter</artifactId>
 8      <version>1.3.0</version>
 9  </dependency>
10  <dependency>
11    <groupId>org.mybatis.generator</groupId>
12    <artifactId>mybatis-generator-core</artifactId>
13    <scope>test</scope>
14    <version>1.3.2</version>
15    <optional>true</optional>
16  </dependency>
17  <dependency>
18     <groupId>org.springframework.boot</groupId>
19     <artifactId>spring-boot-starter-jdbc</artifactId>
20  </dependency>
21  <dependency>
22        <groupId>com.alibaba</groupId>
23        <artifactId>druid</artifactId>
24        <version>1.1.10</version>
25  </dependency>
26  <dependency>
27     <groupId>com.alibaba</groupId>
28     <artifactId>fastjson</artifactId>
29     <version>1.2.7</version>
30  </dependency>

引入配置文件 application.yml

这里采用 Druid 数据库连接池。

 1spring:
 2   datasource:
 3      url: jdbc:mysql://192.168.31.220:33306/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&connectTimeout=3000&socketTimeout=1000
 4      username: root         
 5      password: 123       
 6      type: com.alibaba.druid.pool.DruidDataSource
 7      driver-class-name: com.mysql.jdbc.Driver
 8      minIdle: 5
 9      maxActive: 100
10      initialSize: 10
11      maxWait: 60000
12      timeBetweenEvictionRunsMillis: 60000
13      minEvictableIdleTimeMillis: 300000
14      validationQuery: select 'x'
15      testWhileIdle: true
16      testOnBorrow: false
17      testOnReturn: false
18      poolPreparedStatements: true
19      maxPoolPreparedStatementPerConnectionSize: 50
20      removeAbandoned: true
21      filters: stat # ,wall,log4j # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
22      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
23      useGlobalDataSourceStat: true # 合并多个DruidDataSource的监控数据
24      druidLoginName: wjf # 登录druid的账号
25      druidPassword: wjf # 登录druid的密码
26      cachePrepStmts: true  # 开启二级缓存
27mybatis:
28   typeAliasesPackage: com.soulboy.mobile.soulboymobileredis.mapper      # 需要学员自己修改填写
29   mapperLocations: classpath:/com/soulboy/mobile/soulboymobileredis/mapper/*.xml
30   mapperScanPackage: com.soulboy.mobile.soulboymobileredis.mapper
31   configLocation: classpath:/mybatis-config.xml

引入 mybatis-config.xml

 1<?xml version="1.0" encoding="UTF-8"?>  
 2<!DOCTYPE configuration  
 3     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
 4     "http://mybatis.org/dtd/mybatis-3-config.dtd">
 5<configuration>
 6	<settings>
 7	    <!-- 使全局的映射器启用或禁用缓存。 -->
 8		<setting name="cacheEnabled" value="true" />
 9		<!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->
10		<setting name="lazyLoadingEnabled" value="true" />
11		<!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->      
12         <setting name="aggressiveLazyLoading" value="true"/>      
13         <!-- 是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true -->
14		<setting name="multipleResultSetsEnabled" value="true" />
15		<!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->
16		<setting name="useColumnLabel" value="true" />
17		<!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false  -->
18		<setting name="useGeneratedKeys" value="false" />
19		<!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分  FULL:全部  -->
20		<setting name="autoMappingBehavior" value="PARTIAL" />
21		<!-- 这是默认的执行类型  (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新)  -->
22		<setting name="defaultExecutorType" value="SIMPLE" />
23	
24		<setting name="defaultStatementTimeout" value="25" />
25	
26		<setting name="defaultFetchSize" value="100" />
27	
28		<setting name="safeRowBoundsEnabled" value="false" />
29		<!-- 使用驼峰命名法转换字段。 -->
30		<setting name="mapUnderscoreToCamelCase" value="true" />
31		<!-- 设置本地缓存范围 session:就会有数据的共享  statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->
32		<setting name="localCacheScope" value="SESSION" />
33		<!-- 默认为OTHER,为了解决oracle插入null报错的问题要设置为NULL -->
34		<setting name="jdbcTypeForNull" value="NULL" />
35		<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
36	</settings>
37
38</configuration>

引入 DataSourceConfig 配置

  1import com.alibaba.druid.pool.DruidDataSource;
  2import com.alibaba.druid.support.http.StatViewServlet;
  3import com.alibaba.druid.support.http.WebStatFilter;
  4import org.mybatis.spring.annotation.MapperScan;
  5import org.slf4j.Logger;
  6import org.slf4j.LoggerFactory;
  7import org.springframework.beans.factory.annotation.Value;
  8import org.springframework.boot.web.servlet.FilterRegistrationBean;
  9import org.springframework.boot.web.servlet.ServletRegistrationBean;
 10import org.springframework.context.annotation.Bean;
 11import org.springframework.context.annotation.Configuration;
 12import org.springframework.context.annotation.Primary;
 13import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
 14import javax.sql.DataSource;
 15import java.sql.SQLException;
 16
 17@Configuration
 18@MapperScan("com.xdclass.mobile.xdclassmobileredis.mapper")
 19@EnableRedisHttpSession(maxInactiveIntervalInSeconds= 50)
 20public class DataSourceConfig {
 21    private Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);
 22
 23    @Value("${spring.datasource.url}")
 24    private String dbUrl;
 25
 26    @Value("${spring.datasource.type}")
 27    private String dbType;
 28
 29    @Value("${spring.datasource.username}")
 30    private String username;
 31
 32    @Value("${spring.datasource.password}")
 33    private String password;
 34
 35    @Value("${spring.datasource.driver-class-name}")
 36    private String driverClassName;
 37
 38    @Value("${spring.datasource.initialSize}")
 39    private int initialSize;
 40
 41    @Value("${spring.datasource.minIdle}")
 42    private int minIdle;
 43
 44    @Value("${spring.datasource.maxActive}")
 45    private int maxActive;
 46
 47    @Value("${spring.datasource.maxWait}")
 48    private int maxWait;
 49
 50    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
 51    private int timeBetweenEvictionRunsMillis;
 52
 53    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
 54    private int minEvictableIdleTimeMillis;
 55
 56    @Value("${spring.datasource.validationQuery}")
 57    private String validationQuery;
 58
 59    @Value("${spring.datasource.testWhileIdle}")
 60    private boolean testWhileIdle;
 61
 62    @Value("${spring.datasource.testOnBorrow}")
 63    private boolean testOnBorrow;
 64
 65    @Value("${spring.datasource.testOnReturn}")
 66    private boolean testOnReturn;
 67
 68    @Value("${spring.datasource.poolPreparedStatements}")
 69    private boolean poolPreparedStatements;
 70
 71    @Value("${spring.datasource.filters}")
 72    private String filters;
 73
 74    @Value("${spring.datasource.connectionProperties}")
 75    private String connectionProperties;
 76
 77    @Value("${spring.datasource.useGlobalDataSourceStat}")
 78    private boolean useGlobalDataSourceStat;
 79
 80    @Value("${spring.datasource.druidLoginName}")
 81    private String druidLoginName;
 82
 83    @Value("${spring.datasource.druidPassword}")
 84    private String druidPassword;
 85
 86    @Bean(name="dataSource",destroyMethod = "close", initMethod="init")
 87    @Primary //不要漏了这
 88    public DataSource dataSource(){
 89        DruidDataSource datasource = new DruidDataSource();
 90        try {
 91            datasource.setUrl(this.dbUrl);
 92            datasource.setDbType(dbType);
 93            datasource.setUsername(username);
 94            datasource.setPassword(password);
 95            datasource.setDriverClassName(driverClassName);
 96            datasource.setInitialSize(initialSize);
 97            datasource.setMinIdle(minIdle);
 98            datasource.setMaxActive(maxActive);
 99            datasource.setMaxWait(maxWait);
100            datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
101            datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
102            datasource.setValidationQuery(validationQuery);
103            datasource.setTestWhileIdle(testWhileIdle);
104            datasource.setTestOnBorrow(testOnBorrow);
105            datasource.setTestOnReturn(testOnReturn);
106            datasource.setPoolPreparedStatements(poolPreparedStatements);
107            datasource.setFilters(filters);
108        } catch (SQLException e) {
109            logger.error("druid configuration initialization filter", e);
110        }
111        return datasource;
112    }
113
114    /////////  下面是druid 监控访问的设置  /////////////////
115    @Bean
116    public ServletRegistrationBean druidServlet() {
117        ServletRegistrationBean reg = new ServletRegistrationBean();
118        reg.setServlet(new StatViewServlet());
119        reg.addUrlMappings("/druid/*");  //url 匹配
120        reg.addInitParameter("allow", "192.168.16.110,127.0.0.1"); // IP白名单 (没有配置或者为空,则允许所有访问)
121        reg.addInitParameter("deny", "192.168.16.111"); //IP黑名单 (存在共同时,deny优先于allow)
122        reg.addInitParameter("loginUsername", this.druidLoginName);//登录名
123        reg.addInitParameter("loginPassword", this.druidPassword);//登录密码
124        reg.addInitParameter("resetEnable", "false"); // 禁用HTML页面上的“Reset All”功能
125        return reg;
126    }
127
128    @Bean(name="druidWebStatFilter")
129    public FilterRegistrationBean filterRegistrationBean() {
130        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
131        filterRegistrationBean.setFilter(new WebStatFilter());
132        filterRegistrationBean.addUrlPatterns("/*");
133        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); //忽略资源
134        filterRegistrationBean.addInitParameter("profileEnable", "true");
135        filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
136        filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
137        return filterRegistrationBean;
138    }
139}

编写 Mapper 类:注解方式

 1@Mapper
 2@Component
 3
 4public interface UserMapper {
 5
 6	@Insert("insert sys_user(id,user_name) values(#{id},#{userName})")
 7	void insert(User u);
 8
 9	@Update("update sys_user set user_name = #{userName} where id=#{id} ")
10	void update(User u);
11
12	@Delete("delete from sys_user where id=#{id} ")
13	void delete(@Param("id") String id);
14
15	@Select("select id,user_name from sys_user where id=#{id} ")
16	User find(@Param("id") String id);
17
18	//注:方法名和要UserMapper.xml中的id一致
19	List<User> query(@Param("userName") String userName);
20
21	@Delete("delete from sys_user")
22	void deleteAll();
23}

编写 Mapper 类:XML 配置方式

1

测试类

 1@RestController
 2public class UserController {
 3    @Resource
 4    private UserMapper userMapper;
 5
 6    @RequestMapping("/getUser")
 7    @ResponseBody
 8    public User getUser(String id) {
 9        User user = userMapper.find(id);
10        return user;
11    }
12}

测试

1// http://localhost:8080/getUser?id=1
2{
3  "id": "1",
4  "userName": "soulboy"
5}

MyBatis 整合 Redis(缓存)

 请求第一次访问从 TRDB 中获取数据,并更新至 Redis,第二次访问时直接从 Redis 中获取数据。

 1@RestController
 2public class UserController {
 3    private static final String key = "userCache_";
 4
 5    @Resource
 6    private UserMapper userMapper;
 7
 8    @Resource
 9    private RedisService redisService;
10
11    /**
12     * set值和get值的时候序列化方式必须保持一致
13     * @param id
14     * @return
15     */
16    @RequestMapping("/getUserCache")
17    @ResponseBody
18    public User getUseCache(String id) {
19
20        //step1 先从redis里面取值
21        User user =  (User)redisService.get(key + id);
22//默认使用JDK序列化方式
23
24        //step2 如果拿不到则从DB取值
25        if (user == null) {
26            User userDB = userMapper.find(id);
27            System.out.println("fresh value from DB id:" + id);
28
29            //step3 DB非空情况刷新redis值
30            if (userDB != null) {
31                redisService.set(key + id, userDB);
32//默认使用JDK序列化方式
33                return userDB;
34            }
35        }
36        return user;
37    }
38
39}

测试

1// http://localhost:8080/getUserCache?id=1
2{
3  "id": "1",
4  "userName": "soulboy"
5}

Redis Desktop Manager 查看数据
mybatis 整合 redis 作为缓存

SpringBoot cache

 上述示例中的 step1、step2、step3 包含大量的重复逻辑,为了消除代码的容易,专业于业务代码,可以使用 SpringBoot cache 注解。SpringBoot cache 可以结合 Redis、ehcache 等缓存。一级缓存是:sqlSession,SQL 建立连接到关闭连接的数据缓存; 二级缓存是:全局。

  • @CacheConfig(cacheNames="userInfoCache") 在同个 Redis 里面必须唯一
  • @Cacheable(查) : 来划分可缓存的方法 - 即,结果存储在缓存中的方法,以便在后续调用(具有相同的参数)时,返回缓存中的值而不必实际执行该方法
  • @CachePut(修改、增加) : 当需要更新缓存而不干扰方法执行时,可以使用 @CachePut 注释。也就是说,始终执行该方法并将其结果放入缓存中(根据 @CachePut 选项)
  • @CacheEvict(删除) : 对于从缓存中删除陈旧或未使用的数据非常有用,指示缓存范围内的驱逐是否需要执行而不仅仅是一个条目驱逐

引入 Maven 依赖

1<dependency>
2			<groupId>org.springframework.boot</groupId>
3			<artifactId>spring-boot-starter-cache</artifactId>
4		</dependency>

开启缓存注解: @EnableCaching

 1@Configuration
 2@EnableCaching
 3public class RedisConfig {
 4    @Bean
 5    public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory factory){
 6        RedisTemplate<String,String> redisTemplate = new RedisTemplate<>();
 7        redisTemplate.setConnectionFactory(factory);
 8        return redisTemplate;
 9    }
10}

在 Service 层的方法上面加入 SpEL

 1import org.springframework.beans.factory.annotation.Autowired;
 2import org.springframework.cache.annotation.CacheConfig;
 3import org.springframework.cache.annotation.CacheEvict;
 4import org.springframework.cache.annotation.CachePut;
 5import org.springframework.cache.annotation.Cacheable;
 6import org.springframework.lang.Nullable;
 7import org.springframework.stereotype.Service;
 8import org.springframework.transaction.annotation.Propagation;
 9import org.springframework.transaction.annotation.Transactional;
10import org.springframework.util.Assert;
11
12@Service
13@CacheConfig(cacheNames="userInfoCache") // 本类内方法指定使用缓存时,默认的名称就是userInfoCache
14@Transactional(propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class)
15public class UserService {
16
17    @Autowired
18    private UserMapper userMapper;
19
20    // 因为必须要有返回值,才能保存到数据库中,如果保存的对象的某些字段是需要数据库生成的,
21    //那保存对象进数据库的时候,就没必要放到缓存了
22    @CachePut(key="#p0.id")  //#p0表示第一个参数
23    //必须要有返回值,否则没数据放到缓存中
24    public User insertUser(User u){
25        this.userMapper.insert(u);
26        //u对象中可能只有只几个有效字段,其他字段值靠数据库生成,比如id
27        return this.userMapper.find(u.getId());
28    }
29
30    @CachePut(key="#p0.id")
31    public User updateUser(User u){
32        this.userMapper.update(u);
33        //可能只是更新某几个字段而已,所以查次数据库把数据全部拿出来全部
34        return this.userMapper.find(u.getId());
35    }
36
37    @Nullable
38    @Cacheable(key="#p0") // @Cacheable 会先查询缓存,如果缓存中存在,则不执行方法
39;#p0代表第一个参数
40    public User findById(String id){
41        System.err.println("根据id=" + id +"获取用户对象,从数据库中获取");
42        Assert.notNull(id,"id不用为空");
43        return this.userMapper.find(id);
44    }
45
46    @CacheEvict(key="#p0")  //删除缓存名称为userInfoCache,key等于指定的id对应的缓存
47    public void deleteById(String id){
48        this.userMapper.delete(id);
49    }
50
51    //清空缓存名称为userInfoCache(看类名上的注解)下的所有缓存
52    //如果数据失败了,缓存时不会清除的
53    @CacheEvict(allEntries = true)
54    public void deleteAll(){
55        this.userMapper.deleteAll();
56    }
57
58    @Nullable
59    @Cacheable(value = "UserInfoList", keyGenerator = "simpleKeyGenerator") // @Cacheable 会先查询缓存,如果缓存中存在,则不执行方法
60    public User findByIdTtl(String id){
61        System.err.println("根据id=" + id +"获取用户对象,从数据库中获取");
62        Assert.notNull(id,"id不用为空");
63        return this.userMapper.find(id);
64    }
65}

编写 Controller 层测试代码

 1@RestController
 2public class UserController {
 3
 4    @Resource
 5    private UserService userService;
 6
 7    @RequestMapping("/getByCache")
 8    @ResponseBody
 9    public User getByCache(String id) {
10        User user = userService.findById(id);
11        return user;
12    }
13
14    @ResponseBody
15    @RequestMapping(value = "/getexpire", method = RequestMethod.GET)
16    public User findByIdTtl(String id) {
17        User u = new User();
18        try{
19            u = userService.findByIdTtl(id);
20        }catch (Exception e){
21            System.err.println(e.getMessage());
22        }
23        return u;
24    }
25}

测试结果

1// http://localhost:8080/getByCache?id=1
2{
3  "id": "1",
4  "userName": "soulboy"
5}
6根据id=1获取用户对象,从数据库中获取
7再次访问// http://localhost:8080/getByCache?id=1
8	发现控制台没有打印,此时证明数据是从redis缓存中获取。

SpringBoot cache 存在的问题

生成 key 过于简单
 生成 key 过于简单,userCache::1 容易冲突。
无法设置过期时间,默认过期时间为永久不过期
 默认过期时间是 -1 ,代表永不过期。
配置序列化方式,默认的是序列化 JDKSerialazable
 JDKSerialazable 序列化方式对空值、日期的支持不好。

修改 RedisConfig 文件

 1import com.fasterxml.jackson.annotation.JsonAutoDetect;
 2import com.fasterxml.jackson.annotation.PropertyAccessor;
 3import com.fasterxml.jackson.databind.ObjectMapper;
 4import org.springframework.cache.CacheManager;
 5import org.springframework.cache.annotation.EnableCaching;
 6import org.springframework.cache.interceptor.KeyGenerator;
 7import org.springframework.context.annotation.Bean;
 8import org.springframework.context.annotation.Configuration;
 9import org.springframework.data.redis.cache.RedisCacheConfiguration;
10import org.springframework.data.redis.cache.RedisCacheManager;
11import org.springframework.data.redis.cache.RedisCacheWriter;
12import org.springframework.data.redis.connection.RedisConnectionFactory;
13import org.springframework.data.redis.core.RedisTemplate;
14import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
15import org.springframework.data.redis.serializer.RedisSerializationContext;
16
17import javax.annotation.Resource;
18import java.time.Duration;
19import java.util.HashMap;
20import java.util.Map;
21
22@Configuration
23@EnableCaching
24public class RedisConfig {
25
26    @Bean
27    public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory factory){
28        RedisTemplate<String,String> redisTemplate = new RedisTemplate<>();
29        redisTemplate.setConnectionFactory(factory);
30        return redisTemplate;
31    }
32
33    /**
34     * 生成key过于简单,userCache::1容易冲突。
35     * @return
36     */
37    @Bean
38    public KeyGenerator simpleKeyGenerator() {
39        return (o, method, objects) -> {
40            StringBuilder stringBuilder = new StringBuilder();
41            stringBuilder.append(o.getClass().getSimpleName());
42            stringBuilder.append(".");
43            stringBuilder.append(method.getName());
44            stringBuilder.append("[");
45            for (Object obj : objects) {
46                stringBuilder.append(obj.toString());
47            }
48            stringBuilder.append("]");
49            return stringBuilder.toString();
50        };
51    }
52
53    /**
54     * 解决:默认过期时间为永久不过期的问题
55     * @param redisConnectionFactory
56     * @return
57     */
58    @Bean
59    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
60        return new RedisCacheManager(
61                RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
62                this.getRedisCacheConfigurationWithTtl(600), // 默认策略,未配置的 key 会使用这个。
63                this.getRedisCacheConfigurationMap() // 指定 key 策略
64        );
65    }
66    //针对指定的key设置缓存时间,其他未配置的key则使用默认策略。
67    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
68        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
69        redisCacheConfigurationMap.put("UserInfoList", this.getRedisCacheConfigurationWithTtl(100));
70        redisCacheConfigurationMap.put("UserInfoListAnother", this.getRedisCacheConfigurationWithTtl(18000));
71
72        return redisCacheConfigurationMap;
73    }
74
75    /**
76     * 修改默认序列化方式
77     * @param seconds
78     * @return
79     */
80    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
81        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
82        ObjectMapper om = new ObjectMapper();
83        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
84        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
85        jackson2JsonRedisSerializer.setObjectMapper(om);
86
87        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
88        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
89                RedisSerializationContext
90                        .SerializationPair
91                        .fromSerializer(jackson2JsonRedisSerializer)//添加jackson的序列化方式
92        ).entryTtl(Duration.ofSeconds(seconds));
93
94        return redisCacheConfiguration;
95    }
96}

Service 层代码

 1import com.xdclass.mobile.xdclassmobileredis.domain.User;
 2import com.xdclass.mobile.xdclassmobileredis.mapper.UserMapper;
 3import org.springframework.beans.factory.annotation.Autowired;
 4import org.springframework.cache.annotation.CacheConfig;
 5import org.springframework.cache.annotation.CacheEvict;
 6import org.springframework.cache.annotation.CachePut;
 7import org.springframework.cache.annotation.Cacheable;
 8import org.springframework.lang.Nullable;
 9import org.springframework.stereotype.Service;
10import org.springframework.transaction.annotation.Propagation;
11import org.springframework.transaction.annotation.Transactional;
12import org.springframework.util.Assert;
13
14@Service
15@CacheConfig(cacheNames="userInfoCache") // 本类内方法指定使用缓存时,默认的名称就是userInfoCache
16@Transactional(propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class)
17public class UserService {
18
19    @Autowired
20    private UserMapper userMapper;
21
22    // 因为必须要有返回值,才能保存到数据库中,如果保存的对象的某些字段是需要数据库生成的,
23    //那保存对象进数据库的时候,就没必要放到缓存了
24    @CachePut(key="#p0.id")  //#p0表示第一个参数
25    //必须要有返回值,否则没数据放到缓存中
26    public User insertUser(User u){
27        this.userMapper.insert(u);
28        //u对象中可能只有只几个有效字段,其他字段值靠数据库生成,比如id
29        return this.userMapper.find(u.getId());
30    }
31
32    @CachePut(key="#p0.id")
33    public User updateUser(User u){
34        this.userMapper.update(u);
35        //可能只是更新某几个字段而已,所以查次数据库把数据全部拿出来全部
36        return this.userMapper.find(u.getId());
37    }
38
39    @Nullable
40    //@Cacheable(key="#p0") // @Cacheable 会先查询缓存,如果缓存中存在,则不执行方法 #p0代表第一个参数
41    @Cacheable(keyGenerator = "simpleKeyGenerator")//使用自定义key生成策略,防止key的冲突
42    //key的格式:userInfoCache::UserService.findById[1]
43    public User findById(String id){
44        System.err.println("根据id=" + id +"获取用户对象,从数据库中获取");
45        Assert.notNull(id,"id不用为空");
46        return this.userMapper.find(id);
47    }
48
49    @CacheEvict(key="#p0")  //删除缓存名称为userInfoCache,key等于指定的id对应的缓存
50    public void deleteById(String id){
51        this.userMapper.delete(id);
52    }
53
54    //清空缓存名称为userInfoCache(看类名上的注解)下的所有缓存
55    //如果数据失败了,缓存时不会清除的
56    @CacheEvict(allEntries = true)
57    public void deleteAll(){
58        this.userMapper.deleteAll();
59    }
60
61    @Nullable
62    // @Cacheable 会先查询缓存,如果缓存中存在,则不执行方法
63    //使用自定义key生成策略,防止key的冲突
64    @Cacheable(value = "UserInfoList", keyGenerator = "simpleKeyGenerator") 
65    //key的格式:UserInfoList::UserService.findByIdTtl[1]
66    public User findByIdTtl(String id){
67        System.err.println("根据id=" + id +"获取用户对象,从数据库中获取");
68        Assert.notNull(id,"id不用为空");
69        return this.userMapper.find(id);
70    }
71}

Controller 层代码

 1@RestController
 2public class UserController {
 3    @Resource
 4    private UserService userService;
 5
 6    @RequestMapping("/getByCache")
 7    @ResponseBody
 8    public User getByCache(String id) {
 9        User user = userService.findById(id);
10        return user;
11    }
12
13    @ResponseBody
14    @RequestMapping(value = "/getexpire", method = RequestMethod.GET)
15    public User findByIdTtl(String id) {
16        User u = new User();
17        try{
18            u = userService.findByIdTtl(id);
19        }catch (Exception e){
20            System.err.println(e.getMessage());
21        }
22        return u;
23    }
24}

测试

1// http://localhost:8080/getexpire?id=1
2{
3  "id": "1",
4  "userName": "soulboy"
5}

ttl

压测 Redis cache VS TRDB


ab 是 Apache HTTP server benchmarking tool,可以用以测试 HTTP 请求的服务器性能

 1# 安装ab测试工具
 2[root@localhost ~]# yum install -y httpd-tools
 3[root@localhost ~]# ab -V
 4This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
 5
 6# ab测试工具命令参数含义
 7-n	#进行http请求的总个数
 8-c	#同时并发请求的Client数量,即请求的并发数
 9qps	#qps即每秒并发数,request per second,通过统计qps来测试性能。
10
11# 进行测试
12# Redis cache
13ab -n1000 -c10 http://192.168.31.230:8080/getByCache?id=1
14# TRDB
15ab -n1000 -c10 http://192.168.31.230:8080/getUser?id=1
16
17#  1000请求数   10并发
18Mysql:		1347.98 [#/sec] (mean)
19Redis cache:	1713.87 [#/sec] (mean)
20
21#  1000请求数   100并发
22Mysql:		2510.13 [#/sec] (mean)
23Redis cache:	4440.42 [#/sec] (mean)
24
25#  10000请求数   100并发
26Mysql:		2953.38 [#/sec] (mean)
27Redis cache:	5752.92 [#/sec] (mean)
28
29#  10000请求数   300并发
30Mysql:		apr_socket_recv: Connection refused(服务过载,拒绝连接)
31Redis cache:	6241.05 [#/sec] (mean)
32
33#  10000请求数   800并发
34Mysql:		apr_socket_recv: Connection refused(服务过载,拒绝连接)
35Redis cache:	4831.11 [#/sec] (mean)

Redis 实现分布式集群环境下的 session 共享

 机器部署同一套服务(代码),性能更好,更承受更高的用户并发,集群之间可以通过 Redis 来共享 session。

  • Cookie: Cookie 是一小段文本信息,伴随着用户请求和页面在 Web 服务器和浏览器之间传递。Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息,我们可以看到在服务器写的 cookie,会通过响应头 Set-Cookie 的方式写入到浏览器。
  • Session:HTTP 协议是无状态的,并非 TCP 一样进行三次握手,对于一个浏览器发出的多次请求,Web 服务器无法区分是不是来源于同一个浏览器。所以服务器为了区分这个过程会通过一个 sessionid 来区分请求,而 sessionid 保存在 cookie 中。引入 Maven 依赖
1<dependency>
2			<groupId>org.springframework.session</groupId>
3			<artifactId>spring-session-data-redis</artifactId>
4		</dependency>

开启 Redis session 缓存:@EnableRedisHttpSession
 DataSourceConfig

  • maxInactiveIntervalInSeconds 指定缓存的时间
  1import com.alibaba.druid.pool.DruidDataSource;
  2import com.alibaba.druid.support.http.StatViewServlet;
  3import com.alibaba.druid.support.http.WebStatFilter;
  4import org.mybatis.spring.annotation.MapperScan;
  5import org.slf4j.Logger;
  6import org.slf4j.LoggerFactory;
  7import org.springframework.beans.factory.annotation.Value;
  8import org.springframework.boot.web.servlet.FilterRegistrationBean;
  9import org.springframework.boot.web.servlet.ServletRegistrationBean;
 10import org.springframework.context.annotation.Bean;
 11import org.springframework.context.annotation.Configuration;
 12import org.springframework.context.annotation.Primary;
 13import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
 14import javax.sql.DataSource;
 15import java.sql.SQLException;
 16
 17@Configuration
 18@MapperScan("com.xdclass.mobile.xdclassmobileredis.mapper")
 19@EnableRedisHttpSession(maxInactiveIntervalInSeconds= 50)
 20public class DataSourceConfig {
 21    private Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);
 22
 23    @Value("${spring.datasource.url}")
 24    private String dbUrl;
 25
 26    @Value("${spring.datasource.type}")
 27    private String dbType;
 28
 29    @Value("${spring.datasource.username}")
 30    private String username;
 31
 32    @Value("${spring.datasource.password}")
 33    private String password;
 34
 35    @Value("${spring.datasource.driver-class-name}")
 36    private String driverClassName;
 37
 38    @Value("${spring.datasource.initialSize}")
 39    private int initialSize;
 40
 41    @Value("${spring.datasource.minIdle}")
 42    private int minIdle;
 43
 44    @Value("${spring.datasource.maxActive}")
 45    private int maxActive;
 46
 47    @Value("${spring.datasource.maxWait}")
 48    private int maxWait;
 49
 50    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
 51    private int timeBetweenEvictionRunsMillis;
 52
 53    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
 54    private int minEvictableIdleTimeMillis;
 55
 56    @Value("${spring.datasource.validationQuery}")
 57    private String validationQuery;
 58
 59    @Value("${spring.datasource.testWhileIdle}")
 60    private boolean testWhileIdle;
 61
 62    @Value("${spring.datasource.testOnBorrow}")
 63    private boolean testOnBorrow;
 64
 65    @Value("${spring.datasource.testOnReturn}")
 66    private boolean testOnReturn;
 67
 68    @Value("${spring.datasource.poolPreparedStatements}")
 69    private boolean poolPreparedStatements;
 70
 71    @Value("${spring.datasource.filters}")
 72    private String filters;
 73
 74    @Value("${spring.datasource.connectionProperties}")
 75    private String connectionProperties;
 76
 77    @Value("${spring.datasource.useGlobalDataSourceStat}")
 78    private boolean useGlobalDataSourceStat;
 79
 80    @Value("${spring.datasource.druidLoginName}")
 81    private String druidLoginName;
 82
 83    @Value("${spring.datasource.druidPassword}")
 84    private String druidPassword;
 85
 86    @Bean(name="dataSource",destroyMethod = "close", initMethod="init")
 87    @Primary //不要漏了这
 88    public DataSource dataSource(){
 89        DruidDataSource datasource = new DruidDataSource();
 90        try {
 91            datasource.setUrl(this.dbUrl);
 92            datasource.setDbType(dbType);
 93            datasource.setUsername(username);
 94            datasource.setPassword(password);
 95            datasource.setDriverClassName(driverClassName);
 96            datasource.setInitialSize(initialSize);
 97            datasource.setMinIdle(minIdle);
 98            datasource.setMaxActive(maxActive);
 99            datasource.setMaxWait(maxWait);
100            datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
101            datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
102            datasource.setValidationQuery(validationQuery);
103            datasource.setTestWhileIdle(testWhileIdle);
104            datasource.setTestOnBorrow(testOnBorrow);
105            datasource.setTestOnReturn(testOnReturn);
106            datasource.setPoolPreparedStatements(poolPreparedStatements);
107            datasource.setFilters(filters);
108        } catch (SQLException e) {
109            logger.error("druid configuration initialization filter", e);
110        }
111        return datasource;
112    }
113
114    /////////  下面是druid 监控访问的设置  /////////////////
115    @Bean
116    public ServletRegistrationBean druidServlet() {
117        ServletRegistrationBean reg = new ServletRegistrationBean();
118        reg.setServlet(new StatViewServlet());
119        reg.addUrlMappings("/druid/*");  //url 匹配
120        reg.addInitParameter("allow", "192.168.16.110,127.0.0.1"); // IP白名单 (没有配置或者为空,则允许所有访问)
121        reg.addInitParameter("deny", "192.168.16.111"); //IP黑名单 (存在共同时,deny优先于allow)
122        reg.addInitParameter("loginUsername", this.druidLoginName);//登录名
123        reg.addInitParameter("loginPassword", this.druidPassword);//登录密码
124        reg.addInitParameter("resetEnable", "false"); // 禁用HTML页面上的“Reset All”功能
125        return reg;
126    }
127
128    @Bean(name="druidWebStatFilter")
129    public FilterRegistrationBean filterRegistrationBean() {
130        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
131        filterRegistrationBean.setFilter(new WebStatFilter());
132        filterRegistrationBean.addUrlPatterns("/*");
133        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); //忽略资源
134        filterRegistrationBean.addInitParameter("profileEnable", "true");
135        filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
136        filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
137        return filterRegistrationBean;
138    }
139}

Controller 层代码

 1import org.springframework.web.bind.annotation.RequestMapping;
 2import org.springframework.web.bind.annotation.RequestMethod;
 3import org.springframework.web.bind.annotation.RestController;
 4import javax.servlet.http.HttpServletRequest;
 5import java.util.HashMap;
 6import java.util.Map;
 7
 8@RestController
 9public class SessionController {
10
11    @RequestMapping(value = "/setSession", method = RequestMethod.GET)
12    public Map<String, Object> setSession (HttpServletRequest request){
13        Map<String, Object> map = new HashMap<>();
14        request.getSession().setAttribute("request Url", request.getRequestURL());
15        map.put("request Url", request.getRequestURL());
16        return map;
17    }
18
19    @RequestMapping(value = "/getSession", method = RequestMethod.GET)
20    public Object getSession (HttpServletRequest request){
21        Map<String, Object> map = new HashMap<>();
22        map.put("sessionIdUrl",request.getSession().getAttribute("request Url"));
23 //设置sessionIdUrl
24        map.put("sessionId", request.getSession().getId());
25 //设置sessionId
26        return map;
27    }
28}

测试

 1// http://192.168.31.230:8080/setSession
 2{
 3  "request Url": "http://192.168.31.230:8080/setSession"
 4}
 5
 6// http://192.168.31.230:8080/getSession
 7{
 8  "sessionIdUrl": "http://192.168.31.230:8080/setSession",
 9  "sessionId": "2c950216-dbd1-4b3b-a748-67e71611a084"
10}

查询 Redis

1spring:session:sessions:expires:81d06c82-5312-41d7-8afe-4d08b77352d6

session 共享


作者:Soulboy