摘自:https://www.2cto.com/kf/201701/591954.html
先讲讲本次遇到的bug所在,在平常的开发中时长遇到要从session里面拿属性的一些时候,所以程序里面多了很多类是于这样的代码
1
2
3
4
|
public
String toQuestionDetail(HttpSession httpSession) {
if
(httpSession.getAttribute(
"user"
) ==
null
) {
return
"public/loginTip"
;
}
else
{.....
|
开发的时候经常要注入session然后再从session里面取属性进行判断,这样让我感觉非常的烦恼,为什么不能直接注入到方法里面简化开发呢?
后来百度之
认识到了@SessionAttributes,在认识不深刻的情况下就开始使用了,使用的大致流程是这样的,在控制器类的最上面写上你要拿到session里面的值,代码如下:
1
2
3
4
5
|
@Controller
@RequestMapping
(
"/view"
)
@SessionAttributes
(
"user"
)
public
class
PublicController {
....
|
然后就可以直接在方法里面直接使用下面代码对session属性值进行注入
1
2
3
4
5
|
//对比第一例代码
public
String toQuestionDetail(User user) {
if
(user ==
null
) {
return
"public/loginTip"
;
}
else
{.....
|
让我用起来非常爽。
终于有一天,发现问题来了,做一个退出登录,原本代码如下:
1
2
3
4
5
6
7
|
@RequestMapping
(
"/tologout"
)
public
String tologout(HttpSession session,HttpServletRequest request,HttpServletResponse response) {
session.removeAttribute(
"user"
);
session.removeAttribute(
"date"
);
System.out.println(
"logout:"
+session.getAttribute(
"user"
));
return
"redirect:/view/index"
;
}
|
进行退出,然后打印的代码如下:
1
|
logout:
null
|
在我认知里,”user”属性已经从session里面移除了,没想到,在服务器端打印完了为空但是jsp页面里面却仍然能读取到 session里面的user属性(非常的诡异现象)
前端代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<
c:if
test
=
"${sessionScope.user.account==null}"
>
<
li
><
a
href
=
"/fs/view/tologin"
>登陆</
a
></
li
>
</
c:if
>
<
c:if
test
=
"${sessionScope.user.account!=null}"
>
<
li
>${sessionScope.user.account}</
li
>
</
c:if
>
<
c:if
test
=
"${sessionScope.user.account!=null}"
>
<
li
><
a
href
=
"/fs/view/tologout"
>退出登录</
a
></
li
>
</
c:if
>
<
c:if
test
=
"${sessionScope.user.account==null}"
>
<
li
><
a
href
=
"/fs/view/toregister"
>注册</
a
></
li
>
</
c:if
>
|
明明是删除了,为什么前端里面还显示有user对象,我就奇了怪了,我还以为是缓存问题,还写了测试代码
1
2
3
4
5
6
|
<
input
type
=
"hidden"
value
=
"<%=session.getAttribute("
user") %>"/>
<% SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
Date date = new Date();
String t = sdf.format(date);
%>
<
input
type
=
"hidden"
value="<%=t %>"/>
|
最后发现他吗时间是不同的,也就是说代码是动态变化的,那我删除了的user属性,是被谁又移进去了呢?百思不得其解…调了一天发现原来就是
@SessionAttributes 的问题
先跟我一起来认真掌握@SessionAttributes的具体用法再告诉大家我的解决方案
如果希望多个请求之间共享某个模型数据,可以在控制器类标注@SessionAttributes,spring mvc会将模型中对应的属性暂存到httpSession中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@SessionAttributes
(“user”)
public
class
UserController {
@ModelAttribute
(“user”) publicUser getUser() {
Useruser =
new
User();
User.setUserId(“
1001
”);
returnuser;
}
@RequestMapping
(“ / handle71”)
public
String handle71(
@ModelAttribute
(“user”) User user) {
user.setName(“John”);
return
“redirect: /user/handle71.html”;
}
@RequestMapping
(value =
"/handle72"
)
public
String handle72(ModelMapmodelMap, SessionStatus sessionStatus) {
User user = (User) modelMap.get(“user”);
If(user !=
null
) {
user.setName(“jetty”);
sessionStatus.setComplete();
}
return
"user/showUser"
;
}
}
|
Spring
mvc在调用处理方法前,在请求线程中自动一个隐含的模型对象,调用所有在方法级别标注了@ModelAttribute的方法,并将方法返回值添加到隐含模型中,查看Session中是否存在@SessionAttributes(“xxx”)所指定的xxx属性,如果有,将其添加到隐含模型中,如果隐含模型已经存在xxx属性,该步骤会覆盖隐含模型中已有的属性值。
对应标注了@ModelAttribute(“xxx”)方法的入参按如下流程
如果隐含模型包含了xxx属性,将其赋给该入参,再用请求消息填充该入参对象直接返回,否则如果xxx是会话属性,即处理类定义出标注了@SessionAttributes(“xxx”),则尝试从该会话中获取该属性,并将其值赋给入参,然后再用请求填充该入参对象,如会话中找不到xxx属性,则抛出HttpSessionRequiredException,如果隐含模型不存在xxx属性,且xxx也不是会话属性,则创建入参的对象实例,在用请求填充该入参。
@SessionAttributes 允许指定多个属性。你可以通过字符串数组的方式指定多个属性,如
@SessionAttributes({“attr1”,”attr2”})。此外,@SessionAttributes
还可以通过属性类型指定要 session 化的 ModelMap 属性,如 @SessionAttributes(types =
User.class),当然也可以指定多个类,如@SessionAttributes(types =
{User.class,Dept.class}),还可以联合使用属性名和属性类型指定:@SessionAttributes(types =
{User.class,Dept.class},value={“attr1”,”attr2”})。
@SessionAttributes需要清除时,使用SessionStatus.setComplete();来清除。注意,它只清除@SessionAttributes的session,不会清除HttpSession的数据
@SessionAttributes需要清除时,使用SessionStatus.setComplete();来清除。注意,它只清除@SessionAttributes的session,不会清除HttpSession的数据
@SessionAttributes需要清除时,使用SessionStatus.setComplete();来清除。注意,它只清除@SessionAttributes的session,不会清除HttpSession的数据
@SessionAttributes需要清除时,使用SessionStatus.setComplete();来清除。注意,它只清除@SessionAttributes的session,不会清除HttpSession的数据
重要的事情说三遍!
看到这里大家应该知道问题的所在,@SessionAttributes 自动又把user属性添加回session里面去了。
所以我们只要把刚刚的退出登录方法按如下方法进行修改,就可以解决上述问题
1
2
3
4
5
6
7
8
|
@RequestMapping
(
"/tologout"
)
public
String tologout(HttpSession session,HttpServletRequest request,HttpServletResponse response,SessionStatus sessionStatus) {
session.removeAttribute(
"user"
);
session.removeAttribute(
"date"
);
System.out.println(
"logout:"
+session.getAttribute(
"user"
));
sessionStatus.setComplete();
return
"redirect:/view/index"
;
}
|
SessionStatus也是通过参数注入进去的哟
SessionStatus
1
2
|
public
interface
SessionStatus
Simple
interface
that can be injected into handler methods, allowing them to signal that their session processing is complete. The handler invoker may then follow up with appropriate cleanup, e.g. of session attributes which have been implicitly created during
this
handler's processing (according to the
@SessionAttributes
annotation).
|
1
|
简单的接口,可以注入到处理方法,让他们的知道他们的session处理是完整的。该处理程序调用可以按照适当的清理,session属性已被该处理程序的处理
|
1
2
3
4
5
6
7
8
9
|
setComplete
void
setComplete()
Mark the current handler's session processing as complete, allowing
for
cleanup of session attributes.
//标记session属性已经完成
isComplete
boolean
isComplete()
Return whether the current handler's session processing has been marked as complete.
//告诉session属性是否完成
|