【bean的生命周期】--- BeanDefinition和BeanFactoryPostProcessor简介

源码地址:https://github.com/nieandsun/spring-study


1 单例业务bean的创建流程简介

单实例业务bean创建的基本过程如下图所示:
在这里插入图片描述
即:

(1)源码被编译后生成一个个的class文件
(2)这些class文件被JVM加载到内存
(3)spring为标有@Bean、@Component、@Controller等注解的bean创建一个个的BeanDefinition对象,并将其放入到beanDefinitionMap
(4)在具体的业务bean创建之前BeanFactoryPostProcessor可以对BeanDefinition对象进行修改
(5)循环从beanDefinitionMap中取出BeanDefinition对象,进行具体业务对象的创建+实例化
(6)将创建的业务对象放入到单例缓存Map中

之前写过多篇用于介绍bean的创建过程(即bean的生命周期)的文章:

【bean的生命周期】详解InitializingBean、initMethod和@PostConstruct
【bean的生命周期】— 对象创建+初始化流程分析 — 【重点@Autowired的作用时机】
【bean的生命周期】— 构造方法、@Autowired、BeanPostProcessor、InitializingBean等的执行顺序解析
【bean的生命周期】BeanPostProcessor简介
【bean的生命周期 - spring注解】@Value
【bean的生命周期】— DisposableBean、destroyMethod和@PreDestroy
【bean的生命周期】— InstantiationAwareBeanPostProcessor接口简介

但是这些文章其实介绍的都是循环从beanDefinitionMap中取出BeanDefinition对象,进行具体业务对象的创建+实例化这一阶段的子过程。本篇文章讲解一下bean的另一段生命周期 — 通过BeanFactoryPostProcessor对BeanDefinition对象进行修改来干预bean的创建。
其实bean的这段生命周期在我们平时进行业务开发时一般都不会涉及到,但是如果你想了解框架整合的原理(如spring和Mybatis的整合)时,这块内容就不得不了解了。


2 BeanFactoryPostProcessor修改BeanDefinition来干预bean的创建

如1中图所示,BeanDefinition对象的属性其实有很多,本文只讲解BeanDefinition对象的class、autowireMode和constructorArgumentValues三个属性。


2.1 测试类

  • InstA
package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;

import org.springframework.stereotype.Component;

/***
 * @author : Sun Chuan
 * @date : 2019/12/29
 * Description:
 */
@Component
public class InstA {

    //@Autowired
    private InstB instB;

    public InstA() {
        System.out.println("InstA的无参构造方法");
    }

    public InstA(InstB instB) {
        this.instB = instB;
        System.out.println("InstA通过构造函数注入InstB");
    }

    public InstB getInstB() {
        return instB;
    }

    public void setInstB(InstB instB) {
        this.instB = instB;
    }

    @Override
    public String toString() {
        return "InstA{instB=" + instB + "}";
    }
}

  • InstB 类
package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;
import org.springframework.stereotype.Component;
@Component
public class InstB {
}
  • InstC 类
package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;
import org.springframework.stereotype.Component;
@Component
public class InstC {
}
  • 启动类
import com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans.InstA;
import com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.config.C075Config;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test075_BeanDefinition {
    @Test
    public void test01() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(C075Config.class);
        InstA instA = (InstA) ac.getBean("instA");

        System.out.println(instA);
    }
}

2.2 BeanDefinition的class属性

  • 利用BeanFactoryPostProcessor修改InstA定义对象的class属性,代码如下:
package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.stereotype.Component;
@Component
public class NrscBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        GenericBeanDefinition instABeanDefinition =
                (GenericBeanDefinition)beanFactory.getBeanDefinition("instA");
        //修改InstA的class属性为InstC.class
        instABeanDefinition.setBeanClass(InstC.class);
    }
}

启动项目,会报如下错误,即启动类中的InstA instA = (InstA) ac.getBean("instA");语句报类型转换异常:
在这里插入图片描述

