springboot 请求处理流程

本文打算介绍在 springboot 中,一个 http 请求从过来到响应结束的过程。主要通过介绍一些关键源码来说明。在分析源码之前我们可以先猜一下可能会有的过程。首先在我们的 springboot 应用中一般都会存在多个 controller handler 的方法,所以一个请求过来肯定是要先找到匹配该请求的方法。其次请求过来的参数需要处理成 controller 方法可以识别的参数格式,这样才能做进一步的处理。最后当 controller 的方法处理完之后,可能还要把返回值做一定的处理再返回给前端去。

所以本文重点介绍 url 映射 handler,参数解析,返回值处理三个过程。

首先,我们创建一个演示代码, springboot 版本我用的是 2.2.2.RELEASE,controller 代码如下:

package com.fc.study;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/fc-study")
public class HelloWorldController {

    @GetMapping(value="/hello")
    public String hello(@RequestParam("name") String name, @RequestParam("word") String word){
        return "有人 "+name+" 说了一句话 "+word;
    }
}

应用跑起来,浏览器访问结果如图:

众所周知 springboot  默认是 tomcat 容器 (SERVLET web类型)。我们要分析一个请求过来所经历的过程,自然要从前端总控制器(DispatchServlet)开始说起。按照 Servlet 规范,所有请求都会被tomcat容器交到 dispatchServlet 的 doService 方法中去处理。跟到这个方法中去,我们发现其中设置了变量进 request 对象,然后执行了 doDispatch 方法,这个方法才是真正实现请求处理的核心。

	/**
	 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
	 * for the actual dispatching.
	 */
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		/* 为了博客篇幅我们省略了很多 request.setAttribute 的代码 */
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
	
		try {
			doDispatch(request, response);
		}
		finally {
		}
	}

以下是 doDispatch 方法,为篇幅考虑保留了关键代码, 我们看到首先调用 getHandler 找到 url 匹配的 handler 方法(示例代码中的 hello 方法)。然后调用 ha.handle() 来获得处理结果。

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

  		// Determine handler for the current request.
		mappedHandler = getHandler(processedRequest);
				
		// Determine handler adapter for the current request.
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

		// Actually invoke the handler.
		mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                /* 后续处理 */	
	}

进入 getHandler 方法,我们看到是通过 HandlerMapping 接口对象的集合对象来操作的。HandlerMapping 接口要求实现类实现从请求到处理对象的映射的方法。有兴趣的同学可以断点看一下这个对象包含的集中实现,以 RequestMappingHandlerMapping 实现为例,它底层注册了一个 url -> handler方法的 map,每当请求过来,就会根据请求的url 去 map 中匹配,匹配到对应的handler 方法。

@Nullable
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;
}

匹配到了 handler 方法之后,就到了 handler 方法的调用。通过断点,一步步的进到这个 handle 方法中去,我们发现最终的实现是在 ServletInvocableHandlerMethod 中实现的,它和父类分别实现了对返回值和参数的处理。

/* InvocableHandlerMethod 实现*/
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {

        /* 1.参数解析 */
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	return doInvoke(args);
}

/* ServletInvocableHandlerMethod 实现 */
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {

        /* 2.调用获得返回值 */
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	
        /* 3.处理返回值 */
	this.returnValueHandlers.handleReturnValue(
	returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}

以参数解析为器例子,springboot 实现了一个 Composite 对象,其中有一个 List 和一个 Map 来分别存储所有的扫描到的参数解析器以及曾经匹配过的解析器缓存。外部调用 resolveArgument 方法来进行解析,这个方法首先会调用 composite 的私有方法 getArgumentResolver 来获取参数解析器,然后再用参数解析器进行解析。获取解析器的过程首先是从缓存Map中去取,如果没有取到(也就是之前该种参数解析器被缓存在map中),则会去循环注册的所有解析器的 List,找出第一个可以解析当前请求参数的解析器,进行参数解析,并把它缓存。

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {

	private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();

	private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
			new ConcurrentHashMap<>(256);

	@Override
	@Nullable
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		if (resolver == null) {
			throw new IllegalArgumentException("Unsupported parameter type [" +
					parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
		}
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}

	/**
	 * Find a registered {@link HandlerMethodArgumentResolver} that supports
	 * the given method parameter.
	 */
	@Nullable
	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;
	}

}

再看返回值处理,它主要是把 handler 方法获得的返回值,在model对象上做一些set操作。步骤大体也是类似的,实现了一个 **Composite 对象。在此不再赘述。最后,用一个表格来总结。

步骤接口实现方式
路径映射HandlerMapping在 DispatchServlet 中,springboot 注册了一个 HandlerMapping 列表。请求过来时,会循环该列表,来解析url获取handler方法,获取到之后即跳出循环。
参数解析HandlerMethodArgumentResolver在 InvocableHandlerMethod 中,springboot 注入了一个 **Composite 对象,该对象中有多个解析器放在列表中,解析参数时同样是循环列表,来找出第一个可以解析的解析器进行解析。 
返回处理HandlerMethodReturnValueHanler在 ServletInvocableHandlerMethod 中,springboot 注入了一个 **Composite 对象,该对象中有多个返回值处理器在列表中,处理返回值时,首先同样是循环列表,找到处理器,再进行返回值处理。

 

  • 11
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot 是基于 Spring Framework 的快速开发框架,它可以帮助开发者快速构建 Web 应用程序。在 Spring Boot 中,请求处理是通过控制器(Controller)实现的。 控制器是一个带有 @Controller 注解的类,它包含处理 HTTP 请求的方法。每个方法都使用特定的请求路径和 HTTP 方法(GET、POST、PUT、DELETE 等)进行映射。例如,以下代码段显示了一个控制器类的示例: ``` @Controller @RequestMapping("/user") public class UserController { @GetMapping("/{id}") public ResponseEntity<User> getUserById(@PathVariable Long id) { // 处理获取用户信息的逻辑 User user = userService.getUserById(id); return ResponseEntity.ok(user); } @PostMapping("/") public ResponseEntity<User> createUser(@RequestBody User user) { // 处理创建用户的逻辑 User newUser = userService.createUser(user); return ResponseEntity.ok(newUser); } } ``` 在这个示例中,我们定义了一个名为 UserController 的控制器类,并使用 @RequestMapping 注解指定了该控制器的基本请求路径为 "/user"。然后,我们定义了两个方法,一个用于获取用户信息,另一个用于创建用户。这些方法使用 @GetMapping 和 @PostMapping 注解分别指定了 GET 和 POST 请求的路径和方法类型。 在这些方法中,我们可以通过参数获取请求中的数据,例如在 getUserById 方法中,我们使用 @PathVariable 注解获取请求路径中的参数 id。在 createUser 方法中,我们使用 @RequestBody 注解获取请求体中的数据,并将其转换为 User 对象。 最后,这些方法会返回一个 ResponseEntity 对象,该对象包含响应数据和 HTTP 状态码。在这个例子中,我们使用 ResponseEntity.ok() 方法返回一个 HTTP 状态码为 200(OK)的响应,并将获取或创建的用户对象作为响应数据返回。 这就是 Spring Boot 中请求处理的基本流程。开发者只需要编写控制器类和处理方法,Spring Boot 就会自动将请求路由到正确的方法,并处理响应

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值