当前位置: 首页 > news >正文

福州企业自助建站平面设计招聘58同城

福州企业自助建站,平面设计招聘58同城,中小型企业建设网站,泰安百度公司代理商在黄昏的余晖里#xff0c;梦境渐浓#xff0c;如烟如雾。心随星辰#xff0c;徜徉远方#xff0c;岁月静好#xff0c;愿如此刻般绵长。 文章目录 前言一、SpringBoot 应用二、SpringApplication2.1 SpringApplication 中的属性2.2 SpringApplication 的构造器2.3 Sprin… 在黄昏的余晖里梦境渐浓如烟如雾。心随星辰徜徉远方岁月静好愿如此刻般绵长。 文章目录 前言一、SpringBoot 应用二、SpringApplication2.1 SpringApplication 中的属性2.2 SpringApplication 的构造器2.3 SpringApplication 中的方法 三、SpringBoot 应用的启动流程四、深入 SpringBoot 启动4.1 引导阶段4.2 启动阶段4.3 运行阶段 五、FAQ5.1 Spring 容器的生命周期5.2 Spring 容器的扩展点 六、小结推荐阅读 前言 SpringApplication 是 Spring Boot 框架中的一个类它被用于引导和运行 Spring Boot 应用程序。它提供了一种简化的方式来配置和启动应用程序减少了开发者的工作量。本文将对 SpringApplication 源码进行分析深入理解其核心。 一、SpringBoot 应用 下面是一个 SpringBoot 应用的常规写法。 SpringBootApplication public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);} }上述代码有两个重点 SpringBootApplication 作用是简化配置自动扫描组件SpringApplication SpringBoot 应用的启动入口 二、SpringApplication SpringApplication 是 SpringBoot 的一个入口类。这个类里的内容较多我们将逐一进行分析。 2.1 SpringApplication 中的属性 SpringApplication 中定义了大量的属性这些属性基本上都设置了默认值可供之后的方法使用。 // 默认的横幅Banner位置 private static final String DEFAULT_BANNER_LOCATION banner.txt;// 横幅Banner位置的属性键 private static final String BANNER_LOCATION_PROPERTY spring.banner.location;// Java AWT 无头模式的系统属性键 private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS java.awt.headless;// 日志记录器 private static final Log logger LogFactory.getLog(SpringApplication.class);// 处理应用程序关闭时的清理工作 private SetApplicationContext runningAppContexts;// 主要来源的集合表示主类或主配置类 private SetClass? primarySources;// 资源集合表示应用程序的来源 private SetString sources;// 主应用程序类 private Class? mainApplicationClass;// 横幅模式默认为 Banner.Mode.CONSOLE表示将横幅输出到控制台 private Banner.Mode bannerMode Banner.Mode.CONSOLE;// 是否记录启动信息默认为 true private boolean logStartupInfo true;// 是否添加命令行属性默认为 true private boolean addCommandLineProperties true;// 是否添加转换服务默认为 true private boolean addConversionService true;// 横幅实例 private Banner banner;// 资源加载器用于加载应用程序的资源 private ResourceLoader resourceLoader new DefaultResourceLoader();// Bean 名称生成器用于生成 Bean 的名称 private BeanNameGenerator beanNameGenerator;// 配置环境用于获取应用程序的配置属性 private ConfigurableEnvironment environment;// Web 应用程序类型表示应用程序是一个 Servlet 应用程序还是一个反应式 Web 应用程序 private WebApplicationType webApplicationType;// 是否为无头模式默认为 true private boolean headless true;// 是否注册关闭钩子默认为 true private boolean registerShutdownHook true;// 应用程序上下文初始化器的列表用于初始化应用程序上下文 private ListApplicationContextInitializer? initializers;// 应用程序监听器的列表用于监听应用程序事件 private ListApplicationListener? listeners;// 默认属性的映射用于配置应用程序的默认属性 private MapString, Object defaultProperties;// 引导注册表初始化器的列表用于初始化引导注册表 private ListBootstrapRegistryInitializer bootstrapRegistryInitializers;// 附加配置文件的集合用于指定额外的配置文件 private SetString additionalProfiles;// 是否允许覆盖 Bean 定义默认为 false private boolean allowBeanDefinitionOverriding;// 是否允许循环引用默认为 true private boolean allowCircularReferences true;// 是否使用自定义环境默认为 false private boolean isCustomEnvironment false;// 是否延迟初始化默认为 false private boolean lazyInitialization false;// 环境前缀 private String environmentPrefix;// 应用程序上下文工厂 private ApplicationContextFactory applicationContextFactory ApplicationContextFactory.DEFAULT;// 应用程序启动器 private ApplicationStartup applicationStartup ApplicationStartup.DEFAULT;2.2 SpringApplication 的构造器 SpringApplication 中表面上有两个有参构造器。但是第一个构造器是通过调用第二个构造器实现对象的初始化所以SpringApplication 的初始化我们只需要了解第二个构造器就可以了。 /*** 创建一个新的 SpringApplication 实例。** param primarySources 主要来源的类表示主类或主配置类*/ public SpringApplication(Class?... primarySources) {// 调用另一个构造函数并传入 null 作为 ResourceLoaderthis(null, primarySources); }/*** 使用指定的资源加载器创建一个新的 SpringApplication 实例。** param resourceLoader 资源加载器用于加载应用程序的资源可以为 null* param primarySources 主要来源的类表示主类或主配置类不能为空*/ SuppressWarnings({ unchecked, rawtypes }) public SpringApplication(ResourceLoader resourceLoader, Class?... primarySources) {// 设置资源加载器this.resourceLoader resourceLoader;// 确保 primarySources 不为空否则抛出异常Assert.notNull(primarySources, PrimarySources must not be null);// 将 primarySources 转换为 LinkedHashSet 并赋值给 this.primarySourcesthis.primarySources new LinkedHashSet(Arrays.asList(primarySources));// 根据类路径推断 Web 应用程序类型如 Servlet 或 Reactivethis.webApplicationType WebApplicationType.deduceFromClasspath();// 从 Spring 工厂加载 BootstrapRegistryInitializer 实例并添加到 bootstrapRegistryInitializers 列表中this.bootstrapRegistryInitializers new ArrayList(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));// 从 Spring 工厂加载 ApplicationContextInitializer 实例并设置为初始器setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 从 Spring 工厂加载 ApplicationListener 实例并设置为监听器setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 推断主应用程序类并赋值给 this.mainApplicationClassthis.mainApplicationClass deduceMainApplicationClass(); }之前我们已经了解 SpringApplication 中的属性大部分会有默认值但是还有一部分属性并没有给初始值。在 SpringApplication 的构造器中这一部分没有给定初始值的属性会在构造器中计算出初始值。例如resourceLoader、primarySources、webApplicationType、bootstrapRegistryInitializers、mainApplicationClass 等。 由此我们可以看出 SpringApplication 构造器的作用是完成一些属性的初始化工作。 2.3 SpringApplication 中的方法 SpringApplication 中的方法有许多我们可以将这些方法分类进行讨论。首先我们将 SpringApplication 中的方法分为 private 和非 private 方法。我们知道 private 修饰的方法只能给 SpringApplication 内部使用我们并不会直接使用到。所以其实 private 修饰的方法我们并不需要去了解。 现在剩下的方法我们再进行一次划分我们将剩下的方法分为setter、getter 方法和非 setter、getter 方法。我们知道setter 和 getter 方法是用来控制属性的不用过多的讨论。 至此SpringApplication 中剩下值得讨论的方法就只有下面这些了。 而在这些方法中addBootstrapRegistryInitializer、addInitializers、addListeners、addPrimarySources 这几个方法的功能是和 setter 方法类似的因为相应的属性是集合使用 add 方法向集合中添加值。 最终我们分析出SpringApplication 只有三个可供开发者直接使用的核心方法。 main() 方法exit() 方法run() 方法 main() 方法是命令行场景下使用的。 /*** 一个用于启动应用程序的基本主方法。当应用程序源代码通过* {literal --spring.main.sources} 命令行参数定义时此方法非常有用。* p* 大多数开发人员可能希望自定义自己的主方法并调用* {link #run(Class, String...) run} 方法来启动应用程序。* param args 命令行参数* throws Exception 如果应用程序无法启动* see SpringApplication#run(Class[], String[])* see SpringApplication#run(Class, String...)*/ public static void main(String[] args) throws Exception {SpringApplication.run(new Class?[0], args); }exit() 方法的作用是退出 SpringBoot 应用。 /*** 退出应用程序并返回退出码。接收一个应用程序上下文和一个或多个退出码生成器作为参数。* param context 应用程序上下文* param exitCodeGenerators 退出码生成器* return 退出码*/ public static int exit(ApplicationContext context, ExitCodeGenerator... exitCodeGenerators) {// 检查上下文是否为空Assert.notNull(context, Context must not be null);int exitCode 0;try {try {// 创建所有的退出码生成器ExitCodeGenerators generators new ExitCodeGenerators();// 获取应用程序上下文中所有的退出码生成器CollectionExitCodeGenerator beans context.getBeansOfType(ExitCodeGenerator.class).values();// 添加传入的退出码生成器和上下文中的退出码生成器到生成器集合中generators.addAll(exitCodeGenerators);generators.addAll(beans);// 获取最终的退出码exitCode generators.getExitCode();// 如果退出码不为0则发布退出码事件if (exitCode ! 0) {context.publishEvent(new ExitCodeEvent(context, exitCode));}} finally {// 关闭应用程序上下文close(context);}} catch (Exception ex) {// 打印异常堆栈信息ex.printStackTrace();// 如果之前的退出码为0则将退出码设置为1exitCode (exitCode ! 0) ? exitCode : 1;}return exitCode; }这个方法提供了一种优雅的方式来关闭 Spring 应用程序并根据上下文中存在的退出码生成器确定合适的退出码。如果在关闭过程中发生异常也会适当地处理并返回一个退出代码。 run() 方法的作用是运行 SpringBoot 应用这个方法被重载了 3 3 3 次前 2 2 2 次重载并无实际的处理逻辑。 /*** 这个方法接受一个主要源类和一组命令行参数来运行一个 Spring 应用程序。* 通常主要源是一个带有 SpringBootApplication 注解的类。* 它委托给另一个 run 方法来执行。** param primarySource 应用程序的主要源类* param args 命令行参数* return 可配置的应用程序上下文*/ public static ConfigurableApplicationContext run(Class? primarySource, String... args) {// 委托给另一个 run 方法传递一个包含主要源类的数组和命令行参数return run(new Class?[] { primarySource }, args); }/*** 创建一个新的 SpringApplication 对象并使用给定的主要源类数组初始化它* 然后调用 SpringApplication 的 run 方法来启动应用程序。*/ public static ConfigurableApplicationContext run(Class?[] primarySources, String[] args) {// 创建一个新的 SpringApplication 对象使用给定的主要源类数组初始化它并调用 run 方法启动应用程序return new SpringApplication(primarySources).run(args); }/*** 运行 Spring 应用程序并返回配置好的应用程序上下文。** param args 命令行参数* return 配置好的 Spring 应用程序上下文*/ public ConfigurableApplicationContext run(String... args) {// 记录启动时间long startTime System.nanoTime();// 创建引导上下文DefaultBootstrapContext bootstrapContext createBootstrapContext();ConfigurableApplicationContext context null;// 配置 Headless 属性configureHeadlessProperty();// 获取运行监听器SpringApplicationRunListeners listeners getRunListeners(args);// 通知监听器应用程序即将启动listeners.starting(bootstrapContext, this.mainApplicationClass);try {// 创建应用程序参数对象ApplicationArguments applicationArguments new DefaultApplicationArguments(args);// 准备环境ConfigurableEnvironment environment prepareEnvironment(listeners, bootstrapContext, applicationArguments);// 配置忽略 BeanInfoconfigureIgnoreBeanInfo(environment);// 打印 BannerBanner printedBanner printBanner(environment);// 创建应用程序上下文context createApplicationContext();// 设置应用程序启动信息context.setApplicationStartup(this.applicationStartup);// 准备应用程序上下文prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 刷新应用程序上下文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);// 调用运行器callRunners(context, applicationArguments);}catch (Throwable ex) {// 处理运行失败情况handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {// 计算应用程序准备所花费的时间Duration timeTakenToReady Duration.ofNanos(System.nanoTime() - startTime);// 通知监听器应用程序已准备就绪listeners.ready(context, timeTakenToReady);}catch (Throwable ex) {// 处理运行失败情况handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}// 返回应用程序上下文return context; }三、SpringBoot 应用的启动流程 计算机的启动过程通常按以下步骤进行 步骤 1当我们打开电源时从非易失性存储器中加载 BIOS基本输入/输出系统或 UEFI统一可扩展固件接口固件并执行 POST开机自检。步骤 2BIOS/UEFI 检测连接到系统的设备包括 CPU、RAM 和存储设备。步骤 3选择一个启动设备来引导操作系统。这可以是硬盘、网络服务器或 CD-ROM。步骤 4BIOS/UEFI 运行引导加载程序如 GRUB该程序提供一个菜单供选择操作系统或内核功能。步骤 5内核准备就绪后系统切换到用户空间。内核启动 systemd 作为第一个用户空间进程systemd 管理各个进程和服务探测所有剩余的硬件挂载文件系统并运行桌面环境。步骤 6systemd 默认激活 default.target 单元当系统启动时其他分析单元也会被执行。步骤 7系统运行一组启动脚本并配置环境。步骤 8用户将看到登录窗口。系统现在已经准备就绪。 综上上面的计算机启动过程太多我们可以进一步总结为以下三个主要阶段引导、启动和运行。 引导阶段在此阶段计算机会执行基本输入/输出系统BIOS或统一可扩展固件接口UEFI等引导加载程序以加载操作系统的引导记录并将操作系统内核加载到内存中。启动阶段一旦操作系统内核被加载到内存中计算机将开始执行操作系统的初始化过程包括建立内存管理、初始化进程、加载驱动程序等操作。运行阶段在这个阶段操作系统已经完全加载并且用户界面准备就绪用户可以登录系统并开始使用计算机进行各种任务。 SpringApplication 中的 run() 方法功能和计算机的启动流程是类似的。 run() 方法也可以分成三个阶段 引导阶段创建引导容器 DefaultBootstrapContext、启动监听器 SpringApplicationRunListeners、准备环境 prepareEnvironment、打印 Banner启动阶段创建应用容器 ConfigurableApplicationContext并完成应用容器的初始化工作运行阶段应用容器准备就绪可以使用 引导容器 DefaultBootstrapContext 就如同计算机启动过程中的内核首先完成内核的加载。之后通过内核初始化应用容器 ConfigurableApplicationContext类似操作系统。当操作系统初始化成功最终容器达可运行阶段可供使用。 四、深入 SpringBoot 启动 4.1 引导阶段 SpringApplication 的引导阶段并没有计算机的引导阶段那么复杂。这一阶段主要是创建引导容器 DefaultBootstrapContext 用于下一阶段引导应用容器 ConfigurableApplicationContext。其次在这一阶段也会启动 SpringBoot 的监听器功能 SpringApplicationRunListeners以及准备环境 prepareEnvironment()。 4.2 启动阶段 SpringApplication 的启动阶段内容比较多。这一阶段核心内容是做了以下几件事情 创建应用容器 ConfigurableApplicationContext准备应用程序上下文 prepareContext()刷新应用程序上下文 refreshContext() 第一个核心内容创建应用容器其实很好理解。因为这就是我们真正使用的容器。我们唯一需要知道的就是应用容器的创建时机是在此处。 第二个核心内容准备应用程序上下文这一阶段主要是在启动 Spring 应用程序时准备应用程序的上下文。它的主要步骤包括设置环境、应用初始化器、记录启动信息、注册单例 Bean、设置 Bean 工厂属性、延迟初始化、添加属性源排序后处理器、加载应用程序源并通知监听器应用程序上下文的准备和加载完成。这些步骤确保了应用程序上下文的正确配置和准备为应用程序的顺利启动提供了必要的准备工作。 private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {// 将环境对象设置到应用程序上下文中context.setEnvironment(environment);// 对应用程序上下文进行后处理postProcessApplicationContext(context);// 应用初始化器到应用程序上下文中applyInitializers(context);// 通知监听器应用程序上下文已准备好listeners.contextPrepared(context);// 关闭引导上下文bootstrapContext.close(context);// 如果需要记录启动信息记录启动信息和启动配置文件信息if (this.logStartupInfo) {logStartupInfo(context.getParent() null);logStartupProfileInfo(context);}// 获取应用程序上下文的 Bean 工厂ConfigurableListableBeanFactory beanFactory context.getBeanFactory();// 注册特定于 Spring Boot 的单例 bean如 springApplicationArgumentsbeanFactory.registerSingleton(springApplicationArguments, applicationArguments);// 如果存在打印的横幅则注册 springBootBanner 单例 beanif (printedBanner ! null) {beanFactory.registerSingleton(springBootBanner, printedBanner);}// 检查 Bean 工厂实例是否为 AbstractAutowireCapableBeanFactory 类型if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {// 设置是否允许循环引用((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);// 检查 Bean 工厂实例是否为 DefaultListableBeanFactory 类型if (beanFactory instanceof DefaultListableBeanFactory) {// 设置是否允许 Bean 定义覆盖((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}}// 如果启用了延迟初始化则添加延迟初始化 Bean 工厂后处理器if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// 添加属性源排序的 Bean 工厂后处理器context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));// 加载应用程序的源SetObject sources getAllSources();Assert.notEmpty(sources, Sources must not be empty);load(context, sources.toArray(new Object[0]));// 通知监听器应用程序上下文已加载完成listeners.contextLoaded(context); }设置环境将传入的 ConfigurableEnvironment 对象设置到应用程序上下文中以便上下文可以使用这个环境中的配置和属性。 context.setEnvironment(environment);后处理应用程序上下文调用自定义方法对应用程序上下文进行额外的处理。具体实现取决于开发者的需求这一步一般用于添加额外的配置或修改上下文的某些属性。 postProcessApplicationContext(context);应用初始化器调用所有注册的 ApplicationContextInitializer 初始化器对应用程序上下文进行进一步的配置。 applyInitializers(context);通知监听器上下文已准备通知所有 SpringApplicationRunListener应用程序上下文已准备好。这通常用于在上下文被刷新之前执行一些操作。 listeners.contextPrepared(context);关闭引导上下文关闭引导上下文释放资源。 bootstrapContext.close(context);记录启动信息如果启用了启动信息记录记录启动信息和配置文件信息。这对于调试和诊断问题很有帮助。 if (this.logStartupInfo) {logStartupInfo(context.getParent() null);logStartupProfileInfo(context); }注册单例 Bean将 applicationArguments 和 printedBanner如果有注册为单例 Bean以便它们可以在应用程序的其他部分使用。 ConfigurableListableBeanFactory beanFactory context.getBeanFactory(); beanFactory.registerSingleton(springApplicationArguments, applicationArguments);if (printedBanner ! null) {beanFactory.registerSingleton(springBootBanner, printedBanner); }设置 Bean 工厂属性根据配置设置 Bean 工厂的属性例如是否允许循环引用和 Bean 定义覆盖。 if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);} }延迟初始化如果启用了延迟初始化向应用程序上下文添加一个 LazyInitializationBeanFactoryPostProcessor以推迟 Bean 的初始化。 if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); }添加属性源排序后处理器添加一个 PropertySourceOrderingBeanFactoryPostProcessor确保属性源按照预期顺序被处理。 context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));加载应用程序源获取所有应用程序源并加载它们。确保源不为空否则抛出异常。 SetObject sources getAllSources(); Assert.notEmpty(sources, Sources must not be empty); load(context, sources.toArray(new Object[0]));通知监听器上下文已加载通知所有 SpringApplicationRunListener应用程序上下文已加载完成。这是最后一步表示上下文已经完全准备好可以启动应用程序了。 listeners.contextLoaded(context);此阶段我们需要了解的是这一阶段是在处理容器准备容器还未开始处理 Bean因为这一阶段并没有将真正的 Bean 注册到容器。上面虽有一个注册单例 Bean但是注册的是 springApplicationArguments 和 springBootBanner这两个 Bean 都属于容器相关的。 第三个核心内容刷新应用程序上下文 public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) { // 确保线程安全StartupStep contextRefresh this.applicationStartup.start(spring.context.refresh); // 记录启动步骤// 准备此上下文以进行刷新。prepareRefresh();// 告诉子类刷新内部 bean 工厂。ConfigurableListableBeanFactory beanFactory obtainFreshBeanFactory();// 为该上下文准备 bean 工厂。prepareBeanFactory(beanFactory);try {// 允许在上下文子类中对 bean 工厂进行后处理。postProcessBeanFactory(beanFactory);StartupStep beanPostProcess this.applicationStartup.start(spring.context.beans.post-process);// 调用在上下文中注册为 bean 的工厂处理器。invokeBeanFactoryPostProcessors(beanFactory);// 注册拦截 bean 创建的 bean 处理器。registerBeanPostProcessors(beanFactory);beanPostProcess.end();// 初始化此上下文的消息源。initMessageSource();// 初始化此上下文的事件广播器。initApplicationEventMulticaster();// 初始化特定上下文子类中的其他特殊 bean。onRefresh();// 检查监听器 bean 并注册它们。registerListeners();// 实例化所有剩余的非 lazy-init单例。finishBeanFactoryInitialization(beanFactory);// 最后一步发布相应事件。finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn(在上下文初始化期间遇到异常 - 取消刷新尝试: ex);}// 销毁已创建的单例以避免资源悬挂。destroyBeans();// 重置 active 标志。cancelRefresh(ex);// 将异常传播给调用者。throw ex;}finally {// 重置 Spring 核心中的常见内省缓存因为我们可能不再需要单例 bean 的元数据...resetCommonCaches();contextRefresh.end(); // 结束启动步骤记录}} }记录启动步骤记录上下文刷新过程的启动步骤用于性能监控和排错。 StartupStep contextRefresh this.applicationStartup.start(spring.context.refresh);准备刷新这个方法用于准备上下文刷新包括设置环境、属性源和早期事件发布等。 prepareRefresh();获取新的 Bean 工厂刷新 Bean 工厂通常是销毁旧的 Bean 工厂并创建一个新的。 ConfigurableListableBeanFactory beanFactory obtainFreshBeanFactory();准备 Bean 工厂设置 Bean 工厂的一些标准配置如类加载器、表达式解析器和一些默认的 Bean 后处理器。 prepareBeanFactory(beanFactory);后处理 Bean 工厂子类可以重写这个方法以便在 Bean 工厂标准初始化之后做一些自定义的修改。 postProcessBeanFactory(beanFactory);调用 Bean 工厂后处理器这一步调用所有注册的 BeanFactoryPostProcessor用于修改应用上下文的内部 Bean 定义。 invokeBeanFactoryPostProcessors(beanFactory);注册 Bean 后处理器注册 BeanPostProcessor这些处理器会在 Bean 实例化前后进行一些自定义操作。 registerBeanPostProcessors(beanFactory);初始化消息源初始化应用上下文中的消息源用于国际化消息处理。 initMessageSource();初始化事件广播器初始化事件广播器用于发布应用上下文中的事件。 initApplicationEventMulticaster();特定子类的刷新操作留给子类实现的钩子方法允许在刷新上下文时添加一些特定的逻辑。 onRefresh();注册监听器查找并注册所有的事件监听器。 registerListeners();实例化所有剩余的单例实例化所有非延迟初始化的单例 Bean确保它们都已准备好使用。 finishBeanFactoryInitialization(beanFactory);完成刷新最后一步主要是清理缓存和发布事件标志着上下文刷新完成。 finishRefresh();刷新应用程序上下文的逻辑是比较复杂的。我们可以看到在 refresh() 方法中只有刷新的流程但是没有具体的操作具体的操作都在方法里面。 这些方法中有几个我们需要特别讨论一下 obtainFreshBeanFactory()postProcessBeanFactory()invokeBeanFactoryPostProcessors()registerBeanPostProcessors() obtainFreshBeanFactory() 方法底层调用了 loadBeanDefinitions() 方法。loadBeanDefinitions() 见名知意它的作用是将加载所有 BeanDefinition。 postProcessBeanFactory() 方法底层调用了 doRegisterBean() 方法。同样地doRegisterBean() 方法也可见名知意它的作用是注册 Bean。 invokeBeanFactoryPostProcessors() 方法的核心作用是在 Spring 容器初始化时正确地调用所有 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor以便开发者可以在 Bean 实例化之前修改 Bean 定义和配置从而影响整个应用程序上下文中的 Bean 行为。这种机制提供了很大的灵活性使得 Spring 应用程序可以根据需要动态调整 Bean 的配置和定义。 registerBeanPostProcessors() 方法的核心是处理和注册 BeanPostProcessor 实例。在 Spring 容器启动时会调用该方法将所有的 BeanPostProcessor 注册到容器中以便在 Bean 的生命周期中进行处理。 由此我们可以知道 SpringApplication 刷新的核心是什么 这也解释了为什么 Spring 容器会有两大扩展点 容器的扩展点通过实现 BeanFactoryPostProcessor 或 BeanDefinitionRegistryPostProcessorBean 的扩展点通过实现 BeanPostProcessor 4.3 运行阶段 运行阶段其实没什么特别需要讨论的。这一阶段容器已准备就绪Bean 已加载完成。剩下的只是计算一下整个容器启动所花费的时间然后通知监听器容器已处于就绪状态。 五、FAQ 5.1 Spring 容器的生命周期 其实Spring 的官方文档中并没有说明 Spring 容器的生命周期可是我们经常听到 Spring 容器生命周期这一说法。这一说法其实有两个来源 通过对上述 Spring 容器的启动流程进行总结出来的通过分析 SpringApplicationRunListener 接口 许多人都是通过分析 Spring 容器启动流程然后在此基础上进行总结的。这种方式有个非常大的弊端由于每个人总结的思路不同会导致结果不一致从而形成了多种说法。懂的人自然懂初学者或者想要深入理解 Spring 容器生命周期的人会自然而然的产生疑惑不知道究竟哪个版本更为准确。 其实我更加推荐通过分析 SpringApplicationRunListener 接口来讨论 Spring 容器的生命周期。 public interface SpringApplicationRunListener {/*** 在应用启动之初被调用。此时除非必要不应该执行任何需要配置环境或上下文的操作。** param bootstrapContext Spring Boot 的引导上下文用于存储和共享跨多个阶段的数据。*/default void starting(ConfigurableBootstrapContext bootstrapContext) {}/*** 当环境准备完毕时被调用。在这个阶段可以对环境进行进一步的自定义。** param bootstrapContext Spring Boot 的引导上下文用于存储和共享跨多个阶段的数据。* param environment 配置环境对象包含了所有的配置信息。*/default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,ConfigurableEnvironment environment) {}/*** 当应用上下文准备完毕时被调用。此时上下文已经创建但还没有加载 Bean 定义。** param context Spring 应用上下文。*/default void contextPrepared(ConfigurableApplicationContext context) {}/*** 当应用上下文加载完毕时被调用。此时所有的 Bean 定义已经加载但没有刷新上下文。** param context Spring 应用上下文。*/default void contextLoaded(ConfigurableApplicationContext context) {}/*** 当应用上下文刷新并完全启动后被调用。这个方法包含了一个 Duration 参数表示启动所花费的时间。** param context Spring 应用上下文。* param timeTaken 启动所花费的时间。*/default void started(ConfigurableApplicationContext context, Duration timeTaken) {started(context);}/*** 这个方法已废弃不推荐继续使用。建议使用带 Duration 参数的 started 方法。** param context Spring 应用上下文。*/Deprecateddefault void started(ConfigurableApplicationContext context) {}/*** 当应用完全就绪并可以接受请求时被调用。这个方法包含了一个 Duration 参数表示从启动到就绪所花费的时间。** param context Spring 应用上下文。* param timeTaken 从启动到就绪所花费的时间。*/default void ready(ConfigurableApplicationContext context, Duration timeTaken) {running(context);}/*** 这个方法已废弃不推荐继续使用。建议使用带 Duration 参数的 ready 方法。** param context Spring 应用上下文。*/Deprecateddefault void running(ConfigurableApplicationContext context) {}/*** 当应用启动过程中发生错误时被调用。此时可以记录错误信息或进行其他的错误处理操作。** param context Spring 应用上下文。* param exception 启动过程中发生的异常。*/default void failed(ConfigurableApplicationContext context, Throwable exception) {} }在之前分析 SpringBoot 容器启动流程时我们故意忽略了 Spring Listener 的逻辑。我们仔细回顾一下源代码就会发现SpringApplication 会在完成一个阶段之后就会调用的 SpringApplicationRunListener 中相应的方法标志当前的处理阶段同时会执行一些该阶段的处理逻辑。 由此可见Spring 官方其实对容器生命周期是有自己的定义的 源码中有 failed但这个是容器异常的情况可能会在任意阶段发生所以不应该属于生命周期的某个指定阶段。 我们进一步分析 SpringApplicationRunListener 中的源码会发现一些方法被废弃了。废弃的方法有两种原因 有更好的替代方案比如 started() 方法更推荐使用有计算启动时间的方法 /*** 当应用上下文刷新并完全启动后被调用。这个方法包含了一个 Duration 参数表示启动所花费的时间。** param context Spring 应用上下文。* param timeTaken 启动所花费的时间。*/ default void started(ConfigurableApplicationContext context, Duration timeTaken) {started(context); }/*** 这个方法已废弃不推荐继续使用。建议使用带 Duration 参数的 started 方法。** param context Spring 应用上下文。*/ Deprecated default void started(ConfigurableApplicationContext context) {}阶段差异不大running 阶段和 ready 阶段差别不大直接使用 ready 阶段替代 default void ready(ConfigurableApplicationContext context, Duration timeTaken) {running(context); }/*** 这个方法已废弃不推荐继续使用。建议使用带 Duration 参数的 ready 方法。** param context Spring 应用上下文。*/ Deprecated default void running(ConfigurableApplicationContext context) {}所以我们知道对于 Spring 容器的生命周期其实并没有官方的明确定义官方也可能会对一些细节进行重新定义。但是我们可以采用官方在 SpringApplicationRunListener 中的定义来描述 Spring 容器的生命周期。这样做有几个好处 源码更能使人信服统一大家对 Spring 容器生命周期的理解最大限度拉齐认知帮助我们深入理解 Spring 官方团队的处理思路可以更好的理解源码 综上通常我们认为 Spring 容器的生命周期为 starting 时间点在 Spring Boot 应用启动之初run 方法被调用时。作用这个阶段最早发生可以用于执行一些初始化操作比如设置启动参数、记录日志等。 environmentPrepared 时间点在 Spring Boot 应用的 Environment包括系统属性、环境变量、配置文件等准备好之后。作用此阶段可以用来修改或添加环境属性或者做一些依赖于环境配置的初始化操作。 contextPrepared 时间点在 Spring 应用上下文创建之后但还未加载 BeanDefinition 之前。作用可以对应用上下文进行进一步配置设置一些全局属性或注册额外的组件。 contextLoaded 时间点在所有的 BeanDefinition 被加载但尚未刷新上下文之前。作用此阶段可以操作 BeanDefinition比如动态注册 Bean修改某些 Bean 的属性等。 started 时间点在应用上下文刷新并完全启动后。作用此时所有的单例 Bean 已经初始化完毕可以执行一些需要在 Bean 完全初始化后的操作如启动后台任务、初始化连接池等。 ready 时间点在应用完全准备好并能够响应请求时。作用此阶段标志着应用已经完全启动并准备就绪可以处理实际的业务请求。 5.2 Spring 容器的扩展点 之前我们在分析 SpringApplication 源码时已经发现了两个扩展点 容器的扩展点通过实现 BeanFactoryPostProcessor 或 BeanDefinitionRegistryPostProcessorBean 的扩展点通过实现 BeanPostProcessor Spring 是非常灵活的框架除了上述的扩展点Spring 也为生命周期的各个阶段提供了扩展点。Spring 容器生命周期的扩展点是通过 SpringApplicationRunListener 实现的监听器机制。 开发者只需要发布生命周期对应的事件就可以实现在生命周期的指定阶段实现特定功能。 starting 阶段对应 ApplicationStartingEvent 事件environmentPrepared 阶段对应 ApplicationEnvironmentPreparedEvent 事件contextPrepared 阶段对应 ApplicationContextInitializedEvent 事件contextLoaded 阶段对应 ApplicationPreparedEvent 事件started 阶段对应 ApplicationStartedEvent 事件ready 阶段对应 ApplicationReadyEvent 事件 六、小结 SpringApplication 源码中包含许多 Spring 容器的底层原理仔细阅读其源码可以帮助我们深入理解 SpringBoot。 推荐阅读 Spring 三级缓存深入了解 MyBatis 插件定制化你的持久层框架深入探究 Spring Boot Starter从概念到实践Zookeeper 注册中心单机部署【JavaScript】探索 JavaScript 中的解构赋值
http://www.w-s-a.com/news/700028/

