1. 程式人生 > >Spring 原始碼閱讀 二

Spring 原始碼閱讀 二

程式入口:

接著上一篇部落格中看完了在AnnotationConfigApplicationContext的建構函式中的register(annotatedClasses);將我們傳遞進來的主配置類新增進了BeanFactory, 本片部落格繼續跟進refresh(); 看看Spring如何繼續初始化Spring的環境

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();
    register(annotatedClasses)
    refresh();
}

跟進refresh(), 原始碼如下: 主要做了如下幾件工作

  • 重新整理的預準備
    • 比如: 設定時間的錨點,載入上下文環境變數
  • 獲取BeanFactory
  • 執行所有的BeanFactoryPostProcessor
  • 執行所有的BeanPostProcessor
  • ...
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing.
    //準備重新整理
    prepareRefresh();

    //獲取BeanFactory 
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // 準備BeanFactory
    prepareBeanFactory(beanFactory);
    try {
        // 方法中沒有任何實現的邏輯
        postProcessBeanFactory(beanFactory);

        // invoke BeanFactoryPostprocessor, 執行bean工廠的後置處理器
        //如果我們沒有手動往Spring中注入bean工廠的後置處理器,那麼此時僅有一個,也是beanFactoryMap中的第一個RootBeanDefinition-> ConfigurationClassPostProcessor
        invokeBeanFactoryPostProcessors(beanFactory);

        // 註冊 bean的後置處理器, 這些處理器可以在bean的構造方法執行之後再執行init()方法前後執行指定的邏輯
        registerBeanPostProcessors(beanFactory);

        // Initialize message source for this context.
        initMessageSource();

        // Initialize event multicaster for this context.
        initApplicationEventMulticaster();

        // Initialize other special beans in specific context subclasses.
        onRefresh();

        // Check for listener beans and register them.
        registerListeners();

        // Instantiate all remaining (non-lazy-init) singletons.
        finishBeanFactoryInitialization(beanFactory);

        // Last step: publish corresponding event.
        finishRefresh();
    }
  }
}

重新整理的準備工作

這個方法沒啥可看的重要邏輯,記錄了下開始的時間,然後為Spring的上下文載入可用的環境變數

    protected void prepareRefresh() {
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);

        if (logger.isInfoEnabled()) {
            logger.info("Refreshing " + this);
        }

        //  這個是protected型別的方法,目前還沒有任何實現
        initPropertySources();

        //   校驗所有需要的properties是否都被解析過了
        //   getEnvironment() 得到系統環境, 後續的@Profile使用
        getEnvironment().validateRequiredProperties();
        
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }

獲取BeanFactory

獲取出BeanFactory,接下來的工作重點是去掃描出程式設計師提供的類,然後將它們放進BeanFactoryMap中,在此過程中穿插執行BeanFactoryPostProcessorBeanPostPorcessor, 不難看出後續工作的進展都離不開這個BeanFactoryMap,這個map在哪裡呢? 就在我們的beanFactory中,因此在重新整理的最開始,獲取出bean工廠

當前類是AbstractApplicationContext,上圖是它的繼承類圖,通過上圖可以看到,它是入口AnnotationConfigApplicationContextGenericApplicationContext的父類,而Spring的BeanFactory是在GenericApplicationContext中例項化的,故, 獲取beanFactory的邏輯肯定在當前方法中被設計成抽象的方法,而由自己具體實現,原始碼如下:

    @Override
    public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

準備beanFactory

上面的邏輯是獲取出BeanFactory, 那什麼是準備BeanFactory呢? 看它的註解解釋是: 為BeanFactory配置上它應該具有的所有特徵, 那BeanFactory應該有什麼特徵呢? 類載入器 , bean表示式的解析器 , property與物件的轉換器 , bean的後置處理器 , 新增禁止使用者注入的bean的資訊 , 注入bean的替換 , 新增預設的和環境相關的bean

原始碼如下:它的解析我們寫在下面

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 新增類載入器
    beanFactory.setBeanClassLoader(getClassLoader());

    // 設定bean標籤的解析器, 一般我們使用spel標籤比較多,但是Spring也有自己的Bean標籤 
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

    // </property ref="XXX"> 解析轉換xxx 替換成物件
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // 新增bean的後置處理器,很顯然這裡新增的是Spring自己的後置處理器
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

    // 當用戶企圖注入下面型別的物件時, 會被Spring忽略
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

    //使用者穿進來的是 BeanFactory.class , 那Spring會將她替換成beanFactory
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // Register early post-processor for detecting inner beans as ApplicationListeners.
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // Detect a LoadTimeWeaver and prepare for weaving, if found.
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // Set a temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // 意思是如果自定義的Bean中沒有名為"systemProperties"和"systemEnvironment"的Bean,
    // 則註冊兩個Bena,Key為"systemProperties"和"systemEnvironment",Value為Map,
    // 這兩個Bean就是一些系統配置和系統環境資訊
    //  註冊預設的和環境相關的 bean   Sping會檢測,我們自己註冊進來的Bean中有沒有下面的名字叫下面三個串的物件, 沒有的話就幫我們注入進來
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { //   environment_bean_name
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {//   system_properties_bean_name
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {//   system_environment_bean_name
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

如上程式碼的作用就是為BeanFactory初始化了Spring規定的幾個必須的配置屬性,其中比較值得注意的地方就是它新增的BeanPostProcessor, 雖然這是Spring原生的bean的後置處理器,但是也是第一次出現,很有意義,配置expressContext等工作, 這個後置處理器會在Bean的構造的構造過程中,動態的攔截插手

此外,添加了忽略注入的物件,當程式設計師向注入Spring啟動時,依賴的原生物件時,會被忽略注入,企圖注入BeanFactory,資源解析器,事件釋出器,應用上下文時,被Spring使用原生的物件替換掉

執行所有的BeanFactory的後置處理器

執行BeanFactory的後置處理器,具體是哪些呢? 其實是兩部分,一部分是使用者自己新增的,另一部分是Spring在啟動過程中自己新增進去的

先說使用者自己新增的情況,不知道大家有沒有發現,原始碼讀到這裡其實還沒看到Spring進行包掃描,既然沒有進行包掃描那程式設計師通過@Compoennt註解新增進去的 bean工廠的後置處理器 就還沒有被Spring所識別到,沒錯,這裡能被識別到的 BeanFactoryPostProcessor是程式設計師通過context.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());新增進來的

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {

    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

繼續跟進invokeBeanFactoryPostProcessors()方法,原始碼如下,這個方法在執行BeanFactoryPostProcessor不得不說他真的很重要,很長

它的主要邏輯是: 開始是一個if分支判斷beanFactory的合法性,上篇博文中我們看到了,所謂的beanFactory其實本身也實現了註冊器介面,有註冊bean的功能, 於是他將BeanFactory強轉成了註冊器型別

緊接著Spring定義了兩個新的List, 一個叫regularPostProcessors一個叫registryProcessors, 前者用來存放程式設計師自己新增進來的BeanFactoryPostProcessor, 後者用來存放程式設計師自己新增進來的BeanDefinitionRegistryPostProcessor, 為什麼使用兩個集合呢? 參見下圖:

通過上面的圖可以看到,BeanFactoryPostProcessor是頂級的介面,BeanDefinitionRegistryPostProcessor是繼承了頂級介面然後自己做出了拓展, 一般程式設計師通過BeanFactoryPostProcessor在bean的構造方法之前進行插手的話,最常用的就是選擇直接自己實現BeanFactoryPostProcessor,雖然實現BeanDefinitionRegistryPostProcessor也行

BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor做出來拓展, Spring需要保證拓展的方法被執行到,重寫的父類的方法也要被執行到,因此選擇使用兩個集合,迴圈所有的bean工廠的後置處理器,按照不同的分類分別對待

分好類之後,又建立了一個list叫currentRegistryProcessors這個List中存放的是 Spring自己的提供的BeanFactoryPostProcessor的實現, 其實這個實現在前面提到過好多次了. 他就是ConfigurationClassPostProcessor

現在一共是三個集合,其中兩個集合中的存放的物件是一樣的,於是Spring將其實兩個存放BeanDefinitionRegistryPostProcessor的集合進行了合併

接下來就是真正的開始執行的工作

  • 執行BeanDefinitionRegistryPostProcessor
  • 執行BeanFactoryPostProcessor
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();

//   通過看BanFactory的繼承體系,能看到它實現了 BeanDefinitionRegistry介面
if (beanFactory instanceof BeanDefinitionRegistry) {
//    將工廠強轉成 註冊器
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

//   存放程式設計師新增進來的 BeanFactoryPostProcessor
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
//   存放程式設計師新增進來的 BeanDefinitionRegistryPostProcessor
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

System.out.println(beanFactoryPostProcessors.size());

//  自定義的beanFactoryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {

    if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
        BeanDefinitionRegistryPostProcessor registryProcessor =
                (BeanDefinitionRegistryPostProcessor) postProcessor;
        registryProcessor.postProcessBeanDefinitionRegistry(registry);
        registryProcessors.add(registryProcessor);
    }
    else {//BeanDefinitionRegistryPostProcessor  BeanfactoryPostProcessor
        //   將自定義的BeanFactoryPostPorcessor 新增到了 上面的 ArrayList中regularPostProcessors
        regularPostProcessors.add(postProcessor);
    }
}


List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

String[] postProcessorNames =
        beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

for (String ppName : postProcessorNames) {
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}

sortPostProcessors(currentRegistryProcessors, beanFactory);

registryProcessors.addAll(currentRegistryProcessors);

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

currentRegistryProcessors.clear();

postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
    reiterate = false;
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
        if (!processedBeans.contains(ppName)) {
            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
            processedBeans.add(ppName);
            reiterate = true;
        }
    }
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    registryProcessors.addAll(currentRegistryProcessors);
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    currentRegistryProcessors.clear();
}


invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);


invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
...
}

接下來與其說 看一下它如何執行BeanDefinitionRegistryPostProcessor, 到不如說 看一下如何執行ConfigrationClassPostProcessorBeanFactoryPostProcessor的拓展方法

postProcessor.postProcessBeanDefinitionRegistry(registry);

繼續跟進,進過兩個沒有啥重要邏輯的方法之後,進入到下面的邏輯中, 有來了一個高潮,在這個方法中完成了包掃描工作

一開始獲取出beanFactoryMap中的全部的BeanDefinitionName, 這時一共有幾個? 其實是7個, 其中6個是在建立AnnotatedBeanDefinitionReader時新增進去的6個, 另外的哪一個就是在前面的register()方法中,註冊的我們的主配置類MainConfig

緊接著是一個迴圈判斷語句,迴圈這七個BeanDefinition, 目的有兩個,第一個把我們自己的MainConfig配置類找出來放到下面的configCandidates集合中,因為這是個配置類啊,上面會有@ComponentScan(value="XXX") 通過這個註解提供的包資訊,Spring就能進一步進行包掃描,找到使用者提供的所有的類資訊,將它們載入進容器中, 第二個判斷一下我們的MainConfig類上有沒有新增@Configuration 如果存在這個註解標記它為full,Spring就認為我們的當前的執行的上下文環境是全註解環境,並且會為MainConfig生成一個cglib代理物件,進一步保證了Spring的單例特徵,如果沒有這個註解,但是存在@Component @ComponentScan @Import @ImportResourceSpring標記它為lite.認為當前的上下文環境為非全註解模式

怎麼理解這個全註解與非全註解呢? 字面意思也是,全註解就是不存在配置檔案, 不存在配置檔案的話,程式設計師不可能不提供@ComponentScan讓Spring去掃描完成Bean的注入,同時程式設計師也會提供一個@Configuration明確的標識這是一個配置類, 那非全註解呢, 就是可能存在註解和XML共存的現象, Spring這時也會同時支援註解+xml的讀取

接著又是排序,建立名稱生成器

緊接著建立了一個ConfigurationClassParser配置類的解析器,這個解析器,見名知意,用來解析配置類, 現在誰是配置類呢? 其實就是在上面的迴圈中唯一被新增進list的,我們提供的MainConfig, 因為我在他身上添加了@Configuration註解

下面的主要邏輯是解析配置類,我把解釋寫在如下程式碼的下面

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//  定義一個List 存放專案中添加了 @Compennt註解的類
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();

//  獲取容器中註冊的所有bd名字
//  一共 7個 , 6個rootBeanDefinition  1個我們自己的MainConfig
//   獲取出一開始我們Spring自己新增的6個Processor, 和我們新增的MainConfig
String[] candidateNames = registry.getBeanDefinitionNames();

for (String beanName : candidateNames) {
    BeanDefinition beanDef = registry.getBeanDefinition(beanName);
    //  
    if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
            ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
        if (logger.isDebugEnabled()) {
            logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
        }
    }

    else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {

        configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
    }
}

