spring源碼案例分析之bean初始化優(yōu)先級(jí)順序

前言

本來(lái)這篇文章去年就應(yīng)該發(fā)了,但是有些事情被耽擱了,最近整理了下發(fā)了出來(lái),關(guān)于spring源碼相關(guān)有想溝通的小伙伴可以隨時(shí)聯(lián)系我。
本篇文章內(nèi)容有點(diǎn)臃腫,可以選擇性跳躍去看。干貨還是有點(diǎn)的,尤其總結(jié)部分。
本篇承接上篇末尾之討論spring源碼案例分析之健康檢查,有興趣的讀者可以去看看,說(shuō)不定有對(duì)你幫助的驚喜。
本篇開始之前,我們先看下我項(xiàng)目總體的結(jié)構(gòu),如下圖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xxq</groupId>
    <artifactId>spring-source-analysis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

pom文件很簡(jiǎn)單,引入父工程spring-boot-starter-parent,因?yàn)檫@次我們分析借助于web切入點(diǎn),因此我們只簡(jiǎn)單引入一個(gè)starter-web就夠了,其他沒什么可說(shuō)的,再看下代碼結(jié)構(gòu):

項(xiàng)目工程圖

很簡(jiǎn)單,一個(gè)springboot啟動(dòng)類,我們看下面一段代碼:

@Configuration
@AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class)
public class WebClientAutoConfiguration {

    @Configuration
    @ConditionalOnClass(RestTemplate.class)
    public static class RestTemplateConfiguration {

        private final ObjectProvider<HttpMessageConverters> messageConverters;

        private final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers;

        public RestTemplateConfiguration(
                ObjectProvider<HttpMessageConverters> messageConverters,
                ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
            this.messageConverters = messageConverters;
            this.restTemplateCustomizers = restTemplateCustomizers;
        }

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateBuilder restTemplateBuilder() {
            RestTemplateBuilder builder = new RestTemplateBuilder();
            HttpMessageConverters converters = this.messageConverters.getIfUnique();
            if (converters != null) {
                builder = builder.messageConverters(converters.getConverters());
            }
            List<RestTemplateCustomizer> customizers = this.restTemplateCustomizers
                    .getIfAvailable();
            if (!CollectionUtils.isEmpty(customizers)) {
                customizers = new ArrayList<RestTemplateCustomizer>(customizers);
                AnnotationAwareOrderComparator.sort(customizers);
                builder = builder.customizers(customizers);
            }
            return builder;
        }

    }

}

org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration.RestTemplateConfiguration#restTemplateBuilder

這段代碼spring自動(dòng)裝配了一個(gè)RestTemplateBuilder,我們注意到一個(gè)注解,@ConditionalOnMissingBean,這里我大致說(shuō)下作用,不做深入研究,以后有機(jī)會(huì)我會(huì)單獨(dú)寫篇文章去分析這個(gè)注解,這個(gè)注解的大致作用就是如果容器中沒有這個(gè)RestTemplateBuilder,那么這個(gè)@Bean的配置就生效,簡(jiǎn)單來(lái)說(shuō),就是沒有這個(gè)bean,spring就會(huì)默認(rèn)生成一個(gè),有的話就不生成,這個(gè)注解是spring自動(dòng)裝配的一個(gè)核心點(diǎn)。

我們看下面這段代碼:

@Configuration
public class Appconfig {
    @Bean
    public RestTemplateBuilder restTemplateBuilder() {
        return new RestTemplateBuilder();
    }

}

這段代碼,注入了一個(gè)RestTemplateBuilder,那么問(wèn)題來(lái)了,如果我也寫了這樣一個(gè)注入方法,那么spring用的RestTemplateBuilder到底是我們的還是spring自己的,有的人說(shuō)是我們自己的,有的人說(shuō)是spring默認(rèn)的,那么,原因呢?下面我們來(lái)看下到底使用的是哪個(gè)。

自定義的RestTemplateBuilder

獲取容器中的bean

獲取容器中的bean

可以看到容器中的bean的內(nèi)存地址和自定義的內(nèi)存地址是一致的,所以可以確定的是,spring確實(shí)初始化的是我們定義的的,我們稍后我具體分析這個(gè)流程。

