Punto de extensión Springboot BeanPostProcessor

Colección de métodos de implementación y principios de funcionamiento de la serie de puntos de extensión Springboot:

Punto de extensión Springboot ApplicationContextInitializer

Punto de extensión Springboot BeanFactoryPostProcessor

Punto de extensión Springboot BeanDefinitionRegistryPostProcessor

Punto de extensión Springboot BeanPostProcessor

Punto de extensión Springboot InstantiationAwareBeanPostProcessor

Punto de extensión Springboot SmartInstantiationAwareBeanPostProcessor

Punto de extensión Springboot ApplicationContextAwareProcessor

Punto de extensión Springboot @PostConstruct

Punto de extensión Springboot InicializandoBean

Punto de extensión Springboot SmartInitializingSingleton

Punto de extensión Springboot CommandLineRunner y ApplicationRunner

Punto de extensión Springboot FactoryBean

Punto de extensión Springboot DesechableBean

El final de la serie de puntos de extensión Springboot: ciclo de vida del Bean

Prefacio

En realidad, hay muchos puntos de extensión de Springboot (Spring), pero todos tienen una cosa en común: todos giran en torno a Bean y BeanFactory (contenedor). De hecho, esto también es fácil de entender. El núcleo de Spring es la inversión de control, inyección de dependencia y orientación Programación de aspectos, y luego dejando de lado todos los detalles, ¿qué encontraste? Spring proporciona un contenedor para administrar Beans y todo el ecosistema parece girar en torno a esto. El estudio del significado del código fuente reside, por un lado, en la propia tecnología y, por otro, en la comprensión y aceptación de las ideas que contiene.

Siempre te perderás si deambulas sin un propósito. Es diferente si tienes una meta. Por eso, este artículo se centra en las siguientes preguntas. Esto es lo que quiero compartir contigo: (Si tienes la misma pregunta que yo, sígueme, colecciona + me gusta, no te pierdas)

  • 1. ¿Cuáles son las características funcionales de la interfaz BeanPostProcessor?

  • 2. ¿Cómo ampliar la interfaz BeanPostProcessor?

  • 3. ¿Cuál es el principio de funcionamiento de la clase de implementación de la interfaz BeanPostProcessor?

  • 4. ¿Cuáles son los escenarios de aplicación de la interfaz BeanPostProcessor?

Características

  • 1. BeanPostProcessor es una interfaz de extensión a nivel de Bean. Una vez completada la creación de instancias de Bean administrada por Spring, se reservan dos puntos de extensión;

  • 2. El método de implementación de estas dos extensiones es implementar la interfaz BeanPostProcessor y registrar la clase de implementación en el contenedor Spring;

  • 3. Los dos puntos de extensión son el método postProcessBeforeInitialization y el método postProcessAfterInitialization de la interfaz BeanPostProcessor;

  • 4. El tiempo de ejecución del método postProcessBeforeInitialization es después de que se completan la creación de instancias del Bean administrado por Spring y la inyección de propiedades, y antes del método InitializingBean#afterPropertiesSet y el método de inicialización personalizado;

  • 5. El tiempo de ejecución del método postProcessAfterInitialization es posterior al método InitializingBean#afterPropertiesSet y al método de inicialización personalizado;

  • 6. El método postProcessBeforeInitialization y el método postProcessAfterInitialization de la clase de implementación de la interfaz BeanPostProcessor se ejecutarán después de que se inicialice cada bean administrado por Spring;

Método para realizar

1. Defina una clase de entidad Dog, implemente la interfaz InitializingBean e implemente afterPropertiesSet (). Entre ellos, afterPropertiesSet () e init () son para demostrar el tiempo de ejecución del método postProcessBeforeInitialization y el método postProcessAfterInitialization de la clase de implementación de la interfaz BeanPostProcessor;

@Getter
@Setter
@Slf4j
public class Dog implements InitializingBean {
    
    
    private String name = "旺财";
    private String color = "黑色";
    public Dog() {
    
    
        log.info("---dog的无参构造方法被执行");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
    
    
        log.info("---afterPropertiesSet被执行");
    }
    public void init() {
    
    
        log.info("---initMethod被执行");
    }
}

Registre la clase Dog en el contenedor Spring y configure el método de inicialización después de la creación de instancias de Bean;

@Configuration
public class SpringConfig {
    
    
    @Bean(initMethod = "init")
    public Dog dog(){
    
    
        Dog dog = new Dog();
        return dog;
    }
}

