在@Stateless中注入@RequestScoped

问题描述 投票:0回答:1

我正在尝试将依赖于客户端的对象注入到无状态对象中。我确实做到了,但我不太确定它是如何以及为什么工作的,因为我不完全理解 JavaEE 依赖注入。我有3个相关课程

  • BookmarkRepository - 从数据库获取数据
  • RequestData - 我可能需要的用户请求的所有相关信息
  • BookmarkEndpoint - REST 端点

import java.util.UUID;
import javax.enterprise.context.RequestScoped;

@RequestScoped
public class RequestData {

    private final UUID correlationId;

    public RequestData() {
        correlationId = UUID.randomUUID();
    }

    public UUID getCorrelationId() {
        return correlationId;
    }
}


import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
public class BookmarkRepository {

    @PersistenceContext
    private EntityManager em;

    @Inject
    RequestData requestData;

    @Override
    public String test() {
        StringBuilder sb = new StringBuilder();
        String objectid = Integer.toString(System.identityHashCode(this));
        String correlationId = requestData.getCorrelationId().toString();

        sb.append(correlationId);
        sb.append("    OBJECT ID: " + objectid);
        return sb.toString();
    }
}
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("bookmarks")
public class BookmarkEndpoint {

    @Inject
    private BookmarkRepository bookmarkRepository;

    @Inject
    private RequestData requestData;

   
    @GET
    @Path("/test")
    @Produces(MediaType.TEXT_PLAIN)
    public Response testEndpoint() {
        String outerCorelationId = requestData.getCorrelationId().toString();
        sb.append(outerCorelationId);

        String innerCorelationId1 = bookmarkRepository.test();
        sb.append("\n" + innerCorelationId1);

        String innerCorelationId2 = bookmarkRepository.test();
        sb.append("\n" + innerCorelationId2);

        return ResponseBuilder.createOkResponse(Response.Status.OK, finalMessage);
    }
}

当我通过 2 个 HTTP 调用调用程序时的输出

FIRST HTTP CALL
ba4ac8b9-8e3b-4632-9c5e-89fe9b6f2cc5
ba4ac8b9-8e3b-4632-9c5e-89fe9b6f2cc5    OBJECT ID: 717717270
ba4ac8b9-8e3b-4632-9c5e-89fe9b6f2cc5    OBJECT ID: 717717270

SECOND HTTP CALL
9f563047-3d8f-48f5-849d-9a0bf1df46ae
9f563047-3d8f-48f5-849d-9a0bf1df46ae    OBJECT ID: 717717270
9f563047-3d8f-48f5-849d-9a0bf1df46ae    OBJECT ID: 717717270

我的问题:

  • 每次调用 BookmarkEndpoint 中的方法时,@Inject 都会触发吗?我虽然 @Inject 仅在第一次创建对象时触发,但正如我们在输出中看到的那样,我们似乎正在处理同一个对象,并且每次调用它的方法时,它都会被注入一个新的 RequestData 对象
  • 是否有可能两个 HTTP 调用在同一时间获取相同的 @Statless 对象,然后一个客户端的 RequestData 将因为另一个客户端的 RequestData 而被压扁
  • @Statless 何时创建以及何时销毁?我见过很多主题,其中都说“需要时创建”和“不需要时销毁”。那个的真实意义是什么。如果我们信任我的输出,那么我的 @Statless 对象始终在内存中
  • BookmarkRepository 真的应该是 @Statless 因为我显然有一个对每个客户端来说都是唯一的状态
java jakarta-ee cdi
1个回答
1
投票

在非常重要的一点上,您的问题缺失了:

CDI 注入一个代理,就像路由器一样。它注入实际的对象。如果您在

getCorrelationId()
中放置断点并查看堆栈跟踪,您将看到有一个堆栈帧,其中在某个时刻调用了 CDI 代理。

为什么这很重要?

这允许创建可以将 RequestScoped bean 与 SessionScoped bean 一起注入的程序,反之亦然。当代理上的函数被调用时,容器会将您路由到该类的正确实例。它确实会查找并将函数调用发送到正确的位置!

回答你的问题:

每次调用 BookmarkEndpoint 中的方法时,@Inject 都会触发吗?

都不是。

@Inject
只是一个注释。
BookmarkEndpoint
注入了
BookmarkRepository
的代理,它是
BookmarkRepository
的子类,并且 CDI 框架在运行时定义了这个子类。
BookmarkRepository
是一个无状态 EJB,因此它将有一个实例池。代理将从池中选择一个实例并调用它的方法。由于您在
BookmarkEndpoint
上没有 CDI 范围,我相信每个请求都会重新创建它(不要引用我的话,请使用调试器)。我们通常对所有 JAX-RS bean 进行
@ApplicationScoped
作为优化。

是否有可能两个 HTTP 调用在同一时间获取相同的 @Statless 对象,然后一个客户端的 RequestData 将因为另一个客户端的 RequestData 而被压扁

不。相反,无状态 bean 被池化。如果池中有可用的 bean,则将同时处理两个请求。如果没有可用的 bean,请求将被阻塞,直到有实例可用为止。池大小和超时是可调的,并且是容器特定的设置。

@Statless 何时创建以及何时销毁?

简短的故事:bean 池在启动时创建并在关闭时销毁,除非您从 bean 中抛出异常,否则该实例将被销毁。请参阅此处了解完整说明:https://docs.oracle.com/javaee/6/tutorial/doc/giplj.html

BookmarkRepository 真的应该是@Statless吗

@Stateless
绝对可以工作,但这不是必需的:您实际上不需要任何池。
@Singleton @Lock(LockType.Read)
会更合适。最好的是
@ApplicationScoped @Transactional(TxType.Required)
因为这样可以完全避免 EJB

因为我显然有一个对每个客户来说都是唯一的状态

你确实...但不是你想的那样!请记住以下是代理:

    @Inject
    RequestData requestData;

它将把您路由到绑定到当前请求的 bean 实例。你的“状态”由容器保存。

© www.soinside.com 2019 - 2024. All rights reserved.