SpringMVC------RequestMappingHandlerMapping

前言

1.RequestMappingHandlerMapping的初始化流程,首先看下RequestMappingHandlerMapping的类图:

从类图可以看出RequestMappingHandlerMapping 实现了InitializingBean>>>在AbstractHandlerMethodMapping 中实现了afterPropertiesSet()方法,会在容器注入的进去的时候执行。

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

	/**
	 * Scan beans in the ApplicationContext, detect and register handler methods.
	 * @see #getCandidateBeanNames()
	 * @see #processCandidateBean
	 * @see #handlerMethodsInitialized
	 */
	protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

看源码可知RequestMappingHandlerMapping大致工作流程如下:

1.RequestMappingHandlerMapping 向容器中注册的时候,检测到实现了 InitializingBean接口,容器去执行afterPropertiesSet(),在afterPropertiesSet中完成Controller中完成方法的映射。

2.initHandlerMethods 首先获取容器中全部的beanNames。

3.根据名字获取类,判断该类是不是要转换的类型。

	/**
	 * {@inheritDoc}
	 * <p>Expects a handler to have either a type-level @{@link Controller}
	 * annotation or a type-level @{@link RequestMapping} annotation.
	 */
	@Override
	protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

4.调用detectHandlerMethods开始做类型和路径映射转换。

/**
	 * Look for handler methods in the specified handler bean.
	 * @param handler either a bean name or an actual handler instance
	 * @see #getMappingForMethod
	 */
	protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}
	@Override
	@Nullable
	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				info = typeInfo.combine(info);
			}
			String prefix = getPathPrefix(handlerType);
			if (prefix != null) {
				info = RequestMappingInfo.paths(prefix).build().combine(info);
			}
		}
		return info;
	}

根据类型的方法做映射转换。

5.此处应该是getMappingForMethod 是扩展点,可以继承RequestMappingHandlerMapping重写getMappingForMethod实现自己的映射规则。例如:

package com.xf.springboot;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;

/**
 * @Author: xf
 * @Date: 2019/7/29 13:55
 * @Version 1.0
 */
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomerRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo requestMappingInfo = super.getMappingForMethod(method, handlerType);
        if (requestMappingInfo != null) {
            return requestMappingInfo;
        }
        return createCustomRequestMappingInfo(method);
    }


    private RequestMappingInfo createCustomRequestMappingInfo(Method method) {
        RestMapping mapping = AnnotatedElementUtils.findMergedAnnotation(method, RestMapping.class);
        if (mapping != null) {
            return RequestMappingInfo.paths(Samples.URI_PREFIX_OF_API + mapping.value())
                    .methods(mapping.method())
                    .build();
        }
        return null;
    }
}

如何通过RequestMappingHandlerMapping 找到请求对应的handler(方法)

spring mvc中,当请求到来的时候,会被统一到DispatcherServlet的doDispatch去执行。
看下doDispatch的部分代码:

mappedHandler = getHandler(processedRequest); 就是根据请求获取处理的handler。

	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

此方法会根据注入到DispatcherServlet的RequestMappingHandlerMapping 集合来找到该请求对应的handler用于处理该请求。

        @Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //getHandlerInternal(request)方法为抽象方法,供子类实现
        //获取到的handler对象一般为bean/HandlerMethod
		Object handler = getHandlerInternal(request);
 //上述找不到则使用默认的处理类,没有设定则返回null,则会返回前台404错误
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}

        //创建处理链对象
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		if (logger.isTraceEnabled()) {
			logger.trace("Mapped to " + handler);
		}
		else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
			logger.debug("Mapped to " + executionChain.getHandler());
		}
         //针对cros跨域请求的处理
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}

RequestMappingHandlerMapping 如何注入到DispatcherServlet中

在DispatcherServlet初始化的时候,会调用内部的的一个onRefresh方法。最终会调用initHandlerMappings方法。

/**
	 * Initialize the HandlerMappings used by this class.
	 * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
	 * we default to BeanNameUrlHandlerMapping.
	 */
	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

initHandlerMappings方法获取注入到容器中的全部的RequestMappingHandlerMapping,按照规则排序。赋值给本身的handlerMappings。当请求到来的时候会遍历handlerMappings,找到合适初始方法进行处理。如下是DispatcherServlet当请求达到的时候遍历handlerMappings。

RequestMappingHandlerMapping 的注入容器

RequestMappingHandlerMapping 作为spring mvc 路由组件,起到了从路径到指定方法的转换作用。只有注入到容器中,在请求达到的时候,从容器中获取到RequestMappingHandlerMapping 在通过RequestMappingHandlerMapping获取到handler用于处理请求。
RequestMappingHandlerMapping的注入分为四种:

  1. 如果是基于xml配置的springmvc项目 在springmvc的配置文件中加入<mvc:annotation-driven/> ,在容器解析配置的文件的时候,会初始化RequestMappingHandlerMapping 并注入到容器中。
  2. 如果是基于注解的形式springmvc项目。可以使用@EnableWebMvc来初始化RequestMappingHandlerMapping并注入到容器中。具体实现请看
    Spring注解开发
  3. 当基于spring boot 开发的时候 WebMvcAutoConfiguration 会自动加载web开发相关的配置。其中EnableWebMvcConfiguration 继承自DelegatingWebMvcConfiguration,DelegatingWebMvcConfiguration的内部原理 在Spring注解开发 已经做了阐述。EnableWebMvcConfiguration在WebMvcAutoConfiguration中一@Bean的形式被注入到容器中。因此RequestMappingHandlerMapping 此时会被创建注入到容器中。
  4. 自定义RequestMappingHandlerMapping,注入到容器中。可以采用xml的形式注入,也可以通过@Bean的形式注入;例如
    @Bean
    public RequestMappingHandlerMapping customRequestMappingHandlerMapping() {
        CustomerRequestMappingHandlerMapping mappingHandlerMapping = new CustomerRequestMappingHandlerMapping();
        mappingHandlerMapping.setOrder(Ordered.HIGHEST_PRECEDENCE);//设置排序
        return mappingHandlerMapping;
    }

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值