zoukankan      html  css  js  c++  java
  • springmvc(1)DispatcherServlet源码简单解析

    springmvc的简单配置

    1.首先需要在web.xml中配置DispatcherServlet,这个类是springmvc的核心类,所以的操作都是由这里开始,并且大部分都是在这里面实现的,比如各种视图的解析,视图的

    映射等等。配置文件:

        <servlet>
            <servlet-name>springServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>

    既然是和spring一套的,那肯定是有配置文件的,但是我们却没有配置文件路径,文件的默认路径是可以修改的,如果我们的这个文件没有配置路径的话,默认的路径是

    /WEB-INF/springServlet-servlet.xml //  默认的路径就是:/WEB-INF/[servlet的名称]-servlet.xml 

    但是我们是可以自己设置属性的,除了文件路径,还可以设置一下的几个属性:

    contextClass:实现WebApplicationContext接口的类,当前的servlet用它来创建上下文。如果这个参数没有指定, 默认使用XmlWebApplicationContext。
    contextConfigLocation:传给上下文实例(由contextClass指定)的字符串,用来指定上下文的位置。这个字符串可以被分成多个字符串(使用逗号作为分隔符) 来支持多个上下文(在多上下文的情况下,如果同一个bean被定义两次,后面一个优先)。
    namespace:WebApplicationContext命名空间。默认值是[server-name]-servlet。

    文件既可以放在WEB-INF下面,也可以放在classpath下面,比如:

    <servlet>
         <servlet-name>springServlet</servlet-name>
         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
         <init-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>classpath:springmvc.xml</param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
     </servlet>
      <servlet-mapping>
         <servlet-name>springServlet</servlet-name>
         <url-pattern>/</url-pattern>
     </servlet-mapping>

    注意一点的是,这个classpath指的是编译后的位置,刚开始学习框架的时候,总会犯这样的错误,找不到配置文件,就是因为classpath的问题。

    对于web项目,还有一种通用的写法,就是放在ContextLoaderListener下面:

    restful风格的

      <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath*:/spring-context*.xml</param-value>
      </context-param>

    支持多文件的:

    <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>
              classpath:spring-common-config.xml,
              classpath:spring-budget-config.xml
          </param-value>
    </context-param>

    这样子,springmvc基本上是配置好了,这里没有讲spring的配置。

    上面配置好了简单的web.xml以后,我们来讲一下DispatcherServlet.

    从上面我们应该就明白了,其实它就是一个servlet,既然是servlet,那么理解起来就简单多了。

    DispathcherServlet最主要的就是下面几个方法,下面一一来简单看看:

    /**
         * Initialize the strategy objects that this servlet uses.
         * <p>May be overridden in subclasses in order to initialize further strategy objects.
         */
        protected void initStrategies(ApplicationContext context) {
            initMultipartResolver(context);
            initLocaleResolver(context);
            initThemeResolver(context);
            initHandlerMappings(context);
            initHandlerAdapters(context);
            initHandlerExceptionResolvers(context);
            initRequestToViewNameTranslator(context);
            initViewResolvers(context);
            initFlashMapManager(context);
        }

    这个方法是初始化各种解析器,适配等等的方法,这个方法是在其父类的初始化方法中调用的,没记错的话,其实就是相当于Servlet的init方法执行时调用。首先一起了解下上面每一个方法初始化的都是什么:

    1.MultipartResolver指的是文件上传解析类,对于文件上传的时候,需要使用到,我们看他的方法:
    private void initMultipartResolver(ApplicationContext context) {
            try {
            //这里获取bean 注意这个常量在前面的是定义的:MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
            //所以我们配置的时候 bean的名称必须是上面的这个 不然是不能识别的哦 注意咯
    this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); if (logger.isDebugEnabled()) { logger.debug("Using MultipartResolver [" + this.multipartResolver + "]"); } } catch (NoSuchBeanDefinitionException ex) { // Default is no multipart resolver. this.multipartResolver = null; if (logger.isDebugEnabled()) { logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME + "': no multipart request handling provided"); } } }

    如果没有配置  那么默认就是没有文件上传解析的。这个时候,如果上传,那么会出一些问题的。

    2.LocaleResolver这个是本地化解析,也就是做国际化的,如果没有设置,那么会默认为我们设置一个

    3.ThemeResolver 看名字也知道,就是风格和主题解析类,默认也会有为我们设置一个

    4.HandlerMappings :请求到处理器的映射,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象;如BeanNameUrlHandlerMapping将URL与Bean名字映射,映射成功的Bean就是此处的处理器;从下面的代码中可以看出

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //请求到处理器的映射,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象);
    //如BeanNameUrlHandlerMapping将URL与Bean名字映射,映射成功的Bean就是此处的处理器,注意这里处理器值得是handler,而不是handlerMapping,形象的将
         //就是handlerMapping是一个模板,处理器handler就是根据你的bean刻出的成型产品。
    for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }

    5.HandlerAdapters:看到适配器了吧,对各种处理器包装成适配器,这样就能支持多种类型的处理器了。

    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//将处理器进行包装,后面的操作都是都是由适配器进行工作

    6HandlerExceptionResolvers:一场解析器,就比如出现错误的时候,可能统一调到某一个页面。默认也有实现,这个需求有太多中解决方案了,没什么好讲

    7.RequestToViewNameTranslator:当处理器没有返回逻辑视图名等相关信息时,自动将请求URL映射为逻辑视图名;

    8ViewResolvers:视图解析器。我们的配置中都会一般都会配置一个视图,就像这样:

            <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                <property name="suffix" value=".jsp"></property>
                <property name="prefix" value="/WEB-INF/views/module/"></property>
            </bean>

    视图解析器就是将上面的进行解析。超类只有一个方法

    View resolveViewName(String viewName, Locale locale) throws Exception;
    9.FlashMapManager值得是redirect的时候保存数据的一个相当于map分装的一个容器吧,但是官方是说在程序中一般不会使用的,比如在controller,就经常是用RedirectAttributes 来传输数据

     讲完了几个比较重要的属性,然后将主要的几个放大,既然是servlet,那么我们就来看看service方法吧,这里的service是这样的:

    protected void doService(HttpServletRequest request, HttpServletResponse response) 

    里面有一个这样的依据代码需要引起重视,其他的代码都是配置一些属性什么的

    try {
                doDispatch(request, response);
            }

    这个方法其实就是DispatchServlet的核心,我们前面讲到的解析器,适配器等等都是在这里操作的:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            
         HandlerExecutionChain mappedHandler
    = null;//前面讲过。handlerMapping映射成功,都会返回这个东西,其中包括处理器和拦截器 boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try {
              //查看请求是否是Multipart(如文件上传) processedRequest
    = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request.
            
             //映射 mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; }           //进行适配器的包装 // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; }          // applyDefaultViewName(request, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; }
          
           //解析视图 最终会调用view.render(mv.getModelInternal(), request, response);将model传进去 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }
         //后面都是编译以后的一些操作了
    catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }

    上面也就简单的了解了一下springmvc的入口,接下来几天在研究一下springmvc其他的一些功能。好多东西都是自己理解的,不知道对不对==。

  • 相关阅读:
    android点滴 之 进度条
    android点滴(26)之让线程拥有自己的消息循环
    Python property
    MD5加密
    哈希表加载xml文件
    .NET自定义控件制作
    如何用Treeview树加载xml
    xml的属性
    FFT实现——有趣的移位寄存流水线形式
    频率选择滤波器 线性时不变系统
  • 原文地址:https://www.cnblogs.com/zr520/p/5087189.html
Copyright © 2011-2022 走看看