`
wugaokai
  • 浏览: 62055 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring-MVC 源码分析(二):DispacherServlet 初始化

 
阅读更多

DispatcherServlet


 

 

    Servlet Servlet规范中规定的一个服务器组件的接口,任何一个可以处理用户请求的服务器组件需要实现这个接口, Web容器就是根据 URL Servlet的映射派遣一个 HTTP请求到这个 Servlet组件的实现,进而对这个HTTP请求进行处理,并且产生 HTTP响应。

 

通用 Servlet(GenericServlet) Servlet的一个抽象实现。这个实现是和协议无关的。它提供了 Servlet应该具有的基础功能。例如,保存 Servlet配置,为后来的操作提供初始化参数和信息等等。

 

HTTP Servlet(HttpServlet)是针对 HTTP协议对通用 Servlet的实现。它实现了 HTTP协议的一些基本操作。例如,根据 HTTP请求的方法分发不同类型的 HTTP请求到不同的方法进行处理。对于简单的 HTTP方法 (HEAD, OPTIONS, TRACE)提供了通用的实现,这些实现在子类中通常是不需要重写的。而对其他的业务服务类型的方法 (GET, POST, PUT, DELETE)提供了占位符方法。子类应该有选择的根据业务逻辑重写这些服务类型方法的实现。

 

HTTP Servlet Bean(HttpServletBean) Spring Web MVC的一个抽象实现。它提供了一个特殊的功能,可以将Servlet配置的初始化参数作为 Bean的属性,自动赋值给 Servlet的属性。子类 Servlet的很多属性都是通过这个功能进行配置的。

 

Framework Servlet(FrameworkServlet)也是一个抽象的实现。在这个是层次上,它提供了加载一个对应的Web应用程序环境的功能。这个 Web应用程序环境可以存在一个根环境,这个根环境可以是共享的也可以是这个Servlet或者几个 Servlet专用的。它也提供了功能将 HTTP GET, POST, PUT, DELETE方法统一派遣到 Spring Web MVC的控制器方法进行派遣。在派遣前到处请求环境等信息到线程的局部存储。

 

派遣器 Servlet(DispatcherServlet)是这个继承链中最后一个类,它是 Spring Web MVC的核心实现类,它在框架Servlet中加载的 Web应用程序环境中查找一切 Spring Web MVC所需要的并且注册的组件,如果一个需要的Spring Web MVC组件没有注册,则通过缺省策略的配置创建并且初始化这些组件。在一个 HTTP请求被派遣的时候,它使用得到的 Spring Web MVC组件进行处理和响应。


HTTP协议支持各种类型的方法,其中包括, GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE

  • GET方法将请求参数放在请求的头中,请求服务器做某些服务操作并返回响应。
  • POST方法从客户机向服务器传送数据,并可能要求服务器做出某些服务操作进行响应。
  • PUT方法请求将一个资源放在服务器的某个路径下。
  • DELETE方法请求将服务器某路径下的一个资源删除。
  • HEAD要求服务器查找某对象的头信息,包括应该包含的请求体的长度,而不是对象本身。
  • OPTION方法用来查询服务器的实现信息。
  • TRACE多数情况下用在调试目操作上。
DispatcherServlet 类有一个重要的初始化方法initStrategies(ApplicationContext context),这方法可以被子类重写
 protected void initStrategies(ApplicationContext context) {
          initMultipartResolver(context);
          initLocaleResolver(context);
          initThemeResolver(context);
          initHandlerMappings(context);
          initHandlerAdapters(context);
          initHandlerExceptionResolvers(context);
          initRequestToViewNameTranslator(context);
          initViewResolvers(context);
     }
 
这方法启动整个Spring MVC框架的初始化。他的调用关系如下:



 第一步:

javax.servlet.GenericServlet 的init(ServletConfig)是Servlet初始化的入口

 public void init(ServletConfig config) throws ServletException {
          // 保存 Servlet 配置对象,对于处理一个 HTTP 请求的许多操作都需要的 Servlet 配置所包含的信息,例如 ,Servlet 名字, Servlet 配置的参数等等 
          this.config = config;
          //init()是占位符方法,供子类实现
          this.init();
    }
 
第二步:
org.springframework.web.servlet.HttpServletBean的init()方法实现

  //重写通用Servlet的init()方法占位符进行初始化Servlet Bean,这些初始化信息来自于Serlvet配置的参数,
       //我们通常通过Servlet初始化参数给一个Serlvet指定一个非默认名字的Spring Context的文件路径,就是这里实现的  
     public final void init() throws ServletException {
          if (logger.isDebugEnabled()) {
                //这个Servlet名字就是从Servlet配置对象中取得的,而Serlvet配置是在通用Servlet初始化阶段保存的 
              logger.debug("Initializing servlet '" + getServletName() + "'");
          }

           //设置Servlet初始化参数作为Servlet Bean的属性  
          try {
               //使用Servlet配置的初始化参数创建一个PropertyValues对象,PropertyValues对象是名值对的集合, 子类也可以指定哪些属性是必须的  
              PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
                //把当前的Servlet当作一个Bean, 把Bean的属性以及属性的存取方法信息放入BeanWrapper对象  
              BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
               //注册一个可以在资源和路径之间进行转化的客户化编辑器,这些资源是这个Web应用的内部资源,例如,一个文件,一个图片等等  
              ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
              bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader));
               //提供给子类机会增加更多的客户化的编辑器,或者对BeanWrapper进行更多的初始化 
              initBeanWrapper(bw);
               //把初始化制定的参数值赋值到Servlet的属性中,第二个参数true表明忽略位置属性  
              bw.setPropertyValues(pvs, true);
          }
          catch (BeansException ex) {
              logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
              throw ex;
          }

           //给子类一个机会去初始化子类需要的资源,同样是一个占位符方法  
          initServletBean();

          if (logger.isDebugEnabled()) {
              logger.debug("Servlet '" + getServletName() + "' configured successfully");
          }
     }
 
第三步:
org.springframework.web.servlet.FrameworkServlet

No1:initServletBean

  //重写了Http Servlet Bean的初始化占位符方法initServletBean(),进而初始化框架Serlvet所需的资源,在这里就是Web应用程序环境  
     protected final void initServletBean() throws ServletException {
          //打印初始化信息到Servlet容易的日志  
          getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
          if (this.logger.isInfoEnabled()) {
              this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
          }
          //取得初始化环境的开始时间  
          long startTime = System.currentTimeMillis();

          try {
               //初始中化Servlet的环境
              this.webApplicationContext = initWebApplicationContext();
              initFrameworkServlet();
          }
          catch (ServletException ex) {
              this.logger.error("Context initialization failed", ex);
              throw ex;
          }
          catch (RuntimeException ex) {
              this.logger.error("Context initialization failed", ex);
              throw ex;
          }

          if (this.logger.isInfoEnabled()) {
                //取得初始化环境的结束时间
              long elapsedTime = System.currentTimeMillis() - startTime;
               //log初始化Web应用程序环境所需的总体时间  
              this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                        elapsedTime + " ms");
          }
     }
 
No2: initWebApplicationContext

protected WebApplicationContext initWebApplicationContext() {
          //首先查找是否这个派遣器Servlet有一个专用的根环境,这个根环境是通过一个属性(contextAttribute)作为关键字存储在Servlet环境里的,
              //这个属性可以在Servlet的初始化参数中指定,因为在HTTP Servlet Bean的初始化过程中,初始化参数将被当作为Bean属性进行赋值 
          WebApplicationContext wac = findWebApplicationContext();
           //如果这个Servlet不存在专用的根环境 
               //通常我们不需要这个环境,因为我们通常使用一个ContextLoaderListener进行加载一个默认的共享的根环境  
          if (wac == null) {
                 //取得默认的共享的根环境,这个根环境通过关键字ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE保存在Servlet环境对象里  
              WebApplicationContext parent =
                        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
                //创建派遣器Servlet的子环境,这个子环境引用得到的主环境,这个主环境是可选的
                      //一般这个方法就是子环境读取getServletName()-servlet.xml文件的加载过程
              wac = createWebApplicationContext(parent);
          }

           //Web应用程序环境在创建之后,指定了这个类作为Web应用程序环境事件处理的监听器,如果这个Web应用程序环境支持刷新, 这个onRefresh方法应该已经调用,否则,我们需要手工激发初始化事件  
               //这个刷新方法将被派遣器Servlet重写,它将提取并初始化Spring Web MVC的各个组件。 

                //框架Servlet初始化为子类准备了初始化占位符方法initFrameworkServlet(), 但是,同时也准备了onRefresh()方法,
               //因为一个ConfigurableApplicationContext是支持动态刷新的,依赖于Web应用程序环境的子类组件应该监听这个方法进行重新初始化,派遣器Servlet就是这样实现的  
          if (!this.refreshEventReceived) {
              // Apparently not a ConfigurableApplicationContext with refresh support:
              // triggering initial onRefresh manually here.
              onRefresh(wac);
          }

           //如果设置了发布环境属性,则把这个Web应用程序环境以ServletContextAttributeName的值作为关键字保存到Servlet环境对象里,
                //这个关键字是org.springframework.web.servlet.FrameworkServlet.CONTEXT. + Servlet名称  
          if (this.publishContext) {
              String attrName = getServletContextAttributeName();
              getServletContext().setAttribute(attrName, wac);
              if (this.logger.isDebugEnabled()) {
                   this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                             "' as ServletContext attribute with name [" + attrName + "]");
              }
          }

          return wac;
     }
 
No3: createWebApplicationContext

 protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
          //获取容器的类型别,默认是XmlWebApplicationContext
          Class<?> contextClass = getContextClass();
          if (this.logger.isDebugEnabled()) {
              this.logger.debug("Servlet with name '" + getServletName() +
                        "' will try to create custom WebApplicationContext context of class '" +
                        contextClass.getName() + "'" + ", using parent context [" + parent + "]");
          }
          //如果contextClass的类型不是ConfigurableWebApplicationContext,则抛出异常
          if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
              throw new ApplicationContextException(
                        "Fatal initialization error in servlet with name '" + getServletName() +
                        "': custom WebApplicationContext class [" + contextClass.getName() +
                        "] is not of type ConfigurableWebApplicationContext");
          }
          //实例化Web应用程序环境类 
          ConfigurableWebApplicationContext wac =
                   (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

           //获取Servlet上下文
          ServletContext sc = getServletContext();
          if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
               //如果 Servlet规范 <= 2.4,则使用web.xml里定义的应用程序名字定义Web应用程序名 
              String servletContextName = sc.getServletContextName();
              if (servletContextName != null) {
                   wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
                             "." + getServletName());
              }
              else {
                   wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getServletName());
              }
          }
          else {
              // 如果Servlet规范是 2.5, 则使用配置的ContextPath定义Web应用程序名  
               wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + sc.getContextPath() +
                        "/" + getServletName());
          }
          
           //如果父环境存在,则引用使用父环境 
          wac.setParent(parent);
          //保存Servlet环境  
          wac.setServletContext(getServletContext());
         //
          wac.setServletConfig(getServletConfig());
          wac.setNamespace(getNamespace());
           //设置根环境XML文件存放的位置,这里应该是getServletName()-servlet.xml文件
          wac.setConfigLocation(getContextConfigLocation());
          wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

          postProcessWebApplicationContext(wac);
          wac.refresh();

          return wac;
     }
 
org.springframework.web.servlet.DispatcherServlet

No1: onRefresh

protected void onRefresh(ApplicationContext context) {
          initStrategies(context);
     }
 
No2: initStrategies

protected void initStrategies(ApplicationContext context) {
          //初始化多部请求解析器,没有默认的实现  
          initMultipartResolver(context);
          //初始化地域解析器,默认实现是AcceptHeaderLocaleResolver
          initLocaleResolver(context);
           //初始化主题解析器,默认实现是FixedThemeResolver
          initThemeResolver(context);
           //初始化处理器映射,这是个集合, 默认实现是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping 
          initHandlerMappings(context);
           //初始化处理器适配器,这是个集合,默认实现是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter  
          initHandlerAdapters(context);
            //初始化处理器异常解析器,这是个集合,默认实现是AnnotationMethodHandlerExceptionResolver,ResponseStatusExceptionResolver和DefaultHandlerExceptionResolver 
          initHandlerExceptionResolvers(context);
          //初始化请求到视图名解析器,默认实现是DefaultRequestToViewNameTranslator  
          initRequestToViewNameTranslator(context);
          //初始化视图解析器,这是个集合,默认实现是InternalResourceViewResolver  
          initViewResolvers(context);
     }
 
No3: initMultipartResolver

 private void initMultipartResolver(ApplicationContext context) {
          try {
                //从配置的Web应用程序环境中查找多部请求解析器  
              this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
              if (logger.isDebugEnabled()) {
                   logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
              }
          }
          catch (NoSuchBeanDefinitionException ex) {
              // 如果没有,那就没有默认的实现
              this.multipartResolver = null;
              if (logger.isDebugEnabled()) {
                   logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
                             "': no multipart request handling provided");
              }
          }
     }
 
No4: initLocaleResolver

private void initLocaleResolver(ApplicationContext context) {
          try {
               //从配置的Web应用程序环境中查找地域请求解析器  
              this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
              if (logger.isDebugEnabled()) {
                   logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
              }
          }
          catch (NoSuchBeanDefinitionException ex) {
               //如果没有地域请求解析器在Web应用程序环境中注册,则查找缺省配置的策略,并且根据配置初始化缺省的地域请求解析器,后面将代码注解如何加载默认策略  
              this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
              if (logger.isDebugEnabled()) {
                   logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
                             "': using default [" + this.localeResolver + "]");
              }
          }
     }
 
No5:  initThemeResolver 与 initLocaleResolver 类似

No6:  initHandlerMappings

 private void initHandlerMappings(ApplicationContext context) {
          this.handlerMappings = null;
           
               //这个detectAllHandlerMappings默认值设为true,即默认地从所有的IoC容器中取
          if (this.detectAllHandlerMappings) {
              //如果配置成为自动检测所有的处理器映射,则在加载的Web应用程序环境中查找所有实现HandlerMapping接口的Bean 
              Map<String, HandlerMapping> matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
              if (!matchingBeans.isEmpty()) {
                   this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                    // 根据这些Bean所实现的Order接口进行排序  
                   OrderComparator.sort(this.handlerMappings);
              }
          }
          else {       
              try {
                    //如果没有配置成为自动检测所有的处理器映射,则在Web应用程序环境中查找指定名字为”handlerMapping”的Bean作为处理器映射 
                   HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                    //构造单个Bean的集合  
                   this.handlerMappings = Collections.singletonList(hm);
              }
              catch (NoSuchBeanDefinitionException ex) {
                   //忽略异常,后面将使用对象引用是否为空判断是否查找成功  
              }
          }

          // Ensure we have at least one HandlerMapping, by registering
          // a default HandlerMapping if no other mappings are found.
          if (this.handlerMappings == null) {
                //如果仍然没有查找到注册的HanlderMapping的实现,则使用缺省的配置策略加载处理器映射  
              this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
              if (logger.isDebugEnabled()) {
                   logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
              }
          }
     }
 
No7: initHandlerAdapters(), initHandlerExceptionResolvers()initViewResolvers ()同样是对多值组件进行初始化,他们和initHandlerMappings()具有相同的实现

No8: 缺省的配置策略实现

 private static final Properties defaultStrategies;

     static {
        //静态初始化,加载DispatcherServlet.properties文件       
          try {
              ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
              defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
          }
          catch (IOException ex) {
              throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
          }
     }

     protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
     
                         //取得组件接口的完全的类名,缺省策略是通过组件接口的类名作为关键字存储在属性文件里的  
          String key = strategyInterface.getName();
          //取得以这个接口名为关键字配置的所有实现类的名字,这些类的名字是通过逗号为分隔的
          String value = defaultStrategies.getProperty(key);
          if (value != null) {
                //把逗号分隔的类名字符串转化成字符串的数组  
              String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
              List<T> strategies = new ArrayList<T>(classNames.length);
              for (String className : classNames) {
                   try {
                         //通过类的名字加载这个类  
                        Class clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                          //在Web应用程序环境中创建这个组件的类的对象  
                        Object strategy = createDefaultStrategy(context, clazz);
                         //将初始化的组件Bean加入到返回的集合结果中  
                        strategies.add((T) strategy);
                   }
                   catch (ClassNotFoundException ex) {
                          //如果找不到这个类的定义  
                        throw new BeanInitializationException(
                                  "Could not find DispatcherServlet's default strategy class [" + className +
                                           "] for interface [" + key + "]", ex);
                   }
                   catch (LinkageError err) {
                          //如果找不到加载的类的依赖类  
                        throw new BeanInitializationException(
                                  "Error loading DispatcherServlet's default strategy class [" + className +
                                           "] for interface [" + key + "]: problem with class file or dependent class", err);
                   }
              }
              return strategies;
          }
          else {
               //如果没有缺省策略配置,则返回一个空列表  
              return new LinkedList<T>();
          }
     }
 
  • 大小: 4.4 KB
  • 大小: 19.5 KB
分享到:
评论
1 楼 jy0902 2012-08-28  

   不错 正在运用分析。好好学习!

相关推荐

Global site tag (gtag.js) - Google Analytics