SpringBoot啟動(dòng)流程源碼分析

一、源碼分析

首先是項(xiàng)目啟動(dòng)類

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

初始化時(shí)癣亚,會(huì)加載META-INF/spring.factories文件,來看一下deduceWebEnvironment()方法

private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
            && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : WEB_ENVIRONMENT_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}

這里主要是通過判斷REACTIVE相關(guān)的字節(jié)碼是否存在嗓化,如果不存在搀庶,則web環(huán)境即為SERVLET類型蜕径。這里設(shè)置好web環(huán)境類型纷纫,在后面會(huì)根據(jù)類型初始化對(duì)應(yīng)環(huán)境。ApplicationContextInitializer是spring組件spring-context組件中的一個(gè)接口三妈,主要是spring ioc容器刷新之前的一個(gè)回調(diào)接口,用于處于自定義邏輯莫绣。spring.factories文件中的實(shí)現(xiàn)類:

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

還有1個(gè)為:org.springframework.boot.autoconfigure.BackgroundPreinitializer這10個(gè)監(jiān)聽器會(huì)貫穿SpringBoot整個(gè)生命周期畴蒲。

來看一下run方法:

public ConfigurableApplicationContext run(String... args) {
    //時(shí)間監(jiān)控
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //java.awt.headless是J2SE的一種模式用于在缺少顯示屏、鍵盤或者鼠標(biāo)時(shí)的系統(tǒng)配置对室,很多監(jiān)控工具如jconsole 需要將該值設(shè)置為true模燥,系統(tǒng)變量默認(rèn)為true
    configureHeadlessProperty();
    //獲取spring.factories中的監(jiān)聽器變量,args為指定的參數(shù)數(shù)組掩宜,默認(rèn)為當(dāng)前類SpringApplication
    //第一步:獲取并啟動(dòng)監(jiān)聽器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        //第二步:構(gòu)造容器環(huán)境
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        //設(shè)置需要忽略的bean
        configureIgnoreBeanInfo(environment);
        //打印banner
        Banner printedBanner = printBanner(environment);
        //第三步:創(chuàng)建容器
        context = createApplicationContext();
        //第四步:實(shí)例化SpringBootExceptionReporter.class蔫骂,用來支持報(bào)告關(guān)于啟動(dòng)的錯(cuò)誤
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        //第五步:準(zhǔn)備容器
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        //第六步:刷新容器
        refreshContext(context);
        //第七步:刷新容器后的擴(kuò)展接口
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}
  • 第一步:獲取并啟動(dòng)監(jiān)聽器
  • 第二步:構(gòu)造容器環(huán)境
  • 第三步:創(chuàng)建容器
  • 第四步:實(shí)例化SpringBootExceptionReporter.class,用來支持報(bào)告關(guān)于啟動(dòng)的錯(cuò)誤
  • 第五步:準(zhǔn)備容器
  • 第六步:刷新容器
  • 第七步:刷新容器后的擴(kuò)展接口

二牺汤、步驟分析

2.1 獲取并啟動(dòng)監(jiān)聽器
2.1.1 獲取監(jiān)聽器

SpringApplicationRunListeners listeners = getRunListeners(args); 跟進(jìn)getRunListeners方法:

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
            SpringApplicationRunListener.class, types, this, args));
}

上面可以看到辽旋,args本身默認(rèn)為空,但是在獲取監(jiān)聽器的方法中,getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)將當(dāng)前對(duì)象作為參數(shù)补胚,該方法用來獲取spring.factories對(duì)應(yīng)的監(jiān)聽器:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

整個(gè)SpringBoot 框架中獲取factories的方式統(tǒng)一如下:

@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
        Set<String> names) {
    List<T> instances = new ArrayList<>(names.size());
    for (String name : names) {
        try {
            //裝載class文件到內(nèi)存
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            Constructor<?> constructor = instanceClass
                    .getDeclaredConstructor(parameterTypes);
            //主要通過反射創(chuàng)建實(shí)例
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
        }
        catch (Throwable ex) {
            throw new IllegalArgumentException(
                    "Cannot instantiate " + type + " : " + name, ex);
        }
    }
    return instances;
}

上面通過反射獲取實(shí)例時(shí)會(huì)觸發(fā)EventPublishingRunListener的構(gòu)造函數(shù):

public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    for (ApplicationListener<?> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}

重點(diǎn)來看一下addApplicationListener方法:

