欢迎光临
我们一直在努力

一个 @RequestMapping 就能接收请求?Spring MVC 是怎么做到的

一个 @RequestMapping 就能接收请求?Spring MVC 是怎么做到的

上一篇我们讲了:

SpringBoot 怎么知道该创建哪些 Bean?

SpringBoot 启动时会扫描 Bean,注册到 Spring 容器中。

但新的问题来了。

我们平时写接口时,只需要一个注解:

@RestController
public class UserController {

@GetMapping("/user")
public String user() {
return "ok";
}
}

浏览器访问:

http://localhost:8080/user

居然就能直接进入这个方法。

很多人天天写 SpringMVC,却很少思考:

  • 请求是谁接收的?
  • URL 是什么时候和方法绑定的?
  • Spring 为什么知道调用哪个 Controller?
  • 这个映射关系存在哪里?

今天我们就把 SpringMVC 最核心的一条链路彻底搞明白。

先思考一个问题

如果让你自己实现 MVC。

你会怎么做?

最简单的方法可能是:

Map<String, Method> mapping = new HashMap<>();

mapping.put("/user", UserController.class.getMethod("user"));
mapping.put("/order", OrderController.class.getMethod("order"));

请求来了:

Method method = mapping.get("/user");
method.invoke(controller);

是不是就能实现?

SpringMVC 的核心思想其实也差不多。

只是做得更复杂、更完善。

Spring 启动时干了什么

SpringBoot 启动完成后。

SpringMVC 会扫描所有 Controller。

例如:

@RestController
public class UserController {

@GetMapping("/user")
public String user() {
return "ok";
}
}

扫描到后会解析:

@GetMapping("/user")

然后生成映射关系:

/user

UserController.user()

最终保存到一个非常重要的组件中:

MappingRegistry

源码中大概是这样的:

Map<RequestMappingInfo, MappingRegistration<?>> registry;

里面保存了所有 URL 和方法的对应关系。

可以理解成:

/user

UserController.user()

/order

OrderController.order()

/login

LoginController.login()

所以启动的时候。

Spring 其实已经把路由表提前建立好了。

请求来了以后发生什么

浏览器发送请求:

GET /user

首先到达:

DispatcherServlet

它是 SpringMVC 的核心入口。

所有请求都会先经过它。

流程大概如下:

浏览器

DispatcherServlet

此时 DispatcherServlet 并不知道该调用哪个 Controller。

于是它会去找:

HandlerMapping

HandlerMapping 在干什么

HandlerMapping 的职责非常简单:

根据请求路径找到对应的方法。

例如:

请求路径:

/user

它会去 MappingRegistry 中查找:

/user

UserController.user()

找到后返回一个对象:

HandlerMethod

HandlerMethod 是什么

很多同学 Debug 时都见过:

HandlerMethod

里面其实保存了两个核心信息:

bean
method

例如:

UserController

user()

可以理解成:

new HandlerMethod(
userController,
userMethod
)

也就是说:

URL

HandlerMethod

Controller方法

Spring 已经知道该执行谁了。

找到方法为什么不直接执行

很多人看到这里会疑惑。

既然已经找到方法了。

为什么不直接:

method.invoke(...)

还要多一个组件?

因为 SpringMVC 还要解决很多问题:

  • 参数绑定
  • 文件上传
  • JSON转换
  • 返回值处理
  • 异常处理

所以 Spring 又引入了:

HandlerAdapter

执行流程变成:

DispatcherServlet

HandlerMapping

HandlerMethod

HandlerAdapter

执行方法

真正调用 Controller 的其实是 HandlerAdapter。

Debug 一次最容易看懂

在 Controller 打断点:

@GetMapping("/user")
public String user() {
return "ok";
}

访问接口。

调用栈大概会看到:

DispatcherServlet.doDispatch()

RequestMappingHandlerMapping

HandlerMethod

RequestMappingHandlerAdapter

InvocableHandlerMethod

UserController.user()

看到这里。

SpringMVC 的核心流程就彻底清楚了。

整个流程总结

启动阶段:

扫描 Controller

解析 @RequestMapping

生成 RequestMappingInfo

注册到 MappingRegistry

请求阶段:

请求到达

DispatcherServlet

HandlerMapping

找到 HandlerMethod

HandlerAdapter

执行 Controller 方法

返回结果

所以:

@RequestMapping 并不是收到请求时才解析。

而是在 Spring 启动时就已经解析完成,并建立好了 URL 和方法的映射关系。

请求来了以后,本质上只是查表找到对应的方法再执行而已。

最后一个问题

既然 Spring 已经找到 Controller 方法了。

那么下面这个代码:

@GetMapping("/user")
public User user(
@RequestParam Long id,
HttpServletRequest request) {
...
}

参数是谁赋值的?

@RequestParam 为什么能自动接收请求参数?

HttpServletRequest 又是谁传进来的?

下一篇我们继续聊:

DispatcherServlet 为什么是 SpringMVC 的大脑?

赞(0)
未经允许不得转载:171主机测评 » 一个 @RequestMapping 就能接收请求?Spring MVC 是怎么做到的
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址