복붙노트

[SPRING] Spring에서 Lifecycle 인터페이스는 어떻게 작동합니까? "최상위 싱글 톤 빈"이란 무엇입니까?

SPRING

Spring에서 Lifecycle 인터페이스는 어떻게 작동합니까? "최상위 싱글 톤 빈"이란 무엇입니까?

Spring javadoc에서 "Lifecycle 인터페이스는 최상위 레벨 싱글 톤에서만 지원됩니다."라고 말합니다. 여기에 URL

My LifecycleBeanTest.xml은 bean을 다음과 같이 설명합니다.

<beans ...>
    <bean id="lifecycle" class="tests.LifecycleBean"/>
</beans>

그래서 그것은 "topish"와 "singletonish"정도로 보인다.

무슨 뜻이에요? 스프링이 라이프 사이클을 구현하는 빈에 대해 어떻게 알게하고 그걸로 무엇을 할 수 있습니까?

Spring의 주요 메소드가 다음과 같다고 가정 해 보겠습니다.

public static void main(String[] args) {
    new ClassPathXmlApplicationContext("/tests/LifecycleBeanTest.xml").close();
}

따라서 컨텍스트를 인스턴스화 한 다음 즉시 컨텍스트를 닫습니다.

내 구성에 일부 bean을 만들면 응용 프로그램이 작동 할 때까지 close () 실행이 지연 될 수 있습니까? 그래서 메인 메소드 스레드는 애플리케이션 종료를 기다린다.

예를 들어, 다음 bean은 내가 생각한 방식으로 작동하지 않습니다. start ()가 아니라 stop ()이 호출됩니다.

package tests;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.Lifecycle;

public class LifecycleBean implements Lifecycle {

    private static final Logger log = LoggerFactory.getLogger(LifecycleBean.class);

    private final Thread thread = new Thread("Lifecycle") {
        {
            setDaemon(false);
            setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    log.error("Abnormal thread termination", e);
                }
            });
        }

        public void run() {
            for(int i=0; i<10 && !isInterrupted(); ++i) {
                log.info("Hearbeat {}", i);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    return;
                }
            }
        };
    };


    @Override
    public void start() {
        log.info("Starting bean");
        thread.start();
    }

    @Override
    public void stop() {
        log.info("Stopping bean");
        thread.interrupt();
        try {
            thread.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return;
        }
    }

    @Override
    public boolean isRunning() {
        return thread.isAlive();
    }

}

업데이트 1

나는 코드에서 콩을 기다릴 수 있다는 것을 안다. Spring 자체에 연결하는 것은 흥미 롭습니다.

