前言: springmvc是什麼,能作什麼,即便是新手,也應該有所瞭解,俺這裏就不一一贅述了。java
首先咱們須要引入javaee的一些概念,爲何要引入它呢?不明覺厲,無論是什麼樣的web框架,那麼這玩意是基礎。不用懷疑。因此無論如何變化,咱們要謹記,是跟javaee的東西打交道。git
須要你們自行掌握的概念有: servlet容器,包括ServletContext,Listener,ServletConfig。 session,request這些就不說了。github
工欲善其事,必先利其器。因此咱們須要先折騰一個springmvc的工程。我這裏使用maven模板去建立。web
選擇這一個模板spring
- org.jetbrains.idea.maven.model.MavenArchetype
工程建立完後咱們須要引入對應的jar包。我這裏按照最小化配置來。express
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.2.RELEASE</version> </dependency>
加入webmvc(ps: 這裏使用了webmvc,它裏面又內置了好幾個。包括咱們熟悉的servlet, spring-beans,context等等)api
具體詳見webmvc的pom。這裏再也不累述,咱們繼續配置spring-mvc
最後加完的jar包有這麼多,實際上由於servlet-api是provided的 因此這裏沒有出現,但並不意味着他不存在。。tomcat
在web.xml中添加session
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
在resources目錄中建立 applicationContext, 添加掃包(這裏不掃描Controller註解的)
<!-- configure auto scan --> <context:component-scan base-package="org.spring"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan>
在WEB-INF中建立spring-servlet.xml,加入掃描Controller的掃包配置和激活RequestMappingHanlder適配器
<context:component-scan base-package="org.spring.*.controller" /> <mvc:annotation-driven />
到此咱們的工程已經能夠在容器中跑起來了。固然,上面的並非重點,接下來纔是重頭戲。跟着咱們的代碼飛吧。
啓動工程,進入 ContextLoaderListener , 咱們會發現他是實現了繼承自ContextLoader,實現的是ServletContextListener接口。
這裏多說一句。
有好奇的小夥伴就會問,繼承ContextLoader 的做用是什麼呀?
且聽我一一道來。
ContextLoaderListener他的主要做用是在web應用程序啓動時載入ioc容器。具體詳見下面會分解。而ContextLoader正是對ioc容器初始化進行了資源加載等操做,屬於靈魂級的類
ok, 咱們繼續說監聽器。
咱們打開ServletContextListener接口。發現以下方法, 見名知意。init開頭的就是初始化執行的動做,另一個則是銷燬
銷燬暫且不論,初始化纔是咱們的重頭戲。咱們看到他最後執行了這樣的操做。其實就是調用了ContextLoader.initWebApplicationContext(ServletContext servletContext) 方法。具體源碼以下
廢話很少說,咱們先看initWebApplicationContext(event.getServletContext()); 作了什麼
根源:
首先建立一個抽象的ClassPathResource,這玩意有點相似於java的File對象,是一個抽象的東東。
而後將其load進來。咱們看看這個properties文件中的內容。
# Default WebApplicationContext implementation class for ContextLoader. # Used as fallback when no explicit context implementation has been specified as context-param. # Not meant to be customized by application developers. org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
能夠看得出來,這是一個ApplicationContext的實現類。這就是spring真正的上下文類。咱們看看他的繼承體系結構圖
進入正題。開始建立它
進入createWebApplicationContext。
這個時候從servletContext中去取contextClass 是空。那麼他從配置文件中根據這個類名去獲取對應的value值。也就是org.springframework.web.context.support.XmlWebApplicationContext 而後使用Class.forName的方式將其加載
而後拿到加載完畢的類,使用BeanUtils.instantiateClass(contextClass); 初始化
等同於Object object = new Object()這種方式。由於它使用的是反射。
這裏把上面的流程屢一下。。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { // 若是上下文中存在 WebApplicationContext 則拋異常。說明WebApplicationContext已經被初始化過了。 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. if (this.context == null) { // 這裏去初始化上下文。具體流程上面已經有講到 this.context = createWebApplicationContext(servletContext); } // 咱們已知 從properties中讀取到的類爲XmlWebApplicationContext 且 XmlWebApplicationContext 是 ConfigurableWebApplicationContext的子類,因此這個判斷會進去 if (this.context instanceof ConfigurableWebApplicationContext) { // 強轉 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; // cwac還沒有被激活,目前尚未進行配置文件加載 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); } } servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }
而後咱們進入加載配置文件那一段
有IOC經驗的朋友應該知道,只有真正刷新IOC容器的時候纔是IOC容器真正初始化並註冊各類bean的時候。這裏再也不贅述。後續文章會對ioc容器進行覆蓋式轟炸。。這裏只須要知道概念就OK。
總結: 經過講解ContextLoaderListener,咱們會發現這個東西實際上是將javaEE的容器和spring的IOC整合到了一塊兒,以後會把建立完成的上下文對象塞入ServletContext上下文中,注意這裏的ServletContext是JavaEE的上下文。也就是說spring本身的上下文在javaee的上下文中只是一個屬性。。。至於爲何是這樣呢。你們一想就明白了。由於javaee是最外層的規範,而咱們的web容器(tomcat, jetty等)實現了這一規範。而spring呢,是須要依託於javaee容器來運行。那麼, 就意味着, Spring的IOC容器對於javaEE來說只是一個屬性。。沒錯。因此javaEE的容器就是spring容器的一個屬性。。
本文就寫到了這裏,但願能幫到你們,謝謝。若是對本文有疑問的請你們留言給我,我會及時回覆。
最後打個小廣告,歡迎你們加入qq羣:77174608 一塊兒討論學習。。
文章代碼已上傳至GitHub: 點我進入
gitee地址 : 點此進入