Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject request scoped bean into another bean

I want to create a UUID that is unique in a request life cycle. To do this, I create a UUID bean with the @Scope("request") annotation.

@Bean
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST)
public UUID requestUUID() {
    return UUID.randomUUID();
}

I want to access this bean in my controller. So I inject it with @Autowired. This works fine.

@Controller
public class DashboardController {

    @Autowired
    UUID uuid;

    @Autowired
    WelcomeMessageService welcomeMessageService;

    @Autowired
    IssueNotificationService issueNotificationService;

    @RequestMapping("/")
    public String index(Model model) throws InterruptedException, ExecutionException {
        System.out.println(uuid);
        PortalUserDetails userLog = getPortalUserDetails();

        BusinessObjectCollection<WelcomeMessage> welcomeMessages = welcomeMessageService.findWelcomeMessages(
                20,
                0,
                userLog.getZenithUser(),
                userLog.getConnectionGroup().getConnectionGroupCode(),
                "FR");
        if(welcomeMessages!=null) {
            model.addAttribute("welcomeMessages", welcomeMessages.getItems());
        }

        BusinessObjectCollection<IssueNotification> issueNotifications =
                issueNotificationService.findIssueNotifications(userLog.getZenithUser());

        if(welcomeMessages!=null) {
            model.addAttribute("welcomeMessages", welcomeMessages.getItems());
        }
        model.addAttribute("issueNotifications", issueNotifications);

        return "index";
    }
}

The controller call multiple services. Every service use a RestTemplate bean. In this RestTemplate bean, I want to get the UUID.

@Component
public class ZenithRestTemplate extends RestTemplate {   
    @Autowired
    private UUID uuid;

    public void buildRestTemplate() {
        List restTemplateInterceptors = new ArrayList();
        restTemplateInterceptors.add(new HeaderHttpRequestInterceptor("UUID", uuid.toString()));
        this.setInterceptors(restTemplateInterceptors);
    }
}

When I try to inject the UUID here, I have an error :

Error creating bean with name 'zenithRestTemplate': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.UUID com.geodis.rt.zenith.framework.webui.service.ZenithRestTemplate.uuid; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestUUID': 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.

What can I do to access my UUID bean inside the RestTemplate bean ?

My project use Spring-MVC, Spring-boot with java configuration.

I already tried to add a RequestContextListener but it doesn't solve the problem.

@Bean public RequestContextListener requestContextListener(){
    return new RequestContextListener();
}
like image 691
YLombardi Avatar asked Dec 04 '15 14:12

YLombardi


People also ask

How do you inject request in scoped bean?

To do this, I create a UUID bean with the @Scope("request") annotation. I want to access this bean in my controller. So I inject it with @Autowired. This works fine.

What is scoped bean injection problem?

By default, Spring beans are singletons. The problem arises when we try to wire beans of different scopes. For example, a prototype bean into a singleton. This is known as the scoped bean injection problem.

What happens if we inject prototype bean in singleton bean?

Thus if you dependency-inject a prototype-scoped bean into a singleton-scoped bean, a new prototype bean is instantiated and then dependency-injected into the singleton bean. The prototype instance is the sole instance that is ever supplied to the singleton-scoped bean.

Which annotation is used in injecting a prototype-scoped bean into a singleton bean?

Using Lookup Method Essentially, Spring will override our annotated method and use our method's return type and parameters as arguments to BeanFactory#getBean. @Lookup is useful for: Injecting a prototype-scoped bean into a singleton bean (similar to Provider) Injecting dependencies procedurally.


1 Answers

I think you need to mark your UUID request scoped bean like:

@Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)

Where controller is singleton scoped bean you are injecting request scoped bean in it. As singleton beans are injected only once per their lifetime you need to provide scoped beans as proxies which takes care of that.

Another option is to instead use the org.springframework.web.context.annotation.RequestScope annotation:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_REQUEST)
public @interface RequestScope {

    @AliasFor(annotation = Scope.class)
    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}

@RequestScope is a meta-annotation on @Scope that 1) sets the scope to "request" and 2) sets the proxyMode to ScopedProxyMode.TARGET_CLASS so you don't have to do it every time you want to define a request-scoped bean.

Edit:

Note that you may need to add @EnableAspectJAutoProxy on your main configuration class.

like image 57
Jan Zyka Avatar answered Sep 21 '22 09:09

Jan Zyka