首页 > 其他分享 >Spring_Session解决Session共享的问题(二十三)

Spring_Session解决Session共享的问题(二十三)

时间:2022-11-10 16:07:27浏览次数:70  
标签:return 二十三 登录 Session Spring session public String


二八佳人体似酥,腰间仗剑斩愚夫。虽然不见人头落,暗里教君骨髓枯。 愿世人皆有所爱,皆可以爱。

上一章简单介绍了SpringBoot整合Ehcache缓存(二十二),如果没有看过,​​请观看上一章​​

关于 Spring-Session 共享,参考了 ​​阿飞云​​ 大牛的文章:
​Spring-Session实现Session共享入门教程​​

为了便于用户查看,将其中的理论部分摘抄下来.

一. Spring-Session介绍

一.一 Spring-Session使用的场景

HttpSession是通过Servlet容器进行创建和管理的,在单机环境中。通过Http请求创建的Session信息是存储在Web服务器内存中,如Tomcat/Jetty。

假如当用户通过浏览器访问应用服务器,session信息中保存了用户的登录信息,并且session信息没有过期失,效那么用户就一直处于登录状态,可以做一些登录状态的业务操作

Spring_Session解决Session共享的问题(二十三)_spring

但是现在很多的服务器都采用分布式集群的方式进行部署,一个Web应用,

可能部署在几台不同的服务器上,通过LVS或者Nginx等进行负载均衡

(一般使用Nginx+Tomcat实现负载均衡)。

此时来自同一用户的Http请求将有可能被分发到不同的web站点中去(如:

第一次分配到A站点,第二次可能分配到B站点)。

那么问题就来了,如何保证不同的web站点能够共享同一份session数据呢?

假如用户在发起第一次请求时候访问了A站点,并在A站点的session中保存了登录信息,

当用户第二次发起请求,通过负载均衡请求分配到B站点了,

那么此时B站点能否获取用户保存的登录的信息呢?答案是不能的,因为上面说明,

Session是存储在对应Web服务器的内存的,不能进行共享,此时Spring-session就出现了,

来帮我们解决这个session共享的问题!

Spring_Session解决Session共享的问题(二十三)_spring_02

一.二 如何进行Session共享

简单点说就是请求http请求经过Filter职责链,根据配置信息过滤器将创建session的权利
由tomcat交给了Spring-session中的SessionRepository,通过Spring-session创建会话,
并保存到对应的地方。

Spring_Session解决Session共享的问题(二十三)_Spring-Session_03

实际上实现Session共享的方案很多,其中一种常用的就是使用Tomcat、Jetty等服务器
提供的Session共享功能,将Session的内容统一存储在一个数据库(如MySQL)
或缓存(如Redis,Mongo)中

二. SpringBoot 整合 Spring-Session 使用

按照上一章节的方式 创建对应的项目 SpringBoot_Session

二.一 pom.xml 添加依赖

<!--依赖 data-redis的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--不能忘记这个依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--添加cache的依赖信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--添加 session的依赖-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

二.二 application.yml 配置redis和session

采用多环境配置

server:
servlet:
context-path: /Session
# 引入 数据库的相关配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true
username: root
password: abc123
# 配置thymeleaf的相关信息
thymeleaf:
# 开启视图解析
enabled: true
#编码格式
encoding: UTF-8
#前缀配置
prefix: classpath:/templates/
# 后缀配置
suffix: .html
#是否使用缓存 开发环境时不设置缓存
cache: false
# 格式为 HTML 格式
mode: HTML5
# 配置类型
servlet:
content-type: text/html
# 配置Redis的使用
redis:
database: 15 # 所使用的数据库 默认是0
host: 127.0.0.1 #所使用的redis的主机地址
port: 6379 # 端口号 默认是 6379
password: zk123 # 密码
timeout: 5000 # 超时时间 5000毫秒
# 连接池 lettuce 的配置
lettuce:
pool:
max-active: 100
min-idle: 10
max-wait: 100000
profiles:
active: 8081 # 默认启用的端口号是 8081
# 配置session的相关信息
session:
store-type: redis # 配置存储的类型
timeout: 3600 # 配置过期时间
redis:
flush-mode: on_save # 保存时刷新
namespace: springSession # 命令空间
#整合mybatis时使用的
mybatis:
#包别名
type-aliases-package: top.yueshushu.learn.pojo
#映射文件路径
mapper-locations: classpath:mybatis/mapper/**/*.xml
configuration:
#日志信息
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 多环境配置,默认为 8081
---
server:
port: 8081
spring:
profiles: 8081

---
server:
port: 8082
spring:
profiles: 8082

