美文网首页有些文章不一定是为了上首页投稿
不,你不了解Spring实例化bean的时候做了什么

不,你不了解Spring实例化bean的时候做了什么

作者: Java面试官 | 来源:发表于2019-05-29 11:18 被阅读2次

    Spring加载bean的时候构造函数什么时候调用、@PostConstruct什么时候调用、实现了BeanPostProcessor接口的bean中的postProcessAfterInitialization和postProcessBeforeInitialization什么时候调用?你是否清楚呢?如果清楚的话可以直接忽略该篇文章!!!

    最近来了几个新人,被问了一个和bean生命周期相关的一个知识点,解决新人的问题后自己再写了一个demo,目的是为了清晰的描述整个bean的生命周期。

    注意注意,以下demo有五个类,可能会引起部分人不适,建议可以直接跳到最后看最终总结,或者自己下载源码运行下。
    demo地址:https://github.com/wiatingpub/demo/tree/master/springboot-demo

    给出一个demo

    首先给出一个实现了BeanFactoryPostProcessor的类,目的是为了比较清晰的看出postProcessBeanFactory接口被调用的时间点。

    /**
     * BeanFactoryPostProcessor是bean工厂的处理器
     */
    @Component
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
        public MyBeanFactoryPostProcessor() {
            super();
            System.out.println("【BeanFactoryPostProcessor】实现类postProcessBeanFactory的构造函数");
        }
    
        // 允许我们在工厂里所有的bean被加载进来后但是还没初始化前,对所有bean的属性进行修改也可以add属性值,该操作在对应bean的构造函数执行前
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0)
                throws BeansException {
            System.out
                    .println("【BeanFactoryPostProcessor.postProcessBeanFactory】,来自MyBeanFactoryPostProcessor");
            //获取到Spring中所有的beanName
            String[] beanStr = arg0.getBeanDefinitionNames();
            //循环打印
            for (String beanName : beanStr) {
                System.out.print("bean name:" + beanName + ";");
            }
    
            System.out.println();
        }
    }
    
    这里给出一个实现了BeanPostProcessor的类,目的是为了看出postProcessAfterInitialization、postProcessBeforeInitialization调用的时间点。
    
    /**
     * 完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理则要实现接口BeanPostProcessor
     */
    @Component
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        public MyBeanPostProcessor() {
            super();
            System.out.println("【MyBeanPostProcessor】BeanPostProcessor实现类的构造函数");
        }
    
        // 实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的业务
        @Override
        public Object postProcessAfterInitialization(Object arg0, String arg1)
                throws BeansException {
    
            if (arg0.getClass() == TestBeanA.class || arg0.getClass() == TestBeanB.class) {
                System.out
                        .println("【BeanPostProcessor.postProcessAfterInitialization】来自MyBeanPostProcessor,beanName:" + arg1);
            }
            return arg0;
        }
    
        // 实例化、依赖注入、初始化后完成一些定制的业务
        @Override
        public Object postProcessBeforeInitialization(Object arg0, String arg1)
                throws BeansException {
    
            if (arg0.getClass() == TestBeanA.class || arg0.getClass() == TestBeanB.class) {
                System.out
                        .println("【BeanPostProcessor.postProcessBeforeInitialization】来自MyBeanPostProcessor,beanName:" + arg1);
            }
            return arg0;
        }
    }
    

    这里给出继承了InstantiationAwareBeanPostProcessorAdapter的子类,目的是为了看出postProcessBeforeInitialization、postProcessAfterInitialization、postProcessPropertyValues被调用的时间点。

    /**
     * 适配器类,基类是BeanPostProcessor的实现类
     */
    @Component
    public class MyInstantiationAwareBeanPostProcessor extends
            InstantiationAwareBeanPostProcessorAdapter {
    
        public MyInstantiationAwareBeanPostProcessor() {
            super();
            System.out
                    .println("【MyInstantiationAwareBeanPostProcessor】InstantiationAwareBeanPostProcessorAdapter实现类的构造函数");
        }
    
        // 接口方法、实例化Bean之前调用
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
            if (bean.getClass() == TestBeanA.class || bean.getClass() == TestBeanB.class) {
                System.out
                        .println("【InstantiationAwareBeanPostProcessorAdapter.postProcessBeforeInitialization】来自MyInstantiationAwareBeanPostProcessor,beanName:" + beanName);
    
            }
            return bean;
        }
    
        // 接口方法、实例化Bean之后调用
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName)
                throws BeansException {
            if (bean.getClass() == TestBeanA.class || bean.getClass() == TestBeanB.class) {
                System.out
                        .println("【InstantiationAwareBeanPostProcessorAdapter.postProcessAfterInitialization】来自MyInstantiationAwareBeanPostProcessor,beanName:" + beanName);
            }
    
            return bean;
        }
    
        // 接口方法、设置某个属性时调用
        @Override
        public PropertyValues postProcessPropertyValues(PropertyValues pvs,
                                                        PropertyDescriptor[] pds, Object bean, String beanName)
                throws BeansException {
    
            if (bean.getClass() == TestBeanA.class || bean.getClass() == TestBeanB.class) {
                System.out
                        .println("【InstantiationAwareBeanPostProcessorAdapter.postProcessPropertyValues】来自MyInstantiationAwareBeanPostProcessor,beanName:" + beanName);
            }
    
            return pvs;
        }
    

    这个就牛逼了,直接交给applicationContext.xml注入并且实现了BeanFactoryAware, BeanNameAware,InitializingBean,DisposableBean四个接口类,目的是为了清晰的看出以下好几个接口被调用的时间点。

    public class TestBeanA implements BeanFactoryAware, BeanNameAware,
            InitializingBean, DisposableBean {
    
        private String name;
        private String address;
        private int phone;
    
        private BeanFactory beanFactory;
        private String beanName;
    
        @PostConstruct
        public void init() {
            System.out.println("【TestBeanA.@PostConstruct】");
        }
    
        public TestBeanA() {
            System.out.println("【TestBeanA.默认构造器】");
        }
    
        public TestBeanA(String name) {
            System.out.println("【TestBeanA.带参构造器】");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public int getPhone() {
            return phone;
        }
    
        public void setPhone(int phone) {
            this.phone = phone;
        }
    
        @Override
        public String toString() {
            return "TestBeanA [address=" + address + ", name=" + name + ", phone="
                    + phone + "]";
        }
    
        // 这是BeanFactoryAware接口方法
        @Override
        public void setBeanFactory(BeanFactory arg0) throws BeansException {
            System.out
                    .println("【BeanFactoryAware.setBeanFactory】来自TestBeanA");
            this.beanFactory = arg0;
        }
    
        // 这是BeanNameAware接口方法
        @Override
        public void setBeanName(String arg0) {
            System.out.println("【BeanNameAware.setBeanName】来自TestBeanA");
            this.beanName = arg0;
        }
    
        // 这是InitializingBean接口方法
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out
                    .println("【InitializingBean.afterPropertiesSet】来自TestBeanA");
        }
    
        // 这是DiposibleBean接口方法
        @Override
        public void destroy() throws Exception {
            System.out.println("【DiposibleBean.destory】来自TestBeanA");
        }
    
        // 通过<bean>的init-method属性指定的初始化方法
        public void myInit() {
            System.out.println("【TestBeanA.myInit】");
        }
    
        // 通过<bean>的destroy-method属性指定的初始化方法
        public void myDestory() {
            System.out.println("【TestBeanA.destroy-method】");
        }
    }
    

    TestBeanB的作用比较简单,为了看出内部数据成员有其他容器Bean的时候Spring是如何加载bean的。

    @Component
    public class TestBeanB {
    
        @Autowired
        private TestBeanA testBeanA;
    
        public TestBeanB() {
            System.out.println("【TestBeanB.默认构造器】");
        }
    }
    

    这个是启动类。

    @SpringBootApplication
    @ImportResource(locations = {"classpath:applicationContext.xml"})
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }
    

    虽然使用的是SpringBoot,这里还使用xml是为了看看init-method和destroy-method的调用时间。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="testBeanA" class="com.example.demo.bean.TestBeanA" init-method="myInit" destroy-method="myDestory">
            <property name="name" value="关注微信公众号 饭说编程"/>
        </bean>
    </beans>
    

    很长很长的代码算是都给出来了,接下来看下运行后给出的结果

    【BeanFactoryPostProcessor】实现类postProcessBeanFactory的构造函数
    【BeanFactoryPostProcessor.postProcessBeanFactory】,来自MyBeanFactoryPostProcessor
    【MyBeanPostProcessor】BeanPostProcessor实现类的构造函数
    【MyInstantiationAwareBeanPostProcessor】InstantiationAwareBeanPostProcessorAdapter实现类的构造函数
    【TestBeanB.默认构造器】
    【InstantiationAwareBeanPostProcessorAdapter.postProcessPropertyValues】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanB
    【TestBeanA.默认构造器】
    【InstantiationAwareBeanPostProcessorAdapter.postProcessPropertyValues】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanA
    【BeanNameAware.setBeanName】来自TestBeanA
    【BeanFactoryAware.setBeanFactory】来自TestBeanA
    【BeanPostProcessor.postProcessBeforeInitialization】来自MyBeanPostProcessor,beanName:testBeanA
    【InstantiationAwareBeanPostProcessorAdapter.postProcessBeforeInitialization】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanA
    【TestBeanA.@PostConstruct】
    【InitializingBean.afterPropertiesSet】来自TestBeanA
    【TestBeanA.myInit】
    【BeanPostProcessor.postProcessAfterInitialization】来自MyBeanPostProcessor,beanName:testBeanA
    【InstantiationAwareBeanPostProcessorAdapter.postProcessAfterInitialization】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanA
    【BeanPostProcessor.postProcessBeforeInitialization】来自MyBeanPostProcessor,beanName:testBeanB
    【InstantiationAwareBeanPostProcessorAdapter.postProcessBeforeInitialization】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanB
    【BeanPostProcessor.postProcessAfterInitialization】来自MyBeanPostProcessor,beanName:testBeanB
    【InstantiationAwareBeanPostProcessorAdapter.postProcessAfterInitialization】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanB
    【DiposibleBean.destory】来自TestBeanA
    【TestBeanA.destroy-method】
    

    貌似又是一段看完头晕脑胀的恶心代码,不过不方不方,直接进入分析阶段,跟着分析入手就简单多了。

    通过结果分析

    首先从

    【BeanFactoryPostProcessor】实现类postProcessBeanFactory的构造函数
    【BeanFactoryPostProcessor.postProcessBeanFactory】,来自MyBeanFactoryPostProcessor
    【MyBeanPostProcessor】BeanPostProcessor实现类的构造函数
    【MyInstantiationAwareBeanPostProcessor】InstantiationAwareBeanPostProcessorAdapter实现类的构造函数
    

    可以看出执行顺序是:

    • 调用postProcessBeanFactory实现类的构造函数

    BeanFactoryPostProcessor是bean工厂的处理器

    • 调用postProcessBeanFactory实现类的postProcessBeanFactory实现函数

    postProcessBeanFactory函数允许我们在工厂里所有的bean被加载进来后但是还没初始化前,对所有bean的属性进行修改也可以add属性值

    • 调用BeanPostProcessor实现类的构造函数

    完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理则要实现接口BeanPostProcessor

    • 调用InstantiationAwareBeanPostProcessorAdapter实现类的构造函数

    适配器类,基类是BeanPostProcessor的实现类

    再从

    【TestBeanB.默认构造器】
    【InstantiationAwareBeanPostProcessorAdapter.postProcessPropertyValues】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanB
    

    可以看到,此处spring容器先扫描到了TestBeanB,因此调用顺序是

    • TestBeanB的默认构造器【无参构造器】
    • 调用InstantiationAwareBeanPostProcessorAdapter实现类的postProcessPropertyValues接口

    postProcessPropertyValues在接口方法、设置某个属性时调用,通过实验看到实例化一个Bean的时候也会调用该接口方法

    之后实例化TestBeanB的时候发现内部注入了TestBeanA,因此Spring转而实例化TestBeanA。

    接下来看到的是TestBeanA的实例化过程中调用的顺序

    【TestBeanA.默认构造器】
    【InstantiationAwareBeanPostProcessorAdapter.postProcessPropertyValues】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanA
    【BeanNameAware.setBeanName】来自TestBeanA
    【BeanFactoryAware.setBeanFactory】来自TestBeanA
    【BeanPostProcessor.postProcessBeforeInitialization】来自MyBeanPostProcessor,beanName:testBeanA
    【InstantiationAwareBeanPostProcessorAdapter.postProcessBeforeInitialization】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanA
    【TestBeanA.@PostConstruct】
    【InitializingBean.afterPropertiesSet】来自TestBeanA
    【TestBeanA.myInit】
    【BeanPostProcessor.postProcessAfterInitialization】来自MyBeanPostProcessor,beanName:testBeanA
    【InstantiationAwareBeanPostProcessorAdapter.postProcessAfterInitialization】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanA
    
    • 调用了TestBeanA的默认构造器
    • 调用InstantiationAwareBeanPostProcessorAdapter实现类的postProcessPropertyValues接口
    • 调用BeanNameAware的setBeanName接口

    可以从源码看到TestBeanA实现了接口BeanNameAware

    • 调用BeanFactoryAware的setBeanFactory接口

    可以从源码看到TestBeanA实现了接口BeanFactoryAware

    • 调用BeanPostProcessor的postProcessBeforeInitialization接口
    • 调用InstantiationAwareBeanPostProcessorAdapter的postProcessBeforeInitialization接口
    • 调用TestBeanA中注解了@PostConstruct的函数
    • 调用了InitializingBean的afterPropertiesSet接口

    可以从源码看到TestBeanA实现了接口InitializingBean

    • 调用了TestBeanA的myInit接口

    可以看到该init-method接口是在applicationContext中init-method配置上的

    • 调用BeanPostProcessor实现类的postProcessAfterInitialization接口
    • 调用InstantiationAwareBeanPostProcessorAdapter子类的postProcessAfterInitialization接口

    等TestBeanA实例化结束后,则继续TestBeanB的实例化路程

    【BeanPostProcessor.postProcessBeforeInitialization】来自MyBeanPostProcessor,beanName:testBeanB
    【InstantiationAwareBeanPostProcessorAdapter.postProcessBeforeInitialization】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanB
    【BeanPostProcessor.postProcessAfterInitialization】来自MyBeanPostProcessor,beanName:testBeanB
    【InstantiationAwareBeanPostProcessorAdapter.postProcessAfterInitialization】来自MyInstantiationAwareBeanPostProcessor,beanName:testBeanB
    
    • 调用BeanPostProcessor实现类的postProcessBeforeInitialization接口
    • 调用InstantiationAwareBeanPostProcessorAdapter子类的postProcessBeforeInitialization接口
    • 调用BeanPostProcessor实现类的postProcessAfterInitialization接口
    • 调用InstantiationAwareBeanPostProcessorAdapter子类的postProcessAfterInitialization接口

    最后

    【DiposibleBean.destory】来自TestBeanA
    【TestBeanA.destroy-method】
    
    • 调用DiposibleBean实现类的destory接口

    TestBeanA实现了DiposibleBean接口,实现了destroy接口

    • 调用TestBeanA的myDestory函数

    可以看到该myDestory接口是在applicationContext中destroy-method配置上的。

    最终总结

    【超重点,面试回答模板,老牛逼了】

    • 如果有bean实现了BeanFactoryPostProcessor接口类,则会实例化该bean,并且调用默认构造器,然后调用postProcessBeanFactory接口
    • 如果有bean实现了BeanPostProcessor接口类,则会先实例化该bean,同样调用默认构造器
    • 如果有bean继承了InstantiationAwareBeanPostProcessorAdapter类,则会先实例化该bean,同样调用默认构造器

    之后真正进入一个bean的生命周期过程

    • 先调用bean的默认构造函数
    • 如果有bean继承了InstantiationAwareBeanPostProcessorAdapter类,此刻会调用postProcessPropertyValues函数
    • 如果这个bean已经实现了BeanNameAware接口,会调用它实现的setBeanName方法,此处传递的就是applicationContext.xml配置文件中Bean的id值
    • 如果这个bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory,传递的是Spring工厂自身
    • 如果存在其他bean实现了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
    • 如果存在其他bean继承了InstantiationAwareBeanPostProcessorAdapter类,则会调用postProcessBeforeInitialization接口
    • 如果这个bean中有函数加了@PostConstruct注解,则该函数会在此刻被调用
    • 如果这个bean实现了InitializingBean接口重写了afterPropertiesSet方法,该方法会在此刻被调用
    • 如果这个bean在Spring配置文件中配置了init-method属性,则会自动调用其配置的初始化方法。
    • 如果存在其他bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法
    • 如果存在其他bean继承了InstantiationAwareBeanPostProcessorAdapter类,则会调用postProcessAfterInitialization接口

    走到这一步以后就可以应用这个Bean了,而这个Bean是一个Singleton的,虽然在Spring配置文件中也可以配置非Singleton,这里不做赘述。

    走入销毁阶段

    • 当bean不再需要时,会经过清理阶段,如果这个bean实现了DisposableBean接口,会调用其实现的destroy()方法;
    • 如果这个bean的applicationContext.xml配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

    Java源码分析、go语言应用、微服务,更多干货欢迎关注公众号:

    公众号.jpg

    相关文章

      网友评论

        本文标题:不,你不了解Spring实例化bean的时候做了什么

        本文链接:https://www.haomeiwen.com/subject/hxmetctx.html