springboot源码分析: 请求方式+请求映射原理+获取参数原理

注解相关

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

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

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

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

MVC处理流程
在这里插入图片描述

一:springmvc请求方式的选择

如果是用的表单提交, 不能支持delete那些方式, 需要开启一下过滤器。但是现在前后端分离, 直接就可以发delete那些方式。
WebMvcAutoConfiguration中, 配置了一个命令隐藏方法过滤器
在这里插入图片描述
一直进去可以看见这里默认的参数是_method
在这里插入图片描述
回到WebMvcAutoConfiguration中, 我们看见, 如果默认过滤器是false, 未开启, 我们去配置文件中开启
在这里插入图片描述

配置
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,进行了包装

在这里插入图片描述
这里可以看见mvc支持这些请求
在这里插入图片描述
特别注意: 因为表单只能发post和get, 所有才会进入if里面, 如果是客户端工具, 直接发送delete就不用进去, 直接就用的requestToUse

自定义_method
在这里插入图片描述
这里给了我们方法, 我们只需要修改一下, 给他注入到容器里面就可以了。

@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中, 我们找到了
在这里插入图片描述

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

这里的doService是一个抽象方法, 并没有实现, 所有一定在子类里面实现。
在中央处理器DispatcherServlet里面, doService被实现了
在这里插入图片描述
经过一系列的初始化过程, doService里面 调用了doDispatch(request, response);每个请求进来都会调用doDispatch方法, 这才是我们要研究的方法。

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

请求过来以后, 会把请求包装一下,然后判断是不是文件上传multipartRequestParsed请求, 默认是false.
在这里插入图片描述

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

再往下走:
在这里插入图片描述
走到核心这里, 这就是找到映射地址的关键: 这里调用本类的一个getHandler方法, 在这个方法里面找到映射地址
进入这个方法, 我们看见这里是一个for循环的方式来找寻找。
在这里插入图片描述
这里的handermappers一共有5个, 我们需要的就是第一个
在这里插入图片描述
在这里面我们找到了所有的映射地址, 包括系统给我们写的, 当然, 也有这次要找的。
在这里插入图片描述
在这里面就可以找到,这个映射地址的方法叫getTest, 在helloController这个类里面
在这里插入图片描述
在for循环的Mapping.getHander()方法里面, 调用了一个方法,在这个方法里面找到最终的结果
在这里插入图片描述
我们一直往下运行, 找到RequestMappingInfoHandlerMapping类, 这里面有一个方法处理了request请求
在这里插入图片描述
进入这个方法, 我们看见这里面找到了路径
在这里插入图片描述

但是路径可能相同, 但是请求方式不同,这里把路径和requst再进行处理
在这里插入图片描述
我们发现这里相同的路径找到了两个请求
在这里插入图片描述
然后又把这两个结果放到一个方法里面去找最佳匹配
在这里插入图片描述
经过这里的循环, 就找到了最终的结果
在这里插入图片描述

三:参数解析原理

走到了这一步
在这里插入图片描述

接着上面的, 拿到具体的方法以后, 我们就要去到一个适配器
在这里插入图片描述
进去后发现, 这里有4个处理器适配器

  1. 支持RequestMapping,(我们的controller都是这个)
  2. 支持函数式编程
    在这里插入图片描述
    这里返回的就是RequestMappingHandlerAdapter
    返回以后(中央处理器)我们继续往下走, 我们会走到一个核心方法,Actually invoke the handler.实际调用处理程序
    在这里插入图片描述
    进入这个方法, 我们会走到RequestMappingHandlerAdapter, 这里做一些真正的处理, 在这里,执行目标方法
    在这里插入图片描述
    在这个方法里面有一个参数解析器
    在这里插入图片描述
    参数解析器里面有这些,这里确定我们将要执行的目标方法的每一个参数的值是什么。我们平时写的@RequestParam,就在这里第一个处理的。
    在这里插入图片描述
    这里还有返回值处理器
    在这里插入图片描述

继续往下,处理请求的一个方法就在这里,
在这里插入图片描述
进去这个方法里面, 就会来到我们的controller
在这里插入图片描述
真真真执行目标方法ServletInvocableHandlerMethod类里面。
在这里插入图片描述
这里面再进去, 会到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;
	}

总结:
请求进来以后会先去走过滤器拿到它是哪一种请求方式,
在这里插入图片描述

拿到以后走service方法去走一个请求转发,通过if else找到请求方式, 去调用doGet, doPost等方法。
在这里插入图片描述
在这里插入图片描述
然后就是上面的去寻找具体方法。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Archie_java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值