一、前世今生
在弄清楚為什么有自動(dòng)裝配這個(gè)概念前,不妨讓我們回顧下,作為JAVA后端程序員,我們歷史上是怎么做的。
- spring 配置大量的xml,形如如下的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="moocAppctx" class="imooc_spring.test.aware.MoocApplicationContext"
init-method="hhhh">
</bean>
</beans>
- spring boot時(shí)代,你會(huì)驚奇的發(fā)現(xiàn),這些配置文件都不存在了。但來(lái)的是你加了些基礎(chǔ)依賴后,直接啟動(dòng)部分調(diào)用如下代碼后,整個(gè)spring就起飛了。
@SpringBootApplication(scanBasePackages = {"com.bianque.dqc"})
@MapperScan(basePackages = "com.test.mybatis.mapper", annotationClass = Mapper.class)
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(DqcApplication.class, args);
}
}
作為對(duì)springboot不熟悉的你,會(huì)比較疑惑,為什么會(huì)有這種情況出現(xiàn),是不是
- spring boot重復(fù)造了套輪子,沒(méi)有在使用這套xml技術(shù)了。
- spring boot使用了牛逼的技術(shù),跳出三界之外了。
帶著這樣的疑惑,我們發(fā)現(xiàn)spring boot的開(kāi)發(fā)與以往的spring開(kāi)發(fā)相比,優(yōu)勢(shì)就是只需要很少的代碼,就可以實(shí)現(xiàn)基礎(chǔ)的功能,也就是大家常說(shuō)的開(kāi)箱即用。哪么為什么能這么簡(jiǎn)單呢。
答案就是今天的主角:<b>自動(dòng)裝配</b>。
總結(jié)一句話:自動(dòng)裝配技術(shù),使得我們?cè)谑褂胹pringboot框架時(shí),通過(guò)以往積累的經(jīng)驗(yàn),針對(duì)大部分的配置采用默認(rèn)化,從大量重復(fù)的勞動(dòng)中解放出來(lái),專注業(yè)務(wù)本身。
這里可能有同學(xué)會(huì)問(wèn),沒(méi)有SpringBoot的前提下,我們可以完成Spring程序開(kāi)發(fā)嘛,答案是一定可以,只是技術(shù)的進(jìn)步是解放生產(chǎn)力,由于SpringBoot。解放了大量的配置勞動(dòng)。此時(shí)生產(chǎn)力自然提升。
二、抽絲剝繭
在理解了為什么SpringBoot出現(xiàn)的背景后,接下來(lái),我們要核心的講解下,他是怎么實(shí)現(xiàn)的自動(dòng)裝配。學(xué)習(xí)理解框架,最直接的版本,就是讀源碼。所以接下來(lái),我們會(huì)以一個(gè)實(shí)際的測(cè)試工程的源碼開(kāi)始,來(lái)一層層抽絲剝繭的系統(tǒng)講解,自動(dòng)裝配的實(shí)現(xiàn)邏輯。
1. @SpringBootApplication
在很多源碼剖析的書(shū)中或者文章中,都開(kāi)篇來(lái)將這個(gè)注解。我們也不能免俗,大家通過(guò)idea新建一工程后,首先看到的代碼大概如下所示:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
點(diǎn)擊到SpringBootApplication內(nèi)部,我們發(fā)現(xiàn)最核心的就
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
核心就看EnableAutoConfiguration
2. @EnableAutoConfiguration
定義如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
三、返璞歸真
SpringBoot所有自動(dòng)配置類都是在啟動(dòng)的時(shí)候進(jìn)行掃描并加載,通過(guò)spring.factories可以找到自動(dòng)配置類的路徑,但是不是所有存在于spring,factories中的配置都進(jìn)行加載,而是通過(guò)@ConditionalOnClass注解進(jìn)行判斷條件是否成立(只要導(dǎo)入相應(yīng)的stater,條件就能成立),如果條件成立則加載配置類,否則不加載該配置類。
總結(jié)如下
1.通過(guò)各種注解實(shí)現(xiàn)了類與類之間的依賴關(guān)系,容器在啟動(dòng)的時(shí)候Application.run,會(huì)調(diào)用EnableAutoConfigurationImportSelector.class的selectImports方法(其實(shí)是其父類的方法)--這里需要注意,調(diào)用這個(gè)方法之前發(fā)生了什么和是在哪里調(diào)用這個(gè)方法需要進(jìn)一步的探討
2.selectImports方法最終會(huì)調(diào)用SpringFactoriesLoader.loadFactoryNames方法來(lái)獲取一個(gè)全面的常用BeanConfiguration列表
3.loadFactoryNames方法會(huì)讀取FACTORIES_RESOURCE_LOCATION(也就是spring-boot-autoconfigure.jar 下面的spring.factories),獲取到所有的Spring相關(guān)的Bean的全限定名ClassName,大概120多個(gè)
4.selectImports方法繼續(xù)調(diào)用filter(configurations, autoConfigurationMetadata);這個(gè)時(shí)候會(huì)根據(jù)這些BeanConfiguration里面的條件,來(lái)一一篩選,最關(guān)鍵的是
@ConditionalOnClass,這個(gè)條件注解會(huì)去classpath下查找,jar包里面是否有這個(gè)條件依賴類,所以必須有了相應(yīng)的jar包,才有這些依賴類,才會(huì)生成IOC環(huán)境需要的一些默認(rèn)配置Bean
5.最后把符合條件的BeanConfiguration注入默認(rèn)的EnableConfigurationPropertie類里面的屬性值,并且注入到IOC環(huán)境當(dāng)中