深入理解其工作原理和最佳实践-SpringBoot的配置加载机制 (深入理解yii2)
概述
SpringBoot 的配置加载机制基于 Environment 的属性源管理,它提供了一种非常灵活的方式来管理应用程序的配置信息。Environment 支持多种配置格式,可以根据不同的环境和需求加载不同的配置,而且还支持属性绑定到 Java 对象,使得配置更加类型安全。Environment
Environment 是 SpringBoot 中配置管理的核心组件。它负责加载和管理从各种来源收集的配置属性。这些来源包括: 系统属性 环境变量 properties 文件 YAML 文件 命令行参数 Environment 提供了以下方法来访问配置属性: `getProperty(String name)`:获取指定名称的属性值,如果没有找到则返回 null。 `getProperty(String name, String defaultValue)`:获取指定名称的属性值,如果没找到则返回默认值。 `containsProperty(String name)`:检查是否存在指定名称的属性。 `getAllPropertySources()`:获取所有配置属性源的列表。配置格式
SpringBoot 支持多种配置格式,包括: Properties 文件: 这是最常用的配置格式,它是一个文本文件,其中属性以 `key=value` 对的形式存储。 YAML 文件: YAML 是一种类似于 JSON 的数据序列化格式,它比 Properties 文件更简洁。 JSON 文件: JSON 是一种流行的数据交换格式,它可以表示复杂的数据结构。 命令行参数: 命令行参数是传递给应用程序的字符串参数,它们可以用于设置配置属性。配置加载
SpringBoot 在启动时会自动加载配置。它会从所有配置源收集属性,并将其合并到 Environment 中。属性的加载顺序如下: 1. 系统属性 2. 环境变量 3. properties 文件 4. YAML 文件 5. 命令行参数 如果同一属性存在于多个配置源中,则优先级较高的配置源中的值将被使用。属性绑定
SpringBoot 支持将配置属性绑定到 Java 对象。这可以使配置更加类型安全,并简化对配置的访问。要绑定属性,可以使用 `@ConfigurationProperties` 注解。 例如,以下代码将 `application.properties` 文件中的 `my.config` 属性绑定到 `MyConfig` 对象: ```java @ConfigurationProperties("my.config") public class MyConfig { private String property1; private int property2; // getter 和 setter 方法... } ``` 在应用程序启动后,可以使用 `@Autowired` 注解来注入 `MyConfig` 对象,然后访问其属性。总结
SpringBoot 的配置加载机制基于 Environment 的属性源管理,它提供了一种非常灵活的方式来管理应用程序的配置信息。它支持多种配置格式,可以根据不同的环境和需求加载不同的配置,而且还支持属性绑定到 Java 对象,使得配置更加类型安全。springboot框架原理及流程
Spring Boot框架原理及流程Spring Boot是一个开源的Java应用框架,它旨在简化Spring应用的初始搭建以及开发过程。 Spring Boot通过提供默认配置和一系列快捷特性,让开发人员能够更快速地构建出生产级别的Spring应用。 其核心原理主要基于“约定优于配置”的设计理念,通过自动配置和起步依赖来简化开发流程。 在原理上,Spring Boot利用了Spring框架原有的功能,并在此基础上进行了增强和优化。 它提供了一套自动化的配置机制,能够根据项目的依赖关系自动配置Spring应用。 这意味着,开发人员无需手动编写大量的配置代码,Spring Boot会根据项目的结构和添加的依赖自动进行配置。 例如,当项目中添加了Spring Web的依赖时,Spring Boot会自动配置Tomcat作为Web服务器,并设置好相关的Web配置项。 在流程上,使用Spring Boot开发应用通常遵循以下步骤:首先,创建一个Spring Boot项目,可以通过Spring Initializr或IDE的插件来快速生成项目骨架。 然后,在项目中添加所需的依赖,Spring Boot提供了一系列起步依赖,这些依赖包含了开发特定类型应用所需的所有库和配置。 接下来,编写业务代码,由于Spring Boot已经自动配置好了大部分基础设施,开发人员可以专注于业务逻辑的实现。 最后,通过运行Spring Boot的主类来启动应用,Spring Boot会自动检测应用的配置和组件,并启动内置的Web服务器来提供服务。 举个例子,假设我们要开发一个Web应用,使用Spring Boot可以大大简化开发流程。 我们只需要创建一个Spring Boot项目,添加Spring Web的起步依赖,然后编写控制器和业务逻辑代码。 Spring Boot会自动配置Tomcat服务器和相关的Web配置项,我们只需要运行应用的主类,就可以通过浏览器访问Web服务了。 这种简化的开发流程大大提高了开发效率,减少了出错的可能性,让开发人员能够更专注于业务功能的实现。
SpringBoot核心原理:自动配置、事件驱动、Condition
SpringBoot是Spring的包装,通过自动配置使得SpringBoot可以做到开箱即用,上手成本非常低,但是学习其实现原理的成本大大增加,需要先了解熟悉Spring原理。 如果还不清楚Spring原理的,可以先查看博主之前的文章,本篇主要分析SpringBoot的启动、自动配置、Condition、事件驱动原理。 SpringBoot启动非常简单,因其内置了Tomcat,所以只需要通过下面几种方式启动即可: 可以看到第一种是最简单的,也是最常用的方式,需要注意类上面需要标注@SpringBootApplication 注解,这是自动配置的核心实现,稍后分析,先来看看SpringBoot启动做了些什么? 在往下之前,不妨先猜测一下,run方法中需要做什么?对比Spring源码,我们知道,Spring的启动都会创建一个ApplicationContext 的应用上下文对象,并调用其refresh方法启动容器,SpringBoot只是Spring的一层壳,肯定也避免不了这样的操作。 另一方面,以前通过Spring搭建的项目,都需要打成War包发布到Tomcat才行,而现在SpringBoot已经内置了Tomcat,只需要打成Jar包启动即可,所以在run方法中肯定也会创建对应的Tomcat对象并启动。 以上只是我们的猜想,下面就来验证,进入run方法: SpringBoot的启动流程就是这个方法,先看getRunListeners 方法,这个方法就是去拿到所有的 SpringApplicationRunListener 实现类,这些类是用于SpringBoot事件发布的,关于事件驱动稍后分析,这里主要看这个方法的实现原理: 一步步追踪下去可以看到最终就是通过SPI机制根据接口类型从META-INF/ 文件中加载对应的实现类并实例化,SpringBoot的自动配置也是这样实现的。 为什么要这样做呢?通过注解扫描不可以么?当然不行,这些类都在第三方jar包中,注解扫描实现是很麻烦的,当然你也可以通过@Import 注解导入,但是这种方式不适合扩展类特别多的情况,所以这里采用SPI的优点就显而易见了。 回到run方法中,可以看到调用了createApplicationContext 方法,见名知意,这个就是去创建应用上下文对象: 注意这里通过反射实例化了一个新的没见过的上下文对象AnnotationConfigServletWebServerApplicationContext ,这个是SpringBoot扩展的,看看其构造方法: 如果你有看过Spring注解驱动的实现原理,这两个对象肯定不会陌生,一个实支持注解解析的,另外一个是扫描包用的。 上下文创建好了,下一步自然就是调用refresh方法启动容器: 这里首先会调用到其父类中ServletWebServerApplicationContext : 可以看到是直接委托给了父类: 这个方法不会陌生吧,之前已经分析过了,这里不再赘述,至此SpringBoot的容器就启动了,但是Tomcat启动是在哪里呢?run方法中也没有看到。 实际上Tomcat的启动也是在refresh流程中,这个方法其中一步是调用了onRefresh方法,在Spring中这是一个没有实现的模板方法,而SpringBoot就通过这个方法完成了Tomcat的启动: 这里首先拿到TomcatServletWebServerFactory 对象,通过该对象再去创建和启动Tomcat: 上面的每一步都可以对比Tomcat的配置文件,需要注意默认只支持了http协议: 如果想要扩展的话则可以对additionalTomcatConnectors 属性设置值,需要注意这个属性没有对应的setter方法,只有 addAdditionalTomcatConnectors 方法,也就是说我们只能通过实现 BeanFactoryPostProcessor 接口的 postProcessBeanFactory 方法,而不能通过 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法,因为前者可以通过传入的BeanFactory对象提前获取到 TomcatServletWebServerFactory 对象调用 addAdditionalTomcatConnectors 即可;而后者只能拿到BeanDefinition对象,该对象只能通过setter方法设置值。 这段代码会在控制台打印所有的事件名称,按照顺序如下: 以上是正常启动关闭,如果发生异常还有发布ApplicationFailedEvent 事件。 事件的发布遍布在整个容器的启动关闭周期中,事件发布对象刚刚我们也看到了是通过SPI加载的 SpringApplicationRunListener 实现类 EventPublishingRunListener ,同样事件监听器也是在 文件中配置的,默认实现了以下监听器: 可以看到有用于文件编码的(FileEncodingApplicationListener ),有加载日志框架的( LoggingApplicationListener ),还有加载配置的( ConfigFileApplicationListener )等等一系列监听器,SpringBoot也就是通过这系列监听器将必要的配置和组件加载到容器中来,这里不再详细分析,感兴趣的读者可以通过其实现的 onApplicationEvent 方法看到每个监听器究竟是监听的哪一个事件,当然事件发布和监听我们自己也是可以扩展的。 SpringBoot最核心的还是自动配置,为什么它能做到开箱即用,不再需要我们手动使用@EnableXXX 等注解来开启?这一切的答案就在 @SpringBootApplication 注解中: 这里重要的注解有三个:@SpringBootConfiguration 、 @EnableAutoConfiguration 、 @ComponentScan 。 @ComponentScan 就不用再说了, @SpringBootConfiguration 等同于 @Configuration ,而 @EnableAutoConfiguration 就是开启自动配置: @AutoConfigurationPackage 注解的作用就是将该注解所标记类所在的包作为自动配置的包,简单看看就行,主要看 AutoConfigurationImportSelector ,这个就是实现自动配置的核心类,注意这个类是实现的 DeferredImportSelector 接口。 在这个类中有一个selectImports 方法。 这个方法在我之前的文章这一次搞懂Spring事务注解的解析也有分析过,只是实现类不同,它同样会被 ConfigurationClassPostProcessor 类调用,先来看这个方法做了些什么: 追踪源码最终可以看到也是从META-INF/ 文件中拿到所有 EnableAutoConfiguration 对应的值(在 spring-boot-autoconfigure 中)并通过反射实例化,过滤后包装成 AutoConfigurationEntry 对象返回。 看到这里你应该会觉得自动配置的实现就是通过这个selectImports 方法,但实际上这个方法通常并不会被调用到,而是会调用该类的内部类 AutoConfigurationGroup 的process和selectImports方法,前者同样是通过 getAutoConfigurationEntry 拿到所有的自动配置类,而后者这是过滤排序并包装后返回。 下面就来分析ConfigurationClassPostProcessor 是怎么调用到这里的,直接进入 processConfigBeanDefinitions 方法: 前面一大段主要是拿到合格的Configuration 配置类,主要逻辑是在 方法中,该方法完成了对 @Component 、 @Bean 、 @Import 、 @ComponentScans 等注解的解析,这里主要看对 @Import 的解析,其它的读者可自行分析。 一步步追踪,最终会进入到 processConfigurationClass 方法: 这里需要注意 方法的调用,这个方法就是进行Bean加载过滤的,即根据 @Condition 注解的匹配值判断是否加载该Bean,具体实现稍后分析,继续跟踪主流程 doProcessConfigurationClass : 这里就是完成对一系列注解的支撑,我省略掉了,主要看processImports 方法,这个方法就是处理 @Import 注解的: 刚刚我提醒过AutoConfigurationImportSelector 是实现 DeferredImportSelector 接口的,如果不是该接口的实现类则是直接调用 selectImports 方法,反之则是调用 方法: 首先创建了一个DeferredImportSelectorHolder 对象,如果是第一次执行则是添加到 deferredImportSelectors 属性中,等到 的最后调用process方法: 反之则是直接执行,首先通过register拿到AutoConfigurationGroup 对象: 然后在processGroupImports 方法中进行真正的处理: 在getImports 方法中就完成了对process和 selectImports 方法的调用,拿到自动配置类后再递归调用调用 processImports 方法完成对自动配置类的加载。 至此,自动配置的加载过程就分析完了,下面是时序图: 在自动配置类中有很多Condition相关的注解,以AOP为例: 这里就能看到@ConditionalOnProperty 、 @ConditionalOnClass 、 @ConditionalOnMissingClass ,另外还有 @ConditionalOnBean 、 @ConditionalOnMissingBean 等等很多条件匹配注解。 这些注解表示条件匹配才会加载该Bean,以@ConditionalOnProperty 为例,表明配置文件中符合条件才会加载对应的Bean,prefix表示在配置文件中的前缀,name表示配置的名称, havingValue 表示配置为该值时才匹配, matchIfMissing 则是表示没有该配置是否默认加载对应的Bean。 其它注解可类比理解记忆,下面主要来分析该注解的实现原理。 这里注解点进去看会发现每个注解上都标注了@Conditional 注解,并且value值都对应一个类,比如 OnBeanCondition ,而这些类都实现了 Condition 接口,看看其继承体系: 上面只展示了几个实现类,但实际上Condition的实现类是非常多的,我们还可以自己实现该接口来扩展@Condition 注解。 Condition接口中有一个matches方法,这个方法返回true则表示匹配。 该方法在 ConfigurationClassParser 中多处都有调用,也就是刚刚我提醒过的shouldSkip方法,具体实现是在 ConditionEvaluator 类中: 再来看看matches的实现,但OnBeanCondition 类中没有实现该方法,而是在其父类 SpringBootCondition 中: getMatchOutcome 方法也是一个模板方法,具体的匹配逻辑就在这个方法中实现,该方法返回的 ConditionOutcome 对象就包含了是否匹配和日志消息两个字段。 进入到 OnBeanCondition 类中: 可以看到该类支持了@ConditionalOnBean 、 @ConditionalOnSingleCandidate 、 @ConditionalOnMissingBean 注解,主要的匹配逻辑在 getMatchingBeans 方法中: 这里逻辑看起来比较复杂,但实际上就做了两件事,首先通过getNamesOfBeansIgnoredByType 方法调用 拿到容器中对应的Bean实例,然后根据返回的结果判断哪些Bean存在,哪些Bean不存在(Condition注解中是可以配置多个值的)并返回MatchResult对象,而MatchResult中只要有一个Bean没有匹配上就返回false,也就决定了当前Bean是否需要实例化。 本篇分析了SpringBoot核心原理的实现,通过本篇相信读者也将能更加熟练地使用和扩展SpringBoot。 另外还有一些常用的组件我没有展开分析,如事务、MVC、监听器的自动配置,这些我们有了Spring源码基础的话下来看一下就明白了,这里就不赘述了。 最后读者可以思考一下我们应该如何自定义starter启动器,相信看完本篇应该难不倒你。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。