参考资料
- 从原理层面掌握@SessionAttributes的使用【享学Spring MVC】
- 从原理层面掌握@RequestAttribute、@SessionAttribute的使用【享学Spring MVC】
目录
一. @SessionAttribute注解的使用
- 用来从session中获取存入的值
- ❗❗❗注意,@SessionAttribute注解和@SessionAttributes注解是两个不同的注解,作用不同。
@Controller
@RequestMapping("/test20")
public class Test20Controller {
@Resource
private HttpSession session;
@GetMapping("/init")
public ModelAndView init(HttpSession httpSession, Model model) {
// 准备好要放入session中的值
Map<String, String> userInfoMap = new HashMap<String, String>() {
{
put("name", "贾飞天");
put("age", "18");
}
};
// 向注入得到的session中存入用户的相关信息
session.setAttribute("userInfo1", userInfoMap);
// 向接口session中存入测试信息
httpSession.setAttribute("testInfo", "jmw");
// 指定跳转的页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("test20");
return modelAndView;
}
@GetMapping("/getValueFromSession")
public ResponseEntity<Void> gerValueFromSession(
// 通过@SessionAttribute注解来获取session中的attribute所对应的值
@SessionAttribute(value = "userInfo1", required = false) Map<String, String> userInfo1
) {
// 通过@SessionAttribute注解获取到的值
System.out.println(userInfo1);
// 通过原生的servlet来获取到的session中attribute所对应的值
Map userInfo2 = (Map)session.getAttribute("userInfo1");
System.out.println(userInfo2);
return ResponseEntity.status(HttpStatus.OK).build();
}
}
二. @SessionAttributes注解的使用
- 作用于类上,一般放在Controller层中
- 该注解的作用是将Model中的属性同步到session会话当中,
方便在下一次请求中使用(例如重定向场景)。 - Controller层在向其模型
Model
或ModelAndView
中添加数据时,若该数据匹配@SessionAttributes注解指定的名称或者类型时,就会放入session中。 - 在@SessionAttributes所在的Controller中调用
SessionStatus.setComplete()
方法可清除匹配到session中的数据 - ❗❗❗注意,能从session清空的只是和@SessionAttributes注解匹配的数据,通过session原生的api放入的数据是不会被清除的。
- 只能在本页面调用
SessionStatus.setComplete()
方法清空,若重定向到其他画面,然后在重定向的画面调用SessionStatus.setComplete()
方法是不会清空的。此时只能调用原生session的api清空数据。
2.1 @SessionAttributes存入值,清空值示例
⏹Controller层
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/test20")
// 若向Model或ModelAndView中存入value中的值的话,会匹配到session中
@SessionAttributes(value = {"userInfoMap1", "userInfoMap2", "userInfoMap3"})
public class Test20Controller {
// 注入得到的session对象
@Resource
private HttpSession session;
@GetMapping("/init")
public ModelAndView init(HttpSession httpSession, Model model) {
// 省略......
}
@GetMapping("/redirectMethod")
public String redirectMethod(Model model) {
// 省略......
}
@GetMapping("/clearSessionAttributes")
@ResponseBody
public void clearSessionAttributes(SessionStatus sessionStatus) {
// 省略......
}
}
2.1.1 ⏹Controller层-init方法
- 向Model或ModelAndView中存入的数据,如果名称同@SessionAttributes的value值相匹配,会顺带存入session中。
@GetMapping("/init")
public ModelAndView init(HttpSession httpSession, Model model) {
Map<String, String> userInfoMap = new HashMap<String, String>() {
{
put("name", "贾飞天");
put("age", "18");
}
};
// 向session中存入用户的相关信息,直接通过session的API放入
session.setAttribute("userInfo1", userInfoMap);
httpSession.setAttribute("testInfo", "jmw");
// 向Model中放入数据,因为名称和@SessionAttributes中的value值匹配,所以会顺带放入session中
HashMap<String, String> userInfoMap1 = new HashMap<String, String>() {
{
put("id", "110120");
put("name", "枫叶红");
}
};
model.addAttribute("userInfoMap1", userInfoMap1);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("test20");
// 向ModelAndView中放入数据,因为名称和@SessionAttributes中的value值匹配,所以会顺带放入session中
modelAndView.addObject("userInfoMap3", userInfoMap1);
return modelAndView;
}
2.1.2 ⏹Controller层-clearSessionAttributes方法
@GetMapping("/clearSessionAttributes")
@ResponseBody
public void clearSessionAttributes(SessionStatus sessionStatus) {
System.out.println(session.getAttribute("userInfo1"));
System.out.println(session.getAttribute("testInfo"));
System.out.println(session.getAttribute("userInfoMap1"));
System.out.println(session.getAttribute("userInfoMap2"));
System.out.println(session.getAttribute("userInfoMap3"));
// 清空@SessionAttributes注解valu值所对应的session中的值
sessionStatus.setComplete();
System.out.println("------分割线------");
/*
* 执行完 sessionStatus.setComplete()方法 之后,还是可以获取到
* 但是再一次执行clearSessionAttributes方法的时候,就获取不到了。
* 因为已经被清空
* */
System.out.println(session.getAttribute("userInfo1"));
System.out.println(session.getAttribute("testInfo"));
System.out.println(session.getAttribute("userInfoMap1"));
System.out.println(session.getAttribute("userInfoMap2"));
System.out.println(session.getAttribute("userInfoMap3"));
}
👉第一次执行,可以看到sessionStatus.setComplete()
方法执行完毕之后,依旧能从session中获取到@SessionAttribute注解存入的session值。
👉第二次执行(此时sessionStatus.setComplete()方法已经在第一次被执行过一遍),可以看到
原生Session的API存入的值依然可以获取出来,但是通过@SessionAttributes注解放入session中的值已经被清空了。
2.2 重定向到其他页面示例
@GetMapping("/redirectMethod")
public String redirectMethod(Model model) {
/*
👉👉👉在重定向到其他页面之前,向Model中存入值
因为userInfoMap2这个属性与@SessionAttributes的value值匹配
因此会放到session中
*/
HashMap<String, String> userInfoMap2 = new HashMap<String, String>() {
{
put("id", "119120");
put("name", "张三");
}
};
model.addAttribute("userInfoMap2", userInfoMap2);
// 重定向到其他页面
return "redirect:/test21/init";
}
2.2.1 ⏹重定向到的页面-Controller层
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.Map;
@Controller
@RequestMapping("/test21")
public class Test21Controller {
@Resource
private HttpSession session;
@GetMapping("/init")
public ModelAndView redirectMethod(
// 通过@SessionAttribute注解来获取session中的attribute所对应的值
@SessionAttribute(value = "userInfo1", required = false) Map<String, String> userInfo1FromSession,
@SessionAttribute(value = "userInfoMap1", required = false) Map<String, String> userInfoMap1FromSessionAttributes,
@SessionAttribute(value = "userInfoMap2", required = false) Map<String, String> userInfoMap2FromSessionAttributes,
HttpSession httpSession
) {
// 直接通过session对象来获取数据
System.out.println(session.getAttribute("userInfo1"));
System.out.println(httpSession.getAttribute("userInfo1"));
Object testInfo = httpSession.getAttribute("testInfo");
System.out.println(testInfo);
System.out.println(userInfo1FromSession);
System.out.println(userInfoMap1FromSessionAttributes);
System.out.println(userInfoMap2FromSessionAttributes);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("test21");
return modelAndView;
}
@GetMapping("/sessionStatus")
public ResponseEntity<Void> sessionStatus(HttpSession httpSession, SessionStatus sessionStatus) {
// 省略......
}
}
2.2.2 ⏹重定向到的页面-前台HTML
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<button id="sessionStatus">调用sessionStatus.setComplete()方法</button>
</div>
<script th:src="@{/js/public/jquery-3.6.0.min.js}"></script>
<script th:inline="javascript">
// 获取session中存入的值
const {
userInfo1,
userInfoMap1,
userInfoMap2
} = [[${session}]];
console.log(userInfo1);
console.log(userInfoMap1);
console.log(userInfoMap2);
$("#sessionStatus").click(async function () {
await fetch("/test21/sessionStatus");
});
</script>
</body>
</html>
2.2.3 ⏹效果
2.3 在重定向到的页面调用sessionStatus.setComplete()方法
- @SessionAttributes注解作用在的页面上调用sessionStatus.setComplete()方法才能清空对应的session中的值
@GetMapping("/sessionStatus")
public ResponseEntity<Void> sessionStatus(HttpSession httpSession, SessionStatus sessionStatus) {
System.out.println(httpSession.getAttribute("userInfo1"));
System.out.println(httpSession.getAttribute("testInfo"));
System.out.println(httpSession.getAttribute("userInfoMap1"));
System.out.println(httpSession.getAttribute("userInfoMap2"));
System.out.println(httpSession.getAttribute("userInfoMap3"));
/*
尝试清空通过@SessionAttributes注解放入session中的值
但是因为@SessionAttributes不是在本页面放入的,因此清空失败
只有在原页面调用sessionStatus.setComplete()方法,session中对应的值才会被清空
*/
sessionStatus.setComplete();
// 手动清除session中的userInfoMap3对应的值,这种方式可以清空
httpSession.removeAttribute("userInfoMap3");
System.out.println(httpSession.getAttribute("userInfo1"));
System.out.println(httpSession.getAttribute("testInfo"));
System.out.println(session.getAttribute("userInfoMap1"));
System.out.println(httpSession.getAttribute("userInfoMap2"));
System.out.println(httpSession.getAttribute("userInfoMap3"));
return ResponseEntity.noContent().build();
}
⏹ 尝试在在重定向到的页面调用sessionStatus.setComplete()方法清空对应的session中的值
👉第一次
👉第二次
可以看到根本就无法清除,只有手动调用session的API才能清除。