文章目录
1.概述
先提个问题:当浏览器发起http请求,SpringMVC是如何拦截的?又是如何处理请求并响应给用户的呢?
不清楚的读者可以接着往下看。
SpringMVC是基于Servlet实现的。有一个重要的组件就是DispatcherServlet,在配置文件中可以配置它可以拦截的一些请求,如下所示,表示拦截所有的请求。
熟悉Servlet的都清楚,拦截到请求后会执行doService方法,在DispatcherServlet当中,doService的实现如下:
@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方法,这个方法里就包括了如何处理接收到的请求,执行指定的方法逻辑,并返回给用户。下面将对doDispatch方法执行逻辑进行分析。
2.doDispatch
下图展示了doDispatch方法执行的流程
跟着源码来看看,每一步都做了什么以及为什么要这样做。
2.1 getHandler
springMVC当中提供了4种类型的Handler来处理request请求。4中类型分别如下所示:
-
@RequestMapping 也就是我们开发中经常使用的
-
传统的Servlet
-
实现了Controller的类
例如
@Component("/senlin") public class ControllerHandler implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { System.out.println(1); ModelAndView userView = new ModelAndView("userView2");//viewResolver userView.addObject("name","森林"); return userView; } }
-
实现了HttpRequestHandler的类
例如
@Component("/*/senlin") public class SimpleHandler implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("森林大帅哥"); } }
所以首先需要根据request来获取指定的Handler来处理当前请求,调用getHandler方法,getHandler源码如下所示
// DispatcherServlet
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {//遍历handlerMappings
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
遍历handlerMappings,handlerMapping是指不同类型Handler的映射的集合。handlerMapping类图如下所示
例如BeanNameUrlHandlerMapping继承了AbstractUrlHandlerMapping。这个Mapping中维护了一个散列表handlerMap。url作为key,handler作为value,如下图所示
回到上面的方法,遍历每一次都会调用 AbstractHandlerMapping中的getHandler方法,源码如下:
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);//1.从容器中根据名字获取Bean
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);//2.有了Bean并不是直接返回,而是要根据当前的请求看看有没有拦截器会拦截它,得到一个执行器链
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
该方法并不仅仅只是根据request来得到对应的handler,还需要根据当前请求,判断是否有拦截器会拦截,得到一个拦截器链。将handler和拦截器链封装到HandlerExecutionChain当中。
2.2 getHandlerAdapter
由于SpringMVC提供了4种类型的Handler,并且他们没有统一的调用接口,所以需要通过适配器来使用。同样适配器的类型也有4种。如下所示:
调用DispatcherServlet中的getHandlerAdapter,源码如下:
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");
}
遍历这4种适配器,找到支持当前Handler类型的适配器并返回。
2.3 applyPreHandle
该方法会执行HandlerInterceptor 中的preHandle方法,执行拦截器的前置处理
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
遍历之前根据request找到所有拦截器,执行拦截器的preHandle。如果此时拦截器执行结果不放行当前的请求,则不再进行下面的逻辑。
2.4 handle
通过适配器,调用handle执行真正的请求处理逻辑,并返回modelAndView。这一部分有4种handler,而这4种handler的执行的方式不同,参数处理和结果集处理的种类也很多。所以这里先清楚是在什么时候执行的请求逻辑即可。
2.5 applyDefaultViewName
上一步返回的modelAndVIew中 如果对象视图为空,则此时会设置默认的对象视图名字到MV当中
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
2.6 applyPostHandle
之前有拦截器的前置处理,那么必定会有拦截器的后置处理,此时会遍历关联的拦截器,挨个的执行postHandle方法。
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
2.7 processDispatchResult
请求解析视图并分发结果,调用processDispatchResult。若有异常,则会调用异常处理,返回一个异常视图,接着就会执行render方法进行解析视图并分发结果.最后执行拦截器链的完成处理方法。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {//异常视图处理
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);//解析视图 分发结果
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);//触发拦截器完成处理
}
}
基于视图名,遍历所有的ViewResolver,找到符合的ViewResolver进行解析,得到View对象。
如果每次请求都需要去创建解析视图的话,那么想必性能是会受到影响的。所以ViewResolver有缓存的功能,首先会走缓存,缓存中去寻找视图,有的话直接返回。否则才会去解析创建视图,创建好之后放入到缓存当中。缓存是使用LinkedHashMap来实现的,并设置了accessOrder为True。采用最近最少使用的溢出淘汰机制。也就是每次访问过的,会放到链表的最前面,当达到溢出条件的时候,会将最后面的视图删除。
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);//缓存中去寻找视图
if (view == null) {//为空 解析创建视图
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
//放入缓存
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
}
}
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace(formatKey(cacheKey) + "served from cache");
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
3.后续
这篇博客主要介绍了DispatcherServlet的核心流程,具体的每一块的细节将在后面的博客中展开