SpringMVC源码分析(三)——————HTTP请求是如何与对应Handler的对应method映射的

三、HTTP请求是如何与对应Handler的对应method映射的

从上面的分析已经知道了当初始化完成的时候context中所有的handlerMapping都被加载了,并且它们都存放在hadlerMappings这么一个List中并且被排序了。我们看看HandlerMapping的设计及类的结构关系如下:

这里可以看到顶层的父接口就是HandlerMapping,进入这个类发现这个顶层接口定义了一些常量以及一个getHandler方法,并且通过该方法会返回HandlerExecutionChain这么一个对象。

 跟进这个HandlerExecutionChain这个类看看,这个类中有这么Interceptor链和一个handler这个两个关键的成员。说白了这个handler就是HTTP请求对应的那个Controller,并且这个Interceptor链也就是我们配置的各种拦截器,多个拦截器组成了拦截器链,通过拦截器链对我们handler进行一系列的增强。当然这个HandlerExecutionChain类还提供了一些维护这个拦截器链的API

public class HandlerExecutionChain {
    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
    private final Object handler;
    @Nullable
    private HandlerInterceptor[] interceptors;
    @Nullable
    private List<HandlerInterceptor> interceptorList;
    private int interceptorIndex;

    public HandlerExecutionChain(Object handler) {
        this(handler, (HandlerInterceptor[])null);
    }

    public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
        this.interceptorIndex = -1;
        if(handler instanceof HandlerExecutionChain) {
            HandlerExecutionChain originalChain = (HandlerExecutionChain)handler;
            this.handler = originalChain.getHandler();
            this.interceptorList = new ArrayList();
            CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
            CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
        } else {
            this.handler = handler;
            this.interceptors = interceptors;
        }

    }

    public Object getHandler() {
        return this.handler;
    }

    public void addInterceptor(HandlerInterceptor interceptor) {
        this.initInterceptorList().add(interceptor);
    }

    public void addInterceptors(HandlerInterceptor... interceptors) {
        if(!ObjectUtils.isEmpty(interceptors)) {
            CollectionUtils.mergeArrayIntoCollection(interceptors, this.initInterceptorList());
        }

    }

    private List<HandlerInterceptor> initInterceptorList() {
        if(this.interceptorList == null) {
            this.interceptorList = new ArrayList();
            if(this.interceptors != null) {
                CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
            }
        }

        this.interceptors = null;
        return this.interceptorList;
    }

    @Nullable
    public HandlerInterceptor[] getInterceptors() {
        if(this.interceptors == null && this.interceptorList != null) {
            this.interceptors = (HandlerInterceptor[])this.interceptorList.toArray(new HandlerInterceptor[0]);
        }

        return this.interceptors;
    }

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if(!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if(!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }
        }

        return true;
    }

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.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);
            }
        }

    }

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
        HandlerInterceptor[] interceptors = this.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 var8) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
                }
            }
        }

    }

    void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if(!ObjectUtils.isEmpty(interceptors)) {
            for(int i = interceptors.length - 1; i >= 0; --i) {
                if(interceptors[i] instanceof AsyncHandlerInterceptor) {
                    try {
                        AsyncHandlerInterceptor ex = (AsyncHandlerInterceptor)interceptors[i];
                        ex.afterConcurrentHandlingStarted(request, response, this.handler);
                    } catch (Throwable var6) {
                        logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", var6);
                    }
                }
            }
        }

    }

    public String toString() {
        Object handler = this.getHandler();
        StringBuilder sb = new StringBuilder();
        sb.append("HandlerExecutionChain with [").append(handler).append("] and ");
        if(this.interceptorList != null) {
            sb.append(this.interceptorList.size());
        } else if(this.interceptors != null) {
            sb.append(this.interceptors.length);
        } else {
            sb.append(0);
        }

        return sb.append(" interceptors").toString();
    }
}

大致了解了Handler与HandlerExecutionChain后,就可以开始具体分析SpringMVC对HTTP请求处理的原理了,这里以我们最常用的RequestMappingHandlerMapping为例子,首先看看它的类继承体系

