lihongjie0209 / myblog

4 stars 0 forks source link

Spring MVC: HandlerMapping 和 HandlerExecutionChain #273

Open lihongjie0209 opened 3 years ago

lihongjie0209 commented 3 years ago

HandlerMapping

主要负责根据 HttpServletRequest 找到合适的处理器

主要逻辑

Object handler = getHandlerInternal(request); // 交给子类实现

HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); // 包装成HandlerExecutionChain并添加拦截器
    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;
    }
lihongjie0209 commented 3 years ago

HandlerInterceptor

就是一个简单的aop, 执行前, 执行后, 以及异常处理

public interface HandlerInterceptor {

    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable Exception ex) throws Exception {
    }

}
lihongjie0209 commented 3 years ago

HandlerExecutionChain

就是handler + HandlerInterceptor

    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;
    }

    /**
     * Apply postHandle methods of registered interceptors.
     */
    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);
        }
    }
lihongjie0209 commented 3 years ago

HandlerMethod

基于bean + method 的handler的封装, 主要处理注解和返回值等反射内容


    private final Object bean; // 方法所在的bean

    @Nullable
    private final BeanFactory beanFactory;

    private final Class<?> beanType;

    private final Method method; // 被调用的方法

    private final Method bridgedMethod;

    private final MethodParameter[] parameters; 参数

    @Nullable
    private HttpStatus responseStatus;

    @Nullable
    private String responseStatusReason;

    @Nullable
    private HandlerMethod resolvedFromHandlerMethod;

    @Nullable
    private volatile List<Annotation[][]> interfaceParameterAnnotations;

    private final String description;
lihongjie0209 commented 3 years ago

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal

通过request获取 HandlerMethod

  1. 获取当前的url
  2. 通过url以及request在mappingRegistry中查找匹配的结果
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = initLookupPath(request);
        this.mappingRegistry.acquireReadLock();
        try {
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod

查找逻辑, 首先查找路径完全匹配

        List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }

如果没有找到, 那么开始测试所有的映射

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

具体是否满足由字类实现

    private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
        for (T mapping : mappings) {
            T match = getMatchingMapping(mapping, request);
            if (match != null) {
                matches.add(new Match(match,
                        this.mappingRegistry.getRegistrations().get(mapping).getHandlerMethod()));
            }
        }
    }

如果有多个匹配, 那么子类提供一个Comparator选取排在第一个的

if (!matches.isEmpty()) {
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                matches.sort(comparator);
                bestMatch = matches.get(0);
                if (logger.isTraceEnabled()) {
                    logger.trace(matches.size() + " matching mappings: " + matches);
                }
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                Match secondBestMatch = matches.get(1);
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    String uri = request.getRequestURI();
                    throw new IllegalStateException(
                            "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                }
            }
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
lihongjie0209 commented 3 years ago

初始化mappingRegistry

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods

其实就是对于所有的ioc bean, 发现所有合适的方法

    protected void initHandlerMethods() {
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    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);
        }
    }