二.三 启动类 SessionApplication

@MapperScan("top.yueshushu.learn.mapper")
@SpringBootApplication
//开启缓存
@EnableCaching
public class SessionApplication {
public static void main(String[] args) {
SpringApplication.run(SessionApplication.class,args);
System.out.println("共享 Session");
}
}

不要忘记 CacheConfig 和 RedisConfig 的配置信息

二.四 SessionController 创建和获取Session

创建一个Controller 类, 用于创建和获取对应的Session 信息

@RestController
public class SessionController {
@Value("${server.port}")
private String port;

@RequestMapping("/createSession")
public String createSession(HttpSession httpSession){
String sessionId=httpSession.getId();
httpSession.setAttribute("name",port+",两个蝴蝶飞"+sessionId);
httpSession.setAttribute("sname",port+":abc");
return sessionId+"创建端口号是:"+port+"的应用创建Session,属性是:"+httpSession.getAttribute("name").toString();
}

@RequestMapping("/getSession")
public String getSession(HttpSession httpSession){
return "访问端口号是:"+port+",获取Session属性是:"+httpSession.getAttribute("name").toString();
}
}

二.五 测试Session共享

将项目进行打包, 然后启动项目.

Spring_Session解决Session共享的问题(二十三)_redis_04

启动 8081 端口的项目

java -jar learn-1.0-SNAPSHOT.jar --spring.profiles.active=8081

启动8082的端口的项目

java -jar learn-1.0-SNAPSHOT.jar --spring.profiles.active=8082

8081 项目 创建Session

Spring_Session解决Session共享的问题(二十三)_Spring-Session_05

8082 项目 试图获取Session (不是同一个项目,按照以前的逻辑是获取不到的。 但是添加了 Spring-session 是可以获取到的)

Spring_Session解决Session共享的问题(二十三)_Session权限_06

可以获取到 Session

查看Redis 的存储信息

Spring_Session解决Session共享的问题(二十三)_Spring-Session_07

会存储相关的信息, 但是值是乱码.

可以通过 fastjson 进行序列化,解决这个问题。

在config 包下,添加关于 Spring-Session的配置信息, RedisSessionConfig

  1. 首先要在 pom.xml 添加 fastjson的依赖
<!--解决spring-session处理缓存时乱码的问题-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.69</version>
</dependency>
  1. RedisSessionConfig.java 中进行配置序列化方式
@Configuration
@EnableRedisHttpSession
public class RedisSessionConfig {

@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
// 使用 FastJsonRedisSerializer 来序列化和反序列化redis 的 value的值
FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<>(Object.class);
ParserConfig.getGlobalInstance().addAccept("com.muzz");
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setCharset(StandardCharsets.UTF_8);
serializer.setFastJsonConfig(fastJsonConfig);
return serializer;
}
}

删除以前的Redis数据,重新访问 createSession , 生成新的Session

Spring_Session解决Session共享的问题(二十三)_redis_08

解决乱码问题。

但是,不建议这么做. 老蝴蝶只是演示一下,项目里面,不用这一个.

三. SpringSession 的详细使用

关于访问权限的问题,可以看老蝴蝶以前写的文章: ​​RBAC​​

关于 静态资源的问题, 可以看老蝴蝶以前写的文章: ​​SpringBoot静态资源整合Bootstrap(十)​​

三.一 pom.xml 添加依赖

<!--依赖 data-redis的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--不能忘记这个依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--添加cache的依赖信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--添加 session的依赖-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!--解决spring-session处理缓存时乱码的问题-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.69</version>
</dependency>


<!--引入 spring-boot-starter-thymeleaf的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--添加一个webjar jquery-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
<!--引入bootstrap-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.4.1</version>
</dependency>

三.二 数据库信息

在 springboot 数据库里面 添加 user 表, 创建两个用户.

