一、Spring MVC的执行流程
上面介绍了Spring MVC的使用,配置了一个DispatcherServlet作为所有请求的入口,DispatcherServlet继承自抽象类FrameworkServlet,而FrameworkServlet又继承自HttpServlet,所有当有请求进来时,会先进入到FrameworkServlet的service()方法中,而在该方法中又会去调用父类HttpServlet的service()方法,该类的方法在《从Servlet到Spring MVC》中已经介绍过了,它会根据不同的请求去调用doGet()、doPost()等方法
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);
}
}
而FrameworkServlet中全部实现了这些doXxx()方法,而在这些doXxx()方法中,又都会去调用processRequest()方法
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);
}
在processRequest()方法中,除了加载一些上下文信息、绑定参数之后,最核心的就是去调用DispatcherServlet的doService()方法
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
doService(request, response);
}
在doService()方法中,会设置一些请求的参数,但最核心的是去调用doDispatch()方法,而doDispatch()才是SpringMVC处理请求最核心的方法,下面介绍一下doDispatch()方法的执行流程,以@RequestMapping注解为例,这个也是我们开发中用的最多的
1.1 获取处理器执行链
在doDispatch()方法中,首先会去解析请求,得到一个处理器执行链,包含了拦截器集合和处理方法,而在映射的时候,就会用得到HandlerMapping,在《从Servlet到Spring MVC》中,介绍了如何配置HandlerMapping,如果没有配置,SpringMVC提供了四个默认的配置
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
// 进行映射
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
}
调用getHandler()方法就是去遍历所有的映射器处理器,看哪个能根据请求的url匹配到处理器方法
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
/** 拿到所有handlerMappings (容器启动阶段初始化:拿到所有实现了HandlerMapping的Bean)
* @see DispatcherServlet#initHandlerMappings
* 测试发现: 不同的HandlerMapping可以有相同path, 谁先解析到就用哪个
* */
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
1.1.1 获取处理器
调用getHandlerInternal()方法来获取处理器
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
……
}
首先获取request请求中的url,然后调用lookupHandlerMethod()去匹配
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 通过UrlPathHelper对象,用于来解析从们的request中解析出请求映射路径
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
// 通过lookupPath解析最终的handler——HandlerMethod对象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
在lookupHandlerMethod()中,根据url来匹配mappingRegistry.pathLookup,pathLookup是一个MultiValueMap,它最大的特点是value可以重复,所以同一个url可能会匹配到多个RequestMappingInfo
pathLookup的数据,是在SpringMVC容器启动的时候,就回去加载解析的,以@RequestMapping注解为例,在Bean实例化的过程中,就回去解析类中的方法是否有@RequestMapping注解,然后拼接url作为pathLookup的key,将类以及方法封装成RequestMappingInfo
如果通过pathLookup找到了url相匹配的处理器,这个时候还是不够,还需要去解析@RequestMapping注解中的mthod、header等属性是否匹配,RequestMappingInfo中包含了@RequestMapping注解所有配置的条件匹配器,比如ParamsRequestCondition、HeadersRequestCondition等
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// pathLookup<path,RequestMappingInfo>会在初始化阶段解析好
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
// 如果根据path能直接匹配的RequestMappingInfo 则用该mapping进行匹配其他条件(method、header等)
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// 如果无path匹配,用所有的RequestMappingInfo 通过AntPathMatcher匹配
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
……
}
如果直接通过url没有在pathLookup找到,则会去调用getMatchingMapping()方法,通过pathMatcher来匹配,pathMatcher是一个AntPathMatcher类的实例,提供了按照通配符? * {
匹配的逻辑
如果匹配到多个按照? > * > {} >**
进行排序,然后取最匹配的那一个
protected String getMatchingMapping(String pattern, HttpServletRequest request) {
String lookupPath = this.pathHelper.getLookupPathForRequest(request);
String match = (this.pathMatcher.match(pattern, lookupPath) ? pattern : null);
if (match != null) {
this.matches.add(match);
}
return match;
}
1.1.2 封装处理器链
得到处理器之后,还需要取获取配置的拦截器,然后封装成一个执行器链
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
……
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
}
调用getHandlerExecutionChain(),首先将处理器封装成一个HandlerExecutionChain,然后遍历配置的所有拦截器,只有与当前处理器匹配的,就加入到HandlerExecutionChain的interceptorList中,在执行执行器的方法前后,会调用拦截器的方法
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(request)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
1.2 获取处理器适配器
获取完处理器之后,就要为处理器匹配最合适的适配器,那么适配器是干嘛的,简单的来说就是解析参数的。
以@RequestMapping为例,它的方法参数,可以通过@RequestBody、@RequestParam等注解来获取参数,那么肯定就要有能够解析这些注解的适配器
如果没有通过Spring MVC的配置文件进行配置,默认有HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter和HandlerFunctionAdapter四种
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
// 进行映射
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 找到最合适的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
}
寻找最合适的处理器适配器也很简单,遍历所有的是适配器,然后调用它们的support()方法进行匹配
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
}
supports()的方法也很简单,以HttpRequestHandlerAdapter为例:
只需要判断当前处理器是不是HttpRequestHandler的实现类即可,而RequestMappingHandlerAdapter的supports()方法永远返回true
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
1.3 执行处理器方法
在执行处理器的方法前,首先会去执行拦截器的前置方法
// 前置拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 返回false就不进行后续处理了
return;
}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.afterCompletion(request, response, this.handler, ex);
}
}
调用处理器的handle()方法,然后调用invokeHandlerMethod()方法设置一些常用的配置,比如:请求参数解析器、返回参数解析器、数据绑定器等,然后调用ServletInvocableHandlerMethod的invokeAndHandle()方法,执行处理器方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = null;
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
……
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 把我们的请求req resp包装成 ServletWebRequest
ServletWebRequest webRequest = new ServletWebRequest(request, response);
// 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
// 配置的InitBinder,用于进行参数的绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取容器中全局配置的ModelAttribute和当前HandlerMethod所对应的Controller 中配置的ModelAttribute,
// 这些配置的方法将会在目标方法调用之前进行调用
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 封装handlerMethod,会在调用前解析参数、调用后对返回值进行处理
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 让invocableMethod拥有参数解析能力
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 让invocableMethod拥有返回值处理能力
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 让invocableMethod拥有InitBinder解析能力
invocableMethod.setDataBinderFactory(binderFactory);
// 设置ParameterNameDiscoverer,该对象将按照一定的规则获取当前参数的名称
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// ModelAndView处理容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 将request的Attribute复制一份到ModelMap
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// *调用我们标注了@ModelAttribute的方法,主要是为我们的目标方法预加载
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 重定向的时候,忽略model中的数据 默认false
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// *对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
// 还会判断是否需要将FlashAttributes封装到新的请求中
return getModelAndView(mavContainer, modelFactory, webRequest);
}
1.1.1 请求参数解析
在执行处理器方法之前,需要先解析参数,得到处理器方法参数列表的参数值
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//*获取我们目标方法入参的值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
……
}
参数解析的时候,先获取方法的参数列表,然后遍历这些参数,在遍历这些参数的时候,需要添加ParameterNameDiscoverer对象,才能得到参数的名称
然后获取所有的参数解析器,判断是否可以解析当前参数,以RequestParamMethodArgumentResolver参数解析器为例,就是判断当前参数是否有@RequestParam注解,如果可以解析,就直接调用resolveArgument()进行解析和数据绑定
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取目标方法参数的描述数组对象
MethodParameter[] parameters = getMethodParameters();
//用来初始化我们对应参数名称的参数值得数组
Object[] args = new Object[parameters.length];
//循环我们得参数名数组
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
//为我们得MethodParameter设置参数名称探测器对象
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs); // providedArgs
if (args[i] != null) {
continue;
}
// * 获取所有的参数解析器,然后筛选出合适的解析器
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
// 通过上面筛选的 参数解析器来解析我们的参数
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
return args;
}
1.1.2 执行处理器方法
解析完参数之后,利用反射,调用处理器的方法
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//*获取我们目标方法入参的值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//真的的调用我们的目标方法
return doInvoke(args);
}
1.1.3 返回参数解析
执行完处理器的方法之后,会得到返回值,返回值可能是Json,也可以能是ModelAndView,把返回值封装在ModelAndViewContainer中
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
/*真正的调用我们的目标对象 很重要 很重要*/
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
……
// 遍历当前容器中所有ReturnValueHandler,判断哪种handler支持当前返回值的处理,
// 如果支持,则使用该handler处理该返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
最后,调用getModelAndView()处理之前的ModelAndViewContainer,封装成一个ModelAndView对象
// 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
// 还会判断是否需要将FlashAttributes封装到新的请求中
return getModelAndView(mavContainer, modelFactory, webRequest);
1.4 视图解析
执行完处理器方法之后,接着是去执行所有拦截器的postHandle()方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 如果mv有 视图没有,给你设置默认视图
applyDefaultViewName(processedRequest, mv);
//后置拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
最后才是去渲染视图,在render()方法中,调用resolveViewName()方法去解析视图,然后返回一个View对象,最后调用View的render()方法去渲染视图
// 渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
if (mv != null && !mv.wasCleared()) {
// 解析、渲染视图
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 解析视图名
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
}
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/153647.html