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/spring.factories 文件中加载对应的实现类并实例化,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 ,同样事件监听器也是在 spring.factories 文件中配置的,默认实现了以下监听器:

可以看到有用于文件编码的( FileEncodingApplicationListener ),有加载日志框架的( LoggingApplicationListener ),还有加载配置的( ConfigFileApplicationListener )等等一系列监听器,SpringBoot也就是通过这系列监听器将必要的配置和组件加载到容器中来,这里不再详细分析,感兴趣的读者可以通过其实现的 onApplicationEvent 方法看到每个监听器究竟是监听的哪一个事件,当然事件发布和监听我们自己也是可以扩展的。

SpringBoot最核心的还是自动配置,为什么它能做到开箱即用,不再需要我们手动使用 @EnableXXX 等注解来开启?这一切的答案就在 @SpringBootApplication 注解中:

这里重要的注解有三个: @SpringBootConfiguration 、 @EnableAutoConfiguration 、 @ComponentScan 。 @ComponentScan 就不用再说了, @SpringBootConfiguration 等同于 @Configuration ,而 @EnableAutoConfiguration 就是开启自动配置:

@AutoConfigurationPackage 注解的作用就是将该注解所标记类所在的包作为自动配置的包,简单看看就行,主要看 AutoConfigurationImportSelector ,这个就是实现自动配置的核心类,注意这个类是实现的 DeferredImportSelector 接口。

在这个类中有一个 selectImports 方法。这个方法在我之前的文章这一次搞懂Spring事务注解的解析也有分析过,只是实现类不同,它同样会被 ConfigurationClassPostProcessor 类调用,先来看这个方法做了些什么:

追踪源码最终可以看到也是从 META-INF/spring.factories 文件中拿到所有 EnableAutoConfiguration 对应的值(在 spring-boot-autoconfigure 中)并通过反射实例化,过滤后包装成 AutoConfigurationEntry 对象返回。

看到这里你应该会觉得自动配置的实现就是通过这个 selectImports 方法,但实际上这个方法通常并不会被调用到,而是会调用该类的内部类 AutoConfigurationGroup 的process和selectImports方法,前者同样是通过 getAutoConfigurationEntry 拿到所有的自动配置类,而后者这是过滤排序并包装后返回。

下面就来分析 ConfigurationClassPostProcessor 是怎么调用到这里的,直接进入 processConfigBeanDefinitions 方法:

前面一大段主要是拿到合格的 Configuration 配置类,主要逻辑是在 ConfigurationClassParser.parse 方法中,该方法完成了对 @Component 、 @Bean 、 @Import 、 @ComponentScans 等注解的解析,这里主要看对 @Import 的解析,其它的读者可自行分析。一步步追踪,最终会进入到 processConfigurationClass 方法:

这里需要注意 this.conditionEvaluator.shouldSkip 方法的调用,这个方法就是进行Bean加载过滤的,即根据 @Condition 注解的匹配值判断是否加载该Bean,具体实现稍后分析,继续跟踪主流程 doProcessConfigurationClass :

这里就是完成对一系列注解的支撑,我省略掉了,主要看 processImports 方法,这个方法就是处理 @Import 注解的:

刚刚我提醒过 AutoConfigurationImportSelector 是实现 DeferredImportSelector 接口的,如果不是该接口的实现类则是直接调用 selectImports 方法,反之则是调用 DeferredImportSelectorHandler.handle 方法:

首先创建了一个 DeferredImportSelectorHolder 对象,如果是第一次执行则是添加到 deferredImportSelectors 属性中,等到 ConfigurationClassParser.parse 的最后调用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 方法调用 beanFactory.getBeanNamesForType 拿到容器中对应的Bean实例,然后根据返回的结果判断哪些Bean存在,哪些Bean不存在(Condition注解中是可以配置多个值的)并返回MatchResult对象,而MatchResult中只要有一个Bean没有匹配上就返回false,也就决定了当前Bean是否需要实例化。

本篇分析了SpringBoot核心原理的实现,通过本篇相信读者也将能更加熟练地使用和扩展SpringBoot。

另外还有一些常用的组件我没有展开分析,如事务、MVC、监听器的自动配置,这些我们有了Spring源码基础的话下来看一下就明白了,这里就不赘述了。