//   他是怎麼判斷出來的呢? 在上面, 如果判斷得出當前的類添加了@Configration , 就給他標記 full, 在上面的if分支語句中,添加了full的類,不會新增進 configCandidates 中,故為空
if (configCandidates.isEmpty()) {
    return;
}

// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
    int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
    int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
    return Integer.compare(i1, i2);
});

// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;

//   現在我們使用的BeanDefinitionRegistry是其實是Spring的Bean工廠(DefaultListableBeanFactory)  他是SingletonBeanRegistry的子類的話
if (registry instanceof SingletonBeanRegistry) {
    //  將registry強轉為SingletonBeanRegistry
    sbr = (SingletonBeanRegistry) registry;
    if (!this.localBeanNameGeneratorSet) {
        //  是否有自定義的
        BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
        //SingletonBeanRegistry中有id為 org.springframework.context.annotation.internalConfigurationBeanNameGenerator
        //  如果有則利用他的,否則是spring預設的
        if (generator != null) {
            this.componentScanBeanNameGenerator = generator;
            this.importBeanNameGenerator = generator;
        }
    }
}

if (this.environment == null) {
    this.environment = new StandardEnvironment();
}

//   這是個配置類的解析器  會解析每一個添加了  @Configuration 的類
ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
    //   僅僅處理添加了@Configuration註解的類, 進行包掃描,跟進去
    parser.parse(candidates);
    //   執行到這裡完成了掃描,BeanFactory中的BeanDefinitionMap中就多了我們位元組新增進去的bean資訊

    parser.validate();
    //map.keyset
    Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
    configClasses.removeAll(alreadyParsed);

    // Read the model and create bean definitions based on its content
    if (this.reader == null) {
        this.reader = new ConfigurationClassBeanDefinitionReader(
                registry, this.sourceExtractor, this.resourceLoader, this.environment,
                this.importBeanNameGenerator, parser.getImportRegistry());
    }

    this.reader.loadBeanDefinitions(configClasses);
    alreadyParsed.addAll(configClasses);

    candidates.clear();

...

跟進這個parser.parse(candidates);,這裡就來到了又一波高潮,準備開始包掃描了

不怕麻煩,再提一下,當前的這個物件就是我們的MianConfig,它是被AnnotatedBeanDefinitionReader讀取到的,所以它一定是AnnotatedBeanDefinition, 所以一定會進入到第一個if分支中

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<>();
    for (BeanDefinitionHolder holder : configCandidates) {
    BeanDefinition bd = holder.getBeanDefinition();
    try {
        if (bd instanceof AnnotatedBeanDefinition) {
            parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
        }
        else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
            parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
        }

繼續跟進,同樣是經過了幾個沒有重要邏輯的方法之後,進入到下面的方法中,看他是如何下面這個重要的方法中

這個方法主要做了兩件大事:

  • 處理掃描新增有@Component註解的普通類,並將它們直接新增到BeanFactoryMap
  • 掃描處理新增有@Import註解

看他首先取出所有的@CompoenntScan註解,迴圈遍歷註解,每次迴圈都使用this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());進行真正的包掃描

跟進這個方法中,可以看到它一開始就自己重新new 了一個包掃描器,然後解析當前迴圈的@ComponentScan註解上的其他如excludeFilters,includeFilters等屬性,最後開始真正的進行包掃描, 在這個掃描的過程中,會將命中符合條件的普通類(如被@Component標識),進行如下處理

  • 設定scope資訊
  • 生成BeanName
  • 給掃描出來的這些新增上預設的屬性資訊比如預設全是Lazy
  • 進一步,處理這些類上的註解資訊,比如@Lazy , @Primary , @DependsOn , @Role , @Description,用這些資訊覆蓋預設的資訊
  • 將掃描出來的普通類直接新增到BeanFactoryMap中

完成了上面的普通類的掃描工作之後,下一個高潮就來了,處理@Import()的三中情況,它的解析我寫在如下程式碼的下面


protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {

        // Recursively process any member (nested) classes first
        //  遞迴地首先處理任何成員(巢狀)類
        processMemberClasses(configClass, sourceClass);

        // Process any @PropertySource annotations
        for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
                sourceClass.getMetadata(), PropertySources.class,
                org.springframework.context.annotation.PropertySource.class)) {
            if (this.environment instanceof ConfigurableEnvironment) {
                processPropertySource(propertySource);
            }
            else {
                logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                        "]. Reason: Environment must implement ConfigurableEnvironment");
            }
        }

        //   處理所有的@ComponentScan註解, 也就是讀取到了我們在MainConfig中使用@ConponentScans 中新增的元資訊, 如value=com.changwu
        //   basePackages  lazyInit  userDefualtFileter ,,,  includeFileters excludeFilters  scopeResolver  nameGenerate ,,,
        // Process any @ComponentScan annotations
        Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
                sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
        if (!componentScans.isEmpty() &&
                !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
            //   為什麼要迴圈,以為 @ComponentScans(value={1,2,3}) value是一個數組
            for (AnnotationAttributes componentScan : componentScans) {
                // The config class is annotated with @ComponentScan -> perform the scan immediately
                //   掃描com.changwu下面的普通類, 也就是添加了@Component註解的類, 然後將掃描出來的bean放到map中
                Set<BeanDefinitionHolder> scannedBeanDefinitions =
                        this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

                // Check the set of scanned definitions for any further config classes and parse recursively if needed
                //檢查掃描出來的類當中是否還有configuration
                for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                    if (bdCand == null) {
                        bdCand = holder.getBeanDefinition();
                    }
                    //檢查    看看被掃描的普通類有沒有新增 配置相關的註解
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                        parse(bdCand.getBeanClassName(), holder.getBeanName());
                    }
                }
            }
        }

        processImports(configClass, sourceClass, getImports(sourceClass), true);

        // Process any @ImportResource annotations
        AnnotationAttributes importResource =
        ...

跟進上面的processImports(configClass, sourceClass, getImports(sourceClass), true);方法,看他如何處理@Import註解,通過下面的程式碼不難看出@Import註解存在三種情況,分別是

  • ImportSelector
  • ImportBeanDefinitionRegistrar
  • 普通類

第一種情況處理ImportSelector, 這個ImportSelector是很好用的元件,首先第一點: 我們可以通過自動ImportSelector完成類的批量注入,但是吧這個功能感覺就像是雞肋,棄之可惜,食之無味, 其實他還有一個妙用!配合jdk的動態代理我們可以實現類似AOP的切面,針對某一個物件進行動態的代理, 舉個例子: 自定義一個類,實現BeanPostProcessor介面,然後重寫它的public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { ... } 然後根據使用者名稱進行判斷,當找我們指定的使用者時,我們可以使用JDK的動態代理完成將這個物件轉換成代理物件,進而實現切面的增強

看一下它的處理,它判斷出@Import中含有ImportSelector.class時,就通過反射將這個物件創建出來代理物件,狸貓換太子,把代理物件交給Spring,得到ImportSelector的物件,具體反射出來的物件的例項就是程式元自定義的那個ImportSelector,得到這個物件之後,然後呼叫它的selectImports()方法,就返回了程式設計師指定的想批量匯入DaoIOC中的物件的全類名, 下一步就是將這些類注入到IOC中,Spring的做法是遞迴呼叫, 因為上面說了,當前方法可以實現的三種Bean的注入,一般來說,通過 ImportSelecor匯入的類就是普通類了, 會進入下面程式碼中的最後一個else語句塊

第二種情況,處理ImportBeanDefinitionRegistrar,Spring的做法是,將它新增進一個map中

this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);

第三種情況,同樣和第一種情況是一樣的,也是先將資訊放到map中

this.configurationClasses.put(configClass, configClass);

