SpringMVC源码笔记2——执行getHandler获取HandlerExecutionChain
整体流程图

* 遍历List<HandlerMapping> handlerMappings,调用每一个HandlerMapping的getHandler(request)以获取请求对应的HandlerExecutionChain对象
* 返回第一个匹配HandlerMapping的getHandler(request)的结果
HandlerExecutionChain mappedHandler = this.getHandler(processedRequest);
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Iterator var2 = this.handlerMappings.iterator();
HandlerExecutionChain handler;
do {
if (!var2.hasNext()) {
return null;
}
HandlerMapping hm = (HandlerMapping)var2.next();
handler = hm.getHandler(request);
} while(handler == null);
return handler;
}
在initHandlerMappings里初始化HandlerMapping实例集合
* 在容器中获取HandlerMapping类型的对象,多个就进行排序(可基于@Order),不存在就是要默认配置文件指定的类型
* 在WebMvcConfigurationSupport配置支持类中,默认本类注册以下处理器映射:
- RequestMappingHandlerMapping(@RequestMapping注解处理器映射) ,顺序为0,用于映射带注解的控制器方法
- HandlerMapping (处理器映射),顺序1,用于映射url路径和视图名称,基于ViewController方法
- BeanNameUrlHandlerMapping(bean名称处理器映射),顺序2,用于映射url路径到控制器bean名称
- RouterFunctionMapping(路由器功能映射),顺序3,用于映射路由函数
- HandlerMapping (处理器映射),顺序Integer.MAX_VALUE-1,用于处理静态资源请求,基于addResourceHandlers方法
- HandlerMapping (处理器映射),顺序Integer.MAX_VALUE,用于服务器内部重定向到一个默认的servlet
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
OrderComparator.sort(this.handlerMappings);
}
} else {
try {
HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException var3) {}
}
if (this.handlerMappings == null) {
this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
}
}
HandlerMapping体系

