十年之重修SpringBoot启动&自动装载&Bean加载过程
wxin55 2025-05-11 19:03 3 浏览 0 评论
总结
Springboot的自动装载,完全是依赖Bean的自动注册,其中默认的规则,是把需要自动装载的bean全名称编辑在spring.factories(2.7之后的版本,还支持.imports文件)中,然后基于@EnableAutoConfiguration注解的@Import({
AutoConfigurationImportSelector.class})注册,通过
AutoConfigurationImportSelector类,扫描当前classpath下的所有spring.factories文件,完成对bean的扫描与注册,然后后续完全依托Spring对Bean的初始化即可。
在完成Bean的定义注册之后,会启动Bean的初始化,优先完成依赖Bean的初始化,如果出现环形依赖会直接报错,然后对Bean进行实例化,在初始化的后置处理器中,针对Bean会进行判定,是否需要进行代理,如果需要会返回一个代理的Bean。
SpringBoot启动
SpringBoot的启动过程,主要是设置服务启动监听、容器启动监、加载当前环境的配置文件,其中调用refreshContext刷新上下文,这里主要是启动Spring容器,以及创建webServer(Tomcat),在上下文启动成功之后,会回调启动webServer。
// 一切源于这个注解与main函数
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
/*************************** @SpringBootApplication注解 **********************/
// 标明这个是springboot配置类
@SpringBootConfiguration
// 自动装载配置类,springboot自动装载就是基于这个注解实现
// @Import({AutoConfigurationImportSelector.class})
@EnableAutoConfiguration
// 扫描bean路径,未配置则是Application.class所在包
@ComponentScan
public @interface SpringBootApplication {
}
// SpringApplication.run是一个静态方法,后边调用到这个run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 这里先初始化了一个SpringApplication对象,然后再次调用真正干活的run方法
return new SpringApplication(primarySources).run(args);
}
/************************************ 初始化SpringApplication ************************************/
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 目前资源加载器为null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 这里保存主源,会用于后边bean装载的扫描范围
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断服务类型,这里只有两种一个是响应式REACTIVE,一个是SERVLET
// 推断方式也很简单,就是通过Class.forName()去查找有没有例如MVC的DispatcherServlet等类似web服务的类
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 找到服务启动初始化bootstrapRegistryInitializers列表
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 找到应用上下文初始化ApplicationContextInitializer列表
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 找到应用监听器ApplicationListener列表
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
/**
1. getSpringFactoriesInstances:
这个方法会加载当前classpath下的所有META-INF/spring.factories文件
加载里边配置的自动装载的class全名,然后通过传参class类型进行过滤,
例如Mybatis的jar包下配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
再基于反射对class进行初始化实例。
2. BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationListener 这三个
都是初始化过程,用户可以实现接口之后自定义,springboot会在启动时一起执行。
**/
}
/************************************ run方法执行 ************************************/
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
// 这里创建默认的BootstrapContext,同时会执行在上边注册的BootstrapRegistryInitializer
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 获取注册的SpringApplicationRunListener监听器,同样是基于spring.factores注册,可以自定义
// 监听SpringApplication的启动、完成、失败等,会根据接口回调
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 运行环境准备,加载配置文件,广播环境准备事件等
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印启动时springboot横幅
Banner printedBanner = printBanner(environment);
// 创建一个application上线文,基于webApplicationType生成的一个
// AnnotationConfigServletWebServerApplicationContext
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 准备上下文环境,调用注册的ApplicationContextInitializer,注册一些bean
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 更新上下文环境,启动spring容器进行bean装载,启动tomcat,
refreshContext(context);
// 后置处理,目前为空,用户可以自定义
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
// 打印启动日志
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 容器启动完成广播
listeners.started(context, timeTakenToStartup);
// 调用实现ApplicationRunner CommandLineRunner 接口的类
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
// 广播 ready事件
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
SpringBoot自动载入
SpringBoot的自动载入,本质上还是Bean自动初始化的一个过程,其中自动装载,主要是依托Application.class上的@SpringBootApplication注解,里边包含了EnableAutoConfiguration注解,而该注解又引入了@Import({
AutoConfigurationImportSelector.class}),在装载时会扫描classpath下的所有spring.factories和*
.AutoConfiguration.imports文件,对里边申明的类进行装载注入。Bean的扫描、自动装载等会在
invokeBeanFactoryPostProcessors环节完成。在onRefresh环节,会创建tomcat实例,待容器启动完毕之后,基于lifecycle接口触发回调,启动webServer。
/************************** refresh上下文 **********************************/
// springboot在启动时完成配置加载、事件通知等,期间会调用refresh方法开始更新上下文
// 这里会启动spring容器加载bean,自动装载,启动tomcat
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 刷新准备工作,清空scanner缓存,修改上下文状态
prepareRefresh();
// 更新beanFactory相关属性
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 通过beanFactory的ignoreDependencyInterface忽略部分bean,registerResolvableDependency 手动注入bean,以及注册了一些bean
prepareBeanFactory(beanFactory);
try {
// 当前class是抽象类,这里允许上下文容器实现类设置一下beanFactory相关范围与限制
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 这里会根据上下文配置的扫描路径、配置类、自动装载等,把beanDefinition存入beanFactory
invokeBeanFactoryPostProcessors(beanFactory);
// 注册bean后置处理器
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// 这里由于是web服务,会createWebServer()创建一个tomcat
onRefresh();
// 注册监听器
registerListeners();
// 在这里对beanFactory里边的bean进行初始化,涉及bean的实例化,依赖注入,代理类生成
finishBeanFactoryInitialization(beanFactory);
// 结束刷新
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();
contextRefresh.end();
}
}
}
/************* bean装载invokeBeanFactoryPostProcessors ***************/
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
// 当前的bean工厂为 DefaultListableBeanFactory
if (beanFactory instanceof BeanDefinitionRegistry) {
// ... ...
// 省略查找beanDefinition的后置处理器,这里currentRegistryProcessors里边为ConfigurationClassPostProcessor
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
// ... ...
// 省略其他后置处理器场景
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
// ... ... 这里省略掉,需要处理bean的筛选,把当前阶段beanDefinitionHolder整理出来
// 这里边包含Application.class这个类的BeanDefinitionHolder
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 从candidates里边的beanHolder开始处理,比如applicaiton.class上边还有注解,进一步进行扫描装载
parser.parse(candidates);
parser.validate();
}
}
/*************************** 进行bean的扫描装载 ***********************************/
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 扫描注解bean,如果当前bean本身也是一个配置类,还会继续扫描,同时会扫描Import、@bean等
// 由于一个bean本身可能是配置类,触发另外的扫描范围,故里边会不停地递归扫
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 这里延迟加载,处理springboot自动装载bean,
this.deferredImportSelectorHandler.process();
}
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
// ......
// 这里省略对scanner扫描器的初始化,比如扫描“*.class”,忽略、包含,省略对basePackages扫描范围的确定
// 由于未定义,故默认以Application.class包路径以及以下包
// doScan进行包扫描,以及bean注册
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
/************* deferredImportSelectorHandler.process()自动装载 *************************/
// process()最后调用到此方法,
// 因为Application.class上的@EnableAutoConfiguration 通过import引入了 AutoConfigurationImportSelector
// 执行AutoConfigurationImportSelector.getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 这里会扫描classpath下所有spring.factories文件,对配置的进行bean进行加载,针对EnableAutoConfiguration配置的class进行自动装载
// 在springboot2.7之后,支持META-INF/spring/*.AutoConfiguration.imports 文件进行读取,自动装载里边的class
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.<String>removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
Spring Bean初始化
通过
invokeBeanFactoryPostProcessors完成bean的定义之后,在
finishBeanFactoryInitialization环节,对Bean进行初始化,主要是通过getBean方法,如果bean是工厂bean则返回工厂生产的bean对象。getBean时会先去缓存中获取,找不到就会进行bean创建,创建过程先进性属性填充流程,即先初始化依赖对象,然后初始化自身,在初始化的后置处理器流程中,会判定当前bean是否需要代理,如果需要则返回代理bean。
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
// 工厂bean的处理逻辑
}
}
else {
// 普通bean的处理逻辑
getBean(beanName);
}
}
}
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
// 如果当前bean实现了SmartInitializingSingleton接口,则在所有bean初始化完成之后,
// 调用afterSingletonsInstantiated方法
}
}
}
}
/********************** getbean最后调用doGetBean ******************************/
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 规范名称
String beanName = transformedBeanName(name);
Object beanInstance;
// 这里尝试从三级缓存里获取
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 如果缓存里不为空
// 返回bean实例,可以是当前对象,也可以是工厂bean生产的实例
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 缓存为空,需要构建bean
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 如果父工厂不为空,则先从父工厂查找
}
// ......
if (!typeCheckOnly) {
// 将bean添加到已创建或即将创建
markBeanAsCreated(beanName);
}
// ......
try {
// 针对当前bean注解dependsOn 进行初始化
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// 省略依赖注册过程,最后会递归调用doGetBean方法
}
}
// Create bean instance.
if (mbd.isSingleton()) {
// getSingleton获取到单例之后,会清除掉二、三级缓存
sharedInstance = getSingleton(beanName, () -> {
try {
// 进行单利模式的bean创建
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 省略其他模式如Prototype示例的创建流程
} catch (BeansException ex) {
// ......
} finally {
beanCreation.end();
}
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ......
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 这里填充bean,即针对autoware注入的依赖,会进行初始化
populateBean(beanName, mbd, instanceWrapper);
// 初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// ......
}
// ......
return exposedObject;
}
// 填充依赖属性
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// ......
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
// 根据属性后置处理器,进行递归初始化依赖bean处理
}
// ......
}
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// ......
try {
// 初始化bean回调
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
// ......
}
if (mbd == null || !mbd.isSynthetic()) {
// 基于后置处理器,对bean进行代理处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
不积小流,无以成江海!
- 上一篇:一些可以显著提高大型 Java 项目启动速度的尝试
- 下一篇:Monkey自动化测试
相关推荐
- Java框架 —— Spring简介
-
简介一般来说,Spring指的是SpringFramework,它提供了很多功能,例如:控制反转(IOC)、依赖注入(DI)、切面编程(AOP)、事务管理(TX)主要jar包org.sprin...
- Monkey自动化测试
-
Monkey1.通过Monkey程序模拟用户触摸屏幕、滑动Trackball、按键等操作来对设备上的程序进行压力测试,检测程序多久的时间会发生异常;2.Monkey主要用于Android的压力...
- 十年之重修SpringBoot启动&自动装载&Bean加载过程
-
总结Springboot的自动装载,完全是依赖Bean的自动注册,其中默认的规则,是把需要自动装载的bean全名称编辑在spring.factories(2.7之后的版本,还支持.imports文件)...
- 一些可以显著提高大型 Java 项目启动速度的尝试
-
我们线上的业务jar包基本上普遍比较庞大,动不动一个jar包上百M,启动时间在分钟级,拖慢了我们在故障时快速扩容的响应。于是做了一些分析,看看Java程序启动慢到底慢在哪里,如何去优化,...
- class 增量发包改造为 jar 包方式发布
-
大纲class增量发包介绍项目目录结构介绍jar包方式发布落地方案class增量发包介绍当前项目的迭代修复都是通过class增量包来发版本的将改动的代码class增量打包,如下图cla...
- Flink架构及其工作原理(很详细)
-
原文链接:https://www.cnblogs.com/code2one/p/10123112.html关键词:Flink架构、面试杀手锏!更多大数据架构、实战经验,欢迎关注【大数据与机器学习】,...
- 大促系统优化之应用启动速度优化实践
-
作者:京东零售宋维飞一、前言本文记录了在大促前针对SpringBoot应用启动速度过慢而采取的优化方案,主要介绍了如何定位启动速度慢的阻塞点,以及如何解决这些问题。希望可以帮助大家了解如何定位该类问...
- Maven工程如何使用非Maven仓库jar包
-
使用Maven之前,一直都是自己手工在网上搜索需要的jar包,然后添加到工程中。以这样的方式开发,工作了好多年,曾经以为以后也会一直这样下去。直到碰上Maven,用了第一次,就抛弃老方法了。Maven...
- 【推荐】一款开源免费、功能强大的短链接生成平台
-
项目介绍reduce是一款开源免费、功能强大的短链接生成平台。部署在服务器,使用短域名解析即可提供服务。CoodyFramework首秀,自写IOC、MVC、ORM、TASK、JSON、DB连接池、...
- K8S官方java客户端之七:patch操作
-
欢迎访问我的GitHubhttps://github.com/zq2599/blog_demos内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;...
- Java 的业务逻辑验证框架 之-fluent-validator
-
开发人员在维护核心业务逻辑的同时,还需要为输入做严格的校验。当输入不合法时,能够给caller一个明确的反馈,最常见的反馈就是返回封装了result的对象或者抛出exception。一些常见...
- 互联网大厂后端必看!手把手教你替换 Spring Boot 中的日志框架
-
在互联网大厂的后端开发工作中,SpringBoot框架是搭建项目的“得力助手”,使用十分普遍。但不少开发者都遇到过这样的困扰:SpringBoot默认集成的Logback日志框架,在实际...
- 测试经理教你如何用monkey进行压力测试!
-
一、monkey是什么1、monkey程序由android系统自带,使用Java语言写成,在Android文件系统中的存放路径是:/system/framework/monkey.jar2、Mo...
- Java-Maven详解
-
一、什么是Maven?ApacheMaven是一个软件项目管理的综合工具。基于项目对象模型(POM)的概念,提供了帮助管理构建、文档、报告、依赖、发布等方法,Maven简化和标准化项目建设过程。处理...
- SpringBoot打包部署最佳实践
-
springboot介绍SpringBoot目前流行的javaweb应用开发框架,相比传统的spring开发,springboot极大简化了配置,并且遵守约定优于配置的原则即使0配置也能正常运...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- hive行转列函数 (63)
- sourcemap文件是什么 (54)
- display none 隐藏后怎么显示 (56)
- 共享锁和排他锁的区别 (51)
- httpservletrequest 获取参数 (64)
- jstl包 (64)
- qsharedmemory (50)
- watch computed (53)
- java中switch (68)
- date.now (55)
- git-bash (56)
- 盒子垂直居中 (68)
- npm是什么命令 (62)
- python中+=代表什么 (70)
- fsimage (51)
- nginx break (61)
- mysql分区表的优缺点 (53)
- centos7切换到图形界面 (55)
- 前端深拷贝 (62)
- kmp模式匹配算法 (57)
- jsjson字符串转json对象 (53)
- jdbc connection (61)
- javascript字符串转换为数字 (54)
- mybatis 使用 (73)
- 安装mysql数据库 (55)