【问题标题】:micronaut @RequestScope - not creating bean per incoming http-requestmicronaut @RequestScope - 不为每个传入的http请求创建bean
【发布时间】:2020-12-20 14:37:39
【问题描述】:

我有一个类作为 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 请求级别自定义数据的对象可以跨应用程序访问,this 的范围显然应该在当前请求内。这可以用于说.. 日志记录 - 每当开发人员从应用程序记录任何内容时,一些请求元数据都会随之而来。

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

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

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

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

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

【问题讨论】:

  • 如果您的请求范围的 bean 被注入到 Singleton bean 中,这可能不起作用。在这种情况下,您需要使用 Provider 注入 bean
  • @CyrilG。感谢您的回复。您能否进一步了解第一次创建请求范围 bean 的时间?尝试将 Provider 注入单例 bean,但失败并出现 NonUniqueBeanException :找到多个可能的 bean 候选者 - 他们究竟是在什么时候创建的?
  • @CyrilG。不知道那是什么错误,似乎已经清除了。但是通过注入 Provider 提供程序,每次调用 provider.get() 时,它都会创建一个新的 RequestContext 实例,从而达到请求范围的目的
  • @CyrilG。你使用Provider 是对的。在这里找到github.com/micronaut-projects/micronaut-core/issues/1615
  • @Sreerag 我猜你 NonUniqueBeanException 是因为已经有其他名为 RequestContext 的 bean,所以 micronaut 不知道要注入哪一个

标签: java micronaut requestcontext requestscope micronaut-rest


【解决方案1】:

我遇到了关于 @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 = // 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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 感谢您的回复。关于不变性,我知道编写的类是可变的,正如您所指出的,但我的问题是,一旦第一次需要构建 bean,它似乎不会在其他地方注入变异对象( updateRouteName )请求的范围。另外,为新请求创建的实例如何注入到 Singleton bean 中?
    • "如何将为新请求创建的实例注入到 Singleton bean" - 所有常用的注入方法词(构造函数、属性或字段)。
    • 是的,但是当注入到单例 bean 中时,它会在整个应用程序生命周期中保留对请求范围 bean 的相同引用(自然,因为容器 bean 本身就是单例)。因此,它可能应该是注入到单例容器 bean 中的请求范围 bean 的代理,并且代理应该能够对当前请求的请求范围 bean 进行操作。不知道如何实现这一点
    • “是的,但是当注入到单例 bean 中时,它会在整个应用程序生命周期中保留对请求范围 bean 的相同引用”——我认为这是不正确的。 “也许应该为注入到单例容器 bean 中的请求范围 bean 的代理,并且代理应该能够对当前请求的请求范围 bean 进行操作。” - 这就是发生的事情。
    • 查看项目github.com/jeffbrown/rscopegithub.com/jeffbrown/rscope/blob/… 的测试表明请求范围是有效的。
    猜你喜欢
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-06
    • 1970-01-01
    相关资源
    最近更新 更多