这里主要关注的是RequestMappingInfoHandlerMapping、AbstractHandlerMethodMapping。从这张Diagrams图可以发现,这个AbstractHandlerMethodMapping实现了InitializingBean接口并且实现了afterPropertiesSet方法。

 并且整个初始化工作由AbstractHandlerMethodMapping的initHandlerMethods方法主导的。为什么这么说呢,我们都知道SpringBean在创建bean的过程是先经过了bean的实例化、bean的属性填充、bean的初始化等步骤。

那么在SpringBean初始化的时候会调用invokeInitMethods方法它会去判断该bean是否为InitializingBean的实例,是的话就去调用它的afterPropertiesSet方法

 到这里终于清楚了为什么说整个初始化的工作是由AbstractHandlerMethodMapping的initHandlerMethods主导了,原因就是在这个类中Spring回调方法afterPropertiesSet里面又调用了initHandlerMethods方法。

 接下来主要分析以下这个initHandlerMethods方法究竟干了什么,大体逻辑就是先获取应用中所有Object的bean的名字,然后遍历它循环获取这个beanName对应的beanType,判断这个bean是不是一个handler

   protected void initHandlerMethods() {
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Looking for request mappings in application context: " + this.getApplicationContext());
        }
        //这里是获取应用中所有Object的bean的名字
        String[] beanNames = this.detectHandlerMethodsInAncestorContexts?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.obtainApplicationContext(), Object.class):this.obtainApplicationContext().getBeanNamesForType(Object.class);
        String[] var2 = beanNames;
        int var3 = beanNames.length;
        //遍历这个含有应用中所有beanName的字符串数组,并得到这个beanName对应的bean的类型
        for(int var4 = 0; var4 < var3; ++var4) {
            String beanName = var2[var4];
            if(!beanName.startsWith("scopedTarget.")) {
                Class beanType = null;

                try {
                    //根据这个beanName对应的beanType的类型
                    beanType = this.obtainApplicationContext().getType(beanName);
                } catch (Throwable var8) {
                    if(this.logger.isDebugEnabled()) {
                        this.logger.debug("Could not resolve target class for bean with name \'" + beanName + "\'", var8);
                    }
                }
                //判断这个根据这个bean的类型判断是不是一个handler
                if(beanType != null && this.isHandler(beanType)) {
                    this.detectHandlerMethods(beanName);
                }
            }
        }

        this.handlerMethodsInitialized(this.getHandlerMethods());
    }

并且从代码中我们可以知道判断一个Bean是不是一个Handler的逻辑就是判断这个Bean是否含有Controller注解RequestMapping注解

若这个Bean是一个Handler那么就进入detectHandlerMethods方法处理,去检测发现对应的HandlerMethod。继续跟进这个方法看看。这个方法的大体逻辑是获取这个handler中所有由requestMappinng的方法,然后循环去注册该方法与对应requestMapping信息到一个名为registry的一个HashMap中去,具体逻辑继续往下看。

    protected void detectHandlerMethods(Object handler) {
        Class handlerType = handler instanceof String?this.obtainApplicationContext().getType((String)handler):handler.getClass();
        if(handlerType != null) {
            Class userType = ClassUtils.getUserClass(handlerType);
            //获取这个handler中有requestMapping的方法
            //这个methods的Map结构为key是一个Method对象,value是一个RequestMappingInfo对象
            //这个版本的代码是5.0.4的,这里用到了lambda表达,这里可以理解成匿名类
            Map methods = MethodIntrospector.selectMethods(userType, (method) -> {
                try {
                    return this.getMappingForMethod(method, userType);
                } catch (Throwable var4) {
                    throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
                }
            });
            if(this.logger.isDebugEnabled()) {
                this.logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
            }
            //循环去注册Method与RequestMappingInfo的关系
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                this.registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }

    }

 首先看看这个selectMethods方法到底做了什么,从字面意思理解是按照规则来选取给定的类里面中方法。具体逻辑细化可以分为两步.

第一步:若这个targetType不是一个代理类,就获得它本身的类以及它的接口放入handlerTypes这么一个Set中去。