@Override
public void addApplicationListener(ApplicationListener<?> listener) {
    synchronized (this.retrievalMutex) {
        // Explicitly remove target for a proxy, if registered already,
        // in order to avoid double invocations of the same listener.
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }
        //內(nèi)部類對(duì)象
        this.defaultRetriever.applicationListeners.add(listener);
        this.retrieverCache.clear();
    }
}

上述方法定義在SimpleApplicationEventMulticaster父類AbstractApplicationEventMulticaster中码耐。關(guān)鍵代碼為this.defaultRetriever.applicationListeners.add(listener);,這是一個(gè)內(nèi)部類溶其,用來保存所有的監(jiān)聽器骚腥。也就是在這一步,將spring.factories中的監(jiān)聽器傳遞到SimpleApplicationEventMulticaster中瓶逃。

2.1.2 啟動(dòng)監(jiān)聽器

listeners.starting();,獲取的監(jiān)聽器為EventPublishingRunListener束铭,從名字可以看出是啟動(dòng)事件發(fā)布監(jiān)聽器,主要用來發(fā)布啟動(dòng)事件厢绝。

@Override
public void starting() {
//關(guān)鍵代碼契沫,這里是創(chuàng)建application啟動(dòng)事件`ApplicationStartingEvent`
    this.initialMulticaster.multicastEvent(
            new ApplicationStartingEvent(this.application, this.args));
}

EventPublishingRunListener這個(gè)是SpringBoot框架中最早執(zhí)行的監(jiān)聽器,在該監(jiān)聽器執(zhí)行started()方法時(shí)代芜,會(huì)繼續(xù)發(fā)布事件埠褪,也就是事件傳遞。這種實(shí)現(xiàn)主要還是基于Spring的事件機(jī)制挤庇。繼續(xù)跟進(jìn)SimpleApplicationEventMulticaster钞速,有個(gè)核心方法:

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        //獲取線程池,如果為空則同步處理嫡秕。這里線程池為空渴语,還未沒初始化。
        Executor executor = getTaskExecutor();
        if (executor != null) {
            //異步發(fā)送事件
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            //同步發(fā)送事件
            invokeListener(listener, event);
        }
    }
}

這里會(huì)根據(jù)事件類型ApplicationStartingEvent獲取對(duì)應(yīng)的監(jiān)聽器昆咽,在容器啟動(dòng)之后執(zhí)行響應(yīng)的動(dòng)作驾凶。

這是SpringBoot啟動(dòng)過程中,第一處根據(jù)類型掷酗,執(zhí)行監(jiān)聽器的地方调违。根據(jù)發(fā)布的事件類型從上述10種監(jiān)聽器中選擇對(duì)應(yīng)的監(jiān)聽器進(jìn)行事件發(fā)布,當(dāng)然如果繼承了 SpringCloud或者別的框架泻轰,就不止10個(gè)了技肩。這里選了一個(gè) SpringBoot 的日志監(jiān)聽器來進(jìn)行講解,核心代碼如下:

@Override
public void onApplicationEvent(ApplicationEvent event) {
    //在springboot啟動(dòng)的時(shí)候
    if (event instanceof ApplicationStartedEvent) {
        onApplicationStartedEvent((ApplicationStartedEvent) event);
    }
    //springboot的Environment環(huán)境準(zhǔn)備完成的時(shí)候
    else if (event instanceof ApplicationEnvironmentPreparedEvent) {
        onApplicationEnvironmentPreparedEvent(
                (ApplicationEnvironmentPreparedEvent) event);
    }
    //在springboot容器的環(huán)境設(shè)置完成以后
    else if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent((ApplicationPreparedEvent) event);
    }
    //容器關(guān)閉的時(shí)候
    else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
            .getApplicationContext().getParent() == null) {
        onContextClosedEvent();
    }
    //容器啟動(dòng)失敗的時(shí)候
    else if (event instanceof ApplicationFailedEvent) {
        onApplicationFailedEvent();
    }
}

因?yàn)槲覀兊氖录愋蜑锳pplicationEvent浮声,所以會(huì)執(zhí)行onApplicationStartedEvent((ApplicationStartedEvent) event);虚婿。SpringBoot會(huì)在運(yùn)行過程中的不同階段,發(fā)送各種事件泳挥,來執(zhí)行對(duì)應(yīng)監(jiān)聽器的對(duì)應(yīng)方法然痊。

2.2 環(huán)境構(gòu)建

ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); 跟進(jìn)去該方法:

