`

Spring源代码解析(二):IoC容器在Web容器中的启动

 
阅读更多

 

以下引用自博客:http://jiwenke-spring.blogspot.com/ 
上面我们分析了IOC容器本身的实现,下面我们看看在典型的web环境中,Spring IOC容器是怎样被载入和起作用的。 
简单的说,在web容器中,通过ServletContext为Spring的IOC容器提供宿主环境,对应的建立起一个IOC容器的体系。其中,首先需要建立的是根上下文,这个上下文持有的对象可以有业务对象,数据存取对象,资源,事物管理器等各种中间层对象。在这个上下文的基础上,和web MVC相关还会有一个上下文来保存控制器之类的MVC对象,这样就构成了一个层次化的上下文结构。在web容器中启动Spring应用程序就是一个建立这个上下文体系的过程。Spring为web应用提供了上下文的扩展接口 
WebApplicationContext: 

Java代码  收藏代码

  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会使用一个默认的实现,XmlWebApplicationContext - 这个上下文实现作为在web容器中的根上下文容器被建立起来,具体的建立过程在下面我们会详细分析。 

Java代码  收藏代码

  1. public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {  

  2.   

  3.     /** 这是和web部署相关的位置信息,用来作为默认的根上下文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应用程序,可以通过使用Spring代码声明式的指定在web应用程序启动时载入应用程序上下文(WebApplicationContext),Spring的ContextLoader是提供这样性能的类,我们可以使用 ContextLoaderServlet或者ContextLoaderListener的启动时载入的Servlet来实例化Spring IOC容器 - 为什么会有两个不同的类来装载它呢,这是因为它们的使用需要区别不同的Servlet容器支持的Serlvet版本。但不管是 ContextLoaderSevlet还是 ContextLoaderListener都使用ContextLoader来完成实际的WebApplicationContext的初始化工作。这个ContextLoder就像是Spring Web应用程序在Web容器中的加载器booter。当然这些Servlet的具体使用我们都要借助web容器中的部署描述符来进行相关的定义。 
下面我们使用ContextLoaderListener作为载入器作一个详细的分析,这个Servlet的监听器是根上下文被载入的地方,也是整个 Spring web应用加载上下文的第一个地方;从加载过程我们可以看到,首先从Servlet事件中得到ServletContext,然后可以读到配置好的在web.xml的中的各个属性值,然后ContextLoder实例化WebApplicationContext并完成其载入和初始化作为根上下文。当这个根上下文被载入后,它被绑定到web应用程序的ServletContext上。任何需要访问该ApplicationContext的应用程序代码都可以从WebApplicationContextUtils类的静态方法来得到: 

Java代码  收藏代码

  1. WebApplicationContext getWebApplicationContext(ServletContext sc)  


以Tomcat作为Servlet容器为例,下面是具体的步骤: 
1.Tomcat 启动时需要从web.xml中读取启动参数,在web.xml中我们需要对ContextLoaderListener进行配置,对于在web应用启动入口是在ContextLoaderListener中的初始化部分;从Spring MVC上看,实际上在web容器中维护了一系列的IOC容器,其中在ContextLoader中载入的IOC容器作为根上下文而存在于 ServletContext中。 

Java代码  收藏代码

  1. //这里对根上下文进行初始化。  

  2. public void contextInitialized(ServletContextEvent event) {  

  3.     //这里创建需要的ContextLoader  

  4.     this.contextLoader = createContextLoader();  

  5.     //这里使用ContextLoader对根上下文进行载入和初始化  

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

  7. }  


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

Java代码  收藏代码

  1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext)  

  2.         throws IllegalStateException, BeansException {  

  3.     //这里先看看是不是已经在ServletContext中存在上下文,如果有说明前面已经被载入过,或者是配置文件有错误。  

  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.         //这里创建根上下文作为整个应用的上下文同时把它存到ServletContext中去,注意这里使用的ServletContext的属性值是  

  15.         //ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,以后的应用都是根据这个属性值来取得根上下文的 - 往往作为自己上下文的父上下文  

  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中定义的参数:locatorFactorySelector,这是一个可选参数: 

Java代码  收藏代码

  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. }  


得到根上下文的父上下文以后,就是根上下文的创建过程: 

Java代码  收藏代码

  1. protected WebApplicationContext createWebApplicationContext(  

  2.         ServletContext servletContext, ApplicationContext parent) throws BeansException {  

  3.     //这里需要确定我们载入的根WebApplication的类型,由在web.xml中配置的contextClass中配置的参数可以决定我们需要载入什么样的ApplicationContext,  

  4.     //如果没有使用默认的。  

  5.     Class contextClass = determineContextClass(servletContext);  

  6.     .........  

  7.     //这里就是上下文的创建过程  

  8.     ConfigurableWebApplicationContext wac =  

  9.             (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  

  10.     //这里保持对父上下文和ServletContext的引用到根上下文中  

  11.     wac.setParent(parent);  

  12.     wac.setServletContext(servletContext);  

  13.   

  14.     //这里从web.xml中取得相关的初始化参数  

  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调用。  

  21.     wac.refresh();  

  22.     return wac;  

  23. }  


初始化根ApplicationContext后将其存储到SevletContext中去以后,这样就建立了一个全局的关于整个应用的上下文。这个根上下文会被以后的DispatcherServlet初始化自己的时候作为自己ApplicationContext的父上下文。这个在对 DispatcherServlet做分析的时候我们可以看看到。 

3.完成对ContextLoaderListener的初始化以后, Tomcat开始初始化DispatchServlet,- 还记得我们在web.xml中队载入次序进行了定义。DispatcherServlet会建立自己的ApplicationContext,同时建立这个自己的上下文的时候会从ServletContext中得到根上下文作为父上下文,然后再对自己的上下文进行初始化,并最后存到 ServletContext中去供以后检索和使用。 
可以从DispatchServlet的父类FrameworkServlet的代码中看到大致的初始化过程,整个ApplicationContext的创建过程和ContextLoder创建的过程相类似: 

Java代码  收藏代码

  1. protected final void initServletBean() throws ServletException, BeansException {  

  2.     .........  

  3.     try {  

  4.         //这里是对上下文的初始化过程。  

  5.         this.webApplicationContext = initWebApplicationContext();  

  6.         //在完成对上下文的初始化过程结束后,根据bean配置信息建立MVC框架的各个主要元素  

  7.         initFrameworkServlet();  

  8.     }  

  9.    ........  

  10. }  


对initWebApplicationContext()调用的代码如下: 

Java代码  收藏代码

  1. protected WebApplicationContext initWebApplicationContext() throws BeansException {  

  2.     //这里调用WebApplicationContextUtils静态类来得到根上下文  

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

  4.      

  5.     //创建当前DispatcherServlet的上下文,其上下文种类使用默认的在FrameworkServlet定义好的:DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;  

  6.     WebApplicationContext wac = createWebApplicationContext(parent);  

  7.     ........  

  8.     if (isPublishContext()) {  

  9.         //把当前建立的上下文存到ServletContext中去,注意使用的属性名是和当前Servlet名相关的。  

  10.         String attrName = getServletContextAttributeName();  

  11.         getServletContext().setAttribute(attrName, wac);  

  12.     }  

  13.     return wac;  

  14. }  


其中我们看到调用了WebApplicationContextUtils的静态方法得到根ApplicationContext: 

Java代码  收藏代码

  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. 然后创建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的引用和相关的配置信息。  

  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.         //这里调用ApplicationContext的初始化过程,同样需要使用refresh()  

  32.         wac.refresh();  

  33.         return wac;  

  34.     }  


4. 然后就是DispatchServlet中对Spring MVC的配置过程,首先对配置文件中的定义元素进行配置 - 请注意这个时候我们的WebApplicationContext已经建立起来了,也意味着DispatcherServlet有自己的定义资源,可以需要从web.xml中读取bean的配置信息,通常我们会使用单独的xml文件来配置MVC中各个要素定义,这里和web容器相关的加载过程实际上已经完成了,下面的处理和普通的Spring应用程序的编写没有什么太大的差别,我们先看看MVC的初始化过程: 

Java代码  收藏代码

  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进行分发处理由doService()完成,具体的MVC处理过程我们在doDispatch()中完成,其中包括使用Command模式建立执行链,显示模型数据等,这些处理我们都可以在DispatcherServlet的代码中看到: 

Java代码  收藏代码

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

  2.     ......  

  3.     try {  

  4.         doDispatch(request, response);  

  5.     }  

  6.    .......  

  7. }  


实际的请求分发由doDispatch(request,response)来完成: 

Java代码  收藏代码

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

  2.      .......  

  3.      // 这是Spring定义的执行链,里面放了映射关系对应的handler和定义的相关拦截器。  

  4.      HandlerExecutionChain mappedHandler = null;  

  5.      

  6.       ......  

  7.       try {  

  8.           //我们熟悉的ModelAndView在这里出现了。  

  9.           ModelAndView mv = null;  

  10.           try {  

  11.               processedRequest = checkMultipart(request);  

  12.   

  13.               //这里更具request中的参数和映射关系定义决定使用的handler  

  14.               mappedHandler = getHandler(processedRequest, false);  

  15.   

  16.               ......  

  17.               //这里是handler的调用过程,类似于Command模式中的execute.  

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

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

  20.   

  21.               .......  

  22.           //这里将模型数据通过视图进行展现  

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

  24.               render(mv, processedRequest, response);  

  25.           }  

  26.             ........  

  27.   }  


这样具体的MVC模型的实现就由bean配置文件里定义好的view resolver,handler这些类来实现用户代码的功能。 
总结上面的过程,我们看到在web容器中,ServletContext可以持有一系列的web上下文,而在整个web上下文中存在一个根上下文来作为其它 Servlet上下文的父上下文。这个根上下文是由ContextLoader载入并进行初始化的,对于我们的web应用, DispatcherSerlvet载入并初始化自己的上下文,这个上下文的父上下文是根上下文,并且我们也能从ServletContext中根据 Servlet的名字来检索到我们需要的对应于这个Servlet的上下文,但是根上下文的名字是由Spring唯一确定的。这个 DispactcherServlet建立的上下文就是我们开发Spring MVC应用的IOC容器。 
具体的web请求处理在上下文体系建立完成以后由DispactcherServlet来完成,上面对MVC的运作做了一个大致的描述,下面我们会具体就SpringMVC的框架实现作一个详细的分析。

分享到:
评论

相关推荐

    Spring源代码解析(一):IOC容器.doc

    在Spring源代码解析的第一部分,我们将聚焦于IOC容器,特别是BeanFactory接口,它是所有Spring容器的基础。 BeanFactory接口是Spring的基石,它定义了基本的容器操作,如获取Bean、检查Bean是否存在、确定Bean的...

    Spring源代码解析.rar

    Spring源代码解析2:IoC容器在Web容器中的启动.doc Spring源代码解析3:Spring JDBC .doc Spring源代码解析4:Spring MVC .doc Spring源代码解析5:Spring AOP获取Proxy .doc Spring源代码解析6:Spring声明式事务...

    Spring 源代码解析

    Spring源代码解析2:IoC容器在Web容器中的启动;Spring源代码解析3:Spring JDBC ; Spring源代码解析4:Spring MVC ;Spring源代码解析5:Spring AOP获取Proxy;Spring源代码解析6:Spring声明式事务处理 ; ...

    Spring源代码解析(二):IoC容器在Web容器中的启动.doc

    当我们在Web环境中运行Spring应用时,IoC容器需要在Web容器(如Tomcat、Jetty等)中启动并运行。这个过程涉及到一系列的初始化步骤,确保Spring能够正确地与Web容器集成。 首先,`WebApplicationContext`是`...

    Spring源代码解析

    Spring源代码解析(二):IoC容器在Web容器中的启动 Spring源代码解析(三):Spring JDBC Spring源代码解析(四):Spring MVC Spring源代码解析(五):Spring AOP获取Proxy Spring源代码解析(六):Spring声明式事务...

    springyuanmaaping.zip

    Spring源代码解析2:IoC容器在Web容器中的启动;Spring源代码解析3:Spring JDBC ; Spring源代码解析4:Spring MVC ;Spring源代码解析5:Spring AOP获取Proxy;Spring源代码解析6:Spring声明式事务处理 ; ...

    spring源代码解析

    2. **Web环境下的IOC容器启动**:"spring源代码解析(二):IOC容器在web中启动.doc"涵盖了在Web应用中初始化Spring容器的过程,包括ApplicationContext的创建、DispatcherServlet的配置以及如何在Web环境中注入bean...

    Spring源码学习文档,绝对值得好好研究~~

    Spring源代码解析(二):ioc容器在Web容器中的启动.doc Spring源代码分析(三):Spring JDBC.doc Spring源代码解析(四):Spring MVC.doc Spring源代码解析(五):Spring AOP获取Proxy.doc Spring源代码解析(六):...

    spring 源代码解析.zip

    《Spring源代码解析》 Spring框架作为Java领域最流行的开源框架之一,它的设计思想和实现方式一直是广大开发者关注的焦点。深入理解Spring的源代码,能够帮助我们更好地掌握其工作原理,提高我们的开发效率和代码...

    Spring 源代码解析.rar

    这个“Spring源代码解析”压缩包文件很可能是对Spring框架内部实现原理的详细分析,帮助开发者深入理解其工作机制。 在Spring框架中,依赖注入是核心概念之一,它允许开发者在运行时通过容器来管理对象的创建和依赖...

    spring源代码

    源代码分析有助于深入理解Spring的工作原理,提升编程技能,并且能够帮助开发者在遇到问题时进行调试和优化。 1. **Spring IoC容器**: Spring的核心是IoC容器,它负责管理对象的生命周期和依赖关系。通过XML配置...

    精通spring 源代码

    《精通Spring源代码》是罗时飞先生关于Spring框架深入解析的一部著作,旨在帮助开发者更深入地理解Spring的工作原理,提升对Java企业级应用开发的掌控能力。本压缩包包含的文件名为“精通Spring源码”,这通常是一个...

    Spring的源代码

    在`spring-framework-master`这个压缩包中,包含了Spring框架的完整源代码。开发者可以深入研究以下几个关键部分: - `spring-beans`:包含IoC容器的实现。 - `spring-core`:基础核心工具类,提供反射、资源加载等...

    Spring技术内幕:深入解析Spring架构与设计原理(第1部分)

     Spring如何在Web环境中集成IoC容器并为Web应用开发提供利器?  我们耳熟能详的MVC模式在Spring中是如何实现的?  Spring MVC如何灵活地集成各种丰富的视图展现方案?  Spring实现远端调用的方案有很多种,你...

    spring源代码下载地址

    在描述中提到的"Spring源代码的下载地址",通常是指Spring官方仓库或者第三方镜像站点。Spring的源代码托管在GitHub上,你可以访问Spring官方网站(https://spring.io/)找到链接,或者直接前往GitHub...

Global site tag (gtag.js) - Google Analytics