해결법

  1. ==============================

    1.라이프 사이클 대신 SmartLifecycle을 사용해야합니다. 라이프 사이클이 작동 할 것으로 예상 할 때 전임자 만 작동합니다. isRunning () 구현에서 true를 반환해야합니다.

    라이프 사이클 대신 SmartLifecycle을 사용해야합니다. 라이프 사이클이 작동 할 것으로 예상 할 때 전임자 만 작동합니다. isRunning () 구현에서 true를 반환해야합니다.

    필자는 비동기 작업을 위해 SmartLifecycle을 사용했습니다. 나는 그것이 당신을 위해 일할 것이라고 생각하지만 동시에 ContextStoppedEvent와 같은 ApplicationListener와 이벤트를 살펴볼 것입니다.

  2. ==============================

    2.AbstractApplicationContext.doClose () 메소드를 검사하고 Spring 개발자가 애플리케이션 컨텍스트 닫기를 중단하지 않았는지 확인할 수있다.

    AbstractApplicationContext.doClose () 메소드를 검사하고 Spring 개발자가 애플리케이션 컨텍스트 닫기를 중단하지 않았는지 확인할 수있다.

    protected void doClose() {
        boolean actuallyClose;
        synchronized (this.activeMonitor) {
            actuallyClose = this.active && !this.closed;
            this.closed = true;
        }
    
        if (actuallyClose) {
            if (logger.isInfoEnabled()) {
                logger.info("Closing " + this);
            }
    
            try {
                // Publish shutdown event.
                publishEvent(new ContextClosedEvent(this));
            }
            catch (Throwable ex) {
                logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
            }
    
            // Stop all Lifecycle beans, to avoid delays during individual destruction.
            try {
                getLifecycleProcessor().onClose();
            }
            catch (Throwable ex) {
                logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
            }
    
            // Destroy all cached singletons in the context's BeanFactory.
            destroyBeans();
    
            // Close the state of this context itself.
            closeBeanFactory();
    
            // Let subclasses do some final clean-up if they wish...
            onClose();
    
            synchronized (this.activeMonitor) {
                this.active = false;
            }
        }
    }
    

    따라서 응용 프로그램 컨텍스트가 닫히는 것을 막을 수는 없습니다.

    Spring 테스트 컨텍스트 프레임 워크를 JUnit과 함께 사용하는 경우,이를 사용하여 Lifecycle을 구현하는 서비스를 테스트 할 수 있다고 생각합니다. 내부 Spring 테스트 중 하나에서 기술을 사용했습니다.

    약간 수정 된 LifecycleBean (waitForTermination () 메소드를 추가했습니다) :

    public class LifecycleBean implements Lifecycle {
    
        private static final Logger log = LoggerFactory
                .getLogger(LifecycleBean.class);
    
        private final Thread thread = new Thread("Lifecycle") {
            {
                setDaemon(false);
                setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
    
                    @Override
                    public void uncaughtException(Thread t, Throwable e) {
                        log.error("Abnormal thread termination", e);
                    }
                });
            }
    
            public void run() {
                for (int i = 0; i < 10 && !isInterrupted(); ++i) {
                    log.info("Hearbeat {}", i);
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            };
        };
    
        @Override
        public void start() {
            log.info("Starting bean");
            thread.start();
        }
    
        @Override
        public void stop() {
            log.info("Stopping bean");
            thread.interrupt();
            waitForTermination();
        }
    
        @Override
        public boolean isRunning() {
            return thread.isAlive();
        }
    
        public void waitForTermination() {
            try {
                thread.join();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;
            }
        }
    }
    

    테스트 클래스 :

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:Test-context.xml")
    public class LifecycleBeanTest {
    
        @Autowired
        LifecycleBean bean;
    
        Lifecycle appContextLifeCycle;
    
        @Autowired
        public void setLifeCycle(ApplicationContext context){
            this.appContextLifeCycle = (Lifecycle)context;
        }
    
        @Test
        public void testLifeCycle(){
            //"start" application context
            appContextLifeCycle.start();
    
            bean.waitForTermination();
        }
    }
    

    Test-context.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">
    
    <bean class="LifecycleBean"/>
    </beans>
    

    추신 컨텍스트를 시작하고 중지하는 것은 동일한 응용 프로그램 컨텍스트에서 여러 번 할 수있는 일이 아니므로 최상의 결과를 얻으려면 테스트 메서드에 @DirtiesContextannotation을 넣어야 할 수도 있습니다.

    DefaultLifecycleProcessor는 beanFactory.getBeanNamesForType (Lifecycle.class, false, false)를 사용합니다. 생명주기를 구현하는 빈 목록을 검색하는 방법 getBeanNamesForType부터 javadoc :

    따라서이 메소드는 내부 bean을 나열하지 않습니다 (xml 구성 만 사용 가능할 때 중첩 호출 됨 - 중첩 된 bean xml 요소로 선언 됨).

    문서에서 다음 예제를 고려하십시오.

    <bean id="outer" class="...">
      <!-- Instead of using a reference to target, just use an inner bean -->
      <property name="target">
        <bean class="com.mycompany.PersonImpl">
          <property name="name"><value>Tony</value></property>
          <property name="age"><value>51</value></property>
        </bean>
      </property>
    </bean>
    

    Start () 및 Stop ()은 응용 프로그램 컨텍스트의 수명과 연결되지 않은 응용 프로그램 컨텍스트에 의해 전파되는 이벤트 일뿐입니다. 예를 들어 일부 서비스 빈과 함께 다운로드 관리자를 구현할 수 있습니다. 예를 들어 사용자가 "일시 중지" 당신은 "stop"이벤트를 방송 할 것이고, 사용자가 "start"버튼을 눌렀을 때 "start"이벤트를 방송함으로써 처리를 재개 할 수있다. 적절한 순서로 이벤트를 전달하기 때문에 Spring을 사용할 수 있습니다.

  3. ==============================

    3.라이프 사이클 인터페이스를 사용한 적이 없으며 어떻게 작동하는지 잘 모르겠습니다. 그러나이 콜백을 컨텍스트에서 호출하는 것만으로 start ()를 호출하는 것처럼 보입니다.

    라이프 사이클 인터페이스를 사용한 적이 없으며 어떻게 작동하는지 잘 모르겠습니다. 그러나이 콜백을 컨텍스트에서 호출하는 것만으로 start ()를 호출하는 것처럼 보입니다.

    AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("...");
    ctx.start();
    

    그러나 일반적으로 @ PostConstruct / @ PreDestroy 주석을 사용하거나 InitializingBean 및 DisposableBean을 구현합니다.

    public class LifecycleBean implements InitializingBean, DisposableBean {
    
        @Override
        public void afterPropertiesSet() {
            //...
        }
    
        @Override
        public void destroy() {
            //...
        }
    
    }
    

    응용 프로그램 컨텍스트에서 close ()를 호출하지 않습니다. LifecycleBean에서 비 데몬 쓰레드를 생성하고 있기 때문에 JVM은 main이 종료 되더라도 실행 상태를 유지합니다.

    해당 스레드를 중지하면 JVM이 있지만 응용 프로그램 컨텍스트를 제대로 닫지 않습니다. 기본적으로 비 데몬 (non-daemon) 스레드가 중지되어 전체 JVM이 종료됩니다. 해커가 아닌 백그라운드 스레드가 끝날 때 응용 프로그램 컨텍스트를 명시 적으로 닫습니다.

    public class LifecycleBean implements ApplicationContextAware /* ... */ {
    
        private AbstractApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) {
            this.applicationContext = (AbstractApplicationContext)applicationContext;
        }
    
        public void run() {
            for(int i=0; i<10 && !isInterrupted(); ++i) {
                log.info("Hearbeat {}", i);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                }
            }
            applicationContext.close();
        }
    
    }
    
  4. ==============================

    4.그래서, 마침내 나는 만약 내가 발견 :

    그래서, 마침내 나는 만약 내가 발견 :

    1) 내 bean을 라이프 사이클을 구현하는 것으로 정의하십시오.

    2) 다음과 같이 stop () 메소드에서 지연을 도입하십시오.

    @Override
    public void stop() {
        log.info("Stopping bean");
        //thread.interrupt();
        try {
            thread.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return;
        }
    }
    

    3) 코드 컨텍스트 생성은 다음과 같습니다.

    new ClassPathXmlApplicationContext("/tests/LifecycleBeanTest.xml").stop();
    

    그럼 내가 원하는 것을 얻는다.

    컨텍스트 생성 코드는 모든 라이프 사이클 빈의 모든 중지가 실행될 때까지 종료되지 않습니다. 따라서이 코드는 JUnit 테스트에서 작동합니다.

  5. ==============================

    5.SmartLifecycle 사용은 어떻습니까? 필요한 모든 기능을 제공하는 것처럼 보입니다.

    SmartLifecycle 사용은 어떻습니까? 필요한 모든 기능을 제공하는 것처럼 보입니다.

    메소드 public void stop (Runnable contextStopping) {}이 있습니다. 또한 원하는 시간에 contextStopping을 전달하여 앱 컨텍스트 닫기를 계속할 수 있습니다.

    내 환경에서는 J-UNIT에서도 물론 SpringJUnit4ClassRunner를 사용하여 모든 작업을 수행 할 수 있습니다.

  6. from https://stackoverflow.com/questions/13551408/how-does-lifecycle-interface-work-in-spring-what-are-top-level-singleton-beans by cc-by-sa and MIT license