Para interactuar con el mecanismo de gestión del ciclo de vida del bean contenedor, puede implementar las interfaces InitializingBean y DisposableBean en Spring. El contenedor llama a afterPropertiesSet() en el primero y a destroy() en el segundo para que el bean pueda realizar ciertas acciones al inicializar y destruir sus beans.

Las anotaciones @PostConstruct y @PreDestroy estandarizadas por JSR-250 generalmente se consideran la mejor práctica. para obtener devoluciones de llamadas del ciclo de vida en una aplicación Spring moderna. El uso de estas anotaciones significa que sus beans no están vinculados a interfaces específicas de Spring.

Si no desea utilizar anotaciones JSR-250 pero aun así desea eliminar el acoplamiento, considere el bean init-method y destroy-method. metadatos de definición.

Internamente, Spring Framework utiliza implementaciones BeanPostProcessor para manejar cualquier interfaz de devolución de llamada que pueda encontrar y llamar a los métodos apropiados. Si necesita funciones especiales u otra lógica de ciclo de vida que Spring no ofrece de forma predeterminada, puede implementar BeanPostProcessor usted mismo.

Además de las devoluciones de llamadas de inicialización y destrucción, los objetos administrados por Spring También puede implementar la interfaz Lifecycle para que estos objetos puedan participar en el proceso de inicio y terminación definido por el propio ciclo de vida del contenedor.

Las interfaces de devolución de llamada del ciclo de vida se describen en esta sección.

Devoluciones de llamada de inicialización

La interfaz org.springframework.beans.factory.InitializingBean permite que el bean realice la inicialización después de que el contenedor haya establecido todas las propiedades requeridas del bean. La interfaz InitializingBean define un único método:

void afterPropertiesSet() throws Exception;

No recomendamos utilizar InitializingBean La interfaz, ya que es redundante, vincula el código a Spring. Como alternativa, sugerimos utilizar la anotación @PostConstruct o especificar el método de inicialización de POJO. Para los metadatos de configuración basados en XML, puede utilizar el atributo init-method para especificar el nombre de un método que devuelve void y no tiene argumentos. En la configuración de Java, puede utilizar el atributo initMethod en @Bean. Considere el siguiente ejemplo:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
Java

