一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Spring源代碼分析之(二):IOC容器在web容器中的啟動(dòng)

 毀滅號 2010-09-03

Spring源代碼分析之(二):IOC容器在web容器中的啟動(dòng)

以下引用自博客:http://jiwenke-spring./ 

  上面我們分析了IOC容器本身的實(shí)現(xiàn),下面我們看看在典型的web環(huán)境中,Spring IOC容器是怎樣被載入和起作用的。

  簡單的說,在web容器中,通過ServletContext為Spring的IOC容器提供宿主環(huán)境,對應(yīng)的建立起一個(gè)IOC容器的體系。其中,首先需要建立的是根上下文,這個(gè)上下文持有的對象可以有業(yè)務(wù)對象,數(shù)據(jù)存取對象,資源,事物管理器等各種中間層對象。在這個(gè)上下文的基礎(chǔ)上,和web MVC相關(guān)還會(huì)有一個(gè)上下文來保存控制器之類的MVC對象,這樣就構(gòu)成了一個(gè)層次化的上下文結(jié)構(gòu)。在web容器中啟動(dòng)Spring應(yīng)用程序就是一個(gè)建立這個(gè)上下文體系的過程。Spring為web應(yīng)用提供了上下文的擴(kuò)展接口

  WebApplicationContext:

  代碼

public interface WebApplicationContext extends ApplicationContext {  

  //這里定義的常量用于在ServletContext中存取根上下文  

  String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";  

  ......  

  //對WebApplicationContext來說,需要得到Web容器的ServletContext  

  ServletContext getServletContext();  

}  

  而一般的啟動(dòng)過程,Spring會(huì)使用一個(gè)默認(rèn)的實(shí)現(xiàn),XmlWebApplicationContext - 這個(gè)上下文實(shí)現(xiàn)作為在web容器中的根上下文容器被建立起來,具體的建立過程在下面我們會(huì)詳細(xì)分析。

  代碼

public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {  

  /** 這是和web部署相關(guān)的位置信息,用來作為默認(rèn)的根上下文bean定義信息的存放位置*/ 

  public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";  

  public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";  

  public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";  

  //我們又看到了熟悉的loadBeanDefinition,就像我們前面對IOC容器的分析中一樣,這個(gè)加載工程在容器的refresh()的時(shí)候啟動(dòng)。  

  protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {  

    //對于XmlWebApplicationContext,當(dāng)然使用的是XmlBeanDefinitionReader來對bean定義信息來進(jìn)行解析  

    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  

    beanDefinitionReader.setResourceLoader(this);  

    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  

    initBeanDefinitionReader(beanDefinitionReader);  

    loadBeanDefinitions(beanDefinitionReader);  

  }  

  protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {  

  }  

  //使用XmlBeanDefinitionReader來讀入bean定義信息  

  protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  

    String[] configLocations = getConfigLocations();  

    if (configLocations != null) {  

      for (int i = 0; i < configLocations.length; i++) {  

        reader.loadBeanDefinitions(configLocations[i]);  

      }  

    }  

  }  

  //這里取得bean定義信息位置,默認(rèn)的地方是/WEB-INF/applicationContext.xml  

  protected String[] getDefaultConfigLocations() {  

    if (getNamespace() != null) {  

      return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};  

    }  

    else {  

      return new String[] {DEFAULT_CONFIG_LOCATION};  

    }  

  }  

}

