天天看點

Spring源碼分析——AnnotationConfigApplicationContext元件注冊流程

工程搭建

Maven依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.5</version>
    </dependency>
</dependencies>
           

在項目下建立一個

byx.test

包,然後在裡面添加

A

B

Config

三個類:

public class A {
}

public class B {
}

@Component
public class Config {
}
           

A

B

是兩個普通的類(沒有标注

Component

)注解,

Config

标注了

Component

注解,是以理論上隻有

Config

會被注冊到容器中。

然後再添加一個

Main

類作為啟動類:

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext("byx.test");
        // 輸出容器中的所有bean的name
        for (String name : ctx.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }
}
           

main

函數中,建立了一個

AnnotationConfigApplicationContext

,然後輸出容器中所有bean的name。

最終的項目結構是這樣的:

Spring源碼分析——AnnotationConfigApplicationContext元件注冊流程

運作

Main

,控制台輸出如下:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
config
           

可以看到,容器中一共有5個bean,其中四個帶

internal

的都是Spring内部自帶的,

config

則是我們之前定義的

Config

類,下面就來探究一下這些元件是如何被注冊的。

AnnotationConfigApplicationContext元件注冊流程

main

函數的這一行加一個斷點,并啟動調試:

Spring源碼分析——AnnotationConfigApplicationContext元件注冊流程

首先step into,然後多次step over,直到進入

AnnotationConfigApplicationContext

的構造函數:

Spring源碼分析——AnnotationConfigApplicationContext元件注冊流程

繼續step over,執行完

this()

調用:

Spring源碼分析——AnnotationConfigApplicationContext元件注冊流程

這裡分享一個調試Spring的小技巧,就是通過觀察

BeanFactory

内部的

beanDefinitionMap

這個成員變量來分析元件注冊的時機。

beanDefinitionMap

是一個

ConcurrentHashMap

,它的鍵是bean的name,值是對應的

BeanDefinition

。通過觀察這個變量,我們就可以知道目前容器中所有已注冊的bean資訊。

現在把注意力放在調試器的Varieables面闆,找到

this.beanFactory.beanDefinitionMap

這個變量。

Spring源碼分析——AnnotationConfigApplicationContext元件注冊流程

可以看到,

beanDefinitionMap

的大小為4,裡面已經有了四個bean:

這四個bean都是Spring内部自帶的元件,由此可推測,Spring内部自帶的元件的注冊是在

this()

調用中,即

AnnotationConfigApplicationContext

的預設構造函數中完成的,。

繼續step over,執行完

scan(basePackages)

這行後,發現

beanDefinitionMap

的大小變成了5,增加了一個name為config的bean,正是我們自定義的

Config

類(該類被

Component

注解标注):

Spring源碼分析——AnnotationConfigApplicationContext元件注冊流程
Spring源碼分析——AnnotationConfigApplicationContext元件注冊流程

由此可推測,被

Component

注解标注的類是在

scan(basePackages)

調用中被注冊的。從方法名可以推測,其内部執行了一個包掃描的操作。

Spring内置元件的注冊

回到

AnnotationConfigApplicationContext

的構造函數:

public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    scan(basePackages);
    refresh();
}
           

從上面的分析可以知道,

AnnotationConfigApplicationContext

在它的預設構造函數中注冊内部元件,即

this()

調用,實作如下:

public AnnotationConfigApplicationContext() {
    StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    this.reader = new AnnotatedBeanDefinitionReader(this);
    createAnnotatedBeanDefReader.end();
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}
           

使用調試器跟蹤,同時注意

beanDefinitionMap

的變化,發現注冊操作發生在

this.reader = new AnnotatedBeanDefinitionReader(this)

這行代碼中,是以直接檢視

AnnotatedBeanDefinitionReader

的構造函數:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    this(registry, getOrCreateEnvironment(registry));
}
           

繼續進入另一個構造函數:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
           

真正的注冊操作發生在

AnnotationConfigUtils

registerAnnotationConfigProcessors

方法中:

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
    registerAnnotationConfigProcessors(registry, null);
}
           

繼續進入

registerAnnotationConfigProcessors

重載方法,終于看到了核心代碼(省略了一部分無關緊要的内容):

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    ...
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

    // 注冊org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    // ConfigurationClassPostProcessor用來處理Configuration注解
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 注冊org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    // AutowiredAnnotationBeanPostProcessor用來處理Autowired注解
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    ...
    // 注冊org.springframework.context.event.internalEventListenerProcessor
    // 與EventListener有關
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }

    // 注冊org.springframework.context.event.internalEventListenerFactory
    // 還是與EventListener有關
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    return beanDefs;
}
           

