Spring MVC框架基本是Java服务中不可缺少的功能,很多时候我们只知道如何使用,并不清楚如何解析请求的,如何实现Restful、如何返回结果甚至渲染页面的。本文就从源码讲解DispatcherServlet如何帮助我们解析客户端发起的请求。
本文结合SpringBoot讲解,先引用一张架构图,先大致知道每个组件的作用,后续再一一剖析。
1.DispatchServlet
处理请求的核心类
1.引入容器
我们先查看SpringBoot自动装配类,确定是否会在容器中引入,同时了解一下除了核心类外还有什么被引入到容器中,我们打开spring-boot-autoconfigure-2.3.7.RELEASE.jar包的spring.factories文件可以看到DispatcherServletAutoConfiguration配置类会被自动装配进我们容器中。
2.定位具体方法处
这个时候我们肯定还是不清楚,DispatcherServlet调用了哪个方法进行处理,我们可以搜索do开头的非私有方法,可以看到有两个 doService和doDispatch,不知道哪个没关系,可以在两个方法打个断点,然后给当前应用发起个请求,看看最终会进入到哪个方法中。
根据debug结果,我们可以发现两个方法都会执行,先执行doService后执行doDispatch。
那么我们可以确定doDispatch就是具体请求解析和执行的位置。
3.doDispatch
1.为当前请求选择合适的处理器(即Controller)
HandlerExecutionChain mappedHandler = null;
// 省略...
mappedHandler = getHandler(processedRequest);
进入该方法,可以发现我们会有很多个处理器选择器(HandlerMapping),会根据具体的请求选择是否有匹配的handler。
// 初始化时候会从容器中获取,获取不到会设置默认的handlermapping
@Nullable
private List<HandlerMapping> handlerMappings;
/**
* 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) {
// 如果能选择到合适的handlermapping
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
接着我们进入到getHandler中看看是如何选择合适的handler的,我们会发现getHandler这个方法,只在抽象类做了统一实现,所以我们定位到AbstractHandlermapping::getHandler中
/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 核心方法,用于选择handler
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);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
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;
}
我们继续进入到核心方法getHandlerInternal(request)中。发现最后都会调用到AbstractHandlerMethodMapping::getHandlerInternal中
/**
* 根据request寻找合适的handler
*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
// 核心方法用于获取handlermethod返回
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
我们进入核心方法中可以看到它调用了内部mappingRegistry来进行查找。而不同的handlermapping实现类,内部的MappingRegistry都是不同的对象。
所以可以从源码看出来,最终就是通过MappingRegistry来找到恰当的HandlerMethod(Controller)。 而最后它会封装成HandlerExecutionChain返回给DispatchServlet。 本质是把handler、interceptors一起封装进行返回。
2.MappingRegistry如何将Controller注册的?
上节说到,最终我们会根据MappingRegistry来进行handler方法的查找,那么这个mapping映射是怎么注册的呢?
我们观察MappingRegistry这个类,它是在AbstractHandlerMethodMapping中的内部类,我们观察有没有什么方法是往这个集合添加元素的。
我们看到内部有个方法register,比较像是注册的类,这个时候我们可以给个断点,运行后印证一下自己想法。
最终我们定位到AbstractHandlerMethodMapping的afterPropertiesSet方法。 最后恍然大悟,原来是创建bean的时候,回调了初始化方法来注册所有的handler。
3.拦截器调用
我们实现的拦截器就是在此处调用的,方法处理前,处理后,完成结果封装
4.具体Controller执行
前面说了具体执行方法封装成了HandlerMethod,但是因为HandlerMethod类型不统一,所以为了能够统一的执行,封装了一层HandleAdpater适配器,不同适配器处理不同类型的HandlerMethod
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 不同的HandlerMethod选择对应的适配器来执行。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
}
最后关键执行方法。
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
进入到handleInternal中
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 执行处理方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
进入到invokeHandlerMethod中,可以发现核心方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 调用反射执行请求,获取返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 处理返回值,比如@ResponseBody就是在此处执行
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
4.总结
本文对Spring MVC执行请求过程做了核心方法的讲解,解释了其中主流程核心源码。Spring MVC有九大组件,不同组件负责处理入参、结果、视图、异常等等,各有所长,涵盖http请求所有类型。
参考:https://blog.csdn.net/qq_44891295/article/details/116809998
https://zhuanlan.zhihu.com/p/354131900
https://blog.csdn.net/weixin_38413579/article/details/97939757
评论