简体   繁体   English

Spring:SmartLifecycle:Lifecycle.stop() 未调用<mvc:annotation-driven />但它与@EnableWebMvc

[英]Spring: SmartLifecycle: Lifecycle.stop() not called with <mvc:annotation-driven /> but it is with @EnableWebMvc

I cannot figure out why my simple Spring 5.1.12 webapp running in Tomcat calls the Lifecycle.stop() method when I configure using java config but not using XML config.当我使用 java 配置但不使用 XML 配置进行配置时,我无法弄清楚为什么在 Tomcat 中运行的简单 Spring 5.1.12 webapp 调用 Lifecycle.stop() 方法。 I have the bean LifecycleLoggingBean in question implementing SmartLifecycle and I've overridden all the methods (see end of question).我有实现 SmartLifecycle 的有问题的 bean LifecycleLoggingBean,并且我已经覆盖了所有方法(请参阅问题结尾)。

UPDATE: If I instantiate my LifecycleLoggingBean in the mvc-servlet.xml then stop is called.更新:如果我在 mvc-servlet.xml 中实例化我的 LifecycleLoggingBean,则会调用停止。 I guess nothing stops the main application context by default.我想默认情况下没有什么能阻止主应用程序上下文。 Is there a "correct" way to trigger a close on the main application context?是否有一种“正确”的方式来触发关闭主应用程序上下文? As it turns out, it is called from the main application context as well, it just isn't logged.事实证明,它也是从主应用程序上下文中调用的,只是没有记录。

If I configure the app like this, then Lifecycle.stop() is called:如果我这样配置应用程序,则会调用 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;
    }
}

If I remove that class and instead put the following in web.xml:如果我删除 class 并将以下内容放入 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>

and the following in applicationContext.xml:以及 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>

and add a mvc-servlet.xml:并添加一个 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>

Then Lifecycle.stop() is not called.然后 Lifecycle.stop() 不会被调用。

In all cases, Lifecycle.start() is called.在所有情况下,Lifecycle.start() 都会被调用。

Here are logs with Java config:这是带有 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

And here are the logs with xml config:这是带有 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

Here is the LifecycleLoggingBean:这是 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;
    }
}

As it turns out, the stop() method was called from the main application context as well, it just isn't logged.事实证明,stop() 方法也是从主应用程序上下文调用的,只是没有记录。 Log4j2 auto-registers itself as a web fragment which means it is shut down before the ContextLoaderListener's contextDestroyed method is called. Log4j2 自动将自身注册为 web 片段,这意味着它在调用 ContextLoaderListener 的 contextDestroyed 方法之前关闭。 When contextDestroyed is finally called, the log messages are lost.当最终调用 contextDestroyed 时,日志消息将丢失。 However, by logging to System.out I was able to see the messages in the tomcat log.但是,通过登录到 System.out,我能够在 tomcat 日志中看到消息。

By disabling the normal auto-registration of log4j and registering it the old (servlet 2.5 and before) way I was able to get log messages to show up.通过禁用 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>

This ends up being a sub-optimal solution as our logging infrastructure depends on using the Web Lookup ${web:servletContextName} which doesn't seem to get initialized outside of the default registration.这最终成为一个次优的解决方案,因为我们的日志记录基础设施依赖于使用Web Lookup ${web:servletContextName},它似乎没有在默认注册之外进行初始化。 So we are going to use @EventListener's instead of SmartLifecycle.所以我们将使用@EventListener 而不是SmartLifecycle。

@EventListener(classes = {
        ContextStoppedEvent.class,
        ContextClosedEvent.class
})
public void stop() {
    // still get to clean up.
}

There is an open issue in the log4j issue tracker describing exactly this issue: https://issues.apache.org/jira/browse/LOG4J2-2624 log4j 问题跟踪器中有一个未解决的问题,正是描述了这个问题: https://issues.apache.org/jira/browse/LOG4J2-2624

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM