Análisis del Ciclo de Vida de los Beans en Spring con JDK 17

Este artículo explora el ciclo de vida de los beans en Spring Framwork utilizando JDK 17, enfocándose en la implementación a nivel de código fuente. Se describen las fases clave: instanciación, inyección de propiedades, inicialización y destrucción.

  1. Flujo General

El ciclo de vida de un bean en Spring se divide en cuatro etapas principales:

  • Instanciación: Creación del objeto bean.
  • Inyección de Propiedades: Asignación de valores y dependencias.
  • Inicialización: Ejecución de métodos de inicialización y configuración.
  • Destrucción: Liberación de recursos al cerrar el contenedor.
  1. Método createBeanInstance

Este método es responsable de crear la instancia del bean. Se siguen varias estrategias según la definición del bean:

  • Uso de un proveedor (supplier) para la creación.
  • Empleo de un método de fábrica.
  • Reflexión basada en constructores, ya sea el constructor predeterminado o uno inyectado.

El código define constantes para los modos de autoensamblado:

private int modoInyeccion;
public static final int SIN_INYECCION = 0;
public static final int POR_NOMBRE = 1;
public static final int POR_TIPO = 2;
public static final int POR_CONSTRUCTOR = 3;

La lógica para resolver el constructor se basa en cachés y condiciones:

boolean resuelto = false;
boolean requiereInyeccion = false;
if (argumentos == null) {
    synchronized (definicionBean.bloqueArgumentosConstructor) {
        if (definicionBean.metodoConstructoroFabricaResuelto != null) {
            resuelto = true;
            requiereInyeccion = definicionBean.argumentosConstructorResueltos;
        }
    }
}

Si el constructor ya está en caché, se decide entre instanciación con o sin inyección:

if (resuelto) {
    return requiereInyeccion ?
        this.autoensambladoPorConstructor(nombreBean, definicionBean, null, null) :
        this.instanciarBean(nombreBean, definicionBean);
} else {
    Constructor>[] constructores = this.determinarConstructoresDesdePostProcesadores(claseBean, nombreBean);
    if (constructores == null && definicionBean.getModoAutoensambladoResuelto() != 3 &&
        !definicionBean.tieneValoresArgumentosConstructor() && ObjectUtils.estaVacio(argumentos)) {
        constructores = definicionBean.getConstructoresPreferidos();
        return constructores != null ?
            this.autoensambladoPorConstructor(nombreBean, definicionBean, constructores, null) :
            this.instanciarBean(nombreBean, definicionBean);
    } else {
        return this.autoensambladoPorConstructor(nombreBean, definicionBean, constructores, argumentos);
    }
}

  1. Método populateBean

Este método gestiona la inyección de propiedades en el bean. Primero, verifica si el envoltorio del bean (BeanWrapper) es válido y si existen valores de propiedad definidos.

