1. 程式人生 > >Spring初始化過程原始碼分析(1)

Spring初始化過程原始碼分析(1)

本文主要詳細分析Spring初始化過程的原始碼分析,目的是理解Spring具體是如何工作的。部分內容查閱於網路,有不妥之處望指正。

1、web專案中伺服器一啟動就開始載入web.xml,Spring的啟動是從web.xml中的org.springframework.web.context.ContextLoaderListener監聽器(上下文載入監聽器)開始的。

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
/WEB-INF/classes/bean-*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>

進入ContextLoaderListener類,該類中主要是上下文初始化方法contextInitialized來初始化上下文。

public void contextInitialized
(ServletContextEvent event) { this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } this.contextLoader.initWebApplicationContext(event.getServletContext()); }

進入initWebApplicationContext方法,主要看下面這部分

if (this.context == null) {
                this.context = createWebApplicationContext(servletContext);
            }
            if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent ->
                        // determine parent for root web application context, if any.
                        ApplicationContext parent = loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }
                    configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }

第二行的createWebApplicationContext(servletContext)是建立上下文的方法,進去檢視該方法的原始碼如下:

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        Class<?> contextClass = determineContextClass(sc);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        }
        return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

進入determineContextClass(sc)方法,該方法翻譯即為“確定上下文類”,也就是說這個類是能確定上下文的實現類。

protected Class<?> determineContextClass(ServletContext servletContext) {
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
        if (contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load custom context class [" + contextClassName + "]", ex);
            }
        }
        else {
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load default context class [" + contextClassName + "]", ex);
            }
        }
    }

首先從servletContext中查詢是否有上下文類名(contextClassName )即這個servletContext.getInitParameter(CONTEXT_CLASS_PARAM),其中CONTEXT_CLASS_PARAM=“contextClass”是個常量,這個方法要做的就是從web.xml中(如下)查詢param-name 為contextClass的引數值,即XmlWebApplicationContext類。

<param-name>contextClass</param-name>  
        <param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value>  
    </context-param>   

如果web.xml中配置了這段就獲取這個contextClassName,沒有就用預設的策略contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());其中的defaultStrategies物件就是前面宣告的,如下:

private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

    private static final Properties defaultStrategies;

    static {
        // Load default strategy implementations from properties file.
        // This is currently strictly internal and not meant to be customized
        // by application developers.
        try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }

其中用static程式碼塊完成預設的處理方式。ClassPathResource 獲取當前路徑下的資原始檔,從第一行的宣告DEFAULT_STRATEGIES_PATH = “ContextLoader.properties”可以看出是叫ContextLoader.properties的檔案。如下圖:
這裡寫圖片描述
其中的內容為:

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

到此獲取到了上下文實現類XmlWebApplicationContext。即 determineContextClass(sc)返回的值。下面再回到createWebApplicationContext方法,該方法獲取到上下文實現類後返回return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);返回的就是例項化了這個類的物件。注意此處強制轉換為ConfigurableWebApplicationContext介面,該介面是WebApplicationContext介面的子介面,XmlWebApplicationContext實現了ConfigurableWebApplicationContext介面。關係如下

這裡寫圖片描述
之後就走到了呼叫createWebApplicationContext方法的initWebApplicationContext方法,執行到這段程式碼:

if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent ->
                        // determine parent for root web application context, if any.
                        ApplicationContext parent = loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }
                    configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }

此時會進入這個if程式碼段,主要看configureAndRefreshWebApplicationContext(cwac, servletContext)這個方法。這個方法第一步先設定上下文id, 再讀取web.xml中配置的contextConfigLocation引數值,進一步設定環境,並載入環境初始化引數(會取web.xml中讀取,如果有配置的話)

customizeContext(sc, wac); 方法前主要是自定義初始化,只需要實現介面org.springframework.context.ApplicationContextInitializer

wac.refresh(); 這裡才是真正載入xml配置, 這裡呼叫了org.springframework.context.support.AbstractApplicationContext的refresh()。

限於篇幅剩下的部分看第二篇詳解。

相關推薦

Spring初始過程原始碼分析1

本文主要詳細分析Spring初始化過程的原始碼分析,目的是理解Spring具體是如何工作的。部分內容查閱於網路,有不妥之處望指正。 1、web專案中伺服器一啟動就開始載入web.xml,Spring的啟動是從web.xml中的org.springframewo

Spring 初始過程詳細分析 [原始碼] (二)

上章講到org.springframework.context.support.AbstractApplicationContext.refresh() ,這個方法完成了spring IOC容器的初始化, 在看程式碼前,我們首先要大概瞭解下spring BeanFactor

Spring 初始過程詳細分析[原始碼](一)

最近專案空閒期,來看下spring原始碼,把過程全部記錄下來, 方便想了解spring初始化過程的人,先從spring監聽器作為入口。 org.springframework.web.context.ContextLoaderListener 找到初始化spring

比特幣BTC原始碼分析1:地址生成過程