private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    //獲取對(duì)應(yīng)的ConfigurableEnvironment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //配置
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //發(fā)布環(huán)境已準(zhǔn)備事件,這是第二次發(fā)布事件
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (this.webApplicationType == WebApplicationType.NONE) {
        environment = new EnvironmentConverter(getClassLoader())
                .convertToStandardEnvironmentIfNecessary(environment);
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

來看一下getOrCreateEnvironment()方法屉符,前面已經(jīng)提到剧浸,environment已經(jīng)被設(shè)置了servlet類型锹引,所以這里創(chuàng)建的是環(huán)境對(duì)象是StandardServletEnvironment。

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    if (this.webApplicationType == WebApplicationType.SERVLET) {
        return new StandardServletEnvironment();
    }
    return new StandardEnvironment();
}

枚舉類WebApplicationType是SpringBoot2新增的特性辛蚊,主要針對(duì)spring5引入的reactive特性粤蝎。枚舉類型如下:

public enum WebApplicationType {
    //不需要再web容器的環(huán)境下運(yùn)行,普通項(xiàng)目
    NONE,
    //基于servlet的web項(xiàng)目
    SERVLET,
    //這個(gè)是spring5版本開始的新特性
    REACTIVE
}

Environment接口提供了4種實(shí)現(xiàn)方式袋马,StandardEnvironment初澎、StandardServletEnvironment和MockEnvironment、StandardReactiveWebEnvironment虑凛,分別代表普通程序碑宴、Web程序、測(cè)試程序的環(huán)境桑谍、響應(yīng)式web環(huán)境延柠。
在返回return new StandardServletEnvironment();對(duì)象的時(shí)候,會(huì)完成一系列初始化動(dòng)作锣披,主要就是將運(yùn)行機(jī)器的系統(tǒng)變量和環(huán)境變量贞间,加入到其父類AbstractEnvironment定義的對(duì)象MutablePropertySources中,MutablePropertySources對(duì)象中定義了一個(gè)屬性集合:

private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();

執(zhí)行到這里雹仿,系統(tǒng)變量和環(huán)境變量已經(jīng)被載入到配置文件的集合中增热,接下來就行解析項(xiàng)目中的配置文件。來看一下listeners.environmentPrepared(environment);胧辽,上面已經(jīng)提到了峻仇,這里是第二次發(fā)布事件。什么事件呢邑商?顧名思義摄咆,系統(tǒng)環(huán)境初始化完成的事件。

可以看到獲取到的監(jiān)聽器和第一次發(fā)布啟動(dòng)事件獲取的監(jiān)聽器有幾個(gè)是重復(fù)的人断,這也驗(yàn)證了監(jiān)聽器是可以多次獲取吭从,根據(jù)事件類型來區(qū)分具體處理邏輯。上面介紹日志監(jiān)聽器的時(shí)候已經(jīng)提到恶迈。主要來看一下ConfigFileApplicationListener涩金,該監(jiān)聽器非常核心,主要用來處理項(xiàng)目配置蝉绷。項(xiàng)目中的 properties 和yml文件都是其內(nèi)部類所加載鸭廷。

首先還是會(huì)去讀spring.factories 文件枣抱,List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();獲取的處理類有以下四種:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=  //一個(gè)@FunctionalInterface函數(shù)式接口
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor熔吗,//為springCloud提供的擴(kuò)展類
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,//支持json環(huán)境變量
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor //springBoo2提供的一個(gè)包裝類佳晶,主要將`StandardServletEnvironment`包裝成`SystemEnvironmentPropertySourceEnvironmentPostProcessor`對(duì)象

在執(zhí)行完上述三個(gè)監(jiān)聽器流程后桅狠,ConfigFileApplicationListener會(huì)執(zhí)行該類本身的邏輯。由其內(nèi)部類Loader加載項(xiàng)目制定路徑下的配置文件:

private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
2.3 創(chuàng)建容器

context = createApplicationContext(); 繼續(xù)跟進(jìn)該方法:

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, "
                            + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

上面可以看出,這里創(chuàng)建容器的類型 還是根據(jù)webApplicationType進(jìn)行判斷的中跌,因?yàn)樵擃愋蜑镾ERVLET類型咨堤,所以會(huì)通過反射裝載對(duì)應(yīng)的字節(jié)碼,如下:

public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

該對(duì)象是SpringBoot2創(chuàng)建的容器漩符,后續(xù)所有的操作都會(huì)基于該容器一喘。

