Spring MVC @RequestMapping

41 篇文章 1 订阅

http://www.iocoder.cn/Spring-MVC/carlzhao/RequestMapping/

要想了解决Spring MVC是怎么把@RequestMapping注解了的方法以及类解析的,首先我们需要关注AbstractHandlerMethodMapping#afterPropertiesSet这个方法,而这个方法是Spring的init方法,是Spring在进行初始化bean之前在DI注入之后调用到的方法。所以当Spring容器初始化之后,@RequestMapping就会被解析成Spring容器的bean管理。通过这个方法的说明也能验证之前所说的。我们首先来关注一下@RequestMapping的时序图:
这里写图片描述

下面就入口方法。

/**
 * Detects handler methods at initialization.
 */
public void afterPropertiesSet() {
    initHandlerMethods();
}

1、初始化处理client页面request的方法

下面我们就要重点关注AbstractHandlerMethodMapping#initHandlerMethods这个方法了。

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #isHandler(Class)
 * @see #getMappingForMethod(Method, Class)
 * @see #handlerMethodsInitialized(Map)
 */
protected void initHandlerMethods() {
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for request mappings in application context: " + getApplicationContext());
    }

    // 读取Spring容器中的所有beanNames
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
            getApplicationContext().getBeanNamesForType(Object.class));

    // 遍历这些bean,检测出处理方法
    for (String beanName : beanNames) {
        // 判断这个bean是不是处理类(根据这个类是否使用了@Controller或@RequestMapping)
        if (isHandler(getApplicationContext().getType(beanName))){
            detectHandlerMethods(beanName);
        }
    }
    // 处理方法后期处理,Spring空实现,方便以后扩展
    handlerMethodsInitialized(getHandlerMethods());
}

那么我们的关注点就需要放在detectHandlerMethods这个方法上了。

2、detectHandlerMethods

下面我们再来看一下AbstractHandlerMethodMapping#detectHandlerMethods这个方法。

protected void detectHandlerMethods(final Object handler) {
    // 从Spring容器中获取这个类
    Class<?> handlerType =
            (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());

    // Avoid repeated calls to getMappingForMethod which would rebuild RequestMatchingInfo instances
    final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
    final Class<?> userType = ClassUtils.getUserClass(handlerType);

    // 从处理类(@Controller)中获取处理方法(@RequestMapping)
    Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
        public boolean matches(Method method) {
            T mapping = getMappingForMethod(method, userType);
            if (mapping != null) {
                mappings.put(method, mapping);
                return true;
            }
            else {
                return false;
            }
        }
    });
    // 注册方法,
    for (Method method : methods) {
        registerHandlerMethod(handler, method, mappings.get(method));
    }
}

3、getMappingForMethod

Spring MVC通过RequestMappingHandlerMapping#getMappingForMethod(Method method, Class

/**
 * Uses method and type-level @{@link RequestMapping} annotations to create
 * the RequestMappingInfo.
 * @return the created RequestMappingInfo, or {@code null} if the method
 * does not have a {@code @RequestMapping} annotation.
 * @see #getCustomMethodCondition(Method)
 * @see #getCustomTypeCondition(Class)
 */
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    RequestMappingInfo info = null;
    // 这个方法是否使用@RequestMapping信息
    RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
    if (methodAnnotation != null) {
        // 空实现,方便以后扩展
        RequestCondition<?> methodCondition = getCustomMethodCondition(method);
        // 创建RequestMappingInfo比较简单,如果感兴趣可以自己去看看
        info = createRequestMappingInfo(methodAnnotation, methodCondition);
        // 查询这个Controller上面使用@RequestMapping信息
        RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
        if (typeAnnotation != null) {
            // 空实现,方便以后扩展
            RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
            // 把Class上面的@RequestMapping信息与方法上的@RequestMapping信息合并起来
            info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
        }
    }

    return info;
}

返回info主要是包含@RequestMapping的标签信息,我们可以看看info中的信息:
这里写图片描述

4、registerHandlerMethod

然后我们就关注一下。第二步中的detectHandlerMethods方法中的registerHandlerMethod方法。

/**
 * Register a handler method and its unique mapping.
 * @param handler the bean name of the handler or the handler instance
 * @param method the method to register
 * @param mapping the mapping conditions associated with the handler method
 * @throws IllegalStateException if another method was already registered
 * under the same mapping
 */
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
    HandlerMethod oldHandlerMethod = this.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);
    }

    Set<String> patterns = getMappingPathPatterns(mapping);
    // 存在URL与RequestMappingInfo的映射
    for (String pattern : patterns) {
        if (!getPathMatcher().isPattern(pattern)) {
            this.urlMap.add(pattern, mapping);
        }
    }
}

现在我们来看看这个解析之后这个类之中属性。
这里写图片描述
我们主要把关注点停留到urlMap与handlerMethods上。
1、urlMap
这里写图片描述
我们可以看到urlMap属性中保存的是请求路径与RequestMappingInfo的映射信息。
2、handerMethods
这里写图片描述
我们可以看到handerMethods属性中保存的是RequestMappingInfo与HanderMethod的映射。HanderMethod主要保存这个类与处理请求具体的方法。

当页面有url请求过来.首先通过urlMap查询RequestMappingInfo的信息,然后再通过handerMethods查询到HanderMethod的信息。这样url请求是不是与Controller中的方法结合了起来。具体是怎么调用的,可以看看我写的Blog – Spring MVC DispatcherServlet

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值