【问题标题】:Controllers should be 'stateless', does having a final variable for the service object make this stateful?控制器应该是“无状态的”,服务对象的最终变量是否会使它成为有状态的?
【发布时间】:2021-12-30 04:40:51
【问题描述】:

我正在遵循最佳实践来增强我的 SpringBoot 项目的代码库,并且我的控制器类符合所有其他类,但我不确定这是否会构成有状态,因为服务类不会更改,这会使其不可变吗?

例子:

@RestController
@RequestMapping(value = "/users")
public class UserController {

    final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }
}

userService 实例字段设置为 final 并在控制器的构造方法中实例化,这是否意味着控制器类违反了此最佳实践并且是有状态的?

谢谢

【问题讨论】:

    标签: java spring spring-boot spring-mvc model-view-controller


    【解决方案1】:

    final 关键字只是强制在UserController 实例的生命周期内不能更改具体的UserService

    Spring Controllers 是单例,因此每个类有一个实例,这意味着对Controller 的多个后续请求由同一个实例提供服务。

    所以不,final 关键字不会使您的控制器有状态。对另一个类的依赖不会使调用类(你的Controller)有状态。

    另外:final 关键字只是使变量引用不可变,即,您的 userService 属性始终指向相同的 UserService 实例。但是,UserService 实例本身可能仍然是可变的(例如,如果它具有可以更改的状态)。由于 @Service 类在 Spring 中也是单例,因此您可能也希望将 Service 设为无状态。

    【讨论】:

      【解决方案2】:

      依赖关系不会影响对象的“状态”。为了让您的控制器做任何实际工作,您必须注入各种依赖项(其他服务)。但是,您应该将 UserService@Authowired 一起注入,只是在构造函数中声明它可能不起作用(尽管我不是 100% 确定)。

      将被视为状态的是在处理请求时写入的某个值。任何不变的东西都不是真正的状态。

      主要目标是让一个控制器来处理许多请求,而不会有它们(请求)相互干扰的风险。

      【讨论】:

      • 构造函数注入不需要@Autowired注解。代码按原样工作。
      【解决方案3】:

      不,这不会使控制器有状态。我认为这是最好的方法。您也可以将UserService 字段设为private。构造函数注入优于现场注入 IMO,例如https://www.javacodegeeks.com/2019/02/field-setter-constructor-injection.html

      【讨论】:

        【解决方案4】:

        您的代码非常好。 UserService 是您的 UserController 的依赖项,而不是完全将状态“添加”到您的控制器的东西。

        有状态控制器如下所示:

        @RestController
        @RequestMapping(value = "/users")
        public class UserController {
        
            final UserService userService;
        
            private final List<String> requestedUsers = new ArrayList();
        
            public UserController(UserService userService) {
                this.userService = userService;
            }
        
            @GetMapping(value = /{userId})
            @ResponseStatus(HttpStatus.OK)
            public User getUser(@PathVariable long userId) {
                requestedUsers.add(userId);
                return this.userService.getUser(userId);
            }
        }
        

        现在您的控制器类中有一个可变属性:requestedUsers。每次对给定的userId 完成 GET 请求时,您都可以通过将此 userId 添加到列表中来更改此列表。这会将状态“添加”到您的控制器,因为现在它包含可变数据,每次向控制器发出请求时都会共享和更改这些数据。值得注意的是,即使将 requestedUsers 设置为 final 也不会改变控制器的状态。 final 关键字只是保证requestedUsers 属性的引用不会改变,但它指向的实际数据结构可以改变(就像List 一样,您可以在其中添加或删除元素需要更改其引用)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-08-23
          • 2021-01-02
          • 2012-01-04
          • 2010-10-07
          • 2017-11-11
          • 1970-01-01
          • 2017-02-05
          相关资源
          最近更新 更多