原始碼如下:

    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

        //   如果沒有添加了@Implot註解的類,直接退出去
        if (importCandidates.isEmpty()) {
            return;
        }

        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
        }
  else {
  this.importStack.push(configClass);
  try {
    for (SourceClass candidate : importCandidates) {
        //   情況1: 處理 @ImportSelector 註解
        if (candidate.isAssignable(ImportSelector.class)) {
            // Candidate class is an ImportSelector -> delegate to it to determine imports
            //   被迴圈獲取出現在Spring自己的以及掃描出來的全部的物件的Class描述
            Class<?> candidateClass = candidate.loadClass();

            //  只要這個物件的@Import(value= ImportSelector.clas)就被命中
            //  反射實現一個物件,反射建立的這個物件就是我們的手動新增的 繼承 ImportSelector 的那個物件
            ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
            ParserStrategyUtils.invokeAwareMethods(
                    selector, this.environment, this.resourceLoader, this.registry);
            if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                this.deferredImportSelectors.add(
                        new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
            } else {
                //  回撥反射出來的這個物件啊的 SelectImports() 方法,就能動態的獲取出我們手動新增進去的,準備批量注入的 物件的 ClassName 陣列
                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                //   將importClassNames新增進一個list -- annotatedClasses中,然後返回出來
                Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                //   遞迴呼叫, processImports()方法,顯然,再次遞迴的話,傳遞進去的importSourceClasses就是當前的類, 如果當前類是普通類,遞迴時就不再來到這裡了, 而是進入下面的else程式碼塊
                processImports(configClass, currentSourceClass, importSourceClasses, false);
            }
        } //    情況2:  處理@ImportBeanDefinitionRegistrar
        else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
            // Candidate class is an ImportBeanDefinitionRegistrar ->
            // delegate to it to register additional bean definitions
            Class<?> candidateClass = candidate.loadClass();
            ImportBeanDefinitionRegistrar registrar =
                    BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
            ParserStrategyUtils.invokeAwareMethods(
                    registrar, this.environment, this.resourceLoader, this.registry);
            //  沒有和上面一樣進行回撥,而是放入到一個list中
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
        }
        else {
            //   情況3: 處理普通類
            // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
            // process it as an @Configuration class
            // 否則,加入到importStack後呼叫 processConfigurationClass 進行處理
            //processConfigurationClass() 方法就在下面,  裡面主要就是把類放到configurationClasses
            //configurationClasses是一個集合,會在後面拿出來解析成bd繼而註冊
            //可以看到普通類在掃描出來的時候就被註冊了
            //如果是importSelector,會先放到configurationClasses後面進行出來註冊
            this.importStack.registerImport(
                    currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
            processConfigurationClass(candidate.asConfigClass(configClass));
        }
    }
}

當處理完成了三種@Import型別的匯入方式之後,我們繼續往下看,三種方式都是忙著把讀取讀來的資訊往map中放,那麼在哪裡進行處理的呢? 思考一下,接下來是不是得將讀取到的資訊註冊進IOC中? 沒錯,我們退會到ConfigurationClassPostProcessor中的this.reader.loadBeanDefinitions(configClasses);方法中

同樣經過幾個沒有重要邏輯的方法之後,我們來到了ConfigurationClassBeanDefinitionReader中,著重它的loadBeanDefinitionsForConfigurationClass()方法, 原始碼我貼在下面:

看看他做了什麼, 一目瞭然,很清晰的思路,很牛逼很牛逼!!!

如果Spring發現,當前的類是被匯入進來的,他按照Bean匯入進來的方式進行註冊Bean,如果進給看一下,就能看熟悉的一幕,Spring使用Register進行Bean的註冊

如果Spring發現它有BeanMethod,也就是發現這個物件存在方法,換句話說發現我們的物件存在方法,就會進一步解析我們的方法,怎麼解析方法呢? 按照物件的方法和類的方法分別解析,這也是為什麼,當我們在配置類的靜態方法中使用@Bean進行注入物件,即使已經為MainConfig生成了代理,依然會出現重複注入物件的情況,但是BeanName不一樣哦,如果是靜態方法+@Bean, BeanName是當前的方法名

接下來的邏輯是 解析XML與處理Registrar

    private void loadBeanDefinitionsForConfigurationClass(
            ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

        //   如果一個類是被  Import  進來的, 會在Spring進行標記,然後再這裡完成註冊
        //   @Import(aaa.class) 那這個aaa就是被Import的,在這裡完成註冊
        if (configClass.isImported()) {
            registerBeanDefinitionForImportedConfigurationClass(configClass);
        }
    
        for (BeanMethod beanMethod : configClass.getBeanMethods()) {
            loadBeanDefinitionsForBeanMethod(beanMethod);
        }

         //  xml
        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

        //    處理註冊Registrar 的邏輯
        loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }

程式碼讀到這裡其實已經深入很多層了,重要的邏輯也都過了一下,現在的工作就是層層的往回出棧,回到開始的PostProcessorRegistationDelegate中的invokeBeanFactoryPostProcessors()方法

上面的大部分篇幅都是當前方法中的 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);方法層層遞迴進去的,現在我們從它開始往後看

