• 源码解析SpringMVC之RequestMapping注解原理


    1、启动初始化

    核心:得到应用上下文中存在的全部bean后依次遍历,分析每一个目标handler & 目标方法存在的注解@RequestMapping,将其相关属性封装为实例RequestMappingInfo。最终将 uri & handler 之间的映射关系维护在类AbstractHandlerMethodMapping中的内部类RequestMappingInfo中。

    利用RequestMappingHandlerMappingInitializingBean接口特性来完成请求 uri & handler 之间的映射关系。具体详情参考其父类AbstractHandlerMethodMapping实现其功能,如下:

    public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    	//内部类
    	private final MappingRegistry mappingRegistry = new MappingRegistry();
    	
    	protected void initHandlerMethods() {
    		for (String beanName : getCandidateBeanNames()) {//从应用上下文中获取全部的bean
    			processCandidateBean(beanName);
    		}
    	}
    	
    	protected void processCandidateBean(String beanName) {
    		Class<?> beanType = obtainApplicationContext().getType(beanName);
    		// 判断是否为Handler的条件为:是否存在注解Controller 或者 RequestMapping
    		if (beanType != null && isHandler(beanType)) {
    			detectHandlerMethods(beanName);
    		}
    	}
    		
    	protected void detectHandlerMethods(Object handler) {//handler为String类型的beanName
    		Class<?> handlerType = (handler instanceof String ?
    				obtainApplicationContext().getType((String) handler) : handler.getClass());
    		if (handlerType != null) {
    			Class<?> userType = ClassUtils.getUserClass(handlerType);
    			// 集合methods其key:反射中Method类。value:RequestMappingInfo
    			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
    					(MethodIntrospector.MetadataLookup<T>) method -> {
    						// 调用具体的实现类,例如RequestMappingHandlerMapping
    						return getMappingForMethod(method, userType);
    					});
    			methods.forEach((method, mapping) -> {
    				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
    				registerHandlerMethod(handler, invocableMethod, mapping);
    			});
    		}
    	}
    	
    	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    		this.mappingRegistry.register(mapping, handler, method);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    1.1、RequestMappingHandlerMapping

    解析目标类 & 目标方法之@RequestMapping注解相关属性。

    public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping{
    	@Override
    	@Nullable
    	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    		//将目标方法存在的@RequestMapping注解相关属性封装为RequestMappingInfo
    		RequestMappingInfo info = createRequestMappingInfo(method);
    		if (info != null) {
    			//如果目标handler也存在@RequestMapping注解,则也封装为RequestMappingInfo
    			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
    			if (typeInfo != null) {
    				// 将目标方法@RequestMapping相关属性与目标handler之@RequestMapping相关属性合并起来
    				info = typeInfo.combine(info);
    			}
    			String prefix = getPathPrefix(handlerType);//获取目标handler之url前缀
    			if (prefix != null) {
    				info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
    			}
    		}
    		return info;
    	}
    	
    	private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    		return (requestMapping != null ? createRequestMappingInfo(requestMapping, null) : null);
    	}
    	
    	protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping,RequestCondition condition) {
    		// 获取某个具体目标方法其@RequestMapping注解相关属性,最终封装为RequestMappingInfo
    		RequestMappingInfo.Builder builder = RequestMappingInfo
    				.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
    				.methods(requestMapping.method())
    				.params(requestMapping.params())
    				.headers(requestMapping.headers())
    				.consumes(requestMapping.consumes())
    				.produces(requestMapping.produces())
    				.mappingName(requestMapping.name());
    		return builder.options(this.config).build();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    1.2.MappingRegistry

    public abstract class AbstractHandlerMethodMapping<T> {
    
    	class MappingRegistry {
    		private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
    		//集合mappingLookup之key:RequestMappingInfo。value:HandlerMethod
    		private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
    		private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
    		
    		public void register(T mapping, Object handler, Method method) {
    			...
    			// 目标类的目标方法最终封装为HandlerMethod  handler实为目标bean的beanName
    			HandlerMethod handlerMethod = createHandlerMethod(handler, method);
    			this.mappingLookup.put(mapping, handlerMethod);
    			List<String> directUrls = getDirectUrls(mapping);
    			for (String url : directUrls) {
    				this.urlLookup.add(url, mapping);
    			}
    			...
    			this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2、处理请求

    SpringMVC利用请求url获取目标handler。
    在这里插入图片描述

    public class DispatcherServlet extends FrameworkServlet {
    	@Nullable
    	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    		if (this.handlerMappings != null) {
    			for (HandlerMapping mapping : this.handlerMappings) {
    				// 围绕 RequestMappingHandlerMapping 展开
    				HandlerExecutionChain handler = mapping.getHandler(request);
    				if (handler != null) {
    					return handler;
    				}
    			}
    		}
    		return null;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    public abstract class AbstractHandlerMethodMapping<T>  implements InitializingBean {
    	private final MappingRegistry mappingRegistry = new MappingRegistry();
    	@Override
    	protected HandlerMethod getHandlerInternal(HttpServletRequest request){
    		// 获取requestUri
    		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    		// 从 mappingRegistry 获取目标handler
    		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    	}
    	
    	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) {
    		List<Match> matches = new ArrayList<>();
    		// 利用 lookupPath 从MappingRegistry相关属性中获取目标handler之T
    		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    		// 通过章节1得知,返回的类为 RequestMappingInfo,即@RequestMapping注解的相关属性
    		if (directPathMatches != null) {
    			//分析请求
    			addMatchingMappings(directPathMatches, matches, request);
    		}
    		if (matches.isEmpty()) {
    			// No choice but to go through all mappings...
    			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    		}
    		// 如果局部matches中元素为空,说明请求头校验失败
    		if (!matches.isEmpty()) {
    			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
    			matches.sort(comparator);
    			Match bestMatch = matches.get(0);
    			...
    			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
    			handleMatch(bestMatch.mapping, lookupPath, request);
    			return bestMatch.handlerMethod;
    		}else {
    			//最终此处抛出相关的异常
    			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    请求头相关参数校验失败抛出的异常包括:HttpRequestMethodNotSupportedExceptionHttpMediaTypeNotSupportedExceptionHttpMediaTypeNotAcceptableExceptionUnsatisfiedServletRequestParameterException

    但是这些类型的异常好像不能被全局拦截器拦截处理。

    2.1.解析请求头相关属性

    核心就是校验请求request中相关属性跟RequestMappingInfo中属性是否匹配。

    public abstract class AbstractHandlerMethodMapping<T>  implements InitializingBean {
    
    	private void addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) {
    		for (T mapping : mappings) {
    			T match = getMatchingMapping(mapping, request);
    			if (match != null) {//正常请求校验都通过,最终返回重新包装后的RequestMappingInfo
    				matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping {
    	@Override
    	protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
    		return info.getMatchingCondition(request);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.1.1、RequestMappingInfo

    该类中的相关属性是对 注解@RequestMapping之字段属性的封装。

    public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
    	public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
    		//请求方式: 校验Method属性,即是否为post or get 等相对应的请求方式
    		RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
    		if (methods == null) {
    			return null;
    		}
    		//请求参数
    		ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
    		if (params == null) {
    			return null;
    		}
    		//consumer参数:请求头中contentType是否一致。
    		HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
    		if (headers == null) {
    			return null;
    		}
    		//producer参数:请求头中Accept是否一致
    		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());
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
  • 相关阅读:
    Android基础第三天 | 字节跳动第四届青训营笔记
    在vue3中通过vue-i18n实现国际化多语言切换
    集货运输优化:数学建模步骤,Python实现蚁群算法(解决最短路径问题), 蚁群算法解决旅行商问题(最优路径问题),节约里程算法
    Codeforces Round #803 (Div. 2)(A-D)
    抽象类和接口
    自动化测试用例的编写,只需要牢记7点,轻轻松松
    深入浅出 Django 异步编程
    docker compose使用教程(docker-compose教程)
    (DenseNet)Densely Connected Convolutional Networks--Gao Huang
    图解LeetCode——1224. 最大相等频率(难度:困难)
  • 原文地址:https://blog.csdn.net/qq_36851469/article/details/134074996