大数跨境
0
0

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

SpringMVC核心组件HandlerMapping你清楚了吗? Spring全家桶实战案例
2021-10-27
0
导读:SpringMVC核心组件HandlerMapping你清楚了吗?
  • 概述

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

SpringMVC处理的流程:

  1. DispatcherServlet 所有请求的入口

  2. HandlerMapping 将请求地址与处理程序关联

  3. HandlerAdapter 真正的处理程序,如执行上一步中对应的处理程序

  4. HandlerMethodArgumentResolver 对参数进行解析,这里面还涉及到很多其它东西

  5. HanlderMethodReturnValueHandler 对返回值进行输出处理

  6. ViewResolver 当上一步返回结果为ModelAndView时会应用视图解析器

一个请求的处理过程

获取HandlerMapping

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

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

  • 查找HandlerMapping

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

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

系统默认有如下5个HandlerMapping

  1. RequestMappingHandlerMapping

  2. BeanNameUrlHandlerMapping

  3. RouterFunctionMapping

  4. SimpleUrlHandlerMapping

  5. WelcomePageHandlerMapping

一般默认都是RequestMappingHandlerMapping匹配

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

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

public abstract class AbstractHandlerMapping {  public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {    Object handler = getHandlerInternal(request);    // ...    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);    // ...    return executionChain;  }  }public abstract class AbstractHandlerMethodMapping {  protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {    // 获取请求地址    String lookupPath = initLookupPath(request);    try {      // 根据请求地址查询对应的HandlerMethod      HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);      return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);    }    // ...  }  protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {    List<Match> matches = new ArrayList<>();    // 在已注册的Mapping中根据请求的url进行查找    // 这样查找的this.pathLookup.get(urlPath);    List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);    if (!matches.isEmpty()) {      Match bestMatch = matches.get(0);      // ...      handleMatch(bestMatch.mapping, lookupPath, request);      return bestMatch.getHandlerMethod();    }    // ...  }}

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

  • 初始化HandlerMethod

public class RequestMappingHandlerMapping {  public void afterPropertiesSet() {    // ...    super.afterPropertiesSet();  }}public abstract class AbstractHandlerMethodMapping {  public void afterPropertiesSet() {    initHandlerMethods();  }  protected void initHandlerMethods() {    // getCandidateBeanNames获取容器中的所有Bean    for (String beanName : getCandidateBeanNames()) {      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {        processCandidateBean(beanName);      }    }    handlerMethodsInitialized(getHandlerMethods());  }  protected void processCandidateBean(String beanName) {    Class<?> beanType = null;    try {      // 根据BeanName获取对应的Class      beanType = obtainApplicationContext().getType(beanName);    }    // ...    // isHandler方法判断当前的类是否符合条件,该方法在RequestMappingHandlerMapping中实现    // isHandler方法用处就是判断当前的Class@Controller或者@RequestMapping注解    // 这样就将所有的@Controller与RequestMappingHandlerMapping关联一起了。    if (beanType != null && isHandler(beanType)) {      // 查找所有的HandlerMethod      detectHandlerMethods(beanName);    }  }  protected void detectHandlerMethods(Object handler) {    Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());    if (handlerType != null) {      Class<?> userType = ClassUtils.getUserClass(handlerType);      // 查找Class中的所有方法      Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> {        try {          // 将每一个符合条件的方法(方法上有@RequestMapping注解的)          // 封装到RequestMappingInfo对象中          return getMappingForMethod(method, userType);        }        // ...      });      methods.forEach((method, mapping) -> {        Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);        // 将找到的所有Method进行注册添加到Map中        registerHandlerMethod(handler, invocableMethod, mapping);      });    }  }  protected void registerHandlerMethod(Object handler, Method method, T mapping) {    this.mappingRegistry.register(mapping, handler, method);  }  class MappingRegistry {    // T  : RequestMappingInfo, handler: 字符串(usersController)Bean名称,method:请求方法对象    public void register(T mapping, Object handler, Method method) {      // 创建HandlerMethod对象      HandlerMethod handlerMethod = createHandlerMethod(handler, method);      // ...      for (String path : directPaths) {        // 缓存上,在请求到来的时候 会从这个pathLookup集合中查找        this.pathLookup.add(path, mapping);      }    }  }}

完毕!!!

求个关注+转发


【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读195
粉丝0
内容832