zoukankan      html  css  js  c++  java
  • Spring MVC中DispatcherServlet工作原理探究

    转:http://blog.csdn.net/zhouyuqwert/article/details/6853730

    下面类图将主要的类及方法抽离出来,以便查看方便,根据类的结构来说明整个请求是如何工作的

    主要使用到的技术有Spring的IOC容器和Servlet。


    假如我们要实现一个请求home.htm然后返回home.jsp视图资源则

    当home.htm请求到达时,我们需要DispatcherServlet来处理该请求,所以首先配置该Servlet

    第一步需要在web.xml中配置DispatcherServlet,使该servlet来接收请求并做进一步处理。

    1. <servlet> 
    2.     <servlet-name>dispatch</servlet-name> 
    3.     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
    4.     <load-on-startup>1</load-on-startup> 
    5. </servlet> 
    6. <servlet-mapping> 
    7.     <servlet-name>dispatch</servlet-name> 
    8.     <url-pattern>*.htm</url-pattern> 
    9. </servlet-mapping> 


    这个部分很好理解,如果请求以.htm结尾则交给名为dispatch类为DispatcherServlet的Servlet处理。


    从类图中很容易看出DispatcherServlet最终继承的是HttpServlet,也就是说它同样满足Servlet的工作原理

    Servlet初始化时需要调用init方法,在HttpServletBean中实现,该init方法调用了initServletBean,该方法在FrameworkServlet中实现

    initServletBean主要初始化关于配置文件的内容,比如{servlet-name}-servlet.xml


     

    第二步,需要在/WebRoot/WEB-INF下新建名为{servlet-name}-servlet.xml的spring bean配置文件。(该示例中即为dispatch-servlet.xml)

    在初始化过程中会去寻找该配置文件,当然我们也可以自己去设置参数来更改配置文件所在路径


    比如我们如果在src下新建的该配置文件dispatch-servlet,在编译后会被复制到WEB-INF/classes文件夹下,

    配置文件还是按照命名规范做吧(可以修改为其他名字)

    1. <servlet> 
    2.         <servlet-name>dispatch</servlet-name> 
    3.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
    4.         <init-param> 
    5.             <param-name>namespace</param-name> 
    6.             <param-value>classes/dispatch-servlet</param-value> 
    7.         </init-param> 
    8.         <load-on-startup>1</load-on-startup> 
    9.     </servlet> 


    此时的配置就会去寻找/WEB-INF/classes/dispatch-servlet.xml


    当请求到达后Servlet将调用service方法进行处理,由于我们是通过输入网址方式的get方法请求,Servlet将调用doGet方法

    此处的doGet方法在FrameworkServlet中实现,doGet方法调用processRequest方法,processRequest则调用doService方法处理

    而doService在DispatcherServlet中实现,doService再调用了DispatcherServlet的doDispatch方法,

    该方法则会根据request找到转发对象,并进行请求转发操作,

    下面是获取实际的视图资源部分


    1. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) 
    2.             throws Exception { 
    3.  
    4.         return ((Controller) handler).handleRequest(request, response); 
    5.     } 


    这里需要我们自己实现Controller接口并实现handleRequest方法,返回对应的ModelAndView对象。


    下面是请求转发的部分

    1. /**
    2.      * Render the internal resource given the specified model.
    3.      * This includes setting the model as request attributes.
    4.      */ 
    5.     @Override 
    6.     protected void renderMergedOutputModel( 
    7.             Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { 
    8.  
    9.         // Determine which request handle to expose to the RequestDispatcher. 
    10.         HttpServletRequest requestToExpose = getRequestToExpose(request); 
    11.  
    12.         ... 
    13.                  exposeModelAsRequestAttributes(model, requestToExpose);//这个方法看下面源码,request.setAttribute操作 
    14.                   // Determine the path for the request dispatcher. 
    15.         String dispatcherPath = prepareForRendering(requestToExpose, response); 
    16.  
    17.             ... 
    18.  
    19.         // If already included or response already committed, perform include, else forward. 
    20.         if (useInclude(requestToExpose, response)) { 
    21.             ...... 
    22.         } 
    23.  
    24.         else {//重点看这部分,在根据请求以及配置文件获取到RequestDispatcher 对象之后,使用该对象做转发处理 
    25.             // Note: The forwarded resource is supposed to determine the content type itself. 
    26.             exposeForwardRequestAttributes(requestToExpose); 
    27.             if (logger.isDebugEnabled()) { 
    28.                 logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); 
    29.             } 
    30.             rd.forward(requestToExpose, response); 
    31.         } 
    32.     } 

    下面是设置model和modelValue

    1. /**
    2.      * Expose the model objects in the given map as request attributes.
    3.      * Names will be taken from the model Map.
    4.      * This method is suitable for all resources reachable by {@link javax.servlet.RequestDispatcher}.
    5.      * @param model Map of model objects to expose
    6.      * @param request current HTTP request
    7.      */ 
    8.     protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception { 
    9.         for (Map.Entry<String, Object> entry : model.entrySet()) { 
    10.             String modelName = entry.getKey(); 
    11.             Object modelValue = entry.getValue(); 
    12.             if (modelValue != null) { 
    13.                 request.setAttribute(modelName, modelValue); 
    14.                 if (logger.isDebugEnabled()) { 
    15.                     logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + 
    16.                             "] to request in view with name '" + getBeanName() + "'"); 
    17.                 } 
    18.             } 
    19.             else
    20.                 request.removeAttribute(modelName); 
    21.                 if (logger.isDebugEnabled()) { 
    22.                     logger.debug("Removed model object '" + modelName + 
    23.                             "' from request in view with name '" + getBeanName() + "'"); 
    24.                 } 
    25.             } 
    26.         } 
    27.     } 



     



     

    第三步,编写实现Controller的类

    1. public class HomeController implements Controller 
    2.     private String greeting; 
    3.  
    4.     public String getGreeting() 
    5.     { 
    6.         return greeting; 
    7.     } 
    8.  
    9.     public void setGreeting(String greeting) 
    10.     { 
    11.         this.greeting = greeting; 
    12.     } 
    13.  
    14.     public ModelAndView handleRequest(HttpServletRequest arg0, 
    15.             HttpServletResponse arg1) throws Exception 
    16.     { 
    17.         System.out.println(arg0.getRequestURI());//请求地址 
    18.         return new ModelAndView("home", "message", greeting); 
    19. //返回一个视图资源对象,名为home,model为message的对象(即上面的exposeModelAsRequestAtrributes方法中使用的request.setAttribute 
    20.     } 
    21.  


    第四步,在dispatch-servlet.xml中配置该bean提供给spring web使用。

    1. <bean name="/home.htm" class="com.iss.spring.web.HomeController"> 
    2.     <property name="greeting"><value>Hello!This is Training!你好,这里是训练营!</value></property> 
    3. </bean> 


    这里name将用来匹配请求的资源(默认的使用BeanNameUrlHandlerMapping处理,由bean Name映射 URL),在home.htm请求到达时,

    spring将使用实现了Controller接口的HomeController的handleRequest方法来返回映射的视图资源。


    在得到MoldelAndView对象后,需要根据这个MoldelAndView对象得到View name然后来解析得到View对象


    1. /**
    2.      * Resolve the given view name into a View object (to be rendered).
    3.      * <p>The default implementations asks all ViewResolvers of this dispatcher.
    4.      * Can be overridden for custom resolution strategies, potentially based on
    5.      * specific model attributes or request parameters.
    6.      * @param viewName the name of the view to resolve
    7.      * @param model the model to be passed to the view
    8.      * @param locale the current locale
    9.      * @param request current HTTP servlet request
    10.      * @return the View object, or <code>null</code> if none found
    11.      * @throws Exception if the view cannot be resolved
    12.      * (typically in case of problems creating an actual View object)
    13.      * @see ViewResolver#resolveViewName
    14.      */ 
    15.     protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, 
    16.             HttpServletRequest request) throws Exception { 
    17.  
    18.         for (ViewResolver viewResolver : this.viewResolvers) { 
    19.             View view = viewResolver.resolveViewName(viewName, locale); 
    20.             if (view != null) { 
    21.                 return view; 
    22.             } 
    23.         } 
    24.         return null
    25.     } 


    此处需要我们配置viewResolver bean给spring使用,指明使用哪个类充当viewResolver并具有什么属性

    第五步,配置viewResolver bean

    1. <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
    2.         <property name="suffix"><value>.jsp</value></property> 
    3.     </bean> 


    中间可以加上prefix或者suffix

    这些配置完成后,spring就会根据请求地址以及配置信息,找到视图资源并做请求转发操作


    总结:整个流程分析下来,其实主要就是做两个操作,

    首先请求信息到达DispatchServlet,Servlet中根据请求信息与配置文件找到映射的视图资源

    然后使用RequestDispatch请求转发到该视图资源。

    另外,可以分成多个bean配置文件,在web.xml中配置载入

    1. <listener> 
    2.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
    3. </listener> 
    4. <context-param> 
    5.     <param-name>contextConfigLocation</param-name> 
    6.     <param-value>/WEB-INF/dispatch-data.xml,/WEB-INF/dispatch-service.xml</param-value> 
    7. </context-param> 


    其中contextConfigLocation这个名字可能是匹配FrameworkServlet的setContextConfigLocation方法

    也有可能是匹配ContextLoaderListener继承ContextLoader的CONFIG_LOCATION_PARAM

    public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
    (不确定,不太了解context-param的用法,API上两个类关于这个变量的说明都类似,也分不太清楚,反正可以这么记- -||)


    然后配置的viewResolver bean的id为什么要为viewResolver,下面的是DispatcherServlet中一个静态字符串说明了一切

    public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";

  • 相关阅读:
    Ubuntu自启动服务脚本
    坑(一)—— Django ORM 连接超时的坑
    logging模块详解
    端口扫描之nmap命令
    端口扫描之masscan扫描
    端口扫描之Scapy模块的使用
    端口扫描之开放端口扫描方式
    Android Studio导入Project、Module的正确方法
    ImportError: No module named 'requests'
    运行python程序
  • 原文地址:https://www.cnblogs.com/jackljf/p/3588914.html
Copyright © 2011-2022 走看看