Spring Boot 中的同一个 Bug,竟然把我坑了两次!
真是郁闷,不过这事又一次提醒我解决问题还是要根治,不能囫囵吞枣,否则相同的问题可能会以不同的形式出现,每次都得花时间去搞。刨根问底,一步到位,再遇到类似问题就可以分分钟解决了。
如果大家没看过松哥之前写的 Spring Boot 整合 Spring Session,可以先回顾下:
第一次踩坑
事情是这样的,大概在今年 6 月初的时候,我在项目中使用到了 Session 共享,当时采用的方案就是 Redis+Spring Session。本来这是一个很简单的问题,我在以前的项目中也用过多次这种方案,早已轻车熟路,但是那次有点不对劲,项目启动时候报了如下错误:

一模一样的代码,但是运行就是会出错,我感觉莫名其妙。因为在 Spring Boot 中整合 Spring Session 是一个非常简单的操作,就几行 Redis 的配置而已,我在确认了代码没问题之后,很快想到了可能是版本问题,因为当时 Spring Boot2.1.5 刚刚发布,我喜欢用最新版。于是我尝试将 Spring Boot 的版本切换到 2.1.4 ,切换回去之后,果然就 OK了,再次启动项目又不会报错了。于是基本确定这是 Spring Boot 的版本升级带来的问题。
但是当时我并没有深究,我以为就是官方出于安全考虑,让你在使用 Redis 时强制加上 Spring Security(因为根据错误提示,很容想到加上 Spring Security 依赖),加上 Spring Security 依赖之后,果然就没有问题了,我也没有多想,这件事就这样过了。
第二次踩坑
前两天我在给星球上的小伙伴录制 Spring Boot 视频的时候,采用了 Spring Boot 最新版 2.1.7,也是 Spring Session,但是在创建项目的时候,忘记添加 Spring Security 依赖了(第一次踩坑之后,我每次用 Spring Session 都会自觉的加上 Spring Security 依赖),运行的时候竟然没报错!我就郁闷了。
于是我去试了 Spring Boot2.1.4、Spring Boot2.1.6 发现都没有问题,在使用 Spring Session 的时候都不需要添加 Spring Security 依赖,只有 Spring Boot2.1.5 才有这个问题。于是我大概明白了,这可能是一个 Bug,而不是版本升级的新功能。
这一次,那我就打算追究一下问题的根源。
源头
要追究问题的源头,我们当然得从 Spring Session 的自动化配置类开始。
在 Spring Boot2.1.5 的 org.springframework.boot.autoconfigure.session.SessionAutoConfiguration 类中,我看到如下源码:
@Bean
@Conditional(DefaultCookieSerializerCondition.class)
public DefaultCookieSerializer cookieSerializer(ServerProperties serverProperties,
ObjectProvider<SpringSessionRememberMeServices> springSessionRememberMeServices) {
//.....
map.from(cookie::getMaxAge).to((maxAge) -> cookieSerializer
.setCookieMaxAge((int) maxAge.getSeconds()));
springSessionRememberMeServices.ifAvailable((
rememberMeServices) -> cookieSerializer.setRememberMeRequestAttribute(
SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR));
return cookieSerializer;
}
从这一段源码中我们可以看到,这里使用到了 SpringSessionRememberMeServices ,而这个类中则用到 Spring Security 中相关的类。因此,如果不引入 Spring Security 就会报错。
我们再来看看 Spring Boot2.1.6 中 org.springframework.boot.autoconfigure.session.SessionAutoConfiguration 类的源码,如下:
@Bean
@Conditional(DefaultCookieSerializerCondition.class)
public DefaultCookieSerializer cookieSerializer(ServerProperties serverProperties) {
//...
map.from(cookie::getMaxAge).to((maxAge) -> cookieSerializer.setCookieMaxAge((int) maxAge.getSeconds()));
if (ClassUtils.isPresent(REMEMBER_ME_SERVICES_CLASS, getClass().getClassLoader())) {
new RememberMeServicesCookieSerializerCustomizer().apply(cookieSerializer);
}
return cookieSerializer;
}
可以看到,在 Spring Boot2.1.6 中,这个问题已经得到修复。这里就没有 2.1.5 那么冲动了,上来了先用 ClassUtils.isPresent 方法判断了下 REMEMBER_ME_SERVICES_CLASS(org.springframework.security.web.authentication.RememberMeServices) 是否存在,存在的话,才有后面的操作。
至此,这个问题就总算弄懂了。
结语
大家平时遇到问题,如果项目不是很赶的话,可以留意多想想,多追究一下原因,说不定你会有很多意外的收获。我这次就是一个活生生的例子,一开始没多想,后来又发现不对劲,前前后后一折腾,反而又多浪费了一些时间。
关注公众号【江南一点雨】,专注于 Spring Boot+微服务以及前后端分离等全栈技术,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