public class ExampleBean {
    public void init() {
        // do some initialization
    }
Kotlin

class ExampleBean {
    fun init() {
        // do some initialization
    }
}

El ejemplo anterior produce casi exactamente el mismo resultado que el siguiente ejemplo (que consta de dos listados):

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
Java

public class AnotherExampleBean implements InitializingBean {
    @Override
    public void afterPropertiesSet() {
        // do some initialization
    }
}
Kotlin

class AnotherExampleBean : InitializingBean {
    override fun afterPropertiesSet() {
        // do some initialization
    }
}

Sin embargo, en el primero de los dos anteriores Por ejemplo, el código no está asociado con Spring.

Devoluciones de llamada de destrucción

La implementación de la interfaz org.springframework.beans.factory.DisposableBean permite que un bean reciba una devolución de llamada cuando se destruye el contenedor que lo contiene. La interfaz DisposableBean define un único método:

void destroy() throws Exception;

No recomendamos usar DisposableBean interfaz de devolución de llamada porque acopla innecesariamente el código a Spring. Como alternativa, sugerimos utilizar la anotación @PreDestroy o especificar un método genérico que sea compatible con las definiciones de beans. Cuando utilice metadatos de configuración basados en XML, puede utilizar el atributo destroy-method en <bean/>. En la configuración de Java, puede utilizar el atributo destroyMethod en @Bean. Considere la siguiente definición:

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
Java

public class ExampleBean {
    public void cleanup() {
        // do some cleanup work (e.g. freeing connection pools)
    }
}
Kotlin

class ExampleBean {
    fun cleanup() {
        // do some cleanup work (e.g. free up connection pools)
    }
}

La definición anterior produce casi exactamente el mismo resultado que el siguiente:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
Java

public class AnotherExampleBean implements DisposableBean {
    @Override
    public void destroy() {
        // do some destruction work (e.g. freeing connection pools)
    }
}
Kotlin

class AnotherExampleBean : DisposableBean {
    override fun destroy() {
        // do some destruction work (e.g. freeing connection pools)
    }
}

Sin embargo, la primera de las dos definiciones anteriores no asocia el código con Spring.

Tú puedes establezca el atributo destroy-method del elemento <bean> en un valor especial (inferido) que indica a Spring que detecte automáticamente el public method close o shutdown para una clase de bean específica. (Entonces, cualquier clase que implemente java.lang.AutoCloseable o java.io.Closeable servirá). También puede establecer este valor especial (inferido) en el atributo default-destroy-method del elemento <beans> para aplicar esta lógica. a todo el conjunto de frijoles. Tenga en cuenta que esta es la lógica predeterminada cuando se utiliza una configuración de Java.

Métodos de inicialización y destrucción predeterminados

Si está escribiendo devoluciones de llamada para métodos de inicialización y destrucción que no utilizan los específicos de Spring interfaces de devolución de llamada InitializingBean y DisposableBean, entonces normalmente escribe métodos con nombres como init(), initialize(), dispose() y así sucesivamente. Lo ideal es que los nombres de dichos métodos de devolución de llamadas del ciclo de vida estén estandarizados en todo el proyecto para que todos los desarrolladores utilicen los mismos nombres de métodos, lo que garantiza la coherencia.

Puede configurar el contenedor Spring para "buscar" los nombres de la inicialización. y los métodos de devolución de llamada llaman a la destrucción para cada bean, si se les asignan dichos nombres. Esto significa que usted, como desarrollador de aplicaciones, puede escribir las clases de su aplicación y utilizar la devolución de llamada de inicialización llamada init() sin tener que configurar el init-method="init". atributo para cada definición de frijol. El contenedor Spring IoC llama a este método cuando se crea el bean (y de acuerdo con el contrato de devolución de llamada del ciclo de vida estándar). Esta función también proporciona una convención de nomenclatura coherente para las devoluciones de llamada de inicialización y destrucción.

Supongamos que sus métodos de devolución de llamada de inicialización se denominan init() y sus métodos de devolución de llamada de destrucción son destroy(). Entonces su clase será similar a la clase del siguiente ejemplo:

Java

public class DefaultBlogService implements BlogService {
    private BlogDao blogDao;
    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }
    // this is (unsurprisingly) the initialization callback method
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}
Kotlin

class DefaultBlogService : BlogService {
    private var blogDao: BlogDao? = null
    // this is (unsurprisingly) the initialization callback method
    fun init() {
        if (blogDao == null) {
            throw IllegalStateException("The [blogDao] property must be set.")
        }
    }
}

Luego puede usar esta clase en beans similares a los siguientes:


<beans default-init-method="init">
    <bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>
</beans>

La presencia del atributo default-init-method en el atributo del elemento de nivel superior <beans/> hace que el contenedor Spring IoC reconozca el método init de la clase bean como una devolución de llamada del método de inicialización. Cuando se crea y vincula un bean, si la clase de bean contiene dicho método, entonces se invoca en el momento apropiado.

Puedes configurar las devoluciones de llamada del método de destrucción de una manera similar (es decir, en XML), usando el default attribute -destroy-method en el elemento <beans/> de nivel superior.

En los casos en los que las clases de beans existentes ya contienen devolución de llamada métodos que no se nombran en Por convención, puede anular el valor predeterminado especificando (es decir, XML) el nombre del método utilizando los atributos init-method y destroy-method de el <bean/>.

El contenedor Spring garantiza que la devolución de llamada de inicialización configurada se llame inmediatamente después de que el bean haya recibido todas las dependencias. Entonces, la devolución de llamada de inicialización se llama para hacer referencia al frijol sin procesar, lo que significa ganchos AOP, etc. aún no se han aplicado al frijol. Primero, se crea todo el bean objetivo y luego se aplica un proxy AOP (por ejemplo) con su cadena de interceptores. Si el bean de destino y el proxy se definen por separado, su código puede incluso interactuar con el bean de destino sin formato sin pasar por el proxy. Por lo tanto, sería inconsistente aplicar interceptores al método init, ya que entonces el ciclo de vida del bean objetivo estaría vinculado a sus proxies o interceptores, lo que resultaría en una semántica extraña cuando su código interactúa directamente con el bean objetivo sin formato. .

