我使用 Eclipse Maven 插件创建一个 Java EE 7 项目。我的问题是,当我运行应用程序时,不会调用实现 SerlvetContextListener 的类。是什么导致了这个问题?
@WebListener
public class ApplicationContextListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce)
{
Request request = new HttpRequest(sce);
new Thread (request).start();
HibernateUtil.getSessionFactory();
}
@Override
public void contextDestroyed(ServletContextEvent sce)
{
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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/web-app_3_1.xsd"
version="3.1">
<listener>com.kyrogaming.AppServletContextListener</listener>
<!-- Jersey Mapping -->
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.kyrogaming.webservices</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>jersey-servlet</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
<!-- end Jersey Mapping -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
总结 JNL 和 Ted Goddard 的答案:
对于要由 servlet 容器加载的 ServletContextListener(或其他侦听器,例如 ServletContextAttributeListener 或 ServletRequestAttributeListener),您需要将其告知容器。如 API 文档中所述,有三种方法可以实现此目的:
在部署描述符(web.xml)中声明它:
com.kyrogaming.AppServletContextListener@WebListener
注释它的类(参见下面的“关于注释的注意事项”)
或以编程方式注册它,通过 ServletContext 中的方法,例如 addListener()。
注释注意事项
方法 1) 和 3) 总是有效的。要使方法 2)(注释)起作用,必须将 servlet 容器配置为扫描类路径中的类,以查找带注释的侦听器类。
如果 web.xml 包含属性 WEB-INF/classes
WEB-INF/lib
),则不会扫描 webapp 自己的类(在
metadata-complete="true"
下)和库(在 false
下的 JAR)。请参阅 Java Servlet 规范版本 3.0,第 8.1 章,“注释和可插入性”。
在 Web 应用程序中,使用注释的类仅当位于 WEB-INF/classes 目录中,或者打包在应用程序内的 WEB-INF/lib 中的 jar 文件中时,才会处理其注释。 Web 应用程序部署描述符在 web-app 元素上包含一个新的“metadata-complete”属性。 “metadata-complete”属性定义 Web 描述符是否完整,或者是否应在部署时检查 jar 文件的类文件中的注释和 Web 片段。如果“metadata-complete”设置为“true”,则部署工具必须忽略应用程序和 Web 片段的类文件中存在的任何 servlet 注释。如果未指定metadata-complete属性或设置为“false”,则部署工具必须检查应用程序的类文件中的注释,并扫描Web片段。
因此,为了允许容器在 JAR 中查找带注释的类,请确保 web.xml 设置
metadata-complete="false"
,或者根本不设置它。
注意设置此项可能会延迟应用程序启动;例如,请参阅设置metadata-complete =“true”后如何处理注释(解决了Tomcat 7启动缓慢的问题)?.
不幸的是,这仍然不能解释为什么问题中的 ServletContextListener 没有加载。请注意,问题中的 web.xml 不是
metadata-complete
,这意味着它默认为 false
,因此启用了类路径扫描。可能还有其他问题;这个清单希望有助于找到它。
在 web.xml 中使用metadata-complete="false" 为我解决了这个问题。
<web-app 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/web-app_3_1.xsd"
version="3.1"
metadata-complete="false">
在 web.xml 中,您还需要指定
<listener-class>
。
<listener>
<listener-class>
com.kyrogaming.AppServletContextListener
</listener-class>
</listener>
为了记录,我添加了另一个可能的(而且相当恶毒的)原因
ServletContextListener
未被调用。
当您有
java.lang.LinkageError
时,即您忘记将 <scope>provided</scope>
添加到 javax.servlet-api
依赖项时,就会发生这种情况。
在这种情况下,会创建侦听器实例,但仅执行静态部分,而不执行 contextInitialized
和 contextDestroyed
方法。
只有在调用某些 servlet 时您才会发现,因为在侦听器实例化期间不会引发链接错误。
还有另一种极其罕见的情况也可能导致这种情况。 (我花了4个小时才发现)
如果您使用的是 Tomcat10,那么您无法在 maven/gradle 中使用
javax.servlet
库。
Tomcat9 仍然有
javax.servlet
,但 Tomcat10 迁移到 jakarta.servlet
Tomcat10 期望有使用
jakarta.servlet.ServletContextListener
的监听器类
所以使用这个maven依赖:(提供了范围,因为Tomcat10已经有这样的库)
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
正在运行的容器可能需要显式允许扫描注释:
例如码头:
cd [JETTY_BASE]
java -jar [JETTY_HOME]/start.jar --add-module=annotations
在Spring-Boot 1.3+场景中,需要将@WebListener(和@WebFilter、@WebServlet)注释的类的包置于@ServletComponentScan包范围内。
Per Baeldung。
虽然参加聚会已经很晚了,但我想添加帮助我的答案:
您缺少零参数公共构造函数。添加后,无论以何种方式注册,WebListener 都会被调用。一个简单的
public ApplicationContextListener() {}
将在你的情况下解决问题。
请参阅 Servlet 规范 的第 4.4.3.4 节,了解其中规定的详细信息
给定的 EventListener 类必须定义零参数 构造函数,用于实例化它。