一、加载注册流程

1.在dispatch-servlet.xml中配置< mvc:annotation-driven/>,在控制器的方法上加入@RequestMapping注解即可。

2.mvc:annotation-driven的解析流程 会调用到自定义元素解析器的AnnotationDrivenBeanDefinitionParser.parse方法。

3.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping为RequestMapping注解映射到后台接口的注册表。此类实现了InitializingBean接口,会触发到

afterPropertiesSet方法。
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping@Overridepublic void afterPropertiesSet() {initHandlerMethods();}/*** Scan beans in the ApplicationContext, detect and register handler methods.* @see #getCandidateBeanNames()* @see #processCandidateBean* @see #handlerMethodsInitialized*/protected void initHandlerMethods() {for (String beanName : getCandidateBeanNames()) {if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {processCandidateBean(beanName);}}handlerMethodsInitialized(getHandlerMethods());}

4.在initHandlerMethods方法中会先调用getCandidateBeanNames获取当前容器工厂的所有BEAN,然后逐个BEAN进行处理。

4.1 获取所有BEAN流程

 protected String[] getCandidateBeanNames() {return (this.detectHandlerMethodsInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :obtainApplicationContext().getBeanNamesForType(Object.class));}

4.2 处理BEAN流程

protected void processCandidateBean(String beanName) {Class<?> beanType = null;try {beanType = obtainApplicationContext().getType(beanName);}catch (Throwable ex) {// An unresolvable bean type, probably from a lazy bean - let's ignore it.if (logger.isTraceEnabled()) {logger.trace("Could not resolve type for bean '" + beanName + "'", ex);}}if (beanType != null && isHandler(beanType)) {detectHandlerMethods(beanName);}}

4.3 判断当前BEAN是否HANDLER

 protected boolean isHandler(Class<?> beanType) {return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));}

4.4 如果此类是控制器或者有requestMapping注解,才会处理。

4.5 遍历当前类的所有方法,查找包含RequestMapping注解的方法,然后保存到

AbstractHandlerMethodMapping.MappingRegistry注册表中。
protected void detectHandlerMethods(Object handler) {Class<?> handlerType = (handler instanceof String ?obtainApplicationContext().getType((String) handler) : handler.getClass());if (handlerType != null) {Class<?> userType = ClassUtils.getUserClass(handlerType);Map<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {try {return getMappingForMethod(method, userType);}catch (Throwable ex) {throw new IllegalStateException("Invalid mapping on handler class [" +userType.getName() + "]: " + method, ex);}});if (logger.isTraceEnabled()) {logger.trace(formatMappings(userType, methods));}methods.forEach((method, mapping) -> {Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);registerHandlerMethod(handler, invocableMethod, mapping);});}}

判断当前方法是否包含requestMapping注解

 private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);RequestCondition<?> condition = (element instanceof Class ?getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);}

最终会调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod保存到URL和RequestMappinfo的映射注册表中。

AbstractHandlerMethodMapping
protected void registerHandlerMethod(Object handler, Method method, T mapping) {this.mappingRegistry.register(mapping, handler, method);}

具体的保存逻辑

在这个方法中主要操作的数据对象有四个,分别是mappingLookup、urlLookup、corsLookup和registry。下面对这四个对象进行说明:

  1. mappingLookup对象是Map结构,key表示mapping对象,value表示处理对象,在本例中key是RequestMappingInfo对象,value是Controller中的某一个方法。

  2. urlLookup对象是Map结构,key表示url,value表示mapping对象,本例中key是具体的url值"/demo/postMapping/",value是RequestMappingInfo对象,

  3. corsLookup对象是Map结构,key表示处理方法(Controller中的某个方法),value表示跨域配置,本例中没有进行跨域注解的使用因此数据不存在,如果需要看到跨域数据,可以在method上添加@CrossOrigin注解

  4. registry对象是Map结构,key表示mapping对象,value表示MappingRegistration对象

AbstractHandlerMethodMapping.MappingRegistry  内部类
public void register(T mapping, Object handler, Method method) {this.readWriteLock.writeLock().lock();try {HandlerMethod handlerMethod = createHandlerMethod(handler, method);validateMethodMapping(handlerMethod, mapping);this.mappingLookup.put(mapping, handlerMethod);List<String> directUrls = getDirectUrls(mapping);for (String url : directUrls) {this.urlLookup.add(url, mapping);}String name = null;if (getNamingStrategy() != null) {name = getNamingStrategy().getName(handlerMethod, mapping);addMappingName(name, handlerMethod);}this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));}finally {this.readWriteLock.writeLock().unlock();}}