在正式開始分析之前,我們先區(qū)分下倆個(gè)注解,這對(duì)我們下面的分析有點(diǎn)鋪墊,@Order和@Primary。

  1. @Order一般用于攔截器和過(guò)濾器,是控制同類型bean的執(zhí)行順序,一個(gè)容器可以有多個(gè)filter和interceptor,每個(gè)承擔(dān)不同的角色,但是可能有的先執(zhí)行,有的要后執(zhí)行,這時(shí)候@Order就排上用場(chǎng)了,在ssm架構(gòu)體系下,如果想要控制過(guò)濾器的執(zhí)行順序,是通過(guò)在web.xml中配置過(guò)濾器的順序來(lái)實(shí)現(xiàn)的,如今使用springboot方式,已經(jīng)沒有了web.xml,所以spring就創(chuàng)造了這種方式,不過(guò)現(xiàn)在已經(jīng)不僅僅局限于過(guò)濾器和攔截器,包括spring command以及其他使用場(chǎng)景。
  2. @Primary,是在bean注入的時(shí)候起作用的。如果一個(gè)容器中,有多個(gè)類型一樣bean,不使用相關(guān)注解去處理,那么容器初始化的時(shí)候并不會(huì)報(bào)錯(cuò),但是在注入的時(shí)候就會(huì)報(bào)錯(cuò),這是由于spring并不知道你想要使用的是哪個(gè)。我們看下面的代碼。
@Configuration
public class Appconfig {
    @Bean
    public RestTemplateBuilder restTemplateBuilder() {
        RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
        System.out.println(restTemplateBuilder.hashCode());
        return restTemplateBuilder;
    }

    @Bean
    public RestTemplateBuilder restTemplateBuilder1() {
        RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
        System.out.println(restTemplateBuilder.hashCode());
        return restTemplateBuilder;
    }

}

上面這段代碼,在容器中配置了倆個(gè)RestTemplateBuilder類型的bean,不過(guò)他們的beanName一個(gè)是restTemplateBuilder,一個(gè)是restTemplateBuilder1。這樣直接去啟動(dòng)工程是不會(huì)報(bào)錯(cuò)的。


image.png
image.png

我們可以看到容器中有倆個(gè)restTemplateBuilder,這時(shí)候正常啟動(dòng),當(dāng)我加入下面這段代碼:

    @Autowired
    RestTemplateBuilder templateBuilder;

spring就會(huì)報(bào)出如下錯(cuò)誤:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field templateBuilder in com.xxq.web.MyApplicationContextAwaer required a single bean, but 2 were found:
    - restTemplateBuilder: defined by method 'restTemplateBuilder' in class path resource [com/xxq/appconfig/Appconfig.class]
    - restTemplateBuilder1: defined by method 'restTemplateBuilder1' in class path resource [com/xxq/appconfig/Appconfig.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

異常大致翻譯:找到了倆個(gè)bean,無(wú)法確定使用哪個(gè),建議我們可以使用@Primary或者@Qualifier。于是我試著加入@Primary

    @Bean
    @Primary
    public RestTemplateBuilder restTemplateBuilder() {
        RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
        System.out.println(restTemplateBuilder.hashCode());
        return restTemplateBuilder;
    }
image.png
image.png

可以看到,spring注入的就是我們加上@Primary注解的bean。
舉這么個(gè)例子是想告訴大家,@Primary不會(huì)改變spring容器中的bean的初始化優(yōu)先級(jí),只會(huì)改變注入時(shí)候的優(yōu)先級(jí),這里牽扯到spring容器的概念,springbean容器雖然是一個(gè)map,但是不是普通意義上的map,只要你的beanName不一樣,那么都會(huì)放入到這個(gè)map中,在同一個(gè)config中的@Bean模式下,如果你定義了倆個(gè)beanName一樣的bean,無(wú)論類型,只會(huì)初始化一個(gè)到容器中,至于是哪個(gè),spring會(huì)以在appconfig類中最先讀到的那個(gè)為準(zhǔn)進(jìn)行覆蓋。如果是在不同的config中配置,name一致就算類型不一致,會(huì)使用最后讀取到的。這一塊內(nèi)容也還是比較復(fù)雜,場(chǎng)景略微有點(diǎn)多,且處理的方式也都不一樣,后續(xù)有時(shí)間再解析。

這時(shí)候就有小伙伴問(wèn)了(其實(shí)是我自己),既然上面你說(shuō)spring先讀到那個(gè)就初始化哪個(gè)這種情況,為什么就不可能先讀到spring框架默認(rèn)的bean,然后初始化呢,?接下來(lái),我們開始分析。

1. 首先第一步,我們分析springboot啟動(dòng)類的加載

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

public static ConfigurableApplicationContext run(Object source, String... args) {
    // 重點(diǎn)部分,這里把啟動(dòng)類作為class類型傳遞給source變量,后續(xù)會(huì)用到這個(gè)。
    return run(new Object[] { source }, args);
}

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        analyzers = new FailureAnalyzers(context);
        // 重點(diǎn)代碼,這里會(huì)進(jìn)行容器的初始化工作
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        //重點(diǎn)代碼,后續(xù)會(huì)分析到
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        listeners.finished(context, null);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        return context;
    }
    catch (Throwable ex) {
        handleRunFailure(context, listeners, analyzers, ex);
        throw new IllegalStateException(ex);
    }
}