相关文章:

  • 响应式网站检测工具营销公司业务范围
  • 网站源码如何安装做游戏课程网站
  • 选服务好的网站建设亚洲砖码砖专区2022
  • 网站快速查找wordpress 悬停 图片 文字
  • 网站续费 多久想自己做网站该学些什么
  • 可以自己做网站wordpress英文写作插件
  • 国外可以做会员网站的网站怎么查百度竞价关键词价格
  • 新站网站建设亚马逊关键词
  • 电商网站前端架构设计上海市建设工程安全生产协会网站
  • 东莞企业免费模版网站建设一般网站维护要多久
  • 著名建筑设计网站常州制作网站价格
  • 食品营销型网站广东省广州市白云区
  • 如何做网站哪个站推广描述对于营销型网站建设很重要飘红效果更佳
  • 济阳做网站公司99企业邮箱888
  • 国贸做网站的公司能接做网站的活的网站
  • 淮南建设厅网站上杭县建设局网站
  • 东莞做网站公司首选!西安注册公司费用
  • 做网站包括什么卖水果网站模板
  • 扬州网站建设外包wordpress 文章评分
  • 网站建设网站多少钱公司名字大全列表
  • 设计企业网站内容wordpress 投稿者 权限
  • seo网站推广免费价格低的成语
  • 做网站建设销售辛苦吗专题页是什么
  • 做网站的软件名字全拼wordpress可以上传文件吗
  • 建品牌网站公司关于asp_sql网站开发的书籍
  • 建网站公司营销型网站建设wordpress自定义登录页
  • 泉州市住房和城乡建设局网站淘宝店网站怎么做
  • 企业网站建设费未付款怎样挂账长春网站制作专业
  • 深圳找网站建设邹城市建设局网站
  • 长春火车站停运了吗网站开发概要设计