Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In a spring bean is it possible to have a shutdown method which can use transactions?

In the destroy method of a spring bean I want to execute some queries to clean up some stuff in the database. Spring doesn't seem to allow this by any means I can find.

The error is always something like:

Invocation of destroy method failed on bean with name 'someBean': org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'transactionManager': Singleton bean creation not allowed while the singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)

The following will tell spring to call shutdownDestroy after the bean is no longer needed. But, I get the above error when trying to use transactions.

<bean id="someId" name="someName" class="someClass"
 destroy-method="shutdownDestroy"/>

The same is true when I enable common lifecycle annotations using:

<bean class="org.springframework. ... .CommonAnnotationBeanPostProcessor"/>

and then mark the method with @PreDestroy. That method can't use transactions either.

Is there any way to do this?

EDIT: Thanks! I had the bean implement SmartLifecycle and adding the following and it works very nicely.

private boolean isRunning = false;

@Override
public boolean isAutoStartup() {return true;}

@Override
public boolean isRunning() {return isRunning;}

/** Run as early as possible so the shutdown method can still use transactions. */
@Override
public int getPhase() {return Integer.MIN_VALUE;}

@Override
public void start() {isRunning = true;}

@Override
public void stop(Runnable callback) {
    shutdownDestroy();
    isRunning = false;
    callback.run();
}

@Override
public void stop() {
    shutdownDestroy();
    isRunning = false;
}
like image 476
HappyEngineer Avatar asked May 04 '11 23:05

HappyEngineer


People also ask

What is @bean destroyMethod close?

destroyMethod. The optional name of a method to call on the bean instance upon closing the application context, for example a close() method on a JDBC DataSource implementation, or a Hibernate SessionFactory object.

How does Spring generate bean names for classes annotated with @component that do not specify a name?

How does Spring generate bean names for classes annotated with @Component that do not specify a name? It uses the short name of the class with the first letter in lowercase.

How can the bean life cycle be controlled?

Spring bean life cycle can be controlled in the following ways. Instantiation by using: InitializingBean callback interface. Custom init() method from the bean configuration file.

What are beans in the concept of Spring or what are Spring beans?

A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.


2 Answers

Interesting Question. I'd say you should be able to do it by letting your bean implement SmartLifeCycle.

That way, if your int getPhase(); method returns Integer.MAX_VALUE, it will be among the first to be called when the ApplicationContext finally shuts down.

Reference:

like image 175
Sean Patrick Floyd Avatar answered Oct 10 '22 18:10

Sean Patrick Floyd


I come across this same issue. After check spring's source code, U can try to implements

public class SomeBean implements ApplicationListener<ContextClosedEvent> {
    public void onApplicationEvent(ContextClosedEvent event) {
        stopHook();
    }
}

onApplicationEvent will be call before bean destory, you can check it on spring's org.springframework.context.support.AbstractApplicationContext#doClose method. I paste it below, so ContextEvent -> LifeCycle -> Bean destory.

        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();
like image 2
NewBee Avatar answered Oct 10 '22 18:10

NewBee