剛開始不是說,一共是建立三個list嗎?然後又把其中的兩個進行了合併了, 那麼接下來的工作就是去執行這兩個list中剩下的沒執行的邏輯, 沒執行的就是,Spring自己提供的和程式設計師新增的BeanFactoryPostProcessor的實現,沒錯就是執行重寫的BeanFactoryPostProcessor()postProcessBeanFactory()方法

//  registryProcessors  其實就是唯一的 ConfigurationClassPostProcessor
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);

//  自定義BeanFactoryPostProcessor   的 postProcessorBeanFactory()方法
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);

大家看,上面的兩個方法是一樣,只不過是傳遞進去的引數不一樣而已,其實吧,高潮來了,如果大家還記得的話,應該猜到了現在的入參位置上的引數, 沒錯就是 ConfigurationClassPostProcessor這個類, 它太牛逼了! 同時實現了BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的抽象方法, 下面就去具體看一下它的實現,準備好了嗎? 來高潮了哦

原始碼如下: 它的解析我寫在下面

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    int factoryId = System.identityHashCode(beanFactory);
    if (this.factoriesPostProcessed.contains(factoryId)) {
        throw new IllegalStateException(
                "postProcessBeanFactory already called on this post-processor against " + beanFactory);
    }
    this.factoriesPostProcessed.add(factoryId);
    if (!this.registriesPostProcessed.contains(factoryId)) {
        // BeanDefinitionRegistryPostProcessor hook apparently not supported...
        // Simply call processConfigurationClasses lazily at this point then.
        processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
    }

    enhanceConfigurationClasses(beanFactory);

    beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

enhanceConfigurationClasses(beanFactory); 這是在幹什麼? 跟大家說,這太牛了!!!為什麼說它牛? 不買關子,它在這裡開啟了JDK的動態代理

在這個方法中有一段判斷邏輯,如下: 這是很讚的一段程式碼,感覺到了心跳的加速! 它判斷當前的這個BeanDefinition是不是full型別的, 關於這個Full的解釋,其實我們上面的描述中有說過,就是說,如果我們的MainConfig添加了@Configuration註解,它就被會標記為FUll, 被標記為full的話,就會在下面的程式碼中產生cglib的動態代理,也就是說,我們獲取到的存入容器的MainConfig可以不是普通的原始物件, 而是被Cglib增強後的物件, 這有什麼用呢? 用處可大了! 我們通常會在配置類中新增@Bean註解,注入物件,但是如果被添加了@Bean註解的方法彼此之間相互呼叫的戶,就會出現重複注入的現象,Spring通過下面的判斷,進行代理,不再給使用者原始的Mainconfig,這樣就實現對方法呼叫的控制,進而保證了百分百單例的情況

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
    BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
    //  判斷isFull, 看看是不是添加了@Configuration的全註解的類
    if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
        if (!(beanDef instanceof AbstractBeanDefinition)) {
            throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
                    beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
        }
        else if (logger.isWarnEnabled() && beanFactory.containsSingleton(beanName)) {
            logger.warn("Cannot enhance @Configuration bean definition '" + beanName +
                    "' since its singleton instance has been created too early. The typical cause " +
                    "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
                    "return type: Consider declaring such methods as 'static'.");
        }
        //   如果是的話,放到這個linkedHashMap中
        configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
    }
}

接著往下看程式碼就可以看到Spring底層使用原生cglib進行代理的邏輯了,

    private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(configSuperClass);
        enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
        enhancer.setUseFactory(false);
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
        enhancer.setCallbackFilter(CALLBACK_FILTER);
        enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
        return enhancer;
    }

這個過程中有幾個需要注意的地方,一般我們自己實現Cglib時, 都只是設定一個setSuperclass(XXX)然後對這個XXX進行增強,但是Spring沒這麼簡單,它還enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class}); 想想,為什麼還要這個介面呢?看下面的圖

通過上面的圖可以看到,這個介面實現了beanFactoryAware,而這個beanFactory中存在setBeanFactory(BeanFactory bf) 怎麼樣? 有思路沒?

整理一下思路,就是說,Spring的目的就是將程式設計師傳遞進來的MainConfig進行動態代理,為啥要代理呢? 因為有的程式設計師會故意搞破壞,會使用被@Bean標註的方法之間相互呼叫,導致Bean的多次注入,於是Spring想通過代理,返回給使用者一個代理物件,然後新增動態的判斷, 如果容器中已經存在bean了,就從容器中獲取,不再重複注入,沒有的話就注入進去一個

