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来接收请求并做进一步处理。
- <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>
<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>
<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);
- }
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);
- }
- }
/**
* 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() +
"'"); - }
- }
- }
- }
/**
* 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
- }
- }
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>
<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;
- }
/**
* 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>
<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>
<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";
Spring MVC中DispatcherServlet工作原理探究的更多相关文章
- spring mvc中DispatcherServlet如何得到ModelAndView的
首先看下面这种张图,这张图说明了spring mvc整体的流程. 本文讲的就是如何从DispatcherServlet中得到ModerAndView的过程. 首先看DispatherServlet这个 ...
- Spring mvc中DispatcherServlet详解
简介 DispatcherServlet是前端控制器设计模式的实现,提供SpringWebMVC的集中访问点,而且负责职责的分派,而且与spring IOC容器无缝集成,从而可以获得Spring的优势 ...
- Spring mvc 中 DispatcherServlet 的学习和理解
上图表示当客户请求来到时,spring架构作出响应的流程,可以从图中看到看到请求分发的中心就是 DispatcherServlet 类,DispatcherServlet的任务是将请求发送给Sprin ...
- Spring Mvc中DispatcherServlet和Servlet的区别小结
在web开发过程中开始接触的是servlet,用来处理用户请求.这几年随着spring 框架越来越成熟,几乎成了java web开发界的主流框架.既然这么受欢迎肯定有它的优点,spring框架在原来的 ...
- Spring MVC 中的基于注解的 Controller【转】
原文地址:http://my.oschina.net/abian/blog/128028 终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 H ...
- Spring MVC中基于注解的 Controller
终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法以响 ...
- Spring MVC中各个filter的用法
转载:http://blog.csdn.net/qyp1314/article/details/42023725 Spring MVC中各个filter的用法 2014-12-19 09:08 105 ...
- Spring MVC中的HandlerMapping与HandlerAdapter
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
- Spring MVC 中的基于注解的 Controller(转载)
终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法 ...
随机推荐
- POJ-3278(BFS)
题目: ...
- KinSlideshow参数设置说明
[引用来自:http://blog.sina.com.cn/s/blog_4f4f4a5901014zn1.html] Jquery.KinSlideshow参数设置说明: 附:所有参数列表 in ...
- mvc5 + ef6 + autofac搭建项目(三)
前面已经基本完成了框架的搭建,后面就是实现了,后面主要说下前端的东西bootstrap的使用和相关插件. 看图: 实现比较简单,在主页面只引入共用部分的 js等相关包,毕竟不是所有页面都需要列表以及其 ...
- 重温sql语句中的join操作
1.join语句 Sql join语句用来合并两个或多个表中的记录.ANSI标准SQL语句中有四种JOIN:INNER,OUTER,LEFTER,RIGHT,一个表或视图也可以可以和它自身做JOIN操 ...
- obj.onclick=fnClick与obj.onclick=fnClick()的区别
先说结论:这段代码浏览器会报错,提示 aDiv[this.index] is undefined 所以正确的写法应该是去掉(),直接写为function fnClick. 不加括号的话,相当于指定 ...
- Codeforces 553C Love Triangles(图论)
Solution: 比较好的图论的题. 要做这一题,首先要分析love关系和hate关系中,love关系具有传递性.更关键的一点,hate关系是不能成奇环的. 看到没有奇环很自然想到二分图的特性. 那 ...
- Microsoft Visual Studio 2010 遇到了异常,可能是由某个扩展导致的。 转载
问题: 今天打开好久没用的Microsoft Visual Studio 2010 ,刚才创建了一个C++工程,错误就出现了. 只要在VS2010源码编辑器中输入一个字符,它就报错 ":Mi ...
- html5新增操作类名方式 classList
如果一个元素有多个类名,要如何删除呢,jqeury提供了removeClass()这个api,如果不用插件,自己封装,可以这样 function removeClass(elm,removeClass ...
- JavaScript学习总结【2】、JS基础
1.JS 命名规范 命名规范是很有必要的,可增强代码的可读性,一眼就能看懂要表达的意思,规范就是符合规则,使代码有利于后期维护,也能很大程度的提高开发效率.一个正常的网站有很多 JS 代码,如果在编写 ...
- 【Java多线程与并发库】4.传统线程同步通信技术
我们先通过一道面试题来了解传统的线程同步通信. 题目:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程又循环100次,如此循环50次,请写出程序. 我没有看答案, ...