服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - SpringMVC核心组件HandlerMapping,你清楚了吗?

SpringMVC核心组件HandlerMapping,你清楚了吗?

2021-12-02 22:46Spring源码解读 Java教程

当一个请求过来后Spring是如何进行处理的?下面简单的罗列下请个的过程中核心组件,希望能够帮助到你。

SpringMVC核心组件HandlerMapping,你清楚了吗?

概述

当一个请求过来后Spring是如何进行处理的?下面简单的罗列下请个的过程中核心组件

SpringMVC处理的流程:

  1. DispatcherServlet 所有请求的入口
  2. HandlerMapping 将请求地址与处理程序关联
  3. HandlerAdapter 真正的处理程序,如执行上一步中对应的处理程序
  4. HandlerMethodArgumentResolver 对参数进行解析,这里面还涉及到很多其它东西
  5. HanlderMethodReturnValueHandler 对返回值进行输出处理
  6. ViewResolver 当上一步返回结果为ModelAndView时会应用视图解析器

一个请求的处理过程

获取HandlerMapping

该步从容器中获取所有的HandlerMapping对象。

  1. public class DispatcherServlet extends FrameworkServlet {
  2. private List handlerMappings;
  3. private void initHandlerMappings(ApplicationContext context) {
  4. // 在ApplicationContext中查找所有HandlerMappings,包括祖先上下文。
  5. Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
  6. if (!matchingBeans.isEmpty()) {
  7. this.handlerMappings = new ArrayList<>(matchingBeans.values());
  8. AnnotationAwareOrderComparator.sort(this.handlerMappings);
  9. }
  10. }
  11. }

查找HandlerMapping

该步从获取的HandlerMapping中查找适合当前请求的HandlerMapping。

  1. public class DispatcherServlet extends FrameworkServlet {
  2. private List handlerMappings;
  3. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  4. HandlerExecutionChain mappedHandler = null;
  5. // 查找能够处理当前请求HandlerMapping对象,主要就是根据请求的URI
  6. mappedHandler = getHandler(processedRequest);
  7. }
  8. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  9. if (this.handlerMappings != null) {
  10. // HandlerMapping 实现了Ordered接口,是由顺序的,那在这里,谁先匹配谁就处理
  11. for (HandlerMapping mapping : this.handlerMappings) {
  12. // 在这个过程中会通过查找到的HandlerMapping对象,然后获取合适的处理程序(可能是个Bean对象或是HandlerMethod对象等)
  13. HandlerExecutionChain handler = mapping.getHandler(request);
  14. if (handler != null) {
  15. return handler;
  16. }
  17. }
  18. }
  19. return null;
  20. }
  21. }

系统默认有如下5个HandlerMapping

  1. RequestMappingHandlerMapping
  2. BeanNameUrlHandlerMapping
  3. RouterFunctionMapping
  4. SimpleUrlHandlerMapping
  5. WelcomePageHandlerMapping

一般默认都是RequestMappingHandlerMapping匹配

接下来看看是如何进行匹配的

调用父类AbstractHandlerMapping#getHandler方法,父类中的这个方法中定义了特定的逻辑,而针对每种不同的HandlerMapping实现是需要具体的子类来实现AbstractHandlerMapping#getHandlerInternal方法

  1. public abstract class AbstractHandlerMapping {
  2. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  3. Object handler = getHandlerInternal(request);
  4. // ...
  5. HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
  6. // ...
  7. return executionChain;
  8. }
  9. }
  10. public abstract class AbstractHandlerMethodMapping {
  11. protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
  12. // 获取请求地址
  13. String lookupPath = initLookupPath(request);
  14. try {
  15. // 根据请求地址查询对应的HandlerMethod
  16. HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
  17. return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
  18. }
  19. // ...
  20. }
  21. protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
  22. List matches = new ArrayList<>();
  23. // 在已注册的Mapping中根据请求的url进行查找
  24. // 这样查找的this.pathLookup.get(urlPath);
  25. List directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
  26. if (!matches.isEmpty()) {
  27. Match bestMatch = matches.get(0);
  28. // ...
  29. handleMatch(bestMatch.mapping, lookupPath, request);
  30. return bestMatch.getHandlerMethod();
  31. }
  32. // ...
  33. }
  34. }

到这里就是查找处理请求的HandlerMethod对象。接下来看看系统是如何进行初始化所有的HandlerMethod