2.4 報(bào)告錯(cuò)誤信息

這里還是以同樣的方式獲取spring.factories文件中的指定類:

exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);

該類主要是在項(xiàng)目啟動(dòng)失敗之后,打印log:

private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters,Throwable failure) {
    try {
        for (SpringBootExceptionReporter reporter : exceptionReporters) {
            if (reporter.reportException(failure)) {
                //上報(bào)錯(cuò)誤log
                registerLoggedException(failure);
                return;
            }
        }
    }
    catch (Throwable ex) {
        // Continue with normal handling of the original failure
    }
    if (logger.isErrorEnabled()) {
        logger.error("Application run failed", failure);
        registerLoggedException(failure);
    }
}
2.5 準(zhǔn)備容器

這一步主要是在容器刷新之前的準(zhǔn)備動(dòng)作嗜暴。包含一個(gè)非常關(guān)鍵的操作:將啟動(dòng)類注入容器凸克,為后續(xù)開啟自動(dòng)化配置奠定基礎(chǔ)。

prepareContext(context, environment, listeners, applicationArguments,printedBanner);

繼續(xù)跟進(jìn)該方法:

private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    //設(shè)置容器環(huán)境闷沥,包括各種變量
    context.setEnvironment(environment);
    //執(zhí)行容器后置處理
    postProcessApplicationContext(context);
    //執(zhí)行容器中的ApplicationContextInitializer(包括 spring.factories和自定義的實(shí)例)
    applyInitializers(context);
    //發(fā)送容器已經(jīng)準(zhǔn)備好的事件萎战,通知各監(jiān)聽器
    listeners.contextPrepared(context);
    //打印log
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    //注冊(cè)啟動(dòng)參數(shù)bean,這里將容器指定的參數(shù)封裝成bean舆逃,注入容器
    context.getBeanFactory().registerSingleton("springApplicationArguments",
            applicationArguments);
    //設(shè)置banner
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }
    // Load the sources
    //獲取我們的啟動(dòng)類指定的參數(shù)蚂维,可以是多個(gè)
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    //加載我們的啟動(dòng)類,將啟動(dòng)類注入容器
    load(context, sources.toArray(new Object[0]));
    //發(fā)布容器已加載事件路狮。
    listeners.contextLoaded(context);
}

來看一下上面的幾個(gè)核心處理

2.5.1 容器的后置處理
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    if (this.beanNameGenerator != null) {
        context.getBeanFactory().registerSingleton(
                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        if (context instanceof GenericApplicationContext) {
            ((GenericApplicationContext) context)
                    .setResourceLoader(this.resourceLoader);
        }
        if (context instanceof DefaultResourceLoader) {
            ((DefaultResourceLoader) context)
                    .setClassLoader(this.resourceLoader.getClassLoader());
        }
    }
}

這里默認(rèn)不執(zhí)行任何邏輯虫啥,因?yàn)閎eanNameGenerator和resourceLoader默認(rèn)為空。之所以這樣做览祖,是SpringBoot留給我們的擴(kuò)展處理方式遇西,類似于這樣的擴(kuò)展,Spring中也有很多杰赛。

2.5.2 加載啟動(dòng)指定類(重點(diǎn))

這里會(huì)將我們的啟動(dòng)類加載Spring容器beanDefinitionMap中乖坠,為后續(xù)SpringBoot自動(dòng)化配置奠定基礎(chǔ),SpringBoot為我們提供的各種注解配置也與此有關(guān)锰悼。

load(context, sources.toArray(new Object[0]));
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();
}

這里參數(shù)即為我們項(xiàng)目啟動(dòng)時(shí)傳遞的參數(shù):SpringApplication.run(SpringBootApplication.class, args);由于我們指定了啟動(dòng)類柳骄,所以上面也就是加載啟動(dòng)類到容器。需要注意的是箕般,SpringBoot2會(huì)優(yōu)先選擇groovy加載方式耐薯,找不到再選用java方式∷坷铮或許groovy動(dòng)態(tài)加載class文件的性能更勝一籌曲初。

private int load(Class<?> source) {
    if (isGroovyPresent()
            && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
        // Any GroovyLoaders added in beans{} DSL can contribute beans here
        GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
                GroovyBeanDefinitionSource.class);
        load(loader);
    }
    if (isComponent(source)) {
        //以注解的方式,將啟動(dòng)類bean信息存入beanDefinitionMap
        this.annotatedReader.register(source);
        return 1;
    }
    return 0;
}

