注解相关

@AliasFor:.在同个注解中为同一个功能定义两个名称不一样的属性,那么这两个属性彼此互为别名
@RequestMapping注解里面的代码

@AliasFor("path")
	String[] value() default {};
	@AliasFor("value")
	String[] path() default {};

@GetMapping @PostMapping @DeleteMapping等都是@RequestMapping修饰

@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {}

MVC处理流程

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器

一:springmvc请求方式的选择

如果是用的表单提交, 不能支持delete那些方式, 需要开启一下过滤器。但是现在前后端分离, 直接就可以发delete那些方式。

WebMvcAutoConfiguration中, 配置了一个命令隐藏方法过滤器

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_02


一直进去可以看见这里默认的参数是_method

spring boot请求sentinel处理流程源码 springboot请求原理_java_03


回到WebMvcAutoConfiguration中, 我们看见, 如果默认过滤器是false, 未开启, 我们去配置文件中开启

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_04

配置
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true
前端
 <form action="/test" method="post">
        <input type="hidden" name="_method" value="DELETE">
        <input type="submit" value="提交">
</form>
后端
 	@ResponseBody
    @RequestMapping(value = "/test", method = RequestMethod.DELETE)
    public String getTest(){
        return "DELETE";
    }
结果就是成功请求到, 如果没有开启, 就会失败

现在我们来看一下请求过程

请求进来, 判断是不是post请求, 并且是否正常,如果是就进入if里面
进来后获取到this.methodParam里面的值, 点进去发现就是_method,
所以: 这里就是获取我们过程名字为_method的input标签里面的值
拿到以后会判断这个值是不是正常, 正常的话就变成大写,所有我们前端大写小写都可以
然后去判断ALLOWED_METHODS里面是不是包含这个值, 如果包含,就new一个HttpMethodRequestWrapper返回
HttpMethodRequestWrapper就是重写了ServletRequest,进行了包装

spring boot请求sentinel处理流程源码 springboot请求原理_ide_05


这里可以看见mvc支持这些请求

spring boot请求sentinel处理流程源码 springboot请求原理_ide_06


特别注意: 因为表单只能发post和get, 所有才会进入if里面, 如果是客户端工具, 直接发送delete就不用进去, 直接就用的requestToUse自定义_method

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_07


这里给了我们方法, 我们只需要修改一下, 给他注入到容器里面就可以了。

@Configuration(proxyBeanMethods = false)//@Configuration(proxyBeanMethods = false),提高Spring启动速度
public class WebConfig {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter1 = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter1.setMethodParam("_myMethod");
        return hiddenHttpMethodFilter1;
    }
}

二:请求映射原理

所有请求都会来到中央处理器DispatcherServlet, 我们去里面找到doGet和doPost,在FrameworkServlet中, 我们找到了

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_08

再doGet里面processRequest再调用他本类里面的doService方法处理rqquest和response

这里的doService是一个抽象方法, 并没有实现, 所有一定在子类里面实现。

在中央处理器DispatcherServlet里面, doService被实现了

spring boot请求sentinel处理流程源码 springboot请求原理_映射地址_09


经过一系列的初始化过程, doService里面 调用了doDispatch(request, response);每个请求进来都会调用doDispatch方法, 这才是我们要研究的方法。

综上所述:经过系列的辗转反侧, 我们终于找到了最终处理request, response的方法, 就是doDispatch, 我们继续研究doDispatch方法

请求过来以后, 会把请求包装一下,然后判断是不是文件上传multipartRequestParsed请求, 默认是false.

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_10

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
判断是不是异步, 是的话就使用一个异步管理器。

再往下走:

spring boot请求sentinel处理流程源码 springboot请求原理_映射地址_11


走到核心这里, 这就是找到映射地址的关键: 这里调用本类的一个getHandler方法, 在这个方法里面找到映射地址

进入这个方法, 我们看见这里是一个for循环的方式来找寻找。

spring boot请求sentinel处理流程源码 springboot请求原理_ide_12


这里的handermappers一共有5个, 我们需要的就是第一个

spring boot请求sentinel处理流程源码 springboot请求原理_java_13


在这里面我们找到了所有的映射地址, 包括系统给我们写的, 当然, 也有这次要找的。

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_14


在这里面就可以找到,这个映射地址的方法叫getTest, 在helloController这个类里面

spring boot请求sentinel处理流程源码 springboot请求原理_映射地址_15


在for循环的Mapping.getHander()方法里面, 调用了一个方法,在这个方法里面找到最终的结果

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_16


我们一直往下运行, 找到RequestMappingInfoHandlerMapping类, 这里面有一个方法处理了request请求

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_17


进入这个方法, 我们看见这里面找到了路径

spring boot请求sentinel处理流程源码 springboot请求原理_映射地址_18

但是路径可能相同, 但是请求方式不同,这里把路径和requst再进行处理

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_19


我们发现这里相同的路径找到了两个请求

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_20


然后又把这两个结果放到一个方法里面去找最佳匹配

spring boot请求sentinel处理流程源码 springboot请求原理_ide_21


经过这里的循环, 就找到了最终的结果

spring boot请求sentinel处理流程源码 springboot请求原理_java_22

三:参数解析原理

走到了这一步

spring boot请求sentinel处理流程源码 springboot请求原理_映射地址_23

接着上面的, 拿到具体的方法以后, 我们就要去到一个适配器

spring boot请求sentinel处理流程源码 springboot请求原理_ide_24


进去后发现, 这里有4个处理器适配器

  1. 支持RequestMapping,(我们的controller都是这个)
  2. 支持函数式编程

    这里返回的就是RequestMappingHandlerAdapter 返回以后(中央处理器)我们继续往下走, 我们会走到一个核心方法,Actually invoke the handler.实际调用处理程序

    进入这个方法, 我们会走到RequestMappingHandlerAdapter, 这里做一些真正的处理, 在这里,执行目标方法

    在这个方法里面有一个参数解析器

    参数解析器里面有这些,这里确定我们将要执行的目标方法的每一个参数的值是什么。我们平时写的@RequestParam,就在这里第一个处理的。

    这里还有返回值处理器

继续往下,处理请求的一个方法就在这里,

spring boot请求sentinel处理流程源码 springboot请求原理_ide_25


进去这个方法里面, 就会来到我们的controller

spring boot请求sentinel处理流程源码 springboot请求原理_映射地址_26


真真真执行目标方法ServletInvocableHandlerMethod类里面。

spring boot请求sentinel处理流程源码 springboot请求原理_映射地址_27


这里面再进去, 会到InvocableHandlerMethod, 这里面获取参数的值,代码如下

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}

		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				// Leave stack trace for later, exception may actually be resolved and handled...
				if (logger.isDebugEnabled()) {
					String exMsg = ex.getMessage();
					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, exMsg));
					}
				}
				throw ex;
			}
		}
		return args;
	}

总结:

请求进来以后会先去走过滤器拿到它是哪一种请求方式,

spring boot请求sentinel处理流程源码 springboot请求原理_java_28

拿到以后走service方法去走一个请求转发,通过if else找到请求方式, 去调用doGet, doPost等方法。

spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_29


spring boot请求sentinel处理流程源码 springboot请求原理_中央处理器_30


然后就是上面的去寻找具体方法。