SpringMVC源码学习
SpringMVC的功能
SpringMVC的最大功能就是请求分发,
通过 请求地址(uri) 找到对应的Controller类方法的映射路径,执行对应的方法。
那么就有两个问题
- 首先得找到Controller 对象
- 然后再执行Controller对象的方法
SpringMVC找Controller的流程
- 扫描整个项目(Spring 构建Bean 工厂的时候实现),定义一个Map集合
- 拿到所有加了@Controller注解的类(有很多种实现controller类的方法,这里只用注解说明)
- 遍历类里边的所有方法对象
- 判断是否加了@RequestMapper注解
- 把@RequestMapper注解的value 做为key,把找到的方法对象当做value ,存放在map集合中。
- 根据用户发送的请求拿到请求的uri(URL:http://localhost:8080/hello.do , URI:/hello.do)
- 根据请求的uri 去map集合找对应的方法对象(没有就返回404)
- 根据反射执行方法对象的方法
- …
DispatcherServlet的继承关系图
通过类图发现DispatcherServlet 其实是HttpServlet的子类.
HttpServlet有几个重要的方法 doGet(),doPost(),service();
DispatcherServlet类并没有 doGet(),doPost(),service()方法 ,通过查找发现他的父类FrameworkServlet 有这些方法,但是他们调用的是同一个processRequest() 方法.
找到processRequest() 方法后发现他进行一些处理后又调用了一个 doService() 方法 。
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response); //重点
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
我们去查找doService()方法,发现他其实是抽象方法,在DispatcherServlet类中重写了这个方法,并在方法中对公共的属性进行处理后调用了doDispatch()方法【今天的主角】
/**
* Subclasses must implement this method to do the work of request handling,
* receiving a centralized callback for GET, POST, PUT and DELETE.
* <p>The contract is essentially the same as that for the commonly overridden
* {@code doGet} or {@code doPost} methods of HttpServlet.
* <p>This class intercepts calls to ensure that exception handling and
* event publication takes place.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
* @see javax.servlet.http.HttpServlet#doGet
* @see javax.servlet.http.HttpServlet#doPost
*/
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
doDispatch(request, response); //重点
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
doDispatch()方法的源码分析!
- WebAsyncManager在普通的开发中用不到,在异步处理的时候有用到,这一块我们先忽略
- 关键部分:HandlerMappings,HandlerAdapter,HandlerExecutionChain对这几个熟悉之后基本上就懂了它到底在做什么
/**
* 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 {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
-
processedRequest意思为处理之后的请求,而它是由checkMultipart(request)方法处理的,我们看下这个方法。
(1). 判断是不是multipart的请求,默认的判断方式是先判断是不是"POST"请求,然后判断"contentType"是否以multipart开头。
(2).如果不满足multipart条件,直接返回request,也就是request没有做任何处理
(3). 如果满足multipart基本条件, 将multipartRequest转换为StandardMultipartHttpServletRequest请求。 -
getHandler() 方法是获取request的Handler的方法,handler被封装为HandlerExecutionChain,HandlerExecutionChain为handler方法加上拦截器的集合。我们定义的拦截器就在HandlerExecutionChain这个类中生效。
HandlerExecutionChain为真正的Handler对象与Interceptor的组合类
我们再去看看getHander() 方法
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
-
handlerMappings 是HandlerMapping的一个list 集合他有几个元素
requestMappingHandlerMapping (通过注解实现的Controller)
beanNameHandlerMapping (通过实现接口实现的Controller) -
通过Handler获取HandlerAdpater。HandlerAdpater作为适配器,真正干活的还是Handler,HandelAdpater做了很多的预处理,不过它的接口很简单。
public interface HandlerAdapter {
// 判断当前的HandlerAdpater是否支持handler的实例,也就是当前的adapter能否使用传过来的Handler
boolean supports(Object handler);
// 使用传过来的handler来处理请求
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
//和Servlet的getLastModified功能基本一致
long getLastModified(HttpServletRequest request, Object handler);
}
在DispatcherServlet中getHandlerAdapter的实现,则是遍历HandlerAdpaters,然后返回第一个支持handler的Adpater.
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
为什么要获取适配器?(采用适配器模式)
因为实现Controller 类的方法有很多(实现Controller接口,实现HttpRequest接口,通过注解)
- 如果是以实现接口的方式,要调用请求对应的具体方法,只需要通过调用接口的方法的实现类就行了
- 而通过注解方式实现的,则需要找到请求对应的方法对象利用反射才能实现调用这个方法
SpringMVC注解方式是怎么实现参数绑定的啦?
-
获取到的方法对象getParameters()方法获取到所有参数
Parameter[] parameters = method.getParameters();
-
遍历获取到的参数数组
因为参数类型不确定,所以要判断参数的类型,如果按照我们的想法会通过if-else 判断来实现,但是参数类型有很多,用if-else实现后果可想而知。
所以这里引入了参数处理器来处理你的参数(策略设计模式),默认提供20 多种参数处理器,好处是每一种参数处理器都是一个对象,当要修改某个参数处理器的逻辑时,只修改这个对象就好了,不修改使用的地方。如果要新增也很方便。(更多请参考设计模式:策略设计模式) -
获取到支持参数处理的参数处理器,进行参数处理
(1)获取到参数的名称。
(2)然后从request中获取参数值
(3)判空处理
(4)返回参数值 -
进行参数赋值。
- 执行HanderAdapter方法返回ModelAndView
- 将ModelAndView解析称Model
- 返回给客户端
自定义参数处理器
SpringMVC执行流程
- 一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml中指定), WEB 容器将该请求转交给 DispatcherServlet 处理
DispatcherServlet 接收到请求后, 将根据 请求信息 交给 处理器映射器 (HandlerMapping) - HandlerMapping 根据用户的url请求 查找匹配该url的 Handler,并返回一个执行链
- DispatcherServlet 再请求 处理器适配器(HandlerAdapter) 调用相应的 - Handler 进行处理并返回 ModelAndView 给 DispatcherServlet
- DispatcherServlet 将 ModelAndView 请求 ViewReslover(视图解析器)解析,返回具体 View
- DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet 将页面响应给用户
组件说明
DispatcherServlet:前端控制器
- 用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,
- 由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
HandlerMapping:处理器映射器
- HandlerMapping负责根据用户请求url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,
- 例如:配置文件方式,实现接口方式,注解方式等。
Handler:处理器
- Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
- 由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
HandlAdapter:处理器适配器
- 通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
ViewResolver:视图解析器
- View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,
- 再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
View:视图
- springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。