10.SpringMVC 请求处理 - HandlerMapping

基本概念

即处理器映射,它的主要作用就是根据 reqeust 获取 HandlerExecutionChain。

下面来看它的源码:

public interface HandlerMapping {
    // 省略部分代码...

    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

再来看它的继承关系:

这里写图片描述


AbstractHandlerMapping

AbstractHandlerMapping 是该接口的抽象实现类,该类实现了 getHandler 方法。

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 关键 -> 取得处理器,留给子类
    Object handler = getHandlerInternal(request);

    // 取得默认处理器
    if (handler == null) {
        handler = getDefaultHandler();
    }

    if (handler == null) {
        return null;
    }

    if (handler instanceof String) {
        // 若处理器是 Bean 名称,则从 SpringMVC 容器手动取得该 Bean
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }

    // 关键 -> 取得 HandlerExecutionChain 
    HandlerExecutionChain executionChain = 
        getHandlerExecutionChain(handler, request);

    // 省略部分代码...

    return executionChain;
}

继续追踪到 getHandlerExecutionChain 方法

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {

    // 1.取得 HandlerExecutionChain ,包含了处理器和拦截器,下面会详细分析
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
        (HandlerExecutionChain) handler : newHandlerExecutionChain(handler));

    // 2.获取请求路径
    // 若完整请求地址为 http://localhost:8080/Demo/hello,则 lookupPath = /hello
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);

    // 3.添加拦截器到 HandlerExecutionChain 
    // 遍历 SpringMVC 容器的所有拦截器
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {

        // 判断拦截器类型,属于 MappedInterceptor,则先匹配路径,否则直接添加
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

分析代码可知, HandlerMapping 的主要职责是取得 HandlerExecutionChain,具体步骤如下:

  • 取得 HandlerExecutionChain 的处理器,由不同子类作实现。
  • 取得 HandlerExecutionChain 的拦截器

AbstractHandlerMethodMapping

在介绍该类之前,先来看几个类:

  • RequestMapingInfo ,该类包含了 @ReuestMaping 的所有注解内容,包括 value,method,param,header,consumes,produces 等属性定义的内容 。

    // 注解的所有内容被封装到它的成员变量中
    
    // 包含了注解属性 value 的内容,如 "/hello"
    private final PatternsRequestCondition patternsCondition;
    
    // 包含了注解属性 method 的内容,如 GET
    private final RequestMethodsRequestCondition methodsCondition;
    
    // 省略部分代码...

  • MappingRegistry,它是 AbstractHandlerMethodMapping 的内部类。它内有一个 Map,维护着 SpringMVC 中所有的 RequestMapingInfo 与 HandlerMethod 的映射关系。

    // T 实际就是 RequestMapingInfo 
    private final Map<T, HandlerMethod> mappingLookup = 
        new LinkedHashMap<T, HandlerMethod>();
    
    // 取得映射
    public Map<T, HandlerMethod> getMappings() {
        return this.mappingLookup;
    }

  • Match,它是 AbstractHandlerMethodMapping 的私有内部类,也称匹配者,封装了一个RequestMappingInfo ,以及对应的 HandlerMethod 。

    private class Match {
    
        private final T mapping;
    
        private final HandlerMethod handlerMethod;
    
        public Match(T mapping, HandlerMethod handlerMethod) {
            this.mapping = mapping;
            this.handlerMethod = handlerMethod;
        }
    }

该类是 AbstractHandlerMapping 类的子类,也是个抽象类。它实现了取得处理器的具体过程,也就是对 getHandlerInternal 作了具体实现。


protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {

    // 省略部分代码...

    // 1.取得请求路径
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);

    // 添加读锁,其他线程只能读不能写
    this.mappingRegistry.acquireReadLock();

    try {
        // 关键 -> 2.取得 HandlerMethod 
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);

        // 创建控制器并返回
        return (handlerMethod != null ? 
            handlerMethod.createWithResolvedBean() : null);

    }finally {
        // 释放读锁
        this.mappingRegistry.releaseReadLock();
    }
}

