Lifecycle界面如何在Spring中运行? 什么是“顶级单身豆”?

在Spring javadoc中说,“请注意,Lifecycle接口仅支持顶级单例bean。” 这里的URL

我的LifecycleBeanTest.xml描述了bean,如下所示:

    

所以看起来很“热”和“单调”。

这是什么意思? 如何让Spring知道我的bean实现Lifecycle并用它做点什么?

假设我的main方法在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

我知道我可以在代码中等待bean。 挂钩Spring本身很有意思。

您应该使用SmartLifecycle而不是Lifecycle 。 只有前者按预期Lifecycle才能正常工作。 确保在isRunning()实现中返回true。

我已经将SmartLifecycle用于其听起来像是为其设计的异步作业。 我想它会对你有用,但同时你可能会看看ApplicationListenerContextStoppedEvent事件。

您可以检查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; } } } 

因此,您无法阻止应用程序上下文关闭。

使用TestContext框架测试服务

如果您正在使用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内容:

     

PS启动和停止上下文不是您可能想要在同一应用程序上下文中多次执行的操作,因此您可能需要在测试方法上放置@DirtiesContext注释以获得最佳结果。

回答问题的新版本

DefaultLifecycleProcessor使用beanFactory.getBeanNamesForType(Lifecycle.class, false, false); 从getBeanNamesForType javadoc检索实现生命周期的bean列表:

注意:此方法仅对顶级bean进行内省。 它不会检查可能与指定类型匹配的嵌套bean。

所以这个方法没有列出内部bean(当只有xml配置可用时它们被称为嵌套 - 它们被声明为嵌套的bean xml元素)。

请考虑文档中的以下示例

     Tony 51    

Start()和Stop()只是由应用程序上下文传播的事件,它们与应用程序上下文的生命周期无关,例如,您可以使用某些服务bean实现下载管理器 - 当用户点击“暂停”按钮时,您将播放“停止”事件,然后当用户点击“开始”按钮时,您可以通过广播“开始”事件来恢复处理。 Spring在这里是可用的,因为它以正确的顺序调度事件。

我从未使用Lifecycle接口,我不确定它是如何工作的。 但看起来简单地在上下文中调用start()调用这些回调:

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

但是通常我使用@PostConstruct / @PreDestroy注释或实现InitializingBeanDisposableBean

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

注意我不在应用程序上下文上调用close() 。 由于您在LifecycleBean中创建非守护程序线程,因此即使main退出,JVM仍会继续运行。

当您停止该线程JVM存在但未正确关闭应用程序上下文时。 基本上,最后一个非守护程序线程停止,导致整个JVM终止。 这里有一些hacky解决方法 – 当你的后台非守护程序线程即将完成时,显式关闭应用程序上下文:

 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(); } } 

所以,最后我发现如果我:

1)将我的bean定义为implements Lifecycle

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(); 

然后我得到我想要的东西:

在执行所有Lifecycle bean的所有停止之前,上下文创建代码不会退出。 因此,此代码适用于JUnit测试

那么使用SmartLifecycle呢? 似乎它提供了所有必要的function。

有方法public void stop(Runnable contextStopping){}。 您可以通过在所需的时间内执行contextStopping来继续关闭应用程序上下文。

在我的环境中,即使在J-UNIT上也可以正常工作,当然也可以使用SpringJUnit4ClassRunner运行它们。