prepareContext源码分析

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {// 1️⃣ 把已准备好的 Environment 设置到 ApplicationContext 中context.setEnvironment(environment);// 2️⃣ 后置处理 ApplicationContext(可被子类覆盖,做额外定制)postProcessApplicationContext(context);// 3️⃣ 执行所有 ApplicationContextInitializer(在 refresh 之前运行)// SpringApplication 初始化setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));的set//钩子函数applyInitializers(context);// 4️⃣ 生命周期事件:contextPrepared —— ApplicationContext 已创建但未 refreshlisteners.contextPrepared(context);// 5️⃣ 关闭早期 BootstrapContext,释放临时资源// 实现bootstrapRegistryInitializers这个钩子注册的bean,将其移动到真正的 BeanFactorybootstrapContext.close(context);// 6️⃣ 如果开启启动信息日志,打印应用名、PID、profile 等if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// 7️⃣ 获取 BeanFactory,准备注册单例 BeanConfigurableListableBeanFactory beanFactory = context.getBeanFactory();// 8️⃣ 把命令行参数包装成 Bean 注册到容器(供开发者使用 @Autowired 注入获取applicationArguments)beanFactory.registerSingleton("springApplicationArguments", applicationArguments);// 9️⃣ 如果前面打印了 Banner,也把 Banner 注册成单例 Beanif (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}// 🔟 根据配置决定是否允许循环依赖if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);}// 1️⃣1️⃣ 根据配置决定是否允许 BeanDefinition 覆盖if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// 1️⃣2️⃣ 如果启用懒加载,添加对应的 BeanFactoryPostProcessorif (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// 1️⃣3️⃣ 添加 PropertySource 排序处理器,保证外部配置优先级正确context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));// 1️⃣4️⃣ 获取启动源(主类、@SpringBootApplication、@Import 等)Set<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");// 1️⃣5️⃣ 真正加载启动源(主类、配置类等)到容器load(context, sources.toArray(new Object[0]));// 1️⃣6️⃣ 生命周期事件:contextLoaded —— BeanDefinition 已加载完毕,等待 refreshlisteners.contextLoaded(context);
}

prepareContext重要流程分析

postProcessApplicationContext分析

postProcessApplicationContext源码解析

① 注入自定义 BeanNameGenerator
if (this.beanNameGenerator != null) {//由于之前context是new AnnotationConfigServletWebServerApplicationContext(),使用空构造//会调用父类的public GenericApplicationContext() {//	this.beanFactory = new DefaultListableBeanFactory();//}//通过DefaultListableBeanFactory的super.registerSingleton(beanName, singletonObject);将其注册进单例bean里面,没有bean的创建流程//    @Override//    public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {//        super.registerSingleton(beanName, singletonObject);//        updateManualSingletonNames(set -> set.add(beanName), set -> !this.beanDefinitionMap.containsKey(beanName));//        clearByTypeCache();//    }context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,this.beanNameGenerator);
}
  • 场景:当你显式给 SpringApplication.setBeanNameGenerator(...) 传了自定义实现时。
  • 作用:把它注册成 单例 Bean org.springframework.context.annotation.internalConfigurationBeanNameGenerator
    之后 @Configuration@Component 等扫描时就会用它来决定 Bean 名称。