生成的数据如下:

5.系统拦截器列表初始化过程,会调用到AbstractHandlerMapping.initApplicationContext,这个会查找当前容器工厂中所有继承了MappedInterceptor类的拦截器实例BEAN.然后保存到AbstractHandlerMapping.interceptors

 protected void initApplicationContext() throws BeansException {extendInterceptors(this.interceptors);detectMappedInterceptors(this.adaptedInterceptors);initInterceptors();}

二、调用HTTP请求根据PATH寻找接口方法流程

1.首先tomcat会调用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.mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

2.首先调用getHandler去根据请求PATH查找HandlerExecutionChain,HandlerExecutionChain就是一个RequestHandleMappinfo加上一个拦截器列表。会调用到AbstractHandlerMapping.getHandler

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {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);return executionChain;}

3.最终会调用到AbstractHandlerMapping.lookupHandlerMethod根据PATH查找HandlerMethod,这里面的 this.mappingRegistry.getMappingsByUrl就是初始化时的URL和RequestMappingInfo映射表。

 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {List<Match> matches = new ArrayList<>();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()) {Match bestMatch = matches.get(0);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);}}

调用堆栈1

调用堆栈2

 4.初始化HandlerExecutionChain拦截器列表,这个会查找当前容器工厂中所有实现了handleInteropr 的类,

AbstractHandlerMapping类
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);for (HandlerInterceptor interceptor : this.adaptedInterceptors) {if (interceptor instanceof MappedInterceptor) {MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {chain.addInterceptor(mappedInterceptor.getInterceptor());}}else {chain.addInterceptor(interceptor);}}return chain;}

这个类会根据拦截器的URL匹配规则相应添加拦截器列表。

<mvc:interceptors><bean class="com.tpw.component.HandlerInterceptor1"></bean><mvc:interceptor><mvc:mapping path="/user"/><bean class="com.tpw.component.HandlerInterceptor2"></bean></mvc:interceptor>
</mvc:interceptors>

5.调用所有拦截器的applyPreHandle方法

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];if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}}return true;}

7.然后调用invocableMethod.invokeAndHandle(webRequest, mavContainer),首先通过反射调用handlerMethod中的bean的接口方法

ServletInvocableHandlerMethod
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 {this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}}

9.最后调用HandlerMethodReturnValueHandlerComposite.handleReturnValue 进行返回值处理,例如将BEAN转JSON,转XML等。

9.1 这个找HANDLER的过程也是,根据此HANDLER是否支持此方法,如在方法上加上了@ResponseBody,则会由RequestResponseBodyMethodProcessor处理。

 RequestResponseBodyMethodProcessor
public boolean supportsReturnType(MethodParameter returnType) {return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||returnType.hasMethodAnnotation(ResponseBody.class));}
 public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}

HandlerMethodReturnValueHandler的初始化流程和参数解析器一样,也可以自定义。

9.2 由于我们在方法上加了@ResponseBody注解,所有此handler为

RequestResponseBodyMethodProcessor。此类继承了AbstractMessageConverterMethodProcessor,这个类会调用当前容器工厂中所有
 protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {Object body;Class<?> valueType;Type targetType;if (value instanceof CharSequence) {body = value.toString();valueType = String.class;targetType = String.class;}else {body = value;valueType = getReturnValueType(body, returnType);targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());}if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();for (HttpMessageConverter<?> converter : this.messageConverters) {GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ?((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :converter.canWrite(valueType, selectedMediaType)) {body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) converter.getClass(),inputMessage, outputMessage);if (body != null) {Object theBody = body;LogFormatUtils.traceDebug(logger, traceOn ->"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");addContentDispositionHeader(inputMessage, outputMessage);if (genericConverter != null) {genericConverter.write(body, targetType, selectedMediaType, outputMessage);}else {((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);}}else {if (logger.isDebugEnabled()) {logger.debug("Nothing to write: null body");}}return;}}}}

9.3 messageConverts的数据初始化来源为 RequestMappingHandlerAdapter.getDefaultArgumentResolvers方法中,
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));

messageConverts来源于RequestMappingHandlerAdapter.messageConverters中。

在初始化时会,依赖注入当前系统中所有的messageConvert.

10.最后调用拦截器的所有postHandle方法进行,处理完回调。

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable 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);}}}