/*!40101 SET NAMES utf8 */;
-- 创建员工 user 表
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(15) DEFAULT NULL,
`sex` varchar(20) DEFAULT NULL,
`age` int(6) DEFAULT NULL,
`description` varchar(50) DEFAULT NULL,
`account` varchar(100) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_un_account` (`account`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 插入员工信息数据

insert into user(name,sex,age,description,account,password)
values ('两个蝴蝶飞','男',26,'一个快乐的程序员','yzl','123456'),
('周小欢','女',22,'一个小坏蛋','zxh','123456');

三.三 其他的相关信息

application.yml 配置信息不需要改变, 启动类不需要改变,

创建User 类和 对应的 mapper, service 等基础的服务( 与Mybatis一样)

提供一个 根据员工的 账号 account 和 密码 password 的查询服务

UserServiceImpl.java

@Override
public User findByAccountAndPassword(String name, String password) {
return userMapper.findByAccountAndPassword(name,password);
}

UserMapper.xml

<select id="findByAccountAndPassword" resultType="top.yueshushu.learn.pojo.User">
select * from user where account=#{account}
and password=#{password}
</select>

三.四 添加登录和权限拦截的过滤器 LoginInterceptor

放置在 interceptor 包下.

LoginInterceptor.java类:

package top.yueshushu.learn.interceptor;

import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import top.yueshushu.learn.pojo.User;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;

/**
*
* @author 两个蝴蝶飞
*
* 登录和授权拦截器
*/
public class LoginInterceptor implements HandlerInterceptor {


//不需要登录验证的url
private static List<String> noLoginValidateUrl;
//不需要权限验证的url
private static List<String> noPriValidateUrl;

//跳转到的登录页面
private static String LOGIN_URL;
//没有权限的界面
private static String NO_PRIVILEGE_URL;

static{
noLoginValidateUrl=new ArrayList<String>();

//静态资源
noLoginValidateUrl.add("/static/");
noLoginValidateUrl.add("/webjars/");
noLoginValidateUrl.add("/templates/");
noLoginValidateUrl.add("/login.html");
noLoginValidateUrl.add("/noPrivilege.html");
//登录页面
noLoginValidateUrl.add("/toLogin");
//登录方法
noLoginValidateUrl.add("/login");


noPriValidateUrl=new ArrayList<String>();


LOGIN_URL="/login.html";

NO_PRIVILEGE_URL="/noPrivilege.html";
}
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
// TODO 自动生成的方法存根

}

@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
// TODO 自动生成的方法存根

}

@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object arg2) throws Exception {

//获取Session
HttpSession session=req.getSession();

//请求路径
String realPath=req.getRequestURI();
System.out.println("地址是:"+realPath);

//验证是否在 不需要验证登录的url里面
if(isContain(realPath,1)){
return true;
}
//如果为空,表示没有登录
if(session.getAttribute("loginUser")==null){
req.getRequestDispatcher(LOGIN_URL).forward(req,resp);
return false;
}else{
//最后一个 //为权限
String privilegeUrl=realPath.substring(realPath.lastIndexOf("/"));
//如果不为空,表示登录了。
//重新获取全部权限 , 需要缓存, 这儿不用缓存。
User user=(User)session.getAttribute("loginUser");
List<String> privileges=(List<String>)session.getAttribute("privilegeList_"+user.getId());
boolean isHavePri=true;
if(CollectionUtils.isEmpty(privileges)||!privileges.contains(privilegeUrl)){
isHavePri=false;
}
if(isHavePri){
//放行
return true;
}else{
req.getRequestDispatcher(NO_PRIVILEGE_URL).forward(req,resp);
return false;
}
}

}
private boolean isContain(String realPath,int type){
List<String> urls;
if(type==1){
urls=noLoginValidateUrl;
}else{
urls=noPriValidateUrl;
}

boolean flag=false;

for(String url:urls){
//包括,返回-1
if(realPath.indexOf(url)!=-1){
flag=true;
break;
}
}
return flag;

}
}

三.五 配置静态资源,添加过滤器

以前的 MvcConfig 静态资源配置

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
/**
* 配置静态的资源信息
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
//映射 static 目录
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
//放置其他 业务页面资源
registry.addResourceHandler("/**").addResourceLocations("classpath:/templates/");
}
@Override
public void addInterceptors(InterceptorRegistry registry)
{
//注册自己的拦截器并设置拦截的请求路径
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
}

三.六 配置静态资源

三.六.一 static 目录下添加库

Spring_Session解决Session共享的问题(二十三)_redis_09

三.六.二 templates 目录下 添加静态页面

Spring_Session解决Session共享的问题(二十三)_Spring-Session_10

login.html 是登录页, main.html 是登录后的展示主页 noPrivilege 是没有权限展示的页面

add delete select update 是 添加,删除,查询,修改的页面,表示 员工的权限。

login.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录页面</title>
<link rel="StyleSheet" href="static/bootstrap/css/bootstrap.css" type="text/css">
<script type="text/javascript" src="static/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="static/bootstrap/js/bootstrap.js"></script>
</head>
<script>
$(function(){
$("#submit").click(function(){
var code=$("#code").val();
var password=$("#password").val();

var info=new Object();
//传入进去,员工的id编号
info.account=code;
info.password=password;

$.post("User/login",info,function(data){
if(data.code==200){
alert("登录成功");
window.location.href="toMain";
}else{
alert("用户名或者密码错误");
}
},"json")

})
})
</script>
<body>
<div class="col-sm-6 col-sm-offset-3">
<div style="margin-top:40px;">
<div class="row col-md-offset-3 ">
<h3>登录页面</h3>
</div>
<div class="row" style="margin-top:30px;">
<form class="form-horizontal" role="form">

<div class="form-group">
<label for="code" class="col-md-3 control-label">用户名:</label>
<div class="col-md-4">
<input type="text" class="form-control" id="code"
name="code" value=""/>
</div>

</div>
<div class="form-group">
<label for="password" class="col-md-3 control-label">密码:</label>
<div class="col-md-4">
<input type="password" class="form-control" id="password"
name="password"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4">
<input type="button" value="登录" id="submit" class="btn btn-success"/>
</div>
</div>
</form>
</div>
</div>
</div>
</body>
</html>

main.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录成功,展示首页</title>
</head>
<body>
登录成功 <span>
<a href="javascript:void(0);" onclick="window.location.href='User/logout'">退出</a>
</span>
</body>
</html>

其他的页面,都是一句话描述

<body>
抱歉,您没有权限访问!!!
</body>
<body>
添加用户
</body>
<body>
删除用户
</body>
<body>
查询用户
</body>
<body>
修改用户
</body>

三.七 页面跳转 Controller PageController

@Controller
public class PageController {
@RequestMapping("/toLogin")
//跳转到登录页面
public String toLogin(){
return "login";
}


@RequestMapping("/toMain")
//跳转到登录页面
public String toMain(){
return "main";
}
@RequestMapping("/add")
//跳转到登录页面
public String add(){
return "add";
}


@RequestMapping("/update")
//跳转到登录页面
public String update(){
return "update";
}
@RequestMapping("/select")
//跳转到登录页面
public String select(){
return "select";
}

@RequestMapping("/delete")
//跳转到登录页面
public String delete(){
return "delete";
}
}

三.八 员工的登录和注销

UserController.java

@Controller
@RequestMapping("/User")
public class UserController {
@Autowired
private UserService userService;

@RequestMapping("/login")
@ResponseBody
public OutputResult login(User userInfo, HttpSession session){
//将请求信息,封装到对象里面。
Map<String,Object> map=new HashMap<String,Object>();
//从数据库中查询密码
User user=userService.findByAccountAndPassword(userInfo.getAccount(),
userInfo.getPassword());
if(null==user){
return OutputResult.fail();
}

//说明登录成功,放置到session里面
session.setAttribute("loginUser",user);
//模拟设置权限
List<String> privileges=getPrivilegeByAccount(user.getAccount());
session.setAttribute("privilegeList_"+user.getId(),privileges);
//登录成功
return OutputResult.success(user);
}
/**
* 模拟权限 yzl 有 添加的 权限, zxh有 delete的权限。 都没有 update 的权限
* @date 2021/10/21 17:39
* @author zk_yjl
* @param account
* @return java.util.List<java.lang.String>
*/
private List<String> getPrivilegeByAccount(String account) {
List<String> privileges = new ArrayList<>();
privileges.add("/select");
privileges.add("/toMain");
privileges.add("/logout");
if("yzl".equals(account)){
privileges.add("/add");
}else{
privileges.add("/delete");
}
return privileges;

}

@RequestMapping("/logout")
//退出登录
public String logout(HttpSession session){
//注销
session.invalidate();
return "login";
}

其中 OutputResult 与以前是一样的.

/**
* @ClassName:OutputResult
* @Description 返回的响应实体信息
* @Author 岳建立
* @Date 2021/1/1 10:09
* @Version 1.0
**/
@Data
public class OutputResult implements Serializable {
/**
* @param code 响应代码
* @param message 响应信息
* @param data 响应的数据
*/
private Integer code;
private String message;
private Map<String,Object> data=new HashMap<String,Object>();

/**
* 构造方法 私有。 避免外部构造
*/
private OutputResult(){

}
/**
* 成功
* @return
*/
public static OutputResult fail(){
OutputResult outputResult=new OutputResult();
outputResult.code=500;
outputResult.message="失败";
return outputResult;
}

/**
* 成功
* @return
*/
public static OutputResult success(){
OutputResult outputResult=new OutputResult();
outputResult.code=200;
outputResult.message="成功";
return outputResult;
}

/**
* 成功
* @param data 要响应的数据
* @return
*/
public static OutputResult success(Object data){
OutputResult outputResult=new OutputResult();
outputResult.code=200;
outputResult.message="成功";
outputResult.data.put("result",data);
return outputResult;
}
}

三.九 模拟验证

将项目进行打包, 然后启动项目.

Spring_Session解决Session共享的问题(二十三)_redis_11

启动 8081 端口的项目

java -jar learn-1.0-SNAPSHOT.jar --spring.profiles.active=8081

启动8082的端口的项目

java -jar learn-1.0-SNAPSHOT.jar --spring.profiles.active=8082

在 8081 项目上 直接 请求 /select 进行查询, 会跳转到登录的页面

Spring_Session解决Session共享的问题(二十三)_json_12

输入正确的用户名和密码, yzl/12345 会登录进去

Spring_Session解决Session共享的问题(二十三)_redis_13

可以进行查询

Spring_Session解决Session共享的问题(二十三)_json_14

但是如果是修改和删除的话

Spring_Session解决Session共享的问题(二十三)_Spring-Session_15

Spring_Session解决Session共享的问题(二十三)_json_16

是没有权限的

直接访问 8082 项目,查询 select 用户, 是可以直接访问的

Spring_Session解决Session共享的问题(二十三)_spring_17

如果在 8081 项目上 退出登录

Spring_Session解决Session共享的问题(二十三)_Session权限_18

那么在 8081项目上会退出,

同时8082 项目上,也将无法访问

Spring_Session解决Session共享的问题(二十三)_Session权限_19

这是整合 Spring-Session的基本用法。

本章节的代码放置在 github 上:

​https://github.com/yuejianli/springboot/tree/develop/SpringBoot_Session​

谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!


标签:return,二十三,登录,Session,Spring,session,public,String
From: https://blog.51cto.com/u_13420484/5841791

相关文章

  • SpringBoot整合Redis(十九)
    二八佳人体似酥,腰间仗剑斩愚夫。虽然不见人头落,暗里教君骨髓枯。上一章简单介绍了多数据源配置MyBatisPlus(十八),如果没有看过,​​请观看上一章​​一.Redis的介绍和安装......
  • SpringBoot整合Velocity(十二)
    二八佳人体似酥,腰间仗剑斩愚夫。虽然不见人头落,暗里教君骨髓枯。上一章简单介绍了SpringBoot整合FreeMarker(十一),如果没有看过,​​请观看上一章​​学习整合之前,可以看一......
  • SpringBoot通过Cors解决跨域问题(三十一)
    上一章简单介绍了SpringBoot全局异常处理(三十),如果没有看过,​​请观看上一章​​本章节参考江南一点雨大神的文章:​​SpringBoot2系列教程(十四)CORS解决跨域问题......
  • SpringMVC的单文件上传,多文件上传和下载文件(十二)
    你未看此花时,则此花与汝心同归于寂,你来看此花时,此花颜色一时明白起来,便知此花不在你的心外。上一章简单介绍了SpringMVC的数据验证和JSR303国际化显示(十一),如果没有看......
  • SpringBoot自定义Starter(二十四)
    即使有一天,我放弃了自己的身体,也请你,不要放弃我,我亲爱的灵魂.上一章简单介绍了Spring_Session解决Session共享的问题(二十三),如果没有看过,​​请观看上一章​​一.自定义......
  • SpringBoot上传和下载文件(二十七)
    当死亡来临,每一个人都不会接受自己的命运,他们会反抗.上一章简单介绍了SpringBoot启用Https(二十六),如果没有看过,​​请观看上一章​​文件上传和下载,是常用的功能可以看老......
  • SpringBoot全局异常处理(三十)
    生活打了我们一巴掌,我们,一定要想办法再打回来上一章简单介绍了SpringBoot上传文件到远程服务器(二十九),如果没有看过,​​请观看上一章​​一.为什么要实现异常信息自定义......
  • SpringBoot自定义日志Starter(二十五)
    即使有一天,我放弃了自己的身体,也请你,不要放弃我,我亲爱的灵魂.上一章简单介绍了SpringBoot自定义Starter(二十四),如果没有看过,​​请观看上一章​​一.AOP实现日志功能......
  • SpringBoot整合Ehcache缓存(二十二)
    二八佳人体似酥,腰间仗剑斩愚夫。虽然不见人头落,暗里教君骨髓枯。上一章简单介绍了SpringBoot整合Cache缓存技术(二十一),如果没有看过,​​请观看上一章​​一.Ehcache关于......
  • SpringBoot整合Redis_Jedis版(二十)
    二八佳人体似酥,腰间仗剑斩愚夫。虽然不见人头落,暗里教君骨髓枯。上一章简单介绍了SpringBoot整合Redis(十九),如果没有看过,​​请观看上一章​​SpringBoot2.0版本之后,采......