-
首先,對於一個web應用,其部署在web容器中,web容器提供其一個全局的上下文環境,這個上下文就是ServletContext,其為后面的spring IoC容器提供宿主環境;
-
其次,在web.xml中會提供有contextLoaderListener。在web容器啟動時,會觸發容器初始化事件,此時contextLoaderListener會監聽到這個事件,其contextInitialized方法會被調用,在這個方法中,spring會初始化一個啟動上下文,這個上下文被稱為根上下文,即WebApplicationContext,這是一個接口類,確切的說,其實際的實現類是XmlWebApplicationContext。這個就是spring的IoC容器,其對應的Bean定義的配置由web.xml中的context-param標簽指定。在這個IoC容器初始化完畢后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE為屬性Key,將其存儲到ServletContext中,便於獲取;
-
再次,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中的信息都是由容器提供的。
舉例:
<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>標簽中聲明應用范圍內的初始化參數
3.容器將<context-param></context-param>轉化為鍵值對,並交給ServletContext.
5.在監聽中會有contextInitialized(ServletContextEvent event)初始化方法
“context-param的值” = ServletContext.getInitParameter("context-param的鍵");
二、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>
該監聽器,默認讀取/WEB-INF/下的applicationContext.xml文件。但是通過context-param指定配置文件路徑后,便會去你指定的路徑下讀取對應的配置文件,並進行初始化。
三、spring上下文容器配置后,初始化了什么?
既然,ServletContext是由Servlet容器初始化的,那spring的ContextLoaderListener又做了什么初始化呢?

四、spring配置時:<context:exclude-filter>的使用原因,為什么在applicationContext.xml中排除controller,而在spring-mvc.xml中incloud這個controller
談談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的內部類,不能直接繼承該類增強代碼,必須要該代碼后重新編譯.當然,如果緩存起來,必須要考慮緩存的線程安全問題.