11.在渲染完输出视图后,会调用所有拦截器的afterCompletion方法,注意,JSON,XML这种没有视图,只有HTML等才有。

 void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}}}

springmvc path请求映射到bean 方法的流程相关推荐

  1. springMVC源码学习之:springMVC响应请求的几种方法

    spring mvc 支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void. ModelAndView @RequestMap ...

  2. SpringMVC的请求参数,类型转换器(日期格式),请求映射

    SpringMVC的请求参数,类型转换器(日期格式),请求映射 请求参数 springMVC将传递的参数封装到处理的方法形参中,达到快速访问参数的目的 普通参数类型 参数名与处理器方法形参名保持一致 ...

  3. SpringMVC@RequestMapping请求方法限定与请求参数限定

    SpringMVC@RequestMapping请求方法限定与请求参数限定 一.请求方法限定 除了可以对url进行设置,还可以限定请求进来的方法 限定GET方法 @RequestMapping(met ...

  4. SpringMVC之请求参数的获取方式

    转载出处:https://www.toutiao.com/i6510822190219264516/ SpringMVC之请求参数的获取方式 常见的一个web服务,如何获取请求参数? 一般最常见的请求 ...

  5. SpringMVC处理请求或响应数据

    SpringMVC处理请求数据或响应数据: 请求处理方法签名 Spring MVC 通过分析处理方法的签名,HTTP请求信息绑定到处理方法的相应人参中. Spring MVC 对控制器处理方法签名的限 ...

  6. springmvc自定义处理器映射器和处理器适配器

    springmvc通过处理器映射器(HandlerMapping)和处理器适配器(HandlerAdapter)来处理http请求,当一个请求经过DispatcherServlet后,Dispatch ...

  7. 【SSM框架系列】SpringMVC的请求和响应

    request和response request对象的作用 获取请求参数 作为域对象存取数据 获取请求头 请求转发 response功能 回写数据,JSON格式字符串 页面跳转(请求重定向) Spri ...

  8. SpringBoot中请求映射的原理(源码)

    一.先看一下SpringMVC解析流程 时序图: 二.SpringBoot请求映射原理 SpringBoot跟spring一脉相承,所以直接找DispatcherServlet这个类. 其继承关系如下 ...

  9. spring—SpringMVC的请求和响应

    SpringMVC的数据响应-数据响应方式 页面跳转 直接返回字符串 @RequestMapping(value = {"/qq"},method = {RequestMethod ...

最新文章

  1. 清华大学精密仪器系:智能传感器太重要了!
  2. Istio 1.3 发布:HTTP 遥测不再需要 Mixer
  3. python使用什么注释语句和运算-怎么使用python注释语句
  4. MySQL 数据库规范--调优篇(终结篇)
  5. 正则匹配class并替换整个class为空
  6. Druid 常见问题
  7. pyqt5获取文本框里输入的值_实战PyQt5: 060-输入对话框QInputDialog
  8. Arts 第十九周(7/22 ~ 7/28)
  9. ubuntu安装phpmyadmin
  10. Nginx资源合并优化模块nginx-http-concat
  11. 基于python实现细胞自动机
  12. 小米手机怎么在图片显示定位服务器,小米浏览器中,图片导致fixed定位的元素无法显示...
  13. 一个页面播放多个视频
  14. Unexpected token u in JSON at position 0 at JSON.parse (<anonymous>)
  15. cannot unbox null value
  16. SpringBoot+Dubbo+zookeeper整合
  17. 2019初创企业投融资服务平台都有哪些?
  18. 笔记 GWAS 操作流程5-2:利用GEMMA软件进行LMM+PCA+协变量
  19. 了解Cookie是什么
  20. 2022年安全员-B证考试模拟100题及在线模拟考试

热门文章

  1. SAP创建Web Service以及用ABAP调用
  2. SAP 对话屏幕长文本编辑框
  3. 批量下载ABAP程序和表结构
  4. abap range 或 Filter的说明
  5. “红人经济第一股”搞虚拟社交,天下秀是变道还是扩道?
  6. mysql group by 无效_Mysql5.7版本group by 子查询中order by 无效的问题解决办法
  7. ffmpeg库编译加文字_ffmpeg第三方库编译记录
  8. Python常用模块之hashlib模块
  9. Python基础教程:用模块化来搭项目
  10. delphi base64 java_Base64以及delphi、Java实现[转]