将 Spring bean 范围从请求更改为单例的影响

Effects of changing Spring bean scope from request to singleton

我的代码中有以下 class,你可以说它只是对标准 RestTemplate 的包装。因此,每当我们必须发出外部请求而不是使用 RestTemplate 时,我们 autowire 自定义 MyRestTemplate.

@Service
@Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyRestTemplateImpl implements MyRestTemplate {

private final RestTemplate restTemplate;
private Logger logger = LogManager.getLogger(MyRestTemplateImpl.class);

@Autowired
public MyRestTemplateImpl(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;
}


@Override
public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException {

    ResponseEntity responseEntity = restTemplate.exchange(url, method, requestEntity, responseType, uriVariables);
          return responseEntity;
    }
}

现在的问题是我正在进行一些 Async 休息调用,这些调用又调用 MyRestTemplate 来发出外部 REST 请求。 但它没有给出以下错误:

Error creating bean with name 'scopedTarget.myRestTemplateImpl': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton

我理解这是因为为 Async 操作生成了一个新线程,并且范围 request 对于该子线程无效。它可以通过使用 How to enable request scope in async task executor 中非常好的解决方案来解决,但是,我现在不想去做,因为这种异步操作并不常见,而且我有一些时间限制。

所以,我的主要问题是,如果我将 MyRestTemplateImpl 设置为 singleton 一切正常,我没有遇到任何此类问题。 但是外部请求使用MyRestTemplateImpl的方法很多,应用上的流量也是

将其从 Request 更改为 Singleton 是否会导致任何不利影响,例如速度缓慢、竞争条件或我目前尚未意识到或未遇到的任何此类有害影响?

如果是,除了范围之间的基本区别之外,如果您能给出适当的解释,那将是很好的,因为它会在我现在开始编写代码时为 bean 确定范围时提供更好的推理。 如果不是,为什么?

我知道每个请求都会创建一个新 bean,但请用小示例解释 request 范围的实际用例是什么?

如果您的自定义 RestTemplate 看起来与您在示例代码中放置的完全一样,那么它的实用性为零 - 您可以只使用标准 RestTemplate。此外,由于您将(可能)非请求范围的 RestTemplate 传递给构造函数,我怀疑它甚至没有按照您认为的那样做。如果您真的希望它具有请求范围和自动代理功能,您可以在不对其进行子类化的情况下获得相同的效果。例如:

@Configuration
class RestTemplateConfig {

  @Bean
  @Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
  RestTemplate restTemplate() {
    return new RestTemplate();
  }
}

另请注意,@Service 刻板印象可能不准确,因为我不认为 RestTemplate 是一项服务。这无关紧要,但将其定型为 @Component.

可能更准确

如果您的 RestTemplate 实际上添加了额外的功能,而您为了简洁而省略了它,那么答案是 "it depends"。一般来说,单例范围总是正确的答案。

请求范围适用于您需要 Spring bean 来拥有仅与当前请求相关的数据的情况。我建议应该避免这种代码,但有时它很有用。例如,也许您有一个用于收集单个请求统计信息的对象 - 调用了多少方法,每个方法需要多长时间才能完成,等等。您不想在所有请求中重复使用它,因此您可以将其设为请求范围它将为每个请求重新创建。

如果您的 RestTemplate 没有任何特定于请求的状态,则每次在请求范围内创建一个新状态是没有意义的。例如,exchange() 可以每次使用不同的参数反复调用 - 调用 exchange().

时无需创建新的 RestTemplate

根据经验,您通常希望避免单例作用域以外的任何东西。您可能会遇到一些其他范围有用的奇怪情况,但应该仔细检查它们,因为很少需要其他范围。