国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Spring Boot 2.x 啟動(dòng)全過(guò)程源碼分析(上)入口類剖析

MobService / 581人閱讀

摘要:設(shè)置應(yīng)用上線文初始化器的作用是什么源碼如下。來(lái)看下方法源碼,其實(shí)就是初始化一個(gè)應(yīng)用上下文初始化器實(shí)例的集合。設(shè)置監(jiān)聽(tīng)器和設(shè)置初始化器調(diào)用的方法是一樣的,只是傳入的類型不一樣,設(shè)置監(jiān)聽(tīng)器的接口類型為,對(duì)應(yīng)的文件配置內(nèi)容請(qǐng)見(jiàn)下方。

Spring Boot 的應(yīng)用教程我們已經(jīng)分享過(guò)很多了,今天來(lái)通過(guò)源碼來(lái)分析下它的啟動(dòng)過(guò)程,探究下 Spring Boot 為什么這么簡(jiǎn)便的奧秘。

本篇基于 Spring Boot 2.0.3 版本進(jìn)行分析,閱讀本文需要有一些 Java 和 Spring 框架基礎(chǔ),如果還不知道 Spring Boot 是什么,建議先看下我們的 Spring Boot 教程。

Spring Boot 的入口類
@SpringBootApplication
public class SpringBootBestPracticeApplication {

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

做過(guò) Spring Boot 項(xiàng)目的都知道,上面是 Spring Boot 最簡(jiǎn)單通用的入口類。入口類的要求是最頂層包下面第一個(gè)含有 main 方法的類,使用注解 @SpringBootApplication 來(lái)啟用 Spring Boot 特性,使用 SpringApplication.run 方法來(lái)啟動(dòng) Spring Boot 項(xiàng)目。

來(lái)看一下這個(gè)類的 run 方法調(diào)用關(guān)系源碼:

public static ConfigurableApplicationContext run(Class primarySource,
        String... args) {
    return run(new Class[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class[] primarySources,
        String[] args) {
    return new SpringApplication(primarySources).run(args);
}

第一個(gè)參數(shù) primarySource:加載的主要資源類

第二個(gè)參數(shù) args:傳遞給應(yīng)用的應(yīng)用參數(shù)

先用主要資源類來(lái)實(shí)例化一個(gè) SpringApplication 對(duì)象,再調(diào)用這個(gè)對(duì)象的 run 方法,所以我們分兩步來(lái)分析這個(gè)啟動(dòng)源碼。

SpringApplication 的實(shí)例化過(guò)程

接著上面的 SpringApplication 構(gòu)造方法進(jìn)入以下源碼:

public SpringApplication(Class... primarySources) {
    this(null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    // 1、資源初始化資源加載器為 null
    this.resourceLoader = resourceLoader;
    
    // 2、斷言主要加載資源類不能為 null,否則報(bào)錯(cuò)
    Assert.notNull(primarySources, "PrimarySources must not be null");
    
    // 3、初始化主要加載資源類集合并去重
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    
    // 4、推斷當(dāng)前 WEB 應(yīng)用類型
    this.webApplicationType = deduceWebApplicationType();
    
    // 5、設(shè)置應(yīng)用上線文初始化器
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));        
            
    // 6、設(shè)置監(jiān)聽(tīng)器            
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    
    // 7、推斷主入口應(yīng)用類
    this.mainApplicationClass = deduceMainApplicationClass();
}

可知這個(gè)構(gòu)造器類的初始化包括以下 7 個(gè)過(guò)程。

1、資源初始化資源加載器為 null
this.resourceLoader = resourceLoader;
2、斷言主要加載資源類不能為 null,否則報(bào)錯(cuò)
Assert.notNull(primarySources, "PrimarySources must not be null");
3、初始化主要加載資源類集合并去重
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
4、推斷當(dāng)前 WEB 應(yīng)用類型
this.webApplicationType = deduceWebApplicationType();

來(lái)看下 deduceWebApplicationType 方法和相關(guān)的源碼:

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

private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
        + "web.reactive.DispatcherHandler";

private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
        + "web.servlet.DispatcherServlet";
        
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
            "org.springframework.web.context.ConfigurableWebApplicationContext" };        

public enum WebApplicationType {

    /**
     * 非 WEB 項(xiàng)目
     */
    NONE,

    /**
     * SERVLET WEB 項(xiàng)目
     */
    SERVLET,

    /**
     * 響應(yīng)式 WEB 項(xiàng)目
     */
    REACTIVE

}

這個(gè)就是根據(jù)類路徑下是否有對(duì)應(yīng)項(xiàng)目類型的類推斷出不同的應(yīng)用類型。

5、設(shè)置應(yīng)用上線文初始化器
setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));

