spring源码案例分析之bean初始化优先级顺序

前言

本来这篇文章去年就应该发了,但是有些事情被耽搁了,最近整理了下发了出来,关于spring源码相关有想沟通的小伙伴可以随时联系我。
本篇文章内容有点臃肿,可以选择性跳跃去看。干货还是有点的,尤其总结部分。
本篇承接上篇末尾之讨论spring源码案例分析之健康检查,有兴趣的读者可以去看看,说不定有对你帮助的惊喜。
本篇开始之前,我们先看下我项目总体的结构,如下图



    4.0.0

    com.xxq
    spring-source-analysis
    1.0-SNAPSHOT

    
        org.springframework.boot
        spring-boot-starter-parent
        1.5.13.RELEASE
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        
    

pom文件很简单,引入父工程spring-boot-starter-parent,因为这次我们分析借助于web切入点,因此我们只简单引入一个starter-web就够了,其他没什么可说的,再看下代码结构:

项目工程图

很简单,一个springboot启动类,我们看下面一段代码:

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

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

        private final ObjectProvider messageConverters;

        private final ObjectProvider> restTemplateCustomizers;

        public RestTemplateConfiguration(
                ObjectProvider messageConverters,
                ObjectProvider> 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 customizers = this.restTemplateCustomizers
                    .getIfAvailable();
            if (!CollectionUtils.isEmpty(customizers)) {
                customizers = new ArrayList(customizers);
                AnnotationAwareOrderComparator.sort(customizers);
                builder = builder.customizers(customizers);
            }
            return builder;
        }

    }

}

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

这段代码spring自动装配了一个RestTemplateBuilder,我们注意到一个注解,@ConditionalOnMissingBean,这里我大致说下作用,不做深入研究,以后有机会我会单独写篇文章去分析这个注解,这个注解的大致作用就是如果容器中没有这个RestTemplateBuilder,那么这个@Bean的配置就生效,简单来说,就是没有这个bean,spring就会默认生成一个,有的话就不生成,这个注解是spring自动装配的一个核心点。

我们看下面这段代码:

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

}

这段代码,注入了一个RestTemplateBuilder,那么问题来了,如果我也写了这样一个注入方法,那么spring用的RestTemplateBuilder到底是我们的还是spring自己的,有的人说是我们自己的,有的人说是spring默认的,那么,原因呢?下面我们来看下到底使用的是哪个。

自定义的RestTemplateBuilder

获取容器中的bean

获取容器中的bean

可以看到容器中的bean的内存地址和自定义的内存地址是一致的,所以可以确定的是,spring确实初始化的是我们定义的的,我们稍后我具体分析这个流程。

在正式开始分析之前,我们先区分下俩个注解,这对我们下面的分析有点铺垫,@Order和@Primary。

  1. @Order一般用于拦截器和过滤器,是控制同类型bean的执行顺序,一个容器可以有多个filter和interceptor,每个承担不同的角色,但是可能有的先执行,有的要后执行,这时候@Order就排上用场了,在ssm架构体系下,如果想要控制过滤器的执行顺序,是通过在web.xml中配置过滤器的顺序来实现的,如今使用springboot方式,已经没有了web.xml,所以spring就创造了这种方式,不过现在已经不仅仅局限于过滤器和拦截器,包括spring command以及其他使用场景。
  2. @Primary,是在bean注入的时候起作用的。如果一个容器中,有多个类型一样bean,不使用相关注解去处理,那么容器初始化的时候并不会报错,但是在注入的时候就会报错,这是由于spring并不知道你想要使用的是哪个。我们看下面的代码。
@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;
    }

}

上面这段代码,在容器中配置了俩个RestTemplateBuilder类型的bean,不过他们的beanName一个是restTemplateBuilder,一个是restTemplateBuilder1。这样直接去启动工程是不会报错的。


image.png
image.png

我们可以看到容器中有俩个restTemplateBuilder,这时候正常启动,当我加入下面这段代码:

    @Autowired
    RestTemplateBuilder templateBuilder;

spring就会报出如下错误:

