1

Lately, I've come to know that RequestScoped beans are not usable outside a web transaction.

The problem is that outside the web transaction I do not want to use that bean, instead of having an error.

How could I achieve this ?

The component using the request scoped bean :

@Component
public class JavaComponent {

    @Autowired
    private RequestScopedBean requestScopedBean;
   
    @Override
    public void doStuff() {
        // TODO if in web transaction, use RequestScopedBean , otherwhise don't

    }
}

The bean:

@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {

    public String getInfo() {
        return "information about the web transaction";
    }

}

EDIT: the error I get when trying to use the JavaComponent outside the web request is :

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'JavaComponent.requestScopedBean': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is 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.

The way I use the bean while outside the web request thread is by having @Async methods running in separated threads.

Gaetano Piazzolla
  • 1,388
  • 1
  • 16
  • 31
  • What error are you getting? Also, what is the use case of using `JavaComponent` outside of the context of a Web request? – crizzis Apr 25 '21 at 16:50
  • I've edited the question with the answers to your questions. thank you – Gaetano Piazzolla Apr 25 '21 at 17:41
  • 2
    Does this answer your question? [Scope 'session' is not active for the current thread; IllegalStateException: No thread-bound request found](https://stackoverflow.com/questions/21286675/scope-session-is-not-active-for-the-current-thread-illegalstateexception-no) – crizzis Apr 25 '21 at 17:55
  • Nope, I do not want to use request scoped bean in the async request, I just want to do not use it when I'm outside the web request. – Gaetano Piazzolla Apr 25 '21 at 18:12
  • 1
    I understand that. Using `proxyMode` (as shown in one of the answers) will prevent the error. This means you'll now be able to initialize your bean correctly. As regards the async request, you should be able to handle the missing request scope at runtime – crizzis Apr 25 '21 at 18:17

2 Answers2

2

Autowire ApplicationContext only and lookup your request scoped bean, then perform a null check.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

@Service
public class TestService {

    @Autowired
    private ApplicationContext applicationContext;

    public void myMethod() {
        RequestScopedBean bean = applicationContext.getBean(RequestScopedBean.class);
        if (bean != null) {
            // do stuff
        }
    } 
}
Tamás Pollák
  • 233
  • 1
  • 8
0

I've come up with a better solution because applicationContext.getBean() throws an exception when called, instead, it's better to check if the current thread is executed in a web request context and then get the bean.

I've also tested the performances and the get bean is very fast ( 0ms ) probably because the request scoped bean pretty light

/**
* The Class AuditConfig.
*/

@Component
public class AuditConfig implements AuditorAware<String> {

    /**
     * The Constant SYSTEM_ACCOUNT.
     */
    public static final String SYSTEM_ACCOUNT = "system";

    @Autowired
    private ApplicationContext context;

    /**
     * Gets the current auditor.
     *
     * @return the current auditor
     */
    @Override
    public Optional<String> getCurrentAuditor() {
        return Optional.ofNullable(getAlternativeUser());
    }

    private String getAlternativeUser() {
     try {
         // thread scoped context has this != null
         if (RequestContextHolder.getRequestAttributes() != null) {
             Object userBean = context.getBean(AuditRequestScopedBean.BEAN_NAME);
             if (StringUtils.isNotBlank(((AuditRequestScopedBean) userBean).getUser())) 
             {
                 return ((AuditRequestScopedBean) userBean).getUser();
             }
          }
        } catch (Exception e) {
            return SYSTEM_ACCOUNT;
        }
        return SYSTEM_ACCOUNT;
    }

}
Gaetano Piazzolla
  • 1,388
  • 1
  • 16
  • 31