
![[8.5.5.6 或更高版本]](../ng_v8556.gif)
将 JAX-RS 2.0 与 EJB 和 CDI 集成
Liberty 中的 JAX-RS 2.0 与 Enterprise JavaBeans (EJB) 及上下文和依赖关系注入 (CDI) 集成。
为使 JAX-RS 2.0 使用企业 bean,您需要使用 @Path 注释 bean 的类并将其转换为根资源类。
通过与 EJB 集成,您可注释 EJB bean 以将它们展示为 REST 端点。还可使用 EJB 的 JTA 和安全功能。Liberty 中的 JAX-RS 2.0 支持使用无状态 bean 和单独会话 bean 作为根资源类、提供者和应用程序子类。通过与 CDI 集成,您可注释 CDI bean 或受管 bean 作为 REST 端点并对 Web Service 使用 CDI 注入。Liberty 中的 JAX-RS 2.0 支持 CDI 样式的 bean 作为根资源类、提供者和应用程序子类。提供者和应用程序子类必须是单体或使用应用程序范围。CDI 规范简化了集成不同类型的 Java™ EE 组件的过程。它提供了一种常用机制以将 EJB 或受管 bean 之类的组件插入至 JSP 或其他 EJB 之类的组件。
- 对于无状态会话 bean,请按以下示例中所示使用 @Stateless 注释:
@Stateless @Path("stateless-bean") public class StatelessResource {...}
- 对于单体 bean,请按以下示例中所示使用 @Singleton 注释:
@Singleton @Path("singleton-bean") public class SingletonResource {...}
@ApplicationScoped
@Path("/ApplicationScopedResource")
public class ApplicationScopedResource {
private @Inject
SimpleBean injected;
...
}
对 JAX-RS 2.0 与 EJB 和 CDI 的限制
请参阅以下各项以了解 Liberty 中的 JAX-RS 2.0 的限制:
- 如果使用 EJB 作为 JAX-RS 资源、提供者或应用程序,那么不能对 EJB bean 的构造函数使用 @Context 注入。原因在于根据 EJB 和 JAX-RS 规范,只能对 JAX-RS 使用带有缺省构造函数的 EJB。
- 如果在 Java 类中使用 EJB 或 CDI 注释,但未在 server.xml 文件中配置 EJB(例如,ejbLite-3.2)或 CDI(例如,cdi-1.0)的 Liberty 功能部件(这意味着 Liberty 运行时中没有 EJB 或 CDI 支持),那么 JAX-RS 2.0 引擎使用 Java 类作为 POJO 类。
- 对于应用程序类,如果它未实现任何接口或者它具有 @Localbean 注释,那么它被视为 EJB;如果它实现本地接口或 POJO 接口,那么它不会被视为 EJB。
- 对于提供者:
- 如果类仅实现 POJO 提供者接口而没有 @Local 注释,那么它被视为有效 EJB 提供者。
- 如果类有 @LocalBean 注释并实现 POJO 提供者接口,那么它被视为有效 EJB 提供者。
- 如果类有带 @Local 注释的本地接口,那么本地接口是提供者接口。如果此类实现提供者接口,那么它是有效 EJB 提供者。
- 如果类有带 @Local 注释的本地接口,并且该本地接口不是提供者接口,那么它不是有效提供者。
原因是在此情况下,EJB 容器只能为本地接口而不是 POJO 提供者接口生成 EJB 存根。
- 如果类只有引用提供者接口的 @Local 注释,而未实现此提供者接口,那么它不是有效提供者;根据 JAX-RS 2.0 规范:提供者是一个类,该类实现此规范中引入的一个或多个 JAX-RS 接口,并可使用 @Provider 进行注释以执行自动发现。
- 对于资源:
- 如果基于 EJB 的资源未实现任何接口,那么此类中声明的所有方法可作为 JAX-RS 资源提供。
- 如果基于 EJB 的资源实现一个接口(本地或 POJO),那么此接口中声明的所有方法以 JAX-RS 资源形式提供。
- 如果基于 EJB 的资源实现多个接口,那么:
- 所有接口是不带 @Local 注释的 POJO 接口时,接口中声明的所有方法以 JAX-RS 资源形式提供。
- 所有接口是带 @Local 注释的本地接口时,接口中声明的所有方法以 JAX-RS 资源形式提供。
- 某些接口是带 @Local 注释的本地接口而其他接口不是本地接口时,只有本地接口中声明的方法才以 JAX-RS 资源形式提供。原因在于,EJB 容器只能为本场景中的本地接口生成 EJB 存根。
- 如果基于 EJB 的资源具有 @LocalBean 注释,那么类中声明的所有方法以 JAX-RS 资源形式提供。
- 如果基于 EJB 的资源实现接口,那么必须在该接口中声明 JAX-RS 资源方法。如果该接口是不能修改的提供者,那么您必须为资源类创建新接口以添加资源方法。否则,它不会被视为 EJB 资源。
- 对于提供者:
- 如果带 @Path 注释的资源类实现 JAX-RS 提供者接口或者它使用 @Provider 注释进行声明,那么此类同时充当资源和提供者。在此情况下,JAX-RS 2.0 引擎仅使用此类的一个实例(由资源和提供者共享),此实例的生命周期为单体,这是缺省情况。
- 如果某个类已在应用程序类的 getClasses 和 getSingletons 方法中进行了注册,那么缺省情况下,JAX-RS 2.0 引擎使用 getSingletons 方法中的实例并忽略 getClasses 方法中的注册。
- 如果 RESTful 资源也是 CDI 管理的 bean 并且其范围为 javax.enterprise.context.Dependent,那么会因为 CDI 限制而不能调用 PreDestroy 方法。
异步处理
可在 JAX-RS 2.0 中使用异步处理方法来处理线程。异步处理在客户机 API 和服务器 API 中都是受支持的。有关异步处理的更多信息,请参阅 JSR-339 Java API for RESTful(“规范”)的第 8 章。
以下代码显示客户机 API 中的异步处理:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://example.org/customers/{id}");
target.resolveTemplate("id", 123).request().async().get(
new InvocationCallbackCustomer() {
@Override
public void completed(Customer customer) {
// Do something
}
@Override
public void failed(Throwable throwable) {
// Process error
}
});
以下代码显示服务器 API 中的异步处理:
@Path("/async")
public class MyResource {
@GET
public void getAsync(@Suspended final AsyncResponse asyncResponse){
CompletionCallback callBack = new CompletionCallback(){
@Override
public void onComplete(Throwable throwable) {
...
}
};
asyncResponse.register(callBack);
asyncResponse.resume("some Response");
}
}
@Stateless
@Path("/")
class EJBResource {
@GET @Asynchronous
public void longRunningOp(@Suspended AsyncResponse ar) {
executeLongRunningOp();
ar.resume("Hello async world!");
}
}
在此情况下,不需要显式线程管理,因为它在 EJB 容器的控制之下。响应是在通过对已插入 AsyncResponse 调用复原而产生的。因此,longRunningOp 的返回类型为空。JAX-RS 2.0 bean 和 EJB bean 生命周期
应用程序 | JAX-RS 2.0 | EJB | 结果 |
---|---|---|---|
资源 | perRequest | 无状态 | 无状态 |
perRequest | 单体 | 单体 | |
单体 | 无状态 | 无状态 | |
单体 | 单体 | 单体 | |
提供者 | 单体 | 无状态 | 无状态 |
单体 | 单体 | 单体 |
JAX-RS 2.0 范围和 CDI 范围生命周期
应用程序 | JAX-RS 2.0 范围 | CDI 范围注释 | 结果 |
---|---|---|---|
资源 | perRequest | @ApplicationScoped | 单体 |
perRequest | @RequestScoped | perRequest | |
perRequest | @Dependent | perRequest | |
perRequest | @SessionScoped | Session | |
perRequest | perRequest | ||
单体 | @ApplicationScoped | 单体 | |
单体 | @RequestScoped | perRequest | |
单体 | @Dependent | 单体 | |
单体 | @SessionScoped | Session | |
单体 | 单体 | ||
提供者 | 单体 | @ApplicationScoped | 单体 |
单体 | @RequestScoped | 单体 | |
单体 | @Dependent | 单体 | |
单体 | @SessionScoped | 单体 | |
单体 | 单体 |
JAX-RS 2.0 范围和 CDI 范围生命周期冲突消息
如果 JAX-RS 2.0 与 CDI 两者的范围生命周期冲突,那么会显示以下警告消息。它们只是警告消息,不需要执行任何操作。
如果 JAXRS-2.0 资源范围与 CDI 范围不匹配并且 CDI 中存在该资源实例(导致 Liberty 从 CDI 获取该资源实例),那么会显示此消息。如果实例来自 JAX-RS,那么它未包含 CDI 注入。CWWKW1001W: JAXRS-2.0 资源 {0} 的范围 {1} 与 CDI 范围 {2} 不匹配。Liberty 从 {3} 获取资源实例。
显示此消息的原因是该提供者实例仅为“单体”。如果该提供者的 CDI 范围是“依赖”或 ApplicationScoped,那么 Liberty 从 CDI 获取该提供者实例。如果实例来自 JAX-RS,那么它未包含 CDI 注入。CWWKW1002W: JAXRS-2.0 提供者 {0} 的 CDI 范围为 {1}。Liberty 从 {2} 获取该提供者实例。