對于一個(gè)Spring激活的web應(yīng)用程序,可以通過使用Spring代碼聲明式的指定在web應(yīng)用程序啟動(dòng)時(shí)載入應(yīng)用程序上下文(WebApplicationContext),Spring的ContextLoader是提供這樣性能的類,我們可以使用 ContextLoaderServlet或者ContextLoaderListener的啟動(dòng)時(shí)載入的Servlet來實(shí)例化Spring IOC容器 - 為什么會(huì)有兩個(gè)不同的類來裝載它呢,這是因?yàn)樗鼈兊氖褂眯枰獏^(qū)別不同的Servlet容器支持的Serlvet版本。但不管是 ContextLoaderSevlet還是 ContextLoaderListener都使用ContextLoader來完成實(shí)際的WebApplicationContext的初始化工作。這個(gè)ContextLoder就像是Spring Web應(yīng)用程序在Web容器中的加載器booter。當(dāng)然這些Servlet的具體使用我們都要借助web容器中的部署描述符來進(jìn)行相關(guān)的定義。

  下面我們使用ContextLoaderListener作為載入器作一個(gè)詳細(xì)的分析,這個(gè)Servlet的監(jiān)聽器是根上下文被載入的地方,也是整個(gè) Spring web應(yīng)用加載上下文的第一個(gè)地方;從加載過程我們可以看到,首先從Servlet事件中得到ServletContext,然后可以讀到配置好的在web.xml的中的各個(gè)屬性值,然后ContextLoder實(shí)例化WebApplicationContext并完成其載入和初始化作為根上下文。當(dāng)這個(gè)根上下文被載入后,它被綁定到web應(yīng)用程序的ServletContext上。任何需要訪問該ApplicationContext的應(yīng)用程序代碼都可以從WebApplicationContextUtils類的靜態(tài)方法來得到:

  代碼

  WebApplicationContext getWebApplicationContext(ServletContext sc)  

  以Tomcat作為Servlet容器為例,下面是具體的步驟:

  1.Tomcat 啟動(dòng)時(shí)需要從web.xml中讀取啟動(dòng)參數(shù),在web.xml中我們需要對ContextLoaderListener進(jìn)行配置,對于在web應(yīng)用啟動(dòng)入口是在ContextLoaderListener中的初始化部分;從Spring MVC上看,實(shí)際上在web容器中維護(hù)了一系列的IOC容器,其中在ContextLoader中載入的IOC容器作為根上下文而存在于 ServletContext中。

代碼

//這里對根上下文進(jìn)行初始化?! ?/p>

public void contextInitialized(ServletContextEvent event) {  

  //這里創(chuàng)建需要的ContextLoader  

  this.contextLoader = createContextLoader();  

  //這里使用ContextLoader對根上下文進(jìn)行載入和初始化  

  this.contextLoader.initWebApplicationContext(event.getServletContext());  

}  

  通過ContextLoader建立起根上下文的過程,我們可以在ContextLoader中看到:

  代碼

public WebApplicationContext initWebApplicationContext(ServletContext servletContext)  

    throws IllegalStateException, BeansException {  

  //這里先看看是不是已經(jīng)在ServletContext中存在上下文,如果有說明前面已經(jīng)被載入過,或者是配置文件有錯(cuò)誤?! ?/p>

  if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  

  //直接拋出異?! ?/p>

  .........  

  }  

  ...............  

  try {  

    // 這里載入根上下文的父上下文  

    ApplicationContext parent = loadParentContext(servletContext);  

    //這里創(chuàng)建根上下文作為整個(gè)應(yīng)用的上下文同時(shí)把它存到ServletContext中去,注意這里使用的ServletContext的屬性值是  

    //ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,以后的應(yīng)用都是根據(jù)這個(gè)屬性值來取得根上下文的 - 往往作為自己上下文的父上下文  

    this.context = createWebApplicationContext(servletContext, parent);  

    servletContext.setAttribute(  

        WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  

    ..........  

    return this.context;  

  }  

    ............  

}  

建立根上下文的父上下文使用的是下面的代碼,取決于在web.xml中定義的參數(shù):locatorFactorySelector,這是一個(gè)可選參數(shù):

  代碼

protected ApplicationContext loadParentContext(ServletContext servletContext)  

    throws BeansException {  

  ApplicationContext parentContext = null;  

  String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);  

  String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);  

  if (locatorFactorySelector != null) {  

    BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);  

    ........  

    //得到根上下文的父上下文的引用  

    this.parentContextRef = locator.useBeanFactory(parentContextKey);  

    //這里建立得到根上下文的父上下文  

    parentContext = (ApplicationContext) this.parentContextRef.getFactory();  

  }  

  return parentContext;  

}  

  得到根上下文的父上下文以后,就是根上下文的創(chuàng)建過程:

  代碼

