我在beans.xml
中定义了一个Spring bean,如下所示:
<context:annotation-config />
[...]
<bean id="myBackend" class="mycompany.BackendBean" scope="singleton" />
bean内部有两个方法,必须在Web应用程序的开始和结束之前执行:
public class BackendBean implements IBackend {
private static final Logger LOGGER = LoggerFactory
.getLogger(BackendBean.class);
@PostConstruct
public void init()
{
LOGGER.debug("init");
}
@PreDestroy
public void destroy()
{
LOGGER.debug("destroy");
}
}
当我运行服务器(mvn jetty:run
)时,我可以在控制台中看到init
方法的输出,从中我得出结论init
方法已执行。
当我按Ctrl-C
并且Jetty开始关闭时,我看不到destroy
方法的输出。
当应用程序终止时,为了执行destroy
方法,我应该更改什么?
答案 0 :(得分:12)
对于Spring在应用程序关闭时调用@PreDestroy
回调方法,您必须添加一个关闭钩子并关闭它所在的应用程序上下文。您可以使用Runtime.getRuntime().addShutdownHook(Thread)
将挂钩附加到JVM或者Jetty如果提供这样的API。以下是使用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();
}});
答案 1 :(得分:2)
一个老问题,但想分享我发现的东西。
我有一段类似的代码,最初认为 @PreDestroy 方法没有被调用。但是后来我添加了一个打印语句和 LOGGER info() 并且惊讶地看到打印被执行了。显然这是因为 logback 甚至在调用 @PreDestroy 方法之前就关闭了。
以下来源可能有用:https://github.com/spring-projects/spring-framework/issues/24431。
答案 2 :(得分:1)
在课堂上使用@Scope("prototype")
时,即使尝试使用context.close();
或context.registerShutdownHook();
来关闭,@ PreDestroy也无法正常工作
答案 3 :(得分:0)
我不知道你为什么要Spring
来照顾这个。除非我误解了您的问题,否则您可以使用容器应用程序生命周期。
尝试撰写LifeCycle (jetty)和LifeCycleListener (tomcat)并覆盖LifeCyle
onStart
和onStop
。在适当的event发生时,在tomcat中为LifeCycleListener
开发类似的解决方案。
答案 4 :(得分:0)
Here is a subtle point you need to be aware of with "prototype" scoped beans.
For "prototype" scoped beans, Spring does not call the @PreDestroy method.
Here is the answer from the Spring reference manual. Section 1.5.2
与其他作用域相比,Spring不能管理一个作用域的完整生命周期 原型bean :容器实例化,配置并以其他方式组装 原型对象,然后将其交给客户端,没有该原型的进一步记录 实例。
因此,尽管在所有对象上都调用了初始化生命周期回调方法,而不管作用域如何,但在原型的情况下,未调用已配置的销毁生命周期回调。客户端代码必须清除原型作用域的对象并释放原型Bean所拥有的昂贵资源。
要使Spring容器释放由原型作用域的bean占用的资源,请尝试使用自定义bean后处理器,该处理器包含对需要清理的bean的引用。
注意:这也适用于XML配置。
答案 5 :(得分:0)
古老的问题,但解决方案是创建一个处理PreDestroy的类:
@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();
}
}
使用此类的类应通过以下方式实现:
@Component
public class myClass implements SomeClassName, DisposableBean {
...
@Override
public void destroy() throws Exception {
System.out.println("# myClass: destroy() method called");
}
}
答案 6 :(得分:0)
您可以使用方法引用“close”在 Spring 中执行 @PreDestroy 方法:
Runtime.getRuntime().addShutdownHook(new Thread(applicationContext::close));