ApplicationContextInitializer 的作用是什么?源碼如下。

public interface ApplicationContextInitializer {

    /**
     * Initialize the given application context.
     * @param applicationContext the application to configure
     */
    void initialize(C applicationContext);

}

用來(lái)初始化指定的 Spring 應(yīng)用上下文,如注冊(cè)屬性資源、激活 Profiles 等。

來(lái)看下 setInitializers 方法源碼,其實(shí)就是初始化一個(gè) ApplicationContextInitializer 應(yīng)用上下文初始化器實(shí)例的集合。

public void setInitializers(
        Collection> initializers) {
    this.initializers = new ArrayList<>();
    this.initializers.addAll(initializers);
}

再來(lái)看下這個(gè)初始化 getSpringFactoriesInstances 方法和相關(guān)的源碼:

private  Collection getSpringFactoriesInstances(Class type) {
    return getSpringFactoriesInstances(type, new Class[] {});
}

private  Collection getSpringFactoriesInstances(Class type,
        Class[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

設(shè)置應(yīng)用上下文初始化器可分為以下 5 個(gè)步驟。

5.1)獲取當(dāng)前線程上下文類加載器

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

5.2)獲取 ApplicationContextInitializer 的實(shí)例名稱集合并去重

Set names = new LinkedHashSet<>(
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));

loadFactoryNames 方法相關(guān)的源碼如下:

public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    try {
        Enumeration urls = (classLoader != null ?
                classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry entry : properties.entrySet()) {
                List factoryClassNames = Arrays.asList(
                        StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
                result.addAll((String) entry.getKey(), factoryClassNames);
            }
        }
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

根據(jù)類路徑下的 META-INF/spring.factories 文件解析并獲取 ApplicationContextInitializer 接口的所有配置的類路徑名稱。

spring-boot-autoconfigure-2.0.3.RELEASE.jar!/META-INF/spring.factories 的初始化器相關(guān)配置內(nèi)容如下:

# Initializers
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

5.3)根據(jù)以上類路徑創(chuàng)建初始化器實(shí)例列表

List instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);

private  List createSpringFactoriesInstances(Class type,
        Class[] parameterTypes, ClassLoader classLoader, Object[] args,
        Set names) {
    List instances = new ArrayList<>(names.size());
    for (String name : names) {
        try {
            Class instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            Constructor constructor = instanceClass
                    .getDeclaredConstructor(parameterTypes);
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
        }
        catch (Throwable ex) {
            throw new IllegalArgumentException(
                    "Cannot instantiate " + type + " : " + name, ex);
        }
    }
    return instances;
}

5.4)初始化器實(shí)例列表排序

AnnotationAwareOrderComparator.sort(instances);

5.5)返回初始化器實(shí)例列表

return instances;
6、設(shè)置監(jiān)聽(tīng)器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

ApplicationListener 的作用是什么?源碼如下。

@FunctionalInterface
public interface ApplicationListener extends EventListener {

    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);

}

看源碼,這個(gè)接口繼承了 JDK 的 java.util.EventListener 接口,實(shí)現(xiàn)了觀察者模式,它一般用來(lái)定義感興趣的事件類型,事件類型限定于 ApplicationEvent 的子類,這同樣繼承了 JDK 的 java.util.EventObject 接口。

設(shè)置監(jiān)聽(tīng)器和設(shè)置初始化器調(diào)用的方法是一樣的,只是傳入的類型不一樣,設(shè)置監(jiān)聽(tīng)器的接口類型為:getSpringFactoriesInstances,對(duì)應(yīng)的 spring-boot-autoconfigure-2.0.3.RELEASE.jar!/META-INF/spring.factories 文件配置內(nèi)容請(qǐng)見(jiàn)下方。

# Application Listeners
org.springframework.context.ApplicationListener=
org.springframework.boot.autoconfigure.BackgroundPreinitializer

可以看出目前只有一個(gè) BackgroundPreinitializer 監(jiān)聽(tīng)器。

7、推斷主入口應(yīng)用類
this.mainApplicationClass = deduceMainApplicationClass();

private Class deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

這個(gè)推斷入口應(yīng)用類的方式有點(diǎn)特別,通過(guò)構(gòu)造一個(gè)運(yùn)行時(shí)異常,再遍歷異常棧中的方法名,獲取方法名為 main 的棧幀,從來(lái)得到入口類的名字再返回該類。

總結(jié)

