SpringMVC源码之Controller查找原理

SpringMVC源码之Controller查找原理

Controller查找在上图中对应的步骤1至2的过程

SpringMVC初始化过程 理解初始化过程之前,先认识两个类

RequestMappingInfo类,对RequestMapping注解封装。里面包含http请求头的相关信息。如uri、method、params、header等参数。一个对象对应一个RequestMapping注解

HandlerMethod类,是对Controller的处理请求方法的封装。里面包含了该方法所属的bean对象、该方法对应的method对象、该方法的参数等。

SpringMVC源码之Controller查找原理

上图是RequestMappingHandlerMapping的继承关系。在SpringMVC初始化的时候,首先执行RequestMappingHandlerMapping中的afterPropertiesSet方法,然后会进入AbstractHandlerMethodMapping的afterPropertiesSet方法(line:93),这个方法会进入当前类的initHandlerMethods方法(line:103)。这个方法的职责便是从applicationContext中扫描beans,然后从bean中查找并注册处理器方法,代码如下。

protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } //获取applicationContext中所有的bean name String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); //遍历beanName数组 for (String beanName : beanNames) { //isHandler会根据bean来判断bean定义中是否带有Controller注解或RequestMapping注解 if (isHandler(getApplicationContext().getType(beanName))){ detectHandlerMethods(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }

isHandler方法其实很简单,如下

@Override protected boolean isHandler(Class<?> beanType) { return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) || (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null)); }

就是判断当前bean定义是否带有Controlller注解或RequestMapping注解,看了这里逻辑可能会想如果只有RequestMapping会生效吗?答案是不会的,因为在这种情况下Spring初始化的时候不会把该类注册为Spring bean,遍历beanNames时不会遍历到这个类,所以这里把Controller换成Compoent注解也是可以,不过一般不会这么做。当确定bean为handlers后,便会从该bean中查找出具体的handler方法(也就是我们通常定义的Controller类下的具体定义的请求处理方法),查找代码如下

protected void detectHandlerMethods(final Object handler) { //获取到当前Controller bean的class对象 Class<?> handlerType = (handler instanceof String) ? getApplicationContext().getType((String) handler) : handler.getClass(); //同上,也是该Controller bean的class对象 final Class<?> userType = ClassUtils.getUserClass(handlerType); //获取当前bean的所有handler method。这里查找的依据便是根据method定义是否带有RequestMapping注解。如果有根据注解创建RequestMappingInfo对象 Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() { public boolean matches(Method method) { return getMappingForMethod(method, userType) != null; } }); //遍历并注册当前bean的所有handler method for (Method method : methods) { T mapping = getMappingForMethod(method, userType); //注册handler method,进入以下方法 registerHandlerMethod(handler, method, mapping); } }

以上代码有两个地方有调用了getMappingForMethod方法

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = null; //获取method的@RequestMapping注解 RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class); if (methodAnnotation != null) { RequestCondition<?> methodCondition = getCustomMethodCondition(method); info = createRequestMappingInfo(methodAnnotation, methodCondition); //获取method所属bean的@RequtestMapping注解 RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class); if (typeAnnotation != null) { RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType); //合并两个@RequestMapping注解 info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info); } } return info; }

这个方法的作用就是根据handler method方法创建RequestMappingInfo对象。首先判断该mehtod是否含有RequestMpping注解。如果有则直接根据该注解的内容创建RequestMappingInfo对象。创建以后判断当前method所属的bean是否也含有RequestMapping注解。如果含有该注解则会根据该类上的注解创建一个RequestMappingInfo对象。然后在合并method上的RequestMappingInfo对象,最后返回合并后的对象。现在回过去看detectHandlerMethods方法,有两处调用了getMappingForMethod方法,个人觉得这里是可以优化的,在第一处判断method时否为handler时,创建的RequestMappingInfo对象可以保存起来,直接拿来后面使用,就少了一次创建RequestMappingInfo对象的过程。然后紧接着进入registerHandlerMehtod方法,如下

protected void registerHandlerMethod(Object handler, Method method, T mapping) { //创建HandlerMethod HandlerMethod newHandlerMethod = createHandlerMethod(handler, method); HandlerMethod oldHandlerMethod = handlerMethods.get(mapping); //检查配置是否存在歧义性 if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) { throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() + "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" + oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped."); } this.handlerMethods.put(mapping, newHandlerMethod); if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod); } //获取@RequestMapping注解的value,然后添加value->RequestMappingInfo映射记录至urlMap中 Set<String> patterns = getMappingPathPatterns(mapping); for (String pattern : patterns) { if (!getPathMatcher().isPattern(pattern)) { this.urlMap.add(pattern, mapping); } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxfwg.html