首页 > Python资料 博客日记
SpringMVC启动与请求处理流程解析
2025-01-02 20:00:09Python资料围观41次
目录
DispatcherServlet的doService()方法
SpringMVC的基本结构
1.MVC简介
以前的纯Servlet的处理方式:
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String type = req.getParameter(Constant.REQUEST_PARAMETER_TYPE);
if(type != null && !"".equals(type)){
if(Constant.SERVLET_TYPE_SAVE.equals(type)){
// 添加用户信息
try {
saveOrUpdateUser(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}else if(Constant.SERVLET_TYPE_UPDATE.equals(type)){
// 更新用户信息
}else if(Constant.SERVLET_TYPE_DELETE.equals(type)){
// 删除用户信息
deleteUserById(req, resp);
}else if(Constant.SERVLET_TYPE_QUEYR.equals(type)){
// 查询用户
queryUser(req, resp);
}else if(Constant.SERVLET_TYPE_QUERYBYID.equals(type)){
// 查询单条记录
String id = req.getParameter("id");
User user = userService.queryById(Integer.parseInt(id));
// 跳转到更新的页面同时保存数据到Request作用域中
req.setAttribute("user",user);
req.getRequestDispatcher("/user/userUpdate.jsp").forward(req,resp);
}else if(Constant.SERVLET_TYPE_CHECK.equals(type)){
// 验证账号是否存在
String userName = req.getParameter("userName");
String s = userService.checkUserName(userName);
resp.getWriter().println(s);
resp.flushBuffer();
}
}else{
// 查询用户信息
queryUser(req, resp);
}
}
为了尽量减少依赖Servlet API,提高程序的可测试性、可复用性而发展出了很多的框架技术:
-
Struts1
-
Struts2
-
SpringMVC
2.基本结构
然后我们来看看SpringMVC的基本结构
什么是Handler?
Handler表示请求处理器,在SpringMVC中有四种Handler:
- 实现了Controller接口的Bean对象
- 实现了HttpRequestHandler接口的Bean对象
- 添加了@RequestMapping注解的方法
- 一个HandlerFunction对象
比如实现了Controller接口的Bean对象:
@Component("/test")
public class MyController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("hello");
return new ModelAndView();
}
}
实现了HttpRequestHandler接口的Bean对象:
@Component("/test")
public class MyController implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("hello");
}
}
添加了@RequestMapping注解的方法:
@RequestMapping
@Component
public class MyController {
@Autowired
private MyService myService;
@RequestMapping(method = RequestMethod.GET, path = "/test")
@ResponseBody
public String test(String username) {
return "hello";
}
}
一个HandlerFunction对象(以下代码中有两个):
@Configuration
public class AppConfig {
@Bean
public RouterFunction<ServerResponse> person() {
return route()
.GET("/app/person", request -> ServerResponse.status(HttpStatus.OK).body("Hello GET"))
.POST("/app/person", request -> ServerResponse.status(HttpStatus.OK).body("Hello POST"))
.build();
}
}
什么是HandlerMapping?
HandlerMapping负责去寻找Handler,并且保存路径和Handler之间的映射关系。
因为有不同类型的Handler,所以在SpringMVC中会由不同的HandlerMapping来负责寻找Handler,比如:
- BeanNameUrlHandlerMapping:负责Controller接口和HttpRequestHandler接口
- RequestMappingHandlerMapping:负责@RequestMapping的方法
- RouterFunctionMapping:负责RouterFunction以及其中的HandlerFunction
BeanNameUrlHandlerMapping的寻找流程:
- 找出Spring容器中所有的beanName
- 判断beanName是不是以“/”开头
- 如果是,则把它当作一个Handler,并把beanName作为key,bean对象作为value存入handlerMap中
- handlerMap就是一个Map
RequestMappingHandlerMapping的寻找流程:
RequestMappingHandlerMapping是在初始化方法afterPropertiesSet()中寻找的
- 找出Spring容器中所有beanType
- 判断beanType是不是有@Controller注解,或者是不是有@RequestMapping注解
- 判断成功则继续找beanType中加了@RequestMapping的Method
- 并解析@RequestMapping中的内容,比如method、path,封装为一个RequestMappingInfo对象
- 把path作为key,RequestMappingInfo作为value存入pathLookup中
- 把RequestMappingInfo对象做为key,Method对象封装为HandlerMethod对象后作为value,存入registry中
pathLookup和registry就是一个Map,这样我们就能通过path找到对应的处理方法
RouterFunctionMapping的寻找流程会有些区别,但是大体是差不多的,相当于是一个path对应一个HandlerFunction。
各个HandlerMapping除开负责寻找Handler并记录映射关系之外,自然还需要根据请求路径找到对应的Handler,在源码中这三个HandlerMapping有一个共同的父类AbstractHandlerMapping
AbstractHandlerMapping实现了HandlerMapping接口,并实现了getHandler(HttpServletRequest request)方法。
AbstractHandlerMapping会负责调用子类的getHandlerInternal(HttpServletRequest request)方法从而找到请求对应的Handler,寻找Handler的源码实现在各个HandlerMapping子类中的getHandlerInternal()中,根据请求路径找到Handler的过程并不复杂,因为路径和Handler的映射关系已经存在Map中了。
比较困难的点在于,当DispatcherServlet接收到一个请求时,该利用哪个HandlerMapping来寻找Handler呢?
看源码:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
很简单,就是遍历,找到就返回,默认顺序为:
什么是HandlerAdapter?
找到了Handler之后,接下来就该去执行了,但是由于有不同种类的Handler,所以执行方式是不一样的,再来总结一下Handler的类型:
- 实现了Controller接口的Bean对象,执行的是Bean对象中的handleRequest()
- 实现了HttpRequestHandler接口的Bean对象,执行的是Bean对象中的handleRequest()
- 添加了@RequestMapping注解的方法,具体为一个HandlerMethod,执行的就是当前加了注解的方法
- 一个HandlerFunction对象,执行的是HandlerFunction对象中的handle()
所以,按逻辑来说,找到Handler之后,我们得判断它的类型,比如代码可能是这样的:
Object handler = mappedHandler.getHandler();
if (handler instanceof Controller) {
((Controller)handler).handleRequest(request, response);
} else if (handler instanceof HttpRequestHandler) {
((HttpRequestHandler)handler).handleRequest(request, response);
} else if (handler instanceof HandlerMethod) {
((HandlerMethod)handler).getMethod().invoke(...);
} else if (handler instanceof HandlerFunction) {
((HandlerFunction)handler).handle(...);
}
但是SpringMVC并不是这么写的,还是采用的适配模式,把不同种类的Handler适配成一个HandlerAdapter,后续再执行HandlerAdapter的handle()方法就能执行不同种类Hanlder对应的方法。
针对不同的Handler,会有不同的适配器:
- HttpRequestHandlerAdapter
- SimpleControllerHandlerAdapter
- RequestMappingHandlerAdapter
- HandlerFunctionAdapter
适配逻辑为:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
传入handler,遍历上面四个Adapter,谁支持就返回谁,比如判断的代码依次为:
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
public boolean supports(Object handler) {
return handler instanceof HandlerFunction;
}
根据Handler适配出了对应的HandlerAdapter后,就执行具体HandlerAdapter对象的handle()方法了,比如:
HttpRequestHandlerAdapter的handle():
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
SimpleControllerHandlerAdapter的handle():
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
HandlerFunctionAdapter的handle():
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
serverResponse = handlerFunction.handle(serverRequest);
因为这三个接收的直接就是Requeset对象,不用SpringMVC做额外的解析,所以比较简单,比较复杂的是RequestMappingHandlerAdapter,它执行的是加了@RequestMapping的方法,而这种方法的写法可以是多种多样,SpringMVC需要根据方法的定义去解析Request对象,从请求中获取出对应的数据然后传递给方法,并执行。
@RequestMapping方法参数解析
当SpringMVC接收到请求,并找到了对应的Method之后,就要执行该方法了,不过在执行之前需要根据方法定义的参数信息,从请求中获取出对应的数据,然后将数据传给方法并执行。
一个HttpServletRequest通常有:
- request parameter
- request attribute
- request session
- reqeust header
- reqeust body
比如如下几个方法:
public String test(String username) {
return "123";
}
表示要从request parameter中获取key为username的value
public String test(@RequestParam("uname") String username) {
return "123";
}
表示要从request parameter中获取key为uname的value
public String test(@RequestAttribute String username) {
return "123";
}
表示要从request attribute中获取key为username的value
public String test(@SessionAttribute String username) {
return "123";
}
表示要从request session中获取key为username的value
public String test(@RequestHeader String username) {
return "123";
}
表示要从request header中获取key为username的value
public String test(@RequestBody String username) {
return "123";
}
表示获取整个请求体
所以,我们发现SpringMVC要去解析方法参数,看该参数到底是要获取请求中的哪些信息。
而这个过程,源码中是通过HandlerMethodArgumentResolver来实现的,比如:
- RequestParamMethodArgumentResolver:负责处理@RequestParam
- RequestHeaderMethodArgumentResolver:负责处理@RequestHeader
- SessionAttributeMethodArgumentResolver:负责处理@SessionAttribute
- RequestAttributeMethodArgumentResolver:负责处理@RequestAttribute
- RequestResponseBodyMethodProcessor:负责处理@RequestBody
- 还有很多其他的...
而在判断某个参数该由哪个HandlerMethodArgumentResolver处理时,也是很粗暴:
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
就是遍历所有的HandlerMethodArgumentResolver,哪个能支持处理当前这个参数就由哪个处理。
DispatcherServlet的init()方法
init()做了两件事情
-
完成了IOC的初始化
-
完成了SpringMVC核心组件的初始化
在这个方法里面主要会配置spring容器的父子容器,并且还会执行initStrategies()初始化映射器和适配器分别对应initHandlerMappings()和initHandlerAdapters()这两个方法,最后启动spring容器。
在这两个方法里都会先检查程序员自己有没有定义HandlerMapping.class或HandlerAdapter.class类型的bean,如果没有的话就会走springmvc的默认策略getDefaultStrategies()去DispatcherServlet.properties文件中加载Bean,默认HandlerMapping有3个Bean,HandlerAdapter有4个Bean。
DispatcherServlet的doService()方法
tomcat会处理请求,然后调用service()方法,service方法调用DispatcherServlet的doService()方法,doService()调用doDispatch(),也就是一套模板方法。
service方法是在用户请求到来的时候触发的。也就是具体处理请求的方法。我们来看下,直接进入到doDispatch方法中
核心流程如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 检查是否是multipart请求
processedRequest = checkMultipart(request);
// 进行映射 找到对应的handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
//404
noHandlerFound(processedRequest, response);
return;
}
// 找到最合适的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 前置拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 返回false就不进行后续处理了
return;
}
// 真正执行handle
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//后置拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
SpringBoot整合SpringMVC
然后我们来看下载SpringBoot项目中是怎么自动装配SpringMVC框架的,首先我们找到对应的配置类
同时我们也需要关注下这个配置类
在这个配置类中注入的HandlerMapping和HandlerAdapter的具体实现类
同时也注入了DispatcherServlet
SpringMVC执行流程图
标签:
相关文章
最新发布
- 华为OD机试E卷 --通过软盘拷贝文件--24年OD统一考试(Java & JS & Python & C & C++)
- 宝塔部署-python项目
- Python—selenium —xpath定位方法详解
- 【Python】Python零基础100题测试(附答案)
- Python 程序打包成 EXE 文件及相关操作详解
- 华为OD机试E卷 --最多获得的短信条数--24年OD统一考试(Java & JS & Python & C & C++)
- 一步步理解 Python 异步生成器(AsyncGenerator)——从入门到实践
- 高频 Python 面试题解析(附代码解释)
- Python 潮流周刊#84:2024 年 Python 的最佳实践(摘要)
- 数据库应用课程设计:航班管理及售票系统(SQL Server+Python)
点击排行
- 版本匹配指南:Numpy版本和Python版本的对应关系
- 版本匹配指南:PyTorch版本、torchvision 版本和Python版本的对应关系
- Python 可视化 web 神器:streamlit、Gradio、dash、nicegui;低代码 Python Web 框架:PyWebIO
- 相关性分析——Pearson相关系数+热力图(附data和Python完整代码)
- Anaconda版本和Python版本对应关系(持续更新...)
- Python与PyTorch的版本对应
- Windows上安装 Python 环境并配置环境变量 (超详细教程)
- Python pyinstaller打包exe最完整教程