Java Websphere Liberty 配置文件中的 EE 依赖注入
Java EE Dependency Injection in Websphere Liberty profile
我正在尝试在我非常简单的 Web 应用程序中使用 CDI,该应用程序在通过 Docker 安装的 Websphere Liberty 配置文件中运行。
然而,注入失败,除非我在注入的 bean 上指定范围注释(例如 @ApplicationScoped
),尽管根据很多在线教程(例如 this),Java EE 规范不需要这个。
下面是失败的代码:
HelloWorldServlet.java
package my.simple.app;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/HelloWorld")
public class HelloWorldServlet extends HttpServlet {
static String PAGE_HEADER = "<html><head /><body>";
static String PAGE_FOOTER = "</body></html>";
@Inject
HelloService helloService;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.println(PAGE_HEADER);
writer.println("<h1>" + helloService.createHelloMessage("World") + "</h1>");
writer.println(PAGE_FOOTER);
writer.close();
}
}
HelloService.java
package my.simple.app;
public class HelloService {
String createHelloMessage(String name) {
return "Hello " + name + "!";
}
}
server.xml(Docker 图片是 websphere-liberty:javaee7)
<server description="default servlet engine">
<httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" httpsPort="9443" />
<!-- Enable features -->
<featureManager>
<feature>servlet-3.1</feature>
<feature>cdi-1.2</feature>
</featureManager>
</server>
但是我得到这个错误
Error 404: javax.servlet.UnavailableException: SRVE0319E: For the [my.simple.app.HelloWorldServlet] servlet, my.simple.app.HelloWorldServlet servlet class was found, but a resource injection failure has occurred. The @Inject java.lang.reflect.Field.helloService reference of type my.simple.app.HelloService for the null component in the app.war module of the app application cannot be resolved.
然而,一旦我将 @ApplicationScoped
添加到 HelloService,它就会开始工作。
我做错了什么?
解决方案:
在 CDI1.2(我正在使用的)中,默认情况下只发现带注释的 bean。要让所有bean都被发现,需要在beans.xml
中启用显式发现模式
链接:
我对 EJB 还不够熟悉,但是你可以尝试在你的服务中添加 @Stateless 或 @Service 注解 class
package my.simple.app;
@Stateless
//@Service
public class HelloService {
String createHelloMessage(String name) {
return "Hello " + name + "!";
}
}
编辑:
或者,如果你不能像你提到的那样修改 HelloService,你可以使用 Producer Method http://docs.oracle.com/javaee/6/tutorial/doc/gjdid.html
您可以通过将 bean 发现模式更改为 all
来强制 CDI 将 servlet 视为 bean 并执行注入。
This article 提供了一些有用的背景知识和示例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
或者,在 WDT 中,您可以通过右键单击项目并 selecting Java EE Tools -> Generate CDI Beans Deployment Descriptor Stub,并确保从下拉列表 select all
"Bean discovery mode" selection.
缺点是性能受到影响,因为应用程序需要更长的时间才能启动,但这是您可以做出的权衡,以避免重新编译。
虽然 Scott 的建议可以解决您的问题,但这里是关于它如何工作的完整图片。
在 CDI 1.2 中,所有应用程序都默认启用 CDI。
如果有空 beans.xml 或 beans.xml 且 bean-discovery-mode="all",所有 classes 都选择加入 bean。
在没有 beans.xml 或 beans.xml 且 bean-discovery-mode="annotated" 的情况下,将扫描每个 class 以查找 bean。只有使用 bean 定义注释 (https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#bean_defining_annotations) 注释的 classes 被视为 beans。
在您的第一个示例中,由于您没有 beans.xml,因此执行了对 bean 定义注释的扫描。由于未找到 bean 定义注释,因此禁用了 cdi。结果,注入失败。
要启用注入,有两种解决方案:
1. 使用定义注释的 bean 注释 class HelloService(例如任何范围:ApplicationScoped、RequestScoped、SessionScoped、ConversationScoped、Dependent 等)。这将使 HelloService 成为 CDI bean,然后注入将成功。
2. 在 WEB-INF for .war files or META-INF for .jar files
关于bean archives的更多信息,请参考https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#bean_archive。
我正在尝试在我非常简单的 Web 应用程序中使用 CDI,该应用程序在通过 Docker 安装的 Websphere Liberty 配置文件中运行。
然而,注入失败,除非我在注入的 bean 上指定范围注释(例如 @ApplicationScoped
),尽管根据很多在线教程(例如 this),Java EE 规范不需要这个。
下面是失败的代码:
HelloWorldServlet.java
package my.simple.app;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/HelloWorld")
public class HelloWorldServlet extends HttpServlet {
static String PAGE_HEADER = "<html><head /><body>";
static String PAGE_FOOTER = "</body></html>";
@Inject
HelloService helloService;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.println(PAGE_HEADER);
writer.println("<h1>" + helloService.createHelloMessage("World") + "</h1>");
writer.println(PAGE_FOOTER);
writer.close();
}
}
HelloService.java
package my.simple.app;
public class HelloService {
String createHelloMessage(String name) {
return "Hello " + name + "!";
}
}
server.xml(Docker 图片是 websphere-liberty:javaee7)
<server description="default servlet engine">
<httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" httpsPort="9443" />
<!-- Enable features -->
<featureManager>
<feature>servlet-3.1</feature>
<feature>cdi-1.2</feature>
</featureManager>
</server>
但是我得到这个错误
Error 404: javax.servlet.UnavailableException: SRVE0319E: For the [my.simple.app.HelloWorldServlet] servlet, my.simple.app.HelloWorldServlet servlet class was found, but a resource injection failure has occurred. The @Inject java.lang.reflect.Field.helloService reference of type my.simple.app.HelloService for the null component in the app.war module of the app application cannot be resolved.
然而,一旦我将 @ApplicationScoped
添加到 HelloService,它就会开始工作。
我做错了什么?
解决方案:
在 CDI1.2(我正在使用的)中,默认情况下只发现带注释的 bean。要让所有bean都被发现,需要在beans.xml
链接:
我对 EJB 还不够熟悉,但是你可以尝试在你的服务中添加 @Stateless 或 @Service 注解 class
package my.simple.app;
@Stateless
//@Service
public class HelloService {
String createHelloMessage(String name) {
return "Hello " + name + "!";
}
}
编辑: 或者,如果你不能像你提到的那样修改 HelloService,你可以使用 Producer Method http://docs.oracle.com/javaee/6/tutorial/doc/gjdid.html
您可以通过将 bean 发现模式更改为 all
来强制 CDI 将 servlet 视为 bean 并执行注入。
This article 提供了一些有用的背景知识和示例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
或者,在 WDT 中,您可以通过右键单击项目并 selecting Java EE Tools -> Generate CDI Beans Deployment Descriptor Stub,并确保从下拉列表 select all
"Bean discovery mode" selection.
缺点是性能受到影响,因为应用程序需要更长的时间才能启动,但这是您可以做出的权衡,以避免重新编译。
虽然 Scott 的建议可以解决您的问题,但这里是关于它如何工作的完整图片。
在 CDI 1.2 中,所有应用程序都默认启用 CDI。
如果有空 beans.xml 或 beans.xml 且 bean-discovery-mode="all",所有 classes 都选择加入 bean。
在没有 beans.xml 或 beans.xml 且 bean-discovery-mode="annotated" 的情况下,将扫描每个 class 以查找 bean。只有使用 bean 定义注释 (https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#bean_defining_annotations) 注释的 classes 被视为 beans。
在您的第一个示例中,由于您没有 beans.xml,因此执行了对 bean 定义注释的扫描。由于未找到 bean 定义注释,因此禁用了 cdi。结果,注入失败。
要启用注入,有两种解决方案: 1. 使用定义注释的 bean 注释 class HelloService(例如任何范围:ApplicationScoped、RequestScoped、SessionScoped、ConversationScoped、Dependent 等)。这将使 HelloService 成为 CDI bean,然后注入将成功。 2. 在 WEB-INF for .war files or META-INF for .jar files
关于bean archives的更多信息,请参考https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#bean_archive。