java - Spring:SmartLifecycle:Lifecycle.stop() 未调用但它与@EnableWebMvc
问题描述
当我使用 java config 但不使用 XML config 进行配置时,我无法弄清楚为什么我在 Tomcat 中运行的简单 Spring 5.1.12 webapp 调用 Lifecycle.stop() 方法。我有问题中的 bean LifecycleLoggingBean 实现 SmartLifecycle 并且我已经覆盖了所有方法(见问题结尾)。
更新:如果我在 mvc-servlet.xml 中实例化我的 LifecycleLoggingBean ,则调用 stop 。我想默认情况下没有什么能阻止主应用程序上下文。是否有一种“正确”的方式来触发主应用程序上下文的关闭?事实证明,它也是从主应用程序上下文中调用的,只是没有记录。
如果我像这样配置应用程序,则会调用 Lifecycle.stop():
@EnableWebMvc
@Configuration
@ComponentScan({"some.package"})
public class SpringConfig implements WebMvcConfigurer {
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
如果我删除该类,而是将以下内容放入 web.xml:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
以及 applicationContext.xml 中的以下内容:
<context:component-scan base-package="some.package" />
<mvc:annotation-driven />
<bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name = "prefix" value = "/WEB-INF/jsp/"/>
<property name = "suffix" value = ".jsp"/>
</bean>
并添加一个 mvc-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">
</beans>
然后 Lifecycle.stop() 不被调用。
在所有情况下,都会调用 Lifecycle.start()。
以下是带有 Java 配置的日志:
[org.springframework.web.servlet.DispatcherServlet]:525 - - Initializing Servlet 'dispatcher'
[some.packge.testapp.LifecycleLoggingBean]:15 - - ********** SmartLifecycle isAutoStartup
[some.packge.testapp.LifecycleLoggingBean]:21 - - ********** SmartLifecycle getPhase
[some.packge.testapp.LifecycleLoggingBean]:43 - - ********** Lifecycle isRunning false
[some.packge.testapp.LifecycleLoggingBean]:15 - - ********** SmartLifecycle isAutoStartup
[some.packge.testapp.LifecycleLoggingBean]:27 - - ********** Lifecycle start
[org.springframework.web.servlet.DispatcherServlet]:547 - - Completed initialization in 953 ms
[some.packge.testapp.LifecycleLoggingBean]:21 - - ********** SmartLifecycle getPhase
[some.packge.testapp.LifecycleLoggingBean]:43 - - ********** Lifecycle isRunning true
[some.packge.testapp.LifecycleLoggingBean]:32 - - ********** SmartLifecycle stop
[some.packge.testapp.LifecycleLoggingBean]:39 - - ********** Lifecycle stop
以下是带有 xml 配置的日志:
[org.springframework.web.context.ContextLoader]:271 - - Root WebApplicationContext: initialization started
[some.package.testapp.LifecycleLoggingBean]:15 - - ********** SmartLifecycle isAutoStartup
[some.package.testapp.LifecycleLoggingBean]:21 - - ********** SmartLifecycle getPhase
[some.package.testapp.LifecycleLoggingBean]:43 - - ********** Lifecycle isRunning false
[some.package.testapp.LifecycleLoggingBean]:15 - - ********** SmartLifecycle isAutoStartup
[some.package.testapp.LifecycleLoggingBean]:27 - - ********** Lifecycle start
[org.springframework.web.context.ContextLoader]:307 - - Root WebApplicationContext initialized in 1203 ms
[org.springframework.web.servlet.DispatcherServlet]:525 - - Initializing Servlet 'dispatcher'
[org.springframework.web.servlet.DispatcherServlet]:547 - - Completed initialization in 31 ms
[org.springframework.web.servlet.DispatcherServlet]:525 - - Initializing Servlet 'mvc'
[org.springframework.web.servlet.DispatcherServlet]:547 - - Completed initialization in 31 ms
这是 LifecycleLoggingBean:
@Component
public class LifecycleLoggingBean implements SmartLifecycle {
private final Logger logger = LoggerFactory.getLogger(LifecycleLoggingBean.class);
@Override
public boolean isAutoStartup() {
logger.info("********** SmartLifecycle isAutoStartup");
return true;
}
@Override
public int getPhase() {
// start last and stop first
logger.info("********** SmartLifecycle getPhase");
return Integer.MAX_VALUE;
}
private AtomicBoolean isRunning = new AtomicBoolean(false);
@Override
public void start() {
logger.info("********** Lifecycle start");
isRunning.set(true);
}
@Override
public void stop(Runnable callback) {
logger.info("********** SmartLifecycle stop");
stop();
callback.run();
}
@Override
public void stop() {
isRunning.set(false);
logger.info("********** Lifecycle stop");
}
public boolean isRunning() {
boolean rval = isRunning.get();
logger.info("********** Lifecycle isRunning {}", rval);
return rval;
}
}
解决方案
事实证明,stop() 方法也是从主应用程序上下文调用的,只是没有记录。Log4j2自动将自己注册为 Web 片段,这意味着它在 ContextLoaderListener 的 contextDestroyed 方法被调用之前被关闭。当最终调用 contextDestroyed 时,日志消息会丢失。但是,通过登录到 System.out,我能够看到 tomcat 日志中的消息。
通过禁用 log4j 的正常自动注册并以旧的(servlet 2.5 及之前)方式注册它,我能够显示日志消息。
<context-param>
<param-name>isLog4jAutoInitializationDisabled</param-name>
<param-value>true</param-value>
</context-param>
<listener>
<listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>
<filter>
<filter-name>log4jServletFilter</filter-name>
<filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>log4jServletFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
<!-- Servlet 3.0 w/ disabled auto-initialization only; not supported in 2.5 -->
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
这最终成为了一个次优的解决方案,因为我们的日志基础设施依赖于使用Web 查找${web:servletContextName},它似乎没有在默认注册之外进行初始化。所以我们将使用@EventListener 而不是SmartLifecycle。
@EventListener(classes = {
ContextStoppedEvent.class,
ContextClosedEvent.class
})
public void stop() {
// still get to clean up.
}
log4j 问题跟踪器中有一个未解决的问题准确描述了这个问题: https ://issues.apache.org/jira/browse/LOG4J2-2624
推荐阅读
- flutter - 如何使用getx制作相机屏幕?
- php - 如何在 Drupal 代码中显示商店的渲染实体?
- google-app-engine - 如何使用 App Engine 处理谷歌建议 API 配额问题?
- c# - 如何实现访问同一个表但排除某些数据的“标题”基类
- node.js - 密码验证失败
- c# - 如何解决 MSTest.TestAdapter 构建错误
- amazon-web-services - 如何在 `~/.aws/config` 中生成 AWS 配置文件以用于 CodeBuild 项目
- wordpress - 在 WordPress 子页面中链接到 div 的 ID
- vue.js - 将 URL 参数传递到 VUE.js 中的输入值
- ruby-on-rails - OWASP ZAP 说缺少 httponly 标志,但它以 chrome 显示。任何想法如何解决它?