第二步:遍历这个handlerTypes,找到用户自己定义的方法并过滤出有requestMapping的方法,并将之塞入一个methodMap中

   public static <T> Map<Method, T> selectMethods(Class<?> targetType, MethodIntrospector.MetadataLookup<T> metadataLookup) {
        LinkedHashMap methodMap = new LinkedHashMap();
        LinkedHashSet handlerTypes = new LinkedHashSet();
        Class specificHandlerType = null;
        //若这个targetType不是一个代理类,就获得它本身的类以及它的接口
        if(!Proxy.isProxyClass(targetType)) {
            specificHandlerType = ClassUtils.getUserClass(targetType);
            handlerTypes.add(specificHandlerType);
        }

        handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
        Iterator var5 = handlerTypes.iterator();
        //遍历
        while(var5.hasNext()) {
            Class currentHandlerType = (Class)var5.next();
            Class targetClass = specificHandlerType != null?specificHandlerType:currentHandlerType;
            //找到用户自己定义的方法并过滤出有requestMapping的方法,并将之塞入一个methodMap中
            ReflectionUtils.doWithMethods(currentHandlerType, (method) -> {
                Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                Object result = metadataLookup.inspect(specificMethod);
                if(result != null) {
                    Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                    if(bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
                        methodMap.put(specificMethod, result);
                    }
                }

            }, ReflectionUtils.USER_DECLARED_METHODS);
        }

        return methodMap;
    }

下面继续跟进这个ReflectUtilsl.doWithMethods方法看看究竟是何方神圣。

先来了解以下它的方法入参:第一个是Class、第二个是MethodCallback、第三个是MethodFilter。第一个不用解释了,第二个从字面意思就能看出来就是一个方法回调,第三个也好理解是一个方法过滤器;具体后面再说。

大致清楚它的参数后再来看看这个方法的内部逻辑,可以看到逻辑也比较简单:

1、首先获取这个Class中所有定义的方法并且将之存入一个methods的Method数组中

2、遍历这个methods数组中的method如果这个mf方法拦截器为空或者这个method与方法拦截器mf的匹配规则对应,就回调mc.doWith方法。

3、后面我们还发现对这个类的父类和接口都有一个递归调用

    public static void doWithMethods(Class<?> clazz, ReflectionUtils.MethodCallback mc, @Nullable ReflectionUtils.MethodFilter mf) {
        //首先获取这个Class中所有定义的方法并且将之存入一个methods的Method数组中
        Method[] methods = getDeclaredMethods(clazz);
        Method[] var4 = methods;
        int var5 = methods.length;

        int var6;
        for(var6 = 0; var6 < var5; ++var6) {
            Method superIfc = var4[var6];
        //遍历这个methods数组中的method
        //如果这个mf方法拦截器为空或者这个method与方法拦截器mf的匹配规则对应,就回调mc.doWith方法。
            if(mf == null || mf.matches(superIfc)) {
                try {
                    //调用回调方法
                    mc.doWith(superIfc);
                } catch (IllegalAccessException var9) {
                    throw new IllegalStateException("Not allowed to access method \'" + superIfc.getName() + "\': " + var9);
                }
            }
        }

        if(clazz.getSuperclass() != null) {
            doWithMethods(clazz.getSuperclass(), mc, mf);
        } else if(clazz.isInterface()) {
            Class[] var10 = clazz.getInterfaces();
            var5 = var10.length;

            for(var6 = 0; var6 < var5; ++var6) {
                Class var11 = var10[var6];
                doWithMethods(var11, mc, mf);
            }
        }

    }

 了解这个方法的逻辑之后,再回过头来看看selectMethod方法中的ReflectUtilsl.doWithMethods就很清楚了。

这个mc.doWith就对应这一段逻辑,可以看到这段逻辑里面有一个metadataLookup.inspect方法,这个方法的逻辑就是selectMethods传入的第二个入参的。就是调用了RequestMappingHandlerMapping的getMappingForMethod方法

 

 

而这个getMappingForMethod干的事情就是找到这个方法上的RequestMapping,如果这个方法上的requestMapping信息不为空的话就去照这个handler类上面的requestMapping信息然后将之合并。 

 

然后这个mf方法拦截器就是这个RelectionUtils.USER_DECLARED_METHODS;顾名思义就是用户自己定义的方法,而非继承与Object类的方法什么的

