SpringMVC-dispatcherServlet

SpringMVC整体运行机制

SpringMVC整体运行机制
mCipvD.jpg

  • 用户发送请求至前端控制器DispatcherServlet

  • DispatcherServlet收到请求调用HandlerMapping处理器映射器。

  • 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

  • DispatcherServlet通过HandlerAdapter处理器适配器调用处理器

  • 执行处理器(Controller,也叫后端控制器)。

  • Controller执行完成返回ModelAndView

  • HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet

  • DispatcherServlet将ModelAndView传给ViewReslover视图解析器

  • ViewReslover解析后返回具体View

  • DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。

  • DispatcherServlet响应用户。

从上面可以看出,DispatcherServlet有接收请求,响应结果,转发等作用。有了DispatcherServlet之后,可以减少组件之间的耦合度。

HandlerMapping

当DispatcherServlet接受到客户端的请求后,SpringMVC 通过 HandlerMapping 找到请求的Controller HandlerMapping 在这里起到路由的作用,负责找到请求的Controller。

Spring MVC 默认提供了4种 HandlerMapping的实现

  • org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
    通过配置请求路径和Controller映射建立关系,找到相应的Controller
    访问方式: http://ip:port/project/userlist.htm
1
2
3
4
5
6
7
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/userlist.htm">userController</prop>
</props>
</property>
</bean>
  • org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping
    通过 Controller 的类名找到请求的Controller
    访问方式: http://ip:port/project/user
    注:类的首字母要小写
1
2
@Controller
public class UserController extends AbstractController
  • org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
    通过定义的 beanName 进行查找要请求的Controller
    访问方式: http://ip:port/project/users
    注:bean name属性必须要以“/”开头。
1
<bean id="userController" name="/users" class="cn.com.infcn.web.controller.UserController"></bean>
  • org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
    通过注解 @RequestMapping(“/userlist”) 来查找对应的Controller。
1
2
3
4
5
6
7
8
9
10
11
12
@Override
@RequestMapping("/userlist")
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {

List<User> userList = new ArrayList<User>();

userList.add(new User("zhangsan", 18));
userList.add(new User("list", 16));

return new ModelAndView("userList", "users", userList);
}

HandlerMapping初始化是SpringMVC九大组件初始化总函数initStrategies()中的initHandlerMapping()方法完成的。

判断detectAllHandlerMappings是否为true,如果为true,则加载当前系统中所有的HandlerMapping类,如果为false,则加载bean名称为“handlermapping”的HandlerMapping实现类。如果还没有找到HandlerMapping,则加载SpvingMVC 配置文件中,默认配置的HandlerMapping。

以SimpleUrlHandlerMapping 为例,简单分析下HandlerMapping

从SimpleUrlHandlerMapping 类结构中我们可以发现urlMap属性。这个urlMap中保存了xml中配置的映射关系,通过setMappings方法填充到urlMap中。

1
2
3
4
5
6
7
8
9
10
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {

private final Map<String, Object> urlMap = new LinkedHashMap<String, Object>();

// Map URI paths to handler bean names .
public void setMappings(Properties mappings) {
CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
}

}
1
2
3
4
5
6
7
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/userlist.htm">userController</prop>
</props>
</property>
</bean>

这个urlMap就充当了SpringMVC的路由功能。每个HandlerMapping都会有一个这样的Map。

当用户请求时,真正的请求会执行到DispatcherServlet的doDispatch()方法。

1
2
3
4
5
6
7
8
// 根据请求获取其handler
// handlerMapping解析url得到的controller信息
mappedHandler = getHandler(processedRequest, false);

if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

通过getHandler() 方法去查找HandlerMapping中查找对应的Controller,与拦截器一起封装成HandlerExecutionChain。如果找不到,则执行noHandlerFound() 方法。

getHandler() 方法: 迭代查找所有的HandlerMapping,如果找到则直接返回。

noHandlerFound()方法: 如果找不到Controller 则后台抛出异常或响应给前台 404

HandlerAdapter

DispatcherServlte会根据handlerMapping传过来的controller与已经注册好了的HandlerAdapter一一匹配,看哪一种HandlerAdapter是支持该controller类型的,如果找到了其中一种HandlerAdapter是支持传过来的controller类型,那么该HandlerAdapter会调用自己的handle方法,handle方法运用java的反射机制执行controller的具体方法来获得ModelAndView。

1
2
3
4
5
6
7
8
疑惑:不理解为什么通过handlerMapping已经拿到具体的controller了 
为什么还要匹配一个该controller类型的HandlerAdapter通过反射去执行该controller的内容呢?

回答:因为存在多种类型的controller,不同类型的controller调用方式不同。如果不采用适配器模式,
在DispatcherServlet中就需要出现"判断controller类型->根据类型完成不同方式的调用"的逻辑,
而加入HandlerAdapter之后,DispatcherServlet只需要为handlerMapping拿到的
具体controller匹配一个适配的controller调用器,接下来只需要调用一个统一的接口方法ha.handle()即可,
具体的不同controller类的不同调用方式交给适配的调用器去完成。
1
2
3
4
5
6
7
8
9
10
11
12
// 从handlerMapping中根据url拿到对应controller
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

// 根据controller类型匹配相应的handleradapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// controller实际执行
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

HandlerAdapter getHandlerAdapter(Object handler)方法

1
2
3
4
5
6
7
8
9
10
11
12
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: Does your handler implement a supported interface like Controller?");
}

HandlerAdapter接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

public interface HandlerAdapter {

/**
* Given a handler instance, return whether or not this HandlerAdapter can
* support it. Typical HandlerAdapters will base the decision on the handler
* type. HandlerAdapters will usually only support one handler type each.
* <p>A typical implementation:
* <p><code>
* return (handler instanceof MyHandler);
* </code>
* @param handler handler object to check
* @return whether or not this object can use the given handler
*/
boolean supports(Object handler);

/**
* Use the given handler to handle this request.
* The workflow that is required may vary widely.
* @param request current HTTP request
* @param response current HTTP response
* @param handler handler to use. This object must have previously been passed
* to the <code>supports</code> method of this interface, which must have
* returned <code>true</code>.
* @throws Exception in case of errors
* @return ModelAndView object with the name of the view and the required
* model data, or <code>null</code> if the request has been handled directly
*/
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

/**
* Same contract as for HttpServlet's <code>getLastModified</code> method.
* Can simply return -1 if there's no support in the handler class.
* @param request current HTTP request
* @param handler handler to use
* @return the lastModified value for the given handler
* @see javax.servlet.http.HttpServlet#getLastModified
* @see org.springframework.web.servlet.mvc.LastModified#getLastModified
*/
long getLastModified(HttpServletRequest request, Object handler);

}
1
Handler是用来干活的工具;HandlerMapping用于根据需要干的活找到相应的工具;HandlerAdapter是使用工具干活的人

参考阅读

Donate here.