【问题标题】:Spring Prototype scoped bean. Do I need to make fields in it as threadLocal?Spring Prototype 范围 bean。我需要将其中的字段设置为 threadLocal 吗?
【发布时间】:2021-05-23 17:50:53
【问题描述】:

Spring Framework 中有一条经验法则——将无状态 bean 声明为单例,声明为原型。但是,没有关于原型作用域 bean 中的状态字段的信息,也没有关于是否应该同步查找方法以避免竞争条件的信息?

假设我有一个有多个字段的有状态 bean

@Service
@Scope("prototype") 
class PostOperator {

  @Autowired
  private MailSender mailSender;

  private String lastName;
  private String streetAddress;
  private Long operatorId;
  private Map <String, String> subjectArticle map;


  public PostOperator(String lastName, String streetAddress,  Long operatorId, 
                      Map <String, String> subjectArticle map){
   ...... 
  }

  public void submitEmail(){
     mailSender.send(lastName, streetAddress, operatorId);
  }

}

我有一个带有 Lookup 方法的 Rest Controller

@RestController
class AppointmentController {
    @GetMapping("/submit")
    public ResponseEntity submit() {
    
    PostOperator operator = getOperator("Smith", "Fleet Str.", 7L, 
                                         new ConcurrentHashMap<>());

    operator.submitEmail();
    return ResponseEntity.ok();
}

@Lookup
public PostOperator getOperator(String lastName, String streetAddress,  Long operatorId,
                                 Map <String, String> subjectArticle map) {
    return null;

}
  • 我需要将字段声明为 ThreadLocal 吗?
  • 我是否需要同步查找方法,因为 AFAIK Singleton 不是线程安全的?
  • 是否需要在 PostOperator 中同步 submitEmail() 方法?

非常感谢大家的澄清。

【问题讨论】:

    标签: java spring spring-mvc prototype synchronized


    【解决方案1】:

    在我看来,只要依赖实例不持有任何影响的状态。

    在上面的示例中,您的服务PostOperator 依赖于MailSender,它唯一的责任是根据给定的参数发送电子邮件,其中所有行为都绑定到send 方法,唯一的例外是电子邮件配置。因此,在这种情况下,基于使用的电子邮件凭据send 方法不依赖于任何其他状态来发送方法,它也没有任何副作用,您不必担心线程安全。

    无状态服务工作发现大多数具有线程安全的情况。

    【讨论】:

    • 同意无状态服务,但我担心 AppointmentController 依赖于有状态的 PostOperator。在这种情况下使@Lookup 方法同步是否有意义?由于查找方法接收参数并传递给 PostOperator 的构造函数?在这种情况下,传入的请求是否有可能导致竞争条件?
    • 由于您已将PostOperator 服务声明为prototype,因此应在每个请求中初始化此服务,这意味着每个请求的新实例,我也没有看到将此服务注入您的控制器。如果服务状态不会在请求之间共享,那么我们不需要担心,另一件事是它没有像更新状态或修改这样的副作用,那么我们不需要担心其他更好的方法syncronized
    • 嗨@msucil, PostOperator 确实没有注入控制器,但它是从查找方法getOperator 获得的,因此它是一个本地字段。请看一下控制器 - 在提交方法中有一行代码: PostOperator operator = getOperator("Smith", "Fleet Str.", 7L, new ConcurrentHashMap()) 。起初我在方法中获取 PostOperator 实例,然后我提交电子邮件。
    • 在这种情况下,只要调用getOperator,它就会返回PostOperator 的新实例,在这种情况下无需担心线程安全,因为执行上下文绑定到getOperator 方法。
    猜你喜欢
    • 2014-09-29
    • 2011-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多