spring 啓動過程

 

1.首先,對於一個web應用,其部署在web容器中,web容器提供其一個全局的上下文環境,這個上下文就是ServletContext,其爲後面的spring IoC容器提供宿主環境;

  1. 其次,在web.xml中會提供有contextLoaderListener。在web容器啓動時,會觸發容器初始化事件,此時contextLoaderListener會監聽到這個事件,其contextInitialized方法會被調用,在這個方法中,spring會初始化一個啓動上下文,這個上下文被稱爲根上下文,即WebApplicationContext,這是一個接口類,確切的說,其實際的實現類是XmlWebApplicationContext。這個就是spring的IoC容器,其對應的Bean定義的配置由web.xml中的context-param標籤指定。在這個IoC容器初始化完畢後,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE爲屬性Key,將其存儲到ServletContext中,便於獲取;

  2. 再次,contextLoaderListener監聽器初始化完畢後,開始初始化web.xml中配置的Servlet,這個servlet可以配置多個,以最常見的DispatcherServlet爲例,這個servlet實際上是一個標準的前端控制器,用以轉發、匹配、處理每個servlet請求。DispatcherServlet上下文在初始化的時候會建立自己的IoC上下文,用以持有spring mvc相關的bean。在建立DispatcherServlet自己的IoC上下文時,會利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先從ServletContext中獲取之前的根上下文(即WebApplicationContext)作爲自己上下文的parent上下文。有了這個parent上下文之後,再初始化自己持有的上下文。這個DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化處理器映射、視圖解析等。這個servlet自己持有的上下文默認實現類也是mlWebApplicationContext。初始化完畢後,spring以與servlet的名字相關(此處不是簡單的以servlet名爲Key,而是通過一些轉換,具體可自行查看源碼)的屬性爲屬性Key,也將其存到ServletContext中,以便後續使用。這樣每個servlet就持有自己的上下文,即擁有自己獨立的bean空間,同時各個servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定義的那些bean。

一、先說ServletContext

  javaee標準規定了,servlet容器需要在應用項目啓動時,給應用項目初始化一個ServletContext作爲公共環境容器存放公共信息。ServletContext中的信息都是由容器提供的。

舉例:

通過自定義contextListener獲取web.xml中配置的參數

1.容器啓動時,找到配置文件中的context-param作爲鍵值對放到ServletContext中

2.然後找到listener,容器調用它的contextInitialized(ServletContextEvent event)方法,執行其中的操作

例如:在web.xml中配置

 

<context-param>
   <param-name>key</param-name>
   <param-value>value123</param-value>
</context-param>
<listener> 
   <listener-class>com.brolanda.contextlistener.listener.ContextListenerTest</listener-class>
</listener>

 

配置好之後,在該類中獲取對應的參數信息

 

package com.brolanda.contextlistener.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class ContextListenerTest implements ServletContextListener {
    
    public void contextDestroyed(ServletContextEvent event) {
        System.out.println("*************destroy ContextListener*************");
    }
    
    @SuppressWarnings("unused")
    public void contextInitialized(ServletContextEvent event) {
        System.out.println("*************init ContextListener*************");
        ServletContext servletContext = event.getServletContext();
        System.out.println("key:"+servletContext.getInitParameter("key"));
    }
    
}

 

執行流程:

  web.xml在<context-param></context-param>標籤中聲明應用範圍內的初始化參數

1.啓動一個WEB項目的時候,容器(如:Tomcat)會去讀它的配置文件web.xml.讀兩個節點: <listener></listener> 和 <context-param></context-param>

2.緊接着,容器創建一個ServletContext(上下文)。在該應用內全局共享。

3.容器將<context-param></context-param>轉化爲鍵值對,並交給ServletContext.

4.容器創建<listener></listener>中的類實例,即創建監聽.該監聽器必須實現自ServletContextListener接口

5.在監聽中會有contextInitialized(ServletContextEvent event)初始化方法

              在這個方法中獲得ServletContext = ServletContextEvent.getServletContext();
            “context-param的值” = ServletContext.getInitParameter("context-param的鍵");

6.得到這個context-param的值之後,你就可以做一些操作了.注意,這個時候你的WEB項目還沒有完全啓動完成.這個動作會比所有的Servlet都要早.換句話說,這個時候,你對<context-param>中的鍵值做的操作,將在你的WEB項目完全啓動之前被執行.