上面代碼是spring啟動(dòng)類的核心代碼部份,我們這次只關(guān)心關(guān)于我們研究的部分:

private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }

        // Add boot specific singleton beans
        context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }

        // Load the sources
        Set<Object> sources = getSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // 重點(diǎn)方法,這里會(huì)進(jìn)行啟動(dòng)類的加載
        load(context, sources.toArray(new Object[sources.size()]));
        listeners.contextLoaded(context);
    }

看上面的倒數(shù)第二行代碼,這邊把sources作為一個(gè)對(duì)象數(shù)組傳遞進(jìn)去了,這個(gè)sources是來(lái)自于getSources方法,這個(gè)最終還是我上面提到的來(lái)源于啟動(dòng)類的傳參。

protected void load(ApplicationContext context, Object[] sources) {
    if (logger.isDebugEnabled()) {
        logger.debug(
                "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
    BeanDefinitionLoader loader = createBeanDefinitionLoader(
            getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    loader.load();
}

private int load(Class<?> source) {
    if (isGroovyPresent()) {
        // Any GroovyLoaders added in beans{} DSL can contribute beans here
        if (GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
            GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
                    GroovyBeanDefinitionSource.class);
            load(loader);
        }
    }
    if (isComponent(source)) {
        //重點(diǎn)部份,此處會(huì)把啟動(dòng)類進(jìn)行加載進(jìn)spring容器
        this.annotatedReader.register(source);
        return 1;
    }
    return 0;
}

private boolean isComponent(Class<?> type) {
    // This has to be a bit of a guess. The only way to be sure that this type is
    // eligible is to make a bean definition out of it and try to instantiate it.

    // 一般都會(huì)走這個(gè)分支,因?yàn)閟pringboot啟動(dòng)類似加上這個(gè)@SpringBootApplication注解
    // 就算不走這個(gè)分支,下面的分支也絕大數(shù)多情況不會(huì)走,最終默認(rèn)還是返回true
    if (AnnotationUtils.findAnnotation(type, Component.class) != null) {
        return true;
    }
    // Nested anonymous classes are not eligible for registration, nor are groovy
    // closures
    if (type.getName().matches(".*\\$_.*closure.*") || type.isAnonymousClass()
            || type.getConstructors() == null || type.getConstructors().length == 0) {
        return false;
    }
    return true;
}

上面的load方法最終會(huì)調(diào)用annotatedReader.register(source)方法,

public void register(Class<?>... annotatedClasses) {
    for (Class<?> annotatedClass : annotatedClasses) {
        registerBean(annotatedClass);
    }
}

public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
        return;
    }

    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    if (qualifiers != null) {
        for (Class<? extends Annotation> qualifier : qualifiers) {
            if (Primary.class == qualifier) {
                abd.setPrimary(true);
            }
            else if (Lazy.class == qualifier) {
                abd.setLazyInit(true);
            }
            else {
                abd.addQualifier(new AutowireCandidateQualifier(qualifier));
            }
        }
    }

    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // 此處會(huì)將啟動(dòng)類注冊(cè)進(jìn)容器的beanDefinitionMap中
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

上面最后一行方法,就是將啟動(dòng)類加載進(jìn)容器中,這里僅僅是beanDefinition的加載,還沒有到真正初始化的階段。

2. 首先第一步,我們簡(jiǎn)單分析spring容器的生命周期

refreshContext(context);

上面有提到過(guò)這個(gè)方法很重要,我們現(xiàn)在來(lái)分析。

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        try {
            postProcessBeanFactory(beanFactory);

            // 重點(diǎn)代碼,后置處理器的調(diào)用
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            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();
        }
        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
            destroyBeans();

            cancelRefresh(ex);

            throw ex;
        }
        finally {
            resetCommonCaches();
        }
    }
}

