Spring中Bean的加载与SpringBoot的初始化流程详解

目录

  • 前言
  • 第一章 Spring中Bean的一些简单概念
    • 1.1 SpingIOC简介
    • 1.2 BeanFactory
      • 1.2.1 BeanDefinition
      • 1.2.2 BeanDefinitionRegistry
      • 1.2.3 BeanFactory结构图
    • 1.3 ApplicationContext
    • 第二章 SpringBoot的初始化流程
      • 2.1 准备阶段
        • 2.2 运行阶段
          • 2.2.1 监听器分析
          • 2.2.2 refreshContext
        • 2.3 总结

        前言 一直对它们之间的关系感到好奇,SpringBoot既然是Spring的封装,那么SpringBoot在初始化时应该也会有Bean的加载,那么是在何时进行加载的呢?

        第一章 Spring中Bean的一些简单概念
        1.1 SpingIOC简介
        Spring启动时去读取应用程序提供的Bean配置信息,并在Spring容器中生成相应的Bean定义注册表,然后根据注册表去实例化Bean,装配好Bean之间的依赖关系,为上层提供准备就绪的运行环境.
        Spring提供一个配置文件描述Bean与Bean之间的依赖关系,利用Java语言的反射功能实例化Bean,并建立Bean之间的依赖关系.
        Spring中Bean的加载与SpringBoot的初始化流程详解
        文章图片


        1.2 BeanFactory
        BeanFactory是接口,提供了IOC容器最基本的形式,给具体的IOC容器的实现提供了规范。

        1.2.1 BeanDefinition 主要用来描述Bean的定义,Spring在启动时会将Xml或者注解里Bean的定义解析成Spring内部的BeanDefinition.
        beanClass保存bean的class属性,scop保存bean是否单例,abstractFlag保存该bean是否抽象,lazyInit保存是否延迟初始化,autowireMode保存是否自动装配,等等等
        public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessorimplements BeanDefinition, Cloneable {private volatile Object beanClass; private String scope = SCOPE_DEFAULT; private boolean abstractFlag = false; private boolean lazyInit = false; private int autowireMode = AUTOWIRE_NO; private int dependencyCheck = DEPENDENCY_CHECK_NONE; private String[] dependsOn; private ConstructorArgumentValues constructorArgumentValues; private MutablePropertyValues propertyValues; private String factoryBeanName; private String factoryMethodName; private String initMethodName; private String destroyMethodName; }


        1.2.2 BeanDefinitionRegistry registerBeanDefinition方法主要是将BeanDefinition注册到BeanFactory接口的实现类DefaultListableBeanFacory中的beanDefinitionMap中。
        private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);


        1.2.3 BeanFactory结构图 ListableBeanFactory该接口定义了访问容器中Bean的若干方法,如查看Bean的个数,获取某一类型Bean的配置名,查看容器中是否包括某一Bean等方法.
        HierarchicalBeanFactory是父子级联的IOC容器接口,子容器可以通过接口方法访问父容器,通过HierarchicalBeanFactory接口SpringIOC可以建立父子层级关联的IOC层级体系,子容器可以访问父容器的Bean,父容器不能访问子容器的Bean,比如展现层的Bean位于子容器中而业务层和持久层的Bean位于父容器的Bean.
        • ConfigurableBeanFactory:增强了IOC接口的可定制性,定义了设置类装载器,属性遍历器,以及属性初始化后置处理器等方法.
        • AutowireCapableBeanFactory:定义了将容器中的Bean按某种规则,按名字匹配,按类型匹配等.
        • SingletonBeanRegistry:允许在运行期间向容器注册SingletonBean实例的方法.
        通过这些接口也证明了BeanFactory的体系也确实提供了IOC的基础及依赖注入和Bean的装载等功能.
        Spring中Bean的加载与SpringBoot的初始化流程详解
        文章图片


        1.3 ApplicationContext
        由于BeanFactory的功能还不够强大,于是Spring在BeanFactory的基础上还设计了一个更为高级的接口即ApplicationContext,它是BeanFactory的子接口之一.在我们使用SpringIOC容器时,大部分都是context的实现类。
        Spring中Bean的加载与SpringBoot的初始化流程详解
        文章图片

        我理解着就是BeanFactory只提供IOC,ApplicationContext还提供很多别的功能。
        Spring中Bean的加载与SpringBoot的初始化流程详解
        文章图片


        第二章 SpringBoot的初始化流程
        @SpringBootApplicationpublic class RepApplication { public static void main(String[] args) {//要理解的SpringApplication SpringApplication.run(RepApplication.class, args); }}

        SpringApplication的run分为两个阶段,即new SpringApplication()时的执行构造函数的准备阶段,和run时的运行阶段。
        public static ConfigurableApplicationContext run(Class[] primarySources,String[] args) {return new SpringApplication(primarySources).run(args); }


        2.1 准备阶段
        在准备阶段会
        配置SpringBean的来源
        推断web应用类型
        加载应用上下文初始器
        加载应用事件监听器
        public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //推断web应用类型this.webApplicationType = WebApplicationType.deduceFromClasspath(); //加载应用上下文初始化器setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //加载应用事件监听器setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }


        2.2 运行阶段
        • 加载:SpringApplication获得监听器
        • 运行:SpringApplication运行监听器
        • 监听:SpringBoot事件、Spring事件
        • 创建:应用上下文、Enviroment、其它(不重要),应用上下文创建后会被应用上下文初始化器初始化,Enviroment是抽象的环境对象。
        • 失败:故障分析报告。
        • 回调:CommandLineRunner、ApplicationRunner
        public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //获得监听器SpringApplicationRunListeners listeners = getRunListeners(args); //运行监听器listeners.starting(); try {//应用上下文ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //环境ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); //依据不同的配置加载不同的ApplicationContextcontext = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments,printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); }listeners.started(context); callRunners(context, applicationArguments); }catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try {listeners.running(context); }catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); }return context; }


        2.2.1 监听器分析 这个是看了源码后的个人理解,不保证一定正确,只提供一定的参考。
        在准备阶段加载实现了ApplicationListener的监听器。
        Spring中Bean的加载与SpringBoot的初始化流程详解
        文章图片

        然后在运行阶段调用了starting()方法。
        //获得监听器SpringApplicationRunListeners listeners = getRunListeners(args); //运行监听器listeners.starting();

        下面是listeners的源码,可以看到它的每一个方法,都对应SpringBoot的一个阶段,这表明每到对应的阶段,都要广播对应的事件。
        Spring中Bean的加载与SpringBoot的初始化流程详解
        文章图片

        class SpringApplicationRunListeners { private final Log log; private final List listeners; SpringApplicationRunListeners(Log log,Collection listeners) {this.log = log; this.listeners = new ArrayList<>(listeners); } public void starting() {for (SpringApplicationRunListener listener : this.listeners) {listener.starting(); } } public void environmentPrepared(ConfigurableEnvironment environment) {for (SpringApplicationRunListener listener : this.listeners) {listener.environmentPrepared(environment); } } public void contextPrepared(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {listener.contextPrepared(context); } } public void contextLoaded(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {listener.contextLoaded(context); } } public void started(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {listener.started(context); } } //省略... }

        那么问题来了,广播事件后,事件是怎么被监听到的呢?我们打开listener.environmentPrepared(environment)的源码,发现其调用了initialMulticaster进行了事件广播
        @Override public void environmentPrepared(ConfigurableEnvironment environment) {this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); }

        广播的代码如下,看了一下感觉大意就是找到根事件匹配的监听器,然后调用线程池去执行对应的触发函数。
        @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (final ApplicationListener listener : getApplicationListeners(event, type)) {Executor executor = getTaskExecutor(); if (executor != null) {executor.execute(() -> invokeListener(listener, event)); }else {invokeListener(listener, event); }} }


        2.2.2 refreshContext 再往下最核心的是refreshContext方法,一直点进去可以看到如下:
        • prepareRefresh:完成配置之类的解析,设置Spring的状态,初始化属性源信息,验证环境信息中必须存在的属性.
        • ConfigurableListableBeanFactory:是用来获取beanFactory的实例的(第一张也写过BeanFactory负责bean的加载与获取)。
        • PrepareBeanFactory:对beanFactory进行相关的设置,为后续的使用做准备,包括设置classLoader用来加载Bean,设置表达式解析器等等.
        • postProcessBeanFactory:是用于在BeanFactory设置之后进行后续的BeanFactory的操作.
        • invokeBeanFactoryPostProcessors:点进去发现调用了如下方法,点进去之后发现逻辑相当复杂,主要调用工厂后处理器,调用Bean标签,扫描Bean文件,并解析成一个个的Bean,这时候这些Bean是被加载进了Spirng容器当中,这里涉及了各种类,我们在这里主要说一下ConfigurationClassParser,主要是解析Bean的类.该方法会对带有@configuration,@import,@bean,以及@SpringBootApplication等标签的Bean进行解析,
        • registerBeanPostProcessors:会从Spring容器中找出实现BeanPostProcessors接口的Bean,并设置到BeanFactory的属性之中,之后Bean实例化时会调用BeanProcessor,也就是Bean的后置处理器.会和AOP比较相关.
        • initMessageSource:初始化消息源(这个自己推断的)
        • initApplicationEventMuticaster:初始化事件广播器
        • onRefresh:是一个模板方法,不同的Spring容器会重写它做不同的事情.比如web程序的容器,会调用create..方法去创建内置的servlet容器.
        • registerListeners:注册事件监听器
        • finishBeanFactoryInitialization:会实例化BeanFactory中已被注册但未被实例化的所有实例,懒加载是不需要被实例化的.前面的invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的Bean在这个时候都会被初始化,同时初始化过程中的各种PostProcessor就会开始起作用了.
        • finishRefresh:会做初始化生命周期处理器相关的事情.
        @Override public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh(); // Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory); try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory); // Initialize message source for this context.initMessageSource(); // Initialize event multicaster for this context.initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.onRefresh(); // Check for listener beans and register them.registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.finishRefresh(); } catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources.destroyBeans(); // Reset 'active' flag.cancelRefresh(ex); // Propagate exception to caller.throw ex; } finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches(); }} }

        当上面的代码执行完毕后,返回上层代码,后面是注册钩子,这钩子是希望开发者能结合自己的实际需求扩展出一些在Spring容器关闭时的行为.
        private void refreshContext(ConfigurableApplicationContext context) {refresh(context); if (this.registerShutdownHook) {try {context.registerShutdownHook(); }catch (AccessControlException ex) {// Not allowed in some environments.}} }

        继续返回上层代码,可以看到afterRefresh方法它的方法体是空的, 也就说明Spring框架考虑了扩展性,留了很多的口子,让大家在框架层面继承很多的模块并去做自定义的实现
        protected void afterRefresh(ConfigurableApplicationContext context,ApplicationArguments args) { }


        2.3 总结
        总的来说,SpringBoot加载的Bean的时机为,点进一开始的run方法,层层递进后,由
        refreshContext(context);

        进行了bean的加载,更详细的话,那就层层递进点进去,是在如下方法进行了bean的加载。
        invokeBeanFactoryPostProcessors(beanFactory);

        【Spring中Bean的加载与SpringBoot的初始化流程详解】以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

          推荐阅读