我不清楚使用 @SessionAttributes
的一些微妙之处通过 Spring Boot 2.3.3.RELEASE 在 Spring MVC 中。
Step1Controller
和 Step2Controller
. @SessionAttributes("foobar")
在类(class)层面。 Step1Controller
在处理 @PostMapping
的请求期间添加一个特殊的FooBar
使用 model.addAttribute("foobar", new FooBar("foo", "bar"))
将实例添加到模型中. Step2Controller
, 在完全独立的 HTTP POST
下调用,拿起FooBar
@PostMapping
中的实例使用 doSomething(FooBar fooBar)
的服务方法. 但我不清楚它为什么起作用的一些细节。
@SessionAttributes
API 文档部分说明:Those attributes will be removed once the handler indicates completion of its conversational session. Therefore, use this facility for such conversational attributes which are supposed to be stored in the session temporarily during the course of a specific handler's conversation. For permanent session attributes, e.g. a user authentication object, use the traditional
session.setAttribute
method instead.
@SessionAttributes
仅在 HTTP session 中临时存储模型属性并在 session 结束时将其删除,为什么 foobar
仍然出现在对 Step2Controller
的请求中?在我看来,仍然在 session 中。当文档提到“临时”和“处理程序的对话”时,我不明白文档的含义。它会出现 foobar
正常存储在 session 中。 @SessionAttributes("foobar")
在 Step1Controller
, Spring 会自动复制 foobar
处理请求后从模型到 session 。这在文档中有所暗示,但只有通过实验我才变得清楚。 @SessionAttributes("foobar")
在 Step2Controller
, Spring 副本foobar
在请求之前从 session 到模型。从文档中我根本不清楚。 Step2Controller.doSomething(FooBar fooBar)
我在 FooBar
上根本没有任何注释。参数,除了 @SessionAttributes("foobar")
(但那是在 Controller 类上)。 documentation似乎表明我需要添加 @ModelAttribute
方法参数注解,如Step2Controller.doSomething(@ModelAttribute("foobar") FooBar fooBar)
或至少 Step2Controller.doSomething(@ModelAttribute FooBar fooBar)
.但是 Spring 似乎仍然可以找到 session 变量,即使参数上根本没有注释。为什么?我怎么会知道这个? 这是 Spring 一直困扰我的事情之一:太多的事情“神奇地”发生,没有明确的文档说明预期会发生什么。我想那些使用 Spring 多年的人只是对哪些有效哪些无效有一种“感觉”;但是查看代码的新开发人员只需要相信它神奇地完成了它应该做的事情。
有人可以澄清为什么我所描述的工作,特别是在第一个问题上启发我吗?也许这样我也可以发展这种“ Spring 的感觉”,本能地知道要唤起哪些咒语。谢谢你。
最佳答案
这个答案有两个部分
SessionAttributes
的一般信息@SessionAttributes
在 Spring @SessionAttributes
's javadoc声明它应该用于临时存储属性:use this facility for such conversational attributes which are supposed to be stored in the session temporarily during the course of a specific handler's conversation.
这种“对话”的时间边界由程序员明确定义,或者更准确地说:程序员定义对话的完成 , 他们可以通过
SessionStatus
. Here是文档和示例的相关部分:On the first request, when a model attribute with the name,
pet
, is added to the model, it is automatically promoted to and saved in the HTTP Servlet session. It remains there until another controller method uses aSessionStatus
method argument to clear the storage, as the following example shows:
@Controller
@SessionAttributes("pet")
public class EditPetForm {
@PostMapping("/pets/{id}")
public String handle(Pet pet, BindingResult errors, SessionStatus status) {
if (errors.hasErrors) {
// ...
}
status.setComplete();
// ...
}
}
如果您想深入挖掘,可以研究以下源代码:SessionAttributesHandler
通过问题
- If
@SessionAttributes
only stores model attributes in the HTTP session temporarily and removes them at the end of the conversation, why doesfoobar
still show up in the request toStep2Controller
?
因为,很可能您还没有定义对话完成。
It appears to me to still be in the session.
确切地
I don't understand what the docs mean when they refer to "temporarily" and "handler's conversation".
我猜它与 Spring WebFlow 有某种关系。 . (见 this 介绍性文章)
It would appear
foobar
is stored in the session normally.
是的,见
DefaultSessionAttributeStore
你可以在这里问:是什么使某些 session 属性具有时间性,而另一些则不是?它们是如何区分的? .答案可以在源代码中找到:
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);
}
}
- It would appear that simply by having
@SessionAttributes("foobar")
onStep1Controller
, Spring will automatically copyfoobar
from the model to the session after handling the request.
Yes, it will
- It would appear that by placing
@SessionAttributes("foobar")
onStep2Controller
, Spring copiesfoobar
from the session to the model before the request.
Also true
- And finally, note that in
Step2Controller.doSomething(FooBar fooBar)
I don't have any annotation at all on theFooBar
parameter, other than the@SessionAttributes("foobar")
(but that is on the controller class). The documentation seemed to indicate I need to add a@ModelAttribute
annotation to the method parameter, such asStep2Controller.doSomething(@ModelAttribute("foobar") FooBar fooBar)
or at leastStep2Controller.doSomething(@ModelAttribute FooBar fooBar)
. But Spring still seems to find the session variable, even with no annotation at all on the parameter. Why? How would I have known this?
见 Method Arguments部分:
If a method argument is not matched to any of the earlier values in this table and it is a simple type (as determined by BeanUtils#isSimpleProperty, it is a resolved as a @RequestParam. Otherwise, it is resolved as a @ModelAttribute.
This is on of the things that has always bugged me about Spring: too many things happen "magically", with no clear documentation of what is expected to happen. People who use Spring for years I suppose just get a "feel" for what works and doesn't; but a new developer looking at the code just has to trust that it magically does what it's supposed to.
在这里,我建议您阅读引用文档,它可以提供一个线索,您如何描述 Spring 的某些特定行为
2020 年 10 月 11 日更新 :
Denis, does this ability to automatically apply an argument from the model as a method argument only work with interfaces? I've found that if FooBar is an interface, Step2Controller.doSomething(FooBar fooBar) works as discussed above . But if FooBar is a class, even if I have an instance of FooBar in the model, Step2Controller.doSomething(FooBar fooBar) results in a "No primary or default constructor found for class FooBar" exception. Even @ModelAttribute won't wor k. I have to use @ModelAttribute("foobar"). Why do classes work differently from interfaces in parameter substitution?
在我看来,命名/
@SessionAttributes#names
存在一些问题.我创建了一个 sample project证明问题可能隐藏在哪里。
该项目有两个部分:
该项目的入口点是两个测试(见
ClassFooBarControllerTest
和 InterfaceFooBarControllerTest
)我留下评论来解释这里发生了什么
关于java - Spring MVC 中@SessionAttributes 对象的生命周期和注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63649999/