亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

當(dāng)用SpringApplication.run的時(shí)候發(fā)生了什么(一)

 

提問(wèn)

- 服務(wù)啟動(dòng)的時(shí)候,SpringApplication內(nèi)部做了什么?
- 創(chuàng)建上下文的時(shí)候是使用的哪一種ApplicationContext?
- Bean 是在哪個(gè)步驟定義的,BeanDefinition怎么排序的?
- Bean 是在哪個(gè)步驟創(chuàng)建和初始化的?
- 監(jiān)聽(tīng)器有什么作用,都發(fā)布了哪些事件?

本篇文章主要介紹Bean是什么時(shí)候定義的,及Bean是如何創(chuàng)建的。

實(shí)例講解

application.yml

client:
  id: 1
  server: localhost

MyClientAutoConfiguration

@Configuration
@EnableConfigurationProperties(ClientProperties.class)
@ConditionalOnProperty(prefix = "client", name = "enable", havingValue = "true")
public class MyClientAutoConfiguration {
?
    private ClientProperties properties;
    @Autowired
    public MyClientAutoConfiguration(ClientProperties properties) {
        this.properties = properties;
    }
?
    @Bean
    public MyClient client1() {
        return new MyClient(1);
    }
?
?
    @Configuration
    @ConditionalOnProperty(name = "client.valid", havingValue = "true", matchIfMissing = true)
    static class ClientConfiger {
?
        @Bean
        public MyClient client2() {
            return new MyClient(2);
        }
?
        @Configuration
        static class MyClientConfiger {
            private final MyClient client;
?
            @Autowired
            public MyClientConfiger(MyClient client) {
                this.client = client;
            }
        }
    }
}

ConditionalBootstrap

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

當(dāng)服務(wù)啟動(dòng)的時(shí)候,大家可以先猜測(cè)一下,哪些Bean會(huì)被注冊(cè)(registerBeanDefinition)?

結(jié)合源碼講解

讓我們直接達(dá)到 SpringApplication.run 中的 refreshContext 方法,看看里面做了什么?

@Override
    public void refresh() throws BeansException, IllegalStateException {
            ...
?
            try {
                ...
?
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);
?
                ...
?
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
?
            }

從源碼中我們看看這兩個(gè)方法主要是完成什么工作的。

invokeBeanFactoryPostProcessors

Instantiate and invoke all registered BeanFactoryPostProcessor beans, respecting explicit order if given. Must be called before singleton instantiation

這個(gè)方法主要完成BeanDefinition的工作

- 哪些Class 實(shí)現(xiàn)了 registerBeanDefinition?
- BeanDefinitions 的順序是什么樣的?

提示:到refreshContext這一步之前,有些內(nèi)置的類(lèi)和 primarySource(即 ConditionalBootStrap)已經(jīng)放到了BeanDefinitionMap中

哪些Class 實(shí)現(xiàn)了 registerBeanDefinition?

  1. 首先會(huì)將之前的BeanDefinitionNames 循環(huán),然后找出 @Configuration Class (isFullConfigurationClass || isLiteConfigurationClass)
當(dāng)用SpringApplication.run的時(shí)候發(fā)生了什么(一)

 

  1. 解析每一個(gè) @Configuration 類(lèi)

通過(guò)ConfigurationClassParser#parse 解析上面的configCandidates:

  • 首先判斷這個(gè)類(lèi)是否有效,是否應(yīng)該忽略,這個(gè)地方有個(gè)很重要的提示,目前版本Spring/SpringBoot 都是通過(guò) ConditionEvaluator#shouldSkip 來(lái)判斷一個(gè)類(lèi)是否應(yīng)該忽略(Determine if an item should be skipped based on {@code @Conditional} annotations)
  • 該類(lèi)如果不應(yīng)該忽略時(shí),就會(huì)繼續(xù)尋找該類(lèi)是否有 @ComponentScan 注解(我們知道@SpringBootApplication 注解也包含 @Component注解)
  • 找到 @ComponentScan 注解后,使用 ComponentScanAnnotationParser#parse 完成 basePackages 下所有類(lèi)的掃描(如果@ComponentScan中沒(méi)有加basePackages,默認(rèn)會(huì)使用primarySource類(lèi)所在包為basePackages)
當(dāng)用SpringApplication.run的時(shí)候發(fā)生了什么(一)

 

  • 獲取basePackages下的所有類(lèi)(getResources)
  • 循環(huán)每個(gè)類(lèi),并且判斷是否是有效的沒(méi)有被排除(不在excludeFilter內(nèi))是@Component類(lèi),或者是派生類(lèi)(@Configuration等)而且不應(yīng)該被跳過(guò),即 ConditionEvaluator#shouldSkip = false
  • 將有效的類(lèi)進(jìn)行 registerBeanDefinition