所以这下detectHandlerMethods里面上半部分在干什么就已经知道了:无非就是获取这个handler类里面用户自定义的方法中有requestMapping注解信息的方法,并将该方法作为key,requestMapping注解信息作为value存入一个名为methods的Map中去

这里不得不提到这个RequestMappingInfo这个类,这个类就是封装了requestMapping注解信息的这么一个类,有兴趣的可以自己去看看它的内部定义,这里就不详解了。

接下来这个方法中就只剩下了这么一小段逻辑了,看名字就知道应该是遍历这个methods注册handlerMethod到某个地方去。

继续跟进这个方法看看,里面并没有做什么而是调用了另外一个方法,继续跟进。

 终于找到了最终的实现,首先加了一把锁,考虑的两个requestMapping并发注册的问题;然后根据这个handler与method获取了一个HandlerMethod对象,这个对象包含了handler与method的信息;接着确保同一个requestMapping唯一映射一个method
    ,因为不可能说一个url路径 /aaa/bbb 又对应methodA 又对应methodB是不允许的’;最终注册这个requestMappingInfo与对应handlerMethod的关系。(这里有意思的事情是发现有这么一个log的info语句 “Mapped xxx onto xxxx” 是不是在SpringBoot项目或者SpringMVC项目启动的时候控制台上见过,没错就是这里的)

       public void register(T mapping, Object handler, Method method) {
            this.readWriteLock.writeLock().lock();

            try {
    //根据这个handler与method获取了一个HandlerMethod对象,这个对象包含了handler与method的信息
                HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);
    //确保同一个requestMapping唯一映射一个method
    //因为不可能说一个url路径 /aaa/bbb 又对应methodA 又对应methodB是不允许的
                this.assertUniqueMethodMapping(handlerMethod, mapping);
                if(AbstractHandlerMethodMapping.this.logger.isInfoEnabled()) {
                    AbstractHandlerMethodMapping.this.logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
                }
                //后面都差不多是注册requestMapping与HandlerMethodInfo的关系
                this.mappingLookup.put(mapping, handlerMethod);
                List directUrls = this.getDirectUrls(mapping);
                Iterator name = directUrls.iterator();

                while(name.hasNext()) {
                    String corsConfig = (String)name.next();
                    this.urlLookup.add(corsConfig, mapping);
                }

                String name1 = null;
                if(AbstractHandlerMethodMapping.this.getNamingStrategy() != null) {
                    name1 = AbstractHandlerMethodMapping.this.getNamingStrategy().getName(handlerMethod, mapping);
                    this.addMappingName(name1, handlerMethod);
                }

                CorsConfiguration corsConfig1 = AbstractHandlerMethodMapping.this.initCorsConfiguration(handler, method, mapping);
                if(corsConfig1 != null) {
                    this.corsLookup.put(handlerMethod, corsConfig1);
                }

                this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod, directUrls, name1));
            } finally {
                this.readWriteLock.writeLock().unlock();
            }
        }

至此这个 detectHandlerMethods 方法的逻辑全都清楚了,前面先找寻出又requestMapping信息的method,接着就是将这个handler里面的RequestMappingInfo与一个HandlerMethod建立映射关系。

所以建立映射关系这一步已经全都清楚了,最后贴一个启动的控制台语句,果然对应的上非常 的有成就感啊。

小结:

说白了我们常用的@RequestMapping映射处理的初始化是由SpringIOC的这么一个回调接口InitializingBean来主导的,由于AbstractHandlerMethodMapping这个父类实现了InitializingBean,所以SpringIOC容器会回调父类的这么一个afterPropertiesSet方法,最后间接的调用了initHandlerMethods这个方法。在initHandlerMethods这个方法里面就是得到容器中所有的beanName,遍历这个beanName得到它的beanType,再看这个beanType是不是由@Controller或者@RequestMapping这个注解判断它是否是一个Handler,若是就去获取这个handler中所有含有requestMapping的方法,并放入一个key为Method并且value为RequestMappingInfo这么Map中去,接着遍历这个map注册ReuquestMappingInfo与HandlerMethod这么一个映射关系。这个HandlerMethod方法其实就是包含着handler与method的这么一个类。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值