由此可知:BeanDefinition对象的class属性用来控制Bean实例化到底实例化成哪种类型的对象。


2.2 BeanDefinition的autowireMode属性

autowiedMode属性的取值有如下5个:
所在类:AutowireCapableBeanFactory

//默认值,表示如果A对象里要注入B对象必须显示的在B对象上标注@Autowired注解
int AUTOWIRE_NO = 0;

//通过bean的名字注入对象
int AUTOWIRE_BY_NAME = 1;

//通过类型注入对象
int AUTOWIRE_BY_TYPE = 2;

//通过构造函数注入
int AUTOWIRE_CONSTRUCTOR = 3;

/** //废弃掉的
 * Constant that indicates determining an appropriate autowire strategy
 * through introspection of the bean class.
 * @see #createBean
 * @see #autowire
 * @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
 * prefer annotation-based autowiring for clearer demarcation of autowiring needs.
 */
@Deprecated
int AUTOWIRE_AUTODETECT = 4;

开发中要想在InstA中注入InstB,我们肯定会在InstA中的InstB属性上加上@Autowired注解,如2.1中的InstA类所示。
但能不能不加呢??? 其实是可以的,即用BeanFactoryPostProcessor来修改InstA定义对象的自动注入模型属性 — autowireMode。

  • 利用BeanFactoryPostProcessor修改InstA定义对象的autowireMode属性,代码如下:
@Component
@Component
public class NrscBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        GenericBeanDefinition instABeanDefinition =
                (GenericBeanDefinition)beanFactory.getBeanDefinition("instA");
        //class属性:用来控制Bean实例化到底实例化成哪种类型的对象。
        //instABeanDefinition.setBeanClass(InstC.class);

        //autowireMode属性:控制Bean属性的注入方式
        //instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
        //instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    }
}

启动项目,可以看到即使在InstA中的InstB属性上不加@Autowired属性,InstB也可以注入到InstA对象里:
在这里插入图片描述

由此可知: BeanDefinition对象的autowireMode属性会控制Bean属性的注入方式。


2.3 BeanDefinition的constructorArgumentValues属性

2.3.1 constructorArgumentValues中的addIndexedArgumentValue

有如下测试类 — InstD:

package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;
import org.springframework.stereotype.Component;
@Component
public class InstD {

    private String username;

    private Integer age;

    public InstD() {
        System.out.println("调用D的无参构造创建D对象");
    }

    public InstD(String username) {
        this.username = username;
        System.out.println("调用D的有参构造创建D对象====username");
    }

    public InstD(Integer age) {
        this.age = age;
        System.out.println("调用D的有参构造创建D对象====age");
    }

    public InstD(String username, Integer age) {
        this.username = username;
        this.age = age;
        System.out.println("调用D的有参构造创建D对象====username+age");
    }

    public InstD(Integer age, String username) {
        this.age = age;
        this.username = username;
        System.out.println("调用D的有参构造创建D对象====age+username");
    }
}

在一般情况下,spring肯定会调用InstD对象的无参构造方法来创建该对象,那有没有可能不调用其构造方法,而调用上面任意一个构造方法来实现D对象的创建呢?其实是可以的,即用BeanFactoryPostProcessor来修改InstA定义对象的constructorArgumentValues属性。

  • 利用BeanFactoryPostProcessor修改InstD定义对象的constructorArgumentValues属性的代码如下:
@Component
public class NrscBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        //GenericBeanDefinition instABeanDefinition =
        //        (GenericBeanDefinition)beanFactory.getBeanDefinition("instA");
        //class属性:用来控制Bean实例化到底实例化成哪种类型的对象。
        //instABeanDefinition.setBeanClass(InstC.class);

        //autowireMode属性:控制Bean属性的注入方式
        //instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
        //instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        //instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);

        GenericBeanDefinition instDBeanDefinition =
                (GenericBeanDefinition)beanFactory.getBeanDefinition("instD");
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        //表示构造函数的第一个参数的类型为12的类型 ---> 即int型 ---》spring会进行类型推断
        constructorArgumentValues.addIndexedArgumentValue(0,12);
        //表示构造函数的第二个参数的类型为"haha"的类型 ---> 即String类型
        constructorArgumentValues.addIndexedArgumentValue(1,"haha");
		//将instD的定义对象的constructorArgumentValues属性设置为自己定义的类型
        instDBeanDefinition.setConstructorArgumentValues(constructorArgumentValues);
    }
}

