Spring

Spring : SmartLifecycle : Lifecycle.stop ()은 <mvc : annotation-driven />으로 호출되지 않지만 @EnableWebMvc로 호출됩니다.

기록만이살길 2021. 3. 13. 09:05
반응형

Spring : SmartLifecycle : Lifecycle.stop ()은 으로 호출되지 않지만 @EnableWebMvc로 호출됩니다.

1. 질문(문제점):

Java 구성을 사용하고 XML 구성을 사용하지 않고 구성 할 때 Tomcat에서 실행되는 간단한 Spring 5.1.12 웹 응용 프로그램이 Lifecycle.stop () 메서드를 호출하는 이유를 알 수 없습니다. SmartLifecycle을 구현하는 LifecycleLoggingBean 빈이 있으며 모든 메서드를 재정의했습니다 (질문 끝 참조).

업데이트 : 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;
    }
}

2. 해결방안:

결과적으로 stop () 메서드 기본 응용 프로그램 컨텍스트에서도 호출되었으며 로깅되지 않습니다. Log4j2는 자신을 웹 조각으로 자동 등록 하므로 ContextLoaderListener의 contextDestroyed 메서드가 호출되기 전에 종료됩니다. contextDestroyed가 마지막으로 호출되면 로그 메시지가 손실됩니다. 그러나 System.out에 로그인하여 tomcat 로그의 메시지를 볼 수있었습니다.

log4j의 정상적인 자동 등록을 비활성화하고 이전 (서블릿 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 : servletContextName} 사용에 의존 하기 때문에 이는 결국 차선책이됩니다. 따라서 SmartLifecycle 대신 @EventListener를 사용할 것입니다.

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

log4j 문제 추적기에이 문제를 정확하게 설명하는 미해결 문제가 있습니다. https://issues.apache.org/jira/browse/LOG4J2-2624

65709164
반응형