我相信對(duì)spring如果有稍微了解的話,對(duì)于上述核心代碼應(yīng)該都比較熟悉,上述代碼算得上是spring最最最最核心的代碼了,以后有時(shí)間我們?nèi)慷挤治鲆贿?,這次我們重點(diǎn)分析這段代碼

invokeBeanFactoryPostProcessors(beanFactory);

分析spring源碼,要記住最關(guān)鍵的一點(diǎn)是,spring永遠(yuǎn)都是分倆步走的:
1. 將所有要初始化的bean信息放入到beanDefinitionMap中。
2. 遍歷掃描上面的map,進(jìn)行bean的實(shí)例化以及初始化操作。

spring容器的構(gòu)建幾乎所有的操作都是圍繞上述倆步來(lái)處理的,大家可以想想為什么要這么去做,我直接生成一個(gè)beanDefinition初始化一個(gè)bean難道不行嗎?

    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()));
        }
    }

第一行代碼是最重要的,看方法名就知道這里可能會(huì)調(diào)用后置處理器。

    public static void invokeBeanFactoryPostProcessors(
            ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

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

        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>();
            List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<BeanDefinitionRegistryPostProcessor>();

            for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryProcessor =
                            (BeanDefinitionRegistryPostProcessor) postProcessor;
                    registryProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryProcessors.add(registryProcessor);
                }
                else {
                    regularPostProcessors.add(postProcessor);
                }
            }

            // Do not initialize FactoryBeans here: We need to leave all regular beans
            // uninitialized to let the bean factory post-processors apply to them!
            // Separate between BeanDefinitionRegistryPostProcessors that implement
            // PriorityOrdered, Ordered, and the rest.
            List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();

            // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
            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();

            // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
            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();
            }

            // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
            invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
            invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
        }

        else {
            // Invoke factory processors registered with the context instance.
            invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the bean factory post-processors apply to them!
        String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

        // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
        // Ordered, and the rest.
        List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        List<String> orderedPostProcessorNames = new ArrayList<String>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
        for (String ppName : postProcessorNames) {
            if (processedBeans.contains(ppName)) {
                // skip - already processed in first phase above
            }
            else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
            }
        }

        // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
        sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

        // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
        List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        for (String postProcessorName : orderedPostProcessorNames) {
            orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }
        sortPostProcessors(orderedPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

        // Finally, invoke all other BeanFactoryPostProcessors.
        List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        for (String postProcessorName : nonOrderedPostProcessorNames) {
            nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }
        invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

        // Clear cached merged bean definitions since the post-processors might have
        // modified the original metadata, e.g. replacing placeholders in values...
        beanFactory.clearMetadataCache();
    }

這一段代碼是非常的長(zhǎng),而且也非常的復(fù)雜,沒有認(rèn)真分析過(guò)這段的,這里一時(shí)半會(huì)也無(wú)法說(shuō)清,總而言之,這段代碼就是spring進(jìn)行分類整合,把所有的后置處理器進(jìn)行區(qū)分成以下幾種

  • 掃描BeanDefinitionRegistry的所有bean
    1. 使用BeanDefinitionRegistryPostProcessor應(yīng)用于對(duì)BeanDefinitionRegistry對(duì)bean的信息進(jìn)行調(diào)整和解析
  • 調(diào)用所有后置處理器(BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor)
    1. 應(yīng)用于BeanDefinitionRegistry的后置處理器
    2. 應(yīng)用于BeanFactory的后置處理器
    3. 以及PriorityOrdered, Ordered排序執(zhí)行的后置處理器

在我看來(lái)這倆步最大的區(qū)別就是,第一步是把所有外部的類加載解析到spring容器中,在第一步執(zhí)行前,spring容器中幾乎沒有什么bean,除了手動(dòng)放進(jìn)去的,第二步處理的就是第一步處理完的spring容器。也就是說(shuō)第一步是基礎(chǔ),第二步是升華。因此對(duì)于本文的探究點(diǎn),顯而易見是第一步的解析,我們的bean為什么能優(yōu)先于spring默認(rèn)的,很大概率是在第一步已經(jīng)處理好的。

image.png