2. Defina MyBeanPostProcessor e implemente la interfaz BeanPostProcessor (el nombre de la clase y la lógica dentro del método son solo para fines de demostración. En el desarrollo real, el contenido de la demostración debe reemplazarse con lógica real)

@Component
@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {
    
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        if (beanName.equals("dog")) {
    
    
            log.info("postProcessBeforeInitialization---" + beanName);
            //如果特定的bean实例化完成后,还未执行InitializingBean.afterPropertiesSet()方法之前,有一些其他操作,可以在这里实现
        }
        return bean;
    }
 
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        if (beanName.equals("dog")) {
    
    
            log.info("postProcessAfterInitialization---" + beanName);
            //如果特定的bean实例化完成,InitializingBean.afterPropertiesSet()方法执行后,有一些其他操作,可以在这里实现
        }
        return bean;
    }
}

3. Escriba pruebas unitarias para verificar los resultados;

@SpringBootTest
@Slf4j
public class FanfuApplicationTests {
    
    
   @Test
    public void test3(){
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");
        Dog dog = ((Dog) context.getBean("dog"));
        log.info(dog.getName());
    }
}

Insertar descripción de la imagen aquí

Conclusión: a juzgar por los resultados de ejecución de la prueba unitaria, se verifica que el tiempo de ejecución del punto de extensión BeanPostProcessor de Spring, es decir, el tiempo de ejecución del método postProcessBeforeInitialization es después de que se completan la creación de instancias del Bean administrado por Spring y la inyección de propiedades. El método InitializingBean#afterPropertiesSet y el método de inicialización personalizado Antes; el tiempo de ejecución del método postProcessAfterInitialization es antes y después del método InitializingBean#afterPropertiesSet y el método de inicialización personalizado;

Lo anterior demuestra el método de implementación y el tiempo de ejecución de BeanPostProcessor como uno de los puntos de extensión de Springboot. Comencemos con un ejemplo para comprender su principio de funcionamiento básico. Como dice el refrán, para saber qué está sucediendo, también debes saber por qué.

principio de funcionamiento

La clave para el principio de funcionamiento de eanPostProcessor son en realidad dos puntos: primero, ¿cuándo se registró la clase de implementación de BeanPostProcessor? En segundo lugar, ¿cómo se ejecutan el método postProcessBeforeInitialization y el método postProcessAfterInitialization de la clase de implementación BeanPostProcessor?

Hora de registro

  • 1. Entre los dos métodos de extensión en BeanPostProcessor, el método postProcessBeforeInitialization se ejecuta primero, es decir, una vez completadas la creación de instancias del Bean y la inyección de atributos, la entrada de la clase de implementación de la interfaz BeanPostProcessor al contenedor Spring se encuentra mediante la depuración del código de muestra de implementación, es decir, org.springframework.context.support.AbstractApplicationContext#refresh—>registerBeanPostProcessors
    Insertar descripción de la imagen aquí

  • 2. Ingrese el método AbstractApplicationContext#registerBeanPostProcessors y encontrará que este código es muy limpio, es decir, se basa en el método RegisterBeanPostProcessors () de la clase PostProcessorRegistrationDelegate;

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    
    
   PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
  • 3. Ingresar el método RegisterBeanPostProcessors () de la clase PostProcessorRegistrationDelegate es otra historia: el primer paso es obtener los nombres de todas las clases de implementación que implementan la interfaz BeanPostProcessor, MyBeanPostProcessors en el ejemplo de implementación se encuentra entre ellas;
    Insertar descripción de la imagen aquí

El segundo paso es registrar BeanPostProcessorChecker con anticipación, cuyo objetivo principal es imprimir la información de registro durante el proceso de creación del Bean;
Insertar descripción de la imagen aquí

El tercer paso es dividir todas las clases de implementación de la interfaz BeanPostProcessor en tres grupos según si implementan la interfaz PriorityOrdered, si implementan la interfaz Ordered y otras;
Insertar descripción de la imagen aquí

Finalmente, el siguiente contenido es muy largo, pero muy simple, es decir, de acuerdo con las tres categorías divididas en el segundo paso, regístrese en orden, el orden específico es la clase de implementación de la interfaz BeanPostProcessor que implementa la interfaz PriorityOrdered, la implementación clase de la interfaz BeanPostProcessor que implementa la interfaz Ordered, y otras Clase de implementación de la interfaz BeanPostProcessor;
Insertar descripción de la imagen aquí