***************************
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

异常大致翻译:找到了俩个bean,无法确定使用哪个,建议我们可以使用@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。
举这么个例子是想告诉大家,@Primary不会改变spring容器中的bean的初始化优先级,只会改变注入时候的优先级,这里牵扯到spring容器的概念,springbean容器虽然是一个map,但是不是普通意义上的map,只要你的beanName不一样,那么都会放入到这个map中,在同一个config中的@Bean模式下,如果你定义了俩个beanName一样的bean,无论类型,只会初始化一个到容器中,至于是哪个,spring会以在appconfig类中最先读到的那个为准进行覆盖。如果是在不同的config中配置,name一致就算类型不一致,会使用最后读取到的。这一块内容也还是比较复杂,场景略微有点多,且处理的方式也都不一样,后续有时间再解析。

这时候就有小伙伴问了(其实是我自己),既然上面你说spring先读到那个就初始化哪个这种情况,为什么就不可能先读到spring框架默认的bean,然后初始化呢,?接下来,我们开始分析。

1. 首先第一步,我们分析springboot启动类的加载

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

public static ConfigurableApplicationContext run(Object source, String... args) {
    // 重点部分,这里把启动类作为class类型传递给source变量,后续会用到这个。
    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);
        // 重点代码,这里会进行容器的初始化工作
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        //重点代码,后续会分析到
        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启动类的核心代码部份,我们这次只关心关于我们研究的部分:

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 sources = getSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // 重点方法,这里会进行启动类的加载
        load(context, sources.toArray(new Object[sources.size()]));
        listeners.contextLoaded(context);
    }
 
 

看上面的倒数第二行代码,这边把sources作为一个对象数组传递进去了,这个sources是来自于getSources方法,这个最终还是我上面提到的来源于启动类的传参。

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)) {
        //重点部份,此处会把启动类进行加载进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.

    // 一般都会走这个分支,因为springboot启动类似加上这个@SpringBootApplication注解
    // 就算不走这个分支,下面的分支也绝大数多情况不会走,最终默认还是返回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方法最终会调用annotatedReader.register(source)方法,

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

public void registerBean(Class annotatedClass, String name, Class... 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 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);
    // 此处会将启动类注册进容器的beanDefinitionMap中
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

上面最后一行方法,就是将启动类加载进容器中,这里仅仅是beanDefinition的加载,还没有到真正初始化的阶段。

2. 首先第一步,我们简单分析spring容器的生命周期

refreshContext(context);

上面有提到过这个方法很重要,我们现在来分析。

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

            // 重点代码,后置处理器的调用
            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();
        }
    }
}

我相信对spring如果有稍微了解的话,对于上述核心代码应该都比较熟悉,上述代码算得上是spring最最最最核心的代码了,以后有时间我们全部都分析一边,这次我们重点分析这段代码

invokeBeanFactoryPostProcessors(beanFactory);

分析spring源码,要记住最关键的一点是,spring永远都是分俩步走的:
1. 将所有要初始化的bean信息放入到beanDefinitionMap中。
2. 遍历扫描上面的map,进行bean的实例化以及初始化操作。

spring容器的构建几乎所有的操作都是围绕上述俩步来处理的,大家可以想想为什么要这么去做,我直接生成一个beanDefinition初始化一个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()));
        }
    }

第一行代码是最重要的,看方法名就知道这里可能会调用后置处理器。

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

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

        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            List regularPostProcessors = new LinkedList();
            List registryProcessors = new LinkedList();

            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 currentRegistryProcessors = new ArrayList();

            // 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 priorityOrderedPostProcessors = new ArrayList();
        List orderedPostProcessorNames = new ArrayList();
        List nonOrderedPostProcessorNames = new ArrayList();
        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 orderedPostProcessors = new ArrayList();
        for (String postProcessorName : orderedPostProcessorNames) {
            orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }
        sortPostProcessors(orderedPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

        // Finally, invoke all other BeanFactoryPostProcessors.
        List nonOrderedPostProcessors = new ArrayList();
        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();
    }