protected WebApplicationContext createWebApplicationContext(  

    ServletContext servletContext, ApplicationContext parent) throws BeansException {  

  //這里需要確定我們載入的根WebApplication的類型,由在web.xml中配置的contextClass中配置的參數(shù)可以決定我們需要載入什么樣的ApplicationContext,  

  //如果沒有使用默認(rèn)的?! ?/p>

  Class contextClass = determineContextClass(servletContext);  

  .........  

  //這里就是上下文的創(chuàng)建過程  

  ConfigurableWebApplicationContext wac =  

      (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  

  //這里保持對父上下文和ServletContext的引用到根上下文中  

  wac.setParent(parent);  

  wac.setServletContext(servletContext);  

  //這里從web.xml中取得相關(guān)的初始化參數(shù)  

  String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);  

  if (configLocation != null) {  

    wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,  

        ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));  

  }  

  //這里對WebApplicationContext進(jìn)行初始化,我們又看到了熟悉的refresh調(diào)用?! ?/p>

  wac.refresh();  

  return wac;  

}

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

  3.完成對ContextLoaderListener的初始化以后, Tomcat開始初始化DispatchServlet,- 還記得我們在web.xml中隊(duì)載入次序進(jìn)行了定義。DispatcherServlet會(huì)建立自己的ApplicationContext,同時(shí)建立這個(gè)自己的上下文的時(shí)候會(huì)從ServletContext中得到根上下文作為父上下文,然后再對自己的上下文進(jìn)行初始化,并最后存到 ServletContext中去供以后檢索和使用。

  可以從DispatchServlet的父類FrameworkServlet的代碼中看到大致的初始化過程,整個(gè)ApplicationContext的創(chuàng)建過程和ContextLoder創(chuàng)建的過程相類似:

  代碼

protected final void initServletBean() throws ServletException, BeansException {  

  .........  

  try {  

    //這里是對上下文的初始化過程?! ?/p>

    this.webApplicationContext = initWebApplicationContext();  

    //在完成對上下文的初始化過程結(jié)束后,根據(jù)bean配置信息建立MVC框架的各個(gè)主要元素  

    initFrameworkServlet();  

  }  

  ........  

} 

  對initWebApplicationContext()調(diào)用的代碼如下:

  代碼

protected WebApplicationContext initWebApplicationContext() throws BeansException {  

  //這里調(diào)用WebApplicationContextUtils靜態(tài)類來得到根上下文  

  WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());  

  //創(chuàng)建當(dāng)前DispatcherServlet的上下文,其上下文種類使用默認(rèn)的在FrameworkServlet定義好的:DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;  

  WebApplicationContext wac = createWebApplicationContext(parent);  

  ........  

  if (isPublishContext()) {  

    //把當(dāng)前建立的上下文存到ServletContext中去,注意使用的屬性名是和當(dāng)前Servlet名相關(guān)的?! ?/p>

    String attrName = getServletContextAttributeName();  

    getServletContext().setAttribute(attrName, wac);  

  }  

  return wac;  

} 

其中我們看到調(diào)用了WebApplicationContextUtils的靜態(tài)方法得到根ApplicationContext:

  代碼

  public static WebApplicationContext getWebApplicationContext(ServletContext sc) {  

    //很簡單,直接從ServletContext中通過屬性名得到根上下文  

    Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);  

    .......  

    return (WebApplicationContext) attr;  }  

  然后創(chuàng)建DispatcherServlet自己的WebApplicationContext:  

  protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)  

      throws BeansException {  

    .......  

    //這里使用了BeanUtils直接得到WebApplicationContext,ContextClass是前面定義好的DEFAULT_CONTEXT_CLASS =               

    //XmlWebApplicationContext.class;  

    ConfigurableWebApplicationContext wac =  

        (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());  

    //這里配置父上下文,就是在ContextLoader中建立的根上下文  

    wac.setParent(parent);  

    //保留ServletContext的引用和相關(guān)的配置信息?! ?/p>

    wac.setServletContext(getServletContext());  

    wac.setServletConfig(getServletConfig());  

    wac.setNamespace(getNamespace());  

    //這里得到ApplicationContext配置文件的位置  

    if (getContextConfigLocation() != null) {  

      wac.setConfigLocations(  

        StringUtils.tokenizeToStringArray(  

              getContextConfigLocation(), ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));  

    }  

    //這里調(diào)用ApplicationContext的初始化過程,同樣需要使用refresh()  

    wac.refresh();  

    return wac;  

  }

4. 然后就是DispatchServlet中對Spring MVC的配置過程,首先對配置文件中的定義元素進(jìn)行配置 - 請注意這個(gè)時(shí)候我們的WebApplicationContext已經(jīng)建立起來了,也意味著DispatcherServlet有自己的定義資源,可以需要從web.xml中讀取bean的配置信息,通常我們會(huì)使用單獨(dú)的xml文件來配置MVC中各個(gè)要素定義,這里和web容器相關(guān)的加載過程實(shí)際上已經(jīng)完成了,下面的處理和普通的Spring應(yīng)用程序的編寫沒有什么太大的差別,我們先看看MVC的初始化過程:

  代碼

