SpringMVC源码从入门到放弃-DispatcherServlet
引
本系列文章主要对SpringMVC源码作详细介绍,面向对Spring有所了解的Java程序员
本文拟解惑几个问题
1.http请求如何映射到Controller的
2.Controller是如何被执行的
DispatcherServlet
在有SpringMVC之前,我们要对外提供http接口,那么我们需要编写HttpServlet,并在web.xml中配置http请求到HttpServlet的映射关系
在有SpringMVC之后,开发人员开始写Controller并用注解@
RequestMapping("\test")定义http请求到Controller.method()的映射关系
从HttpServlet到Controller其实是因为SpringMVC对HttpServlet进行了一层封装,对外提供一个接收所有请求的HttpServlet(DispatcherServlet),Spring把Controller加了@
RequestMapping的方法定义为HandlerMethod,由DispatcherServlet统一分发http请求到对应的HandlerMethod去处理
所以SpringMVC项目我们都要在web.xml中配上所有请求到DispatcherServlet的映射
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
DispatcherServlet的本质是一个HttpServlet
核心的方法有
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
doDispatch(request, response);
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
父类FrameworkServlet重写了HttpServlet的doGet()、doPost()等方法,最后都会调用DispatcherServlet的doService()方法,然后核心的执行流程在doDispatch()方法中
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//由HandlerMapping找处理该请求的HandlerMethod和拦截该请求的HandlerInterceptor,包装成HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 找到该HandlerMethod的HandlerAdapter(具体的方法调用流程)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//执行HandlerInterceptor
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler. 实际调用HandlerMethod
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//执行HandlerInterceptor
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//返回渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (ha.supports(handler)) {
return ha;
}
}
}
这里涉及到两个核心的类
1.HandlerMapping
Interface to be implemented by objects that define a mapping between
requests and handler objects.
2.HandlerAdapter
- MVC framework SPI, allowing parameterization of the core MVC workflow.
-
Interface that must be implemented for each handler type to handle a request.
- This interface is used to allow the {@link DispatcherServlet} to be indefinitely
- extensible. The {@code DispatcherServlet} accesses all installed handlers through
- this interface, meaning that it does not contain code specific to any handler type.
HandlerMapping
如官方文档里提到的,HandlerMapping定义了request和handler的映射关系,通俗点来说就是url对应的是哪个HandlerMethod
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
核心方法返回HandlerExecutionChain,包装了当前请求需要执行的Handler和interceptors
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler;
private HandlerInterceptor[] interceptors;
在DispatcherServlet.getHandler中
遍历容器中所有的HandlerMapping,为当前请求找到最匹配的HandlerMethod,包装成HandlerExecutionChain,SpringMVC默认有很多HandlerMapping实现策略,每个HandlerMapping中注册着不同的HandlerMethod映射关系
我们项目中默认对于Controller+@RequestMapping形式的HandlerMapping实现类为RequestMappingHandlerMapping
他的父类的内部中维护了请求和HandlerMethod的映射关系
class MappingRegistry {
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();
下一期我们将详细阐述HandlerMapping
HandlerAdapter
DispatcherServlet.getHandlerAdapter(Object handler)方法为当前handler找到合适的HandlerAdapter
从文档定义可以看出,HandlerAdapter定义了一个handler执行的流程,大致包括以下流程
1.请求参数解析成调用handler的参数
2.通过反射调用handler
3.处理handler的返回值
Spring上下文中有3个默认的HandlerAdapter实现,@RequestMapping定义的handler默认由RequestMappingHandlerAdapter处理
handler被交由RequestMappingHandlerAdapter的handle(HttpServletRequest request, HttpServletResponse response, Object handler)方法执行
经过层层调用,在invokeHandlerMethod()中定义了执行流程
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//包装请求,提供额外方法
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//包装执行器
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);//参数解析
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);//返回值处理
invocableMethod.setDataBinderFactory(binderFactory);//参数解析时特殊类型处理
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);//
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//执行调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
我们先来看看invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//执行调用
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
try {//处理返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
}
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
//
Object returnValue = doInvoke(args);
return returnValue;
}
最后实际调用Controller的方法的就是doInvoke中,传入参数,利用反射进行调用
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
return getBridgedMethod().invoke(getBean(), args);
}
以上就是一次htpp请求的调用过程,下一篇文章我们将详细阐述handleMethod的注册过程
继承结构
文章的末尾我们来看看DispatcherServlet的继承结构
DispatcherServlet
用于HTTP请求handler/controllers的中央调度程序,例如用于Web UI controllers或基于HTTP的远程服务导出程序。调度注册的handlers以处理Web请求,提供方便的url映射和异常处理功能。
这个servlet非常灵活:它可以在任何工作流程中使用,只要实现了合适的adapter类。它提供了以下功能,将其与其他请求驱动的Web MVC框架区分开来:
1.基于JavaBeans配置机制。
2.它可以使用任何HandlerMapping实现 - 预先构建或作为应用程序的一部分提供 - 控制分发 requests 到 handler objects。默认是 BeanNameUrlHandlerMapping和 RequestMappingHandlerMapping。HandlerMapping对象可以定义为servlet应用程序上下文中的bean,实现HandlerMapping接口,覆盖默认的HandlerMapping(如果存在)。
3.它可以使用任何HandlerAdapter; 这允许使用任何处理程序接口。默认适配器HttpRequestHandlerAdapter, 默认 RequestMappingHandlerAdapter 也会被注册。HandlerAdapter对象可以在应用程序上下文中作为bean添加,并覆盖默认的HandlerAdapter。
4。调度程序的exception解决策略可以通过一个HandlerExceptionResolver例如映射到错误页面的特定例外来指定 。默认的是 ExceptionHandlerExceptionResolver, ResponseStatusExceptionResolver和 DefaultHandlerExceptionResolver。这些HandlerExceptionResolvers可以通过应用程序上下文重写。
5.其视图解析策略可以通过ViewResolver 实现来指定,将符号视图名称解析为视图对象。默认是 InternalResourceViewResolver。可以在应用程序上下文中将ViewResolver对象添加为bean,覆盖默认的ViewResolver。
注意:@RequestMapping只有在调度程序中存在相应HandlerMapping和HandlerAdapter时才会处理。 这是默认情况。但是,如果您正在定义自定义HandlerMappings 或者HandlerAdapters,那么您需要确保相应的自定义 RequestMappingHandlerMapping和/或RequestMappingHandlerAdapter 定义 - 只要您打算使用@RequestMapping。
Web应用程序可以定义任意数量的DispatcherServlet。 每个servlet将在其自己的名称空间中运行,使用映射,处理程序等加载其自己的应用程序上下文。只有ContextLoaderListener共享根据应用程序上下文。
核心方法
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {