为什么这里不需要@ModelAttribute?

时间:2021-04-12 13:27:08

标签: spring spring-mvc

我是 Spring 的新手,正在阅读“Spring in Action”(这确实是一本好书)。 在这里,我在本书的一个示例中遇到了一个问题,让我很困惑。 在这个example中,有两个控制器分别对应两条路径。一个是 DesignTacoController,另一个是 OrderController。

在我自己试用这个例子的过程中,我首先复制了 OrderController,它是一个非常简单的类,如下所示。当网页上有/没有错误输入时,这段代码可以完美运行。

@Slf4j
@Controller
@RequestMapping("/orders")
public class OrderController {

    @GetMapping("/current")
    public String orderForm(Model model) {
        model.addAttribute("order", new Order());
        return "orderForm";
    }

    @PostMapping
    public String processOrder(@Valid Order order, Errors errors) {
        if (errors.hasErrors()) {
            return "orderForm";
        }
        log.info("Order submitted: " + order);
        return "redirect:/";
    }
}

然后我根据上面的OrderController自己实现了DesignTacoController

@Slf4j
@Controller
@RequestMapping("/design")
public class DesignTacoController {

    @GetMapping
    public String showDesignForm(Model model) {
        List<Ingredient> ingredients = Arrays.asList(
                // adding some ingredients
        );
        Type[] types = Ingredient.Type.values();
        for (Type type: types) {
            model.addAttribute(
                    type.toString().toLowerCase(Locale.ROOT),
                    filterByType(ingredients, type));
        }
        model.addAttribute("design", new Taco());
        return "design";
    }

    @PostMapping
    public String processDesign(@Valid Taco design, Errors errors) {
        if (errors.hasErrors()) {
            return "design";
        }
        log.info("Processing design: " + design);
        return "redirect:/orders/current";
    }

    // more methods ignored
}

有了这个实现,当没有错误输入时它可以正常工作。但是,如果我针对验证器输入一些内容,我会得到一个

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'design' available as request attribute

通过在stackoverflow(this question)上搜索并查看github上的original example code,我发现这个类应该是这样实现的

@Slf4j
@Controller
@RequestMapping("/design")
public class DesignTacoController {


    @ModelAttribute
    public void addIngredientsToModel(Model model) {
        List<Ingredient> ingredients = Arrays.asList(
            // adding some ingredients
        );

        Type[] types = Ingredient.Type.values();
        for (Type type : types) {
        model.addAttribute(type.toString().toLowerCase(),
            filterByType(ingredients, type));
        }
    }

    @GetMapping
    public String showDesignForm(Model model) {
        model.addAttribute("design", new Taco());
        return "design";
    }

    @PostMapping
    public String processDesign(@Valid @ModelAttribute("design") Taco design, Errors errors, Model model) {
        if (errors.hasErrors()) {
            return "design";
        }

        // Save the taco design...
        // We'll do this in chapter 3
        log.info("Processing design: " + design);

        return "redirect:/orders/current";
    }
    // more methods ignored
}

我的问题来了,

  1. 为什么即使不使用 @ModelAttribute 注解,OrderController 也能正常工作?
  2. 为什么我们需要在 DesignTacoController 中使用 @ModelAttribute
  3. 什么时候会调用 addIngredientsToModel?
  4. 我们可以合并 addIngredientsToModel 和 showDesignForm 吗?

我觉得我缺少一些Spring的基本概念。希望有人能帮我解决这个问题。

非常感谢。

1 个答案:

答案 0 :(得分:1)

在方法级别使用@ModelAttribute 的目的是向模型添加一个或多个模型属性。在这里添加这个

到模型。 model.addAttribute(type.toString().toLowerCase(), filterByType(ingredients, type)); }

事情是 Spring 在任何请求映射之前创建模型对象。所以 addIngredientsToModel 方法将在任何 Handler 方法之前被调用。