[英]Life span and injection of @SessionAttributes object in Spring MVC
我不清楚通过 Spring Boot 2.3.3.RELEASE 在 Spring MVC 中使用@SessionAttributes
一些微妙之处。
Step1Controller
和Step2Controller
。@SessionAttributes("foobar")
。Step1Controller
其请求处理过程中@PostMapping
增加了一个特殊FooBar
实例来使用模型model.addAttribute("foobar", new FooBar("foo", "bar"))
Step2Controller
在完全独立的 HTTP POST
下调用,使用doSomething(FooBar fooBar)
在其@PostMapping
服务方法中获取FooBar
实例。但我不清楚它为什么起作用的一些细节。
@SessionAttributes
API 文档部分说明:
一旦处理程序指示其会话会话完成,这些属性将被删除。 因此,将此功能用于此类会话属性,这些属性应该在特定处理程序的会话过程中临时存储在会话中。 对于永久会话属性,例如用户身份验证对象,请改用传统的
session.setAttribute
方法。
@SessionAttributes
仅在 HTTP 会话中临时存储模型属性并在会话结束时将其删除,为什么foobar
仍然出现在对Step2Controller
的请求中? 在我看来,它仍然在会议中。 我不明白文档在提到“临时”和“处理程序的对话”时是什么意思。 看起来foobar
正常存储在会话中。Step1Controller
上使用@SessionAttributes("foobar")
,Spring 就会在处理请求后自动将foobar
从模型复制到会话中。 这在文档中有所暗示,但我只有通过实验才明白这一点。@SessionAttributes("foobar")
放在Step2Controller
,Spring 会在请求之前将foobar
从会话复制到模型。 从文档中我根本不清楚这一点。Step2Controller.doSomething(FooBar fooBar)
,除了@SessionAttributes("foobar")
之外,我在FooBar
参数上没有任何注释(但这是在控制器类上)。 文档似乎表明我需要向方法参数添加@ModelAttribute
注释,例如Step2Controller.doSomething(@ModelAttribute("foobar") FooBar fooBar)
或至少Step2Controller.doSomething(@ModelAttribute FooBar fooBar)
。 但是 Spring 似乎仍然找到了 session 变量,即使在参数上根本没有注释。 为什么? 我怎么会知道这个?这是关于 Spring 一直困扰我的事情之一:太多事情“神奇地”发生,没有明确的文档说明预期会发生什么。 我想那些使用 Spring 多年的人只是对什么有效什么无效有一种“感觉”; 但是查看代码的新开发人员只需要相信它神奇地完成了它应该做的事情。
有人可以澄清为什么我所描述的有效,尤其是在第一个问题上启发了我吗? 也许这样我也可以培养这种“春天的感觉”,本能地知道要唤起哪些咒语。 谢谢你。
这个答案有两部分
SessionAttributes
一般信息@SessionAttributes
@SessionAttributes
的 javadoc声明它应该用于临时存储属性:
during the course of a specific handler's conversation.将此功能用于此类会话属性,这些属性应该在特定处理程序的会话过程中存储在会话中。
这种“对话”的时间边界由程序员明确定义,或者更准确地说:程序员定义对话的完成,他们可以通过SessionStatus
。 这是文档和示例的相关部分:
在第一个请求中,当一个名为
pet
的模型属性被添加到模型中时,它会自动提升并保存在 HTTP Servlet 会话中。 它保持在那里,直到另一个控制器方法使用SessionStatus
方法参数来清除 storage ,如以下示例所示:
@Controller
@SessionAttributes("pet")
public class EditPetForm {
@PostMapping("/pets/{id}")
public String handle(Pet pet, BindingResult errors, SessionStatus status) {
if (errors.hasErrors) {
// ...
}
status.setComplete();
// ...
}
}
如果你想深入挖掘,你可以研究以下源代码:
- 如果
@SessionAttributes
仅在 HTTP 会话中临时存储模型属性并在会话结束时将其删除,为什么foobar
仍然出现在对Step2Controller
的请求中?
因为,很可能您还没有定义对话完成。
在我看来,它仍然在会议中。
确切地
我不明白文档在提到“临时”和“处理程序的对话”时是什么意思。
我想它与Spring WebFlow 有某种关系。 (见这篇介绍性文章)
看起来
foobar
正常存储在会话中。
是的,请参阅DefaultSessionAttributeStore
您可能会在这里问:什么使某些会话属性具有时间性,而有些则不是? 它们是如何区分的? . 答案可以在源代码中找到:
SessionAttributesHandler.java
#L146 :
/**
* Remove "known" attributes from the session, i.e. attributes listed
* by name in {@code @SessionAttributes} or attributes previously stored
* in the model that matched by type.
* @param request the current request
*/
public void cleanupAttributes(WebRequest request) {
for (String attributeName : this.knownAttributeNames) {
this.sessionAttributeStore.cleanupAttribute(request, attributeName);
}
}
- 看起来,只要在
Step1Controller
上使用@SessionAttributes("foobar")
,Spring 就会在处理请求后自动将foobar
从模型复制到会话中。
- 看起来,通过将
@SessionAttributes("foobar")
放在Step2Controller
,Spring 会在请求之前将foobar
从会话复制到模型。
- 最后,请注意,在
Step2Controller.doSomething(FooBar fooBar)
,除了@SessionAttributes("foobar")
之外,我在FooBar
参数上没有任何注释(但这是在控制器类上)。 文档似乎表明我需要向方法参数添加@ModelAttribute
注释,例如Step2Controller.doSomething(@ModelAttribute("foobar") FooBar fooBar)
或至少Step2Controller.doSomething(@ModelAttribute FooBar fooBar)
。 但是 Spring 似乎仍然找到了 session 变量,即使在参数上根本没有注释。 为什么? 我怎么会知道这个?
请参阅方法参数部分:
如果方法参数与此表中的任何早期值都不匹配,并且它是简单类型(由 BeanUtils#isSimpleProperty 确定,则将其解析为 @RequestParam。否则,将其解析为 @ModelAttribute。
这是关于 Spring 一直困扰我的事情之一:太多事情“神奇地”发生,没有明确的文档说明预期会发生什么。 我想那些使用 Spring 多年的人只是对什么有效什么无效有一种“感觉”; 但是查看代码的新开发人员只需要相信它神奇地完成了它应该做的事情。
在这里,我建议阅读参考文档,它可以提供线索如何描述 Spring 的某些特定行为
10/11/2020 更新:
Denis,这种自动应用模型中的参数作为方法参数的能力是否只适用于接口? 我发现如果 FooBar 是一个接口, Step2Controller.doSomething(FooBar fooBar) 的工作原理如上所述。 但是如果 FooBar 是一个类,即使我在模型中有一个 FooBar 的实例, Step2Controller.doSomething(FooBar fooBar) 也会导致“找不到类 FooBar 的主要或默认构造函数”异常。 甚至@ModelAttribute 也行不通。 我必须使用@ModelAttribute("foobar")。 为什么类在参数替换中与接口的工作方式不同?
在我看来,命名/ @SessionAttributes#names
存在一些问题。
我创建了一个示例项目来演示问题可能隐藏在哪里。
该项目分为两部分:
项目的入口点是两个测试(参见ClassFooBarControllerTest
和InterfaceFooBarControllerTest
)
我留下评论来解释这里发生的事情
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.