這就引出了為什麼,代理物件需要一個BeanFactory,因為BeanDefinition都在BeanFactory中,這也是為什麼上面需要setInterface()

接著enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader)); 設定一個生成策略, 因為我們生曾的代理類需要一個BeanFactory型別的變數啊,沒有這個引用,如何接受前面set的BeanFactory???

再往後的亮點就是enhancer.setCallbackFilter(CALLBACK_FILTER);設定回撥的攔截器,看看有哪些攔截器呢? 程式碼如下:

private static final Callback[] CALLBACKS = new Callback[] {
        //   第一個實現, 增強方法, 主要控制bean的作用域換句話說就是讓每一次呼叫方法不再去new,  跟進去看看
        new BeanMethodInterceptor(), //   他是當前類的內部類
        //設定一個beanFactory
        new BeanFactoryAwareMethodInterceptor(),
        NoOp.INSTANCE
};

我們跟進去BeanMethodInterceptor,這個類也很精彩,玩過cglib的人都知道需要一個inteceptor,而我們正在看的這個介面就實現了methodInterceptor,重不重要,你懂的...

直接看它的intercept()方法, 細細品味這個方法,很有味道哦!!!, 它的解析我寫在這個方法的下面

public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                    MethodProxy cglibMethodProxy) throws Throwable {

    //    enhancedConfigInstance 是代理物件
    //   通過代理物件enhancedConfigInstance中cglib生成的成員變數$$beanFactory獲得beanFactory。
    ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);

    String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

    // Determine whether this bean is a scoped-proxy
    Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
    if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
        String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
        if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
            beanName = scopedBeanName;
        }
    }

    if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
            factoryContainsBean(beanFactory, beanName)) {

        Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);

        if (factoryBean instanceof ScopedProxyFactoryBean) {
            // Scoped proxy factory beans are a special case and should not be further proxied
        }
        else {
            return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
        }
    }

    if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
        // The factory is calling the bean method in order to instantiate and register the bean
        // (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
        // create the bean instance.
        if (logger.isWarnEnabled() &&
                BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
            logger.warn(String.format("@Bean method %s.%s is non-static and returns an object " +
                            "assignable to Spring's BeanFactoryPostProcessor interface. This will " +
                            "result in a failure to process annotations such as @Autowired, " +
                            "@Resource and @PostConstruct within the method's declaring " +
                            "@Configuration class. Add the 'static' modifier to this method to avoid " +
                            "these container lifecycle issues; see @Bean javadoc for complete details.",
                    beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
        }
        //   滿足條件 呼叫父類的構造方法new 物件
        return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
    }

    return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

首先,它從代理物件中獲取出beanFactory

然後他處理 FactoryBean的情況,這個FactoryBean太牛了,因為當程式設計師把一個FactoryBean注入到IOC時,附帶的還會把另一個物件駐入進IOC, 它是如何進行區分判斷的呢? Spring會使用一個BeanFactory.FACTORY_BEAN_PREFIX == & 這個字首去匹配, 比如userDao3()中呼叫了userDao4(), 他就是用&userDao4當成key去beanFactory中獲取,如果獲取獲取出物件了,說明這是個FactoryBean 需要對獲取出來的這個物件進一步生成代理

接下來判斷,是new 呢? 還是從Factory中獲取呢?

Spring的判斷依據是根據方法名,判斷呼叫方法和正在執行的方法是同一個方法,根據什麼呢? 只要名字相同, 結論就是直接new

舉個例子:

userDao3(){}
// 它的呼叫方法和正在執行的方法是同一個方法,怎麼相同呢? 名字相同, 結論就是直接new

userDao4(){
   userDao3()
}
userDao4()是呼叫方法, userDao3()執行方法  userDao4 和 userDao3 名字不一樣, 所以選擇getBean()

第二個例子:

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    System.out.println("intercept.............");
    methodProxy.invokeSuper(o,objects);
    return null;
}

比如我們僅僅執行代理方法A, 這個A方法 就時上面的method 也是methodProxy, 但是如果我們在代理方法A中執行B方法, 這時 A == method != methodProxy == B

程式碼看到這裡,其實一開始的refresh()中的invokeBeanFactoryPostProcessors(beanFactory);方法就看完了, 著呢麼樣刺激不?

有錯誤的話歡迎批評指出,有過對您有幫助,歡迎點贊支