【问题标题】:Effects of changing Spring bean scope from request to singleton将 Spring bean 范围从请求更改为单例的影响
【发布时间】:2020-05-11 20:22:33
【问题描述】:

我的代码中有以下类,你可以说它只是标准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 请求。 但它未能给出以下错误:

创建名为“scopedTarget.myRestTemplateImpl”的 bean 时出错: 范围“请求”对当前线程无效;考虑 如果您打算引用它,请为此 bean 定义一个作用域代理 单身人士

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

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

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

如果是的话,如果你能给出正确的解释,除了范围之间的基本区别之外,它会很棒,因为当我现在开始编码时,它会给我更好的推理,同时确定 bean 的范围。 如果不是,为什么?

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

【问题讨论】:

  • 只要不保持状态就没什么好担心的。构造后的RestTEmplate 是线程安全的。它甚至可以提高性能和内存使用率。

标签: spring spring-boot asynchronous scope


【解决方案1】:

如果您的自定义 RestTemplate 看起来与您在示例代码中的内容完全相同,那么它的实用性为零 - 您可以使用标准的 RestTemplate。另外,由于您将(可能)范围为RestTemplate 的非请求范围传递给构造函数,因此它甚至没有按照您认为的方式执行我怀疑的操作。如果你真的希望它是请求范围和自动代理的,你可以在不继承它的情况下获得相同的效果。例如:

@Configuration
class RestTemplateConfig {

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

还要注意@Service 的刻板印象是有争议的不准确,因为我不认为RestTemplate 是一种服务。这并不重要,但将其定型为@Component 可能更准确。

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

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

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

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

【讨论】:

  • 感谢您的回答。我目前在MyRestTemplate 中没有任何其他功能,我将删除这个request 范围。您是否认为它会产生任何不利影响,因为有数千个请求通过MyRestTemplate 从应用程序发出,我正在删除它,因为根据我request 没有做任何特别的事情,但因为我正在更改其他人的代码,我担心它的后果。
  • 如果实现中没有“奇怪”的东西,它应该可以正常工作。但与任何更改一样 - 测试!
  • 实现中没有什么奇怪的。是的,会正确测试它。谢谢!
猜你喜欢
  • 2013-01-21
  • 2011-08-15
  • 1970-01-01
  • 2011-08-19
  • 1970-01-01
  • 2012-06-04
  • 1970-01-01
  • 2017-01-22
  • 1970-01-01
相关资源
最近更新 更多