最后读者可以思考一下我们应该如何自定义starter启动器,相信看完本篇应该难不倒你。

  • springboot妗嗘灦鍘熺悊鍙婃祦绋
    绛旓細Spring Boot鏄竴涓紑婧愮殑Java搴旂敤妗嗘灦锛屽畠鏃ㄥ湪绠鍖朣pring搴旂敤鐨勫垵濮嬫惌寤轰互鍙婂紑鍙戣繃绋嬨係pring Boot閫氳繃鎻愪緵榛樿閰嶇疆鍜屼竴绯诲垪蹇嵎鐗规э紝璁╁紑鍙戜汉鍛樿兘澶熸洿蹇熷湴鏋勫缓鍑虹敓浜х骇鍒殑Spring搴旂敤銆傚叾鏍稿績鍘熺悊涓昏鍩轰簬“绾﹀畾浼樹簬閰嶇疆”鐨勮璁$悊蹇碉紝閫氳繃鑷姩閰嶇疆鍜岃捣姝ヤ緷璧栨潵绠鍖栧紑鍙戞祦绋嬨傚湪鍘熺悊涓婏紝Spring Boot...
  • SpringBoot鏍稿績鍘熺悊:鑷姩閰嶇疆銆佷簨浠堕┍鍔銆丆ondition
    绛旓細SpringBoot鏍稿績鍘熺悊:鑷姩閰嶇疆銆佷簨浠堕┍鍔ㄣ丆ondition  鎴戞潵绛 1涓洖绛 #鐑# 鍥介檯娌逛环涓轰綍绐佺劧璺岀牬100缇庡厓澶у叧?浼氬摥鐨勭ぜ鐗17 2022-08-19 路 TA鑾峰緱瓒呰繃1287涓禐 鐭ラ亾绛斾富 鍥炵瓟閲:0 閲囩撼鐜:100% 甯姪鐨勪汉:0 鎴戜篃鍘荤瓟棰樿闂釜浜洪〉 鍏虫敞 灞曞紑鍏ㄩ儴 SpringBoot鏄疭pring鐨勫寘瑁,閫氳繃鑷姩閰嶇疆浣垮緱Sprin...
  • spring boot鍘熺悊
    绛旓細鍓嶇甯镐娇鐢ㄦā鏉垮紩鎿庯紝涓昏鏈塅reeMarker鍜孴hymeleaf锛屽畠浠兘鏄敤Java璇█缂栧啓鐨勶紝娓叉煋妯℃澘骞惰緭鍑虹浉搴旀枃鏈紝浣垮緱鐣岄潰鐨勮璁′笌搴旂敤鐨勯昏緫鍒嗙锛屽悓鏃跺墠绔紑鍙戣繕浼氫娇鐢ㄥ埌Bootstrap銆丄ngularJS銆丣Query绛夛紱鍦ㄦ祻瑙堝櫒鐨勬暟鎹紶杈撴牸寮忎笂閲囩敤Json锛岄潪xml锛屽悓鏃舵彁渚汻ESTfulAPI锛SpringMVC妗嗘灦鐢ㄤ簬鏁版嵁鍒拌揪鏈嶅姟鍣ㄥ悗澶勭悊璇锋眰锛涘埌鏁...
  • SpringBoot鍚姩鍘熺悊鍒嗘瀽
    绛旓細棣栧厛閬嶅巻鎵ц鎵鏈夐氳繃SpringFactoriesLoader锛屽湪褰撳墠classpath涓嬬殑META-INF/spring.factories涓煡鎵炬墍鏈夊彲鐢ㄧ殑SpringApplicationRunListeners骞跺疄渚嬪寲銆傝皟鐢ㄥ畠浠殑starting()鏂规硶锛岄氱煡杩欎簺鐩戝惉鍣SpringBoot搴旂敤鍚姩銆傚垱寤哄苟閰嶇疆褰撳墠SpringBoot搴旂敤灏嗚浣跨敤鐨凟nvironment锛屽寘鎷綋鍓嶆湁鏁堢殑PropertySource浠ュ強Profile銆傞亶鍘嗚皟鐢ㄦ墍鏈...
  • springboot starter 鍘熺悊瑙f瀽鍙婂疄璺
    绛旓細3銆佺劧鍚庡氨鏄疌onfiguration绫荤殑鍒涘缓锛岃繖涓被鏄痵tarter鑷姩鍒濆鍖栫殑鏍稿績绫伙紝璐熻矗鎶婁笟鍔$浉鍏崇殑bean鏅鸿兘鐨勫姞杞借繘鏉ャ4銆侀厤缃 spring.factories 锛岄氳繃璇ラ厤缃紝鎵嶈兘璁springboot鏉ヨ嚜鍔ㄥ姞杞芥垜浠殑Configuration绫汇傚叿浣鍘熺悊鎴戜滑绋嶅悗娣卞叆浜嗚В銆傚叿浣撶殑锛屾槸鍦ㄦā鍧楃殑 resources/META-INF 鐩綍涓嬶紝鏂板缓 spring.factories 鏂囦欢...
  • springboot鑷姩閰嶇疆鍘熺悊
    绛旓細springboot鑷姩閰嶇疆鍘熺悊鏄熀浜庢潯浠跺垽鏂潵閰嶇疆Bean銆俻ring Boot鐨勮嚜鍔ㄩ厤缃師鐞嗘槸鍩轰簬Spring妗嗘灦鐨勬潯浠跺寲閰嶇疆锛圕onditional Configuration锛夋満鍒跺疄鐜扮殑銆傚湪Spring Boot涓紝鑷姩閰嶇疆绫婚兘鏄娇鐢ˊConfiguration娉ㄨВ鏍囨敞鐨凧ava閰嶇疆绫伙紝骞朵笖浣跨敤浜嗗绉嶆潯浠舵敞瑙f潵鎺у埗鑷姩閰嶇疆鐨勬潯浠跺拰鑼冨洿銆傚綋鎸囧畾鐨勭被鍦ㄧ被璺緞涓瓨鍦ㄦ椂锛屾墠浼...
  • SpringBoot鑷姩瑁呴厤鍘熺悊
    绛旓細浠庢簮鐮佷腑鍙互鐭ラ亾锛屾渶鍏抽敭鐨勮灞濦Import(EnableAutoConfigurationImportSelector.class)锛屽熷姪EnableAutoConfigurationImportSelector锛孈EnableAutoConfiguration鍙互甯姪SpringBoot搴旂敤灏嗘墍鏈夌鍚堟潯浠剁殑@Configuration閰嶇疆閮藉姞杞藉埌褰撳墠SpringBoot鍒涘缓骞朵娇鐢ㄧ殑IoC瀹瑰櫒銆傚悓鏃跺熷姪浜嶴pring妗嗘灦鍘熸湁鐨勪竴涓伐鍏风被锛歋pringFactories...
  • SpringBoot搴旂敤鍚姩鍘熺悊(浜) 鎵╁睍URLClassLoader瀹炵幇宓屽jar鍔犺浇_鐧惧害鐭...
    绛旓細鍦ㄤ笂绡囨枃绔犮SpringBoot搴旂敤鍚姩鍘熺悊(涓) 灏嗗惎鍔ㄨ剼鏈祵鍏ar銆嬩腑浠嬬粛浜哠pringBoot濡備綍灏嗗惎鍔ㄨ剼鏈笌Runnable Jar鏁村悎涓篍xecutable Jar鐨勫師鐞嗭紝浣垮緱鐢熸垚鐨刯ar/war鏂囦欢鍙互鐩存帴鍚姩 鏈瘒灏嗕粙缁峉pringBoot濡備綍鎵╁睍URLClassLoader瀹炵幇宓屽jar鐨勭被(璧勬簮)鍔犺浇锛屼互鍚姩鎴戜滑鐨勫簲鐢ㄣ傞鍏堬紝浠庝竴涓畝鍗曠殑绀轰緥寮濮 build....
  • spring boot鏄粈涔堟鏋
    绛旓細瀹冪殑鏍稿績鐞嗗康鏄氳繃鑷姩閰嶇疆鍜屽噺灏戝熀纭璁炬柦绠$悊锛岃寮鍙戣呰兘澶熸洿涓撴敞浜庝笟鍔¢昏緫銆傚綋鎮ㄤ娇鐢 Spring Boot锛屽畠浼氳嚜鍔ㄨ瘑鍒苟娣诲姞绫昏矾寰勪笂鎵闇鐨勭粍浠讹紝濡係pring MVC鎵闇鐨勭壒瀹歜ean鍜屽唴缃殑Servlet瀹瑰櫒锛堝宓屽叆寮廡omcat鎴朖etty锛夈傚畠杩樻敮鎸佽濡俆hymeleaf杩欐牱鐨勬ā鏉垮紩鎿庯紝浼氳嚜鍔ㄦ坊鍔犵浉鍏砨ean鍒颁笂涓嬫枃涓紝浣嗗張鍏佽鎮...
  • spring boot鑷姩瑁呴厤鍘熺悊
    绛旓細棣栧厛鎵撳紑涓涓熀鏈殑springboot椤圭洰锛岀偣杩涘幓@SpringBootApplication娉ㄨВ銆傚彲浠ユ牴鎹悕瀛楃煡閬撳疄鐜拌嚜鍔ㄨ閰嶅簲璇ユ槸涓婇潰鐨凘EnableAutoConfiguration娉ㄨВ锛岀户缁偣杩涘幓 杩欐椂鍊欏spring娉ㄨВ姣旇緝浜嗚В鐨勫悓瀛﹀簲璇ヨ兘鎰熻鍒板疄鐜鍘熺悊灏卞湪@Import(AutoConfigurationImportSelector.class)杩欎釜娉ㄨВ涓紝@Import娉ㄨВ鐨勫弬鏁板彲浠ユ槸闈欐佺被锛堢敤浣...
  • 扩展阅读:free xbox live code ... zookeeper与java交互小结 ... 欧洲vps windows网站 ... springboot实战视频 ... web前端三大主流框架 ... springboot三大核心注解 ... spring boot基本原理 ... spring boot入门教程 ... spring boot三层架构 ...

    本站交流只代表网友个人观点,与本站立场无关
    欢迎反馈与建议,请联系电邮
    2024© 车视网