上面代碼中啟動(dòng)類被加載到 beanDefinitionMap中杯聚,后續(xù)該啟動(dòng)類將作為開啟自動(dòng)化配置的入口臼婆。

2.5.3 通知監(jiān)聽器,容器已準(zhǔn)備就緒
listeners.contextLoaded(context);

主還是針對(duì)一些日志等監(jiān)聽器的響應(yīng)處理幌绍。

2.6 刷新容器

執(zhí)行到這里颁褂,SpringBoot相關(guān)的處理工作已經(jīng)結(jié)束故响,接下的工作就交給了Spring。

synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing.
    /**
     * 刷新上下文環(huán)境
     * 初始化上下文環(huán)境颁独,對(duì)系統(tǒng)的環(huán)境變量或者系統(tǒng)屬性進(jìn)行準(zhǔn)備和校驗(yàn)
     * 如環(huán)境變量中必須設(shè)置某個(gè)值才能運(yùn)行彩届,否則不能運(yùn)行,這個(gè)時(shí)候可以在這里加這個(gè)校驗(yàn)誓酒,
     * 重寫initPropertySources方法就好了
     */
    prepareRefresh();

    // Tell the subclass to refresh the internal bean factory.
    /**
     * 初始化BeanFactory樟蠕,解析XML,相當(dāng)于之前的XmlBeanFactory的操作靠柑,
     */
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // Prepare the bean factory for use in this context.
    /**
     * 為上下文準(zhǔn)備BeanFactory坯墨,即對(duì)BeanFactory的各種功能進(jìn)行填充,如常用的注解@Autowired @Qualifier等
     * 設(shè)置SPEL表達(dá)式#{key}的解析器
     * 設(shè)置資源編輯注冊(cè)器病往,如PerpertyEditorSupper的支持
     * 添加ApplicationContextAwareProcessor處理器
     * 在依賴注入忽略實(shí)現(xiàn)*Aware的接口捣染,如EnvironmentAware、ApplicationEventPublisherAware等
     * 注冊(cè)依賴停巷,如一個(gè)bean的屬性中含有ApplicationEventPublisher(beanFactory)耍攘,則會(huì)將beanFactory的實(shí)例注入進(jìn)去
     */
    prepareBeanFactory(beanFactory);

    try {
        // Allows post-processing of the bean factory in context subclasses.
        /**
         * 提供子類覆蓋的額外處理,即子類處理自定義的BeanFactoryPostProcess
         */
        postProcessBeanFactory(beanFactory);

        // Invoke factory processors registered as beans in the context.
        /**
         * 激活各種BeanFactory處理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
         * 執(zhí)行對(duì)應(yīng)的postProcessBeanDefinitionRegistry方法 和  postProcessBeanFactory方法
         */
        invokeBeanFactoryPostProcessors(beanFactory);

        // Register bean processors that intercept bean creation.
        /**
         * 注冊(cè)攔截Bean創(chuàng)建的Bean處理器畔勤,即注冊(cè)BeanPostProcessor蕾各,不是BeanFactoryPostProcessor,注意兩者的區(qū)別
         * 注意庆揪,這里僅僅是注冊(cè)式曲,并不會(huì)執(zhí)行對(duì)應(yīng)的方法,將在bean的實(shí)例化時(shí)執(zhí)行對(duì)應(yīng)的方法
         */
        registerBeanPostProcessors(beanFactory);

        // Initialize message source for this context.
        /**
         * 初始化上下文中的資源文件缸榛,如國(guó)際化文件的處理等
         */
        initMessageSource();

        // Initialize event multicaster for this context.
        /**
         * 初始化上下文事件廣播器吝羞,并放入applicatioEventMulticaster,如ApplicationEventPublisher
         */
        initApplicationEventMulticaster();

        // Initialize other special beans in specific context subclasses.
        /**
         * 給子類擴(kuò)展初始化其他Bean
         */
        onRefresh();

        // Check for listener beans and register them.
        /**
         * 在所有bean中查找listener bean,然后注冊(cè)到廣播器中
         */
        registerListeners();

        // Instantiate all remaining (non-lazy-init) singletons.
        /**
         * 設(shè)置轉(zhuǎn)換器
         * 注冊(cè)一個(gè)默認(rèn)的屬性值解析器
         * 凍結(jié)所有的bean定義内颗,說明注冊(cè)的bean定義將不能被修改或進(jìn)一步的處理
         * 初始化剩余的非惰性的bean钧排,即初始化非延遲加載的bean
         */
        finishBeanFactoryInitialization(beanFactory);

        // Last step: publish corresponding event.
        /**
         * 初始化生命周期處理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法均澳,spring啟動(dòng)的時(shí)候調(diào)用start方法開始生命周期恨溜,
         * spring關(guān)閉的時(shí)候調(diào)用stop方法來結(jié)束生命周期,通常用來配置后臺(tái)程序找前,啟動(dòng)有一直運(yùn)行糟袁,如一直輪詢kafka
         * 啟動(dòng)所有實(shí)現(xiàn)了Lifecycle接口的類
         * 通過spring的事件發(fā)布機(jī)制發(fā)布ContextRefreshedEvent事件,以保證對(duì)應(yīng)的監(jiān)聽器做進(jìn)一步的處理躺盛,即對(duì)那種在spring啟動(dòng)后需要處理的一些類项戴,這些類實(shí)現(xiàn)了
         * ApplicationListener<ContextRefreshedEvent> ,這里就是要觸發(fā)這些類的執(zhí)行(執(zhí)行onApplicationEvent方法)另外,spring的內(nèi)置Event有ContextClosedEvent颗品、ContextRefreshedEvent肯尺、ContextStartedEvent、ContextStoppedEvent躯枢、RequestHandleEvent
         * 完成初始化则吟,通知生命周期處理器lifeCycleProcessor刷新過程,同時(shí)發(fā)出ContextRefreshEvent通知其他人
         */
        finishRefresh();
    }

    catch (BeansException ex) {
        if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                    "cancelling refresh attempt: " + ex);
        }

        // Destroy already created singletons to avoid dangling resources.
        destroyBeans();
        // Reset 'active' flag.
        cancelRefresh(ex);
        // Propagate exception to caller.
        throw ex;
    }

    finally {
        // Reset common introspection caches in Spring's core, since we
        // might not ever need metadata for singleton beans anymore...
        resetCommonCaches();
    }
}

