SpringMVC之RequestMappingHandlerMapping

Posted 木叶之荣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC之RequestMappingHandlerMapping相关的知识,希望对你有一定的参考价值。

我们在这篇文章中接着上一篇文章的内容接着说,我们在上一篇文章中说到在doWithMethods这个方法中会调用MethodCallback的doWith方法进行方法处理,那么我们在RequestMappingHandlerMapping中MethodCallback的实现是什么呢?

			ReflectionUtils.doWithMethods(currentHandlerType, 
			//下面的这一段代码就是MethodCallback的实现
			method -> 
				//获取真正的方法 因为这个方法可能会是接口的default
				Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
				//这个地方又是一个回调的方法 真正的解析RequestHandlerMapping的地方
				T result = metadataLookup.inspect(specificMethod);
				if (result != null) 
				//这里是寻找桥接方法,如果没有桥接方法,则返回本方法
					Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
					if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) 
						//这里是放到methodMap中,key是Method 在Method中重写里equals和hashcode方法
						methodMap.put(specificMethod, result);
					
				
			
			, ReflectionUtils.USER_DECLARED_METHODS);

metadataLookup.inspect调用的是getMappingForMethod这个方法。

	Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> 
						try 
							//	metadataLookup.inspect调用的是这个方法
							return getMappingForMethod(method, userType);
						
						catch (Throwable ex) 
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						
					);

我们把上面的过程做一个简单的总结:看着前面的代码很复杂,其实做的事情很简单。我们首先先查找到带有Controller或者RequestMapping注解的类,然后遍历这个类和它的父类的所有方法以及它们所实现的接口中的default方法,然后把得到的方法调用getMappingForMethod方法进行解析。

	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) 
		//根据Method获取RequestMappingInfo
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) 
			//根据Class获取RequestMappingInfo
			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;
	
	private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) 
		//这里是根据Method获取RequestMapping注解的信息
		//这里卖个关子,如果我们的Controller的method上面没有直接写RequestMapping相关的注解
		//而是在他的接口上写了RequestMapping相关的信息,如我们使用SpringCloud进行为服务开发
		//那么Controller的method是怎么获取到RequestMapping相关的信息的呢???
		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
		//这里的RequestCondition是用来给实现着自定义实现用的 基本上不会用到
		RequestCondition<?> condition = (element instanceof Class ?
				getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
		return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
	

createRequestMappingInfo方法

	protected RequestMappingInfo createRequestMappingInfo(
			RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) 
		//这里用到了一个典型的建造者模式
		RequestMappingInfo.Builder builder = RequestMappingInfo
				//这里对路径进行解析,在path中是支持SpEL表达式的,
				//RequestMappingHandlerMapping实现了EmbeddedValueResolverAware这个接口
				.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 RequestMappingInfo build() 
		//内容协商管理器 有什么用呢? 如果我们的请求是.json结尾的则返回json数据
		//如果我们的请求是.xml结尾的则返回xml格式的数据。
			ContentNegotiationManager manager = this.options.getContentNegotiationManager();
			//构建了一个PatternsRequestCondition对象
			PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
					this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
					this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
					this.options.getFileExtensions());
			//从这里我们可以看到RequestMappingHandlerMapping中的每一个属性都被封装成了一个对应的处理类,典型的单一职责的体现
			return new RequestMappingInfo(this.mappingName, patternsCondition,
					new RequestMethodsRequestCondition(this.methods),
					new ParamsRequestCondition(this.params),
					new HeadersRequestCondition(this.headers),
					new ConsumesRequestCondition(this.consumes, this.headers),
					new ProducesRequestCondition(this.produces, this.headers, manager),
					this.customCondition);
		

我们为上面的代码写个小总结,我们在method上添加的RequestMapping的信息都被解析成了RequestMappingInfo这个类,并且每一个属性值都对应一个处理类。他们都实现了RequestCondition这个接口。我们的RequestMapping注解可以写在方法上,也可以写在Controller类上,如果我们同时在Controller类和method上都添加类RequestMapping注解,那么Spring MVC是怎么处理的呢?

以上是关于SpringMVC之RequestMappingHandlerMapping的主要内容,如果未能解决你的问题,请参考以下文章

40 个 SpringBoot 常用注解:让生产力爆表!

springmvc学习笔记-springmvc整合mybatis之controller

springmvc学习笔记-springmvc整合mybatis之service

springmvc学习笔记(11)-springmvc注解开发之简单参数绑定

springmvc学习笔记(13)-springmvc注解开发之集合类型参数绑定

SpringMVC学习系列 之 初识SpringMVC