Spring MVC 执行流程 (DispatcherServlet)

Spring MVC 执行流程 (DispatcherServlet)

  1. Servlet容器将http请求封装为HttpServletRequestHttpServletReponse
  2. 交于Spring MVC 的前端总控制器 DispatcherServlet 处理(门面模式)
  3. 执行DispatcherServlet#service
  4. 执行DispatcherServlet#doService
  5. 执行DispatcherServlet#doDispatch
  6. HandlerExecutionChain -> mappedHandler
  7. HandlerAdapter
  8. 执行HandlerExecutionChain 前置执行方法 -> applyPreHandle
  9. HandlerAdapter 执行 HandlerAdapter#handle 获取一个 ModelAndView
  10. ModelAndView设置viewName (转发路径)
  11. 执行HandlerExecutionChain 后置方法 -> applyPostHandle
  12. 执行DispatcherServlet#processDispatchResult -> 处理调度结果 (返回前端的数据)

通过图片可以看出 HandlerExecutionChain -> mappedHandler 其中的 handler 就是我们Controller层中对应映射的执行方法。在mappedHandler还有一些HandlerInterceptor 方法执行之前的拦截器

DispatcherServlet#doDispatch 源码

以下为DispatcherServlet类的doDispatch方法

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.
				// 根据当前的HttpServletRequest获取HandlerExecutionChain
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					//没有获取到目前处理器
					//但是根据debug情况当404时其实是有HandlerExecutionChain的
					//并且其中的handler为ResourceHttpRequestHandler
					//ResourceHttpRequestHandler [locations=[class path resource [META-INF/resources/], 
					//class path resource [resources/], class path resource [static/], 
					//class path resource [public/], ServletContext resource [/]], 
					//resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@7b92bcde]]
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 获取 HandlerAdapter (适配器)
				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;
					}
				}
				// 执行HandlerExecutionChain前置方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// 使用HandlerAdapter执行方法
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				// 设置 viewName 内部转发 
				applyDefaultViewName(processedRequest, mv);
				// 执行HandlerExecutionChain后置方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			
			// 执行方法过程中如果发生异常 就将 Exception dispatchException 赋值
			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);
			}
			// 页面渲染 404 500 等页面也会进行渲染 将上一步过程中的异常一起渲染 常见的就是500状态码错误
			// 处理调度结果 (返回前端的数据)
			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);
				}
			}
		}
	}

HandlerAdapter是如何执行handle方法 (RequestMappingHandlerAdapter)

在上述的方法进行debug 最后会执行 RequestMappingHandlerAdapter 的 handle 方法

首先RequestMappingHandlerAdapter 是继承 AbstractHandlerMethodAdapter这个抽象类的

所以先看看AbstractHandlerMethodAdapter

@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {
	
	//注意这里 Object handler 进行了强制类型转换
	return handleInternal(request, response, (HandlerMethod) handler);
}

@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;

所以在观察RequestMappingHandlerAdapter之前,我们可以先看看 HandlerExecutionChain 是什么样的


public class HandlerExecutionChain {

	private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

	//这里的 handler 是 Object 类型的
	private final Object handler;

	//有一组按照顺序执行的HandlerInterceptor
	
	@Nullable
	private HandlerInterceptor[] interceptors;

	@Nullable
	private List<HandlerInterceptor> interceptorList;

	private int interceptorIndex = -1;
	
	...
}

HandlerInterceptor 是什么样子的

public interface HandlerInterceptor {

	//前置处理方法
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}

	//后置处理方法
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}
	
	//完成请求处理后,即渲染后回调视图。将调用处理程序执行的任何结果,从而允许进行适当的资源清理。(谷歌翻译)
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

}

我们再看看AbstractHandlerMethodAdapter#handleInternal中涉及到了handler的强制类型转换 转换成了HandlerMethod
那么就看看HandlerMethod

public class HandlerMethod {

	/** Logger that is available to subclasses */
	protected final Log logger = LogFactory.getLog(getClass());

	private final Object bean;

	@Nullable
	private final BeanFactory beanFactory;

	private final Class<?> beanType;

	//方法
	private final Method method;
	
	//桥接方法
	private final Method bridgedMethod;

	//方法参数的描述
	private final MethodParameter[] parameters;

	@Nullable
	//HTTP协议的状态码 200 500 404 403 等
	private HttpStatus responseStatus;

	@Nullable
	private String responseStatusReason;

	@Nullable
	private HandlerMethod resolvedFromHandlerMethod;

	...
	
	/**
	 * Create an instance from a bean instance and a method.
	 */
	public HandlerMethod(Object bean, Method method) {
		Assert.notNull(bean, "Bean is required");
		Assert.notNull(method, "Method is required");
		this.bean = bean;
		this.beanFactory = null;
		this.beanType = ClassUtils.getUserClass(bean);
		this.method = method;
		this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
		this.parameters = initMethodParameters();
		evaluateResponseStatus();
	}
}

我们是时候看看RequestMappingHandlerAdapter#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 {
			// No synchronization on session demanded at all...
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}
		return mav;
	}

可以看出 最终都是 ==mav = invokeHandlerMethod(request, response, handlerMethod);==这句代码


//这里就是invoke的核心了
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		//这里两步就获取一些参数绑定  InvocableHandlerMethod 其实 在之前的一篇文章中有涉及到Spring MVC 参数获取
		//这里就是进入前的步骤 
		//CSDN 地址 https://blog.csdn.net/cmmchenmm/article/details/82774568
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

		// ServletInvocableHandlerMethod extends InvocableHandlerMethod
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
			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);

		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			if (logger.isDebugEnabled()) {
				logger.debug("Found concurrent result value [" + result + "]");
			}
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}

		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}

		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		webRequest.requestCompleted();
	}
}

使用 HandlerInterceptor 做鉴权?

首先编写一个 HandlerInterceptor 的实现类

/**
 *
 * 希望利用 HandlerInterceptor 方式实现 注解校验 Token
 * @author chenmingming
 * @date 2018/11/24
 */
@Component
public class TokenHandlerInterceptor implements HandlerInterceptor {

    @Autowired
    private ModelRepo modelRepo;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        try {

            if(handler instanceof HandlerMethod){
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                boolean needToken = handlerMethod.getMethod().getAnnotation(Token.class) != null;
                if(needToken){
                    String token = request.getHeader("token");
                    Optional<Model> byId = modelRepo.findById(token);
                    Model model = byId.get();
                    request.setAttribute("token",model);
                }
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            throw new Exception("Token Error",e);
        }

    }
}

编写配置类 可以查看Spring 官网的推荐配置 (基于 Spring Boot 2.x,Spring 5.x)

@Configuration
public class TokenWebConfigurer implements WebMvcConfigurer {

    @Autowired
    private TokenHandlerInterceptor tokenHandlerInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenHandlerInterceptor);
    }
}

编写注解以及测试Contrller


@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Token {
}

@RestController
@RequestMapping("/token")
public class TokenController {

    @GetMapping("/t")
    @Token
    public Object test(@RequestAttribute("token") Model model){
        return model;
    }

}

最终结果截图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值