`micronaut @RequestScope` – 不会为每个传入的 HTTP 请求创建 bean。

huangapple go评论79阅读模式
英文:

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

问题

我有一个类,如下所示的RequestScope bean:

@RequestScope
class RequestContext {

  private String requestId;
  private String traceId; 
  private String authorisedId; 
  private String routeName; 
        
  // 其他字段
    
  @Inject RequestContext(SecurityContext securityContext) {
        this.requestId = UUID.randomUUID().toString();
        if(securityService.getAuthentication().isPresent()){
          this.authorisedId = (securityService
                              .getAuthentication().get()).getUserId().toString();
    }
  }
  
  /* 在控制器方法拦截器中更新 */ 
  public void updateRouteName(String name){
      this.routeName = name; 
  }
}

这个想法是创建一个对象,其中包含在整个应用程序中可访问的REST请求级别的自定义数据,这个对象的范围显然应该限定在当前请求内。例如,这可以用于日志记录 - 每当开发人员从应用程序记录任何内容时,一些请求的元数据也会随之传递。

我不清楚@RequestScope bean 到底是什么:

从其定义中 - 我的假设是它会在每个新的http请求中被创建,并且相同的实例会在该请求的生命周期内共享。

Micronaut 何时构造它?它是不可变的吗?

在多个请求之间,我可以看到相同的 requestId(预期每个请求都会有一个新的UUID)。

这是否是使用@RequestScope bean的正确用例?

英文:

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?

答案1

得分: 3

我遇到了关于@RequestScope的问题,所以我会在这里发表一个答案供其他人参考。

我试图将一个@RequestScope的bean注入到一个HTTP过滤器中,然后在该bean中设置一个值,然后稍后从另一个bean中读取它。例如:

@RequestScope
class RequestScopeBean() {
    var id: Int? = null
}

@Filter
class SetRequestScopeBeanHere(
    private val requestScopeBean: Provider<RequestScopeBean>
) {

    override fun doFilterOnce(request: HttpRequest<*>, chain: ServerFilterChain): Publisher<MutableHttpResponse<*>> {
        requestScopeBean.get().id = // 从Http请求获取的id
    }
}

@Singleton
class GetRequestScopeBeanHere(
    private val requestScopeBean: Provider<RequestScopeBean>
) {

    fun getIdFromRequestScopeBean() {
        println(requestScopeBean.get().id)
    }
}

在这个示例中,在执行任何控制器之前,我的过滤器(SetRequestScope)被调用,这将设置requestScopeBean.id,但关键是请求作用域bean必须包装在javax.inject.Provider中,否则设置字段不起作用。

在后续操作中,当调用GetRequestScopeBeanHere::getIdFromRequestScopeBean时,它将可以访问之前设置的requestScopeBean.id

这是Micronaut的意图:
https://github.com/micronaut-projects/micronaut-core/issues/1615

英文:

I was running into an issue regarding @RequestScope so I'll post an answer here for others.

I was trying to inject a @RequestScope bean into an HTTP filter, set a value in the bean, and then read it later from another bean. For example

@RequestScope
class RequestScopeBean() {
    var id: Int? = null
}


@Filter
class SetRequestScopeBeanHere(
    private val requestScopeBean: Provider&lt;RequestScopeBean&gt;
) {

    override fun doFilterOnce(request: HttpRequest&lt;*&gt;, chain: ServerFilterChain): Publisher&lt;MutableHttpResponse&lt;*&gt;&gt; {
        requestScopeBean.get().id = // id from Http Request
    }
}


@Singleton
class GetRequestScopeBeanHere(
    private val requestScopeBean: Provider&lt;RequestScopeBean&gt;
) {

    fun getIdFromRequestScopeBean() {
        println(requestScopeBean.get().id)
    }
}

In this example before any controller is executed my filter (SetRequestScope) is called, this will set requestScopeBean.id but the key is that the request scope bean must be wrapped in a javax.inject.Provider, otherwise setting the field won't work.

Down the line, when GetRequestScopeBeanHere::getIdFromRequestScopeBean is called it'll have access to the requestScopeBean.id set earlier

This is intentional by Micronaut:
https://github.com/micronaut-projects/micronaut-core/issues/1615

答案2

得分: 1

> 何时由Micronaut构造?

@RequestScope的bean是在请求处理过程中创建的,在第一次需要该bean时。

> 它是不可变的吗?

可以是。当你编写类时,你可以决定bean是否可变。如你的示例中所写,RequestContext是可变的。如果删除updateRouteName方法,该bean将是不可变的。

> 这是使用@RequestScope bean的正确用例吗?

我认为不是,但这实际上是一个基于观点的问题。

编辑:基于下面的评论添加

请查看项目: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 {
        // 这些将包含处理这些请求的DemoBean的标识
        String firstResponse = client.toBlocking().retrieve("/doit");
        String secondResponse = client.toBlocking().retrieve("/doit");

        assertTrue(firstResponse.matches("^Bean identity: \\d*$"));
        assertTrue(secondResponse.matches("^Bean identity: \\d*$"));

        // 如果将DemoBean修改为@Singleton而不是
        // @RequestScope,这将失败,因为相同的实例
        // 将用于两个请求
        assertNotEquals(firstResponse, secondResponse);
    }
}
英文:

> 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(&quot;/&quot;)
public class DemoController {

    private final DemoBean demoBean;

    public DemoController(DemoBean demoBean) {
        this.demoBean = demoBean;
    }

    @Get(&quot;/doit&quot;)
    public String doit() {
        return String.format(&quot;Bean identity: %d&quot;, 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(&quot;/&quot;)
    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(&quot;/doit&quot;);
        String secondResponse = client.toBlocking().retrieve(&quot;/doit&quot;);

        assertTrue(firstResponse.matches(&quot;^Bean identity: \\d*$&quot;));
        assertTrue(secondResponse.matches(&quot;^Bean identity: \\d*$&quot;));

        // 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);
    }
}

huangapple
  • 本文由 发表于 2020年9月1日 21:10:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/63688363.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定