Combinación de mecanismos del ciclo de vida

A partir de Spring 2.5, hay tres opciones para administrar la lógica del ciclo de vida del bean:

  • Devoluciones de llamadas de interfaces InitializingBean y DisposableBean

  • Métodos especiales init() y destroy()

  • Anotaciones @PostConstruct y @PreDestroy. Puede combinar estos mecanismos para controlar un bean específico.

Si se configuran múltiples mecanismos de ciclo de vida para un bean, y para cada Si el mecanismo está configurado con su propio nombre de método, entonces cada método configurado se ejecuta en el orden especificado después de esta nota. Sin embargo, si se configura el mismo nombre de método (por ejemplo, init() para el método de inicialización) para más de uno de estos mecanismos de ciclo de vida, ese método se ejecuta una vez, como se explica en la sección anterior.

Múltiples mecanismos de ciclo de vida configurados para el mismo bean, con diferentes métodos de inicialización, se invocan de la siguiente manera:

  1. Métodos anotados con @PostConstruct

  2. afterPropertiesSet(), según lo definido por la interfaz de devolución de llamada InitializingBean

  3. Método especialmente configurado init()

Los métodos de destrucción se llaman en el mismo orden:

  1. Métodos anotados con @PreDestroy

  2. destroy(), según lo definido por la interfaz de devolución de llamada DesechableBean

  3. Método configurado especial destroy()

Inicio y terminación devoluciones de llamada

La interfaz Lifecycle define métodos básicos para cualquier objeto que tenga sus propios requisitos de ciclo de vida (por ejemplo, iniciar y detener algún proceso en segundo plano):


public interface Lifecycle {
    void start();
    void stop();
    boolean isRunning();
}

Cualquier objeto administrado por Spring puede implementar la interfaz Lifecycle. Luego, cuando el propio ApplicationContext recibe señales de inicio y detención (por ejemplo, para un script de detención/reinicio en tiempo de ejecución), pone en cascada esas llamadas en todas las implementaciones de Lifecycle definidas en ese contexto. . Para ello, delegue en LifecycleProcessor, como se muestra en el siguiente listado:


public interface LifecycleProcessor extends Lifecycle {
    void onRefresh();
    void onClose();
}

Tenga en cuenta que LifecycleProcessor es en sí mismo una extensión de la interfaz Lifecycle. También agrega otros dos métodos para responder a la actualización y cierre del contexto.

Tenga en cuenta que el familiar interfaz org.springframework.context.Lifecycle es un contrato común para notificaciones explícitas de inicio y detención y no se inicia automáticamente cuando se actualiza el contexto. Para un control más preciso sobre el inicio automático de un bean específico (incluidas las fases de inicio), considere implementar org.springframework.context.SmartLifecycle.

Tenga en cuenta también que las notificaciones de detención no están garantizadas antes de la destrucción. Durante un apagado normal, todos los beans Lifecycle reciben primero una notificación del apagado antes de que se propaguen las devoluciones de llamada de eliminación generales. Sin embargo, en el caso de una actualización en caliente, durante la vida útil del contexto o cuando se detienen los intentos de actualización, solo se llaman los métodos de destrucción.

El orden de las llamadas de inicio y apagado puede ser importante. Si existe una relación de dependencia entre dos objetos cualesquiera, entonces la parte dependiente comienza después de su dependencia y se detiene antes de su dependencia. Sin embargo, en ocasiones se desconocen las relaciones directas. Todo lo que puedes saber es que los objetos de un determinado tipo deben ejecutarse antes que los objetos de otro tipo. En estos casos, la interfaz SmartLifecycle define una opción diferente, concretamente el método getPhase() definido en su superinterfaz Phased. La siguiente lista muestra la definición de la interfaz Phased:


public interface Phased {
    int getPhase();
}

La siguiente lista muestra la definición de la interfaz SmartLifecycle:


public interface SmartLifecycle extends Lifecycle, Phased {
    boolean isAutoStartup();
    void stop(Runnable callback);
}

