• Spring注解驱动之BeanPostProcessor后置处理器详解


    概述

    在学习Spring的时候,在了解基本用法的时候,如果有时间一定要深入源码了解Spring的底层原理,这样在做一些适配工作、写一些轮子的时候就会比较容易,否则会很难,甚至一头雾水,无法完成工作。
    吃透Spring的原理和源码,往往可以拉开人们之间的差距,当前只要是使用Java技术栈开发的Web项目,几乎都会使用Spring框架。而且目前各招聘网站上对于Java开发的要求几乎清一色的都是熟悉或者精通Spring,所以,你很有必要学习Spring的细节知识点。

    BeanPostProcessor后置处理器概述

    首先,看下BeanPostProcessor的源码。

    package org.springframework.beans.factory.config;
    
    import org.springframework.beans.BeansException;
    
    public interface BeanPostProcessor {
    	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    
    	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    从源码可以看出,BeanPostProcessor是一个接口,其中有两个方法:
    postProcessBeforeInitialization和postProcessAfterInitialization这两个方法分别是在Spring容器中的bean初始化前后执行,所以Spring容器中的每个bean对象初始化前后,都会执行BeanPostProcessor接口的两个方法。

    也就是说,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。当容器中存在多个BeanPostProcessor的实现类时,会按照它们在容器中注册的顺序执行。对于自定义的BeanPostProcessor实现类,还可以让其实现Ordered接口自定义排序。
    因此我们可以在每个bean对象初始化前后,加上自己的逻辑。实现方式是自定义一个BeanPostProcessor接口的实现类,例如MyBeanPostProcessor,然后在该类的postProcessBeforeInitialization和postProcessAfterInitialization这两个方法写上自定义逻辑。

    BeanPostProcessor后置处理器实例

    我们创建一个MyBeanPostProcessor类,实现BeanPostProcessor接口,如下所示。

    package com.meimeixia.bean;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.stereotype.Component;
    
    @Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了,否则无法工作
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
    	@Override
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
    		return bean;
    	}
    
    	@Override
    	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
    		return bean;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    接下来,我们应该是要编写测试用例来进行测试了。

    package com.meimeixia.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Scope;
    
    import com.meimeixia.bean.Car;
    
    @ComponentScan("com.meimeixia.bean")
    @Configuration
    public class MainConfigOfLifeCycle {
    
    	@Bean(initMethod="init", destroyMethod="destroy")
    	public Car car() {
    		return new Car();
    	}	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    第二处改动是将Cat类上添加的@Scope(“prototype”)注解给注释掉,因为咱们之前做测试的时候,也是将Cat对象设置成多实例bean了。

    package com.meimeixia.bean;
    
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    // @Scope("prototype")
    @Component
    public class Cat implements InitializingBean, DisposableBean {
    	
    	public Cat() {
    		System.out.println("cat constructor...");
    	}
    
    	/**
    	 * 会在容器关闭的时候进行调用
    	 */
    	@Override
    	public void destroy() throws Exception {
    		// TODO Auto-generated method stub
    		System.out.println("cat destroy...");
    	}
    
    	/**
    	 * 会在bean创建完成,并且属性都赋好值以后进行调用
    	 */
    	@Override
    	public void afterPropertiesSet() throws Exception {
    		// TODO Auto-generated method stub
    		System.out.println("cat afterPropertiesSet...");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    好了,现在咱们就可以编写测试用例来进行测试了。可喜的是,我们也不用再编写一个测试用例了,直接运行IOCTest_LifeCycle类中的test01()方法就行,该方法的代码如下所示。

    @Test
    public void test01() {
        // 1. 创建IOC容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
        System.out.println("容器创建完成");
        // 关闭容器
        applicationContext.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    此时,运行IOCTest_LifeCycle类中的test01()方法,输出的结果信息如下所示。
    在这里插入图片描述
    可以看到,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。

    当然了,也可以让我们自己写的MyBeanPostProcessor类来实现Ordered接口自定义排序,如下所示。

    package com.meimeixia.bean;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.core.Ordered;
    import org.springframework.stereotype.Component;
    
    /**
     * 后置处理器,在初始化前后进行处理工作
     * @author liayun
     *
     */
    @Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了
    public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
    	@Override
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
    		return bean;
    	}
    
    	@Override
    	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
    		return bean;
    	}
    
    	@Override
    	public int getOrder() {
    		return 3;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    BeanPostProcessor后置处理器作用

    后置处理器可用于bean对象初始化前后进行逻辑增强。
    Spring提供了BeanPostProcessor接口的很多实现类,例如AutowiredAnnotationBeanPostProcessor用于@Autowired注解的实现,AnnotationAwareAspectJAutoProxyCreator用于Spring AOP的动态代理等等。

    除此之外,我们还可以自定义BeanPostProcessor接口的实现类,在其中写入咱们需要的逻辑。

    bean的初始化和销毁流程

    我们知道BeanPostProcessor的postProcessBeforeInitialization()方法是在bean的初始化之前被调用;而postProcessAfterInitialization()方法是在bean初始化的之后被调用。
    并且bean的初始化和销毁方法我们可以通过如下方式进行指定。

    1. 通过@Bean指定init-method和destroy-method
    @Bean(initMethod="init", destroyMethod="destroy")
    
    • 1
    1. 通过让bean实现InitializingBean和DisposableBean这俩接口
    @Component
    public class Cat implements InitializingBean, DisposableBean {}
    
    • 1
    • 2
    1. 使用JSR-250规范里面定义的@PostConstruct和@PreDestroy这俩注解
      • @PostConstruct:在bean创建完成并且属性赋值完成之后,来执行初始化方法
      • @PreDestroy:在容器销毁bean之前通知我们进行清理工作
    2. 通过让bean实现BeanPostProcessor接口
    @Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了
    public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {}
    
    • 1
    • 2

    通过以上四种方式就可以对bean的整个生命周期进行控制:

    • bean的实例化:调用bean的构造方法,我们可以在bean的无参构造方法中执行相应的逻辑。
    • bean的初始化:在初始化时可以通过BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法进行拦截,执行自定义的逻辑。通过@PostConstruct注解、InitializingBean和init-method来指定bean初始化前后执行的方法,在该方法中可以执行自定义的逻辑。
    • bean的销毁:可以通过@PreDestroy注解、DisposableBean和destroy-method来指定bean在销毁前执行的方法,在方法中可以执行自定义的逻辑。
      所以,通过上述4种方式,我们可以控制Spring中bean的整个生命周期。

    BeanPostProcessor源码解析

    如果想深刻理解BeanPostProcessor的工作原理,那么就不得不看下相关的源码,我们可以在MyBeanPostProcessor类的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法这两处打上断点来进行调试,如下所示。

    public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("postProcessBeforeInitialization" + ",beanName:"+beanName + ",bean=>" + bean);
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("postProcessAfterInitialization" + ",beanName:"+beanName + ",bean=>" + bean);
            return bean;
        }
    
        @Override
        public int getOrder() {
            return 3;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    通过断点调试,我们可以看到,在applyBeanPostProcessorBeforeInitialization()方法中,会遍历所有BeanPostProcessor对象,然后依次执行所有BeanPostProcessor对象的postProcessorBeforeInitialization()方法,一旦BeanPostProcessor对象的postProcessBeforeInitialization()方法返回null以后,则后面的BeanPostProcessor对象便不再执行了,而是直接退出for循环。这些都是我们看源码看到的。
    看Spring源码,我们还看到一个细节,在Spring中调用initializeBean()方法之前,还调用了populateBean()方法来为bean的属性赋值。
    经过一系列的跟踪源码分析,我们可以将关键代码的调用过程使用如下伪代码表述出来。

    populateBean(beanName, mbd, instanceWrapper); // 给bean进行属性赋值
    initializeBean(beanName, exposedObject, mbd)
    {
    	applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    	invokeInitMethods(beanName, wrappedBean, mbd); // 执行自定义初始化
    	applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    也就是说,在Spring中,调用initializeBean()方法之前,调用了populateBean()方法为bean的属性赋值,为bean的属性赋好值之后,再调用initializeBean()方法进行初始化。
    在initializeBean()中,调用自定义的初始化方法(即invokeInitMethods())之前,调用了applyBeanPostProcessorsBeforeInitialization()方法,而在调用自定义的初始化方法之后,又调用了applyBeanPostProcessorsAfterInitialization()方法。至此,整个bean的初始化过程就这样结束了。

    BeanPostProcessor接口在Spring底层的应用案例

    ApplicationContextAwareProcessor类

    org.springframework.context.support.ApplicationContextAwareProcessor是BeanPostProcessor接口的一个实现类,这个类的作用是可以向组件中注入IOC容器,大致的源码如下。
    在这里插入图片描述
    注意:我这里的Spring版本为4.3.12.RELEASE。

    那具体如何使用ApplicationContextAwareProcessor类向组件中注入IOC容器呢?
    如果需要向组件中注入IOC容器,那么可以让组件实现ApplicationContextAware接口。
    例如,我们创建一个创建一个Dog类,使其实现ApplicationContextAware接口,此时,我们需要实现ApplicationContextAware接口中的setApplicationContext()方法,在setApplicationContext()方法中有一个ApplicationContext类型的参数,这个就是IOC容器对象,我们可以在Dog类中定义一个ApplicationContext类型的成员变量,然后在setApplicationContext()方法中为这个成员变量赋值,此时就可以在Dog类中的其他方法中使用ApplicationContext对象了,如下所示。

    package com.meimeixia.bean;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    /**
     * ApplicationContextAwareProcessor这个类的作用是可以帮我们在组件里面注入IOC容器,
     * 怎么注入呢?我们想要IOC容器的话,比如我们这个Dog组件,只需要实现ApplicationContextAware接口就行
     * 
     */
    @Component
    public class Dog implements ApplicationContextAware {
    	
    	private ApplicationContext applicationContext;
    
    	public Dog() {
    		System.out.println("dog constructor...");
    	}
    	
    	// 在对象创建完成并且属性赋值完成之后调用
    	@PostConstruct
    	public void init() { 
    		System.out.println("dog...@PostConstruct...");
    	}
    	
    	// 在容器销毁(移除)对象之前调用
    	@PreDestroy
    	public void destory() {
    		System.out.println("dog...@PreDestroy...");
    	}
    
    	@Override
    	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 在这儿打个断点调试一下
    		// TODO Auto-generated method stub
    		this.applicationContext = applicationContext;
    	}	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    看到这里,相信不少小伙伴们都有一种很熟悉的感觉,没错,我之前也在项目中使用过!是的,这就是BeanPostProcessor在Spring底层的一种使用场景。至于上面的案例代码为何会在setApplicationContext()方法中获取到ApplicationContext对象,这就是ApplicationContextAwareProcessor类的功劳了!

    接下来,我们就深入分析下ApplicationContextAwareProcessor类。
    我们先来看下ApplicationContextAwareProcessor类中对于postProcessBeforeInitialization()方法的实现,如下所示。
    在这里插入图片描述
    在bean初始化之前,首先对当前bean的类型进行判断,如果当前bean的类型不是EnvironmentAware,不是EmbeddedValueResolverAware,不是ResourceLoaderAware,不是ApplicationEventPublisherAware,不是MessageSourceAware,也不是ApplicationContextAware,那么直接返回bean。如果是上面类型中的一种类型,那么最终会调用invokeAwareInterfaces()方法,并将bean传递给该方法。

    invokeAwareInterfaces()方法又是个什么呢?我们继续看invokeAwareInterfaces()方法的源码,如下所示。
    在这里插入图片描述
    可以看到invokeAwareInterfaces()方法的源码比较简单,就是判断当前bean属于哪种接口类型,然后将bean强转为哪种接口类型的对象,接着调用接口中的方法,将相应的参数传递到接口的方法中。
    我们可以看到,此时会将this.applicationContext传递到ApplicationContextAware接口的setApplicationContext()方法中。所以,我们在Dog类的setApplicationContext()方法中就可以直接接收到ApplicationContext对象了。

    BeanValidationPostProcessor类

    org.springframework.validation.beanvalidation.BeanValidationPostProcessor类注意是用来为bean进行校验操作的,当我们创建bean,并为bean赋值后,我们可以通过BeanValidationPostProcessor类为bean进行校验操作。BeanValidationPostProcessor类源码如下:
    在这里插入图片描述
    这里,我们也来看看postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法的实现,如下所示。
    在这里插入图片描述

    InitDestroyAnnotationBeanPostProcessor类

    org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor类主要用来处理
    @PostConstruct注解和@PreDestroy注解。
    使用了@PostConstruct注解和@PreDestroy注解来标注方法,Spring怎么就知道什么时候执行@PostConstruct注解标注的方法,什么时候执行@PreDestroy注解标注的方法呢?这就要归功于InitDestroyAnnotationBeanPostProcessor类了。
    接下来,我们也通过Debug的方式来跟进下代码的执行流程。首先,在Dog类的initt()方法上打上一个断点,如下所示。
    在这里插入图片描述
    在InitDestroyAnnotationBeanPostProcessor类的postProcessBeforeInitialization()方法中,首先会找到bean中有关生命周期的注解,比如@PostConstruct注解等,找到这些注解之后,则将这些信息赋值给LifecycleMetadata类型的变量metadata,之后调用metadata的invokeInitMethods()方法,通过反射来调用标注了@PostConstruct注解的方法。这就是为什么标注了@PostConstruct注解的方法会被Spring执行的原因。

    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    		try {
    			metadata.invokeInitMethods(bean, beanName);
    		}
    		catch (InvocationTargetException ex) {
    			throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
    		}
    		catch (Throwable ex) {
    			throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
    		}
    		return bean;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    AutowiredAnnotationBeanPostProcessor类

    org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor类主要是用于处理标注了@Autowired注解的变量或方法。

    参考

    Spring注解驱动开发第15讲——关于BeanPostProcessor后置处理器,你了解多少?
    Spring注解驱动开发第16讲——面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!
    Spring注解驱动开发第17讲——BeanPostProcessor在Spring底层是如何使用的?看完这篇我懂了!!

  • 相关阅读:
    【云原生 | Kubernetes 系列】--K8s环境rdb,cefs的使用
    【数据结构】时间复杂度和空间复杂度
    coredump-X: 构造函数未作成员指针的初始化复合多线程导致
    AIGC 设计能替代真正的设计师设计吗?
    DPDK的VFIO
    如何查找无物流信息单号
    微信小程序毕业设计论文教学管理系统+后台管理系统项目源代码
    第十五天设计 Goal 解析器
    centos 安装 percona-xtrabackup
    【在SpringBoot项目中使用Validation框架检查数据格式-常用的检查注解】
  • 原文地址:https://blog.csdn.net/tianzhonghaoqing/article/details/126690711