一、生成一個比特幣錢地址 二、根據原始碼整理比特幣地址生成過程 1、取得公鑰PubKey 2、使用 RIPEMD160(SHA256(PubKey)) 雜湊演算法,取公鑰並對其雜湊兩次 3、給雜湊加上地址生成演算法版本的字首 4、對於第二步生成的結果,使用SHA256(SHA256

以太坊ETH原始碼分析1:地址生成過程

一、生成一個以太坊錢包地址 通過以太坊命令列客戶端geth可以很簡單的獲得一個以太坊地址,如下: ~/go/src/github.com/ethereum/go-ethereum/build/bin$geth account new INFO [11-03|20:09:33.219]

spring IOC原始碼分析1

1.何謂Spring IOC         何謂Spring IOC?書上謂之“依賴注入”,那何謂“依賴注入”?         作為一個Java程式猿,應該遇到過這樣的問題,當你在程式碼中需要使用某個類提供的功能時,你首先需要new一個物件,給它傳遞必要的引數,然後才

Spring Session 原始碼分析1——springSessionRepositoryFilter

#Tomcat Session 對於session 是一個老生暢談的話題了,Session管理是JavaEE容器比較重要的一部分, Tomcat中主要由每個context容器內的一個Manager物件來管理session。對於這個manager物件的實現,可以

Bootstrap初始過程原始碼分析--netty客戶端的啟動

Bootstrap初始化過程 netty的客戶端引導類是Bootstrap,我們看一下spark的rpc中客戶端部分對Bootstrap的初始化過程 TransportClientFactory.createClient(InetSocketAddress address) 只需要貼出Bootstrap初始化

Mybatis原始碼分析1—— Mapper檔案解析

感覺CSDN對markdown的支援不夠友好,總是伴隨各種問題,很惱火! xxMapper.xml的解析主要由XMLMapperBuilder類完成,parse方法來完成解析: public void parse() { if (!configuration.isRes

jdk原始碼分析1java.lang.Object

java.lang.Object原始碼分析 public final native Class<?> getClass() public native int hashCode(); public boolean e

tensorflow原始碼分析1

variable類:        通過例項化Variable類可以新增一個變數到graph,在使用變數之前必須對變數顯示的初始化,初始化可以使用assign為變數賦值也可以通過變數本身的initializer方法。     &nb

ES5.6.2原始碼分析1:準備工作

1、gradle安裝 下載4.5版本,解壓後配置環境變數即可。 注:gradle安裝完成後, 為了加快依賴檔案的下載需要在使用者目錄中新建init.gradle檔案(讓全域性可見,build時會用到)。檔案的具體內容為: 目錄:C:\Users\admin.gradle

tensorflowV1.11-原始碼分析1

##</Users/deepmyhaspl/docs/tensorflow-src/tensorflow-r1.11>####[4]|<====configure.py=====>|## # Copyright 2017 The TensorFlow Authors. All

Django rest framework原始碼分析1----認證

目錄 一、基礎 1.1.安裝 兩種方式: pip install djangorestframework 1.2.需要先了解的一些知識 理解下面兩個知識點非常重要,django-rest-framework原始碼中到處都是基於CBV和麵向物件的封裝 (1)面向物件封裝的兩大特性

spring boot 2.0 原始碼分析

在學習spring boot 2.0原始碼之前,我們先利用spring initializr快速地建立一個基本的簡單的示例: 1.先從建立示例中的main函式開始讀起: package com.example; import org.springfra

Android6.0的Looper原始碼分析1

Android在Java標準執行緒模型的基礎上,提供了訊息驅動機制,用於多執行緒之間的通訊。而其具體實現就是Looper。 Android Looper的實現主要包括了3個概念:Message,MessageQueue,Handler,Looper。其中Message就是

libevent原始碼分析1

有過看nginx原始碼的基礎,現在來看libevent原始碼,感覺要輕鬆多了。。 第一篇文章,主要是還是介紹一些幾個重要的資料結構吧。。。。 首先是event結構:struct event { TAILQ_ENTRY (event) ev_next; //用於構成eve

Freescale i.MX6 Linux Ethernet Driver驅動原始碼分析1

最近需要在Freescale i.MX6上移植Ethernet AVB的核心patch,Ethernet AVB的Wiki:http://en.wikipedia.org/wiki/Audio_Video_Bridging,而Freescale原來已經在kernel 3.

Android系統原理與原始碼分析1:利用Java反射技術阻止通過按鈕關閉對話方塊

本文為原創,如需轉載,請註明作者和出處,謝謝!     眾所周知,AlertDialog類用於顯示對話方塊。關於AlertDialog的基本用法在這裡就不詳細介紹了,網上有很多,讀者可以自己搜尋。那

支援向量機—SMO演算法原始碼分析1

支援向量機的理論支援在此不細說,可以參考李航的《統計學習》,還有西瓜書。 簡化版SMO演算法處理小規模資料集 SMO演算法是一種啟發式演算法。此簡化版首先在資料集上遍歷每一個alpha,然後在剩下的alpha集合中隨機選擇另一個alpha,從而建立alpha