Request scoped bean 在 Spring 4 中在请求范围之外运行代码时始终可用。

huangapple go评论65阅读模式
英文:

Request scoped bean is always available when running code outside of request scope in Spring 4

问题

在Spring 4中遇到了一个问题,可能在Spring 5中也会遇到类似的情况。

我的情况如下:

  1. Spring Boot 1.5 Web应用程序

  2. 请求范围的Bean:

    @RequestScope
    @Component
    public class APIAction { ... }
    
  3. 从与Web请求相关或不相关的线程访问此组件的代码:

    private final ObjectProvider<APIAction> apiAction;
    
    apiAction.getIfAvailable()...
    
  4. 当它在绑定到Web请求的线程中运行时,一切正常。但是,当我从守护线程调用它时,我期望得到null、异常或其他一些东西。相反,我收到了一些代理对象,无法测试是否为null,或者是否有任何状态表明Bean确实可用。如果我尝试调用任何Bean方法,最终会得到异常,提示在绑定到Web请求的线程之外访问Bean。

所以问题是,我用错了吗?现在,我在访问Bean之前会检查请求范围,通过调用此方法:RequestContextHolder.getRequestAttributes() != null,这真的很丑陋,而且我需要一直告诉人们为什么他们应该这样使用。

还有一个额外的问题,是否可能在没有请求绑定的线程中实例化该Bean?

英文:

Stuck on this thing in Spring 4, probably the same will be for 5.

So, what I have:

  1. Spring Boot 1.5 web app
  2. Request scoped bean:
  @RequestScope
  @Component
  public class APIAction { ... }
  1. Code which accesses this component from threads related or not related to webrequest:
  private final ObjectProvider&lt;APIAction&gt; apiAction;

  apiAction.getIfAvailable()...
  1. When it runs in Thread bounded to web request everything is fine. But when I invoke it from daemon thread I expect to get null, exception or something else. Instead I'm receiving some proxy object which can't be tested for null, or any kind of state indicating that bean is really available. If I'll try to invoke any bean method, I'll get exception finally saying accessing to bean outside of thread bounded to web request.

So the question is, am I using it wrong? Right now, I'm checking request scope before accessing to bean by invoking this: RequestContextHolder.getRequestAttributes() != null, which is really ugly, and I need all the time to tell people why they should use it like this.

And bonus question, is it possible actually to instantiate that bean in threads without request bound?

答案1

得分: 1

简要概述:您不能使用 ObjectProvider.getIfAvailable() 来检查请求范围。

请改用 if (RequestContextHolder.getRequestAttributes() != null)

解决方案:要检查是否处于请求上下文中,请调用 RequestContextHolder.getRequestAttributes() 并检查返回值是否为 null

英文:

TL;DR: You can't use ObjectProvider.getIfAvailable() to check if in request scope.

Use if (RequestContextHolder.getRequestAttributes() != null) instead.


As the javadoc of ObjectProvider says:

> A variant of ObjectFactory designed specifically for injection points, allowing for programmatic optionality and lenient not-unique handling.

For singleton beans, ObjectProvider&lt;APIAction&gt; is an alternative to @Autowired(required = false) List&lt;APIAction&gt; with methods that better represent the purpose.

For prototype beans, it allows the on-demand creation of the prototype, including optional constructor arguments.

However, it's all about the existence of the bean, i.e. about whether the bean has been registered (and how many). Any @Component (or other) annotated class is registered by the component scanning, regardless of the bean scope.

The @RequestScope bean exists, so the code could be changed to @Autowired private final APIAction apiAction;, and it would always be non-null.

The fact that the object referred to by apiAction is a proxy that will apply method calls to different instances depending the the request context is besides the point.

When you call an APIAction method, you will get an IllegalStateException saying:

> No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

Internally, a @RequestScope annotated class has a Scope of type RequestScope, and the javadoc says:

> Relies on a thread-bound RequestAttributes instance.

It does this by calling RequestContextHolder.currentRequestAttributes(), which throws the above exception.

Solution: To check if you are in a request context, call RequestContextHolder.getRequestAttributes() and check for null return value.

huangapple
  • 本文由 发表于 2020年9月15日 22:15:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/63903823.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定