上述截圖,是容器第一次執(zhí)行后置處理器的地方
以下分析 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

image.png

可以看到除了第六個(gè)是我們程序自定義的,其他都是spring默認(rèn)的,也就是說(shuō),到這一步容器就這7個(gè)bean。然后看263行代碼,這邊有個(gè)判斷,我們看下具體作用是什么。


image.png

image.png
image.png

這邊有個(gè)spring得fullConfiguration以及l(fā)iteConfigurationClass一說(shuō),一般來(lái)說(shuō)一個(gè)springbean要么是full或者lite,經(jīng)過(guò)上面的過(guò)濾后,只剩下我們程序得啟動(dòng)類符合了,其中具體邏輯可以自行去查看。

image.png

上圖第388最終調(diào)用到下面方法塊
image.png

第一部分 主要解析當(dāng)前類得內(nèi)部類
第二部分 主要解析一些propertySource
第三部分 以當(dāng)前類為基類掃描包下面得所有類
首先他會(huì)判斷我們啟動(dòng)類上有沒有componentScan注解,有的話進(jìn)行獲取參數(shù)。
image.png

我們可以看到springbootApplication注解默認(rèn)包含了ComponentScan注解,因此是可以解析到的。
image.png

在第289行,對(duì)這個(gè)componentScan進(jìn)行掃描,如果這個(gè)注解value為null,那么就直接獲取當(dāng)前類得所在包名進(jìn)行掃描,而我們所定義的倆個(gè)restTemplateBuild就是在當(dāng)前啟動(dòng)類的包名下,所以理所當(dāng)然能夠被掃描到。

根據(jù)上面的大致分析,可以得到,spring為何優(yōu)先掃描我們程序自定義的類,是因?yàn)樗麜?huì)先默認(rèn)掃描啟動(dòng)類上componentScan注解得包名,如果包名未被設(shè)置,那么默認(rèn)使用啟動(dòng)得包名進(jìn)行掃描,這就是為什么很多博客或者新手使用springboot時(shí)候,大家都說(shuō)把啟動(dòng)類放在最外層得原因所在
至于spring自身定義的bean為何沒有被初始化,是因?yàn)橹饕蛉缦拢?br>

image.png

image.png

看到?jīng)],先解析啟動(dòng)類componentScan的元數(shù)據(jù),再去解析啟動(dòng)類的上的import的注解元數(shù)據(jù),正是因?yàn)檫@個(gè)先后順序,等去解析自動(dòng)裝配也就是一開始spring內(nèi)置bean的那里的時(shí)候,就會(huì)發(fā)現(xiàn)內(nèi)部已經(jīng)有了當(dāng)前名為restTemplateBuilder的bean,就會(huì)跳過(guò)當(dāng)前內(nèi)置bean的解析流程。

得到了上面的結(jié)果,那么我們可以對(duì)代碼進(jìn)行如下改造:


image.png

這時(shí)候就會(huì)發(fā)現(xiàn)spring容器中就只有一個(gè)restTemplateBuilder且是spring自身的。


image.png

到此為止,我們可以得出一個(gè)非常有意思的結(jié)論,當(dāng)然也算是spring的規(guī)范,我們?cè)趩?dòng)類上加了componentScan注解后,整個(gè)springboot只會(huì)掃描我們加的這個(gè)注解指定的包名,在也不是默認(rèn)當(dāng)前啟動(dòng)類的默認(rèn)包名,因此在啟動(dòng)類上加這個(gè)注解一定要尤其小心,否則就會(huì)放一些不易發(fā)現(xiàn)的錯(cuò)誤。
至于為什么,或者原理是什么,可以細(xì)看下如下代碼:

// Process any @ComponentScan annotations
        Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
                sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);

寫在文后:

關(guān)于下篇文章,我暫時(shí)還沒想好寫哪些,其實(shí)有時(shí)候底層知識(shí)很重要,有時(shí)候框架知識(shí)也很重要,框架一般工作中用的最多,底層考驗(yàn)的是面試和基本功。后續(xù)可能會(huì)陸續(xù)出一些底層相關(guān)的,spring源碼相關(guān)的網(wǎng)上有太多了,我寫的話也只會(huì)寫我比較關(guān)注的,或者大家有什么想了解的我也可以去寫一下。

最后請(qǐng)大家?guī)兔θB,寫作不易,還請(qǐng)諒解?。。。?!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容