【Spring Bean生命周期】小白也能看懂的入门篇

1,199 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天 Spring Bean生命周期,从入门到进阶:
【Spring Bean生命周期】小白也能看懂的入门篇
【Spring Bean生命周期】高手如何解读源码(一) - 资源的扫描和注册
【Spring Bean生命周期】高手如何解读源码(二) - Bean的生命周期

1 Spring Bean的生命周期

  理解Spring的生命周期,千万不要一上来就去看源码,抠细节。Spring发展这么多年,已经增加了越来越多的特性,很容易就迷失在代码细节里。越复杂的东西,就越要学会抓大放小,比如:Spring的生命周期通俗点理解就是:

  Spring初始化一个IOC容器,然后加载XML中描述的 或 含有指定注解的Java类,将其统一用Resource表示,并扫描为BeanDefinition,然后注册到IOC容器中,最后完成实例化和属性注入。如图所示:

image.png 整个过程可以分解成下面几个核心动作。

2 核心动作及组件

2.1 首先构造一个IOC容器

BeanFactory 接口定义了IOC容器最基本的形式。主要方法是getBean(),其中DefaultListableBeanFactory这个是核心实现类,实现了大部分方法。

ApplicationContext Spring提供的高级形态的IOC容器。在BeanFactory的基础上提供了其他功能:

  • 支持不同的信息源。扩展了MessageSource接口
  • 访问资源。实现ResourceLoader 和 Resource等
  • 支持应用事件

2.2 获取资源

Spring通过扫描Xml文件 或 被注解的类,将这些统一用Resource表示。其中,核心组件包括:

Resource 表示的是各种资源。包括了各种Bean描述文件,如java类中@Bean @Service和@Controller的定义,又比如xml中标签的定义,groovy中bean的定义等等。这些够可以统称为资源。

ResourceLoader这些资源都是在classpath下面的,只要通过ClassLoader我们可以扫描到和加载到的。

Reader/Scanner Reader解析不同的文件,如xmlReader,groovyReader等,Scanner解析通过注解定义的Bean,并且解析识别。

image.png

2.3 Bean的元数据注册到容器中

BeanDefinition 表示了其实就是我们交给对Spring容器管理对象,也就是Bean的描述和定义,比如这个Bean是否是单例、是否有依赖其他Bean、依赖了那些Bean、Bean的名称、别名等等。与class的区别是,class描述的是类的信息,而BeanDefinition描述的是实例的信息。针对不同的资源,有不同的实现类:

接口:
● BeanDefinition: Bean的实例的基础定义。
● AnnotatedBeanDefinition:该接口扩展了 BeanDefinition 的功能,其用来操作注解元数据
实现类:
● AbstractBeanDefinition:抽象类,接口BeanDefinition的主要实现类
● RootBeanDefinition:该类继承自 AbstractBeanDefinition,它可以单独作为一个 BeanDefinition,也可以作为其他 BeanDefinition 的父类。
● ChildBeanDefinition:不可以单独存在,必须依赖一个父 BeanDetintion,构造 ChildBeanDefinition 时,通过构造方法传入父 BeanDetintion 的名称或通过 setParentName 设置父名称。
● GenericBeanDefinition:是 Spring 2.5 以后新引入的 BeanDefinition,是 ChildBeanDefinition 更好的替代者,它同样可以通过 setParentName 方法设置父 BeanDefinition,既可以单独使用也可以作为子BeanDefinition使用。
● ConfigurationClassBeanDefinition:该类继承自 RootBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个BeanDefinition用来描述加了@Bean 注解的Bean
● AnnotatedGenericBeanDefinition:该类继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述标注 @Configuration 注解的 Bean。
● ScannedGenericBeanDefinition:该类继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述标注 @Component 注解的 Bean,其派生注解如 @Service@Controller 也同理。
      

BeanDefinitionRegistry 封装了对BeanDefinition常见操作的接口,容器默认实现了这个接口,所以一般它也代表了容器。容器BeanFactory实现了它,可以通过实现的方法,维护List。

InternalBean 其实是指Spring自己注入的相关Bean,用来实现SpringBoot的核心功能的。

2.4 Bean的实例化及前后扩展

Spring提供了很多的扩展点,可以满足第三方框架的自定义需求,何为扩展点?具体点就是:只预留接口,不做具体的功能,子类去实现。比如:

public abstract class CustomIoc {

    public void getBean(){
        // 获取Bean前的扩展点,只定义不实现,由子类实现
        beforeGetBean();
        doGetBean();
    }
    //    do nothing
    public abstract void beforeGetBean();
    private void doGetBean() {}
}

我可以针对获取Bean的方式不同,分别写自己的实现类,比如定义一个XmlCustomIoc类 和 AnnotationCustomIoc类,

class XmlCustomIoc extends CustomIoc{

    @Override
    public void beforeGetBean() {
      //   Xml扫描,获取资源
    }
}

class AnnotationCustomIoc extends CustomIoc{

    @Override
    public void beforeGetBean() {
     //   注解扫描,获取资源
    }
}

这样在调用CustomIoc的getBean()方法时,实例子类的beforeGetBean()就会被执行到。 同样的,Spring也预留了扩展点,其中比较核心的两个是BeanFactorypostProcessBeanpostProcess。可以在子类中去实现这些方法。

BeanFactorypostProcess接口

实现该接口,可以在spring的bean创建之前,修改bean的定义属性。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如:

  • 可以把bean的scope从singleton改为prototype,
  • 也可以把property的值给修改掉。
  • 还可以增加或者修改BeanDefinition信息。
    注意:BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。
    接口方法的入参是ConfigurableListableBeanFactory,使用该参数,可以获取到相关bean的定义信息。
@Component
public class FactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("******调用了BeanFactoryPostProcessor");
        String[] beanStr = beanFactory.getBeanDefinitionNames();
        for (String beanName : beanStr) {
            if ("person".equals(beanName)) {
                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
                MutablePropertyValues m = beanDefinition.getPropertyValues();
                if (m.contains("name")) {
                    m.addPropertyValue("name", "赵四");
                    System.out.println("修改了name属性初始值了");
                }
            }
        }
    }

}

BeanPostProcessor接口

这个接口是单独成类的,它的作用范围是Spring容器,一旦在容器中配置了这个类,那么该容器中所有bean在初始化 的前后都会调用这个接口对应的方法。

@Component
public class PostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后置处理器处理bean=【"+beanName+"】开始");
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后置处理器处理bean=【"+beanName+"】完毕!");
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return bean;
    }
}

经过以上四步之后,基本实现了最简单的Spring的IOC容器中BeanDefinition注册,具体可参考下图:

image.png