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