web.xml中可以定義兩種參數:

    一個是全局參數(ServletContext),通過<context-param></context-param>

    一個是servlet參數,通過在servlet中聲明        <init-param>

                                                                          <param-name>param1</param-name>

                                                                          <param-value>avalible in servlet init()</param-value>   

                                                                    </init-param> 

 

    第一種參數在servlet裏面可以通過getServletContext().getInitParameter("context/param")得到

 

    第二種參數只能在servlet的init()方法中通過this.getInitParameter("param1")取得

 

二、spring上下文容器配置

  spring爲我們提供了實現ServletContextListener接口的上下文初始化監聽器:org.springframework.web.context.ContextLoaderListener

  spring爲我們提供的IOC容器,需要我們指定容器的配置文件,然後由該監聽器初始化並創建該容器。要求你指定配置文件的地址及文件名稱,一定要使用:contextConfigLocation作爲參數名稱。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml,/WEB-INF/action-servlet.xml,/WEB-INF/jason-servlet.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

copycode.gifuploading.4e448015.gif正在上傳…重新上傳取消

copycode.gifuploading.4e448015.gif正在上傳…重新上傳取消

該監聽器,默認讀取/WEB-INF/下的applicationContext.xml文件。但是通過context-param指定配置文件路徑後,便會去你指定的路徑下讀取對應的配置文件,並進行初始化。

三、spring上下文容器配置後,初始化了什麼?

  既然,ServletContext是由Servlet容器初始化的,那spring的ContextLoaderListener又做了什麼初始化呢?

        1、servlet容器啓動,爲應用創建一個“全局上下文環境”:ServletContext

        2、容器調用web.xml中配置的contextLoaderListener,初始化WebApplicationContext上下文環境(即IOC容器),加載context-param指定的配置文件信息到IOC容器中。WebApplicationContext在ServletContext中以鍵值對的形式保存

        3、容器初始化web.xml中配置的servlet,爲其初始化自己的上下文信息servletContext,並加載其設置的配置信息到該上下文中。將WebApplicationContext設置爲它的父容器。

        4、此後的所有servlet的初始化都按照3步中方式創建,初始化自己的上下文環境,將WebApplicationContext設置爲自己的父上下文環境。

 

 

       對於作用範圍而言,在DispatcherServlet中可以引用由ContextLoaderListener所創建的ApplicationContext中的內容,而反過來不行。

       當Spring在執行ApplicationContext的getBean時,如果在自己context中找不到對應的bean,則會在父ApplicationContext中去找。這也解釋了爲什麼我們可以在DispatcherServlet中獲取到由ContextLoaderListener對應的ApplicationContext中的bean。

 

 四、spring配置時:<context:exclude-filter>的使用原因,爲什麼在applicationContext.xml中排除controller,而在spring-mvc.xml中incloud這個controller

    既然知道了spring的啓動流程,那麼web容器初始化webApplicationContext時作爲公共的上下文環境,只需要將service、dao等的配置信息在這裏加載,而servlet自己的上下文環境信息不需要加載。故,在applicationContext.xml中將@Controller註釋的組件排除在外,而在dispatcherServlet加載的配置文件中將@Controller註釋的組件加載進來,方便dispatcherServlet進行控制和查找。故,配置如下:

 

applicationContext.mxl中:

 <context:component-scan base-package="com.linkage.edumanage">

      <context:exclude-filter expression="org.springframework.stereotype.Controller"    type="annotation" /> 

 </context:component-scan>

spring-mvc.xml中:

  <context:component-scan base-package="com.brolanda.cloud"   use-default-filters="false"> 

      <context:include-filter expression="org.springframework.stereotype.Controller"    type="annotation" /> 

 </context:component-scan>

談談springmvc的優化

  上面我們已經對springmvc的工作原理和源碼進行了分析,在這個過程發現了幾個優化點:

  1.controller如果能保持單例,儘量使用單例,這樣可以減少創建對象和回收對象的開銷.也就是說,如果controller的類變量和實例變量可以以方法形參聲明的儘量以方法的形參聲明,不要以類變量和實例變量聲明,這樣可以避免線程安全問題.

  2.處理request的方法中的形參務必加上@RequestParam註解,這樣可以避免springmvc使用asm框架讀取class文件獲取方法參數名的過程.即便springmvc對讀取出的方法參數名進行了緩存,如果不要讀取class文件當然是更加好.

  3.閱讀源碼的過程中,發現springmvc並沒有對處理url的方法進行緩存,也就是說每次都要根據請求url去匹配controller中的方法url,如果把url和method的關係緩存起來,會不會帶來性能上的提升呢?有點噁心的是,負責解析url和method對應關係的ServletHandlerMethodResolver是一個private的內部類,不能直接繼承該類增強代碼,必須要該代碼後重新編譯.當然,如果緩存起來,必須要考慮緩存的線程安全問題.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章