(1)从classpath中搜索所有META-INF/spring.factories配置文件,然后将其中
org.springframework.boot.autoconfigure.EnableAutoConfiguration的Key对应的配置项加载到Spring容器。
(2)@EnableAutoConfiguration可以排除配置选项,排除方式有两 种 , 一 种 方 式 是 在 使 用 @SpringBootApplication 注 解 时 , 使 用exc 需要zi料+ 绿色徽【vip1024b】
lude属性排除指定的类,代码如下:
另外一种方式是:单独使用@EnableAutoConfiguration注解,其内部的关键代码实现如下:
@ComponentScan注解解析
@ComponentScan注解代码如下:
@ComponentScan注解本身是Spring框架加载Bean的主要组件,它并不是Spring Boot的新功能,这里不对@ComponentScan扫描和解析Bean的过程进行详细说明,感兴趣的读者可以自行查阅资料进行了解。
@ComponentScan注解的作用总结一句话就是:定义扫描路径,默认会扫描该类所在的包下所有符合条件的组件和Bean定义,最终将这些Bean加载到Spring容器中。下面是我们总结的@ComponentScan的主要使用方式:
● @ComponentScan注解默认会装配标识了@Component注解的类到Spring容器中。
● 通过basepackage可以指定扫描包的路径。
● 通过includeFilters将扫描路径下没有以上注解的类加入Spring容器。
● 通过excludeFilters过滤出不用加入Spring容器的类。
Spring Boot启动流程进阶
=================
每一个Spring Boot程序都有一个主入口,这个主入口就是main方法,而main方法中都会调用SpringBootApplication.run方法,一个快速了解SpringBootApplication启动过程的好方法就是在run方法中打一个断点,然后通过Debug的模式启动工程,逐步跟踪了解SpringBoot源码是如何完成环境准备和启动加载Bean的。
查看SpringBootApplication.run方法的源码就可以发现SpringBoot的启动流程主要分为两个大的阶段:初始化SpringApplication和运行SpringApplication。而运行SringApplication的过程又可以细化为下面几个部分,后面我们会对启动的主要模块加以详解。
初始化SpringApplication
步骤1进行SpringApplication的初始化,配置基本的环境变量、资 源 、 构 造 器 、 监 听 器 。 初 始 化 阶 段 的 主 要 作 用 是 为 运 行SpringApplication对象实例启动做环境变量准备以及进行必要资源构造器的初始化动作,代码如下:
SpringApplication构造方法的核心是this.initialize(sources)初始化方法,SpringApplication通过调用该方法完成初始化工作。deduceWebEnvironment方法用来判断当前应用的环境,该方法通过获取两个类来判断当前环境是否是Web环境。
而
getSpringFactoriesInstances方法主要用来从spring.factories文件中找出Key为ApplicationContextInitializer的类并实例化,然后调用setInitializers方法设置到SpringApplication的initializers属性中,找到它所有应用的初始化器。接着调用setListeners方法设置应用监听器,这个过程可以找到所有应用程序的监听器,最后找到应用启动主类名称。
运行SpringApplication
步骤2 Spring Boot正式地启动加载过程,包括启动流程监控模块、配置环境加载模块、ApplicationContext容器上下文环境加载模块。refreshContext方法刷新应用上下文并进行自动化配置模块加载,也就是上文提到的SpringFactoriesLoader根据指定classpath加载META-INF/spring.factories文件的配置,实现自动配置核心功能。
运行SpringApplication的主要代码如下:
1.SpringApplicationRunListeners应用启动监控模块
应用启动监控模块对应上述步骤2.1,它创建了应用的监听器
SpringApplicationRunListeners 并 开 始 监 听 , 监 听 模 块 通 过 调 用getSpringFactoriesInstances私有协议从METAINF/spring.factories文件中取得SpringApplicationRunListeners监听器实例。
当前的事件监听器
SpringApplicationRunListeners中只有一个EventPublishingRunListener广播事件监听器,它的Starting方法会封装成SpringApplicationEvent事件广播出去,被SpringApplication中配置的listeners所监听。这一步骤执行完成后也会同时通知SpringBoot其他模块目前监听初始化已经完成,可以开始执行启动方案了。
2.ConfigurableEnvironment配置环境模块和监听
对应上述步骤2.2,下面是分解步骤说明。
(1)创建配置环境,对应上述步骤2.2.1,创建应用程序的环境信息。如果是Web程序,创建
StandardServletEnvironment,否则创建StandardEnvironment。
(2)加载属性配置文件,对应上述步骤2.2.2,将配置环境加入监 听 器 对 象 中 (
SpringApplicationRunListeners ) 。 通 过configurePropertySources方法设置properties配置文件,通过执行configureProfiles方法设置profiles。
(3)配置监听,对应上述步骤2.2.3,发布environmentPrepared事件,即调用ApplicationListener的onApplicationEvent事件,通知Spring Boot应用的environment已经准备完成。
3.ConfigurableApplicationContext配置应用上下文
对应上述步骤2.3,下面是分解步骤说明。
(1)配置Spring应用容器上下文对象,对应上述步骤2.3.1,它的作用是创建run方法的返回对象
ConfigurableApplicationContext(应用配置上下文),此类主要继承 了 ApplicationContext 、 Lifecycle 、 Closeable 接 口 , 而ApplicationContex是Spring框架中负责Bean注入容器的主要载体,负责Bean加载、配置管理、维护Bean之间的依赖关系及Bean的生命周期管理。
(2)配置基本属性,对应上述步骤2.3.2,prepareContext方法将listeners、environment、banner、applicationArguments等重要组件与Spring容器上下文对象相关联。借助SpringFactoriesLoader查找可用的
ApplicationContextInitializer,它的initialize方法会对创 建 好 的 ApplicationContext 进 行 初 始 化 , 然 后 它 会 调 用SpringApplicationRunListener的contextPrepared方法,此时SpringBoot应用的ApplcaionContext已经准备就绪,为刷新应用上下文准备好容器。
( 3 ) 刷 新 应 用 上 下 文 , 对 应 上 述 的 步 骤 2.3.3 ,refreshContext(context)方法将通过工厂模式产生应用上下文环境中所需要的Bean。实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载、Bean的实例化等核心工作。最后
SpringApplicationRunListener调用finished方法告诉Spring Boot应用程序容器已经完成ApplicationContext装载。
Spring Boot自动装配机制
=================
Spring Boot的快速发展壮大,得益于“约定优于配置”的理念。
Spring Boot 自 动 装 配 流 程 中 最 核 心 的 注 解 是@EnableAutoConfiguration,在上一节的启动流程中我们已经讲过,它 可 以 借 助 SpringFactoriesLoader“ 私 有 协 议 特 性 ” 将 标 注 了@Configuration的JavaConfig全部加载到Spring容器中,而如果是基于条件的装配及调整顺序的Bean装配,需要Spring Boot有额外的自动化装配机制。下面从@EnableAutoConfiguration开始进阶讲解,加深我们对Spring Boot自动装配机制的认识。
基于条件的自动装配
下面是@EnableAutoConfiguration注解,它同样是一个组合注解:
从源码可见,最关键的就是@Import(
AutoConfigurationImportSelector.class)注解的实现。借助EnableAutoConfigurationImportSelector模块,@EnableAutoConfiguration可以帮助Spring Boot应用将所有符合条件的@Configuration配置都加载到当前的容器中。同时借助Spring框架原有的底层工具SpringFactoriesLoader(服务发现机制)和根据特定条件装备Bean的Conditionxxx条件注解实现智能的自动化配置工作。
Bean的加载过滤过程主要是通过下面的方法实现的。
源码解析如下:
(1)执行
AutoConfigurationMetadataLoader.loadMetadata ( this.beanClassLoader)会加载META-INF/spring-autoconfiguremetadata.properties下的所有配置信息。
(2)执行
getCandidateConfigurations(annotationMetadata,attributes)会加载所有包下META-INF/spring.factories的信息并组装成Map,然后读取Key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的数组,并将这个数组返回。
(3)执行getExclusions(annotationMetadata,attributes)会获取限制候选配置的所有排除项(找到不希望被自动装配的配置类)。
( 4 ) 执 行 checkExcludedClasses ( configurations ,exclusions)会对参数exclusions进行验证并去除多余的类,它对应@EnableAutoConfiguration注解中的exclusions属性。
(5)执行filter(configurations,autoConfigurationMetadata ) 会 根 据 项 目 中 配 置 的
AutoConfiguration-ImportFilter类进行配置过滤。
通过查看源码,我们可以发现
AutoConfigurationImportFilter是一个接口,OnClassCondition才是它的实现类,而OnClassCondition就是Spring Boot的Condition实现类。@ConditionalOnClass代码如下:
@ConditionalOnClass是基于@Conditional的组合注解,在上述的第(5)步中,Spring Boot可以通过这个注解实现按需加载,只有在@Configuration中符合条件的Class才会被加载进来。@Conditional注解本身是一个元注解,用来标注其他注解,如下所示:
通过利用@Conditional元注解,可以构造满足自己条件的组合条件注解,Spring Boot正是通过这样的方式实现了众多条件注解,实现了基于条件的Bean构造,还有Bean相互依赖情况下的顺序加载,它不需要再通过显性的基于XML文件的依赖文件进行构造。从上述讲解我们可以知道,Spring Boot结合Java元注解概念、Spring底层容器配置机制,以及使用类似Java SPI(Service Provider Interface)机制实现的私有配置加载协议,最终实现了“约定优于配置”。
在Spring Boot的Autoconfigure模块中,还包含了一批这样的组合注解,这些条件的限制在Spring Boot中以注解的形式体现,通常这些条件注解使用@Conditional来配合@Configuration和@Bean等注解来干预Bean的生成,常见的条件注解如下。
● @ConditionalOnBean:Spring容器中存在指定Bean时,实例化当前Bean。
● @ConditionalOnClass:Spring容器中存在指定Class时,实例化当前Bean。
● @ConditionalOnExpression:使用SpEL表达式作为判断条件,满足条件时,实例化当前Bean。
● @ConditionalOnJava:使用JVM版本作为判断条件来实例化当前Bean。
● @ConditionalOnJndi:在JNDI存在时查找指定的位置,满足条件时,实例化当前Bean。
● @ConditionalOnMissingBean:Spring容器中不存在指定Bean时,实例化当前Bean。
● @ConditionalOnMissingClass : Spring 容 器 中 不 存 在 指 定Class时,实例化当前Bean。
● @
ConditionalOnNotWebApplication:当前应用不是Web项目时,实例化当前Bean。
● @ConditionalOnProperty:指定的属性是否有指定的值。
● @ConditionalOnResource:类路径是否有指定的值。
● @
ConditionalOnSingleCandidate:指定Bean在Spring容器中只有一个。
● @
ConditionalOnWebApplication:当前应用是Web项目时,则实例化当前Bean。
有了组合注解,开发人员从大量的XML和Properties中得到了解放,可以抛弃Spring传统的外部配置,使用Spring自动配置,springboot-autoconfigure依赖默认配置项,根据添加的依赖自动加载相关的配置属性并启动依赖。应用者只需要引入对应的jar包,SpringBoot就可以自动扫描和加载依赖信息。调整自动配置顺序在Spring Boot的Autoconfigure模块中还可以通过注解对配置和组件的加载顺序做出调整,从而可以让这些存在依赖关系的配置和组件顺利地在Spring容器中被构造出来。
● @AutoConfigureAfter是spring-boot-autoconfigure包下的注解,其作用是将一个配置类在另一个配置类之后加载。
● @AutoConfigureBefore是spring-boot-autoconfigure包下的注解,其作用是将一个配置类在另一个配置类之前加载。
例如,在加载ConfigurationB之后加载ConfigurationA:
○ 实现ConfigurationA.class
○ 实现ConfigurationB.class
○ 创建配置META-INF/spring.factories文件
通过上面的步骤,就可以实现自动调整Bean的加载顺序。另外,Spring为我们提供了@AutoConfigureOrder注解,也可以修改配置文件的加载顺序,示例代码如下:
自动化配置流程
无论是应用初始化还是具体的执行过程,都要调用Spring Boot自动配置模块,下图有助于我们形象地理解自动配置流程。
例 如 ,
mybatis-spring-boot-starter 、 spring-boot-starterweb等组件的META-INF下均含有spring.factories文件,在自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,最后工厂实例来生成组件所需要的Bean。
最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分
例 如 ,
mybatis-spring-boot-starter 、 spring-boot-starterweb等组件的META-INF下均含有spring.factories文件,在自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,最后工厂实例来生成组件所需要的Bean。
[外链图片转存中…(img-3sxB7RyD-1710369359209)]
最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分
[外链图片转存中…(img-fGqeEZzt-1710369359210)]