The important thing in life is to have a great aim , and the determination

          常用鏈接

          統(tǒng)計

          IT技術(shù)鏈接

          保險相關(guān)

          友情鏈接

          基金知識

          生活相關(guān)

          最新評論

          Spring源代碼解析(二):IoC容器在Web容器中的啟動

          上面我們分析了IOC容器本身的實現(xiàn),下面我們看看在典型的web環(huán)境中,Spring IOC容器是怎樣被載入和起作用的。
          簡單的說,在web容器中,通過ServletContext為Spring的IOC容器提供宿主環(huán)境,對應(yīng)的建立起一個IOC容器的體系。其中,首先需要建立的是根上下文,這個上下文持有的對象可以有業(yè)務(wù)對象,數(shù)據(jù)存取對象,資源,事物管理器等各種中間層對象。在這個上下文的基礎(chǔ)上,和web MVC相關(guān)還會有一個上下文來保存控制器之類的MVC對象,這樣就構(gòu)成了一個層次化的上下文結(jié)構(gòu)。在web容器中啟動Spring應(yīng)用程序就是一個建立這個上下文體系的過程。Spring為web應(yīng)用提供了上下文的擴展接口
          WebApplicationContext:
          Java代碼 復(fù)制代碼 收藏代碼
          1. public interface WebApplicationContext extends ApplicationContext {   
          2.     //這里定義的常量用于在ServletContext中存取根上下文   
          3.     String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";   
          4.     ......   
          5.     //對WebApplicationContext來說,需要得到Web容器的ServletContext   
          6.     ServletContext getServletContext();   
          7. }  

          而一般的啟動過程,Spring會使用一個默認的實現(xiàn),XmlWebApplicationContext - 這個上下文實現(xiàn)作為在web容器中的根上下文容器被建立起來,具體的建立過程在下面我們會詳細分析。
          Java代碼 復(fù)制代碼 收藏代碼
          1. public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {   
          2.   
          3.     /** 這是和web部署相關(guān)的位置信息,用來作為默認的根上下文bean定義信息的存放位置*/  
          4.     public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";   
          5.     public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";   
          6.     public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";   
          7.       
          8.     //我們又看到了熟悉的loadBeanDefinition,就像我們前面對IOC容器的分析中一樣,這個加載工程在容器的refresh()的時候啟動。   
          9.     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {   
          10.         //對于XmlWebApplicationContext,當然使用的是XmlBeanDefinitionReader來對bean定義信息來進行解析   
          11.         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);   
          12.   
          13.         beanDefinitionReader.setResourceLoader(this);   
          14.         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));   
          15.   
          16.         initBeanDefinitionReader(beanDefinitionReader);   
          17.         loadBeanDefinitions(beanDefinitionReader);   
          18.     }   
          19.   
          20.     protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {   
          21.     }   
          22.     //使用XmlBeanDefinitionReader來讀入bean定義信息   
          23.     protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {   
          24.         String[] configLocations = getConfigLocations();   
          25.         if (configLocations != null) {   
          26.             for (int i = 0; i < configLocations.length; i++) {   
          27.                 reader.loadBeanDefinitions(configLocations[i]);   
          28.             }   
          29.         }   
          30.     }   
          31.     //這里取得bean定義信息位置,默認的地方是/WEB-INF/applicationContext.xml   
          32.     protected String[] getDefaultConfigLocations() {   
          33.         if (getNamespace() != null) {   
          34.             return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};   
          35.         }   
          36.         else {   
          37.             return new String[] {DEFAULT_CONFIG_LOCATION};   
          38.         }   
          39.     }   
          40. }  

          對于一個Spring激活的web應(yīng)用程序,可以通過使用Spring代碼聲明式的指定在web應(yīng)用程序啟動時載入應(yīng)用程序上下文(WebApplicationContext),Spring的ContextLoader是提供這樣性能的類,我們可以使用 ContextLoaderServlet或者ContextLoaderListener的啟動時載入的Servlet來實例化Spring IOC容器 - 為什么會有兩個不同的類來裝載它呢,這是因為它們的使用需要區(qū)別不同的Servlet容器支持的Serlvet版本。但不管是 ContextLoaderSevlet還是 ContextLoaderListener都使用ContextLoader來完成實際的WebApplicationContext的初始化工作。這個ContextLoder就像是Spring Web應(yīng)用程序在Web容器中的加載器booter。當然這些Servlet的具體使用我們都要借助web容器中的部署描述符來進行相關(guān)的定義。
          下面我們使用ContextLoaderListener作為載入器作一個詳細的分析,這個Servlet的監(jiān)聽器是根上下文被載入的地方,也是整個 Spring web應(yīng)用加載上下文的第一個地方;從加載過程我們可以看到,首先從Servlet事件中得到ServletContext,然后可以讀到配置好的在web.xml的中的各個屬性值,然后ContextLoder實例化WebApplicationContext并完成其載入和初始化作為根上下文。當這個根上下文被載入后,它被綁定到web應(yīng)用程序的ServletContext上。任何需要訪問該ApplicationContext的應(yīng)用程序代碼都可以從WebApplicationContextUtils類的靜態(tài)方法來得到:
          Java代碼 復(fù)制代碼 收藏代碼
          1. WebApplicationContext getWebApplicationContext(ServletContext sc)  

          以Tomcat作為Servlet容器為例,下面是具體的步驟:
          1.Tomcat 啟動時需要從web.xml中讀取啟動參數(shù),在web.xml中我們需要對ContextLoaderListener進行配置,對于在web應(yīng)用啟動入口是在ContextLoaderListener中的初始化部分;從Spring MVC上看,實際上在web容器中維護了一系列的IOC容器,其中在ContextLoader中載入的IOC容器作為根上下文而存在于 ServletContext中。
          Java代碼 復(fù)制代碼 收藏代碼
          1. //這里對根上下文進行初始化。   
          2. public void contextInitialized(ServletContextEvent event) {   
          3.     //這里創(chuàng)建需要的ContextLoader   
          4.     this.contextLoader = createContextLoader();   
          5.     //這里使用ContextLoader對根上下文進行載入和初始化   
          6.     this.contextLoader.initWebApplicationContext(event.getServletContext());   
          7. }  

          通過ContextLoader建立起根上下文的過程,我們可以在ContextLoader中看到:
          Java代碼 復(fù)制代碼 收藏代碼
          1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext)   
          2.         throws IllegalStateException, BeansException {   
          3.     //這里先看看是不是已經(jīng)在ServletContext中存在上下文,如果有說明前面已經(jīng)被載入過,或者是配置文件有錯誤。   
          4.     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {   
          5.     //直接拋出異常   
          6.     .........   
          7.     }   
          8.      
          9.     ...............   
          10.     try {   
          11.         // 這里載入根上下文的父上下文   
          12.         ApplicationContext parent = loadParentContext(servletContext);   
          13.   
          14.         //這里創(chuàng)建根上下文作為整個應(yīng)用的上下文同時把它存到ServletContext中去,注意這里使用的ServletContext的屬性值是   
          15.         //ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,以后的應(yīng)用都是根據(jù)這個屬性值來取得根上下文的 - 往往作為自己上下文的父上下文   
          16.         this.context = createWebApplicationContext(servletContext, parent);   
          17.         servletContext.setAttribute(   
          18.                 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);   
          19.         ..........   
          20.   
          21.         return this.context;   
          22.     }   
          23.        ............   
          24. }  

          建立根上下文的父上下文使用的是下面的代碼,取決于在web.xml中定義的參數(shù):locatorFactorySelector,這是一個可選參數(shù):
          Java代碼 復(fù)制代碼 收藏代碼
          1. protected ApplicationContext loadParentContext(ServletContext servletContext)   
          2.         throws BeansException {   
          3.   
          4.     ApplicationContext parentContext = null;   
          5.   
          6.     String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);   
          7.     String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);   
          8.   
          9.     if (locatorFactorySelector != null) {   
          10.         BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);   
          11.         ........   
          12.         //得到根上下文的父上下文的引用   
          13.         this.parentContextRef = locator.useBeanFactory(parentContextKey);   
          14.         //這里建立得到根上下文的父上下文   
          15.         parentContext = (ApplicationContext) this.parentContextRef.getFactory();   
          16.     }   
          17.   
          18.     return parentContext;   
          19. }  

          得到根上下文的父上下文以后,就是根上下文的創(chuàng)建過程:
          Java代碼 復(fù)制代碼 收藏代碼
          1. protected WebApplicationContext createWebApplicationContext(   
          2.         ServletContext servletContext, ApplicationContext parent) throws BeansException {   
          3.     //這里需要確定我們載入的根WebApplication的類型,由在web.xml中配置的contextClass中配置的參數(shù)可以決定我們需要載入什么樣的ApplicationContext,   
          4.     //如果沒有使用默認的。   
          5.     Class contextClass = determineContextClass(servletContext);   
          6.     .........   
          7.     //這里就是上下文的創(chuàng)建過程   
          8.     ConfigurableWebApplicationContext wac =   
          9.             (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);   
          10.     //這里保持對父上下文和ServletContext的引用到根上下文中   
          11.     wac.setParent(parent);   
          12.     wac.setServletContext(servletContext);   
          13.   
          14.     //這里從web.xml中取得相關(guān)的初始化參數(shù)   
          15.     String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);   
          16.     if (configLocation != null) {   
          17.         wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,   
          18.                 ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));   
          19.     }   
          20.    //這里對WebApplicationContext進行初始化,我們又看到了熟悉的refresh調(diào)用。   
          21.     wac.refresh();   
          22.     return wac;   
          23. }  

          初始化根ApplicationContext后將其存儲到SevletContext中去以后,這樣就建立了一個全局的關(guān)于整個應(yīng)用的上下文。這個根上下文會被以后的DispatcherServlet初始化自己的時候作為自己ApplicationContext的父上下文。這個在對 DispatcherServlet做分析的時候我們可以看看到。

          3.完成對ContextLoaderListener的初始化以后, Tomcat開始初始化DispatchServlet,- 還記得我們在web.xml中隊載入次序進行了定義。DispatcherServlet會建立自己的ApplicationContext,同時建立這個自己的上下文的時候會從ServletContext中得到根上下文作為父上下文,然后再對自己的上下文進行初始化,并最后存到 ServletContext中去供以后檢索和使用。
          可以從DispatchServlet的父類FrameworkServlet的代碼中看到大致的初始化過程,整個ApplicationContext的創(chuàng)建過程和ContextLoder創(chuàng)建的過程相類似:
          Java代碼 復(fù)制代碼 收藏代碼
          1. protected final void initServletBean() throws ServletException, BeansException {   
          2.     .........   
          3.     try {   
          4.         //這里是對上下文的初始化過程。   
          5.         this.webApplicationContext = initWebApplicationContext();   
          6.         //在完成對上下文的初始化過程結(jié)束后,根據(jù)bean配置信息建立MVC框架的各個主要元素   
          7.         initFrameworkServlet();   
          8.     }   
          9.    ........   
          10. }  

          對initWebApplicationContext()調(diào)用的代碼如下:
          Java代碼 復(fù)制代碼 收藏代碼
          1. protected WebApplicationContext initWebApplicationContext() throws BeansException {   
          2.     //這里調(diào)用WebApplicationContextUtils靜態(tài)類來得到根上下文   
          3.     WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());   
          4.       
          5.     //創(chuàng)建當前DispatcherServlet的上下文,其上下文種類使用默認的在FrameworkServlet定義好的:DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;   
          6.     WebApplicationContext wac = createWebApplicationContext(parent);   
          7.     ........   
          8.     if (isPublishContext()) {   
          9.         //把當前建立的上下文存到ServletContext中去,注意使用的屬性名是和當前Servlet名相關(guān)的。   
          10.         String attrName = getServletContextAttributeName();   
          11.         getServletContext().setAttribute(attrName, wac);   
          12.     }   
          13.     return wac;   
          14. }  

          其中我們看到調(diào)用了WebApplicationContextUtils的靜態(tài)方法得到根ApplicationContext:
          Java代碼 復(fù)制代碼 收藏代碼
          1.     public static WebApplicationContext getWebApplicationContext(ServletContext sc) {   
          2.         //很簡單,直接從ServletContext中通過屬性名得到根上下文   
          3.         Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);   
          4.         .......   
          5.         return (WebApplicationContext) attr;   
          6.     }   
          7. 然后創(chuàng)建DispatcherServlet自己的WebApplicationContext:   
          8.     protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)   
          9.             throws BeansException {   
          10.         .......   
          11.         //這里使用了BeanUtils直接得到WebApplicationContext,ContextClass是前面定義好的DEFAULT_CONTEXT_CLASS =                              
          12.         //XmlWebApplicationContext.class;   
          13.         ConfigurableWebApplicationContext wac =   
          14.                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());   
          15.   
          16.         //這里配置父上下文,就是在ContextLoader中建立的根上下文   
          17.         wac.setParent(parent);   
          18.   
          19.         //保留ServletContext的引用和相關(guān)的配置信息。   
          20.         wac.setServletContext(getServletContext());   
          21.         wac.setServletConfig(getServletConfig());   
          22.         wac.setNamespace(getNamespace());   
          23.   
          24.         //這里得到ApplicationContext配置文件的位置   
          25.         if (getContextConfigLocation() != null) {   
          26.             wac.setConfigLocations(   
          27.                 StringUtils.tokenizeToStringArray(   
          28.                             getContextConfigLocation(), ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));   
          29.         }   
          30.           
          31.         //這里調(diào)用ApplicationContext的初始化過程,同樣需要使用refresh()   
          32.         wac.refresh();   
          33.         return wac;   
          34.     }  

          4. 然后就是DispatchServlet中對Spring MVC的配置過程,首先對配置文件中的定義元素進行配置 - 請注意這個時候我們的WebApplicationContext已經(jīng)建立起來了,也意味著DispatcherServlet有自己的定義資源,可以需要從web.xml中讀取bean的配置信息,通常我們會使用單獨的xml文件來配置MVC中各個要素定義,這里和web容器相關(guān)的加載過程實際上已經(jīng)完成了,下面的處理和普通的Spring應(yīng)用程序的編寫沒有什么太大的差別,我們先看看MVC的初始化過程:
          Java代碼 復(fù)制代碼 收藏代碼
          1. protected void initFrameworkServlet() throws ServletException, BeansException {   
          2.     initMultipartResolver();   
          3.     initLocaleResolver();   
          4.     initThemeResolver();   
          5.     initHandlerMappings();   
          6.     initHandlerAdapters();   
          7.     initHandlerExceptionResolvers();   
          8.     initRequestToViewNameTranslator();   
          9.     initViewResolvers();   
          10. }  

          5. 這樣MVC的框架就建立起來了,DispatchServlet對接受到的HTTP Request進行分發(fā)處理由doService()完成,具體的MVC處理過程我們在doDispatch()中完成,其中包括使用Command模式建立執(zhí)行鏈,顯示模型數(shù)據(jù)等,這些處理我們都可以在DispatcherServlet的代碼中看到:
          Java代碼 復(fù)制代碼 收藏代碼
          1. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {   
          2.     ......   
          3.     try {   
          4.         doDispatch(request, response);   
          5.     }   
          6.    .......   
          7. }  

          實際的請求分發(fā)由doDispatch(request,response)來完成:
          Java代碼 復(fù)制代碼 收藏代碼
          1. protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {   
          2.      .......   
          3.      // 這是Spring定義的執(zhí)行鏈,里面放了映射關(guān)系對應(yīng)的handler和定義的相關(guān)攔截器。   
          4.      HandlerExecutionChain mappedHandler = null;   
          5.       
          6.       ......   
          7.       try {   
          8.           //我們熟悉的ModelAndView在這里出現(xiàn)了。   
          9.           ModelAndView mv = null;   
          10.           try {   
          11.               processedRequest = checkMultipart(request);   
          12.   
          13.               //這里更具request中的參數(shù)和映射關(guān)系定義決定使用的handler   
          14.               mappedHandler = getHandler(processedRequest, false);   
          15.   
          16.               ......   
          17.               //這里是handler的調(diào)用過程,類似于Command模式中的execute.   
          18.               HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());   
          19.               mv = ha.handle(processedRequest, response, mappedHandler.getHandler());   
          20.   
          21.               .......   
          22.           //這里將模型數(shù)據(jù)通過視圖進行展現(xiàn)   
          23.           if (mv != null && !mv.wasCleared()) {   
          24.               render(mv, processedRequest, response);   
          25.           }   
          26.             ........   
          27.   }  

          這樣具體的MVC模型的實現(xiàn)就由bean配置文件里定義好的view resolver,handler這些類來實現(xiàn)用戶代碼的功能。
          總結(jié)上面的過程,我們看到在web容器中,ServletContext可以持有一系列的web上下文,而在整個web上下文中存在一個根上下文來作為其它 Servlet上下文的父上下文。這個根上下文是由ContextLoader載入并進行初始化的,對于我們的web應(yīng)用, DispatcherSerlvet載入并初始化自己的上下文,這個上下文的父上下文是根上下文,并且我們也能從ServletContext中根據(jù) Servlet的名字來檢索到我們需要的對應(yīng)于這個Servlet的上下文,但是根上下文的名字是由Spring唯一確定的。這個 DispactcherServlet建立的上下文就是我們開發(fā)Spring MVC應(yīng)用的IOC容器。
          具體的web請求處理在上下文體系建立完成以后由DispactcherServlet來完成,上面對MVC的運作做了一個大致的描述,下面我們會具體就SpringMVC的框架實現(xiàn)作一個詳細的分析。

          posted on 2011-10-26 23:36 鴻雁 閱讀(304) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 石首市| 苗栗县| 昆明市| 通许县| 赤水市| 青龙| 铁岭县| 崇左市| 前郭尔| 全南县| 垦利县| 碌曲县| 白水县| 东乌珠穆沁旗| 大余县| 鄢陵县| 田阳县| 仲巴县| 双城市| 巴林左旗| 宜兴市| 迁安市| 阜平县| 元阳县| 东至县| 东乌珠穆沁旗| 百色市| 邹平县| 邢台市| 泰安市| 马公市| 兴国县| 石泉县| 太谷县| 远安县| 商丘市| 商洛市| 开原市| 英山县| 扶风县| 陇西县|