Spring Boot 中的同一个 Bug,竟然把我坑了两次!的更多相关文章
- spring boot(三):Spring Boot中Redis的使用
spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...
- Spring Boot 中的静态资源到底要放在哪里?
当我们使用 SpringMVC 框架时,静态资源会被拦截,需要添加额外配置,之前老有小伙伴在微信上问松哥Spring Boot 中的静态资源加载问题:"松哥,我的HTML页面好像没有样式?& ...
- Spring Boot:Spring Boot 中 Redis 的使用
Redis 介绍 Redis 是目前业界使用最广泛的内存数据存储.相比 Memcached,Redis 支持更丰富的数据结构,例如 hashes, lists, sets 等,同时支持数据持久化.除此 ...
- (转)Spring Boot(三):Spring Boot 中 Redis 的使用
http://www.ityouknow.com/springboot/2016/03/06/spring-boot-redis.html Spring Boot 对常用的数据库支持外,对 Nosql ...
- Spring Boot 中配置文件application.properties使用
一.配置文档配置项的调用(application.properties可放在resources,或者resources下的config文件夹里) package com.my.study.contro ...
- 学习Spring Boot:(二十三)Spring Boot 中使用 Docker
前言 简单的学习下怎么在 Spring Boot 中使用 Docker 进行构建,发布一个镜像,现在我们通过远程的 docker api 构建镜像,运行容器,发布镜像等操作. 这里只介绍两种方式: 远 ...
- Spring Boot(三):Spring Boot 中 Redis 的使用
Spring Boot 对常用的数据库支持外,对 Nosql 数据库也进行了封装自动化. Redis 介绍 Redis 是目前业界使用最广泛的内存数据存储.相比 Memcached,Redis 支持更 ...
- Spring Boot中使用EhCache实现缓存支持
SpringBoot提供数据缓存功能的支持,提供了一系列的自动化配置,使我们可以非常方便的使用缓存.,相信非常多人已经用过cache了.因为数据库的IO瓶颈.一般情况下我们都会引入非常多的缓存策略, ...
- Spring Boot中实现logback多环境日志配置
在Spring Boot中,可以在logback.xml中的springProfile标签中定义多个环境logback.xml: <springProfile name="produc ...
随机推荐
- 嗨,你真的懂this吗?
this关键字是JavaScript中最复杂的机制之一,是一个特别的关键字,被自动定义在所有函数的作用域中,但是相信很多JvaScript开发者并不是非常清楚它究竟指向的是什么.听说你很懂this,是 ...
- Java编程思想:泛型方法
import java.util.*; public class Test { public static void main(String[] args) { // GenericMethods.t ...
- python爬虫常用正则表达式组合匹配
["\']匹配什么?(.*?)匹配什么? ["\'] ----------- 匹配单双引号 (.*?)xxx ----------- 匹配任意长度字符+xxx ...
- Eclipse安装STS插件
由于Spring的配置文件较多,基于Eclipse配置也比较复杂.为了提高开发的效率,建议使用STS开发工具开发,或者在Eclipse安装一个STS插件. 在开发者配置bean的class时候能够根据 ...
- 【CYH-02】NOIp考砸后虐题赛:坐标:题解
代码: 这道题也并不难,用排序+简单动规即可.因为@Kevin_Wa 大佬放的是c++,所以我来一发Pascal. var min,max,i,a1,b1,c1,d1,n:longint; a,b,c ...
- canvas动画:气泡上升效果
HTML5中的canvas真是个很强大的东西呢! 这几天突发奇想想做一个气泡上升的动画,经过许久的思考和多次失败,终于做出了如下效果 由于是录制的gif图,看着会有点卡顿,实际演示是很自然的 想要做出 ...
- 题解 P2835 【刻录光盘】
P2835 刻录光盘 来一波FLOYD最短代码qwq #include<cstdio> using namespace std; #define FOR(i) for (register ...
- 字符串的排列(剑指offer)
题目描述: 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. 思路: 利用 ...
- Spring Cloud Config 实现配置中心,看这一篇就够了
Spring Cloud Config 是 Spring Cloud 家族中最早的配置中心,虽然后来又发布了 Consul 可以代替配置中心功能,但是 Config 依然适用于 Spring Clou ...
- Java EE.JSP.指令
JSP的指令是从JSP向Web容器发送消息,它用来设置页面的全局属性,如输出内容类型等. JSP的指令的格式为:<%@ 指令名 属性="属性值"%> 1.page指令 ...