m1ld
m1ld

Reputation: 1200

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

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<APIAction> 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?

Upvotes: 0

Views: 2860

Answers (1)

Andreas
Andreas

Reputation: 159086

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.

Upvotes: 3

Related Questions