继续追踪到 lookupHandlerMethod 方法。

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {

    List<Match> matches = new ArrayList<Match>();

    // 1.取得 RequestMappingInfo,根据路径进行匹配  
    List<T> directPathMatches = 
        this.mappingRegistry.getMappingsByUrl(lookupPath);

    if (directPathMatches != null) {
        // 2.构建 Match 并加入集合
        addMatchingMappings(directPathMatches, matches, request);
    }

    if (matches.isEmpty()) {
        addMatchingMappings(this.mappingRegistry.getMappings().keySet(),
            matches, request);
    }

    // 3.选出最佳的 Match
    if (!matches.isEmpty()) {
        // 利用比较器进行排序
        Comparator<Match> comparator = 
            new MatchComparator(getMappingComparator(request));

        Collections.sort(matches, comparator);

        // 省略部分代码...

        Match bestMatch = matches.get(0);

        // 存在多个 Match 时,对前两个匹配者再比较,若相同则抛出异常
        if (matches.size() > 1) {
            // 省略部分代码...

            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                // 抛出异常...
            }
        }

        // 将 相关信息添加到 reqeust 的属性。
        handleMatch(bestMatch.mapping, lookupPath, request);

        return bestMatch.handlerMethod;
    } else {
        // 处理没有 Match 的情况
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(),
            lookupPath, request);
    }
}

再来看看 addMatchingMappings 的实现过程:

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
    for (T mapping : mappings) {
        // 关键 -> 取得匹配的 RequetMapingInfo,留给子类实现
        T match = getMatchingMapping(mapping, request);

        // 创建 Match 并加入集合
        if (match != null) {
            matches.add(new Match(match,
                this.mappingRegistry.getMappings().get(mapping)));
        }
    }
}

综上所述,该类的主要作用是负责找到相应的处理器,也就是 HandlerMethod。它的具体步骤如下:

  • 1.取得 RequestMappingInfo。根据请求路径进行匹配。

  • 2.构建 Match 并加入集合。过滤 RequestMappingInfo 与 rqeust 请求不符的内容,再从 MappingRegistry 找到对应的 HandlerMethod,最后添加进集合。

  • 3.选出最佳的 Match。利用比较器筛选中最佳的 Match,再返回 Match 中的 HandlerMethod。


RequestMappingHandlerMapping

该类是 AbstractHandlerMethodMapping 的子类。

若控制器采用了注解方法实现,则就会采用 RequestMappingHandlerMapping 来处理。在其内部实现了 getMatchingMapping 方法:

protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
    // 将 info 的内容 rqeust 的内容进行匹配
    // 假设 @RquestMapping 中 method 为 GET、POST
    // 而 reqeuest 的请求类为 GET
    // 经过匹配,RequestMappingInfo 中关于 method 的内容只剩下 GET
    return info.getMatchingCondition(request);
}

总结

整个 HandlerMapping 的工作流程如下:

这里写图片描述

根据目的就是通过 reqeust 获取 HandlerExecutionChain 的 HandlerMethod、Interceptor。

而实现的重点又在获取 HandlerMethod 的过程,即根据请求获取 Controller 中处理方法的过程。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
1. web.xml:这是一个Java Web 项目的核心配置文件,主要用于配置Servlet、Filter、Listener等Web组件,并且定义了Servlet容器的一些基本配置,如编码、Session管理、错误页面等。其中,常用的配置包括: - 配置Servlet:用于处理HTTP请求的Java。 - 配置Filter:用于对HTTP请求进行过滤和处理。 - 配置Listener:用于监听Web应用程序的生命周期事件。 2. springmvc-config.xml:这是一个Spring MVC框架的配置文件,主要用于配置Spring MVC的核心组件,如HandlerMapping、ViewResolver、Interceptor等。其中,常用的配置包括: - 配置HandlerMapping:用于映射请求到相应的控制器方法。 - 配置ViewResolver:用于将控制器方法返回的逻辑视图名映射到实际的视图模板。 - 配置Interceptor:用于拦截请求,在处理请求前或处理请求后进行一些操作,如权限控制、日志记录等。 3. spring-mybatis.xml:这是一个整合Spring和MyBatis框架的配置文件,主要用于配置数据库连接、事务管理、Mapper接口扫描等。其中,常用的配置包括: - 配置数据源:用于连接数据库,设置连接池等。 - 配置事务管理器:用于管理数据库事务,保证事务的一致性和可靠性。 - 配置Mapper接口扫描:用于自动扫描Mapper接口,并将其注册为Spring的Bean。 4. applicationcontext.xml:这是一个Spring框架的核心配置文件,主要用于配置Spring容器中的各种Bean,包括Service、DAO、Interceptor等。其中,常用的配置包括: - 配置Bean:用于定义Spring容器中的各种Bean。 - 配置AOP:用于实现面向切面编程,如事务管理、日志记录等。 - 配置属性文件:用于加载外部的属性文件,如数据库连接信息、邮件服务器信息等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oxf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值