启动项目,可以看到创建InstD对象时调用的并不是无参构造函数,而是我指定的第一个参数类型为int/Integrt型,第二个为String类型的构造函数。
在这里插入图片描述

由此可知: BeanDefinition对象中constructorArgumentValues的addIndexedArgumentValue方法会控制Bean创建时使用的构造函数。


2.3.1 constructorArgumentValues中的addGenericArgumentValue

有如下测试类InstE:

@Component
public class InstE {

    private Class aaa;

    public InstE(Class aaa) {
        System.err.println("==InstE调用有参构造==" + aaa);
    }
}

接下来看看对应的BeanFactoryPostProcessor测试类:

package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.stereotype.Component;

//测试ConstructorArgumentValues的addGenericArgumentValue方法
@Component
public class NrscBeanFactoryPostProcessor2 implements BeanFactoryPostProcessor {


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition instEBeanDefinition =
                (GenericBeanDefinition) beanFactory.getBeanDefinition("instE");

        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        //addGenericArgumentValue方法会在实例化当前BeanDefinition对象对应的bean时,给其属性赋值
        constructorArgumentValues.addGenericArgumentValue("com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans.InstB");

        instEBeanDefinition.setConstructorArgumentValues(constructorArgumentValues);
    }
}

启动项目,测试结果如下:
在这里插入图片描述

由此可知: 可以通过BeanDefinition对象中constructorArgumentValues的addGenericArgumentValue方法来为当前BeanDefinition对应的具体对象的属性赋值。


3 bean的生命周期总结

结合前面的文章加上本篇的内容对bean的生命周期再做一次总结,总结内容如下图所示:
在这里插入图片描述

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring的Bean生命周期是由Spring容器负责管理的,主要包括以下阶段: 1. 实例化:Spring容器根据配置信息或注解,创建Bean的实例。这一过程一般通过Java反射机制实现。 2. 属性注入:Spring容器将依赖注入到Bean的属性中,可以通过构造函数、Setter方法或者字段注入来实现。 3. 初始化:在Bean实例创建完成后,Spring容器会调用一系列的初始化回调方法。常见的初始化回调方法有`@PostConstruct`注解、`InitializingBean`接口的`afterPropertiesSet()`方法以及自定义的初始化方法。 4. 使用:Bean实例被使用,执行业务逻辑。 5. 销毁:当Bean不再被使用时,Spring容器会调用一系列的销毁回调方法。常见的销毁回调方法有`@PreDestroy`注解、`DisposableBean`接口的`destroy()`方法以及自定义的销毁方法。 下面是一个简化的示例代码,展示了Bean生命周期的常用方法: ```java public class MyBean implements InitializingBean, DisposableBean { private String name; public MyBean() { System.out.println("Bean实例化"); } public void setName(String name) { this.name = name; } @Override public void afterPropertiesSet() throws Exception { System.out.println("初始化回调方法"); } public void doSomething() { System.out.println("执行业务逻辑:" + name); } @Override public void destroy() throws Exception { System.out.println("销毁回调方法"); } } ``` 此外,Spring还提供了更细粒度的扩展点,如BeanPostProcessor接口和BeanFactoryPostProcessor接口,可以在Bean的实例化和初始化过程中进行自定义处理。 以上是一个简单的概述,实际的源码分析涉及到Spring框架的很多细节,包括BeanDefinitionBeanFactory、ApplicationContext等。你可以参考Spring源码来深入了解Bean生命周期的实现细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值