Al iniciar, los objetos con la fase más pequeña se lanzan primero. Al detenerse, proceda en orden inverso. Por lo tanto, un objeto que implemente SmartLifecycle y cuyo método getPhase() devuelva Integer.MIN_VALUE será uno de los primeros en ejecutarse y el último. correr ¿quién se detendrá? En el otro extremo del espectro, el valor de fase Integer.MAX_VALUE indica que el objeto debe iniciarse en último lugar y detenerse primero (probablemente porque depende de otros procesos en ejecución). Al considerar el valor de fase, también es importante saber que la fase predeterminada para cualquier objeto Lifecycle "normal" que no implemente SmartLifecycle es 0. Por lo tanto, cualquier valor de fase negativo indica que el objeto debe comenzar antes (y detenerse después) de estos componentes estándar. Lo contrario es cierto para cualquier valor de fase positivo.

El método de detención definido por SmartLifecycle acepta una devolución de llamada. Cualquier implementación debe iniciar el método run() de esta devolución de llamada después de que se haya completado el proceso de cierre de esa implementación. Esto garantiza un apagado asíncrono cuando sea necesario porque la implementación predeterminada de la interfaz LifecycleProcessor, DefaultLifecycleProcessor, espera un tiempo de espera de valor especificado hasta que un grupo de objetos en cada fase inicia esta devolución de llamada. El tiempo de espera predeterminado para cada fase es de 30 segundos. Puede anular la instancia predeterminada del procesador de ciclo de vida definiendo un bean llamado lifecycleProcessor en el contexto. Si solo necesita cambiar el tiempo de espera, simplemente defina lo siguiente:


<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- timeout value in milliseconds -->
    <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

Como se mencionó anteriormente, la interfaz LifecycleProcessor define métodos de devolución de llamada para actualizar y cerrar el contexto. Este último controla el proceso de apagado como si se hubiera llamado explícitamente a stop(), pero esto ocurre cuando el contexto está cerrado. La devolución de llamada de actualización, por otro lado, le permite aprovechar otra característica de los beans SmartLifecycle. Si el contexto se actualiza (después de que se hayan creado e inicializado todas las instancias de objetos), se activa esta devolución de llamada. En este punto, el procesador de ciclo de vida predeterminado verifica el valor booleano devuelto por el método isAutoStartup() de cada objeto SmartLifecycle. Si true, entonces este objeto comienza en ese momento, en lugar de esperar una llamada explícita al método start() del contexto o a su propio método (a diferencia de actualizar el contexto, iniciar el contexto no ocurre automáticamente en la implementación estándar del contexto). El valor de fase y cualquier relación "dependiente" determinan el orden de inicio, como se describió anteriormente.

Cerrar gradualmente el contenedor Spring IoC en aplicaciones que no son web

Esta sección se aplica únicamente a aplicaciones que no son web. Las implementaciones de ApplicationContext para aplicaciones web en el marco Spring contienen código para cerrar correctamente el contenedor Spring IoC cuando sale la aplicación web correspondiente.

Si está utilizando IoC- Contenedor Spring en un entorno de aplicación que no sea web (por ejemplo, un entorno de cliente de escritorio enriquecido), registre un enlace de apagado con la JVM. De esta manera, puede asegurarse de que sus beans singleton se cierren correctamente y se invoquen los métodos de destrucción adecuados para liberar todos los recursos. Aún necesita configurar e implementar correctamente estas devoluciones de llamada de eliminación.

Para registrar un enlace de apagado, llame al método registerShutdownHook(), que se declara en el ConfigurableApplicationContext. Interfaz, como se muestra en el siguiente ejemplo:

Java

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
    public static void main(final String[] args) throws Exception {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        // add a shutdown hook for the above context...
        ctx.registerShutdownHook();
        // the application starts...
        // the main method ends, the interceptor is called before the application exits...
    }
}
Kotlin

import org.springframework.context.support.ClassPathXmlApplicationContext
fun main() {
        val ctx = ClassPathXmlApplicationContext("beans.xml")
        // add a shutdown hook for the above context...
        ctx.registerShutdownHook()
        // application runs...
        // the main method ends, the interceptor is called before the application exits...
}