目录
前言
上一篇文章中分析了tomcat的请求处理流程,在springmvc项目中,tomcat接收到请求后,最终会将请求发送到dispatcherServlet中,dispatcherServlet可以看成是tomcat中的一个普通servlet,本文就dispatcherServlet的处理过程进行分析。
一、service方法
上一篇分析到tomcat会最终调用servlet.service(request,response)方法,我们先从service方法进行分析,最终调用到p'rdispatcherServlet继承了FrameworkServlet,下面进行源码分析
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
}
。。。。。
}
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
try {
doService(request, response);
}
}
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
doDispatch(request, response);
}
}
最终调用到doDispatch()方法,具体的controller调用都在这个方法中,下面进行重点分析。
二、doDispatch方法
1.总体流程
doDispatch方法首先是使用请求地址匹配找到当前请求对应的mapperHanler,然后获取相应的HandlerAdapter以及当前请求对应的拦截器,然后执行拦截器的前置方法,再执行controller对应方法,最后执行拦截器的后置方法,当在这一系列流程中发生异常时,会执行异常处理器的方法,下面我将对一些重点方法进行分析。
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);
// 2.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.这里并不是直接返回controller,
// 而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handler和interceptors.
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
// 如果handler为空,则返回404
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//3. 获取处理request的处理器适配器handler adapter
// 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 (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 4.拦截器的预处理方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//5.实际的处理方法 RequestMappingHandlerAdapter继承了AbstractHandlerMethodAdapter
//对于不同的handler有不同不同的处理方式,此处使用的是适配器模式,可以避免对handler类型的判断,减少if-else
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//6.拦截器的后置处理方法
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);
}
}
}
}
2.getHandler方法
gethandler(processedRequest)可以为当前请求获取对应的controller方法,下面主要对这个方法进行分析。
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//返回访问的路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock(); //获取读锁 为啥用锁,似乎启动的时候对其赋值,会有影响吗
try {
//根据路径匹配对应的controller方法,好玩的是匹配到多个会进行排序,优先级相等才会报错
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
//各种条件的过滤,包括请求方式,
// 同一路径不同请求方式对应的请求通过地址匹配两个match,通过请求方式判断只会留下一个
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock(); //释放读锁
}
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) { //根据匹配度排序
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
lookupPath + "] : " + matches);
}
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
3.applyPreHandle方法
mappedHandler.applyPreHandle(processedRequest,response)方法负责执行所有handlerInterceptor的前置处理方法,具体代码如下
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];
//执行前置方法,前置方法返回false结束当前请求访问
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
4.handle方法
handle方法负责执行controller中的处理方法,其中包括了参数值的解析,参数值的校验,具体方法的执行和返回值的处理,下面将对之中的重点方法进行分析。实验中使用json传递参数,所以使用了RequestResponseBodyMethodProcessor的resolveArgument方法进行参数解析,同时进行参数校验。校验不通过的时候会抛出MethodArgumentNotValidException异常,可以自定义相关的异常处理器进行统一处理。
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
//读取参数
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
//调用hibernate的校验方法,手动抛出MethodArgumentNotValidException异常
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
return adaptArgumentIfNecessary(arg, parameter);
}
所有在解析请求体body的过程中还有一个扩展点,可以通过实现RequestBodyAdvice,在body解析前后进行操作,如字符串解密。
//body读取前操作
inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
body = genericConverter.read(targetType, contextClass, inputMessage);
//body读取后操作
body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
5.applyPostHandle方法
mappedHandler.applyPreHandle(processedRequest,response,mv)方法负责执行所有handlerInterceptor的后置处理方法,具体代码如下,可以看到后置处理器的处理顺序还前置处理器正好相反。
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, 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);
}
}
}
6.processDispatchResult方法
processDispatchResult方法是对请求的结果进行处理,同时,这个方法也会对请求中产生的异常进行处理,自定义的异常处理切面就是在此处起作用。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
//如果发生ModelAndViewDefiningException,直接返回对应的ModelAndView
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);
}
}
。。。。。
}
主要通过getExceptionHandlerMethod获取对应的异常处理器
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = null;
if (handlerMethod != null) {
// Local exception handler methods on the controller class itself.
// To be invoked through the proxy, even in case of an interface-based proxy.
handlerType = handlerMethod.getBeanType();
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
// For advice applicability check below (involving base packages, assignable types
// and annotation presence), use target class instead of interface-based proxy.
if (Proxy.isProxyClass(handlerType)) {
handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
}
}
//找到对应的ExceptionAdvice并进行处理
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
}
}
}
return null;
}
三、逗号拼接字符串转list那件事
最近遇到一个好玩的问题,在参数中定义一个list,然后请求中传入逗号拼接的字符串是能正确传输的,这是挺有意思的,springmvc是在哪实现的这个字符串转list,最终是在StringToCollectionConverter中实现的,其实也可以通过StringToNumberConverterFactory实现string到数字的转换
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
String string = (String) source;
//逗号分割
String[] fields = StringUtils.commaDelimitedListToStringArray(string);
TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),
(elementDesc != null ? elementDesc.getType() : null), fields.length);
if (elementDesc == null) {
for (String field : fields) {
target.add(field.trim());
}
}
else {
for (String field : fields) {
//自动去掉空格,将分割后字符串转成list中元素的类型(如string->long),调用StringToNumberConverterFactory中的convert方法
Object targetElement = this.conversionService.convert(field.trim(), sourceType, elementDesc);
target.add(targetElement);
}
}
return target;
}
实也还有一种情况,我们可以在get请求的接受参数中直接放置一个自定义对象,然后通过参数对其中的属性赋值,还是挺有意思,Springmvc帮我们实例化了这个对象,并且将其中的属性与穿进去的参数绑定,使用ModelAttributeMethodProcessor进行操作。
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired &&
//不是基本对象
!BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
}
else {
// Create attribute instance
try {
//创建属性对象,一般就是调用对象的无餐构造
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
}
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
//绑定参数值,将参数赋值给对象的属性,还会待用string->long等转化器
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
}
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
总结
本文主要对dispatcherServlet的处理流程进行了分析,流程比较复杂,只针对其中的重点方法进行了分析,在以后遇到相关的问题再进行补充。