protected void poblarBean(String nombreBean, RootBeanDefinition definicionBean, @Nullable BeanWrapper envoltorio) {
    if (envoltorio == null) {
        if (definicionBean.tieneValoresPropiedad()) {
            throw new BeanCreationException(definicionBean.getDescripcionRecurso(), nombreBean,
                "No se pueden aplicar valores de propiedad a una instancia nula");
        }
    } else {
        // Aplicar postprocesadores antes de la inyección
        if (!definicionBean.esSintetico() && this.tienePostProcesadoresDeInstancia()) {
            Iterator> iterador = this.getCachePostProcesadores().instantaneos.iterator();
            while (iterador.hasNext()) {
                InstantiationAwarePostProcesador bp = (InstantiationAwarePostProcesador) iterador.next();
                if (!bp.postProcesarDespuesInstanciacion(envoltorio.getInstanciaEnvuelta(), nombreBean)) {
                    return;
                }
            }
        }
        PropertyValues propiedades = definicionBean.tieneValoresPropiedad() ? definicionBean.getValoresPropiedad() : null;
        int modoAutoensamblado = definicionBean.getModoAutoensambladoResuelto();
        if (modoAutoensamblado == 1 || modoAutoensamblado == 2) {
            MutablePropertyValues nuevasPropiedades = new MutablePropertyValues(propiedades);
            if (modoAutoensamblado == 1) {
                this.autoensambladoPorNombre(nombreBean, definicionBean, envoltorio, nuevasPropiedades);
            }
            if (modoAutoensamblado == 2) {
                this.autoensambladoPorTipo(nombreBean, definicionBean, envoltorio, nuevasPropiedades);
            }
            propiedades = nuevasPropiedades;
        }
        boolean tienePostProcesadores = this.tienePostProcesadoresDeInstancia();
        boolean requiereVerificacion = definicionBean.getVerificacionDependencia() != 0;
        PropertyDescriptor[] descriptoresFiltrados = null;
        if (tienePostProcesadores) {
            if (propiedades == null) {
                propiedades = definicionBean.getValoresPropiedad();
            }
            PropertyValues propiedadesParaUsar;
            for (Iterator> iterador = this.getCachePostProcesadores().instantaneos.iterator(); iterador.hasNext();
                 propiedades = propiedadesParaUsar) {
                InstantiationAwarePostProcesador bp = (InstantiationAwarePostProcesador) iterador.next();
                propiedadesParaUsar = bp.postProcesarPropiedades(propiedades, envoltorio.getInstanciaEnvuelta(), nombreBean);
                if (propiedadesParaUsar == null) {
                    if (descriptoresFiltrados == null) {
                        descriptoresFiltrados = this.filtrarDescriptoresParaVerificacion(envoltorio, definicionBean.permiteCache);
                    }
                    propiedadesParaUsar = bp.postProcesarValoresPropiedad(propiedades, descriptoresFiltrados, envoltorio.getInstanciaEnvuelta(), nombreBean);
                    if (propiedadesParaUsar == null) {
                        return;
                    }
                }
            }
        }
        if (requiereVerificacion) {
            if (descriptoresFiltrados == null) {
                descriptoresFiltrados = this.filtrarDescriptoresParaVerificacion(envoltorio, definicionBean.permiteCache);
            }
            this.verificarDependencias(nombreBean, definicionBean, descriptoresFiltrados, propiedades);
        }
        if (propiedades != null) {
            this.aplicarValoresPropiedad(nombreBean, definicionBean, envoltorio, propiedades);
        }
    }
}

  1. Método initializeBean

Este método ejecuta la inicialización del bean, incluyendo la invocación de métodos Aware y postprocesadores.

protected Object inicializarBean(String nombreBean, Object bean, @Nullable RootBeanDefinition definicionBean) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged(() -> {
            this.invocarMetodosAware(nombreBean, bean);
            return null;
        }, this.getContextoControlAcceso());
    } else {
        this.invocarMetodosAware(nombreBean, bean);
    }
    Object beanEnvuelto = bean;
    if (definicionBean == null || !definicionBean.esSintetico()) {
        beanEnvuelto = this.aplicarPostProcesadoresAntesInicializacion(bean, nombreBean);
    }
    try {
        this.invocarMetodosInicializacion(nombreBean, beanEnvuelto, definicionBean);
    } catch (Throwable ex) {
        throw new BeanCreationException(definicionBean != null ? definicionBean.getDescripcionRecurso() : null,
            nombreBean, "Fallo en la invocación del método de inicialización", ex);
    }
    if (definicionBean == null || !definicionBean.esSintetico()) {
        beanEnvuelto = this.aplicarPostProcesadoresDespuesInicializacion(beanEnvuelto, nombreBean);
    }
    return beanEnvuelto;
}

La invocación de métodos Aware se detalla a continuación:

private void invocarMetodosAware(String nombreBean, Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(nombreBean);
        }
        if (bean instanceof BeanClassLoaderAware) {
            ClassLoader cargador = this.getCargadorClases();
            if (cargador != null) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(cargador);
            }
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }
    }
}

