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