SpringMVC启动与请求处理流程解析

目录

SpringMVC的基本结构

1.MVC简介

2.基本结构

什么是Handler?

什么是HandlerMapping?

什么是HandlerAdapter?

@RequestMapping方法参数解析

DispatcherServlet的init()方法

DispatcherServlet的doService()方法

SpringBoot整合SpringMVC

SpringMVC执行流程图


SpringMVC的基本结构

1.MVC简介

以前的纯Servlet的处理方式:

java 复制代码
@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:

  1. 实现了Controller接口的Bean对象
  2. 实现了HttpRequestHandler接口的Bean对象
  3. 添加了@RequestMapping注解的方法
  4. 一个HandlerFunction对象

比如实现了Controller接口的Bean对象:

java 复制代码
@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对象:

java 复制代码
@Component("/test")
public class MyController implements HttpRequestHandler {

	@Override
	public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("hello");
	}
}

添加了@RequestMapping注解的方法:

java 复制代码
@RequestMapping
@Component
public class MyController {

	@Autowired
	private MyService myService;

	@RequestMapping(method = RequestMethod.GET, path = "/test")
	@ResponseBody
	public String test(String username) {
		return "hello";
	}

}

一个HandlerFunction对象(以下代码中有两个):

java 复制代码
@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,比如:

  1. BeanNameUrlHandlerMapping:负责Controller接口和HttpRequestHandler接口
  2. RequestMappingHandlerMapping:负责@RequestMapping的方法
  3. RouterFunctionMapping:负责RouterFunction以及其中的HandlerFunction

BeanNameUrlHandlerMapping的寻找流程:

  1. 找出Spring容器中所有的beanName
  2. 判断beanName是不是以"/"开头
  3. 如果是,则把它当作一个Handler,并把beanName作为key,bean对象作为value存入handlerMap
  4. handlerMap就是一个Map

RequestMappingHandlerMapping的寻找流程:

RequestMappingHandlerMapping是在初始化方法afterPropertiesSet()中寻找的

  1. 找出Spring容器中所有beanType
  2. 判断beanType是不是有@Controller注解,或者是不是有@RequestMapping注解
  3. 判断成功则继续找beanType中加了@RequestMapping的Method
  4. 并解析@RequestMapping中的内容,比如method、path,封装为一个RequestMappingInfo对象
  5. 把path作为key,RequestMappingInfo作为value存入pathLookup
  6. 把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呢?

看源码:

java 复制代码
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的类型:

  1. 实现了Controller接口的Bean对象,执行的是Bean对象中的handleRequest()
  2. 实现了HttpRequestHandler接口的Bean对象,执行的是Bean对象中的handleRequest()
  3. 添加了@RequestMapping注解的方法,具体为一个HandlerMethod,执行的就是当前加了注解的方法
  4. 一个HandlerFunction对象,执行的是HandlerFunction对象中的handle()

所以,按逻辑来说,找到Handler之后,我们得判断它的类型,比如代码可能是这样的:

java 复制代码
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,会有不同的适配器:

  1. HttpRequestHandlerAdapter
  2. SimpleControllerHandlerAdapter
  3. RequestMappingHandlerAdapter
  4. HandlerFunctionAdapter

适配逻辑为:

java 复制代码
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,谁支持就返回谁,比如判断的代码依次为:

java 复制代码
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():

java 复制代码
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	((HttpRequestHandler) handler).handleRequest(request, response);
	

SimpleControllerHandlerAdapter的handle():

java 复制代码
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	return ((Controller) handler).handleRequest(request, response);
}

HandlerFunctionAdapter的handle():

java 复制代码
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
serverResponse = handlerFunction.handle(serverRequest);

因为这三个接收的直接就是Requeset对象,不用SpringMVC做额外的解析,所以比较简单,比较复杂的是RequestMappingHandlerAdapter,它执行的是加了@RequestMapping的方法,而这种方法的写法可以是多种多样,SpringMVC需要根据方法的定义去解析Request对象,从请求中获取出对应的数据然后传递给方法,并执行。

@RequestMapping方法参数解析

当SpringMVC接收到请求,并找到了对应的Method之后,就要执行该方法了,不过在执行之前需要根据方法定义的参数信息,从请求中获取出对应的数据,然后将数据传给方法并执行。

一个HttpServletRequest通常有:

  1. request parameter
  2. request attribute
  3. request session
  4. reqeust header
  5. reqeust body

比如如下几个方法:

java 复制代码
public String test(String username) {
    return "123";
}

表示要从request parameter中获取key为username的value

java 复制代码
public String test(@RequestParam("uname") String username) {
    return "123";
}

表示要从request parameter中获取key为uname的value

java 复制代码
public String test(@RequestAttribute String username) {
    return "123";
}

表示要从request attribute中获取key为username的value

java 复制代码
public String test(@SessionAttribute String username) {
    return "123";
}

表示要从request session中获取key为username的value

java 复制代码
public String test(@RequestHeader String username) {
    return "123";
}

表示要从request header中获取key为username的value

java 复制代码
public String test(@RequestBody String username) {
    return "123";
}

表示获取整个请求体

所以,我们发现SpringMVC要去解析方法参数,看该参数到底是要获取请求中的哪些信息。

而这个过程,源码中是通过HandlerMethodArgumentResolver来实现的,比如:

  1. RequestParamMethodArgumentResolver:负责处理@RequestParam
  2. RequestHeaderMethodArgumentResolver:负责处理@RequestHeader
  3. SessionAttributeMethodArgumentResolver:负责处理@SessionAttribute
  4. RequestAttributeMethodArgumentResolver:负责处理@RequestAttribute
  5. RequestResponseBodyMethodProcessor:负责处理@RequestBody
  6. 还有很多其他的...

而在判断某个参数该由哪个HandlerMethodArgumentResolver处理时,也是很粗暴:

java 复制代码
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()做了两件事情

  1. 完成了IOC的初始化

  2. 完成了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方法中

核心流程如下:

java 复制代码
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执行流程图

spring MVC

相关推荐
别惹CC1 小时前
Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
人工智能·spring boot·spring
寒士obj1 小时前
Spring事物
java·spring
IT毕设实战小研9 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
甄超锋10 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
Java小白程序员14 小时前
Spring Framework:Java 开发的基石与 Spring 生态的起点
java·数据库·spring
甄超锋14 小时前
Java Maven更换国内源
java·开发语言·spring boot·spring·spring cloud·tomcat·maven
还是鼠鼠15 小时前
tlias智能学习辅助系统--Maven 高级-私服介绍与资源上传下载
java·spring boot·后端·spring·maven
还是大剑师兰特17 小时前
Spring面试题及详细答案 125道(1-15) -- 核心概念与基础1
spring·大剑师·spring面试题·spring教程
python_13619 小时前
web请求和响应
java·spring·github
ciku21 小时前
Spring AI Starter和文档解读
java·人工智能·spring