Spring MVC 执行流程 (DispatcherServlet)
- Servlet容器将http请求封装为
HttpServletRequest
和HttpServletReponse
- 交于Spring MVC 的前端总控制器
DispatcherServlet
处理(门面模式) - 执行
DispatcherServlet#service
- 执行
DispatcherServlet#doService
- 执行
DispatcherServlet#doDispatch
HandlerExecutionChain
->mappedHandler
HandlerAdapter
- 执行
HandlerExecutionChain
前置执行方法 ->applyPreHandle
HandlerAdapter
执行HandlerAdapter#handle
获取一个ModelAndView
- 给
ModelAndView
设置viewName (转发路径) - 执行
HandlerExecutionChain
后置方法 ->applyPostHandle
- 执行
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;
}
}
最终结果截图