初始化HandlerMethod

  1. public class RequestMappingHandlerMapping {
  2. public void afterPropertiesSet() {
  3. // ...
  4. super.afterPropertiesSet();
  5. }
  6. }
  7. public abstract class AbstractHandlerMethodMapping {
  8. public void afterPropertiesSet() {
  9. initHandlerMethods();
  10. }
  11. protected void initHandlerMethods() {
  12. // getCandidateBeanNames获取容器中的所有Bean
  13. for (String beanName : getCandidateBeanNames()) {
  14. if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
  15. processCandidateBean(beanName);
  16. }
  17. }
  18. handlerMethodsInitialized(getHandlerMethods());
  19. }
  20. protected void processCandidateBean(String beanName) {
  21. Class beanType = null;
  22. try {
  23. // 根据BeanName获取对应的Class
  24. beanType = obtainApplicationContext().getType(beanName);
  25. }
  26. // ...
  27. // isHandler方法判断当前的类是否符合条件,该方法在RequestMappingHandlerMapping中实现
  28. // isHandler方法用处就是判断当前的Class@Controller或者@RequestMapping注解
  29. // 这样就将所有的@Controller与RequestMappingHandlerMapping关联一起了。
  30. if (beanType != null && isHandler(beanType)) {
  31. // 查找所有的HandlerMethod
  32. detectHandlerMethods(beanName);
  33. }
  34. }
  35. protected void detectHandlerMethods(Object handler) {
  36. Class handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());
  37. if (handlerType != null) {
  38. Class userType = ClassUtils.getUserClass(handlerType);
  39. // 查找Class中的所有方法
  40. Map methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup) method -> {
  41. try {
  42. // 将每一个符合条件的方法(方法上有@RequestMapping注解的)
  43. // 封装到RequestMappingInfo对象中
  44. return getMappingForMethod(method, userType);
  45. }
  46. // ...
  47. });
  48. methods.forEach((method, mapping) -> {
  49. Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
  50. // 将找到的所有Method进行注册添加到Map中
  51. registerHandlerMethod(handler, invocableMethod, mapping);
  52. });
  53. }
  54. }
  55. protected void registerHandlerMethod(Object handler, Method method, T mapping) {
  56. this.mappingRegistry.register(mapping, handler, method);
  57. }
  58. class MappingRegistry {
  59. // T : RequestMappingInfo, handler: 字符串(usersController)Bean名称,method:请求方法对象
  60. public void register(T mapping, Object handler, Method method) {
  61. // 创建HandlerMethod对象
  62. HandlerMethod handlerMethod = createHandlerMethod(handler, method);
  63. // ...
  64. for (String path : directPaths) {
  65. // 缓存上,在请求到来的时候 会从这个pathLookup集合中查找
  66. this.pathLookup.add(path, mapping);
  67. }
  68. }
  69. }
  70. }

原文链接:https://www.toutiao.com/a7017295098270564902/

延伸 · 阅读

精彩推荐
  • Java教程关于IntelliJ IDEA 打包代码报错的问题

    关于IntelliJ IDEA 打包代码报错的问题

    这篇文章主要介绍了关于IntelliJ IDEA 打包代码报错的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考...

    一只爱阅读的程序员7062021-08-23
  • Java教程Java实现的微信图片处理工具类【裁剪,合并,等比例缩放等】

    Java实现的微信图片处理工具类【裁剪,合并,等比例缩放等】

    这篇文章主要介绍了Java实现的微信图片处理工具类,可实现针对图片的裁剪、合并、等比例缩放、旋转、识别等各种常见的图片处理功能,需要的朋友可以参...

    shenjianxz7122021-02-22
  • Java教程Java Assert.assertEquals案例详解

    Java Assert.assertEquals案例详解

    这篇文章主要介绍了Java Assert.assertEquals案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...

    俊墨客9722021-11-24
  • Java教程SpringBoot Devtools实现项目热部署的方法示例

    SpringBoot Devtools实现项目热部署的方法示例

    这篇文章主要介绍了SpringBoot Devtools实现项目热部署的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要...

    qianmoQ8332021-07-12
  • Java教程MyBatis无缝对接Spring的方法

    MyBatis无缝对接Spring的方法

    Spring框架与MyBatis框架是Java互联网技术的主流框架。那么mybatis如何无缝对接spring呢?下面通过本文给大家介绍,需要的的朋友参考下吧...

    落叶飞逝的恋8142020-12-25
  • Java教程浅析JDK和Tomcat的安装与配置方法

    浅析JDK和Tomcat的安装与配置方法

    这篇文章主要介绍了JDK和Tomcat的安装与配置方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友参考下吧...

    厚德载物,行胜于言12212021-06-20
  • Java教程Java GZIPOutputStream流压缩文件的操作

    Java GZIPOutputStream流压缩文件的操作

    这篇文章主要介绍了Java GZIPOutputStream流压缩文件的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    30757630076122021-08-06
  • Java教程Java中final变量使用总结

    Java中final变量使用总结

    这篇文章主要介绍了Java中final变量使用总结,final关键字可用于变量声明,一旦该变量被设定,就不可以再改变该变量的值,通常final定义的变量为常量,需要的朋...

    junjie5492019-12-21