registerAnnotationConfigProcessors

方法内部注冊了我們在控制台輸出中看到的四個Spring内置元件。

Component注解的處理

回到

AnnotationConfigApplicationContext

的構造函數:

public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    scan(basePackages);
    refresh();
}
           

從上面的分析可以知道,

scan(basePackages)

這個調用負責掃描并注冊被

Component

标注的bean,該方法的實作如下:

public void scan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
            .tag("packages", () -> Arrays.toString(basePackages));
    this.scanner.scan(basePackages);
    scanPackages.end();
}
           

真正幹活的是

this.scanner.scan(basePackages)

這個調用,其中

this.scanner

是一個

ClassPathBeanDefinitionScanner

的執行個體,它在

AnnotationConfigApplicationContext

的預設構造函數中被初始化:

public AnnotationConfigApplicationContext() {
    StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    this.reader = new AnnotatedBeanDefinitionReader(this);
    createAnnotatedBeanDefReader.end();
    this.scanner = new ClassPathBeanDefinitionScanner(this); // 初始化scanner
}
           

ClassPathBeanDefinitionScanner

scan

方法實作如下:

public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

    doScan(basePackages);

    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
           

scan

方法内部調用了

doScan

方法,同時還記錄了bean數量的改變量。

doScan

方法實作如下:

// basePackages就是我們在main函數中構造AnnotationConfigApplicationContext時傳入的包名
// 從這裡也可以看出,我們可以同時傳入多個包名
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    // 周遊每個包名
    for (String basePackage : basePackages) {
        // 尋找每個包下符合條件的類,并包裝成BeanDefinition
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        // 周遊找到的每個BeanDefinition
        for (BeanDefinition candidate : candidates) {
            // 設定scope屬性
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // 生成beanName
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            // 設定一些預設屬性
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            // 處理Lazy、Primary、DependsOn、Role、Description這些注解
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // 把BeanDefinition包裝成BeanDefinitionHolder
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                // 真正執行注冊操作
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    // 傳回所有BeanDefinitionHolder
    return beanDefinitions;
}
           

doScan

方法是注冊bean的核心邏輯,它周遊每個傳入的包名,通過調用

findCandidateComponents

方法來擷取每個包下滿足條件的bean,然後進行一些必要的設定,最後調用

registerBeanDefinition

方法完成注冊操作。

findCandidateComponents

方法的實作如下:

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else {
        return scanCandidateComponents(basePackage);
    }
}
           

scanCandidateComponents

方法的實作如下:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // 将包名轉換成一個資源url
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // 讀取資源
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        // 周遊所有資源,每個資源表示一個.class檔案
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            if (resource.isReadable()) {
                try {
                    // 擷取class的中繼資料,包括注解的資訊
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    // 判斷是否滿足條件
                    if (isCandidateComponent(metadataReader)) {
                        // 構造BeanDefinition
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setSource(resource);
                        if (isCandidateComponent(sbd)) {
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            candidates.add(sbd);
                        }
                        else {
                            if (debugEnabled) {
                                logger.debug("Ignored because not a concrete top-level class: " + resource);
                            }
                        }
                    }
                    else {
                        if (traceEnabled) {
                            logger.trace("Ignored because not matching any filter: " + resource);
                        }
                    }
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to read candidate component class: " + resource, ex);
                }
            }
            else {
                if (traceEnabled) {
                    logger.trace("Ignored because not readable: " + resource);
                }
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    // 傳回所有滿足條件的BeanDefinition
    return candidates;
}
           

scanCandidateComponents

方法使用Spring内置的資源讀取機制讀取指定包下的所有class檔案,然後轉換成

MetadataReader

,并傳入

isCandidateComponent

方法判斷是否滿足要求,如果滿足要求則加入

isCandidateComponent

集合。

isCandidateComponent

方法的實作如下:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return false;
        }
    }
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}
           

isCandidateComponent

方法通過

excludeFilters

includeFilters

兩個集合來對

MetadataReader

進行過濾。在調試中可以發現,

includeFilters

包含了一個

Component

注解的過濾器,是以可以過濾出标注了

Component

的類。

如果使用調試器調試程式,可以發現,

isCandidateComponent

方法隻會對

Config

類傳回

true

,而對其他類(

A

B

Main

)都傳回

false

includeFilters

的初始化是在

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters

方法中被初始化的(

ClassPathScanningCandidateComponentProvider

ClassPathBeanDefinitionScanner

的父類):

protected void registerDefaultFilters() {
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ...
}
           

到此,

Component

注解的處理過程就分析完了。