Spring中Bean的加载与SpringBoot的初始化流程详解
前言
一直对它们之间的关系感到好奇,SpringBoot既然是Spring的封装,那么SpringBoot在初始化时应该也会有Bean的加载,那么是在何时进行加载的呢?
第一章 Spring中Bean的一些简单概念
1.1 SpingIOC简介
Spring启动时去读取应用程序提供的Bean配置信息,并在Spring容器中生成相应的Bean定义注册表,然后根据注册表去实例化Bean,装配好Bean之间的依赖关系,为上层提供准备就绪的运行环境.
Spring提供一个配置文件描述Bean与Bean之间的依赖关系,利用java语言的反射功能实例化Bean,并建立Bean之间的依赖关系.
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保存是否自动装配,等等等
1 public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor 2 implements BeanDefinition, Cloneable { 3 private volatile Object beanClass; 4 private String scope = SCOPE_DEFAULT; 5 private boolean abstractFlag = false; 6 private boolean lazyInit = false; 7 private int autowireMode = AUTOWIRE_NO; 8 private int dependencyCheck = DEPENDENCY_CHECK_NONE; 9 private String[] dependsOn; 10 private ConstructorArgumentValues constructorArgumentValues; 11 private MutablePropertyValues propertyValues; 12 private String factoryBeanName; 13 private String factoryMethodName; 14 private String initMethodName; 15 private String destroyMethodName; 16 }
1.2.2 BeanDefinitionRegistry
registerBeanDefinition方法主要是将BeanDefinition注册到BeanFactory接口的实现类DefaultListableBeanFacory中的beanDefinitionMap中。
1 private final Map<String, BeanDefinition> 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的装载等功能.
1.3 ApplicationContext
由于BeanFactory的功能还不够强大,于是Spring在BeanFactory的基础上还设计了一个更为高级的接口即ApplicationContext,它是BeanFactory的子接口之一.在我们使用SpringIOC容器时,大部分都是context的实现类。
我理解着就是BeanFactory只提供IOC,ApplicationContext还提供很多别的功能。
第二章 SpringBoot的初始化流程
1 @SpringBootApplication 2 public class RepApplication { 3 public static void main(String[] args) { 4 //要理解的SpringApplication 5 SpringApplication.run(RepApplication.class, args); 6 } 7 }
SpringApplication的run分为两个阶段,即new SpringApplication()时的执行构造函数的准备阶段,和run时的运行阶段。
1 public static ConfigurableApplicationContext run(Class<?>[] primarySources, 2 String[] args) { 3 return new SpringApplication(primarySources).run(args); 4 }
2.1 准备阶段
在准备阶段会:
- 配置SpringBean的来源
- 推断web应用类型
- 加载应用上下文初始器
- 加载应用事件监听器
1 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 2 this.resourceLoader = resourceLoader; 3 Assert.notNull(primarySources, "PrimarySources must not be null"); 4 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 5 //推断web应用类型 6 this.webApplicationType = WebApplicationType.deduceFromClasspath(); 7 //加载应用上下文初始化器 8 setInitializers((Collection) getSpringFactoriesInstances( 9 ApplicationContextInitializer.class)); 10 //加载应用事件监听器 11 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 12 this.mainApplicationClass = deduceMainApplicationClass(); 13 }
2.2 运行阶段
- 加载:SpringApplication获得监听器
- 运行:SpringApplication运行监听器
- 监听:SpringBoot事件、Spring事件
- 创建:应用上下文、Enviroment、其它(不重要),应用上下文创建后会被应用上下文初始化器初始化,Enviroment是抽象的环境对象。
- 失败:故障分析报告。
- 回调:CommandLineRunner、ApplicationRunner
1 public ConfigurableApplicationContext run(String... args) { 2 StopWatch stopWatch = new StopWatch(); 3 stopWatch.start(); 4 ConfigurableApplicationContext context = null; 5 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); 6 configureHeadlessProperty(); 7 //获得监听器 8 SpringApplicationRunListeners listeners = getRunListeners(args); 9 //运行监听器 10 listeners.starting(); 11 try { 12 //应用上下文 13 ApplicationArguments applicationArguments = new DefaultApplicationArguments( 14 args); 15 //环境 16 ConfigurableEnvironment environment = prepareEnvironment(listeners, 17 applicationArguments); 18 configureIgnoreBeanInfo(environment); 19 Banner printedBanner = printBanner(environment); 20 //依据不同的配置加载不同的ApplicationContext 21 context = createApplicationContext(); 22 exceptionReporters = getSpringFactoriesInstances( 23 SpringBootExceptionReporter.class, 24 new Class[] { ConfigurableApplicationContext.class }, context); 25 prepareContext(context, environment, listeners, applicationArguments, 26 printedBanner); 27 refreshContext(context); 28 afterRefresh(context, applicationArguments); 29 stopWatch.stop(); 30 if (this.logStartupInfo) { 31 new StartupInfoLogger(this.mainApplicationClass) 32 .logStarted(getApplicationLog(), stopWatch); 33 } 34 listeners.started(context); 35 callRunners(context, applicationArguments); 36 } 37 catch (Throwable ex) { 38 handleRunFailure(context, ex, exceptionReporters, listeners); 39 throw new IllegalStateException(ex); 40 } 41 42 try { 43 listeners.running(context); 44 } 45 catch (Throwable ex) { 46 handleRunFailure(context, ex, exceptionReporters, null); 47 throw new IllegalStateException(ex); 48 } 49 return context; 50 }
2.2.1 监听器分析
这个是看了源码后的个人理解,不保证一定正确,只提供一定的参考。
在准备阶段加载实现了ApplicationListener的监听器。
然后在运行阶段调用了starting()方法。
1 //获得监听器 2 SpringApplicationRunListeners listeners = getRunListeners(args); 3 //运行监听器 4 listeners.starting();
下面是listeners的源码,可以看到它的每一个方法,都对应SpringBoot的一个阶段,这表明每到对应的阶段,都要广播对应的事件。
1 class SpringApplicationRunListeners { 2 private final Log log; 3 private final List<SpringApplicationRunListener> listeners; 4 SpringApplicationRunListeners(Log log, 5 Collection<? extends SpringApplicationRunListener> listeners) { 6 this.log = log; 7 this.listeners = new ArrayList<>(listeners); 8 } 9 10 public void starting() { 11 for (SpringApplicationRunListener listener : this.listeners) { 12 listener.starting(); 13 } 14 } 15 16 public void environmentPrepared(ConfigurableEnvironment environment) { 17 for (SpringApplicationRunListener listener : this.listeners) { 18 listener.environmentPrepared(environment); 19 } 20 } 21 22 public void contextPrepared(ConfigurableApplicationContext context) { 23 for (SpringApplicationRunListener listener : this.listeners) { 24 listener.contextPrepared(context); 25 } 26 } 27 28 public void contextLoaded(ConfigurableApplicationContext context) { 29 for (SpringApplicationRunListener listener : this.listeners) { 30 listener.contextLoaded(context); 31 } 32 } 33 34 public void started(ConfigurableApplicationContext context) { 35 for (SpringApplicationRunListener listener : this.listeners) { 36 listener.started(context); 37 } 38 } 39 //省略... 40 }
那么问题来了,广播事件后,事件是怎么被监听到的呢?我们打开listener.environmentPrepared(environment)的源码,发现其调用了initialMulticaster进行了事件广播
1 @Override 2 public void environmentPrepared(ConfigurableEnvironment environment) { 3 this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( 4 this.application, this.args, environment)); 5 }
广播的代码如下,看了一下感觉大意就是找到根事件匹配的监听器,然后调用线程池去执行对应的触发函数。
1 @Override 2 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { 3 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); 4 for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { 5 Executor executor = getTaskExecutor(); 6 if (executor != null) { 7 executor.execute(() -> invokeListener(listener, event)); 8 } 9 else { 10 invokeListener(listener, event); 11 } 12 } 13 }
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
:会做初始化生命周期处理器相关的事情.
1 @Override 2 public void refresh() throws BeansException, IllegalStateException { 3 synchronized (this.startupShutdownMonitor) { 4 // Prepare this context for refreshing. 5 prepareRefresh(); 6 7 // Tell the subclass to refresh the internal bean factory. 8 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 9 10 // Prepare the bean factory for use in this context. 11 prepareBeanFactory(beanFactory); 12 13 try { 14 // Allows post-processing of the bean factory in context subclasses. 15 postProcessBeanFactory(beanFactory); 16 17 // Invoke factory processors registered as beans in the context. 18 invokeBeanFactoryPostProcessors(beanFactory); 19 20 // Register bean processors that intercept bean creation. 21 registerBeanPostProcessors(beanFactory); 22 23 // Initialize message source for this context. 24 initMessageSource(); 25 26 // Initialize event multicaster for this context. 27 initApplicationEventMulticaster(); 28 29 // Initialize other special beans in specific context subclasses. 30 onRefresh(); 31 32 // Check for listener beans and register them. 33 registerListeners(); 34 35 // Instantiate all remaining (non-lazy-init) singletons. 36 finishBeanFactoryInitialization(beanFactory); 37 38 // Last step: publish corresponding event. 39 finishRefresh(); 40 } 41 42 catch (BeansException ex) { 43 if (logger.isWarnEnabled()) { 44 logger.warn("Exception encountered during context initialization - " + 45 "cancelling refresh attempt: " + ex); 46 } 47 48 // Destroy already created singletons to avoid dangling resources. 49 destroyBeans(); 50 51 // Reset 'active' flag. 52 cancelRefresh(ex); 53 54 // Propagate exception to caller. 55 throw ex; 56 } 57 58 finally { 59 // Reset common introspection caches in Spring's core, since we 60 // might not ever need metadata for singleton beans anymore... 61 resetCommonCaches(); 62 } 63 } 64 }
当上面的代码执行完毕后,返回上层代码,后面是注册钩子,这钩子是希望开发者能结合自己的实际需求扩展出一些在Spring容器关闭时的行为.
1 private void refreshContext(ConfigurableApplicationContext context) { 2 refresh(context); 3 if (this.registerShutdownHook) { 4 try { 5 context.registerShutdownHook(); 6 } 7 catch (AccessControlException ex) { 8 // Not allowed in some environments. 9 } 10 } 11 }
继续返回上层代码,可以看到afterRefresh方法它的方法体是空的, 也就说明Spring框架考虑了扩展性,留了很多的口子,让大家在框架层面继承很多的模块并去做自定义的实现
1 protected void afterRefresh(ConfigurableApplicationContext context, 2 ApplicationArguments args) { 3 }
2.3 总结
总的来说,SpringBoot加载的Bean的时机为,点进一开始的run方法,层层递进后,由
1 refreshContext(context);
进行了bean的加载,更详细的话,那就层层递进点进去,是在如下方法进行了bean的加载。
1 invokeBeanFactoryPostProcessors(beanFactory);
相关文章
- Spring基础-09-事务
- 【Spring Boot】Spring Boot之两种引入spring boot maven依赖的方式
- spring boot: spring Aware的目的是为了让Bean获得Spring容器的服务
- Spring Boot 2.x :通过 spring-boot-starter-hbase 集成 HBase
- [Spring boot] Read values from external properties file
- spring boot:thymeleaf给fragment传递参数的方法(spring boot 2.3.3)
- RabbitMQ使用及与spring boot整合
- Spring、SpringMVC和Springboot的区别(网摘)
- 深入理解Spring Redis的使用 (三)、使用RedisTemplate的操作类访问Redis
- [Java Spring] Testing a view controller
- Java Spring Beans.xml里的Bean定义是如何被解析出来的
- 为什么Spring Boot推荐使用logback-spring.xml来替代logback.xml来配置logback日志的问题分析
- 【日志系统最全】Spring Cloud Sleuth使用ELK收集&;分析日志
- 《Springboot极简教程》使用Spring Boot, JPA, Mysql, ThymeLeaf,gradle, Kotlin快速构建一个CRUD Web App
- Springboot热部署,使用spring-boot-devtools、springloaded两种方式进行热部署
- Spring Cloud下基于OAUTH2认证授权的实现
- Spring读书笔记——bean加载
- 007-spring cache-缓存实现-02-springboot ehcahe2、ehcache3实现、springboot caffeine实现
- 009-Spring Boot 事件监听、监听器配置与方式、spring、Spring boot内置事件
- Spring面试题/SpringBoot面试题
- SpringBoot入门:第一个SpringBoot应用:SpringBoot的创建过程Hello Spring Boot!
- Spring Cloud Alibaba微服务第17章之消息服务
- 【Spring Boot】SpringBoot参数验证以及实现原理
- 【java】Spring Cloud--Feign 基本使用