refresh方法在Spring整個(gè)源碼體系中舉足輕重锄蹂,是實(shí)現(xiàn)IOC和AOP的關(guān)鍵氓仲。

2.7 刷新容器后的擴(kuò)展接口
protected void afterRefresh(ConfigurableApplicationContext context,
        ApplicationArguments args) {
}

擴(kuò)展接口,設(shè)計(jì)模式中的模板方法得糜,默認(rèn)為空實(shí)現(xiàn)敬扛。如果有自定義需求,可以重寫該方法朝抖。比如打印一些啟動(dòng)結(jié)束log啥箭,或者一些其它后置處理。

轉(zhuǎn)載自:SpringBoot | 第一篇:?jiǎn)?dòng)流程源碼分析(上)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末治宣,一起剝皮案震驚了整個(gè)濱河市急侥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌侮邀,老刑警劉巖坏怪,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異绊茧,居然都是意外死亡铝宵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門华畏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹏秋,“玉大人,你說我怎么就攤上這事亡笑∑丛溃” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵况芒,是天一觀的道長(zhǎng)惜纸。 經(jīng)常有香客問我,道長(zhǎng)绝骚,這世上最難降的妖魔是什么耐版? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮压汪,結(jié)果婚禮上粪牲,老公的妹妹穿的比我還像新娘。我一直安慰自己止剖,他們只是感情好腺阳,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布落君。 她就那樣靜靜地躺著,像睡著了一般亭引。 火紅的嫁衣襯著肌膚如雪绎速。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天焙蚓,我揣著相機(jī)與錄音纹冤,去河邊找鬼。 笑死购公,一個(gè)胖子當(dāng)著我的面吹牛萌京,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宏浩,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼知残,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了比庄?” 一聲冷哼從身側(cè)響起橡庞,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎印蔗,沒想到半個(gè)月后扒最,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡华嘹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年吧趣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耙厚。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡强挫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出薛躬,到底是詐尸還是另有隱情俯渤,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布型宝,位于F島的核電站八匠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏趴酣。R本人自食惡果不足惜梨树,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岖寞。 院中可真熱鬧抡四,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至藻雪,卻和暖如春秘噪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背阔涉。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捷绒,地道東北人瑰排。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像暖侨,于是被迫代替她去往敵國(guó)和親椭住。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容