到這一步時(shí),可以猜一下上面實(shí)例中哪些類(lèi)是有效的

當(dāng)用SpringApplication.run的時(shí)候發(fā)生了什么(一)

 

這里你有沒(méi)有疑問(wèn):

問(wèn):為什么MyClientAutoConfiguration類(lèi)是無(wú)效的?

答:因?yàn)锧ConditionalOnProperty(prefix = "client", name = "enable", havingValue = "true") 不滿足條件,yml中沒(méi)有 client.enable=true,所以 ConditionEvaluator#shouldSkip=true,就被跳過(guò)了。

問(wèn):為什么內(nèi)部類(lèi) MyClientConfiger 和 ClientConfiger是有效的?

答:因?yàn)檫@兩個(gè)類(lèi)都是 @Configuration Class ,而且 ConditionEvaluator#shouldSkip=false ;@ConditionalOnProperty(matchIfMissing = true) 中 matchIfMissing=true的意思是如果沒(méi)有找到匹配的也放行。

  1. 獲取BeanMethod

所謂BeanMethod 就是標(biāo)有@Bean的方法

上面實(shí)例中標(biāo)有@Bean 的方法有:

MyClientAutoConfiguration#client1 方法 和 MyClientAutoConfiguration$ClientConfiger#client2 方法,但是因?yàn)镸yClientAutoConfiguration類(lèi)不符合條件,所以client1 bean method也是無(wú)效的,到這里獲取到的 BeanMethod 只有client2

當(dāng)用SpringApplication.run的時(shí)候發(fā)生了什么(一)

 

將此BeanMethod進(jìn)行注冊(cè),即
registry.registerBeanDefinition(beanName, beanDefToRegister)。

到目前為止在此過(guò)程中注冊(cè)了3個(gè)BeanDefinition, 即MyClientConfiger 和 ClientConfiger ,及client2,那么MyClientConfiger 和 ClientConfiger的順序是怎么控制的?

BeanDefinitions 的順序是什么樣的?

在上面步驟中我們說(shuō)了,獲取basePackages下的所有類(lèi)(getResources),那么Class的順序就是在getResources 過(guò)程中改變:

protected File[] listDirectory(File dir) {
        File[] files = dir.listFiles();
        if (files == null) {
            if (logger.isInfoEnabled()) {
                logger.info("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
            }
            return new File[0];
        }
        Arrays.sort(files, Comparator.comparing(File::getName));
        return files;
    }

從上面源碼中我們可以看到,如果是文件,那么是根據(jù)文件名進(jìn)行排序的,排序后變成

當(dāng)用SpringApplication.run的時(shí)候發(fā)生了什么(一)

 

那么如果是Jar文件呢,大家可以看下源碼,是根據(jù)JarFile解壓之后的順序。

finishBeanFactoryInitialization

此方法主要完成BeanDefinition -> Bean的過(guò)程。

當(dāng)用SpringApplication.run的時(shí)候發(fā)生了什么(一)

 

這里或按照順序依次遍歷之前的 BeanDefinitions,然后進(jìn)行 getBean -> createBean -> initialize ,但是有個(gè)地方需要注意就是當(dāng)一個(gè)Bean創(chuàng)建/初始化過(guò)程中如果需要/依賴其他的Bean,且這個(gè)依賴的Bean 還沒(méi)有創(chuàng)建的時(shí)候,則優(yōu)先會(huì)創(chuàng)建這個(gè)Bean(doResolveDependency),比如:

 @Autowired
 public MyClientConfiger(MyClient client) {
        this.client = client;
 }

MyClientConfiger 在創(chuàng)建Bean初始化過(guò)程中,發(fā)現(xiàn)構(gòu)造函數(shù)中需要依賴 MyClient 類(lèi)型的Bean ,此時(shí)就會(huì)優(yōu)先創(chuàng)建MyClient Bean ,即beanName=client2,如果沒(méi)有找到這個(gè)BeanDefinition,則此時(shí)就會(huì)報(bào)錯(cuò) throw new
NoSuchBeanDefinitionException
 。

好了,Bean定義和創(chuàng)建的過(guò)程已經(jīng)講完了,下面我拋一個(gè)問(wèn)題給大家,假如我把 client.enable=true 配置加上會(huì)發(fā)生什么?

client:
  id: 1
  server: localhost
  enable: true

分享到:
標(biāo)簽:SpringApplication run
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定