@未调用Spring单例bean的PreDestroy方法
我在@未调用Spring单例bean的PreDestroy方法,spring,Spring,我在beans.xml中定义了一个Springbean,如下所示: <context:annotation-config /> [...] <bean id="myBackend" class="mycompany.BackendBean" scope="singleton" /> 当我运行服务器(mvnjetty:run)时,我可以在控制台中看到init方法的输出,从中我得出结论,执行了init方法 当我按下Ctrl-C并且Jetty开始关闭时,我看不到destroy
beans.xml
中定义了一个Springbean,如下所示:
<context:annotation-config />
[...]
<bean id="myBackend" class="mycompany.BackendBean" scope="singleton" />
当我运行服务器(mvnjetty:run
)时,我可以在控制台中看到init
方法的输出,从中我得出结论,执行了init
方法
当我按下Ctrl-C
并且Jetty开始关闭时,我看不到destroy
方法的输出
当应用程序终止时,为了执行
destroy
方法,我应该更改什么?我不知道您为什么希望Spring
处理这个问题。除非我误解了您的问题,否则您可以使用容器应用程序生命周期
尝试在
lifecycle
onStart
和onStop
中编写and和override。当适当的情况发生时,在tomcat中为LifeCycleListener
使用类似的解决方案。要让Spring在应用程序关闭时调用@PreDestroy
回调方法,必须添加一个关闭钩子并关闭其中的应用程序上下文。您可以使用Runtime.getRuntime().addShutdownHook(Thread)
将钩子连接到JVM,如果Jetty提供这样的API,则可以将钩子连接到Jetty。下面是如何使用JVM关闭挂钩:
final ApplicationContext appContext = ... // create your application context
// using one of the various application context classes
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
appContext.close();
}});
当您在类中使用
@Scope(“prototype”)
时,即使尝试使用context.close()关闭,@PreDestroy也无法工作
或context.registerShutdownHook()代码>对于“原型”范围的bean,这里有一点需要注意
对于“原型”范围的bean,Spring不调用@PreDestroy方法
以下是Spring官方参考手册的答案:
第1.5.2节()
与其他作用域不同,Spring不管理完整的
原型bean的生命周期**:容器实例化,
配置或组装原型对象,并将其交给
给客户端,没有该原型实例的进一步记录
因此,尽管初始化生命周期回调方法是在
所有对象,无论范围如何,对于原型,
配置的销毁生命周期回调不被调用。这个
客户机代码必须清理原型范围的对象并发布
原型bean所拥有的昂贵资源
让Spring容器释放
原型范围的bean,尝试使用自定义bean后处理器,,它
保存对需要清理的bean的引用
注意:这也适用于XML配置。旧问题,但解决方案是创建一个类来处理预销毁:
@Component
public class SetBeanProcessor implements BeanPostProcessor, BeanFactoryAware, DisposableBean {
private BeanFactory beanFactory;
private final List<Object> prototypeBeans = new LinkedList<>();
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanFactory.isPrototype(beanName)) {
synchronized (prototypeBeans) {
prototypeBeans.add(bean);
}
}
return bean;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void destroy() throws Exception {
// note : if we have several instance of a prototype (each instance is distinct) the method will be called several times ...
synchronized (prototypeBeans) {
for (Object bean : prototypeBeans) {
if (bean instanceof DisposableBean) {
DisposableBean disposable = (DisposableBean)bean;
try {
disposable.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
}
prototypeBeans.clear();
}
}
您可以使用方法引用“close”在Spring中执行@PreDestroy方法:
Runtime.getRuntime().addShutdownHook(new Thread(applicationContext::close));
一个老问题,但我想分享我发现的一些东西
我有一段类似的代码,最初认为@PreDestroy方法没有被调用。但是后来我在LOGGER info()中添加了一个print语句,并惊讶地看到打印被执行了。显然,这是因为logback甚至在调用@PreDestroy方法之前就自动关闭了
以下来源可能很有用:。您是否计划在jetty上运行该应用程序?或者你需要在更多的容器中运行它?我使用Jetty只是为了快速测试。在生产环境中,我使用的是ApacheTomcat7。为此,我们提供了appContext.RegisterShotDownhook()
。谢谢。我应该将关闭钩子注册放在哪里(在哪个方法中)?@Dmitrisarenko最好的地方是初始化spring应用程序上下文的地方。@Vitaly需要详细说明RegisterSuttonHook()是AbstractApplicationContext的方法,而不是ApplicationContext的方法。如果重新部署应用程序,java应用程序不会关闭,因此不会被调用。因此,如果您正在使用spring boot或禁用了重新部署,请使用此选项。如果您这样做,将导致关闭代码和容器之间存在依赖关系。最好避免这种情况,并保持对Spring的单一依赖,这种依赖已经存在(或者最好删除对Spring的任何依赖,并使用javax.inject注释)。
@Component
public class myClass implements SomeClassName, DisposableBean {
...
@Override
public void destroy() throws Exception {
System.out.println("# myClass: destroy() method called");
}
}
Runtime.getRuntime().addShutdownHook(new Thread(applicationContext::close));