En resumen, el tiempo de registro de BeanPostProcessor es durante el proceso de inicio del contenedor Spring, es decir, después de que se completa la ejecución lógica del punto de extensión BeanFactoryPostProcessor, comienza el registro de BeanPostProcessor. La lógica de registro específica está en PostProcessorRegistrationDelegate#registerBeanPostProcessors() .

tiempo de ejecución

A partir del ejemplo del método de implementación se verifica que el tiempo de ejecución de la clase de implementación de la interfaz BeanPostProcessor es después de que se completan la instanciación y la inyección de atributos del Bean administrado por Spring. Luego se encuentra la entrada de instanciación de la clase Dog y luego se ejecuta la clase de implementación de la interfaz BeanPostProcessor, y el momento no está muy lejos.

1. A través de la depuración, registre la entrada de creación de instancias de la clase Dog en el contenedor Spring, es decir, org.springframework.context.support.AbstractApplicationContext#refresh—>finishBeanFactoryInitialization();
Insertar descripción de la imagen aquí

2. Ingrese FinishBeanFactoryInitialization() y descubra que la clase Dog en el ejemplo de implementación tiene una instancia de DefaultListableBeanFactory#preInstantiateSingletons—>getBean(). Aquí hay una breve introducción a la lógica de negocios de getBean (): al obtener un determinado bean, primero consulte el caché para determinar si existe. Si existe, regresará directamente. Si no existe, comience a crear el Bean. Si el Bean depende de otro Bean, entonces es una recursión del proceso anterior.
Insertar descripción de la imagen aquí

3. Después de ingresar desde el método getBean, el proceso principal es AbstractBeanFactory#doGetBean–>AbstractBeanFactory#createBean–>AbstractAutowireCapableBeanFactory#doCreateBean–>AbstractAutowireCapableBeanFactory#createBeanInstance, en este punto se completa la creación de instancias y la inyección de atributos del Bean. Llegados a este punto anímate, el tiempo de ejecución de la clase de implementación de la interfaz BeanPostProcessor que estás buscando está por llegar. Efectivamente, en el método AbstractAutowireCapableBeanFactory # doCreateBean, después de crear una instancia de la clase Dog, se llama a inicializeBean () para inicializar el bean. El tiempo de ejecución del método postProcessBeforeInitialization y el método postProcessAfterInitialization de la clase de implementación de la interfaz BeanPostProcessor es antes y después del ejecución del método de inicialización del Bean. Activado, entonces este método es muy probablemente la entrada al tiempo de ejecución de la clase de implementación de la interfaz BeanPostProcessor.
Insertar descripción de la imagen aquí

4. Ingrese inicializeBean() y el juicio es correcto. Primero ejecute el método postProcessBeforeInitialization de la clase de implementación de la interfaz BeanPostProcessor. Luego, si el bean implementa InitializingBean o personaliza initMethod, los métodos InitializingBean#afterPropertiesSet e initMethod se ejecutarán aquí. Finalmente, Se ejecutará el método postProcessAfterInitialization de la clase de implementación de la interfaz BeanPostProcessor;

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    
    
   if (System.getSecurityManager() != null) {
    
    
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
    
    
         invokeAwareMethods(beanName, bean);
         return null;
      }, getAccessControlContext());
   }else {
    
    
      invokeAwareMethods(beanName, bean);
   }
   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
    
    
       //执行BeanPostProcessor接口实现类的postProcessBeforeInitialization方法
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }
   try {
    
    
       //如果bean实现了InitializingBean或者自定义了initMethod,
       //会在这里执行InitializingBean#afterPropertiesSet和initMethod方法
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
    
    
      throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }
   if (mbd == null || !mbd.isSynthetic()) {
    
    
       //执行BeanPostProcessor接口实现类的postProcessAfterInitialization方法
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}

5. A continuación, ingrese applyBeanPostProcessorsBeforeInitialization(), invokeInitMethods(), applyBeanPostProcessorsAfterInitialization() respectivamente para ver cómo se implementan. Primero veamos applyBeanPostProcessorsBeforeInitialization(): si ha estudiado detenidamente los artículos anteriores sobre BeanFactoryPostProcessor del punto de extensión Springboot, BeanDefinitionRegistryPostProcessor del punto de extensión Springboot y ApplicationContextInitializer del punto de extensión Springboot, entonces estará muy familiarizado con la rutina de este método: primero obtener Todas las clases de implementación registradas en la interfaz BeanPostProcessor en el contenedor Spring luego se atraviesan y ejecutan para activar el método, que es muy simple y sin pretensiones.

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {
    
    
   Object result = existingBean;
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
    
    
      Object current = processor.postProcessBeforeInitialization(result, beanName);
      if (current == null) {
    
    
         return result;
      }
      result = current;
   }
   return result;
}