源碼分析內(nèi)容有點(diǎn)多,也很麻煩,本章暫時(shí)分析到 SpringApplication 構(gòu)造方法的初始化流程,下章再繼續(xù)分析其 run 方法,作者很快寫完過(guò)兩天就發(fā)布,掃碼關(guān)注下面的公眾號(hào) "Java技術(shù)棧" 即可獲取推送更新。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/76638.html

相關(guān)文章

  • 漲姿勢(shì):Spring Boot 2.x 啟動(dòng)過(guò)程源碼分析

    摘要:參考創(chuàng)建所有運(yùn)行監(jiān)聽(tīng)器并發(fā)布應(yīng)用啟動(dòng)事件來(lái)看下創(chuàng)建運(yùn)行監(jiān)聽(tīng)器相關(guān)的源碼創(chuàng)建邏輯和之前實(shí)例化初始化器和監(jiān)聽(tīng)器的一樣,一樣調(diào)用的是方法來(lái)獲取配置的監(jiān)聽(tīng)器名稱并實(shí)例化所有的類。 上篇《Spring Boot 2.x 啟動(dòng)全過(guò)程源碼分析(一)入口類剖析》我們分析了 Spring Boot 入口類 SpringApplication 的源碼,并知道了其構(gòu)造原理,這篇我們繼續(xù)往下面分析其核心 ru...

    suemi 評(píng)論0 收藏0
  • spring boot - 收藏集 - 掘金

    摘要:引入了新的環(huán)境和概要信息,是一種更揭秘與實(shí)戰(zhàn)六消息隊(duì)列篇掘金本文,講解如何集成,實(shí)現(xiàn)消息隊(duì)列。博客地址揭秘與實(shí)戰(zhàn)二數(shù)據(jù)緩存篇掘金本文,講解如何集成,實(shí)現(xiàn)緩存。 Spring Boot 揭秘與實(shí)戰(zhàn)(九) 應(yīng)用監(jiān)控篇 - HTTP 健康監(jiān)控 - 掘金Health 信息是從 ApplicationContext 中所有的 HealthIndicator 的 Bean 中收集的, Spring...

    rollback 評(píng)論0 收藏0
  • SpringBoot源碼分析系列(一)--核心注解

    摘要:用于主類上最最最核心的注解,表示這是一個(gè)項(xiàng)目,用于開啟的各項(xiàng)能力。下面我們來(lái)分析一下這個(gè)注解的組成以及作用通過(guò)上面的代碼我們可以看出來(lái)是一個(gè)組合注解,主要由和這三個(gè)注解組成的。通過(guò)源碼可以看出也是一個(gè)組合注解。 ??SpringBoot項(xiàng)目一般都會(huì)有Application的入口類,入口類中會(huì)有main方法,這是一個(gè)標(biāo)準(zhǔn)的java應(yīng)用程序的入口方法。@SpringBootApplicat...

    seanlook 評(píng)論0 收藏0
  • Spring Boot 最核心的 3 個(gè)注解詳解

    摘要:核心注解講解最大的特點(diǎn)是無(wú)需配置文件,能自動(dòng)掃描包路徑裝載并注入對(duì)象,并能做到根據(jù)下的包自動(dòng)配置。所以最核心的個(gè)注解就是這是添加的一個(gè)注解,用來(lái)代替配置文件,所有這個(gè)配置文件里面能做到的事情都可以通過(guò)這個(gè)注解所在類來(lái)進(jìn)行注冊(cè)。 最近面試一些 Java 開發(fā)者,他們其中有些在公司實(shí)際用過(guò) Spring Boot, 有些是自己興趣愛(ài)好在業(yè)余自己學(xué)習(xí)過(guò)。然而,當(dāng)我問(wèn)他們 Spring Boo...

    hzx 評(píng)論0 收藏0
  • Spring Boot 面試,一個(gè)問(wèn)題就干趴下了!

    摘要:我又問(wèn)微服務(wù)和有什么關(guān)系不用行不行然后對(duì)方就吱吱唔唔了可以打包部署,內(nèi)部集成了。為什么說(shuō)是自動(dòng)配置的開啟注解是,其實(shí)它就是由下面三個(gè)注解組成的上面三個(gè)注解,前面兩個(gè)都是自帶的,和無(wú)關(guān),所以說(shuō)上面的回答的不是在點(diǎn)上。 最近棧長(zhǎng)面試了不少人,其中不乏說(shuō)對(duì) Spring Boot 非常熟悉的,然后當(dāng)我問(wèn)到一些 Spring Boot 核心功能和原理的時(shí)候,沒(méi)人能說(shuō)得上來(lái),或者說(shuō)不到點(diǎn)上,可以...

    junbaor 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

MobService

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<