如何全局传递模型属性以避免在Spring控制器中重复代码?

问题描述

我想问您一些最佳实践,以减少控制器方法中可重复代码数量-下面介绍其中一种方法

我有很多信息和两种形式的大而复杂的视图。在控制器的每种方法中(并且有很多),我必须将相同的属性传递给我的视图(在后期控制器中甚至两次)。我在下面的代码片段中添加一个“ SAME CODE”注释,指示相同的代码段。

我想知道是否有可能在控制器中创建一个全局方法来收集所有要传递给模型的属性,并仅在任何特定方法中引用它?

我调查了ModelAndView或ModelMap,但看不到适合的模型。

只想避免重复此部分:

可重复的代码

model.addAttribute("hotels",hotelService.getAllHotels());
List<GetRoomDto> roomsDto = roomService.getAllRoomsByHotelId(hotelId);
model.addAttribute("rooms",roomsDto);
model.addAttribute("roomTypes",roomTypeService.findAllRoomTypeNames());

完整方法,其中一段代码出现两次

@PostMapping("/hotels/{hotelId}/rooms")
    public String createRoomForHotelById(@modelattribute("room") @Valid NewRoomDto roomDto,BindingResult result,@PathVariable("hotelId") Long hotelId,Model model) {
        if(result.hasErrors()) {
            // SAME CODE
            model.addAttribute("hotels",hotelService.getAllHotels());
            List<GetRoomDto> roomsDto = roomService.getAllRoomsByHotelId(hotelId);
            model.addAttribute("rooms",roomsDto);
            model.addAttribute("roomTypes",roomTypeService.findAllRoomTypeNames());
            //
            
            model.addAttribute("hotel",new NewHotelDto());
            LOG.info("Binding error: {}",result.toString());
            return "admin/dashboard";
        }
        
        // SAME CODE
        model.addAttribute("hotels",hotelService.getAllHotels());
        List<GetRoomDto> roomsDto = roomService.getAllRoomsByHotelId(hotelId);
        model.addAttribute("rooms",roomsDto);
        model.addAttribute("roomTypes",roomTypeService.findAllRoomTypeNames());
        //
        
        LOG.info("AdminController: CreateRoomForHotelById: Created room: {}",roomDto.toString());
        
        roomDto.setHotelId(hotelId);
        roomService.createNewRoom(roomDto);
        
        return "redirect:/auth/admin/hotels/{hotelId}/rooms";
    }

解决方法

对于全局模型属性,您可以使用@ControllerAdvice:

创建一个类,并用@ControllerAdvice对其进行注释。 在该类内部传递模型属性(现在将在全球范围内提供),如下所示:

@ModelAttribute("foo")
public Foo foo() {
    return new Foo();
}
,

您还可以将代码从J Asgarov's answer移动到同一控制器,而不是用@ControllerAdvice注释的另一个类。这样,该代码将仅针对该控制器内的@RequestMapping个方法执行。

对于多个值,您还可以执行以下操作:

@ModelAttribute
public void foo(Model model,@PathVariable(required = false) Long hotelId) {
        model.addAttribute("hotels",hotelService.getAllHotels());
        if (hotelId != null) {
            List<GetRoomDto> roomsDto = roomService.getAllRoomsByHotelId(hotelId);
            model.addAttribute("rooms",roomsDto);
        }
        model.addAttribute("roomTypes",roomTypeService.findAllRoomTypeNames());
}

但是看到您的代码,我宁愿建议您将重复的代码移到私有方法中,并在模型中需要它们时进行调用。 例如,您的方法createRoomForHotelById会导致重定向,这基本上会丢弃您放入模型中的所有内容。