6. Echemos un vistazo nuevamente a AbstractAutowireCapableBeanFactory # invokeInitMethods. La lógica también es muy clara. Primero determine si la interfaz InitializingBean está implementada. Si la interfaz InitializingBean está implementada, activará la ejecución de afterPropertiesSet () y luego determinará si hay es un método initMethod personalizado. Si es así, inicie la ejecución aquí;

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
      throws Throwable {
    
    
    //判断是否实现了InitializingBean接口
   boolean isInitializingBean = (bean instanceof InitializingBean);
   if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
    
    
      if (logger.isTraceEnabled()) {
    
    
         logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
      }
      if (System.getSecurityManager() != null) {
    
    
         try {
    
    
            AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
    
    
               ((InitializingBean) bean).afterPropertiesSet();
               return null;
            }, getAccessControlContext());
         }
         catch (PrivilegedActionException pae) {
    
    
            throw pae.getException();
         }
      }else {
    
    
          //如果实现了InitializingBean接口,就会重写afterPropertiesSet(),这里就会触发执行
         ((InitializingBean) bean).afterPropertiesSet();
      }
   }
   if (mbd != null && bean.getClass() != NullBean.class) {
    
    
       //判断有没有自定义initMethod方法,如果有,则在这里开始执行;
      String initMethodName = mbd.getInitMethodName();
      if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
    
    
         invokeCustomInitMethod(beanName, bean, mbd);
      }
   }
}

7. Finalmente, echemos un vistazo a applyBeanPostProcessorsAfterInitialization(). Si comprende applyBeanPostProcessorsBeforeInitialization() antes, no es necesario analizarlo aquí. Es exactamente lo mismo. Está familiarizado con la receta y el sabor.

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {
    
    
 
   Object result = existingBean;
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
    
    
      Object current = processor.postProcessAfterInitialization(result, beanName);
      if (current == null) {
    
    
         return result;
      }
      result = current;
   }
   return result;
}

En este punto, se ha analizado el principio de funcionamiento del punto de extensión Springboot BeanPostProcessor. Se reduce a dos puntos: primero, durante el proceso de inicialización del contenedor Spring, se completa el registro del punto de extensión; segundo, después de que el Bean es instanciado y atributo inyectado en Spring, las acciones de extensión de activación para puntos de extensión registrados. El contenido es muy largo, pero la lógica es simple, espero que los amigos que lean este artículo tengan la paciencia de leerlo, porque después de haber estudiado todo el proceso con claridad, siento que me he beneficiado mucho y espero tú también lo harás.

Escenarios de aplicación

De hecho, después de comprender las características funcionales, los métodos de implementación y los principios de funcionamiento de BeanPostProcessor, puede aplicar este punto de extensión cuando encuentre necesidades comerciales similares. Aquí se le ocurren dos escenarios de aplicación:

  • Procesamiento de anotaciones personalizadas
    En el programa, podemos personalizar anotaciones y marcarlas en las clases correspondientes. Cuando una clase se registra en el contenedor Spring y se crea una instancia, y queremos activar algunas otras operaciones correspondientes a las anotaciones personalizadas, podemos implementar a través de BeanPostProcessor .

  • Hay dos artículos anteriores sobre verificación de parámetros
    : Verificación elegante de parámetros Springboot (1) y Verificación elegante de parámetros Springboot (2). Comparten con usted el método de implementación específico de la verificación de parámetros. El principio básico es utilizar el punto de extensión BeanPostProcessor. Específicamente, la clase de implementación es org.springframework.validation.beanvalidation.BeanValidationPostProcessor

——————————————
Declaración de derechos de autor: este artículo es un artículo original del blogger de CSDN "Ordinary Trafficker" y sigue el acuerdo de derechos de autor CC 4.0 BY-SA. Adjunte el enlace de la fuente original y esta copia. al reimprimir la declaración.
Enlace original: https://blog.csdn.net/fox9916/article/details/128917992

Supongo que te gusta

Origin blog.csdn.net/tian830937/article/details/132947732
Recomendado
Clasificación