diff --git "a/Spring/2 Spring346円230円257円345円246円202円344円275円225円345円256円236円347円216円260円344円272円213円344円273円266円347円233円221円345円220円254円346円234円272円345円210円266円347円232円204円357円274円237円 Spring346円272円220円347円240円201円357円274円210円344円272円214円357円274円211円.md" "b/Spring/2 Spring346円230円257円345円246円202円344円275円225円345円256円236円347円216円260円344円272円213円344円273円266円347円233円221円345円220円254円346円234円272円345円210円266円347円232円204円357円274円237円 Spring346円272円220円347円240円201円357円274円210円344円272円214円357円274円211円.md" index e528c89..cb33a16 100644 --- "a/Spring/2 Spring346円230円257円345円246円202円344円275円225円345円256円236円347円216円260円344円272円213円344円273円266円347円233円221円345円220円254円346円234円272円345円210円266円347円232円204円357円274円237円 Spring346円272円220円347円240円201円357円274円210円344円272円214円357円274円211円.md" +++ "b/Spring/2 Spring346円230円257円345円246円202円344円275円225円345円256円236円347円216円260円344円272円213円344円273円266円347円233円221円345円220円254円346円234円272円345円210円266円347円232円204円357円274円237円 Spring346円272円220円347円240円201円357円274円210円344円272円214円357円274円211円.md" @@ -502,7 +502,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader 小结:这篇文章是本人第二篇源码解析的文章,写作速度仍然很慢,希望以后思路捋清楚后能快点写完,加油。 +/Users/zhangyuhang/Library/Application Support/JetBrains/IntelliJIdea2020.1/plugins +/Users/zhangyuhang/Library/Application Support/JetBrains/IntelliJIdea2020.1/port 参考: diff --git "a/Spring/Spring345円270円270円347円224円250円346円263円250円350円247円243円(344円270円211円).md" "b/Spring/Spring345円270円270円347円224円250円346円263円250円350円247円243円(344円270円211円).md" new file mode 100644 index 0000000..e0b18f7 --- /dev/null +++ "b/Spring/Spring345円270円270円347円224円250円346円263円250円350円247円243円(344円270円211円).md" @@ -0,0 +1,77 @@ +# 1 Spring常用注解 + + + +# 2 Spring 注解注册bean + +## 2.1 @Configuration + +```java +@Import(ConditionConfig.class)//参见https://www.cnblogs.com/iiiiiher/p/12781618.html +@Configuration //该注解相当于xml +public class BasicConfig { + @Bean//默认不指定名字,则bean id是返回值类型名字,该注解相当于xml中 + //@Primary + public Student getStu1() { + return new Student("小张", 12); + } + + @Bean + public Student getStu2() { + return new Student("小李", 20); + } +} + +// id name https://www.cnblogs.com/xiewuqing/p/4678407.html +// bean name https://www.cnblogs.com/1540340840qls/p/6962777.html +// Spring创建bean默认的id值 https://www.jianshu.com/p/77d0ec46703c +//spring中bean配置和bean注入 https://www.cnblogs.com/wuchanming/p/5426746.html +``` + + + + + +## 2.2 @Bean + +注意,形参默认使用@Autowire + +xml name type + +## 2.3 @Import + +## 2.4 @Primary + +## 2.5 @Component + +### 2.5.1 @Controller和@RestfulController + +### 2.5.2 @Service + +### 2.5.3 @Repository + +# 3 Spring 使用注解注入 + +## 3.1 @Autoware和@Qualifier + +默认是通过类型(byType)找bean,如存在多个类型则通过名称(byName)找bean + +```java + +``` + +三种解决方案 + +1、注册bean 使用@Primary + +2、注册bean时指定注入时属性名称为bean name,覆盖name=默认方法名;这种不合适声明(注册)bean和使用(注入)耦合过密bean + +3、注入时,使用@Qualifier注解显式指定bean name(推荐) + +## 3.2 @Resource ? + +首先`@Resource`不是Spring的注解,是JavaEE的注解,默认是先按照名称(byName)找bean,如名称无找到则通过类型(byType) + +https://blog.csdn.net/weixin_40423597/article/details/80643990 + +https://www.yuque.com/docs/share/fe2e6770-216c-4ea8-a56c-3c94b90faf46 \ No newline at end of file diff --git "a/SpringBoot/1 345円246円202円344円275円225円346円220円255円345円273円272円350円207円252円345円267円261円347円232円204円SpringBoot346円272円220円347円240円201円350円260円203円350円257円225円347円216円257円345円242円203円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円344円270円200円357円274円211円.md" "b/SpringBoot/1 345円246円202円344円275円225円346円220円255円345円273円272円350円207円252円345円267円261円347円232円204円SpringBoot346円272円220円347円240円201円350円260円203円350円257円225円347円216円257円345円242円203円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円344円270円200円357円274円211円.md" index f77d6b4..82c43bd 100644 --- "a/SpringBoot/1 345円246円202円344円275円225円346円220円255円345円273円272円350円207円252円345円267円261円347円232円204円SpringBoot346円272円220円347円240円201円350円260円203円350円257円225円347円216円257円345円242円203円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円344円270円200円357円274円211円.md" +++ "b/SpringBoot/1 345円246円202円344円275円225円346円220円255円345円273円272円350円207円252円345円267円261円347円232円204円SpringBoot346円272円220円347円240円201円350円260円203円350円257円225円347円216円257円345円242円203円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円344円270円200円357円274円211円.md" @@ -1,44 +1,253 @@ ## 1 前言 -这是SpringBoot2.1源码分析专题的第一篇文章,主要讲如何来搭建我们的源码阅读调试环境。如果有经验的小伙伴们可以略过此篇文章。 +这是SpringBoot2.1.0源码分析专题的第一篇文章,主要讲如何来搭建我们的源码阅读调试环境。如果有经验的小伙伴们可以略过此篇文章。 ## 2 环境安装要求 * IntelliJ IDEA * JDK1.8 * Maven3.5以上 + * 其中如果Maven版本过低,比如目前我使用的是mac idea2018内置maven版本为3.3.9,则必须要进行idea升级或者使用外部maven安装方式,我选择了后者。 + * 参见:[mac下载安装maven](https://www.jianshu.com/p/3322d6e64a81) + * 安装好后只需把idea选择maven的方式改变一下 + ![](./images/maven.png) + ## 3 从github上将SpringBoot源码项目下载下来 首先提供**SpringBoot2.1.0**的github地址: https://github.com/spring-projects/spring-boot/tree/v2.1.0.RELEASE 因为要进行阅读源码和分析源码项目,我们是不是要在里面写一些注释帮助我们阅读理解源码,因此需要将SpringBoot源码项目fork到自己的github仓库中,然后再利用**git clone url**命令将已经fork到自己github仓库的SpringBoot源码拉取下来即可。 -但由于以上方式往往很慢,通常会超时,所以笔者直接将SpringBoot项目直接下载下来,然后再导入IDEA中。 +但由于以上方式往往很慢,通常会超时,所以 -![](https://user-gold-cdn.xitu.io/2020/2/23/1706fb164630bfac?w=1310&h=484&f=png&s=70931) -## 4 将SpringBoot源码项目导入到IDEA中 -将刚才下载的spring-boot2.1.0.RELEASE项目选择maven方式导入到IDEA中,然后一直next即可导入完成,注意选择JDK版本是1.8,maven版本是3.5+。 +* 方式一、笔者直接将SpringBoot项目直接下载下来,将SpringBoot源码项目导入到IDEA中将刚才下载的spring-boot2.1.0.RELEASE项目选择maven方式导入到IDEA中,然后一直next即可导入完成,注意选择JDK版本是1.8,maven版本是3.5+。 -![](https://user-gold-cdn.xitu.io/2020/2/23/1706fb50079899ac?w=734&h=266&f=png&s=12373) +> ![](https://user-gold-cdn.xitu.io/2020/2/23/1706fb164630bfac?w=1310&h=484&f=png&s=70931) + +> ![](https://user-gold-cdn.xitu.io/2020/2/23/1706fb50079899ac?w=734&h=266&f=png&s=12373) 此时下载maven依赖是一个漫长的等待过程,建议maven没有配置(阿-里-云)仓库的小伙伴们配置一下,这样下载速度会快很多。参考[配置maven使用(阿-里-云)仓库](https://blog.csdn.net/zhuzj12345/article/details/93200211)进行配置即可。 -## 5 编译构建SpringBoot源码项目 + +* 方式二、采用github插件 + +> github + +> gitspeed + +## 4 编译构建SpringBoot源码项目 此时导入项目后,我们进行编译构建SpringBoot源码项目了,在构建之前做两个配置: 1. 我们要禁用maven的代码检查,在根pom.xml中增加一下配置即可,如下图: > ![](https://user-gold-cdn.xitu.io/2020/2/23/1706fbc5725ffaf8?w=947&h=303&f=png&s=33333) 2. 可能有的小伙伴们的pom.xml文件的project标签上显示`java.lang.OutOfMemoryError`错误,这是因为IDEA里的Maven的importer设置的JVM最大堆内存过小而导致的,如下图,此时可参考[Maven依赖包导入错误(IntelliJ IDEA)](https://blog.csdn.net/w605283073/article/details/85107497)解决即可。 > ![](https://user-gold-cdn.xitu.io/2020/2/23/1706fc20d5eaba6b?w=688&h=254&f=png&s=98291) +3. 把spring-boot-project/spring-boot-tools中的gradle module移除并注释掉pom中该module引入,否则引起package失败 +```yaml + + spring-boot-antlib + spring-boot-autoconfigure-processor + spring-boot-configuration-metadata + spring-boot-configuration-processor + + spring-boot-loader + spring-boot-loader-tools + spring-boot-maven-plugin + spring-boot-test-support + +``` + +4. **注意此步骤是可选择的,因为spring-boot-samples module依赖的三方包太多,导致build变慢,程序运行消耗cpu和内存,如果想测试自己单独启用某个包,或者自己创建** + + 把SpringBoot自带的sample包全部导入到项目中(选择maven方式导入),并在根pom添加module + +> ![image-20200622000642373](/Users/zhangyuhang/Library/Application Support/typora-user-images/image-20200622000642373.png) + +> ![image-20200622000605536](/Users/zhangyuhang/Library/Application Support/typora-user-images/image-20200622000605536.png) + +## 5 打包项目 + +进行了上面配置后,此时我们就可以直接执行以下maven命令来编译构建源码项目了。 -进行了上面的两点配置后,此时我们就可以直接执行以下maven命令来编译构建源码项目了。 ``` -mvn clean install -DskipTests -Pfast +mvn clean install -DskipTests -Pfast -e -X(-e -X输出错误信息) ``` -![](https://user-gold-cdn.xitu.io/2020/2/23/1706fc5696712841?w=697&h=92&f=png&s=8216) +![image-20200622000415996](/Users/zhangyuhang/Library/Application Support/typora-user-images/image-20200622000415996.png) 此时又是漫长的等待,我这里等待5分钟左右就显示构建成功了,如下图: -![](https://user-gold-cdn.xitu.io/2020/2/23/1706fdcd5edaceb9?w=890&h=563&f=png&s=56801) -## 6 运行SpringBoot自带的sample -因为SpringBoot源码中的spring-boot-samples模块自带了很多DEMO样例,我们可以利用其中的一个sample来测试运行刚刚构建的springboot源码项目即可。但此时发现spring-boot-samples模块是灰色的,如下图: -![](https://user-gold-cdn.xitu.io/2020/2/23/1706fca29948aa2a?w=300&h=136&f=png&s=5880) -这是因为spring-boot-samples模块没有被添加到根pom.xml中,此时将其添加到根pom.xml中即可,增加如下配置,如下图: -![](https://user-gold-cdn.xitu.io/2020/2/23/1706fcb839241e47?w=815&h=330&f=png&s=34161) + +```java +[INFO] Spring Boot Build 2.1.0.RELEASE .................... SUCCESS [ 0.907 s] +[INFO] Spring Boot Dependencies 2.1.0.RELEASE ............. SUCCESS [ 1.002 s] +[INFO] Spring Boot Parent 2.1.0.RELEASE ................... SUCCESS [ 0.277 s] +[INFO] Spring Boot Tools 2.1.0.RELEASE .................... SUCCESS [ 0.031 s] +[INFO] Spring Boot Testing Support 2.1.0.RELEASE .......... SUCCESS [ 2.195 s] +[INFO] Spring Boot Configuration Processor 2.1.0.RELEASE .. SUCCESS [ 1.208 s] +[INFO] Spring Boot 2.1.0.RELEASE .......................... SUCCESS [ 13.924 s] +[INFO] Spring Boot Test 2.1.0.RELEASE ..................... SUCCESS [ 3.772 s] +[INFO] Spring Boot Auto-Configure Annotation Processor 2.1.0.RELEASE SUCCESS [ 0.308 s] +[INFO] Spring Boot AutoConfigure 2.1.0.RELEASE ............ SUCCESS [ 18.489 s] +[INFO] Spring Boot Actuator 2.1.0.RELEASE ................. SUCCESS [ 3.579 s] +[INFO] Spring Boot Actuator AutoConfigure 2.1.0.RELEASE ... SUCCESS [ 4.316 s] +[INFO] Spring Boot Developer Tools 2.1.0.RELEASE .......... SUCCESS [ 1.139 s] +[INFO] Spring Boot Configuration Metadata 2.1.0.RELEASE ... SUCCESS [ 0.210 s] +[INFO] Spring Boot Starters 2.1.0.RELEASE ................. SUCCESS [ 0.493 s] +[INFO] Spring Boot Logging Starter 2.1.0.RELEASE .......... SUCCESS [ 0.507 s] +[INFO] Spring Boot Starter 2.1.0.RELEASE .................. SUCCESS [ 0.236 s] +[INFO] Spring Boot Test Auto-Configure 2.1.0.RELEASE ...... SUCCESS [ 1.181 s] +[INFO] Spring Boot Test Starter 2.1.0.RELEASE ............. SUCCESS [ 0.293 s] +[INFO] Spring Boot Properties Migrator 2.1.0.RELEASE ...... SUCCESS [ 0.169 s] +[INFO] Spring Boot Loader 2.1.0.RELEASE ................... SUCCESS [ 0.991 s] +[INFO] Spring Boot Loader Tools 2.1.0.RELEASE ............. SUCCESS [ 0.525 s] +[INFO] Spring Boot Antlib 2.1.0.RELEASE ................... SUCCESS [ 0.483 s] +[INFO] Spring Boot Maven Plugin 2.1.0.RELEASE ............. SUCCESS [ 3.956 s] +[INFO] Spring Boot ActiveMQ Starter 2.1.0.RELEASE ......... SUCCESS [ 0.415 s] +[INFO] Spring Boot AMQP Starter 2.1.0.RELEASE ............. SUCCESS [ 0.196 s] +[INFO] Spring Boot AOP Starter 2.1.0.RELEASE .............. SUCCESS [ 0.155 s] +[INFO] Spring Boot Artemis Starter 2.1.0.RELEASE .......... SUCCESS [ 0.453 s] +[INFO] Spring Boot JDBC Starter 2.1.0.RELEASE ............. SUCCESS [ 0.131 s] +[INFO] Spring Boot Batch Starter 2.1.0.RELEASE ............ SUCCESS [ 0.185 s] +[INFO] Spring Boot Cache Starter 2.1.0.RELEASE ............ SUCCESS [ 0.110 s] +[INFO] Spring Boot Spring Cloud Connectors Starter 2.1.0.RELEASE SUCCESS [ 0.189 s] +[INFO] Spring Boot Data Cassandra Starter 2.1.0.RELEASE ... SUCCESS [ 0.428 s] +[INFO] Spring Boot Data Cassandra Reactive Starter 2.1.0.RELEASE SUCCESS [ 0.267 s] +[INFO] Spring Boot Data Couchbase Starter 2.1.0.RELEASE ... SUCCESS [ 0.401 s] +[INFO] Spring Boot Data Couchbase Reactive Starter 2.1.0.RELEASE SUCCESS [ 0.251 s] +[INFO] Spring Boot Data Elasticsearch Starter 2.1.0.RELEASE SUCCESS [ 1.036 s] +[INFO] Spring Boot Data JDBC Starter 2.1.0.RELEASE ........ SUCCESS [ 0.138 s] +[INFO] Spring Boot Data JPA Starter 2.1.0.RELEASE ......... SUCCESS [ 0.451 s] +[INFO] Spring Boot Data LDAP Starter 2.1.0.RELEASE ........ SUCCESS [ 0.153 s] +[INFO] Spring Boot Data MongoDB Starter 2.1.0.RELEASE ..... SUCCESS [ 0.229 s] +[INFO] Spring Boot Data MongoDB Reactive Starter 2.1.0.RELEASE SUCCESS [ 0.178 s] +[INFO] Spring Boot Data Neo4j Starter 2.1.0.RELEASE ....... SUCCESS [ 0.268 s] +[INFO] Spring Boot Data Redis Starter 2.1.0.RELEASE ....... SUCCESS [ 0.279 s] +[INFO] Spring Boot Data Redis Reactive Starter 2.1.0.RELEASE SUCCESS [ 0.164 s] +[INFO] Spring Boot Json Starter 2.1.0.RELEASE ............. SUCCESS [ 0.136 s] +[INFO] Spring Boot Tomcat Starter 2.1.0.RELEASE ........... SUCCESS [ 0.144 s] +[INFO] Spring Boot Web Starter 2.1.0.RELEASE .............. SUCCESS [ 0.214 s] +[INFO] Spring Boot Data REST Starter 2.1.0.RELEASE ........ SUCCESS [ 0.261 s] +[INFO] Spring Boot Data Solr Starter 2.1.0.RELEASE ........ SUCCESS [ 0.380 s] +[INFO] Spring Boot FreeMarker Starter 2.1.0.RELEASE ....... SUCCESS [ 0.144 s] +[INFO] Spring Boot Groovy Templates Starter 2.1.0.RELEASE . SUCCESS [ 0.314 s] +[INFO] Spring Boot HATEOAS Starter 2.1.0.RELEASE .......... SUCCESS [ 0.183 s] +[INFO] Spring Boot Integration Starter 2.1.0.RELEASE ...... SUCCESS [ 0.161 s] +[INFO] Spring Boot Validation Starter 2.1.0.RELEASE ....... SUCCESS [ 0.129 s] +[INFO] Spring Boot Jersey Starter 2.1.0.RELEASE ........... SUCCESS [ 0.515 s] +[INFO] Spring Boot Jetty Starter 2.1.0.RELEASE ............ SUCCESS [ 0.269 s] +[INFO] Spring Boot JOOQ Starter 2.1.0.RELEASE ............. SUCCESS [ 0.170 s] +[INFO] Spring Boot Atomikos JTA Starter 2.1.0.RELEASE ..... SUCCESS [ 0.148 s] +[INFO] Spring Boot Bitronix JTA Starter 2.1.0.RELEASE ..... SUCCESS [ 0.146 s] +[INFO] Spring Boot Log4j 2 Starter 2.1.0.RELEASE .......... SUCCESS [ 0.131 s] +[INFO] Spring Boot Mail Starter 2.1.0.RELEASE ............. SUCCESS [ 0.130 s] +[INFO] Spring Boot Mustache Starter 2.1.0.RELEASE ......... SUCCESS [ 0.111 s] +[INFO] Spring Boot Actuator Starter 2.1.0.RELEASE ......... SUCCESS [ 0.164 s] +[INFO] Spring Boot OAuth2/OpenID Connect Client Starter 2.1.0.RELEASE SUCCESS [ 0.250 s] +[INFO] Spring Boot OAuth2 Resource Server Starter 2.1.0.RELEASE SUCCESS [ 0.143 s] +[INFO] Spring Boot Starter Parent 2.1.0.RELEASE ........... SUCCESS [ 0.045 s] +[INFO] Spring Boot Quartz Starter 2.1.0.RELEASE ........... SUCCESS [ 0.175 s] +[INFO] Spring Boot Reactor Netty Starter 2.1.0.RELEASE .... SUCCESS [ 0.202 s] +[INFO] Spring Boot Security Starter 2.1.0.RELEASE ......... SUCCESS [ 0.135 s] +[INFO] Spring Boot Thymeleaf Starter 2.1.0.RELEASE ........ SUCCESS [ 0.162 s] +[INFO] Spring Boot Undertow Starter 2.1.0.RELEASE ......... SUCCESS [ 0.213 s] +[INFO] Spring Boot WebFlux Starter 2.1.0.RELEASE .......... SUCCESS [ 0.207 s] +[INFO] Spring Boot WebSocket Starter 2.1.0.RELEASE ........ SUCCESS [ 0.177 s] +[INFO] Spring Boot Web Services Starter 2.1.0.RELEASE ..... SUCCESS [ 0.244 s] +[INFO] Spring Boot CLI 2.1.0.RELEASE ...................... SUCCESS [ 3.672 s] +[INFO] Spring Boot Docs 2.1.0.RELEASE ..................... SUCCESS [ 2.770 s] +[INFO] Spring Boot Build 2.1.0.RELEASE .................... SUCCESS [ 0.006 s] +[INFO] Spring Boot Samples 2.1.0.RELEASE .................. SUCCESS [ 0.016 s] +[INFO] Spring Boot Ant Sample 2.1.0.RELEASE ............... SUCCESS [02:53 min] +[INFO] Spring Boot ActiveMQ Sample 2.1.0.RELEASE .......... SUCCESS [ 0.566 s] +[INFO] Spring Boot Actuator Sample 2.1.0.RELEASE .......... SUCCESS [ 0.451 s] +[INFO] Spring Boot Actuator Log4j 2 Sample 2.1.0.RELEASE .. SUCCESS [ 0.221 s] +[INFO] Spring Boot Actuator Non-Web Sample 2.1.0.RELEASE .. SUCCESS [ 0.193 s] +[INFO] Spring Boot Actuator UI Sample 2.1.0.RELEASE ....... SUCCESS [ 0.215 s] +[INFO] Spring Boot Actuator Custom Security Sample 2.1.0.RELEASE SUCCESS [ 0.228 s] +[INFO] Spring Boot AMQP Sample 2.1.0.RELEASE .............. SUCCESS [ 0.135 s] +[INFO] Spring Boot Simple Animated Banner 2.1.0.RELEASE ... SUCCESS [ 0.137 s] +[INFO] Spring Boot AOP Sample 2.1.0.RELEASE ............... SUCCESS [ 0.132 s] +[INFO] Spring Boot Atmosphere Sample 2.1.0.RELEASE ........ SUCCESS [ 0.248 s] +[INFO] Spring Boot Batch Sample 2.1.0.RELEASE ............. SUCCESS [ 0.172 s] +[INFO] Spring Boot Cache Sample 2.1.0.RELEASE ............. SUCCESS [ 0.214 s] +[INFO] Spring Boot Custom Layout Sample 2.1.0.RELEASE ..... SUCCESS [ 0.182 s] +[INFO] Spring Boot Data Cassandra Sample 2.1.0.RELEASE .... SUCCESS [ 3.933 s] +[INFO] Spring Boot Data Couchbase Sample 2.1.0.RELEASE .... SUCCESS [ 0.275 s] +[INFO] Spring Boot Data Elasticsearch Sample 2.1.0.RELEASE SUCCESS [ 0.336 s] +[INFO] Spring Boot Data JDBC Sample 2.1.0.RELEASE ......... SUCCESS [ 0.224 s] +[INFO] Spring Boot Data JPA Sample 2.1.0.RELEASE .......... SUCCESS [ 0.365 s] +[INFO] Spring Boot Data LDAP Sample 2.1.0.RELEASE ......... SUCCESS [ 0.161 s] +[INFO] Spring Boot Data MongoDB Sample 2.1.0.RELEASE ...... SUCCESS [ 0.253 s] +[INFO] Spring Boot Data Neo4j Sample 2.1.0.RELEASE ........ SUCCESS [ 0.176 s] +[INFO] Spring Boot Data Redis Sample 2.1.0.RELEASE ........ SUCCESS [ 0.186 s] +[INFO] Spring Boot Data REST Sample 2.1.0.RELEASE ......... SUCCESS [ 0.355 s] +[INFO] Spring Boot Data Solr Sample 2.1.0.RELEASE ......... SUCCESS [ 0.216 s] +[INFO] Spring Boot Developer Tools Sample 2.1.0.RELEASE ... SUCCESS [ 0.194 s] +[INFO] Spring Boot Flyway Sample 2.1.0.RELEASE ............ SUCCESS [ 0.320 s] +[INFO] Spring Boot Hateoas Sample 2.1.0.RELEASE ........... SUCCESS [ 0.187 s] +[INFO] Spring Boot Integration Sample 2.1.0.RELEASE ....... SUCCESS [ 3.485 s] +[INFO] Spring Boot Jersey Sample 2.1.0.RELEASE ............ SUCCESS [ 1.193 s] +[INFO] Spring Boot Jetty Sample 2.1.0.RELEASE ............. SUCCESS [ 0.179 s] +[INFO] Spring Boot Jetty JSP Sample 2.1.0.RELEASE ......... SUCCESS [ 12.207 s] +[INFO] Spring Boot Jetty SSL Sample 2.1.0.RELEASE ......... SUCCESS [ 0.158 s] +[INFO] Spring Boot jOOQ Sample 2.1.0.RELEASE .............. SUCCESS [ 3.925 s] +[INFO] Spring Boot JPA Sample 2.1.0.RELEASE ............... SUCCESS [ 0.260 s] +[INFO] Spring Boot Atomikos JTA Sample 2.1.0.RELEASE ...... SUCCESS [ 0.409 s] +[INFO] Spring Boot Bitronix JTA Sample 2.1.0.RELEASE ...... SUCCESS [ 0.260 s] +[INFO] Spring Boot JNDI JTA Sample 2.1.0.RELEASE .......... SUCCESS [ 0.558 s] +[INFO] Spring Boot JUnit Jupiter Sample 2.1.0.RELEASE ..... SUCCESS [ 4.604 s] +[INFO] Spring Boot Kafka Sample 2.1.0.RELEASE ............. SUCCESS [ 0.232 s] +[INFO] Spring Boot Liquibase Sample 2.1.0.RELEASE ......... SUCCESS [ 0.160 s] +[INFO] Spring Boot Logback Sample 2.1.0.RELEASE ........... SUCCESS [ 0.102 s] +[INFO] Spring Boot Sample OAuth2 Client 2.1.0.RELEASE ..... SUCCESS [ 8.560 s] +[INFO] Spring Boot Sample OAuth2 Resource Server 2.1.0.RELEASE SUCCESS [ 0.216 s] +[INFO] Spring Boot Profile Sample 2.1.0.RELEASE ........... SUCCESS [ 0.130 s] +[INFO] Spring Boot Property Validation Sample 2.1.0.RELEASE SUCCESS [ 0.258 s] +[INFO] Spring Boot Quartz Sample 2.1.0.RELEASE ............ SUCCESS [ 0.135 s] +[INFO] Spring Boot Sample Reactive OAuth2 Client 2.1.0.RELEASE SUCCESS [ 0.208 s] +[INFO] Spring Boot Sample Reactive OAuth2 Resource Server 2.1.0.RELEASE SUCCESS [ 0.189 s] +[INFO] Spring Boot Security Sample 2.1.0.RELEASE .......... SUCCESS [ 0.125 s] +[INFO] Spring Boot Secure WebFlux Sample 2.1.0.RELEASE .... SUCCESS [ 0.197 s] +[INFO] Spring Boot Servlet Sample 2.1.0.RELEASE ........... SUCCESS [ 0.370 s] +[INFO] Spring Boot Session Sample 2.1.0.RELEASE ........... SUCCESS [ 0.168 s] +[INFO] Spring Boot Session WebFlux Sample 2.1.0.RELEASE ... SUCCESS [ 1.307 s] +[INFO] Spring Boot Simple Sample 2.1.0.RELEASE ............ SUCCESS [ 0.155 s] +[INFO] Spring Boot Test Sample 2.1.0.RELEASE .............. SUCCESS [ 0.341 s] +[INFO] Spring Boot Test Sample No Mockito 2.1.0.RELEASE ... SUCCESS [ 0.090 s] +[INFO] Spring Boot TestNG Sample 2.1.0.RELEASE ............ SUCCESS [ 0.208 s] +[INFO] Spring Boot Tomcat Sample 2.1.0.RELEASE ............ SUCCESS [ 0.155 s] +[INFO] Spring Boot Tomcat JSP Sample 2.1.0.RELEASE ........ SUCCESS [ 0.558 s] +[INFO] Spring Boot Tomcat SSL Sample 2.1.0.RELEASE ........ SUCCESS [ 0.134 s] +[INFO] Spring Boot Multi-Connector Tomcat Sample 2.1.0.RELEASE SUCCESS [ 0.158 s] +[INFO] Spring Boot Traditional Sample 2.1.0.RELEASE ....... SUCCESS [ 0.340 s] +[INFO] Spring Boot Undertow Sample 2.1.0.RELEASE .......... SUCCESS [ 0.186 s] +[INFO] Spring Boot Undertow SSL Sample 2.1.0.RELEASE ...... SUCCESS [ 0.151 s] +[INFO] Spring Boot War Sample 2.1.0.RELEASE ............... SUCCESS [ 0.387 s] +[INFO] Spring Boot Web FreeMarker Sample 2.1.0.RELEASE .... SUCCESS [ 0.147 s] +[INFO] Spring Boot Web Groovy Templates Sample 2.1.0.RELEASE SUCCESS [ 0.260 s] +[INFO] Spring Boot Web JSP Sample 2.1.0.RELEASE ........... SUCCESS [ 0.429 s] +[INFO] Spring Boot Web Method Security Sample 2.1.0.RELEASE SUCCESS [ 0.252 s] +[INFO] Spring Boot Web Mustache Sample 2.1.0.RELEASE ...... SUCCESS [ 0.150 s] +[INFO] Spring Boot Web Secure Sample 2.1.0.RELEASE ........ SUCCESS [ 0.181 s] +[INFO] Spring Boot Web Secure Custom Sample 2.1.0.RELEASE . SUCCESS [ 0.173 s] +[INFO] Spring Boot Web Secure JDBC Sample 2.1.0.RELEASE ... SUCCESS [ 0.189 s] +[INFO] Spring Boot Web Static Sample 2.1.0.RELEASE ........ SUCCESS [ 0.419 s] +[INFO] Spring Boot Web UI Sample 2.1.0.RELEASE ............ SUCCESS [ 0.183 s] +[INFO] Spring Boot WebFlux Sample 2.1.0.RELEASE ........... SUCCESS [ 0.166 s] +[INFO] Spring Boot WebSocket Jetty Sample 2.1.0.RELEASE ... SUCCESS [ 0.256 s] +[INFO] Spring Boot WebSocket Tomcat Sample 2.1.0.RELEASE .. SUCCESS [ 0.204 s] +[INFO] Spring Boot WebSocket Undertow Sample 2.1.0.RELEASE SUCCESS [ 0.224 s] +[INFO] Spring Boot Web Services Sample 2.1.0.RELEASE ...... SUCCESS [ 0.265 s] +[INFO] Spring Boot XML Sample 2.1.0.RELEASE ............... SUCCESS [ 0.098 s] +[INFO] Spring Boot Samples Invoker 2.1.0.RELEASE .......... SUCCESS [ 0.696 s] +[INFO] helloworld-spring-boot-starter 0.0.1-SNAPSHOT ...... SUCCESS [ 0.469 s] +[INFO] spring-boot-ymbj-tests 2.1.0.RELEASE ............... SUCCESS [ 0.183 s] +[INFO] spring-boot-generaldemo-tests 2.1.0.RELEASE ........ SUCCESS [ 0.497 s] +[INFO] Spring Boot Tests 2.1.0.RELEASE .................... SUCCESS [ 0.019 s] +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 05:11 min +[INFO] Finished at: 2020年06月22日T00:02:30+08:00 +[INFO] ------------------------------------------------------------------------ + +``` + +## 6 运行SpringBoot自带的sample(或者自己写个sample) 此时我们挑选spring-boot-samples模块下的spring-boot-sample-tomcat样例项目来测试好了,此时启动`SampleTomcatApplication`的`main`函数,启动成功界面如下: ![](https://user-gold-cdn.xitu.io/2020/2/23/1706fde9a5073f65?w=1843&h=444&f=png&s=130677) @@ -46,14 +255,30 @@ mvn clean install -DskipTests -Pfast ![](https://user-gold-cdn.xitu.io/2020/2/23/1706fd98c2621c7b?w=411&h=161&f=png&s=9614) -## 7 动手实践环节 +## 7 动手实践环节 + 前面已经成功构建了SpringBoot的源码阅读环境,小伙伴们记得自己动手搭建一套属于自己的SpringBoot源码调试环境哦,阅读源码动手调试很重要,嘿嘿。 **下节预告**: 我们该如何去分析SpringBoot源码涉及模块及结构?--SpringBoot源码(二) - **原创不易,帮忙Star一下呗**! -注:该源码分析对应SpringBoot版本为**2.1.0.RELEASE**,本文对应的SpringBoot源码解析项目github地址:https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE +该源码分析对应SpringBoot版本为**2.1.0.RELEASE**,本文对应的SpringBoot源码解析项目github地址:https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE + + + +##8 注意:**作者源码里面有一处大坑、一处小坑** + +1. 大坑 + + 作者不小心敲入的,导致SpringBoot运行时无法反射创建该Bean,如不修改则可直接看到结果。花了2.5小时debug到。。。。。。。 + + > ![image-20200622233426913](/Users/zhangyuhang/Library/Application Support/typora-user-images/image-20200622233426913.png) + +2. 小坑 + + 依据报错自己直观就能判断。 + +> ![image-20200622233456183](/Users/zhangyuhang/Library/Application Support/typora-user-images/image-20200622233456183.png) diff --git "a/SpringBoot/6 SpringBoot345円206円205円347円275円256円347円232円204円345円220円204円347円247円215円Starter346円230円257円346円200円216円346円240円267円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円205円255円357円274円211円.md" "b/SpringBoot/10 SpringBoot345円206円205円347円275円256円347円232円204円345円220円204円347円247円215円Starter346円230円257円346円200円216円346円240円267円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円215円201円357円274円211円.md" similarity index 96% rename from "SpringBoot/6 SpringBoot345円206円205円347円275円256円347円232円204円345円220円204円347円247円215円Starter346円230円257円346円200円216円346円240円267円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円205円255円357円274円211円.md" rename to "SpringBoot/10 SpringBoot345円206円205円347円275円256円347円232円204円345円220円204円347円247円215円Starter346円230円257円346円200円216円346円240円267円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円215円201円357円274円211円.md" index 9e3c631..326dad0 100644 --- "a/SpringBoot/6 SpringBoot345円206円205円347円275円256円347円232円204円345円220円204円347円247円215円Starter346円230円257円346円200円216円346円240円267円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円205円255円357円274円211円.md" +++ "b/SpringBoot/10 SpringBoot345円206円205円347円275円256円347円232円204円345円220円204円347円247円215円Starter346円230円257円346円200円216円346円240円267円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円215円201円357円274円211円.md" @@ -1,7 +1,5 @@ # 1 温故而知新 -本篇接 [外部配置属性值是如何被绑定到XxxProperties类属性上的?--SpringBoot源码(五)](https://github.com/yuanmabiji/Java-SourceCode-Blogs/blob/master/SpringBoot/5%20SpringBoot%E7%9A%84%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%80%BC%E6%98%AF%E5%A6%82%E4%BD%95%E7%BB%91%E5%AE%9A%E7%9A%84%EF%BC%9F%20SpringBoot%E6%BA%90%E7%A0%81%EF%BC%88%E4%BA%94%EF%BC%89.md) - 温故而知新,我们来简单回顾一下上篇的内容,上一篇我们分析了SpringBoot**外部配置属性值是如何被绑定到XxxProperties类属性上**的相关源码,现将外部属性绑定的重要步骤总结如下: 1. 首先是`@EnableConfigurationProperties`注解`import`了`EnableConfigurationPropertiesImportSelector`后置处理器; 2. `EnableConfigurationPropertiesImportSelector`后置处理器又向`Spring`容器中注册了`ConfigurationPropertiesBeanRegistrar`和`ConfigurationPropertiesBindingPostProcessorRegistrar`这两个`bean`; @@ -199,7 +197,6 @@ Maven的`optional`标签表示可选依赖即不可传递的意思,下面直 注:该源码分析对应SpringBoot版本为**2.1.0.RELEASE**,本文对应的SpringBoot源码解析项目github地址:https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE - 参考: -1,[Maven 依赖传递性透彻理解](https://dayarch.top/p/maven-dependency-optional-transitive.html) +1. [Maven 依赖传递性透彻理解](https://dayarch.top/p/maven-dependency-optional-transitive.html) \ No newline at end of file diff --git "a/SpringBoot/7 SpringBoot347円232円204円345円220円257円345円212円250円346円265円201円347円250円213円346円230円257円346円200円216円346円240円267円347円232円204円357円274円237円SpringBoot346円272円220円347円240円201円357円274円210円344円270円203円357円274円211円.md" "b/SpringBoot/3 SpringBoot347円232円204円345円220円257円345円212円250円346円265円201円347円250円213円346円230円257円346円200円216円346円240円267円347円232円204円357円274円237円SpringBoot346円272円220円347円240円201円357円274円210円344円270円211円357円274円211円.md" similarity index 89% rename from "SpringBoot/7 SpringBoot347円232円204円345円220円257円345円212円250円346円265円201円347円250円213円346円230円257円346円200円216円346円240円267円347円232円204円357円274円237円SpringBoot346円272円220円347円240円201円357円274円210円344円270円203円357円274円211円.md" rename to "SpringBoot/3 SpringBoot347円232円204円345円220円257円345円212円250円346円265円201円347円250円213円346円230円257円346円200円216円346円240円267円347円232円204円357円274円237円SpringBoot346円272円220円347円240円201円357円274円210円344円270円211円357円274円211円.md" index bf13048..2adf655 100644 --- "a/SpringBoot/7 SpringBoot347円232円204円345円220円257円345円212円250円346円265円201円347円250円213円346円230円257円346円200円216円346円240円267円347円232円204円357円274円237円SpringBoot346円272円220円347円240円201円357円274円210円344円270円203円357円274円211円.md" +++ "b/SpringBoot/3 SpringBoot347円232円204円345円220円257円345円212円250円346円265円201円347円250円213円346円230円257円346円200円216円346円240円267円347円232円204円357円274円237円SpringBoot346円272円220円347円240円201円357円274円210円344円270円211円357円274円211円.md" @@ -1,18 +1,13 @@ # 1 温故而知新 -本篇接 [SpringBoot内置的各种Starter是怎样构建的? SpringBoot源码(六)](https://github.com/yuanmabiji/Java-SourceCode-Blogs/blob/master/SpringBoot/6%20SpringBoot%E5%86%85%E7%BD%AE%E7%9A%84%E5%90%84%E7%A7%8DStarter%E6%98%AF%E6%80%8E%E6%A0%B7%E6%9E%84%E5%BB%BA%E7%9A%84%EF%BC%9F%20%20SpringBoot%E6%BA%90%E7%A0%81%EF%BC%88%E5%85%AD%EF%BC%89.md) +本篇接 +[如何分析SpringBoot源码模块及结构?--SpringBoot源码(二)](https://github.com/yuanmabiji/Java-SourceCode-Blogs/blob/master/SpringBoot/2%20%E5%A6%82%E4%BD%95%E5%88%86%E6%9E%90SpringBoot%E6%BA%90%E7%A0%81%E6%A8%A1%E5%9D%97%E5%8F%8A%E7%BB%93%E6%9E%84%EF%BC%9F%20%20SpringBoot%E6%BA%90%E7%A0%81%EF%BC%88%E4%BA%8C%EF%BC%89.md) -温故而知新,我们来简单回顾一下上篇的内容,上一篇我们分析了**SpringBoot内置的各种Starter是怎样构建的?**,现将关键点重新回顾总结下: -1. `spring-boot-starter-xxx`起步依赖没有一行代码,而是直接或间接依赖了`xxx-autoconfigure`模块,而`xxx-autoconfigure`模块承担了`spring-boot-starter-xxx`起步依赖自动配置的实现; -2. `xxx-autoconfigure`自动配置模块引入了一些可选依赖,这些可选依赖不会被传递到`spring-boot-starter-xxx`起步依赖中,这是起步依赖构建的**关键点**; -3. `spring-boot-starter-xxx`起步依赖**显式**引入了一些对自动配置起作用的可选依赖,因此会触发 `xxx-autoconfigure`自动配置的逻辑(比如创建某些符合条件的配置`bean`); -4. 经过前面3步的准备,我们项目只要引入了某个起步依赖后,就可以开箱即用了,而不用手动去创建一些`bean`等。 +上一篇分析了SpringBoot源码结构及各个模块pom之间的关系后,那么此篇开始就开始解开SpringBoot新特性之一--自动配置的神秘面纱了。因为SpringBoot自动配置原理是基于其大量的条件注解`ConditionalOnXXX`,因此,本节我们先来撸下Spring的条件注解的相关源码。 # 2 引言 -本来这篇文章会继续SpringBoot自动配置的源码分析的,想分析下`spring-boot-starter-web`的自动配置的源码是怎样的的。但是考虑到`spring-boot-starter-web`的自动配置逻辑跟内置`Tomcat`等有关,因此想以后等分析了SpringBoot的内置`Tomcat`的相关源码后再来继续分析`spring-boot-starter-web`的自动配置的源码。 - -因此,本篇我们来探究下**SpringBoot的启动流程是怎样的?** +本篇我们来探究下**SpringBoot的启动流程是怎样的?** # 3 如何编写一个SpringBoot启动类 我们都知道,我们运行一个SpringBoot项目,引入相关`Starters`和相关依赖后,再编写一个启动类,然后在这个启动类标上`@SpringBootApplication`注解,然后就可以启动运行项目了,如下代码: ```java diff --git "a/SpringBoot/8 SpringApplication345円257円271円350円261円241円346円230円257円345円246円202円344円275円225円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円205円253円357円274円211円.md" "b/SpringBoot/4 SpringApplication345円257円271円350円261円241円346円230円257円345円246円202円344円275円225円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円233円233円357円274円211円.md" similarity index 97% rename from "SpringBoot/8 SpringApplication345円257円271円350円261円241円346円230円257円345円246円202円344円275円225円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円205円253円357円274円211円.md" rename to "SpringBoot/4 SpringApplication345円257円271円350円261円241円346円230円257円345円246円202円344円275円225円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円233円233円357円274円211円.md" index 00b66ca..0b312b9 100644 --- "a/SpringBoot/8 SpringApplication345円257円271円350円261円241円346円230円257円345円246円202円344円275円225円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円205円253円357円274円211円.md" +++ "b/SpringBoot/4 SpringApplication345円257円271円350円261円241円346円230円257円345円246円202円344円275円225円346円236円204円345円273円272円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円233円233円357円274円211円.md" @@ -1,6 +1,3 @@ - -本篇接 [SpringBoot的启动流程是怎样的?SpringBoot源码(七)](https://github.com/yuanmabiji/Java-SourceCode-Blogs/blob/master/SpringBoot/7%20SpringBoot%E7%9A%84%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B%E6%98%AF%E6%80%8E%E6%A0%B7%E7%9A%84%EF%BC%9FSpringBoot%E6%BA%90%E7%A0%81%EF%BC%88%E4%B8%83%EF%BC%89.md) - # 1 温故而知新 温故而知新,我们来简单回顾一下上篇的内容,上一篇我们分析了**SpringBoot的启动流程**,现将关键步骤再浓缩总结下: @@ -190,7 +187,7 @@ private Class deduceMainApplicationClass() { # 4 SpringBoot的SPI机制原理解读 -由于SpringBoot的SPI机制是一个很重要的知识点,因此这里单独一小节来分析。我们都知道,SpringBoot没有使用Java的SPI机制(Java的SPI机制可以看看笔者的[Java是如何实现自己的SPI机制的?](https://juejin.im/post/5e7c26a76fb9a009a441757c),真的是干货满满),而是自定义实现了一套自己的SPI机制。SpringBoot利用自定义实现的SPI机制可以加载初始化器实现类,监听器实现类和自动配置类等等。如果我们要添加自动配置类或自定义监听器,那么我们很重要的一步就是在`spring.factories`中进行配置,然后才会被SpringBoot加载。 +由于SpringBoot的SPI机制是一个很重要的知识点,因此这里单独一小节来分析。我们都知道,SpringBoot没有使用Java的SPI机制(Java的SPI机制可以看看笔者的[Java是如何实现自己的SPI机制的?](https://juejin.im/post/5e7c26a76fb9a009a441757c),真的是干货满满),而是自定义实现了一套自己的SPI机制。SpringBoot利用自定义实现的SPI机制可以**加载初始化器实现类,监听器实现类和自动配置类**等等。如果我们要添加自动配置类或自定义监听器,那么我们很重要的一步就是在`spring.factories`中进行配置,然后才会被SpringBoot加载。 好了,那么接下来我们就来重点分析下**SpringBoot是如何是实现自己的SPI机制的**。 @@ -431,3 +428,8 @@ private List createSpringFactoriesInstances(Class type, 注:该源码分析对应SpringBoot版本为**2.1.0.RELEASE**,本文对应的SpringBoot源码解析项目github地址:https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE +## 7 技术引用参考 + ++ [commaDelimitedListToStringArray](https://blog.csdn.net/weixin_30576859/article/details/95452202) + +* [MultiValueMap](https://blog.csdn.net/zty1317313805/article/details/80096498) \ No newline at end of file diff --git "a/SpringBoot/9 SpringBoot344円272円213円344円273円266円347円233円221円345円220円254円346円234円272円345円210円266円346円272円220円347円240円201円345円210円206円346円236円220円(344円270円212円) SpringBoot346円272円220円347円240円201円(344円271円235円).md" "b/SpringBoot/5 SpringBoot344円272円213円344円273円266円347円233円221円345円220円254円346円234円272円345円210円266円346円272円220円347円240円201円345円210円206円346円236円220円(344円270円212円) SpringBoot346円272円220円347円240円201円(344円272円224円).md" similarity index 97% rename from "SpringBoot/9 SpringBoot344円272円213円344円273円266円347円233円221円345円220円254円346円234円272円345円210円266円346円272円220円347円240円201円345円210円206円346円236円220円(344円270円212円) SpringBoot346円272円220円347円240円201円(344円271円235円).md" rename to "SpringBoot/5 SpringBoot344円272円213円344円273円266円347円233円221円345円220円254円346円234円272円345円210円266円346円272円220円347円240円201円345円210円206円346円236円220円(344円270円212円) SpringBoot346円272円220円347円240円201円(344円272円224円).md" index d8d3d4f..0210d01 100644 --- "a/SpringBoot/9 SpringBoot344円272円213円344円273円266円347円233円221円345円220円254円346円234円272円345円210円266円346円272円220円347円240円201円345円210円206円346円236円220円(344円270円212円) SpringBoot346円272円220円347円240円201円(344円271円235円).md" +++ "b/SpringBoot/5 SpringBoot344円272円213円344円273円266円347円233円221円345円220円254円346円234円272円345円210円266円346円272円220円347円240円201円345円210円206円346円236円220円(344円270円212円) SpringBoot346円272円220円347円240円201円(344円272円224円).md" @@ -1,11 +1,3 @@ -**SpringBoot中文注释项目Github地址:** - -https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE - - - -本篇接 [SpringApplication对象是如何构建的? SpringBoot源码(八)](https://juejin.im/post/5e82bac9518825737a314096) - # 1 温故而知新 温故而知新,我们来简单回顾一下上篇的内容,上一篇我们分析了**SpringApplication对象的构建过程及SpringBoot自己实现的一套SPI机制**,现将关键步骤再浓缩总结下: 1. `SpringApplication`对象的构造过程其实就是给`SpringApplication`类的**6**个成员变量赋值; @@ -80,7 +72,7 @@ public ConfigurableApplicationContext run(String... args) { ``` 可以看到SpringBoot在启动过程中首先会先新建一个`SpringApplicationRunListeners`对象用于发射SpringBoot启动过程中的各种生命周期事件,比如发射`ApplicationStartingEvent`,`ApplicationEnvironmentPreparedEvent`和`ApplicationContextInitializedEvent`等事件,然后相应的监听器会执行一些SpringBoot启动过程中的初始化逻辑。那么,监听这些SpringBoot的生命周期事件的监听器们是何时被加载实例化的呢?还记得上篇文章在分析`SpringApplication`的构建过程吗?没错,这些执行初始化逻辑的监听器们正是在`SpringApplication`的构建过程中根据`ApplicationListener`接口去`spring.factories`配置文件中加载并实例化的。 # 3.1 为广播SpringBoot内置生命周期事件做前期准备 -# 3.1.1 加载ApplicationListener监听器实现类 +## 3.1.1 加载ApplicationListener监听器实现类 我们再来回顾下[SpringApplication对象是如何构建的? SpringBoot源码(八)](https://juejin.im/post/5e82bac9518825737a314096)一文中讲到在构建`SpringApplication`对象时的`setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));`这句代码。 这句代码做的事情就是从`spring.factories`中加载出`ApplicationListener`事件监听接口的SPI扩展实现类然后添加到`SpringApplication`对象的`listeners`集合中,用于后续监听SpringBoot启动过程中的事件,来执行一些初始化逻辑工作。 @@ -94,7 +86,7 @@ SpringBoot启动时的具体监听器们都实现了`ApplicationListener`接口 ![](https://user-gold-cdn.xitu.io/2020/4/13/17170eb8ae034bbd?w=712&h=584&f=png&s=53731) -# 3.1.2 加载SPI扩展类EventPublishingRunListener +## 3.1.2 加载SPI扩展类EventPublishingRunListener 前面讲到,在SpringBoot的启动过程中首先会先新建一个`SpringApplicationRunListeners`对象用于发射SpringBoot启动过程中的生命周期事件,即我们现在来看下`SpringApplicationRunListeners listeners = getRunListeners(args);`这句代码: ```java @@ -116,7 +108,7 @@ private SpringApplicationRunListeners getRunListeners(String[] args) { ![](https://user-gold-cdn.xitu.io/2020/4/18/1718d054c88bebb9?w=692&h=117&f=png&s=13088) 由上图可以看到,`SpringApplicationRunListener`只有`EventPublishingRunListener`这个SPI实现类 -`EventPublishingRunListener`这个哥们在SpringBoot的启动过程中尤其重要,由其在SpringBoot启动过程的不同阶段发射不同的SpringBoot的生命周期事件,**即`SpringApplicationRunListeners`对象没有承担广播事件的职责,而最终是委托`EventPublishingRunListener`这个哥们来广播事件的。** +`EventPublishingRunListener`这个哥们在SpringBoot的启动过程中尤其重要,尤其在SpringBoot启动过程的不同阶段发射不同的SpringBoot的生命周期事件,**即`SpringApplicationRunListeners`对象没有承担广播事件的职责,而最终是委托`EventPublishingRunListener`这个哥们来广播事件的。** 因为从`spring.factories`中加载`EventPublishingRunListener`类后还会实例化该类,那么我们再跟进`EventPublishingRunListener`的源码,看看其是如何承担发射SpringBoot生命周期事件这一职责的? ```java @@ -263,7 +255,7 @@ public EventPublishingRunListener(SpringApplication application, String[] args) 可以看到在`EventPublishingRunListener`的构造函数中有一个`for`循环会遍历之前从`spring.factories`中加载的监听器们,然后添加到集合中缓存起来,用于以后广播各种事件时直接从这个集合中取出来即可,而不用再去`spring.factories`中加载,提高效率。 # 3.2 广播SpringBoot的内置生命周期事件 -从`spring.factories`配置文件中加载并实例化`EventPublishingRunListener`对象后,那么在在SpringBoot的启动过程中会发射一系列SpringBoot内置的生命周期事件,我们再来回顾下SpringBoot启动过程中的源码: +从`spring.factories`配置文件中加载并实例化`EventPublishingRunListener`对象后,那么在SpringBoot的启动过程中会发射一系列SpringBoot内置的生命周期事件,我们再来回顾下SpringBoot启动过程中的源码: ```java // SpringApplication.java diff --git "a/SpringBoot/10 SpringBoot345円206円205円347円275円256円347円224円237円345円221円275円345円221円250円346円234円237円344円272円213円344円273円266円350円257円246円350円247円243円 SpringBoot346円272円220円347円240円201円(345円215円201円).md" "b/SpringBoot/6 SpringBoot345円206円205円347円275円256円347円224円237円345円221円275円345円221円250円346円234円237円344円272円213円344円273円266円350円257円246円350円247円243円 SpringBoot346円272円220円347円240円201円(345円205円255円).md" similarity index 97% rename from "SpringBoot/10 SpringBoot345円206円205円347円275円256円347円224円237円345円221円275円345円221円250円346円234円237円344円272円213円344円273円266円350円257円246円350円247円243円 SpringBoot346円272円220円347円240円201円(345円215円201円).md" rename to "SpringBoot/6 SpringBoot345円206円205円347円275円256円347円224円237円345円221円275円345円221円250円346円234円237円344円272円213円344円273円266円350円257円246円350円247円243円 SpringBoot346円272円220円347円240円201円(345円205円255円).md" index 6d2aec5..077ec81 100644 --- "a/SpringBoot/10 SpringBoot345円206円205円347円275円256円347円224円237円345円221円275円345円221円250円346円234円237円344円272円213円344円273円266円350円257円246円350円247円243円 SpringBoot346円272円220円347円240円201円(345円215円201円).md" +++ "b/SpringBoot/6 SpringBoot345円206円205円347円275円256円347円224円237円345円221円275円345円221円250円346円234円237円344円272円213円344円273円266円350円257円246円350円247円243円 SpringBoot346円272円220円347円240円201円(345円205円255円).md" @@ -22,7 +22,7 @@ https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 3. 其次SpringBoot的生命周期事件基类`SpringApplicationEvent`继承了Spring的事件基类`ApplicationEvent`; 4. 最后SpringBoot具体的7个生命周期事件类再继承了SpringBoot的生命周期事件基类`SpringApplicationEvent`。 -# 3.1 JDK的事件基类EventObject +## 3.1 JDK的事件基类EventObject `EventObject`类是JDK的事件基类,可以说是所有Java事件类的基本,即所有的Java事件类都直接或间接继承于该类,源码如下: ```java // EventObject.java @@ -65,8 +65,9 @@ public class EventObject implements java.io.Serializable { } ``` 可以看到`EventObject`类只有一个属性`source`,这个属性是用来记录最初事件是发生在哪个类,举个栗子,比如在SpringBoot启动过程中会发射`ApplicationStartingEvent`事件,而这个事件最初是在`SpringApplication`类中发射的,因此`source`就是`SpringApplication`对象。 -# 3.2 Spring的事件基类ApplicationEvent +## 3.2 Spring的事件基类ApplicationEvent `ApplicationEvent`继承了DK的事件基类`EventObject`类,是Spring的事件基类,被所有Spring的具体事件类继承,源码如下: + ```java // ApplicationEvent.java @@ -99,8 +100,9 @@ public abstract class ApplicationEvent extends EventObject { } ``` 可以看到`ApplicationEvent`有且仅有一个属性`timestamp`,该属性是用来记录事件发生的时间。 -# 3.3 SpringBoot的事件基类SpringApplicationEvent +## 3.3 SpringBoot的事件基类SpringApplicationEvent `SpringApplicationEvent`类继承了Spring的事件基类`ApplicationEvent`,是所有SpringBoot内置生命周期事件的父类,源码如下: + ```java /** @@ -124,9 +126,9 @@ public abstract class SpringApplicationEvent extends ApplicationEvent { } ``` 可以看到`SpringApplicationEvent`有且仅有一个属性`args`,该属性就是SpringBoot启动时的命令行参数即标注`@SpringBootApplication`启动类中`main`函数的参数。 -# 3.4 SpringBoot具体的生命周期事件类 +## 3.4 SpringBoot具体的生命周期事件类 接下来我们再来看一下`SpringBoot`内置生命周期事件即`SpringApplicationEvent`的具体子类们。 -# 3.4.1 ApplicationStartingEvent +### 3.4.1 ApplicationStartingEvent ```java // ApplicationStartingEvent.java @@ -137,8 +139,8 @@ public class ApplicationStartingEvent extends SpringApplicationEvent { } } ``` -SpringBoot开始启动时便会发布`ApplicationStartingEvent`事件,其发布时机在环境变量Environment或容器ApplicationContext创建前但在注册`ApplicationListener`具体监听器之后,标志标志`SpringApplication`开始启动。 -# 3.4.2 ApplicationEnvironmentPreparedEvent +SpringBoot开始启动时便会发布`ApplicationStartingEvent`事件,其发布时机在环境变量Environment或容器ApplicationContext创建前但在注册`ApplicationListener`具体监听器之后,标志`SpringApplication`开始启动。 +### 3.4.2 ApplicationEnvironmentPreparedEvent ```java // ApplicationEnvironmentPreparedEvent.java @@ -172,7 +174,7 @@ public class ApplicationEnvironmentPreparedEvent extends SpringApplicationEvent 监听`ApplicationEnvironmentPreparedEvent`事件的第一个监听器是`ConfigFileApplicationListener`,因为是`ConfigFileApplicationListener`监听器还要为环境变量`Environment`增加`application.properties`配置文件中的环境变量;此后还有一些也是监听`ApplicationEnvironmentPreparedEvent`事件的其他监听器监听到此事件时,此时可以说环境变量`Environment`几乎已经完全准备好了。 > **思考:** 监听同一事件的监听器们执行监听逻辑时是有顺序的,我们可以想一下这个排序逻辑是什么时候排序的?还有为什么要这样排序呢? -# 3.4.3 ApplicationContextInitializedEvent +### 3.4.3 ApplicationContextInitializedEvent ```java // ApplicationContextInitializedEvent.java @@ -204,7 +206,7 @@ public class ApplicationContextInitializedEvent extends SpringApplicationEvent { > **扩展:** 可以看到`ApplicationContextInitializedEvent`是在为`context`容器配置`environment`变量后触发,此时`ApplicationContextInitializedEvent`等事件只要有`context`容器的话,那么其他需要`environment`环境变量的监听器只需要从`context`中取出`environment`变量即可,从而`ApplicationContextInitializedEvent`等事件没必要再配置`environment`属性。 -# 3.4.4 ApplicationPreparedEvent +### 3.4.4 ApplicationPreparedEvent ```java // ApplicationPreparedEvent.java @@ -237,7 +239,7 @@ public class ApplicationPreparedEvent extends SpringApplicationEvent { 3. 通过`context`属性取出`Environment`环境变量,然后就可以操作环境变量,比如`PropertiesMigrationListener`。 `ApplicationPreparedEvent`事件在`ApplicationContext`容器已经完全准备好时但在容器刷新前触发,在这个阶段`bean`定义已经加载完毕还有`environment`已经准备好可以用了。 -# 3.4.5 ApplicationStartedEvent +### 3.4.5 ApplicationStartedEvent ```java // ApplicationStartedEvent.java @@ -266,7 +268,7 @@ public class ApplicationStartedEvent extends SpringApplicationEvent { `ApplicationStartedEvent`事件将在容器刷新后但`ApplicationRunner`和`CommandLineRunner`的`run`方法执行前触发,标志`Spring`容器已经刷新,此时容器已经准备完毕了。 > **扩展:** 这里提到了`ApplicationRunner`和`CommandLineRunner`接口有啥作用呢?我们一般会在`Spring`容器刷新完毕后,此时可能有一些系统参数等静态数据需要加载,此时我们就可以实现了`ApplicationRunner`或`CommandLineRunner`接口来实现静态数据的加载。 -# 3.4.6 ApplicationReadyEvent +### 3.4.6 ApplicationReadyEvent ```java // ApplicationReadyEvent.java @@ -293,7 +295,7 @@ public class ApplicationReadyEvent extends SpringApplicationEvent { } ``` `ApplicationReadyEvent`事件在调用完`ApplicationRunner`和`CommandLineRunner`的`run`方法后触发,此时标志`SpringApplication`已经正在运行。 -# 3.4.7 ApplicationFailedEvent +### 3.4.7 ApplicationFailedEvent ```java // ApplicationFailedEvent.java diff --git "a/SpringBoot/3 345円212円251円345円212円233円SpringBoot350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円346円235円241円344円273円266円346円263円250円350円247円243円345円216円237円347円220円206円346円217円255円347円247円230円 SpringBoot346円272円220円347円240円201円357円274円210円344円270円211円357円274円211円.md" "b/SpringBoot/7 345円212円251円345円212円233円SpringBoot350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円346円235円241円344円273円266円346円263円250円350円247円243円345円216円237円347円220円206円346円217円255円347円247円230円 SpringBoot346円272円220円347円240円201円357円274円210円344円270円203円357円274円211円.md" similarity index 97% rename from "SpringBoot/3 345円212円251円345円212円233円SpringBoot350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円346円235円241円344円273円266円346円263円250円350円247円243円345円216円237円347円220円206円346円217円255円347円247円230円 SpringBoot346円272円220円347円240円201円357円274円210円344円270円211円357円274円211円.md" rename to "SpringBoot/7 345円212円251円345円212円233円SpringBoot350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円346円235円241円344円273円266円346円263円250円350円247円243円345円216円237円347円220円206円346円217円255円347円247円230円 SpringBoot346円272円220円347円240円201円357円274円210円344円270円203円357円274円211円.md" index 07033c6..9f85a39 100644 --- "a/SpringBoot/3 345円212円251円345円212円233円SpringBoot350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円346円235円241円344円273円266円346円263円250円350円247円243円345円216円237円347円220円206円346円217円255円347円247円230円 SpringBoot346円272円220円347円240円201円357円274円210円344円270円211円357円274円211円.md" +++ "b/SpringBoot/7 345円212円251円345円212円233円SpringBoot350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円346円235円241円344円273円266円346円263円250円350円247円243円345円216円237円347円220円206円346円217円255円347円247円230円 SpringBoot346円272円220円347円240円201円357円274円210円344円270円203円357円274円211円.md" @@ -1,10 +1,11 @@ # 1 前言 -本篇接 -[如何分析SpringBoot源码模块及结构?--SpringBoot源码(二)](https://github.com/yuanmabiji/Java-SourceCode-Blogs/blob/master/SpringBoot/2%20%E5%A6%82%E4%BD%95%E5%88%86%E6%9E%90SpringBoot%E6%BA%90%E7%A0%81%E6%A8%A1%E5%9D%97%E5%8F%8A%E7%BB%93%E6%9E%84%EF%BC%9F%20%20SpringBoot%E6%BA%90%E7%A0%81%EF%BC%88%E4%BA%8C%EF%BC%89.md) +上一篇描述了SpringBoot启动流程(`SpringApplication`对象`run`方法,方法体执行过程)中涉及到的7个发布事件 +![](https://user-gold-cdn.xitu.io/2020/5/2/171d300d55cc4470?w=796&h=769&f=png&s=378851) -上一篇分析了SpringBoot源码结构及各个模块pom之间的关系后,那么此篇开始就开始解开SpringBoot新特性之一--自动配置的神秘面纱了。因为SpringBoot自动配置原理是基于其大量的条件注解`ConditionalOnXXX`,因此,本节我们先来撸下Spring的条件注解的相关源码。 +从本篇开始我们将开始解读`@SpringBootApplication`中`@EnableAutoConfiguration`知识体系,本篇先分析自动配置类相关注解 # 2 SpringBoot的派生条件注解 + 我们都知道,SpringBoot自动配置是需要满足相应的条件才会自动配置,因此SpringBoot的自动配置大量应用了条件注解`ConditionalOnXXX`。如下图: @@ -595,10 +596,12 @@ private ConditionOutcome isServletWebApplication(ConditionContext context) { 参考: -1,[spring 自动配置(上) 配置文件和插件解读](https://www.jianshu.com/p/e575fddb6cb1) +1、[spring 自动配置(上) 配置文件和插件解读](https://www.jianshu.com/p/e575fddb6cb1) + +2、[SpringBoot内置条件注解](https://blog.csdn.net/jdfk423/article/details/82940949) -2,[SpringBoot内置条件注解](https://blog.csdn.net/jdfk423/article/details/82940949) +3、[spring boot 系列之六:深入理解spring boot的自动配置](https://www.cnblogs.com/sam-uncle/p/9111281.html) -3,[spring boot 系列之六:深入理解spring boot的自动配置](https://www.cnblogs.com/sam-uncle/p/9111281.html) +4、[@ConditionalOnBean和@ConditionalOnMissingBean注解默认情况](https://blog.csdn.net/killerofjava/article/details/104515684) diff --git "a/SpringBoot/4 SpringBoot346円230円257円345円246円202円344円275円225円345円256円236円347円216円260円350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円233円233円357円274円211円.md" "b/SpringBoot/8 SpringBoot346円230円257円345円246円202円344円275円225円345円256円236円347円216円260円350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円205円253円357円274円211円.md" similarity index 65% rename from "SpringBoot/4 SpringBoot346円230円257円345円246円202円344円275円225円345円256円236円347円216円260円350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円233円233円357円274円211円.md" rename to "SpringBoot/8 SpringBoot346円230円257円345円246円202円344円275円225円345円256円236円347円216円260円350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円205円253円357円274円211円.md" index 41421d4..fecbee9 100644 --- "a/SpringBoot/4 SpringBoot346円230円257円345円246円202円344円275円225円345円256円236円347円216円260円350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円233円233円357円274円211円.md" +++ "b/SpringBoot/8 SpringBoot346円230円257円345円246円202円344円275円225円345円256円236円347円216円260円350円207円252円345円212円250円351円205円215円347円275円256円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円345円205円253円357円274円211円.md" @@ -1,11 +1,8 @@ # 1 前言 -本篇接 -[助力SpringBoot自动配置的条件注解ConditionalOnXXX分析--SpringBoot源码(三)](https://github.com/yuanmabiji/Java-SourceCode-Blogs/blob/master/SpringBoot/3%20%E5%8A%A9%E5%8A%9BSpringBoot%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E7%9A%84%E6%9D%A1%E4%BB%B6%E6%B3%A8%E8%A7%A3%E5%8E%9F%E7%90%86%E6%8F%AD%E7%A7%98%20%20SpringBoot%E6%BA%90%E7%A0%81%EF%BC%88%E4%B8%89%EF%BC%89.md) - 温故而知新,我们来简单回顾一下上篇的内容,上一篇我们分析了SpringBoot的条件注解@ConditionalOnXxx的相关源码,现挑重点总结如下: 1. SpringBoot的所有`@ConditionalOnXxx`的条件类`OnXxxCondition`都是继承于`SpringBootCondition`基类,而`SpringBootCondition`又实现了`Condition`接口。 -2. `SpringBootCondition`基类主要用来打印一些条件注解评估报告的日志,这些条件评估信息全部来源于其子类注解条件类`OnXxxCondition`,因此其也抽象了一个模板方法`getMatchOutcome`留给子类去实现来评估其条件注解是否符合条件。 +2. `SpringBootConditiony`基类主要用来打印一些条件注解评估报告的日志,这些条件评估信息全部来源于其子类注解条件类`OnXxxCondition`,因此其也抽象了一个模板方法`getMatchOutcome`留给子类去实现来评估其条件注解是否符合条件。 3. 前一篇我们也还有一个重要的知识点还没分析,那就是跟过滤自动配置类逻辑有关的`AutoConfigurationImportFilter`接口,这篇文章我们来填一下这个坑。 前面我们分析了跟SpringBoot的自动配置息息相关内置条件注解`@ConditionalOnXxx`后,现在我们就开始来撸SpringBoot自动配置的相关源码了。 @@ -29,6 +26,7 @@ public @interface SpringBootApplication { ``` `@SpringBootApplication`标注了很多注解,我们可以看到其中跟SpringBoot自动配置有关的注解就有一个即`@EnableAutoConfiguration`,因此,可以肯定的是SpringBoot的自动配置肯定跟`@EnableAutoConfiguration`息息相关(其中`@ComponentScan`注解的`excludeFilters`属性也有一个类`AutoConfigurationExcludeFilter`,这个类跟自动配置也有点关系,但不是我们关注的重点)。 现在我们来打开`@EnableAutoConfiguration`注解的源码: + ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @@ -79,9 +77,26 @@ EnableAutoConfiguration=XxxAutoConfiguration 先看一下`getImports`方法代码: ```java -// ConfigurationClassParser.java +// ConfigurationClassParser$DeferredImportSelectorGrouping.java +private static class DeferredImportSelectorGrouping { + + private final DeferredImportSelector.Group group; + + private final List deferredImports = new ArrayList(); + + DeferredImportSelectorGrouping(Group group) { + this.group = group; + } -public Iterable getImports() { + public void add(DeferredImportSelectorHolder deferredImport) { + this.deferredImports.add(deferredImport); + } + + /** + * Return the imports defined by the group. + * @return each import with its associated configuration class + */ + public Iterable getImports() { // 遍历DeferredImportSelectorHolder对象集合deferredImports,deferredImports集合装了各种ImportSelector,当然这里装的是AutoConfigurationImportSelector for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { // 【1】,利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定导入哪些配置类(这个是我们分析的重点,自动配置的逻辑全在这了) @@ -91,18 +106,70 @@ public Iterable getImports() { // 【2】,经过上面的处理后,然后再进行选择导入哪些配置类 return this.group.selectImports(); } + } ``` 标`【1】`处的的代码是我们分析的**重中之重**,自动配置的相关的绝大部分逻辑全在这里了,将在4.1 分析自动配置的主要逻辑深入分析。那么`this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector())`;主要做的事情就是在`this.group`即`AutoConfigurationGroup`对象的`process`方法中,传入的`AutoConfigurationImportSelector`对象来选择一些符合条件的自动配置类,过滤掉一些不符合条件的自动配置类,就是这么个事情,无他。 - - + + 注: -1. `AutoConfigurationGroup`:是`AutoConfigurationImportSelector`的内部类,主要用来处理自动配置相关的逻辑,拥有`process`和`selectImports`方法,然后拥有`entries`和`autoConfigurationEntries`集合属性,这两个集合分别存储被处理后的符合条件的自动配置类,我们知道这些就足够了; -2. `AutoConfigurationImportSelector`:承担自动配置的绝大部分逻辑,负责选择一些符合条件的自动配置类; -3. `metadata`:标注在SpringBoot启动类上的`@SpringBootApplication`注解元数据 - +1. `AutoConfigurationImportSelector` 其实现了`DeferredImportSelector`接口; + +2. ``AutoConfigurationGroup`是`AutoConfigurationImportSelector`的内部类,其实现了`DeferredImportSelector`内部接口`DeferredImportSelector.Group` + + ```java + public interface DeferredImportSelector extends ImportSelector { + /** + * Return a specific import group or {@code null} if no grouping is required. + * @return the import group class or {@code null} + */ + @Nullable + default Class getImportGroup() { + return null; + } + + /** + * Interface used to group results from different import selectors. + */ + interface Group { + + /** + * Process the {@link AnnotationMetadata} of the importing @{@link Configuration} + * class using the specified {@link DeferredImportSelector}. + */ + void process(AnnotationMetadata metadata, DeferredImportSelector selector); + + /** + * Return the {@link Entry entries} of which class(es) should be imported for this + * group. + */ + Iterable selectImports(); + } + ··· + } + ``` + + ```java + public class AutoConfigurationImportSelector + implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, + BeanFactoryAware, EnvironmentAware, Ordered { + ···· + private static class AutoConfigurationGroup implements DeferredImportSelector.Group, + BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware { + ··· + } + } + ``` + +3. 主要用来处理自动配置相关的逻辑,拥有`process`和`selectImports`方法,然后拥有`entries`和`autoConfigurationEntries`集合属性,这两个集合分别存储被处理后的符合条件的自动配置类,我们知道这些就足够了; + +4. `AutoConfigurationImportSelector`:承担自动配置的绝大部分逻辑,负责选择一些符合条件的自动配置类; + +5. `metadata`:标注在SpringBoot启动类上的`@SpringBootApplication`注解元数据 + + 标`【2】`的`this.group.selectImports`的方法主要是针对前面的`process`方法处理后的自动配置类再进一步有选择的选择导入,将在4.2 有选择的导入自动配置类这小节深入分析。 @@ -114,32 +181,113 @@ public Iterable getImports() { ```java // AutoConfigurationImportSelector$AutoConfigurationGroup.java +private static class AutoConfigurationGroup implements DeferredImportSelector.Group, + BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware { + + private final Map entries = new LinkedHashMap(); + + private final List autoConfigurationEntries = new ArrayList(); + + private ClassLoader beanClassLoader; + + private BeanFactory beanFactory; + + private ResourceLoader resourceLoader; + + private AutoConfigurationMetadata autoConfigurationMetadata; + + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.beanClassLoader = classLoader; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + @Override + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + // 这里用来处理自动配置类,比如过滤掉不符合匹配条件的自动配置类 + @Override + public void process(AnnotationMetadata annotationMetadata, + DeferredImportSelector deferredImportSelector) { + Assert.state( + deferredImportSelector instanceof AutoConfigurationImportSelector, + () -> String.format("Only %s implementations are supported, got %s", + AutoConfigurationImportSelector.class.getSimpleName(), + deferredImportSelector.getClass().getName())); + // 1,调用getAutoConfigurationEntry方法得到自动配置类放入autoConfigurationEntry对象中 + AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) + .getAutoConfigurationEntry(getAutoConfigurationMetadata(), // 这里注意autoConfigurationMetadata和annotationMetadata的区别,autoConfigurationMetadata的properteis的键是自动配置类+条件注解类,值是条件注解类里面的属性值,TODO 唯一得注意的是有些自定义的配置类或加载配置类不在里面,这里不太明白 + annotationMetadata); // annotationMetadata即的启动类标注有@SpringBootApplication的注解属性值 + // 2,又将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合 + this.autoConfigurationEntries.add(autoConfigurationEntry); + // 3,遍历刚获取的自动配置类 + for (String importClassName : autoConfigurationEntry.getConfigurations()) { + // 这里符合条件的自动配置类作为key,annotationMetadata作为值放进entries集合 + this.entries.putIfAbsent(importClassName, annotationMetadata); + } + } + + // selectImports这个方法在上面的process方法后面调用 + @Override + public Iterable selectImports() { + if (this.autoConfigurationEntries.isEmpty()) { + return Collections.emptyList(); + } + // 这里得到所有要排除的自动配置类的set集合 + Set allExclusions = this.autoConfigurationEntries.stream() + .map(AutoConfigurationEntry::getExclusions) + .flatMap(Collection::stream).collect(Collectors.toSet()); + // 这里得到经过滤后所有符合条件的自动配置类的set集合 + Set processedConfigurations = this.autoConfigurationEntries.stream() + .map(AutoConfigurationEntry::getConfigurations) + .flatMap(Collection::stream) + .collect(Collectors.toCollection(LinkedHashSet::new)); + // 移除掉要排除的自动配置类 + processedConfigurations.removeAll(allExclusions); + // 对标注有@Order注解的自动配置类进行排序, + return sortAutoConfigurations(processedConfigurations, + getAutoConfigurationMetadata()) + .stream() + .map((importClassName) -> new Entry( + this.entries.get(importClassName), importClassName)) + .collect(Collectors.toList()); + } + + private AutoConfigurationMetadata getAutoConfigurationMetadata() { + if (this.autoConfigurationMetadata == null) { + this.autoConfigurationMetadata = AutoConfigurationMetadataLoader + .loadMetadata(this.beanClassLoader); + } + return this.autoConfigurationMetadata; + } + + private List sortAutoConfigurations(Set configurations, + AutoConfigurationMetadata autoConfigurationMetadata) { + return new AutoConfigurationSorter(getMetadataReaderFactory(), + autoConfigurationMetadata).getInPriorityOrder(configurations); + } + + private MetadataReaderFactory getMetadataReaderFactory() { + try { + return this.beanFactory.getBean( + SharedMetadataReaderFactoryContextInitializer.BEAN_NAME, + MetadataReaderFactory.class); + } catch (NoSuchBeanDefinitionException ex) { + return new CachingMetadataReaderFactory(this.resourceLoader); + } + } -// 这里用来处理自动配置类,比如过滤掉不符合匹配条件的自动配置类 -public void process(AnnotationMetadata annotationMetadata, - DeferredImportSelector deferredImportSelector) { - Assert.state( - deferredImportSelector instanceof AutoConfigurationImportSelector, - () -> String.format("Only %s implementations are supported, got %s", - AutoConfigurationImportSelector.class.getSimpleName(), - deferredImportSelector.getClass().getName())); - // 【1】,调用getAutoConfigurationEntry方法得到自动配置类放入autoConfigurationEntry对象中 - AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) - .getAutoConfigurationEntry(getAutoConfigurationMetadata(), - annotationMetadata); - // 【2】,又将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合 - this.autoConfigurationEntries.add(autoConfigurationEntry); - // 【3】,遍历刚获取的自动配置类 - for (String importClassName : autoConfigurationEntry.getConfigurations()) { - // 这里符合条件的自动配置类作为key,annotationMetadata作为值放进entries集合 - this.entries.putIfAbsent(importClassName, annotationMetadata); } -} ``` -上面代码中我们再来看标`【1】`的方法`getAutoConfigurationEntry`,这个方法主要是用来获取自动配置类有关,承担了自动配置的主要逻辑。直接上代码: +上面代码中我们再来看标`【1】`的方法`getAutoConfigurationEntry(这里内部类AutoConfigurationGroup方法的参数是是外部类的实例的多态接口,在内部类中使用所在外部类的实例调用外部类的成员方法),是这个方法主要是用来获取自动配置类有关,承担了自动配置的主要逻辑。直接上代码: ```java // AutoConfigurationImportSelector.java - // 获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费 protected AutoConfigurationEntry getAutoConfigurationEntry( AutoConfigurationMetadata autoConfigurationMetadata, @@ -259,31 +407,35 @@ private List filter(List configurations, ## 4.2 有选择的导入自动配置类 这里继续深究前面 4 分析SpringBoot自动配置原理这节标`【2】`处的 `this.group.selectImports`方法是如何进一步有选择的导入自动配置类的。直接看代码: + ```java // AutoConfigurationImportSelector$AutoConfigurationGroup.java - -public Iterable selectImports() { - if (this.autoConfigurationEntries.isEmpty()) { - return Collections.emptyList(); - } - // 这里得到所有要排除的自动配置类的set集合 - Set allExclusions = this.autoConfigurationEntries.stream() - .map(AutoConfigurationEntry::getExclusions) - .flatMap(Collection::stream).collect(Collectors.toSet()); - // 这里得到经过滤后所有符合条件的自动配置类的set集合 - Set processedConfigurations = this.autoConfigurationEntries.stream() - .map(AutoConfigurationEntry::getConfigurations) - .flatMap(Collection::stream) - .collect(Collectors.toCollection(LinkedHashSet::new)); - // 移除掉要排除的自动配置类 - processedConfigurations.removeAll(allExclusions); - // 对标注有@Order注解的自动配置类进行排序, - return sortAutoConfigurations(processedConfigurations, - getAutoConfigurationMetadata()) - .stream() - .map((importClassName) -> new Entry( - this.entries.get(importClassName), importClassName)) - .collect(Collectors.toList()); +private static class AutoConfigurationGroup implements DeferredImportSelector.Group, +BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware { + ······ + public Iterable selectImports() { + if (this.autoConfigurationEntries.isEmpty()) { + return Collections.emptyList(); + } + // 这里得到所有要排除的自动配置类的set集合 + Set allExclusions = this.autoConfigurationEntries.stream() + .map(AutoConfigurationEntry::getExclusions) + .flatMap(Collection::stream).collect(Collectors.toSet()); + // 这里得到经过滤后所有符合条件的自动配置类的set集合 + Set processedConfigurations = this.autoConfigurationEntries.stream() + .map(AutoConfigurationEntry::getConfigurations) + .flatMap(Collection::stream) + .collect(Collectors.toCollection(LinkedHashSet::new)); + // 移除掉要排除的自动配置类 + processedConfigurations.removeAll(allExclusions); + // 对标注有@Order注解的自动配置类进行排序, + return sortAutoConfigurations(processedConfigurations, + getAutoConfigurationMetadata()) + .stream() + .map((importClassName) -> new Entry( + this.entries.get(importClassName), importClassName)) + .collect(Collectors.toList()); + } } ``` 可以看到,`selectImports`方法主要是针对经过排除掉`exclude`的和被`AutoConfigurationImportFilter`接口过滤后的满足条件的自动配置类再进一步排除`exclude`的自动配置类,然后再排序。逻辑很简单,不再详述。 @@ -319,35 +471,38 @@ public interface AutoConfigurationImportFilter { ```java // FilteringSpringBootCondition.java - -@Override -public boolean[] match(String[] autoConfigurationClasses, - AutoConfigurationMetadata autoConfigurationMetadata) { - // 创建评估报告 - ConditionEvaluationReport report = ConditionEvaluationReport - .find(this.beanFactory); - // 注意getOutcomes是模板方法,将spring.factories文件种加载的所有自动配置类传入 - // 子类(这里指的是OnClassCondition,OnBeanCondition和OnWebApplicationCondition类)去过滤 - // 注意outcomes数组存储的是不匹配的结果,跟autoConfigurationClasses数组一一对应 - /*****************************【主线,重点关注】*********************************************/ - ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, - autoConfigurationMetadata); - boolean[] match = new boolean[outcomes.length]; - // 遍历outcomes,这里outcomes为null则表示匹配,不为null则表示不匹配 - for (int i = 0; i < outcomes.length; i++) { - ConditionOutcome outcome = outcomes[i]; - match[i] = (outcome == null || outcome.isMatch()); - if (!match[i] && outcomes[i] != null) { - // 这里若有某个类不匹配的话,此时调用父类SpringBootCondition的logOutcome方法打印日志 - logOutcome(autoConfigurationClasses[i], outcomes[i]); - // 并将不匹配情况记录到report - if (report != null) { - report.recordConditionEvaluation(autoConfigurationClasses[i], this, - outcomes[i]); - } - } - } - return match; +abstract class FilteringSpringBootCondition extends SpringBootCondition + implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware { + ······ + @Override + public boolean[] match(String[] autoConfigurationClasses, + AutoConfigurationMetadata autoConfigurationMetadata) { + // 创建评估报告 + ConditionEvaluationReport report = ConditionEvaluationReport + .find(this.beanFactory); + // 注意getOutcomes是模板方法,将spring.factories文件种加载的所有自动配置类传入 + // 子类(这里指的是OnClassCondition,OnBeanCondition和OnWebApplicationCondition类)去过滤 + // 注意outcomes数组存储的是不匹配的结果,跟autoConfigurationClasses数组一一对应 + /*****************************【主线,重点关注】*********************************************/ + ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, + autoConfigurationMetadata); + boolean[] match = new boolean[outcomes.length]; + // 遍历outcomes,这里outcomes为null则表示匹配,不为null则表示不匹配 + for (int i = 0; i < outcomes.length; i++) { + ConditionOutcome outcome = outcomes[i]; + match[i] = (outcome == null || outcome.isMatch()); + if (!match[i] && outcomes[i] != null) { + // 这里若有某个类不匹配的话,此时调用父类SpringBootCondition的logOutcome方法打印日志 + logOutcome(autoConfigurationClasses[i], outcomes[i]); + // 并将不匹配情况记录到report + if (report != null) { + report.recordConditionEvaluation(autoConfigurationClasses[i], this, + outcomes[i]); + } + } + } + return match; + } } ``` `FilteringSpringBootCondition`的`match`方法主要做的事情还是调用抽象模板方法`getOutcomes`来根据条件来过滤自动配置类,而复写`getOutcomes`模板方法的有三个子类,这里不再一一分析,**只挑选`OnClassCondition`复写的`getOutcomes`方法进行分析。** @@ -356,34 +511,37 @@ public boolean[] match(String[] autoConfigurationClasses, 先直接上`OnClassCondition`复写的`getOutcomes`方法的代码: ```java // OnClassCondition.java - -protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, - AutoConfigurationMetadata autoConfigurationMetadata) { - // Split the work and perform half in a background thread. Using a single - // additional thread seems to offer the best performance. More threads make - // things worse - // 这里经过测试用两个线程去跑的话性能是最好的,大于两个线程性能反而变差 - int split = autoConfigurationClasses.length / 2; - // 【1】开启一个新线程去扫描判断已经加载的一半自动配置类 - OutcomesResolver firstHalfResolver = createOutcomesResolver( - autoConfigurationClasses, 0, split, autoConfigurationMetadata); - // 【2】这里用主线程去扫描判断已经加载的一半自动配置类 - OutcomesResolver secondHalfResolver = new StandardOutcomesResolver( - autoConfigurationClasses, split, autoConfigurationClasses.length, - autoConfigurationMetadata, getBeanClassLoader()); - // 【3】先让主线程去执行解析一半自动配置类是否匹配条件 - ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes(); - // 【4】这里用新开启的线程取解析另一半自动配置类是否匹配 - // 注意为了防止主线程执行过快结束,resolveOutcomes方法里面调用了thread.join()来 - // 让主线程等待新线程执行结束,因为后面要合并两个线程的解析结果 - ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes(); - // 新建一个ConditionOutcome数组来存储自动配置类的筛选结果 - ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; - // 将前面两个线程的筛选结果分别拷贝进outcomes数组 - System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length); - System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length); - // 返回自动配置类的筛选结果 - return outcomes; +@Order(Ordered.HIGHEST_PRECEDENCE) +class OnClassCondition extends FilteringSpringBootCondition { + ······ + protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, + AutoConfigurationMetadata autoConfigurationMetadata) { + // Split the work and perform half in a background thread. Using a single + // additional thread seems to offer the best performance. More threads make + // things worse + // 这里经过测试用两个线程去跑的话性能是最好的,大于两个线程性能反而变差 + int split = autoConfigurationClasses.length / 2; + // 【1】开启一个新线程去扫描判断已经加载的一半自动配置类 + OutcomesResolver firstHalfResolver = createOutcomesResolver( + autoConfigurationClasses, 0, split, autoConfigurationMetadata); + // 【2】这里用主线程去扫描判断已经加载的一半自动配置类 + OutcomesResolver secondHalfResolver = new StandardOutcomesResolver( + autoConfigurationClasses, split, autoConfigurationClasses.length, + autoConfigurationMetadata, getBeanClassLoader()); + // 【3】先让主线程去执行解析一半自动配置类是否匹配条件 + ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes(); + // 【4】这里用新开启的线程取解析另一半自动配置类是否匹配 + // 注意为了防止主线程执行过快结束,resolveOutcomes方法里面调用了thread.join()来 + // 让主线程等待新线程执行结束,因为后面要合并两个线程的解析结果 + ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes(); + // 新建一个ConditionOutcome数组来存储自动配置类的筛选结果 + ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; + // 将前面两个线程的筛选结果分别拷贝进outcomes数组 + System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length); + System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length); + // 返回自动配置类的筛选结果 + return outcomes; + } } ``` 可以看到,`OnClassCondition`的`getOutcomes`方法主要解析自动配置类是否符合匹配条件,当然这个匹配条件指自动配置类上的注解`@ConditionalOnClass`指定的类存不存在于`classpath`中,存在则返回匹配,不存在则返回不匹配。 @@ -396,29 +554,28 @@ protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses 这里对应前面5.1节的代码注释标注`【1】`处的`OutcomesResolver firstHalfResolver = createOutcomesResolver(...);`的方法: ```java -// OnClassCondition.java + // OnClassCondition.java + private OutcomesResolver createOutcomesResolver(String[] autoConfigurationClasses, + int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) { + // 新建一个StandardOutcomesResolver对象 + OutcomesResolver outcomesResolver = new StandardOutcomesResolver( + autoConfigurationClasses, start, end, autoConfigurationMetadata, + getBeanClassLoader()); + try { + // new一个ThreadedOutcomesResolver对象,并将StandardOutcomesResolver类型的outcomesResolver对象作为构造器参数传入 + return new ThreadedOutcomesResolver(outcomesResolver); + } + // 若上面开启的线程抛出AccessControlException异常,则返回StandardOutcomesResolver对象 + catch (AccessControlException ex) { + return outcomesResolver; + } + } -private OutcomesResolver createOutcomesResolver(String[] autoConfigurationClasses, - int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) { - // 新建一个StandardOutcomesResolver对象 - OutcomesResolver outcomesResolver = new StandardOutcomesResolver( - autoConfigurationClasses, start, end, autoConfigurationMetadata, - getBeanClassLoader()); - try { - // new一个ThreadedOutcomesResolver对象,并将StandardOutcomesResolver类型的outcomesResolver对象作为构造器参数传入 - return new ThreadedOutcomesResolver(outcomesResolver); - } - // 若上面开启的线程抛出AccessControlException异常,则返回StandardOutcomesResolver对象 - catch (AccessControlException ex) { - return outcomesResolver; - } -} ``` 可以看到`createOutcomesResolver`方法创建了一个封装了`StandardOutcomesResolver`类的`ThreadedOutcomesResolver`解析对象。 我们再来看下`ThreadedOutcomesResolver`这个线程解析类封装`StandardOutcomesResolver`这个对象的目的是什么?我们继续跟进代码: ```java // OnClassCondtion.java - private ThreadedOutcomesResolver(OutcomesResolver outcomesResolver) { // 这里开启一个新的线程,这个线程其实还是利用StandardOutcomesResolver的resolveOutcomes方法 // 对自动配置类进行解析判断是否匹配 @@ -439,41 +596,42 @@ private ThreadedOutcomesResolver(OutcomesResolver outcomesResolver) { 这里`StandardOutcomesResolver.resolveOutcomes`方法承担了解析自动配置类匹配与否的全部逻辑,是我们要重点分析的方法,`resolveOutcomes`方法最终把解析的自动配置类的结果赋给`secondHalf`数组。那么它是如何解析自动配置类是否匹配条件的呢? ```java // OnClassCondition$StandardOutcomesResolver.java - -public ConditionOutcome[] resolveOutcomes() { - // 再调用getOutcomes方法来解析 - return getOutcomes(this.autoConfigurationClasses, this.start, this.end, - this.autoConfigurationMetadata); -} - -private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, - int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) { // 只要autoConfigurationMetadata没有存储相关自动配置类,那么outcome默认为null,则说明匹配 - ConditionOutcome[] outcomes = new ConditionOutcome[end - start]; - // 遍历每一个自动配置类 - for (int i = start; i < end; i++) { - String autoConfigurationClass = autoConfigurationClasses[i]; - // TODO 对于autoConfigurationMetadata有个疑问:为何有些自动配置类的条件注解能被加载到autoConfigurationMetadata,而有些又不能,比如自己定义的一个自动配置类HelloWorldEnableAutoConfiguration就没有被存到autoConfigurationMetadata中 - if (autoConfigurationClass != null) { - // 这里取出注解在AutoConfiguration自动配置类类的@ConditionalOnClass注解的指定类的全限定名, - // 举个栗子,看下面的KafkaStreamsAnnotationDrivenConfiguration这个自动配置类 - /** - * @ConditionalOnClass(StreamsBuilder.class) - * class KafkaStreamsAnnotationDrivenConfiguration { - * // 省略无关代码 - * } - */ - // 那么取出的就是StreamsBuilder类的全限定名即candidates = org.apache.kafka.streams.StreamsBuilder - String candidates = autoConfigurationMetadata - .get(autoConfigurationClass, "ConditionalOnClass"); // 因为这里是处理某个类是否存在于classpath中,所以传入的key是ConditionalOnClass - // 若自动配置类标有ConditionalOnClass注解且有值,此时调用getOutcome判断是否存在于类路径中 - if (candidates != null) { - // 拿到自动配置类注解@ConditionalOnClass的值后,再调用getOutcome方法去判断匹配结果,若该类存在于类路径,则getOutcome返回null,否则非null - /*******************【主线,重点关注】******************/ - outcomes[i - start] = getOutcome(candidates); - } - } - } - return outcomes; +private final class StandardOutcomesResolver implements OutcomesResolver { + public ConditionOutcome[] resolveOutcomes() { + // 再调用getOutcomes方法来解析 + return getOutcomes(this.autoConfigurationClasses, this.start, this.end, + this.autoConfigurationMetadata); + } + + private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, + int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) { // 只要autoConfigurationMetadata没有存储相关自动配置类,那么outcome默认为null,则说明匹配 + ConditionOutcome[] outcomes = new ConditionOutcome[end - start]; + // 遍历每一个自动配置类 + for (int i = start; i < end; i++) { + String autoConfigurationClass = autoConfigurationClasses[i]; + // TODO 对于autoConfigurationMetadata有个疑问:为何有些自动配置类的条件注解能被加载到autoConfigurationMetadata,而有些又不能,比如自己定义的一个自动配置类HelloWorldEnableAutoConfiguration就没有被存到autoConfigurationMetadata中 + if (autoConfigurationClass != null) { + // 这里取出注解在AutoConfiguration自动配置类类的@ConditionalOnClass注解的指定类的全限定名, + // 举个栗子,看下面的KafkaStreamsAnnotationDrivenConfiguration这个自动配置类 + /** + * @ConditionalOnClass(StreamsBuilder.class) + * class KafkaStreamsAnnotationDrivenConfiguration { + * // 省略无关代码 + * } + */ + // 那么取出的就是StreamsBuilder类的全限定名即candidates = org.apache.kafka.streams.StreamsBuilder + String candidates = autoConfigurationMetadata + .get(autoConfigurationClass, "ConditionalOnClass"); // 因为这里是处理某个类是否存在于classpath中,所以传入的key是ConditionalOnClass + // 若自动配置类标有ConditionalOnClass注解且有值,此时调用getOutcome判断是否存在于类路径中 + if (candidates != null) { + // 拿到自动配置类注解@ConditionalOnClass的值后,再调用getOutcome方法去判断匹配结果,若该类存在于类路径,则getOutcome返回null,否则非null + /*******************【主线,重点关注】******************/ + outcomes[i - start] = getOutcome(candidates); + } + } + } + return outcomes; + } } ``` 可以看到`StandardOutcomesResolver.resolveOutcomes`的方法中再次调用`getOutcomes`方法,主要是从`autoConfigurationMetadata`对象中获取到自动配置类上的注解`@ConditionalOnClass`指定的类的全限定名,然后作为参数传入`getOutcome`方法用于去类路径加载该类,若能加载到则说明注解`@ConditionalOnClass`满足条件,此时说明自动配置类匹配成功。 @@ -482,7 +640,6 @@ private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, ```java // OnClassCondition$StandardOutcomesResolver.java - // 返回的outcome记录的是不匹配的情况,不为null,则说明不匹配;为null,则说明匹配 private ConditionOutcome getOutcome(String candidates) { // candidates的形式为"org.springframework.boot.autoconfigure.aop.AopAutoConfiguration.ConditionalOnClass=org.aspectj.lang.annotation.Aspect,org.aspectj.lang.reflect.Advice,org.aspectj.weaver.AnnotatedElement" @@ -514,7 +671,6 @@ private ConditionOutcome getOutcome(String candidates) { ```java // OnClassCondition$StandardOutcomesResolver.java - private ConditionOutcome getOutcome(String className, ClassNameFilter classNameFilter, ClassLoader classLoader) { // 调用classNameFilter的matches方法来判断`@ConditionalOnClass`指定的类存不存在类路径中 @@ -531,8 +687,7 @@ private ConditionOutcome getOutcome(String className, 我们继续跟进`ClassNameFilter`的源码: ```java -// FilteringSpringBootCondition.java - +// FilteringSpringBootCondition.java 类结构定义参见上方5 protected enum ClassNameFilter { // 这里表示指定的类存在于类路径中,则返回true PRESENT { @@ -592,17 +747,18 @@ protected enum ClassNameFilter { ```java // OnClassCondition$ThreadedOutcomesResolver.java - -public ConditionOutcome[] resolveOutcomes() { - try { - // 调用子线程的Join方法,让主线程等待 - this.thread.join(); - } - catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - // 若子线程结束后,此时返回子线程的解析结果 - return this.outcomes; +private static final class ThreadedOutcomesResolver implements OutcomesResolver { + public ConditionOutcome[] resolveOutcomes() { + try { + // 调用子线程的Join方法,让主线程等待 + this.thread.join(); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + // 若子线程结束后,此时返回子线程的解析结果 + return this.outcomes; + } } ``` 可以看到用了`Thread.join()`方法来让主线程等待正在解析自动配置类的子线程,这里应该也可以用`CountDownLatch`来让主线程等待子线程结束。最终将子线程解析后的结果赋给`firstHalf`数组。 @@ -647,18 +803,32 @@ private void fireAutoConfigurationImportEvents(List configurations, 此时我们再来看下`ConditionEvaluationReportAutoConfigurationImportListener`监听器监听到事件后,它的`onAutoConfigurationImportEvent`方法究竟做了哪些事情: ```java // ConditionEvaluationReportAutoConfigurationImportListener.java +class ConditionEvaluationReportAutoConfigurationImportListener + implements AutoConfigurationImportListener, BeanFactoryAware { + + private ConfigurableListableBeanFactory beanFactory; + + @Override + public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) { + if (this.beanFactory != null) { + // 获取到条件评估报告器对象 + ConditionEvaluationReport report = ConditionEvaluationReport + .get(this.beanFactory); + // 将符合条件的自动配置类记录到unconditionalClasses集合中 + report.recordEvaluationCandidates(event.getCandidateConfigurations()); + // 将要exclude的自动配置类记录到exclusions集合中 + report.recordExclusions(event.getExclusions()); + } + } -public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) { - if (this.beanFactory != null) { - // 获取到条件评估报告器对象 - ConditionEvaluationReport report = ConditionEvaluationReport - .get(this.beanFactory); - // 将符合条件的自动配置类记录到unconditionalClasses集合中 - report.recordEvaluationCandidates(event.getCandidateConfigurations()); - // 将要exclude的自动配置类记录到exclusions集合中 - report.recordExclusions(event.getExclusions()); + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = (beanFactory instanceof ConfigurableListableBeanFactory) + ? (ConfigurableListableBeanFactory) beanFactory : null; } + } + ``` 可以看到,`ConditionEvaluationReportAutoConfigurationImportListener`监听器监听到事件后,做的事情很简单,只是分别记录下符合条件和被`exclude`的自动配置类。 @@ -673,23 +843,25 @@ public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) { public @interface AutoConfigurationPackage { } ``` -可以看到`@AutoConfigurationPackage`注解是跟SpringBoot自动配置所在的包相关的,即将 添加该注解的类所在的package 作为 自动配置package 进行管理。 +可以看到`@AutoConfigurationPackage`注解是跟SpringBoot自动配置所在的包相关的,**即将添加该注解的类所在的package作为自动配置package 进行管理**。 接下来我们再看看`AutoConfigurationPackages.Registrar`类是干嘛的,直接看源码: ```java //AutoConfigurationPackages.Registrar.java - -static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { - @Override - public void registerBeanDefinitions(AnnotationMetadata metadata, - BeanDefinitionRegistry registry) { - register(registry, new PackageImport(metadata).getPackageName()); - } - - @Override - public Set determineImports(AnnotationMetadata metadata) { - return Collections.singleton(new PackageImport(metadata)); - } +public abstract class AutoConfigurationPackages { + ······ + static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata, + BeanDefinitionRegistry registry) { + register(registry, new PackageImport(metadata).getPackageName()); + } + + @Override + public Set determineImports(AnnotationMetadata metadata) { + return Collections.singleton(new PackageImport(metadata)); + } + } } ``` @@ -715,8 +887,8 @@ public static void register(BeanDefinitionRegistry registry, String... packageNa } } ``` -如上,可以看到`register`方法注册了一个`packageNames`即自动配置类注解`@EnableAutoConfiguration`所在的所在的包名相关的`bean`。那么注册这个`bean`的目的是为了什么呢? -结合官网注释知道,注册这个自动配置包名相关的`bean`是为了被其他地方引用,比如`JPA entity scanner`,具体拿来干什么久不知道了,这里不再深究了。 +如上,可以看到`register`方法注册了一个`packageNames`即自动配置类注解`@EnableAutoConfiguration`所在的包名相关的`bean`。那么注册这个`bean`的目的是为了什么呢? +结合官网注释知道,注册这个自动配置包名相关的`bean`是为了被其他地方引用,比如`JPA entity scanner`,具体拿来干什么就不知道了,这里不再深究了。 # 8 小结 @@ -750,4 +922,6 @@ public static void register(BeanDefinitionRegistry registry, String... packageNa 1,[@AutoConfigurationPackage注解](https://blog.csdn.net/ttyy1112/article/details/101284541) +2, 目前全文主体涉及这些类或接口 +![image-20200629005005729](/Users/zhangyuhang/Library/Application Support/typora-user-images/image-20200629005005729.png) \ No newline at end of file diff --git "a/SpringBoot/5 SpringBoot347円232円204円351円205円215円347円275円256円345円261円236円346円200円247円345円200円274円346円230円257円345円246円202円344円275円225円347円273円221円345円256円232円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円344円272円224円357円274円211円.md" "b/SpringBoot/9 SpringBoot347円232円204円351円205円215円347円275円256円345円261円236円346円200円247円345円200円274円346円230円257円345円246円202円344円275円225円347円273円221円345円256円232円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円344円271円235円357円274円211円.md" similarity index 98% rename from "SpringBoot/5 SpringBoot347円232円204円351円205円215円347円275円256円345円261円236円346円200円247円345円200円274円346円230円257円345円246円202円344円275円225円347円273円221円345円256円232円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円344円272円224円357円274円211円.md" rename to "SpringBoot/9 SpringBoot347円232円204円351円205円215円347円275円256円345円261円236円346円200円247円345円200円274円346円230円257円345円246円202円344円275円225円347円273円221円345円256円232円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円344円271円235円357円274円211円.md" index b0256ef..13d7c4d 100644 --- "a/SpringBoot/5 SpringBoot347円232円204円351円205円215円347円275円256円345円261円236円346円200円247円345円200円274円346円230円257円345円246円202円344円275円225円347円273円221円345円256円232円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円344円272円224円357円274円211円.md" +++ "b/SpringBoot/9 SpringBoot347円232円204円351円205円215円347円275円256円345円261円236円346円200円247円345円200円274円346円230円257円345円246円202円344円275円225円347円273円221円345円256円232円347円232円204円357円274円237円 SpringBoot346円272円220円347円240円201円357円274円210円344円271円235円357円274円211円.md" @@ -2,8 +2,6 @@ # 1 前言 -本篇接 [SpringBoot是如何实现自动配置的?--SpringBoot源码(四)](https://github.com/yuanmabiji/Java-SourceCode-Blogs/blob/master/SpringBoot/4%20SpringBoot%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E7%9A%84%EF%BC%9F%20%20SpringBoot%E6%BA%90%E7%A0%81%EF%BC%88%E5%9B%9B%EF%BC%89.md) - 温故而知新,我们来简单回顾一下上篇的内容,上一篇我们分析了SpringBoot的自动配置的相关源码,自动配置相关源码主要有以下几个重要的步骤: 1. 从spring.factories配置文件中加载自动配置类; @@ -22,7 +20,11 @@ > 举个栗子:以配置web项目的服务器端口为例,若我们要将服务器端口配置为`8081`,那么我们会在`application.properties`配置文件中配置`server.port=8081`,此时该配置值`8081`就将会绑定到被`@ConfigurationProperties`注解的类`ServerProperties`的属性`port`上,从而使得配置生效。 +https://www.jianshu.com/p/7f54da1cb2eb + +https://blog.csdn.net/zknxx/article/details/79183698 +https://segmentfault.com/a/1190000018987185?utm_source=tag-newest @@ -100,6 +102,7 @@ public @interface EnableConfigurationProperties { ``` `@EnableConfigurationProperties`注解的主要作用就是为`@ConfigurationProperties`注解标注的类提供支持,即对将外部配置属性值(比如application.properties配置值)绑定到`@ConfigurationProperties`标注的类的属性中。 + > **注意**:SpringBoot源码中还存在了`ConfigurationPropertiesAutoConfiguration`这个自动配置类,同时`spring.factories`配置文件中的`EnableAutoConfiguration`接口也配置了`ConfigurationPropertiesAutoConfiguration`,这个自动配置类上也有`@EnableConfigurationProperties`这个注解,堆属性绑定进行了默认开启。 **那么,`@EnableConfigurationProperties`这个注解对属性绑定提供怎样的支持呢?** @@ -147,11 +150,11 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector { public static class ConfigurationPropertiesBeanRegistrar implements ImportBeanDefinitionRegistrar { @Override - public void registerBeanDefinitions(AnnotationMetadata metadata, // metadata是AnnotationMetadataReadingVisitor对象,存储了某个配置类的元数据 - BeanDefinitionRegistry registry) { - // (1)得到@EnableConfigurationProperties注解的所有属性值, - // 比如@EnableConfigurationProperties(ServerProperties.class),那么得到的值是ServerProperties.class - // (2)然后再将得到的@EnableConfigurationProperties注解的所有属性值注册到容器中 + public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) { + // metadata是AnnotationMetadataReadingVisitor对象,存储了某个配置类的元数据 + //(1)得到@EnableConfigurationProperties注解的所有属性值, + // 比如@EnableConfigurationProperties(ServerProperties.class),那么得到的值是ServerProperties.class + //(2)然后再将得到的@EnableConfigurationProperties注解的所有属性值注册到容器中 getTypes(metadata).forEach((type) -> register(registry, (ConfigurableListableBeanFactory) registry, type)); } @@ -241,7 +244,6 @@ public class ConfigurationPropertiesBindingPostProcessorRegistrar } ``` - `ConfigurationPropertiesBindingPostProcessorRegistrar`类的逻辑非常简单,主要用来注册外部配置属性绑定相关的后置处理器即`ConfigurationBeanFactoryMetadata`和`ConfigurationPropertiesBindingPostProcessor`。 那么接下来我们再来探究下注册的这两个后置处理器又是执行怎样的后置处理逻辑呢? diff --git a/SpringBoot/images/github.png b/SpringBoot/images/github.png new file mode 100644 index 0000000..ef446e4 Binary files /dev/null and b/SpringBoot/images/github.png differ diff --git a/SpringBoot/images/gitspeed.png b/SpringBoot/images/gitspeed.png new file mode 100644 index 0000000..9cafaa5 Binary files /dev/null and b/SpringBoot/images/gitspeed.png differ diff --git a/SpringBoot/images/maven.png b/SpringBoot/images/maven.png new file mode 100644 index 0000000..e1227d0 Binary files /dev/null and b/SpringBoot/images/maven.png differ

AltStyle によって変換されたページ (->オリジナル) /