micronaut @RequestScope - 不为每个传入的 http 请求创建 bean

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

我有一个 class 以下 class 作为 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; 
  }

我们的想法是让一个对象包含跨应用程序可访问的 REST 请求级别自定义数据,其范围显然应该在当前请求内。这可以用于 say.. 日志记录 - 每当开发人员从应用程序记录任何内容时,一些请求元数据就会随之而来。

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

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

Micronaut 什么时候建造的?它是不可变的吗?

在多个请求中,我可以看到相同的 requestId(每个请求都需要新的 UUID)

它是@RequestScope bean 的正确用例吗?

when is it constructed by Micronaut ?

一个 @RequestScope bean 在请求处理期间创建,第一次需要 bean。

Is it immutable ?

有可能。当您编写 class 时,您可以决定该 bean 是否可变。正如您的示例中所写, RequestContext 是可变的。如果删除 updateRouteName 方法,则该 bean 将是不可变的。

Is it the right use-case for @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 {
        // 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);
    }
}

我 运行 遇到了一个关于 @RequestScope 的问题,所以我会 post 在这里为其他人提供答案。

我试图将一个 @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 = // id from Http Request
    }
}


@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