javamicronautrequestcontextrequestscopemicronaut-rest

micronaut @RequestScope - not creating bean per incoming http-request


I have a class the following class as RequestScope bean:

@RequestScope
class RequestContext {

  private String requestId;
  private String traceId; 
  private String authorisedId; 
  private String routeName; 
    
  // few more fields 

  @Inject RequestContext(SecurityContext securityContext) {
        this.requestId = UUID.randomUUID().toString();
        if(securityService.getAuthentication().isPresent()){
          this.authorisedId = (securityService
                              .getAuthentication().get()).getUserId().toString();
    }
  }
  
  /* to be updated in controller method interceptors */ 
  public void updateRouteName(String name){
      this.routeName = name; 
  }

The idea is to have an object containing the REST request level custom data accessible across the application, the scope of the this obviously should be within the current request. This can be used for say.. logging - whenever devs log anything from the application, some of the request meta data goes with it.

I am not clear what the @RequestScope bean really is:

From its definition - my assumption is it is created for every new http-request and same instance is shared for the life of that request.

when is it constructed by Micronaut ? Is it immutable ?

Across multiple requests I can see the same requestId ( expecting new UUID for every request)

Is it the right use-case for @RequestScope bean?


Solution

  • when is it constructed by Micronaut ?

    A @RequestScope bean is created during request processing, the first time the bean is needed.

    Is it immutable ?

    It could be. You get to decide if the bean is mutable or not when you write the class. As written in your example, RequestContext is mutable. If you remove the updateRouteName method, that bean would be immutable.

    Is it the right use-case for @RequestScope bean?

    I don't think so, but that is really an opinion based question.

    EDIT: Based On Comments Added Below

    See the project at https://github.com/jeffbrown/rscope.

    https://github.com/jeffbrown/rscope/blob/2935a4c1fc60f350198d7d3c1dbf9a7eedd333b3/src/main/java/rscope/DemoController.java

    package rscope;
    
    import io.micronaut.http.annotation.Controller;
    import io.micronaut.http.annotation.Get;
    
    @Controller("/")
    public class DemoController {
    
        private final DemoBean demoBean;
    
        public DemoController(DemoBean demoBean) {
            this.demoBean = demoBean;
        }
    
        @Get("/doit")
        public String doit() {
            return String.format("Bean identity: %d", demoBean.getBeanIdentity());
        }
    }
    

    https://github.com/jeffbrown/rscope/blob/2935a4c1fc60f350198d7d3c1dbf9a7eedd333b3/src/main/java/rscope/DemoBean.java

    package rscope;
    
    import io.micronaut.runtime.http.scope.RequestScope;
    
    @RequestScope
    public class DemoBean {
        public DemoBean() {
        }
    
        public int getBeanIdentity() {
            return System.identityHashCode(this);
        }
    }
    

    https://github.com/jeffbrown/rscope/blob/2935a4c1fc60f350198d7d3c1dbf9a7eedd333b3/src/test/java/rscope/DemoControllerTest.java

    package rscope;
    
    import io.micronaut.http.client.RxHttpClient;
    import io.micronaut.http.client.annotation.Client;
    import io.micronaut.test.annotation.MicronautTest;
    import org.junit.jupiter.api.Test;
    
    import javax.inject.Inject;
    
    import static org.junit.jupiter.api.Assertions.assertNotEquals;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    
    @MicronautTest
    public class DemoControllerTest {
    
        @Inject
        @Client("/")
        RxHttpClient client;
    
        @Test
        public void testIndex() throws Exception {
            // these will contain the identity of the the DemoBean used to handle these requests
            String firstResponse = client.toBlocking().retrieve("/doit");
            String secondResponse = client.toBlocking().retrieve("/doit");
    
            assertTrue(firstResponse.matches("^Bean identity: \\d*$"));
            assertTrue(secondResponse.matches("^Bean identity: \\d*$"));
    
            // if you modify DemoBean to be @Singleton instead of
            // @RequestScope, this will fail because the same instance
            // will be used for both requests
            assertNotEquals(firstResponse, secondResponse);
        }
    }