spring boot源碼分析(一:@SpringBootApplication注解做了哪些事)

分析@SpringBootApplication注解做了哪些事。

總結:@SpringBootApplication注解內部有三個注解:
1、SpringBootConfiguration:springBootConfiguration注解上有configuration注解,所以作用和configuration注解類似。
2、EnableAutoConfiguration:這個注解上有AutoConfigurationPackage注解和@Import(AutoConfigurationImportSelector.class)注解。
AutoConfigurationPackage注解:會將啟動類的包名下、以及子包下,所有的類,注入到ioc容器。
@Import(AutoConfigurationImportSelector.class):會通過SpringFactoriesLoader.loadFactories方法獲取META-INF/spirng.factories下的EnableAutoConfiguration接口的所有組件實現類,比如AopAutoConfiguration、RabbitAutoConfiguration,進行各個組件的自動化配置。
3、ComponentScan:這個注解用于自定義spring boot掃描指定的包下的類加入ioc容器,以及可以配置過濾規則,過濾哪些不需要加入ioc容器。SpringBootApplication注解配置了一些自定義的過濾規則。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication 

springBootConfiguration注解上有configuration注解,所以作用類似

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration

看這個類前,先說@import注解。
import注解有三種使用方式:
1、直接普通的類的全類名寫上去,會注入ioc容器。

@Import({com.test.A, com.test.B } )
public class C{
}

2、注入的類,實現了ImportSelector接口,ImportSelector接口的抽象方法會返回需要注入的類的名稱數組。
3、注入的類,實現了ImportBeanDefinitionRegistrar,ImportBeanDefinitionRegistrar接口的抽象方法,會傳入參數BeanDefinitionRegistry ,用于手動注入到ioc容器。

可以看到這個注解上有兩部分,AutoConfigurationPackage注解和import注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration 

AutoConfigurationPackages.Registrar實現了ImportBeanDefinitionRegistrar接口,用于手動注入類到ioc中,可以看到方法內部傳參是啟動類的包名。把啟動類的包名下的所有類都注入ioc。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
        }

        @Override
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImports(metadata));
        }

    }

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
        if (registry.containsBeanDefinition(BEAN)) {
            BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
            beanDefinition.addBasePackages(packageNames);
        }
        else {
// 手動注入到ioc,傳參:啟動類的包名。把啟動類的包名下的所有類都注入ioc。
            registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
        }
    }

回過頭來再看這個注解,接著看@Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

實現了ImportSelector接口,說明selectImports方法返回的類名數組會注入到ioc容器。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
// 獲取注解屬性
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 核心。獲取候選的configuration的全類名數組。
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 下面都是對候選的configuration進行一些去重處理
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = getConfigurationClassFilter().filter(configurations);
// 發布自動配置導入事件
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

SpringFactoriesLoader.loadFactories方法會獲取META-INF/spirng.factories下的指定類的所有實現類。
這里獲取配置的EnableAutoConfiguration的所有實現類。
spring boot自動化配置各種組件的方式就是如此,spring-boot-autoconfiguration.jar包的spring.factories文件里定義好了各種組件的AutoConfiguration,包里也有各個AutoConfiguration類,比如AopAutoConfiguration、RabbitAutoConfiguration。組件又通過ConditionalOnClass達到自適應加載。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }
image.png

發布自動配置導入事件:獲取META-INF/spirng.factories下配置的AutoConfigurationImportListener的所有實現類,循環調用各個類的監聽事件方法。

private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
//  獲取META-INF/spirng.factories下配置的AutoConfigurationImportListener的所有實現類
        List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
        if (!listeners.isEmpty()) {
            AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
            for (AutoConfigurationImportListener listener : listeners) {
// 如果監聽器實現了Aware接口,就填充Aware的所需的參數。
                invokeAwareMethods(listener);
// 調用事件監聽器的監聽事件
                listener.onAutoConfigurationImportEvent(event);
            }
        }
    }

protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
// SpringFactoriesLoader.loadFactories方法會獲取META-INF/spirng.factories下的指定類的所有實現類。
// 這了獲取配置的AutoConfigurationImportListener的所有實現類
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
    }

看一下SpringFactoriesLoader.loadFactoryNames方法。
1、存在一個緩存,也就是說多次調用,不會重復解析加載
2、 獲取所有的META-INF/spring.factories路徑,包括各個jar包的。
3、循環解析,存到result這個map中,并返回這個map。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoaderToUse == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 存在一個緩存,也就是說多次調用,不會重復解析加載
        Map<String, List<String>> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        result = new HashMap<>();
        try {
// 獲取所有的META-INF/spring.factories路徑,包括各個jar包的。
            Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
// 循環解析,存到result這個map中。
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    String[] factoryImplementationNames =
                            StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                    for (String factoryImplementationName : factoryImplementationNames) {
                        result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                                .add(factoryImplementationName.trim());
                    }
                }
            }

            // Replace all lists with unmodifiable lists containing unique elements
            result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                    .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
            cache.put(classLoader, result);
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
        return result;
    }

總結:@SpringBootApplication注解內部有三個注解:
1、SpringBootConfiguration:springBootConfiguration注解上有configuration注解,所以作用和configuration注解類似。
2、EnableAutoConfiguration:這個注解上有AutoConfigurationPackage注解和@Import(AutoConfigurationImportSelector.class)注解。
AutoConfigurationPackage注解:會將啟動類的包名下、以及子包下,所有的類,注入到ioc容器。
@Import(AutoConfigurationImportSelector.class):會通過SpringFactoriesLoader.loadFactories方法獲取META-INF/spirng.factories下的EnableAutoConfiguration接口的所有組件實現類,比如AopAutoConfiguration、RabbitAutoConfiguration,進行各個組件的自動化配置。

3、ComponentScan:這個注解用于自定義spring boot掃描指定的包下的類加入ioc容器,以及可以配置過濾規則,過濾哪些不需要加入ioc容器。SpringBootApplication注解配置了一些自定義的過濾規則。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內容