在Spring javadoc中说,“请注意,Lifecycle接口仅支持顶级单例bean。”这里URL
我的LifecycleBeanTest.xml
描述bean如下:
<beans ...>
<bean id="lifecycle" class="tests.LifecycleBean"/>
</beans>
所以它看起来很“突出”和“单调”。
这是什么意思?如何让Spring知道我的bean实现Lifecycle
并用它做点什么?
假设我的main方法在Spring中看起来跟随
public static void main(String[] args) {
new ClassPathXmlApplicationContext("/tests/LifecycleBeanTest.xml").close();
}
所以,它实例化上下文然后立即关闭它。
我可以在我的配置中创建一些bean,这会延迟close()
执行,直到应用程序完成所有工作吗?那么主方法线程等待应用程序终止?
例如,以下bean不按我想象的方式工作。 <{1}}没有start()
都被调用。
stop()
更新1
我知道我可以在代码中等待bean。挂钩Spring本身很有意思。
答案 0 :(得分:4)
您应该使用SmartLifecycle
代替Lifecycle
。只有前者按预期工作Lifecycle
才能正常工作。确保在isRunning()
实施中返回true。
我已将SmartLifecycle
用于其听起来像是为其设计的异步作业。我想它会对你有用,但与此同时你可能会看到ApplicationListener
和ContextStoppedEvent
之类的事件。
答案 1 :(得分:4)
您可以检查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的服务,我使用了one of the internal Spring tests中的技术
略微修改了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>
P.S。启动和停止上下文不是您可能想在同一应用程序上下文中多次执行的操作,因此您可能需要在测试方法上添加@DirtiesContext
注释以获得最佳结果。
DefaultLifecycleProcessor使用beanFactory.getBeanNamesForType(Lifecycle.class, false, false);
来检索实现Lifecycle的bean列表
来自getBeanNamesForType javadoc:
注意:此方法仅对顶级bean进行内省。确实如此 不检查可能与指定类型匹配的嵌套bean 同样。
所以这个方法没有列出内部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()只是由应用程序上下文传播的事件,它们与应用程序上下文的生命周期无关,例如,您可以使用某些服务bean实现下载管理器 - 当用户点击“暂停时” “按钮,您将播放”停止“事件,然后当用户点击”开始“按钮时,您可以通过广播”开始“事件来恢复处理。 Spring在这里是可用的,因为它以正确的顺序调度事件。
答案 2 :(得分:3)
我从未使用Lifecycle
界面,我不确定它是如何工作的。但看起来只是在上下文中调用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
中创建了非守护程序线程,因此即使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();
}
}
答案 3 :(得分:0)
所以,最后我发现如果我:
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测试
答案 4 :(得分:0)
使用SmartLifecycle怎么样?似乎它提供了所有必要的功能。
有方法public void stop(Runnable contextStopping){}。 并且您可以通过在所需的时间内执行contextStopping来继续关闭应用程序上下文。
在我的环境中,即使在J-UNIT上也可以正常工作,当然也可以使用SpringJUnit4ClassRunner运行它们。