Los postprocesadores antes de la inicialización se aplican de manera iterativa:

public Object aplicarPostProcesadoresAntesInicializacion(Object beanExistente, String nombreBean) throws BeansException {
    Object resultado = beanExistente;
    Object actual;
    for (Iterator> iterador = this.getPostProcesadores().iterator(); iterador.hasNext(); resultado = actual) {
        PostProcesadorBean procesador = (PostProcesadorBean) iterador.next();
        actual = procesador.postProcesarAntesInicializacion(resultado, nombreBean);
        if (actual == null) {
            return resultado;
        }
    }
    return resultado;
}

La invocación de métodos de inicialización incluye la interfaz InitializingBean y métodos init definidos:

protected void invocarMetodosInicializacion(String nombreBean, Object bean, @Nullable RootBeanDefinition definicionBean) throws Throwable {
    boolean esInicializable = bean instanceof InitializingBean;
    if (esInicializable && (definicionBean == null || !definicionBean.tieneMetodoInitGestionadoExternamente("afterPropertiesSet"))) {
        if (this.registrador.isTraceEnabled()) {
            this.registrador.trace("Invocando afterPropertiesSet() en bean con nombre '" + nombreBean + "'");
        }
        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged(() -> {
                    ((InitializingBean) bean).afterPropertiesSet();
                    return null;
                }, this.getContextoControlAcceso());
            } catch (PrivilegedActionException ex) {
                throw ex.getException();
            }
        } else {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }
    if (definicionBean != null && bean.getClass() != NullBean.class) {
        String nombreMetodoInit = definicionBean.getNombreMetodoInit();
        if (StringUtils.tieneLongitud(nombreMetodoInit) && (!esInicializable || !"afterPropertiesSet".equals(nombreMetodoInit)) &&
            !definicionBean.tieneMetodoInitGestionadoExternamente(nombreMetodoInit)) {
            this.invocarMetodoInitPersonalizado(nombreBean, bean, definicionBean);
        }
    }
}

  1. Proceso de Destrucción

El registro de beans para destrucción se gestiona mediante el método registerDisposableBeanIfNecessary, que vincula un adaptador de destrucción al bean según su alcance.

protected void registrarBeanDesechableSiNecesario(String nombreBean, Object bean, RootBeanDefinition definicionBean) {
    AccessControlContext contexto = System.getSecurityManager() != null ? this.getContextoControlAcceso() : null;
    if (!definicionBean.esPrototipo() && this.requiereDestruccion(bean, definicionBean)) {
        if (definicionBean.esSingleton()) {
            this.registrarBeanDesechable(nombreBean, new AdaptadorBeanDesechable(bean, nombreBean, definicionBean,
                this.getCachePostProcesadores().destructivos, contexto));
        } else {
            Alcance alcance = (Alcance) this.alcances.get(definicionBean.getAlcance());
            if (alcance == null) {
                throw new IllegalStateException("No hay alcance registrado para el nombre '" + definicionBean.getAlcance() + "'");
            }
            alcance.registrarCallbackDestruccion(nombreBean, new AdaptadorBeanDesechable(bean, nombreBean, definicionBean,
                this.getCachePostProcesadores().destructivos, contexto));
        }
    }
}

Los beans desechables se almacenan en un mapa sincronizado para su posterior invocación durante el cierre del contenedor.

  1. Resumen Técnico

  • Instanciación: Mediante constructores, métodos de fábrica o proveedores, con lógica de caché y autoensamblado.
  • Inyección de Propiedades: Proceso guiado por postprocesadroes y modos de autoensamblado (por nombre o tipo).
  • Inicialización: Incluye callbacks Aware, postprocesadores antes/después, e invocación de métodos de inicialización.
  • Destrucción: Registro de beans desechables para ejecutar métodos de limpieza al apagar el contenedor.

Etiquetas: spring-framework bean-lifecycle java-17 dependency-injection spring-ioc

Publicado el 6-14 18:30