Spring中Bean的加载与SpringBoot的初始化流程详解
作者:逆袭的小学生 发布时间:2022-12-18 05:02:18
前言
一直对它们之间的关系感到好奇,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保存是否自动装配,等等等
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements 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<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的初始化流程
@SpringBootApplication
public 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<SpringBootExceptionReporter> 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);
//依据不同的配置加载不同的ApplicationContext
context = 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的 * 。
然后在运行阶段调用了starting()方法。
//获得 *
SpringApplicationRunListeners listeners = getRunListeners(args);
//运行 *
listeners.starting();
下面是listeners的源码,可以看到它的每一个方法,都对应SpringBoot的一个阶段,这表明每到对应的阶段,都要广播对应的事件。
class SpringApplicationRunListeners {
private final Log log;
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log,
Collection<? extends SpringApplicationRunListener> 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);
来源:https://blog.csdn.net/q610376681/article/details/108740943


猜你喜欢
- ShardingSphere的路由引擎类型本篇文章源码基于4.0.1版本上篇文章我们了解到了ShardingSphere在路由流程过程中,根
- 当我们需要在Unity客户端做一个限制功能,比如按钮 (最好是发送验证码按钮)要求每天只能点击三次,等到第二天又有三次机会,这个过程不涉及到
- Android开发之设置开机自动启动的几种方法方法一:<!-- 开机启动 --> <receiver android:na
- 一、概述在软件开发中,有些对象由于创建成本高、访问时需要与其它进程交互等原因,直接访问会造成系统速度慢、复杂度增大等问题。这时可以使用代理模
- 自定义控件(类似按钮等)的使用,自定义一个SurfaceView。 如某一块的动态图(自定义相应),或者类似UC浏览器下面的工具栏。 如下图
- 环境:springcloud Hoxton.SR11本节主要了解系统中的谓词与配置的路由信息是如何进行初始化关联生成路由对象的。每个谓词工厂
- ClassLoader概念我们知道,Java源文件(.java)经过编译器编译之后,会转换成Java字节码(.class),然而程序是如何加
- Android 资源文件夹 Layout 文件夹Layout文件是存放Android的布局文件的资源文件夹,但是如果你想要在里面创建子文件夹
- 1.Object类的equals()方法:比较两个对象是否是同一个对象,equals() 方法比较两个对象,是判断两个对象引用指向的是同一个
- Android作为一个伟大的系统,自然提供了设置默认打开程序的实现.在这篇文章中,我会介绍如何在Android系统中设置默认的程序. 在设置
- 前言本章是关于Java数组的最全汇总,本篇为汇总上篇,主要讲了一维数组的相关内容。数组是最常见的一种数据结构,它是相同类型的用一个标识符封装
- 项目需求,一直用eclipse的我,也要改用IDEA了,一开始,很不习惯。经过几天的慢慢摸索和习惯之后,发现IDEA确实很好用。dark的界
- 1. 添加maven依赖包<dependency> <groupId>org.apache.calcit
- Java Map.values()方法获取Map集合中的所有键值对象Java 集合类中的 Map.values() 方法用来获取
- springboot和vue结合的方案网络上的主要有以下两种:1. 【不推荐】在html中直接使用script标签引入vue和一些常用的组件
- 先决条件先运行mongodb肯定是必须的,然后导入以下包:import com.mongodb.MongoClient;import com
- 本文实例讲述了C#判断密码强度的方法。分享给大家供大家参考。具体如下:1.关于密码强度密码强度有强弱之分,包含数字、字母和其他符号三者且长度
- 在java中常常会遇到这样一个问题,在实际应用中,总会碰到对List排序并过滤重复的问题,如果List中放的只是简单的String类型过滤s
- 本文实例讲述了C#使用foreach语句遍历堆栈(Stack)的方法。分享给大家供大家参考。具体如下:using System;using
- 上篇文章给大家介绍了新浪微博第三方登录界面上下拉伸图片之第三方开源PullToZoomListViewEx(一),需要了解的朋友可以点击了解