DispatcherServlet是前端控制器設計模式的實現,提供Spring Web MVC的集中訪問點,並且負責職責的分派,並且與Spring IoC容器無縫集成,從而能夠得到Spring的全部好處。 。html
DispatcherServlet主要用做職責調度工做,自己主要用於控制流程,主要職責以下:前端
一、文件上傳解析,若是請求類型是multipart將經過MultipartResolver進行文件上傳解析;java
二、經過HandlerMapping,將請求映射處處理器(返回一個HandlerExecutionChain,它包括一個處理器、多個HandlerInterceptor攔截器);web
三、經過HandlerAdapter支持多種類型的處理器(HandlerExecutionChain中的處理器);spring
四、經過ViewResolver解析邏輯視圖名到具體視圖實現;設計模式
五、本地化解析;app
六、渲染具體的視圖等;jsp
七、若是執行過程當中遇到異常將交給HandlerExceptionResolver來解析。ide
從以上咱們能夠看出DispatcherServlet主要負責流程的控制(並且在流程中的每一個關鍵點都是很容易擴展的)。ui
假如咱們要實現一個請求home.htm而後返回home.jsp視圖資源則
當home.htm請求到達時,咱們須要DispatcherServlet來處理該請求,因此首先配置該Servlet
第一步須要在web.xml中配置DispatcherServlet,使該servlet來接收請求並作進一步處理。
[html] view plaincopyprint?
<servlet> <servlet-name>dispatch</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatch</servlet-name> <url-pattern>*.htm</url-pattern> </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文件夾下,
配置文件仍是按照命名規範作吧(能夠修改成其餘名字)
<servlet> <servlet-name>dispatch</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>namespace</param-name> <param-value>classes/dispatch-servlet</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
此時的配置就會去尋找/WEB-INF/classes/dispatch-servlet.xml
當請求到達後Servlet將調用service方法進行處理,因爲咱們是經過輸入網址方式的get方法請求,Servlet將調用doGet方法
此處的doGet方法在FrameworkServlet中實現,doGet方法調用processRequest方法,processRequest則調用doService方法處理
而doService在DispatcherServlet中實現,doService再調用了DispatcherServlet的doDispatch方法,
該方法則會根據request找到轉發對象,並進行請求轉發操做,
下面是獲取實際的視圖資源部分
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); }
這裏須要咱們本身實現Controller接口並實現handleRequest方法,返回對應的ModelAndView對象。
下面是請求轉發的部分
/** * Render the internal resource given the specified model. * This includes setting the model as request attributes. */ @Override protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine which request handle to expose to the RequestDispatcher. HttpServletRequest requestToExpose = getRequestToExpose(request); ... exposeModelAsRequestAttributes(model, requestToExpose);//這個方法看下面源碼,request.setAttribute操做 // Determine the path for the request dispatcher. String dispatcherPath = prepareForRendering(requestToExpose, response); ... // If already included or response already committed, perform include, else forward. if (useInclude(requestToExpose, response)) { ...... } else {//重點看這部分,在根據請求以及配置文件獲取到RequestDispatcher 對象以後,使用該對象作轉發處理 // Note: The forwarded resource is supposed to determine the content type itself. exposeForwardRequestAttributes(requestToExpose); if (logger.isDebugEnabled()) { logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.forward(requestToExpose, response); } }
下面是設置model和modelValue
/** * Expose the model objects in the given map as request attributes. * Names will be taken from the model Map. * This method is suitable for all resources reachable by {@link javax.servlet.RequestDispatcher}. * @param model Map of model objects to expose * @param request current HTTP request */ protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception { for (Map.Entry<String, Object> entry : model.entrySet()) { String modelName = entry.getKey(); Object modelValue = entry.getValue(); if (modelValue != null) { request.setAttribute(modelName, modelValue); if (logger.isDebugEnabled()) { logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + "] to request in view with name '" + getBeanName() + "'"); } } else { request.removeAttribute(modelName); if (logger.isDebugEnabled()) { logger.debug("Removed model object '" + modelName + "' from request in view with name '" + getBeanName() + "'"); } } } }
第三步,編寫實現Controller的類
public class HomeController implements Controller { private String greeting; public String getGreeting() { return greeting; } public void setGreeting(String greeting) { this.greeting = greeting; } public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception { System.out.println(arg0.getRequestURI());//請求地址 return new ModelAndView("home", "message", greeting); //返回一個視圖資源對象,名爲home,model爲message的對象(即上面的exposeModelAsRequestAtrributes方法中使用的request.setAttribute } }
第四步,在dispatch-servlet.xml中配置該bean提供給spring web使用。
<bean name="/home.htm" class="com.iss.spring.web.HomeController"> <property name="greeting"><value>Hello!This is Training!你好,這裏是訓練營!</value></property> </bean>
這裏name將用來匹配請求的資源(默認的使用BeanNameUrlHandlerMapping處理,由bean Name映射 URL),在home.htm請求到達時,
spring將使用實現了Controller接口的HomeController的handleRequest方法來返回映射的視圖資源。
在獲得MoldelAndView對象後,須要根據這個MoldelAndView對象獲得View name而後來解析獲得View對象
/** * Resolve the given view name into a View object (to be rendered). * <p>The default implementations asks all ViewResolvers of this dispatcher. * Can be overridden for custom resolution strategies, potentially based on * specific model attributes or request parameters. * @param viewName the name of the view to resolve * @param model the model to be passed to the view * @param locale the current locale * @param request current HTTP servlet request * @return the View object, or <code>null</code> if none found * @throws Exception if the view cannot be resolved * (typically in case of problems creating an actual View object) * @see ViewResolver#resolveViewName */ protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } return null; }
此處須要咱們配置viewResolver bean給spring使用,指明使用哪一個類充當viewResolver並具備什麼屬性
第五步,配置viewResolver bean
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="suffix"><value>.jsp</value></property> </bean>
中間能夠加上prefix或者suffix
這些配置完成後,spring就會根據請求地址以及配置信息,找到視圖資源並作請求轉發操做
總結:整個流程分析下來,其實主要就是作兩個操做,
首先請求信息到達DispatchServlet,Servlet中根據請求信息與配置文件找到映射的視圖資源
而後使用RequestDispatch請求轉發到該視圖資源。
另外,能夠分紅多個bean配置文件,在web.xml中配置載入
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatch-data.xml,/WEB-INF/dispatch-service.xml</param-value> </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";
圖片上面的Frontcontroller 就至關於Dispatcherservlet