我正在尝试将依赖于客户端的对象注入到无状态对象中。我确实做到了,但我不太确定它是如何以及为什么工作的,因为我不完全理解 JavaEE 依赖注入。我有3个相关课程
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
我的问题:
在非常重要的一点上,您的问题缺失了:
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 实例。你的“状态”由容器保存。