Spring Accessing Request Scope Beans outside of web request

Pranav Maniar
3 min readFeb 5, 2019

--

Spring request scope beans are a very convenient way to share request data between multiple places without passing parameters all the way. It also makes code more clean / readable.

But there is one problem, if you access the request scoped beans at some place and want to reuse that piece of code outside web request scope. For e.g. background processing jobs, kafka consumers, etc. Then spring will throw error of below form

java.lang.IllegalStateException: 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/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

There are couple of ways to solve this problem. I will describe the approach I took to solving this.

As you might already know spring has different scopes. Singleton, Prototype, Request, Session, Application, … And there is also a provision through which you can define custom scopes.

Above error occurs because spring request scope is registered only for the web application requests. Through RequestAttribute interface’s implementation spring puts/retrieves request scope information from httpRequest. If the thread was instantiated outside web request, it does not have the required attributes in the threadlocal variables and hence it throws the exception.

The main reason spring does not expose the Request Scope outside web request is that there is no definite way to determine when the request is started and when it is completed. Because for e.g. same thread can be reused across multiple request processing. And all thread local request scoped variables needs to be cleared before another request starts. In that case it’s the application developer who are in best knowledge of when request will start and when it will finish it’s work. But in case of web request filter, request listeners can be used to determine start and end of request.

Now, as a developer if we know when request logically starts and when it ends then we can manually enable request scope in spring.
Below are steps for doing the same.

Implement RequestAttributes Interface:

It maintains a map of beans internally. if it contains the bean then it returns that bean otherwise it returns null. Since we want only Request-Scope to be supported for the context of this blog, session scope related methods are not implemented.

public class CustomRequestScopeAttr implements RequestAttributes {private Map<String, Object> requestAttributeMap = new HashMap<>();@Override
public Object getAttribute(String name, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
return this.requestAttributeMap.get(name);
}
return null;
}
@Override
public void setAttribute(String name, Object value, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST){
this.requestAttributeMap.put(name, value);
}
}
@Override
public void removeAttribute(String name, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
this.requestAttributeMap.remove(name);
}
}
@Override
public String[] getAttributeNames(int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
return this.requestAttributeMap.keySet().toArray(new String[0]);
}
return new String[0];
}
@Overridepublic void registerDestructionCallback(String name, Runnable callback, int scope) {
// Not Supported
}
@Override
public Object resolveReference(String key) {
// Not supported
return null;
}
@Override
public String getSessionId() {
return null;
}
@Override
public Object getSessionMutex() {
return null;
}
}

Now at the start of the request, instruct RequestContextHolder to use this CustomRequestAttr by executing below statement.

RequestContextHolder.setRequestAttributes(new CustomRequestScopeAttr())

And in the finally block clear out the requestAttributes by :

 RequestContextHolder.resetRequestAttributes();

For e.g. if you are trying to achieve this in Quartz Job it would be of the form

public class CustomJob implements Job {public void execute(JobExecutionContext context) {
try {
RequestContextHolder.setRequestAttributes(new CustomRequestAttr());

SomeReqScopeBean reqBean = applicationCtx.getBean(SomeReqScopeBean.class);
//set fields of reqBean
//repeat same process for all the request scope bean
//Note: fetching beans from applicationContext is not required. You can autowire these request scope beans into some initialization method and then call it from here. That will also work. In short, standard spring dependency injection will work in similar way.
} catch ( Exception e ) {
//Exception processing
} finally {
RequestContextHolder.resetRequestAttributes();
}
}

Above example assumes that you are doing some work outside web application’s request thread. But spring container was started using webapplicationContext only.

If you started spring as say command line application, there will not be any webapplication context. In that case there are two approaches.

  1. Using Spring-Web
    i) Add Spring-web dependency
    ii) Register Request Scope by implementing BeanFactoryPostProcessor and registering request scope as
    beanFactory.registerScope(“request”, new RequestScope());
    iii) Then rest of the steps mentioned above (RequestAttributes, RequestContextHolder, … )

2. Create your custom RequestScope.
If you do not want to add dependency on spring-web. Then write your custom request scope by implementing Scope interface and then register that scope as “request” scope in beanFactory.registerScope(“”)

--

--