真是郁闷,不过这事又一次提醒我解决问题还是要根治,不能囫囵吞枣,否则相同的问题可能会以不同的形式出现,每次都得花时间去搞。刨根问底,一步到位,再遇到类似问题就可以分分钟解决了。

如果大家没看过松哥之前写的 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,竟然把我坑了两次!的更多相关文章

  1. Spring Boot 中的静态资源到底要放在哪里?

    当我们使用 SpringMVC 框架时,静态资源会被拦截,需要添加额外配置,之前老有小伙伴在微信上问松哥Spring Boot 中的静态资源加载问题:"松哥,我的HTML页面好像没有样式?& ...

  2. Spring Boot:Spring Boot 中 Redis 的使用

    Redis 介绍 Redis 是目前业界使用最广泛的内存数据存储.相比 Memcached,Redis 支持更丰富的数据结构,例如 hashes, lists, sets 等,同时支持数据持久化.除此 ...

  3. (转)Spring Boot(三):Spring Boot 中 Redis 的使用

    http://www.ityouknow.com/springboot/2016/03/06/spring-boot-redis.html Spring Boot 对常用的数据库支持外,对 Nosql ...

  4. 学习Spring Boot:(二十三)Spring Boot 中使用 Docker

    前言 简单的学习下怎么在 Spring Boot 中使用 Docker 进行构建,发布一个镜像,现在我们通过远程的 docker api 构建镜像,运行容器,发布镜像等操作. 这里只介绍两种方式: 远 ...

  5. Spring Boot(三):Spring Boot 中 Redis 的使用

    Spring Boot 对常用的数据库支持外,对 Nosql 数据库也进行了封装自动化. Redis 介绍 Redis 是目前业界使用最广泛的内存数据存储.相比 Memcached,Redis 支持更 ...

  6. Spring Boot中使用EhCache实现缓存支持

     SpringBoot提供数据缓存功能的支持,提供了一系列的自动化配置,使我们可以非常方便的使用缓存.,相信非常多人已经用过cache了.因为数据库的IO瓶颈.一般情况下我们都会引入非常多的缓存策略, ...

  7. spring boot(三):Spring Boot中Redis的使用

    spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...

  8. Spring Boot中的事务管理

    原文  http://blog.didispace.com/springboottransactional/ 什么是事务? 我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合 ...

  9. Spring Boot中的注解

    文章来源:http://www.tuicool.com/articles/bQnMra 在Spring Boot中几乎可以完全弃用xml配置文件,本文的主题是分析常用的注解. Spring最开始是为了 ...

  10. 在Spring Boot中使用Https

    本文介绍如何在Spring Boot中,使用Https提供服务,并将Http请求自动重定向到Https. Https证书 巧妇难为无米之炊,开始的开始,要先取得Https证书.你可以向证书机构申请证书 ...

随机推荐

  1. Ideas about the future of management

    1. Business markets a. greater competition among companies b. increase in power of global companies ...

  2. Docker安装ruby2.1

    # sudo apt-get install Python-software-properties# sudo apt-add-repository ppa:brightbox/ruby-ng# su ...

  3. Java中sql语句的引号问题

    1..sql语句 在数据库中,当我们查询语句时,会使用类似的语句: Select * from userinfo where userid='1' or 1; Select * from userin ...

  4. 002. Centos7安装mysql5.5.37

    下载cmake-2.8.12.2.tar.gz, 下载地址: https://pan.baidu.com/s/1qYtpX7m 下载mysql-5.5.37.tar.gz, 下载地址: http:// ...

  5. top.location.href和localtion.href有什么不同

    top.location.href=”url”          在顶层页面打开url(跳出框架) self.location.href=”url”         仅在本页面打开url地址 pare ...

  6. mysql学习笔记之基础篇

    数据库学习之基础篇 ① 开放数据库互连(Open Database Connectivity,ODBC ② 结构化查询语言(Structured Query Language) ③ 进入mysql:M ...

  7. BestCoder Round #69 (div.2) Baby Ming and Weight lifting(hdu 5610)

    Baby Ming and Weight lifting Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K ( ...

  8. Warm up 2

    hdu4619:http://acm.hdu.edu.cn/showproblem.php?pid=4619 题意:题目大意:给你两种纸牌 ,一种水平放置共有n张 ,一种竖直放置共有m张.水平放置的纸 ...

  9. IE6下不能定义1px高度的容器和IE6 双边距

        (1)触发的条件---定义一个div,将容器的高度设置成1px (2)  编码得到的结果---在IE6浏览器中,容器的高度不是1px   而是18px (3)出现问题的原因---是因为IE6浏 ...

  10. HDURevenge of Segment Tree(第二长的递增子序列)

    HDURevenge of Segment Tree(第二长的递增子序列) 题目链接 题目大意:这题是求第二长的递增子序列. 解题思路:用n^2的算法来求LIS,可是这里还要记录一下最长的那个序列是否 ...