② 注入自定义 ResourceLoader
if (this.resourceLoader != null) {if (context instanceof GenericApplicationContext) {((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);}if (context instanceof DefaultResourceLoader) {((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());}
}
  • 场景SpringApplication.setResourceLoader(...) 传入过自定义 ResourceLoader(例如读取 jar 外部配置)。
  • 作用
    • GenericApplicationContext:替换整个容器的 资源解析器
    • DefaultResourceLoader:确保 类加载器 与自定义 ResourceLoader 保持一致,避免 classpath 解析错误。
③ 把 Environment 的 ConversionService 塞进 BeanFactory
if (this.addConversionService) {context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
}
  • 场景addConversionService 默认为 true
  • 作用:把 统一类型转换服务 注册进 BeanFactory,后续:
    • @Value("${port:8080}") int port
    • @ConfigurationProperties 绑定
    • 都能用同一套 ConversionService 完成字符串 → 目标类型的转换。

✅ 一句话总结
postProcessApplicationContext 是一个 “启动前置钩子”
默认实现把 BeanNameGenerator、ResourceLoader、ConversionService 这三个基础设施一次性塞进容器,
确保后续注解扫描、资源加载、属性绑定都能按预期工作。


load(context, sources.toArray(new Object[0]));流程解析

load源码

protected void load(ApplicationContext context, Object[] sources) {// 如果启用了调试日志,记录加载的源文件(如主类、配置类等)if (logger.isDebugEnabled()) {logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));}// 创建 BeanDefinitionLoader 实例,用于加载和解析 BeanDefinitionBeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);// 如果指定了自定义的 BeanNameGenerator,将其设置到 BeanDefinitionLoader 中if (this.beanNameGenerator != null) {loader.setBeanNameGenerator(this.beanNameGenerator);}// 如果指定了自定义的 ResourceLoader,将其设置到 BeanDefinitionLoader 中if (this.resourceLoader != null) {loader.setResourceLoader(this.resourceLoader);}// 如果指定了自定义的 Environment,将其设置到 BeanDefinitionLoader 中if (this.environment != null) {loader.setEnvironment(this.environment);}// 调用 BeanDefinitionLoader 的 load 方法,加载和解析 BeanDefinitionloader.load();
}

BeanDefinitionLoader.class load分析

BeanDefinitionLoader 是一个工具类,用于加载和解析 BeanDefinition(Bean 的元数据)。它将这些定义注册到 Spring 容器的 BeanDefinitionRegistry 中,为后续的 Bean 实例化和依赖注入做好准备。

1. load() 方法
void load() {for (Object source : this.sources) {load(source);}
}
  • 作用:遍历所有启动源(如主类、配置类等),并调用 load(Object source) 方法加载每个源。
2. load(Object source) 方法
private void load(Object source) {Assert.notNull(source, "Source must not be null");if (source instanceof Class<?>) {load((Class<?>) source);return;}if (source instanceof Resource) {load((Resource) source);return;}if (source instanceof Package) {load((Package) source);return;}if (source instanceof CharSequence) {load((CharSequence) source);return;}throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
  • 作用:根据传入的源类型(ClassResourcePackageCharSequence),调用相应的加载方法。
  • Assert.notNull(source, "Source must not be null"):确保传入的源不为 null
  • if (source instanceof Class<?>):如果源是 Class 类型,调用 load(Class<?> source) 方法。
  • if (source instanceof Resource):如果源是 Resource 类型,调用 load(Resource source) 方法。
  • if (source instanceof Package):如果源是 Package 类型,调用 load(Package source) 方法。
  • if (source instanceof CharSequence):如果源是 CharSequence 类型,调用 load(CharSequence source) 方法。
  • throw new IllegalArgumentException("Invalid source type " + source.getClass()):如果源类型不支持,抛出异常。
3. load(Class<?> source) 方法
private void load(Class<?> source) {if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {// Any GroovyLoaders added in beans{} DSL can contribute beans hereGroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());}if (isEligible(source)) {this.annotatedReader.register(source);}
}
  • 作用:加载 Class 类型的源。
  • isGroovyPresent():检查是否加载了 Groovy。
  • GroovyBeanDefinitionSource.class.isAssignableFrom(source):检查源是否是 GroovyBeanDefinitionSource 的子类。
  • BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class):实例化 GroovyBeanDefinitionSource
  • ((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans()):将 Groovy 定义的 Bean 加载到 GroovyBeanDefinitionReader 中。
  • isEligible(source):检查源是否符合加载条件。
  • this.annotatedReader.register(source):将注解类注册到 AnnotatedBeanDefinitionReader 中。
4. load(Resource source) 方法
private void load(Resource source) {if (source.getFilename().endsWith(".groovy")) {if (this.groovyReader == null) {throw new BeanDefinitionStoreException("Cannot load Groovy beans without Groovy on classpath");}this.groovyReader.loadBeanDefinitions(source);}else {if (this.xmlReader == null) {throw new BeanDefinitionStoreException("Cannot load XML bean definitions when XML support is disabled");}this.xmlReader.loadBeanDefinitions(source);}
}
  • 作用:加载 Resource 类型的源。
  • source.getFilename().endsWith(".groovy"):检查资源文件是否以 .groovy 结尾。
  • this.groovyReader.loadBeanDefinitions(source):使用 GroovyBeanDefinitionReader 加载 Groovy 资源。
  • this.xmlReader.loadBeanDefinitions(source):使用 XmlBeanDefinitionReader 加载 XML 资源。
5. load(Package source) 方法
private void load(Package source) {this.scanner.scan(source.getName());
}
  • 作用:加载 Package 类型的源。
  • this.scanner.scan(source.getName()):使用 ClassPathBeanDefinitionScanner 扫描指定包路径下的类。
6. load(CharSequence source) 方法
private void load(CharSequence source) {String resolvedSource = this.scanner.getEnvironment().resolvePlaceholders(source.toString());// Attempt as a Classtry {load(ClassUtils.forName(resolvedSource, null));return;}catch (IllegalArgumentException | ClassNotFoundException ex) {// swallow exception and continue}// Attempt as Resourcesif (loadAsResources(resolvedSource)) {return;}// Attempt as packagePackage packageResource = findPackage(resolvedSource);if (packageResource != null) {load(packageResource);return;}throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
}
  • 作用:加载 CharSequence 类型的源。
  • this.scanner.getEnvironment().resolvePlaceholders(source.toString()):解析占位符。
  • ClassUtils.forName(resolvedSource, null):尝试将字符串解析为类名并加载类。
  • loadAsResources(resolvedSource):尝试将字符串解析为资源路径并加载资源。
  • findPackage(resolvedSource):尝试将字符串解析为包路径并加载包。
  • throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'"):如果无法解析,抛出异常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/918562.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/918562.shtml
英文地址,请注明出处:http://en.pswp.cn/news/918562.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HIS系统:医院信息化建设的核心,采用Angular+Java技术栈,集成MySQL、Redis等技术,实现医院全业务流程管理。

HIS系统在医院信息化建设中扮演着核心的角色。它是一个综合性的信息系统&#xff0c;旨在管理和运营医院的各种业务&#xff0c;包括门诊、住院、财务、物资、科研等。技术细节&#xff1a;前端&#xff1a;AngularNginx后台&#xff1a;JavaSpring&#xff0c;SpringBoot&…

深度学习-卷积神经网络-LeNet

卷积神经网络是一种专门用于处理具有网格结构数据&#xff08;如图像、音频等&#xff09;的深度学习模型。它通过卷积层自动提取数据中的特征&#xff0c;利用局部连接和参数共享的特性减少了模型的参数数量&#xff0c;降低了过拟合的风险&#xff0c;同时能够有效地捕捉数据…

【Java项目与数据库、Maven的关系详解】

Java项目与数据库、Maven的关系详解 一、Java项目是否都需要连接本地数据库&#xff1f; 不一定&#xff0c;这取决于项目类型和需求&#xff1a; 1. 需要数据库的项目类型项目类型数据库作用典型场景Web应用存储用户数据/业务数据电商系统、CMS服务端程序持久化数据金融交易系…

两个Maven工程,使用idea开发,工程A中依赖了工程B,改了工程B,工程A如何获取最新代码

两个Maven工程&#xff0c;使用idea开发&#xff0c;工程A中依赖了工程B&#xff0c;改了工程B&#xff0c;工程A如何获取最新代码 如果工程B的版本是快照&#xff0c;那么如下。 步骤一 工程B 执行 clean package install deploy 步骤二 工程A 刷新Maven

奥比中光与地平线、地瓜机器人达成战略合作,携手推动机器人智能化

摘要&#xff1a;机器人“慧眼”与“智脑”强强联合&#xff01;8月11日&#xff0c;奥比中光与地平线及其控股子公司地瓜机器人在北京签订合作协议&#xff0c;双方将在机器人智能化领域展开深度合作&#xff0c;充分发挥各自的技术与产品优势&#xff0c;携手推动机器人产业的…

【Linux】Tomcat

Tomcat简介Tomcat 服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和 并发访问用户不是很多的场合下被普遍使用&#xff0c;Tomcat 具有处理HTML页面的功能&#xff0c;它还是一个Servlet和 JSP容器Tomcat的使用安装ja…

Putting it all together 将所有内容整合在一起

官方链接 https://www.youtube.com/watch?vAa_FAA3v22g&t1s Task1 Putting It All Together 将所有内容整合在一起 图片版 文字版 Putting It All Together 将所有内容整合在一起 From the previous modules, youll have learned that quite a lot of things go on b…

Python 闭包详解:从变量作用域到实战案例

一、变量作用域基础在 Python 中&#xff0c;变量根据作用范围可分为三类&#xff1a;全局变量&#xff1a;定义在函数外部的变量&#xff0c;作用范围是整个程序。如果在函数内部需要修改全局变量&#xff0c;必须使用global关键字声明。局部变量&#xff1a;定义在函数内部的…

Docker 跨主机容器之间的通信macvlan

默认一个物理网卡&#xff0c;只有一个物理mac地址&#xff0c;虚拟多个mac地址 缺点&#xff1a;每次需要手动配置ip地址&#xff0c;容易ip地址冲突。类似于保存到execl表格里面。 两台物理机&#xff1a; docker-01和docker-02 创建macvlan网络 [rootdocker-01 ~]# docker n…

android 换肤框架详解1-换肤逻辑基本

android 换肤框架详解1-换肤逻辑基本-CSDN博客 android 换肤框架详解2-LayoutInflater源码解析-CSDN博客 android 换肤框架详解3-自动换肤原理梳理-CSDN博客 换肤框架流程 1&#xff0c;通过AssetManager获取换肤的资源文件 2&#xff0c;通过原文件中的resId获取到res名称…

NEON性能优化总结

转自 NEON优化&#xff1a;性能优化经验总结-CSDN博客 NEON优化&#xff1a;性能优化经验总结 1. 什么是 NEON Arm Adv SIMD 历史 2. 寄存器 3. NEON 命名方式 4. 优化技巧 5. 优化 NEON 代码(Armv7-A内容&#xff0c;但区别不大) 5.1 优化 NEON 汇编代码 …

计算机网络摘星题库800题笔记 第2章 物理层

第2章 物理层2.1 物理层概述题组闯关1.采用以下哪种设备&#xff0c;可以使数字信号传输得更远 ( )。 A. 放大器 B. 中继器 C. 网桥 D. 路由器1.【参考答案】B 【解析】选项 A 放大器只是单纯地放大信号、抑制噪音和干扰。选项 B 中继器是把一根线缆中的电或者光信号传递给另一…

导入文件到iPhone实现

我们有时候开发需要加载一些自己的文件&#xff0c;这个时候就需要导入文件到iPhone等设备。在info里面open as source code&#xff0c;加入如下配置&#xff1a;<!-- 开启 iTunes / Finder 文件共享 --><key>UIFileSharingEnabled</key><true/>或者o…

Ubuntu Server系统安装磁盘分区方案

最近打算把家里的旧电脑利用起来&#xff0c;装上Ubuntu Server 24.04.3 LTS作为一个家用NAS服务器&#xff0c;但是给旧电脑安装系统时遇到了一些问题&#xff0c;遂记录下来 GPT分区与MBR分区 GPT 指的是 GUID Partition Table&#xff08;全局唯一标识分区表&#xff09;&am…

1小时 MySQL 数据库基础速通

目录 一、MySQL安装配置 1、下载mysql 2、下载mysql-shell 二、MySQL基本概念 1. 数据库&#xff08;Database&#xff09; 2. 表&#xff08;Table&#xff09; 3. 数据类型&#xff08;Data Type&#xff09; 4. 主键&#xff08;Primary Key&#xff09; 5. 索引&am…

HTTP应用层协议-长连接

HTTP应用层协议-长连接 关于 connection 报头 HTTP 中的 Connection 字段是 HTTP 报文头的一部分&#xff0c;它主要用于控制和管理客户端与服务器之间的连接状态 核心作用 • 管理持久连接&#xff1a;Connection 字段还用于管理持久连接&#xff08;也称为长连接&#xff09;…

2020/12 JLPT听力原文 问题一 4番

4番&#xff1a;ホテルの受付で女の人と男の人が話しています。女の人はどこでパソコンを使いますか。女&#xff1a;すみません、パソコンの貸出ってできますか。部屋で仕事をしたいんですけど。 男&#xff1a;申し訳ございません。貸出はしていないんですが、二階にビジネス…

《在 Spring Boot 中安全使用 Qwen API-KEY:环境变量替代明文配置的最佳实践》

《在 Spring Boot 中安全使用 Qwen API-KEY&#xff1a;环境变量替代明文配置的最佳实践》 想要的效果其实就是 把 Qwen API-KEY 放到系统环境变量中&#xff0c;然后在 application.yml 里通过占位符读取&#xff0c;而不写明文。 这样即便 application.yml 被提交到 Git&…

Nginx 反向代理与负载均衡架构

一、反向代理基础 实验目的&#xff1a;通过 Nginx 反向代理&#xff0c;将客户端请求按类型&#xff08;静态页面 / 动态 PHP 页面&#xff09;转发到不同的后端服务器&#xff08;RS1 处理静态资源&#xff0c;RS2 处理动态请求&#xff09;&#xff0c;实现 “客户端只与代…

【Mybatis入门】配置Mybatis(IDEA)

Mybatis和JDBC一样&#xff0c;是连接数据库的工具。它是一款优秀的持久层框架&#xff0c;主要用于 Java 语言中简化数据库操作&#xff0c;实现对象与数据库表之间的映射。Mybatis相比于JDBC的优势Mybatis消除了传统 JDBC 代码中繁琐的手动处理、参数设置、结果集解析等重复工…