这一段代码是非常的长,而且也非常的复杂,没有认真分析过这段的,这里一时半会也无法说清,总而言之,这段代码就是spring进行分类整合,把所有的后置处理器进行区分成以下几种

  • 扫描BeanDefinitionRegistry的所有bean
    1. 使用BeanDefinitionRegistryPostProcessor应用于对BeanDefinitionRegistry对bean的信息进行调整和解析
  • 调用所有后置处理器(BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor)
    1. 应用于BeanDefinitionRegistry的后置处理器
    2. 应用于BeanFactory的后置处理器
    3. 以及PriorityOrdered, Ordered排序执行的后置处理器

在我看来这俩步最大的区别就是,第一步是把所有外部的类加载解析到spring容器中,在第一步执行前,spring容器中几乎没有什么bean,除了手动放进去的,第二步处理的就是第一步处理完的spring容器。也就是说第一步是基础,第二步是升华。因此对于本文的探究点,显而易见是第一步的解析,我们的bean为什么能优先于spring默认的,很大概率是在第一步已经处理好的。

image.png

上述截图,是容器第一次执行后置处理器的地方
以下分析 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

image.png

可以看到除了第六个是我们程序自定义的,其他都是spring默认的,也就是说,到这一步容器就这7个bean。然后看263行代码,这边有个判断,我们看下具体作用是什么。


image.png

image.png
image.png

这边有个spring得fullConfiguration以及liteConfigurationClass一说,一般来说一个springbean要么是full或者lite,经过上面的过滤后,只剩下我们程序得启动类符合了,其中具体逻辑可以自行去查看。

image.png

上图第388最终调用到下面方法块
image.png

第一部分 主要解析当前类得内部类
第二部分 主要解析一些propertySource
第三部分 以当前类为基类扫描包下面得所有类
首先他会判断我们启动类上有没有componentScan注解,有的话进行获取参数。
image.png

我们可以看到springbootApplication注解默认包含了ComponentScan注解,因此是可以解析到的。
image.png

在第289行,对这个componentScan进行扫描,如果这个注解value为null,那么就直接获取当前类得所在包名进行扫描,而我们所定义的俩个restTemplateBuild就是在当前启动类的包名下,所以理所当然能够被扫描到。

根据上面的大致分析,可以得到,spring为何优先扫描我们程序自定义的类,是因为他会先默认扫描启动类上componentScan注解得包名,如果包名未被设置,那么默认使用启动得包名进行扫描,这就是为什么很多博客或者新手使用springboot时候,大家都说把启动类放在最外层得原因所在
至于spring自身定义的bean为何没有被初始化,是因为主要原因如下:

image.png

image.png

看到没,先解析启动类componentScan的元数据,再去解析启动类的上的import的注解元数据,正是因为这个先后顺序,等去解析自动装配也就是一开始spring内置bean的那里的时候,就会发现内部已经有了当前名为restTemplateBuilder的bean,就会跳过当前内置bean的解析流程。

得到了上面的结果,那么我们可以对代码进行如下改造:


image.png

这时候就会发现spring容器中就只有一个restTemplateBuilder且是spring自身的。


image.png

到此为止,我们可以得出一个非常有意思的结论,当然也算是spring的规范,我们在启动类上加了componentScan注解后,整个springboot只会扫描我们加的这个注解指定的包名,在也不是默认当前启动类的默认包名,因此在启动类上加这个注解一定要尤其小心,否则就会放一些不易发现的错误。
至于为什么,或者原理是什么,可以细看下如下代码:

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

写在文后:

关于下篇文章,我暂时还没想好写哪些,其实有时候底层知识很重要,有时候框架知识也很重要,框架一般工作中用的最多,底层考验的是面试和基本功。后续可能会陆续出一些底层相关的,spring源码相关的网上有太多了,我写的话也只会写我比较关注的,或者大家有什么想了解的我也可以去写一下。

最后请大家帮忙三连,写作不易,还请谅解!!!!!

你可能感兴趣的:(spring源码案例分析之bean初始化优先级顺序)