【问题标题】:Spring MVC @ModelAttribute @SessionAttributes - Why does a model attribute need a @ModelAttribute annotated method?Spring MVC @ModelAttribute @SessionAttributes - 为什么模型属性需要 @ModelAttribute 注释方法?
【发布时间】:2013-11-10 13:23:03
【问题描述】:

这就是现在的样子:

@SessionAttributes("shoppingCart")
public class ItemController {

    @ModelAttribute
    public ShoppingCart createShoppingCart() {
        return new ShoppingCart();
    }

    @RequestMapping(value=RequestMappings.ADD_TO_CART + RequestMappings.PARAM_ITEM_ID, method=RequestMethod.GET)
    public String addToCart(@PathVariable("itemId") Item item, @ModelAttribute ShoppingCart shoppingCart) {

        if(item != null) {
            shoppingCartService.addItem(shoppingCart, item);
        }

        return ViewNamesHolder.SHOPPING_CART;
    }
}

第一次调用addToCart方法时,会通过createShoppingCart方法初始化shoppingCart对象。 addToCart 方法运行后,初始化的对象将被添加到会话中,并将从会话中使用以供以后使用。这意味着 createShoppingCart 方法只被调用一次(只要它没有从会话中删除)。

为什么 Spring 通过在需要的时候简单地创建这个对象来消除对 ModelAttribute 带注释的初始化方法的需要?然后看起来就更简单了:

@SessionAttributes("shoppingCart")
public class ItemController {

    @RequestMapping(value=RequestMappings.ADD_TO_CART + RequestMappings.PARAM_ITEM_ID, method=RequestMethod.GET)
    public String addToCart(@PathVariable("itemId") Item item, @ModelAttribute ShoppingCart shoppingCart) {

        if(item != null) {
            shoppingCartService.addItem(shoppingCart, item);
        }

        return ViewNamesHolder.SHOPPING_CART;
    }
}

只要在会话中找不到 shoppingCart 对象,它就会被其默认构造函数初始化。 您认为做出该决定的原因是什么?

【问题讨论】:

    标签: java spring spring-mvc modelattribute


    【解决方案1】:

    我不能直接代表 Spring 团队发言,但是您的建议会将所需的 ModelAttribute 限制为每个请求(在存储在会话中之前)新创建的实例,但是如果您想从完全例如,从数据存储中获取的填充对象?您的方法无法做到这一点。但是,这很有效:

    @ModelAttribute
    public ShoppingCart createShoppingCart() {
        ...
        return someShoppingCartRepo.find(...);
    }
    

    当然,这只是一种可能的情况,其中一个单独的方法的用处应该是显而易见的。

    评论后编辑

    您可以轻松地创建自己的HandlerMethodArgumentResolver,如果不存在对象的新实例,它会为您提供一个新的实例,但考虑到使用您的createShoppingCart() 方法是多么容易,这可能是矫枉过正。如果您使用的是 xml 配置,它会是这样的:

    <mvc:annotation-driven ...>
        <mvc:argument-resolvers>
            <bean class="yourpackage.YourCustomArgumentResolver" />
        </mvc:argument-resolvers>
    </mvc:annotation-driven>
    

    您可以扩展任意数量的现有HandlerMethodArgumentResolver 基类,或者您可以自己直接实现接口,但很可能您会使用这样的东西:

    public class YourCustomArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
        // Implement/override methods for creating your model object when encountered as a method argument
    }
    

    要识别您的论点,您可以创建自定义注释:

    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface YourAutoCreateModelAttribute {
    
        String value() default "";
    
        boolean required() default true;
    
        String defaultValue() default ValueConstants.DEFAULT_NONE;
    }
    

    然后像这样注释您的方法以启动您的自定义解析器:

    @RequestMapping(...)
    public String doStuff(@YourAutoCreateModelAttribute ShoppingCart shoppingCart, ...) {
        // Now your shoppingCart will be created auto-magically if it does not exist (as long as you wrote your resolver correctly, of course.
    }
    

    【讨论】:

    • 我不是这个意思。当然,在许多情况下,我们需要单独的初始化方法。您的代码就是一个完美的例子。但是如果我们不想有一个单独的方法,Spring 也可以通过不抛出 HttpSessionRequiredException 来支持它,而是通过简单地使用其默认构造函数初始化对象并将其放入会话中......
    • 但是,如果您的用例需要一种在模型尚不存在的情况下引发异常的方法,该怎么办?例如,特别是对于存储在会话中的模型属性,如果您有几个控制器是逻辑流的一部分,并且它们都使用相同的模型属性,但第二个和第三个控制器需要模型属性已经存在怎么办?如果 Spring 以某种方式自动创建模型属性,如果在模型存储在与第一个控制器的会话中之前访问了第二个控制器,您将无法处理 HttpSessionRequiredException。
    • 你是对的。在几乎所有控制器中使用几乎无用的 3 行 @ModelAttribute public ShoppingCart createShoppingCart() { return new ShoppingCart();} 对我来说看起来很尴尬。我会更高兴为这个初始化提供另一个快捷方式。像这样:“ @SessionAttributes("shoppingCart", new ShoppingCart())"..
    • 更新了我的答案,如果您愿意,可以开始使用自定义解决方案。
    • 我现在有机会检查您的方法。正如您已经写过的那样,对于这种情况来说这是一种矫枉过正,但背后的想法是超级的。我不知道春天的这种可能性。谢谢!
    猜你喜欢
    • 2013-04-28
    • 2012-12-02
    • 2014-09-20
    • 2018-01-10
    • 2021-04-12
    • 2019-03-25
    • 2012-12-20
    • 1970-01-01
    相关资源
    最近更新 更多