protected void initFrameworkServlet() throws ServletException, BeansException {  

  initMultipartResolver();  

  initLocaleResolver();  

  initThemeResolver();  

  initHandlerMappings();  

  initHandlerAdapters();  

  initHandlerExceptionResolvers();  

  initRequestToViewNameTranslator();  

  initViewResolvers();  

} 

  5. 這樣MVC的框架就建立起來了,DispatchServlet對接受到的HTTP Request進(jìn)行分發(fā)處理由doService()完成,具體的MVC處理過程我們在doDispatch()中完成,其中包括使用Command模式建立執(zhí)行鏈,顯示模型數(shù)據(jù)等,這些處理我們都可以在DispatcherServlet的代碼中看到:

  代碼

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  

  ......  

  try {  

    doDispatch(request, response);  

  }  

  .......  

} 

  實(shí)際的請求分發(fā)由doDispatch(request,response)來完成:

代碼

protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {  

   .......  

   // 這是Spring定義的執(zhí)行鏈,里面放了映射關(guān)系對應(yīng)的handler和定義的相關(guān)攔截器?! ?/p>

   HandlerExecutionChain mappedHandler = null;  

   ......  

   try {  

     //我們熟悉的ModelAndView在這里出現(xiàn)了?! ?/p>

     ModelAndView mv = null;  

     try {  

       processedRequest = checkMultipart(request);  

       //這里更具request中的參數(shù)和映射關(guān)系定義決定使用的handler  

       mappedHandler = getHandler(processedRequest, false);  

       ......  

       //這里是handler的調(diào)用過程,類似于Command模式中的execute.  

       HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  

       mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  

       .......  

     //這里將模型數(shù)據(jù)通過視圖進(jìn)行展現(xiàn)  

     if (mv != null && !mv.wasCleared()) {  

       render(mv, processedRequest, response);  

     }  

      ........  

 } 

  這樣具體的MVC模型的實(shí)現(xiàn)就由bean配置文件里定義好的view resolver,handler這些類來實(shí)現(xiàn)用戶代碼的功能。

  總結(jié)上面的過程,我們看到在web容器中,ServletContext可以持有一系列的web上下文,而在整個(gè)web上下文中存在一個(gè)根上下文來作為其它 Servlet上下文的父上下文。這個(gè)根上下文是由ContextLoader載入并進(jìn)行初始化的,對于我們的web應(yīng)用, DispatcherSerlvet載入并初始化自己的上下文,這個(gè)上下文的父上下文是根上下文,并且我們也能從ServletContext中根據(jù) Servlet的名字來檢索到我們需要的對應(yīng)于這個(gè)Servlet的上下文,但是根上下文的名字是由Spring唯一確定的。這個(gè) DispactcherServlet建立的上下文就是我們開發(fā)Spring MVC應(yīng)用的IOC容器。

  具體的web請求處理在上下文體系建立完成以后由DispactcherServlet來完成,上面對MVC的運(yùn)作做了一個(gè)大致的描述,下面我們會(huì)具體就SpringMVC的框架實(shí)現(xiàn)作一個(gè)詳細(xì)的分析。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    久久大香蕉一区二区三区| 九九热最新视频免费观看| 欧美在线观看视频三区| 国产偷拍盗摄一区二区| 午夜国产福利在线播放| 韩国日本欧美国产三级| 在线观看视频成人午夜| 久久99精品国产麻豆婷婷洗澡| 国产精品一区二区三区日韩av | 国产精品免费不卡视频| 精品少妇人妻av免费看| 中文字幕久久精品亚洲乱码| 欧美一级内射一色桃子| 91香蕉国产观看免费人人| 91欧美日韩精品在线| 偷自拍亚洲欧美一区二页| 微拍一区二区三区福利| 亚洲国产成人一区二区在线观看| 日本欧美视频在线观看免费| 高跟丝袜av在线一区二区三区| 色综合久久六月婷婷中文字幕| 日韩一区二区三区在线欧洲| 亚洲一级二级三级精品| 日韩欧美一区二区不卡视频| 91插插插外国一区二区| 国产三级视频不卡在线观看| 97人妻人人揉人人躁人人| 国产超碰在线观看免费| 色婷婷激情五月天丁香| 亚洲精品一区三区三区| 久热这里只有精品九九| 青青久久亚洲婷婷中文网| 国产一区二区三区午夜精品| 麻豆一区二区三区在线免费| 亚洲美女国产精品久久| 日韩欧美好看的剧情片免费| 日韩欧美国产高清在线| 乱女午夜精品一区二区三区| 久久精品少妇内射毛片| 国产精品免费福利在线| 加勒比东京热拍拍一区二区|