英文:
Request scoped bean is always available when running code outside of request scope in Spring 4
问题
在Spring 4中遇到了一个问题,可能在Spring 5中也会遇到类似的情况。
我的情况如下:
-
Spring Boot 1.5 Web应用程序
-
请求范围的Bean:
@RequestScope @Component public class APIAction { ... }
-
从与Web请求相关或不相关的线程访问此组件的代码:
private final ObjectProvider<APIAction> apiAction; apiAction.getIfAvailable()...
-
当它在绑定到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:
- Spring Boot 1.5 web app
- Request scoped bean:
@RequestScope
@Component
public class APIAction { ... }
- Code which accesses this component from threads related or not related to webrequest:
private final ObjectProvider<APIAction> apiAction;
apiAction.getIfAvailable()...
- 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<APIAction>
is an alternative to @Autowired(required = false) List<APIAction>
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论