HandlerMapping接口
HandlerExecutionChain.getHandler(HttpServletRequest)
* 核心方法HandlerExecutionChain getHandler(HttpServletRequest request)
根据HttpServletRequest信息匹配出一个HandlerExecutionChain实例
public interface HandlerMapping {
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
HandlerExecutionChain对象
* 封装了一个HttpServletRequest 最终匹配的信息,包括:
- 处理器handler,一个用户处理最终请求的处理对象,处理请求的核心逻辑
- 此请求匹配的拦截器
* 提供了拦截器前置、后置处理的调用方法
- applyPreHandle前置处理
①获取拦截器集合,遍历执行每一个拦截器的preHandle方法
②如果一个拦截器的preHandle方法返回false,执行triggerAfterCompletion后返回false,不会执行下一个
③triggerAfterCompletion的逻辑是遍历调用每一个拦截器的afterCompletion方法
- applyPostHandle后置处理
①获取拦截器集合,遍历执行每一个拦截器的postHandle方法,都会执行
* 提供一个afterConcurrentHandlerStarted方法在,整个请求结束之后,即DispatcherServlet的doDispatch的finally里
用于执行AsyncHandlerInterceptor类型拦截器的afterConcurrentHandlingStarted方法
public class HandlerExecutionChain {
private final Object handler;
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex = -1;
public HandlerExecutionChain(Object handler) {
this(handler, (HandlerInterceptor[]) null);
}
public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
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 void addInterceptor(HandlerInterceptor interceptor) {
initInterceptorList().add(interceptor);
}
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 = this.interceptorList.toArray(new HandlerInterceptor[0]);
}
return this.interceptors;
}
/**
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = 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 = 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 ex2) {
}
}
}
}
void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
if (interceptors[i] instanceof AsyncHandlerInterceptor) {
try {
AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
}
catch (Throwable ex) {
}
}
}
}
}
}
AbstractHandlerMapping
继承WebApplicationObjectSupport
* ApplicationObjectSupport抽象类实现了ApplicationContextAware接口(父Aware接口)
- 封装了applicationContext
- 在Aware实现方法setApplicationContext里:
①获取容器传递进来的applicationContext
②调用了一个initApplicationContext(context)方法,子类可以重写对传递进来的ApplicationContext进行自定义处理
* WebApplicationObjectSupport,继承ApplicationObjectSupport,且实现ServletContextAware接口
- 重写了父类的initApplicationContext(context)抽象方法,
①执行super.initApplicationContext(context),其里面又会执行initApplicationContext()
赋值了成员this.servletContext = ((WebApplicationContext) context).getServletContext(),是通过父类的WebApplicationContext中获取到servletContext
②也执行initServletContext(ServletContext servletContext)方法,子类可以重写对传递进来的servletContext进行自定义处理
- 实现ServletContextAware,在setServletContext(ServletContext servletContext)获取了新servletContext,如果和成员this.servletConte的值不一样就执行initServletContext(ServletContext servletContext) 抽象方法
public abstract class ApplicationObjectSupport implements ApplicationContextAware {
@Nullable
private ApplicationContext applicationContext;
@Nullable
private MessageSourceAccessor messageSourceAccessor;
@Override
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
this.applicationContext = null;
this.messageSourceAccessor = null;
}
else if (this.applicationContext == null) {
if (!requiredContextClass().isInstance(context)) {
throw new ApplicationContextException();
}
this.applicationContext = context;
this.messageSourceAccessor = new MessageSourceAccessor(context);
initApplicationContext(context);
}
else {
if (this.applicationContext != context) {
throw new ApplicationContextException();
}
}
}
protected void initApplicationContext(ApplicationContext context) throws BeansException {
initApplicationContext();
}
protected void initApplicationContext() throws BeansException {
}
...
}
public abstract class WebApplicationObjectSupport extends ApplicationObjectSupport implements ServletContextAware {
@Nullable
private ServletContext servletContext;
@Override
public final void setServletContext(ServletContext servletContext) {
if (servletContext != this.servletContext) {
this.servletContext = servletContext;
initServletContext(servletContext);
}
}
@Override
protected void initApplicationContext(ApplicationContext context) {
super.initApplicationContext(context);
if (this.servletContext == null && context instanceof WebApplicationContext) {
this.servletContext = ((WebApplicationContext) context).getServletContext();
if (this.servletContext != null) {
initServletContext(this.servletContext);
}
}
}
protected void initServletContext(ServletContext servletContext) {
}
...
}
收集HandlerInterceptor
概述
//保存用户添加进来的的拦截器
private final List<Object> interceptors = new ArrayList<>();
//对interceptors进行类型过滤后的最终拦截器集合
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
* 类继承WebApplicationObjectSupport实现了initApplicationContext()方法,执行时间在setApplicationContext方法中,以初始化所有拦截器集合
- 执行抽象方法extendInterceptors(this.interceptors) 子类可能会新增一些Interceptors到此List<Object>interceptors中
- 从容器中查找MappedInterceptor类型的实例集合加入到List<HandlerInterceptor> this.adaptedInterceptors
MappedInterceptor是HandlerInterceptor接口实现类,可以指定includePatterns、excludePatterns匹配
- 遍历interceptors(用户通过WebMvcConfigurationSupport配置进来)中的拦截器类型,收集到List<HandlerInterceptor> adaptedInterceptors中
如果是HandlerInterceptor类型直接加入adaptedInterceptors,用户自定义的拦截器一般都是此类型,注MappedInterceptor是HandlerInterceptor类型
如果是WebRequestInterceptor需要转为WebRequestHandlerInterceptorAdapter加入adaptedInterceptors
其他类型报错
* 内部也关联了两个辅助组件
- UrlPathHelper类
是Spring中的一个帮助类,有很多与URL路径有关的实用方法,除了对URL进行去除分号、解码外,还能基于HttpServletRequest获取getContextPath/getServletPath等方法
在本类中使用其getLookupPathForRequest方法返回请求的查找路径,一般返回的是应用中的路径除去ServletPath
- PathMatcher类
用于路径上的模式匹配
自定义拦截器流程
* 用户通过实现WebMvcConfigurer接口,调用addInterceptors,使用registry的addInterceptor自定义的拦截器
- 此方法addInterceptors(InterceptorRegistry registry)会被在getInterceptors(..)中会跳跃用于收集用户自定义的拦截器
- 用户使用registry.addInterceptor(new xxx())会返回InterceptorRegistration代表一个拦截器注册对象
如果用户接着添加了.addPathPatterns/excludePathPatterns,那么在执行registry.getInterceptors()的时候会把这个拦截器转为MappedInterceptor
* 每一个HandlerMapping的bean方法中,都会调用getInterceptors(..)取获取拦截器集合
* getInterceptors(..)会把InterceptorRegistry 内部的List<InterceptorRegistration> registrations 返回
最终会设置到RequestMappingHandlerMappingsetInterceptors(getInterceptors());即AbstractHandlerMapping的List<Object>interceptors中
@Override
public void addInterceptors(InterceptorRegistry registry) {
super.addInterceptors(registry);
registry.addInterceptor(new TestInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/emp/toLogin","/emp/login","/js/**","/css/**","/images/**");
}
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
...
}
protected final Object[] getInterceptors(
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
protected List<Object> getInterceptors() {
return this.registrations.stream()
.sorted(INTERCEPTOR_ORDER_COMPARATOR)
.map(InterceptorRegistration::getInterceptor)
.collect(Collectors.toList());
}
//InterceptorRegistration
protected Object getInterceptor() {
if (this.includePatterns.isEmpty() && this.excludePatterns.isEmpty()) {
return this.interceptor;
}
String[] include = StringUtils.toStringArray(this.includePatterns);
String[] exclude = StringUtils.toStringArray(this.excludePatterns);
MappedInterceptor mappedInterceptor = new MappedInterceptor(include, exclude, this.interceptor);
if (this.pathMatcher != null) {
mappedInterceptor.setPathMatcher(this.pathMatcher);
}
return mappedInterceptor;
}
实现getHandler(request)方法整体框架
* 实现了HandlerMapping接口的HandlerExecutionChain getHandler(HttpServletRequest request)方法,提供一个模板过程返回HandlerExecutionChain,
①向子类暴露一个抽象方法getHandlerInternal以获取对应的处理器handler,表示一个将会处理当前request的方法实体处理器
- 如果为空使用defaultHandler属性指定的
- 具体什么类型有子类决定,例如对应的controller类的一个方法包装类,或者是一个其他类型的对象
②如果第①步返回的是String,则会从容器中获取
③执行getHandlerExecutionChain方法获取HandlerExecutionChain,封装了处理器handle和合适的拦截器集合
- 遍历List<HandlerInterceptor> adaptedInterceptors中的拦截器,如果拦截器是MappedInterceptor,使用mappedInterceptor.matches进行匹配后加入HandlerExecutionChain
- 否则直接加入HandlerExecutionChain
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered, BeanNameAware {
@Nullable
private Object defaultHandler;
private UrlPathHelper urlPathHelper = new UrlPathHelper();
private PathMatcher pathMatcher = new AntPathMatcher();
//保存用户添加进来的的拦截器
private final List<Object> interceptors = new ArrayList<>();
//对interceptors进行类型过滤后的最终拦截器集合
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
@Nullable
private CorsConfigurationSource corsConfigurationSource;
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered
@Nullable
private String beanName;
//set/getter
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
protected void extendInterceptors(List<Object> interceptors) {
}
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
if (interceptor instanceof HandlerInterceptor) {
return (HandlerInterceptor) interceptor;
}
else if (interceptor instanceof WebRequestInterceptor) {
return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
}
else {
throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
}
}
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
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 (hasCorsConfigurationSource(handler)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
@Nullable
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
}
暴露的getHandlerInternal获取处理器的抽象方法(三个直接子类实现了)
* AbstractHandlerMethodMapping
底层有一个注册表MappingRegistry,包含了url和HandlerMethod类型处理器的映射,返回的HandlerMethod包含一个方法对应的实例、方法反射对象,方法参数信息等,是以一个类方法维度处理请求
* AbstractUrlHandlerMapping
底层有一个 Map<String, Object> handlerMap,String表示url,Object是对应的处理对象,这个Object可以是String(会从容器获取)或者其他类型
例如ParameterizableViewController处理器或者自己实现Controller接口类
* RouterFunctionMapping
底层有一个路由表RouterFunction<?> routerFunction(默认为DifferentComposedRouterFunction类型,会收集容器中RouterFunction类型的实例,作为一个路由表
通过对请求的匹配,返回一个匹配的Optional<HandlerFunction<T>>函数指针用于处理请求
AbstractHandlerMethodMapping<T>
概述
* AbstractHandlerMethodMapping<T>是AbstractHandlerMapping其中一个子类
- 从类名可以知道,基于方法的映射规则抽象类,抽取了基于方法作为映射器的实现规则,是以一个类方法维度处理请求
- 需要注意这个的类的泛型T,,表示的是一个mapping,其包含了一个处理器匹配的请求信息,如请求url、请求方法、请求头指定
例如一个@RequestMapping方法A,那么@RequestMapping注解里所有指定的信息即为mapping,方法就为此mapping对应的处理器
* 其底层有一个注册表MappingRegistry,包含了url和HandlerMethod类型处理器的映射
HandlerMethod包含一个方法对应的实例、方法反射对象,方法参数信息等
* 在afterPropertiesSet调用initHandlerMethods进行初始化方法处理器:
- 遍历容器中的所有类型,执行子类的isHandler(beanType)决定此类是否为Handler(比如@Contrller注解的类),是的话执行detectHandlerMethods方法,
- 会找到指定类的所有方法method和其对应的映射信息到Map<Method, T> T表示mapping,其包含了一个处理器方法匹配的请求信息,如请求url、请求方法、请求头指定
T需要调用抽象方法getMappingForMethod()获取一个Method的mapping;
- 最后执行this.mappingRegistry.register(mapping, handler, method)先注册到mappingRegistry中
* 实现了getHandlerInternal返回HandlerMethod具体类型,黑色加粗需要子类实现或重写
①通过UrlPathHelper().getLookupPathForRequest(request)先获取当前request的url路径
②在urlLookup中获取对应的T mapping
- urlLookup表注册了直接明确url和T mapping信息的映射
- 调用addMatchingMappings方法对mapping和request进行进一步的详细匹配,比如对请求方法、producer\consumer的匹配
进一步匹配的逻辑由子类实现getMatchingMapping方法
- 即时是明确的url,同样会再次匹配@RequestMapping中的headers, params, produces, consumes, methods,这一步可能是的直接url匹配的结果为false从而走③
例如:虽然url一样,当时请求算要求的响应格式是json,而此处理器只返回xml。这就不匹配了
③如果②获取不到,那就选择mappingRegistry表,调用addMatchingMappings方法对mapping和request进行进一步的详细匹配,
④获取最终匹配的mapping,有多个就进行排序,比较规则有抽象方法getMappingComparator()提供
排序后,前2个是相等的,报错
⑤如果最佳匹配只有一个
- 先执行handleMatch方法对最终选择的T mapping,进行一些额外处理,子类可以重写,此类默认实现只做了lookupPath的request属性设置
handleMatch(bestMatch.mapping, lookupPath, request);
- 返回T mapping对应的HandlerMethod
⑥如果没有找到对应的映射对象调用抽象方法handleNoMatch(),子类可以重写,此类默认返回null
* 暴露的抽象方法
//判断一个类是否为处理器类
protected abstract boolean isHandler(Class<?> beanType);
//获取一个处理器方法对于的映射信息T mapping
@Nullable
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
//获取映射信息T mapping中的路径,包括模式匹配
protected abstract Set<String> getMappingPathPatterns(T mapping);
//使用T mapping和请求request进行详细的匹配,不单单是url的匹配,还有参数、请求头等
@Nullable
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
//获取最终匹配的mapping后,有多个就进行排序,比较规则
protected abstract Comparator<T> getMappingComparator(HttpServletRequest request);
afterPropertiesSet收集所有处理器方法到mappingRegistry
* 遍历容器中的所有类型实例,执行子类的isHandler(beanType)决定此类是否为Handler(比如@Contrller注解的类),是的话执行detectHandlerMethods方法,
protected abstract boolean isHandler(Class<?> beanType);
* detectHandlerMethods方法
- 会找到指定类的所有方法method和其对应的映射信息到Map<Method, T> T表示mapping,其包含了一个处理器方法匹配的请求信息,如请求url、请求方法、请求头指定
- T需要调用抽象方法getMappingForMethod()获取一个Method的mapping;
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
* 最后执行this.mappingRegistry.register(mapping, handler, method)先注册到mappingRegistry中(见上),维护到如下映射关系对象中
- 注册了T mapping信息和HandlerMethod的映射
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
- 注册了直接明确url(路径上不存在模式匹配)和T mapping信息的映射
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
- 注册了类名+方法名和T mapping信息的映射
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>()
- 把T mapping、handlerMethod、directUrls、name封装到MappingRegistration中
加入registry表 T mapping -> MappingRegistration
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
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();
}
});
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//mappingRegistry.register(mapping, handler, method);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
MappingRegistry映射注册表
* 里面存有多种维度的映射关系表
- 注册了T mapping信息和HandlerMethod的映射
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
- 注册了直接明确url和T mapping信息的映射
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
- 注册了类名+方法名和T mapping信息的映射
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>()
- 把T mapping、handlerMethod、directUrls、name封装到MappingRegistration中
加入registry表 T mapping -> MappingRegistration
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
* 注册一个T mapping映射类方法method的过程,register(T mapping, Object handler, Method method)
- 加锁,进行线程安全控制
- 把类handler、方法method反射信息到HandlerMethod,new HandlerMethod(handler, method);handler表示方法所在的类实例
此时的HandlerMethod仅仅只包含了类handler、方法method两个信息
- 验证T mapping唯一性,使用T mapping的equals,在mappingLookup以及存在就不能再注册了
- 加入mappingLookup表 T mapping -> HandlerMethod
this.mappingLookup.put(mapping, handlerMethod);
- 从T mapping找出明确的url,使用抽象方法getMappingPathPatterns(T mapping)获取此mapping所有封装的url
过滤出非模式匹配的,加入urlLookup表 directUrls -> T mapping
this.urlLookup.add(url, mapping);
例如存在路径参数的就属于模式匹配,此时就不会加入urlLookup
- 使用命名策略,一般为类名.方法名,加入nameLookup表 name -> handlerMethod
addMappingName(name, handlerMethod);
- 跨域处理,略
- 把T mapping、handlerMethod、directUrls、name封装到MappingRegistration中
加入registry表 T mapping -> MappingRegistration
class MappingRegistry {
//把T mapping、handlerMethod、directUrls、name封装到MappingRegistration中
//加入registry表 T mapping -> MappingRegistration
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
//注册了 mapping信息和HandlerMethod的映射
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
//注册了直接明确url和mapping信息的想、映射
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
//注册了类名+方法名和mapping信息的想、映射
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void register(T mapping, Object handler, Method method) {
// Assert that the handler method is not a suspending one.
this.readWriteLock.writeLock().lock();
try {
//封装了类、方法反射信息到HandlerMethod
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
//验证T mapping唯一性,使用T mapping的equals
validateMethodMapping(handlerMethod, mapping);
//加入mappingLookup表 T mapping -> HandlerMethod
this.mappingLookup.put(mapping, handlerMethod);
//从T mapping找出明确的url,使用抽象方法getMappingPathPatterns(T mapping)获取此mapping所有封装的url
//过滤出非模式匹配的
//加入urlLookup表 directUrls -> T mapping
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
//使用命名策略,一般为类名.方法名
//加入nameLookup表 name -> handlerMethod
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
//略
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
//把T mapping、handlerMethod、directUrls、name封装到MappingRegistration中
//加入registry表 T mapping -> MappingRegistration
this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
private List<String> getDirectUrls(T mapping) {
List<String> urls = new ArrayList<>(1);
for (String path : getMappingPathPatterns(mapping)) {
if (!getPathMatcher().isPattern(path)) {
urls.add(path);
}
}
return urls;
}
private void addMappingName(String name, HandlerMethod handlerMethod) {
List<HandlerMethod> oldList = this.nameLookup.get(name);
if (oldList == null) {
oldList = Collections.emptyList();
}
for (HandlerMethod current : oldList) {
if (handlerMethod.equals(current)) {
return;
}
}
List<HandlerMethod> newList = new ArrayList<>(oldList.size() + 1);
newList.addAll(oldList);
newList.add(handlerMethod);
this.nameLookup.put(name, newList);
}
public void unregister/removeMappingName(T mapping) {
//略
}
}
private static class MappingRegistration<T> {
private final T mapping;
private final HandlerMethod handlerMethod;
private final List<String> directUrls;
@Nullable
private final String mappingName;
//....
}
实现getHandlerInternal返回HandlerMethod具体类型
HandlerMethod类型
* 返回的类型是HandlerMethod,包含方法的信息,如下
public class HandlerMethod {
//方法对应的bean实例
private final Object bean;
@Nullable
private final BeanFactory beanFactory;
//bean的类型
private final Class<?> beanType;
//方法反射对象
private final Method method;
//桥架方法
private final Method bridgedMethod;
//方法参数
private final MethodParameter[] parameters;
//@responseStatus注解的属性内容
@Nullable
private HttpStatus responseStatus;
@Nullable
private String responseStatusReason;
//二次封装时,底层的HandlerMethod
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
//获取此方法,对应所有接口上方法标注的所有注解信息
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
private final String description;
}
getHandlerInternal实现逻辑
①通过UrlPathHelper().getLookupPathForRequest(request)先获取当前request的url路径
②在直接路径表urlLookup中获取对应的T mapping
- urlLookup表注册了直接明确url和T mapping信息的映射
- 调用addMatchingMappings方法对mapping和request进行进一步的详细匹配,比如对请求方法、producer\consumer的匹配
进一步匹配的逻辑由子类实现getMatchingMapping方法
在RequestMappingInfoHandlerMapping中使用遍历每一个RequestMappingInfo的getMatchingCondition(request);进行动态匹配
③如果②获取不到,那就选择mappingRegistry表,调用addMatchingMappings方法对mapping和request进行进一步的详细匹配,会进行url动态的模式匹配
④获取最终匹配的mapping,有多个就进行排序,比较规则有抽象方法getMappingComparator()提供
排序后,前2个是相等的,报错
⑤如果最佳匹配只有一个
- 先执行handleMatch方法对最终选择的T mapping,进行一些额外处理,子类可以重写,此类默认实现只做了lookupPath的request属性设置
handleMatch(bestMatch.mapping, lookupPath, request);
- 返回T mapping对应的HandlerMethod
⑥如果没有找到对应的映射对象调用抽象方法handleNoMatch(),子类可以重写,此类默认返回null
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取请求路径url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
//匹配此url对应的最佳HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
//存储最佳候选,封装了T mapping和HandlerMethod
List<Match> matches = new ArrayList<>();
//从urlLookup表中获取T mapping
//urlLookup表注册了直接明确url和T mapping信息的映射
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//对mapping和request进行进一步的详细匹配,比如对请求方法、producer\consumer的匹配
addMatchingMappings(directPathMatches, matches, request);
}
//如果urlLookup表中没有,那就直接在mappingRegistry表,里面包含所有注册进来的mapping->method映射
//例如存在模式匹配的mapping就在里面
if (matches.isEmpty()) {
//对mapping和request进行详细匹配,比如对请求方法、producer\consumer的匹配
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
//排序,调用抽象方法getMappingComparator获取比较器
Comparator<Match> comparator = new MatchComparator((request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
//option跨域询问的请求,直接返回空处理器new HandlerMethod(new EmptyHandler(), ClassUtils.getMethod(EmptyHandler.class, "handle"));
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
//排序后,前2个是相等的,报错
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
throw new IllegalStateException( "Ambiguous handler methods mapped ");
}
}
//缓存以下,封装了T mapping和HandlerMethod
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
//处理最终选择的T mapping,进行一些额外处理
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
//return null;
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
//子类实现getMatchingMapping,返回的是最终完整的T match(和mapping是同一个类型)
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
RequestMappingInfoHandlerMapping
概述
* 主要定义个泛型T mapping的类型为RequestMappingInfo,也是抽象类当时没有暴露抽象方法
* 实现了AbstractHandlerMethodMappin的一些重写方法,例如
- 基于基于RequestMappingInfo实现了getMatchingMapping()和getMappingComparator()方法
- 重写handleMatch()对匹配的映射对象进行后续处理,比如处理路径参数和矩阵参数的解析、producibleMediaTypes属性存于request的属性中
- 重写handleNoMatch()没有找到对应的映射对象调用抽象方法,找到进一步匹配不符合的原因,作为异常提示,抛出异常给用户
例如url虽然匹配,当时Contentype,即Consumes不匹配,就会作为异常的提示
RequestMappingInfo映射条件信息

* 表示的是一个处理器方法对应的映射信息,客户请求只有匹配了这些映射信息,才能由对应的处理器处理
* RequestMappingInfo和XXXRequestCondition实现了同一个接口
RequestMappingInfo是基于底层的多个XXXRequestCondition对象实现了RequestCondition接口
RequestMappingInfo
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
@Nullable
private final String name;
private final PatternsRequestCondition patternsCondition;
private final RequestMethodsRequestCondition methodsCondition;
private final ParamsRequestCondition paramsCondition;
private final HeadersRequestCondition headersCondition;
private final ConsumesRequestCondition consumesCondition;
private final ProducesRequestCondition producesCondition;
private final RequestConditionHolder customConditionHolder;
public RequestMappingInfo(@Nullable String name, @Nullable PatternsRequestCondition patterns,
@Nullable RequestMethodsRequestCondition methods, @Nullable ParamsRequestCondition params,
@Nullable HeadersRequestCondition headers, @Nullable ConsumesRequestCondition consumes,
@Nullable ProducesRequestCondition produces, @Nullable RequestCondition<?> custom) {
this.name = (StringUtils.hasText(name) ? name : null);
this.patternsCondition = (patterns != null ? patterns : new PatternsRequestCondition());
this.methodsCondition = (methods != null ? methods : new RequestMethodsRequestCondition());
this.paramsCondition = (params != null ? params : new ParamsRequestCondition());
this.headersCondition = (headers != null ? headers : new HeadersRequestCondition());
this.consumesCondition = (consumes != null ? consumes : new ConsumesRequestCondition());
this.producesCondition = (produces != null ? produces : new ProducesRequestCondition());
this.customConditionHolder = new RequestConditionHolder(custom);
}
....
}
XXXRequestCondition映射条件
概述
* 使用了RequestCondition类型类封装各种条件
- PatternsRequestCondition:url路径条件,包括模式匹配
- RequestMethodsRequestCondition:指定请求方法匹配条件,可多个
- ParamsRequestCondition:指定请求参数匹配条件,可多个
- HeadersRequestCondition :指定请求头匹配条件,可多个
- ConsumesRequestCondition:指定请求头Content-Type匹配条件,可多个
- ProducesRequestCondition:指定请求头Accept匹配条件,可多个
- RequestConditionHolder:用户自定义的匹配条件,可多个,底层为如上的RequestCondition
* 每一个XXXRequestCondition实现类,也实现了RequestMappingInfo接口方法
- 其底层包含一个带匹配的变量,表示条件,如果没有此变量只为空
例如HeadersRequestCondition(代码见下)中的private final Set<HeaderExpression> expressions;成员,”token=xxxx”就会封装为HeaderExpression加入expressions
- 实现getMatchingCondition(HttpServletRequest request)方法:遍历每一个expressions,调用match(request),只有一个不匹配就返回null
for (HeaderExpression expression : this.expressions) {
if (!expression.match(request)) {
return null;
}
}
- 实现compareTo方法:由自己的顺序比较规则
public final class HeadersRequestCondition extends AbstractRequestCondition<HeadersRequestCondition> {
private static final HeadersRequestCondition PRE_FLIGHT_MATCH = new HeadersRequestCondition();
private final Set<HeaderExpression> expressions;
@Override
@Nullable
public HeadersRequestCondition getMatchingCondition(HttpServletRequest request) {
if (CorsUtils.isPreFlightRequest(request)) {
return PRE_FLIGHT_MATCH;
}
for (HeaderExpression expression : this.expressions) {
if (!expression.match(request)) {
return null;
}
}
return this;
}
@Override
public int compareTo(HeadersRequestCondition other, HttpServletRequest request) {
int result = other.expressions.size() - this.expressions.size();
if (result != 0) {
return result;
}
return (int) (getValueMatchCount(other.expressions) - getValueMatchCount(this.expressions));
}
...
}
PatternsRequestCondition路径URL匹配条件
* 在RequestMappingHandlerMapping实现类中
- 实现了抽象方法getMappingForMethod方法,会去解析每一个@RequestMapping注解,最终形成RequestMappingInfo
- 其中.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))方法就是创建此RequestMappingInfo的PatternsRequestCondition属性
①收集到@RequestMapping注解上path或者value上指定的路径数组,路径是可能是模式路径
②使用EmbeddedValueResolver解析路径上的${}占位符
例如 @RequestMapping(value = "restful/${server.port}", method = RequestMethod.GET)
会解析为 @RequestMapping(value = "restful/8080", method = RequestMethod.GET)
//RequestMappingInfoHandlerMapping
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
RequestMappingInfo.Builder builder = RequestMappingInfo
//使用this.embeddedValueResolver.resolveStringValue(patterns[i]);解析所有模式串
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
return builder.options(this.config).build();
}
//把必要信息封装到PatternsRequestCondition
PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
this.options.getFileExtensions());
* Spring MVC提供如下几种模式匹配的url
- /resources/ima?e.png :匹配单路径段中的一个字符
- /resources/*.png :匹配单路径段中的零个或多个字符
- /resources/** :匹配多个路径段
- /projects/{project}/versions :匹配单路径段并将其【捕获为变量】
- /projects/{project:[a-z]+}/versions :使用正则表达式匹配并【捕获变量】
注:? 表示请求单个字符; * 零个或者多个字符; ** 表示匹配多个字段
* 匹配方式优先级,从上往下,优先级减少
①全路径匹配,例如:配置路由/a/b/c
②带有{}路径的匹配,例如:/a/{b}/c
③正则匹配,例如:/a/{regex:\d{3}}/c
④带有路径的匹配,例如:/a/b/
⑤带有**路径的匹配,例如:/a/b/**
重写getMatchingMapping和getMappingComparator
基于RequestMappingInfo 的getMatchingCondition和compareTo方法实现
//RequestMappingInfoHandlerMapping
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
//RequestMappingInfoHandlerMapping
@Override
protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
return (info1, info2) -> info1.compareTo(info2, request);
}
* RequestMappingInfo的getMatchingCondition和compareTo方法都是基于底层多个XXXRequestCondition实现的
在之前的步骤,匹配到多个结果时List<Match> ,使用此方法进行排序
* getMatchingCondition方法
- 需要匹配所有的XXXRequestCondition条件,只有一个不匹配就返回null
- 需要匹配的条件:请求方法 -> 请求参数 -> 请求头 -> 请求ContenType -> 请求Accept -> 请求url -> 用户自定义
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
if (methods == null) {
return null;
}
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
if (params == null) {
return null;
}
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
if (headers == null) {
return null;
}
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
if (consumes == null) {
return null;
}
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (produces == null) {
return null;
}
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) {
return null;
}
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
* compareTo方法实现
- 比较两个mappingInfo,映射条件也是由顺序的,只有能比较出大小,就不必走下面的比较的
- 比较顺序为:HEAD方法特定 -> 请求路径 -> 请求参数 -> 请求头 -> 请求ContenType -> 请求Accept -> 请求方法 -> 用户自定义
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
int result;
// Automatic vs explicit HTTP HEAD mapping
if (HttpMethod.HEAD.matches(request.getMethod())) {
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
}
result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
if (result != 0) {
return result;
}
result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
if (result != 0) {
return result;
}
result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
if (result != 0) {
return result;
}
result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
if (result != 0) {
return result;
}
result = this.producesCondition.compareTo(other.getProducesCondition(), request);
if (result != 0) {
return result;
}
// Implicit (no method) vs explicit HTTP method mappings
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
if (result != 0) {
return result;
}
return 0;
}
* RequestMappingHandlerMapping
基于@RequstMapping的最终实现类,主要实现抽象方法为getMappingForMethod获取一个Method的T mapping;
- getMappingForMethod()获取RequestMappingInfo
@RequestMapping(value = "/list", headers = "",consumes = "")
@ResponseBody
public List<User> list() {
//..
return List<User> users = userService.list();;
}
重写handleMatch()
* 对匹配的映射对象进行后续处理,比如处理路径参数decodedUriVariables和矩阵参数matrixVars的解析、producibleMediaTypes属性存于request的属性中
- request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
- request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
* 路径参数
@RequestMapping(value="/weibo/{username}/{id}",method=GET)
public String show(
@PathVariable("username") String username,
@PathVariableMap<String,String> map //可获取所有的路径参数
) { }
接收url: http://localhost:8080/weibo/kaka/123
则参数username为kaka,id为123
也支持正则表达式
@RequestMapping(value="/user/{id:正则表达式}")
如:@RequestMapping(value="/user/{id:\\d+}") 表示id的值只能为数字
* 矩阵参数
- Servlet或者Spring都会在映射到servlet或控制器之前会把路径参数去掉,对于@RequestMapping来说是不用匹配路径参数。
例如含路径参数的url:http://localhost:8080/hotel/43;floor=7;room=15/guest
实际上进行匹配的url为http://localhost:8080/hotel/43/guest
而举证参数;floor=7;room=15不参与比较,
- 可使用@MatriVariable获取路径参数
@RequestMapping("hotel/{hotelId/guest}") //只考虑路径段
public String spittles(
@RequestParam("hotelId") int hotelId,
@MatriVariable(pathVar="hotelId "value="floor") int floor,
//pathVar:路径参数的key,value是矩阵参数的key
@MatriVariable(pathVar="hotelId "value="room") int room
) { }
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
super.handleMatch(info, lookupPath, request);
String bestPattern;
Map<String, String> uriVariables;
Set<String> patterns = info.getPatternsCondition().getPatterns();
if (patterns.isEmpty()) {
bestPattern = lookupPath;
uriVariables = Collections.emptyMap();
}
else {
bestPattern = patterns.iterator().next();
uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
}
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
if (isMatrixVariableContentAvailable()) {
Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
}
Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
private boolean isMatrixVariableContentAvailable() {
return !getUrlPathHelper().shouldRemoveSemicolonContent();
}
private Map<String, MultiValueMap<String, String>> extractMatrixVariables(
HttpServletRequest request, Map<String, String> uriVariables) {
Map<String, MultiValueMap<String, String>> result = new LinkedHashMap<>();
uriVariables.forEach((uriVarKey, uriVarValue) -> {
int equalsIndex = uriVarValue.indexOf('=');
if (equalsIndex == -1) {
return;
}
int semicolonIndex = uriVarValue.indexOf(';');
if (semicolonIndex != -1 && semicolonIndex != 0) {
uriVariables.put(uriVarKey, uriVarValue.substring(0, semicolonIndex));
}
String matrixVariables;
if (semicolonIndex == -1 || semicolonIndex == 0 || equalsIndex < semicolonIndex) {
matrixVariables = uriVarValue;
}
else {
matrixVariables = uriVarValue.substring(semicolonIndex + 1);
}
MultiValueMap<String, String> vars = WebUtils.parseMatrixVariables(matrixVariables);
result.put(uriVarKey, getUrlPathHelper().decodeMatrixVariables(request, vars));
});
return result;
}
重写handleNoMatch()
* 找到进一步匹配不符合的原因,作为异常提示,抛出异常给用户
* 例如url虽然匹配,当时Contentype,即Consumes不匹配,就会作为异常的提示
@Override
protected HandlerMethod handleNoMatch(
Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException {
PartialMatchHelper helper = new PartialMatchHelper(infos, request);
if (helper.isEmpty()) {
return null;
}
if (helper.hasMethodsMismatch()) {
Set<String> methods = helper.getAllowedMethods();
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
HttpOptionsHandler handler = new HttpOptionsHandler(methods);
return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
}
throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods);
}
if (helper.hasConsumesMismatch()) {
Set<MediaType> mediaTypes = helper.getConsumableMediaTypes();
MediaType contentType = null;
if (StringUtils.hasLength(request.getContentType())) {
try {
contentType = MediaType.parseMediaType(request.getContentType());
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
}
throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes));
}
if (helper.hasProducesMismatch()) {
Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
}
if (helper.hasParamsMismatch()) {
List<String[]> conditions = helper.getParamConditions();
throw new UnsatisfiedServletRequestParameterException(conditions, request.getParameterMap());
}
return null;
}
RequestMappingHandlerMapping
概述
* 直接继承RequestMappingInfoHandlerMapping,功能等同于父类
主要实现了其父父类AbstractHandlerMethodMapping的抽象方法isHandler和getMappingForMethod
* 是基于@RequestMapping注解的解析
@RequestMapping/@Controller注解
* @RequestMapping注解内部的属性一一对应了RequestMappingInfo映射条件信息
* @Controller注解是一个标志作用的Component
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Mapping {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(annotation = Component.class)
String value() default "";
}
重写isHandler方法
只有存在@RequestMapping/@Controller注解的类都会作为方法处理器类候选,之后会进一步把类中方法解析成T mapping
建立好一些映射关系后会使用this.mappingRegistry.register(mapping, handler, method)先注册到mappingRegistry中
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
重写getMappingForMethod方法解析@RequestMapping注解
* 获取方法上RequestMapping注解的信息,使用createRequestMappingInfo方法创建方法维度上的RequestMappingInfo
RequestMappingInfo info = createRequestMappingInfo(method);
* 获取类上RequestMapping注解的信息,使用createRequestMappingInfo方创建类维度上的RequestMappingInfo
* 合并方法和类上的RequestMappingInfo,即类RequestMappingInfo.combine(方法RequestMappingInfo),各个部分合并的逻辑
- PatternsRequestCondition:结合 (类上 + 方法上) 两个的路径,
/* + /hotel -> /hotel ; "/*.*" + "/*.html" -> /*.html
/user + /user -> /usr/user ; /{foo} + /bar -> /{foo}/bar
/hotels/* + /booking -> /hotels/booking
/hotels/* + booking -> /hotels/booking
/hotels/** + /booking -> /hotels/**/booking
/hotels/** + booking -> /hotels/**/booking
- RequestMethodsRequestCondition/HeadersRequestCondition/ParamsRequestCondition:合并两者的指定的条件集合
- ConsumesRequestCondition/ProducesRequestCondition:优先取方法上的配置
* 前缀路径的处理
- 用户可以配置Map<String, Predicate<Class<?>>>
- 当Predicate此类为true时,取key作为字符串使用embeddedValueResolver.resolveStringValue(prefix)进行$占位符处理,会从环境配置中取值
在配置文件中定义动态动态改变的路径
mvc.url.perfix = /test
@RestController
@RequestMapping("${mvc.url.perfix}/sayhi")
public class HelloController {
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String sayHello(){}
}
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
//创建RequestMappingInfo时的BuilderConfiguration配置设置
private boolean useSuffixPatternMatch = true;
private boolean useRegisteredSuffixPatternMatch = false;
private boolean useTrailingSlashMatch = true;
private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
@Nullable
private StringValueResolver embeddedValueResolver;
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
@Override
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
super.afterPropertiesSet();
}
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//获取方法上RequestMapping注解的信息,创建方法维度上的RequestMappingInfo
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
//获取类上RequestMapping注解的信息,创建类维度上的RequestMappingInfo
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
//合并方法和类上的RequestMappingInfo
// - PatternsRequestCondition:结合类上和方法上两种的路径,
// /* + /hotel -> /hotel ; "/*.*" + "/*.html" -> /*.html
// /user + /user -> /usr/user ; /{foo} + /bar -> /{foo}/bar
// /hotels/* + /booking -> /hotels/booking
// /hotels/* + booking -> /hotels/booking
// /hotels/** + /booking -> /hotels/**/booking
// /hotels/** + booking -> /hotels/**/booking
// - RequestMethodsRequestCondition/HeadersRequestCondition/ParamsRequestCondition:合并两者的指定的条件集合
// - ConsumesRequestCondition/ProducesRequestCondition:优先取方法上的配置
info = typeInfo.combine(info);
}
//前缀路径的处理
//用户可以配置Map<String, Predicate<Class<?>>>
//当Predicate此类为true时,取key作为字符串使用embeddedValueResolver.resolveStringValue(prefix)进行$占位符处理
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
@Nullable
String getPathPrefix(Class<?> handlerType) {
for (Map.Entry<String, Predicate<Class<?>>> entry : this.pathPrefixes.entrySet()) {
if (entry.getValue().test(handlerType)) {
String prefix = entry.getKey();
if (this.embeddedValueResolver != null) {
prefix = this.embeddedValueResolver.resolveStringValue(prefix);
}
return prefix;
}
}
return null;
}
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
@Nullable
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
return null;
}
@Nullable
protected RequestCondition<?> getCustomMethodCondition(Method method) {
return null;
}
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
RequestMappingInfo.Builder builder = RequestMappingInfo
//使用this.embeddedValueResolver.resolveStringValue(patterns[i]);解析所有模式串
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
return builder.options(this.config).build();
}
@Override
public void registerMapping(RequestMappingInfo mapping, Object handler, Method method) {
super.registerMapping(mapping, handler, method);
updateConsumesCondition(mapping, method);
}
//获取@RequestBody注解的required赋值给ConsumesRequestCondition
private void updateConsumesCondition(RequestMappingInfo info, Method method) {
ConsumesRequestCondition condition = info.getConsumesCondition();
if (!condition.isEmpty()) {
for (Parameter parameter : method.getParameters()) {
MergedAnnotation<RequestBody> annot = MergedAnnotations.from(parameter).get(RequestBody.class);
if (annot.isPresent()) {
condition.setBodyRequired(annot.getBoolean("required"));
break;
}
}
}
}
....
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)