本文将深入解析Spring Boot中Controller的核心机制,涵盖@Controller与@RestController的区别、DispatcherServlet的工作流程、参数绑定原理以及常用注解的详细工作方式。通过本文,可以掌握Spring Boot处理HTTP请求的全貌,并理解其背后的设计思想。
在开始正文之前,首先需要明确springboot处理网络请求底层仍依赖于第三方Http服务器(默认是Tomcat,如果追求更高性能可以尝试netty)。其通过自动装配将tomcat自动集成Servlet容器中。而Tomcat处理网络请求的模型就是线程池,当网络请求到达时,其会从线程池中分配一个空闲线程来处理请求,处理完毕后会再归还到池中。
其次一定要明确Servelet和DisptacherSevelet和Tomcat的关系。
-
Servelet是Java EE的一个标准接口,规定了怎么处理网络请求
-
DispatcherSevelet是Seveket的一个实现,是Springboot的核心。完整整个网络请求过程中各个组件的调度和协调
-
Tomcat:是一个Servlet容器,是Web服务器。会把接收到的Http的请求分发给Servlet实例去处理,最终也会把Servelet返回的响应对象写入到网络中,发送回客户端。可以简单把她理解为一个承载着Servelet的平台,供Servelt进行工作。
最后一定要明确DispatcherServelet返回和Controller返回的关系,简单来说Controller返回的对象最终会成为DispatcherServelet返回的一部分。
-
Controller返回的类型可能是
-
String:代表逻辑视图名,会被映射到html文件。注意哈,这和加了@responsebody注解(常用的restcontroller注解默认带这个)后返回string不一样。这个会被DispatcherServlet解析为具体视图,然后进行渲染
-
ModelAndView:这是个复合容器,同时包含着模型数据(Model)和视图信息(View)。DispatcherServlet会取出其中的数据和视图信息进行渲染
-
普通java对象:这是会直接返回给客户端的数据。这中响应就是我们常见的返回值(需要结合responsebody注解),会由HttpMessageConverter(会事先注册到DispatcherServelet中,spring中其实注册了许多转换器,根据不同的返回类型会进行选择,最常用的就是MappingJackson2HttpMessageConverter)序列化为JSON等形式
-
空:用户手动控制响应,手动控制使用HttpServletResponse对象写入响应(注意响应头也需要自己写入,一般来说这部分都是由spring自己完成的,不建议自己手动实现,没必要)
-
-
组装好HttpServletResponse后,Tomcat转换为字节流发送给客户端
-
Tomcat资源清理(注意哈,如果涉及到修改容器源码,要关注于浏览器版本以及使用的http协议等等判断是否在完成请求后需要关闭连接)
1 Controller与RestController的核心区别
在Spring Boot中,@Controller和@RestController是处理HTTP请求的核心注解,它们在设计理念和适用场景上有着本质区别。
1.1 基本概念与设计目标
@Controller注解源自Spring MVC框架,是传统Web应用开发的核心组件。它遵循经典的MVC(Model-View-Controller)模式,旨在构建服务端渲染的页面应用。当使用@Controller时,控制器方法通常返回一个视图名称(如"home"、"user/profile"),该名称将由视图解析器(ViewResolver)解析为具体的视图资源(如JSP、Thymeleaf模板),最终生成HTML页面返回给客户端。这种模式适用于需要服务器端渲染完整页面的传统Web应用。
@RestController注解则是Spring的现代化RESTful服务组件。它在语义上是@Controller和@ResponseBody注解的组合体,专为构建RESTful API而设计。使用@RestController标注的控制器,其所有方法都会直接将返回值序列化为数据格式(如JSON、XML)并写入HTTP响应体,无需视图解析过程。这种模式适用于前后端分离架构,后端仅提供数据接口,不涉及页面渲染。
1.2 工作机制对比
两种注解的核心区别在于它们处理返回值的方式:
|
特性 |
@Controller |
@RestController |
|---|---|---|
|
核心职责 |
处理Web页面请求,返回视图 |
构建RESTful API,返回数据 |
|
返回值处理 |
返回视图名称,由ViewResolver解析 |
直接返回数据,由HttpMessageConverter序列化 |
|
响应内容类型 |
通常为text/html(HTML页面) |
通常为application/json(JSON数据) |
|
注解组合 |
需要单独使用@ResponseBody返回数据 |
内置@ResponseBody功能,默认返回的都是数据 |
|
适用场景 |
传统服务端渲染的Web应用 |
前后端分离架构、API服务 |
|
视图技术依赖 |
需要Thymeleaf、JSP等视图技术 |
不需要视图技术,只需要JSON序列化库 |
// @Controller 返回视图
@Controller
public class WebController {
@GetMapping("/user")
public String userPage(Model model) {
model.addAttribute("user", userService.getCurrentUser());
return "user-details"; // 返回视图名称,解析为user.html
}
// 需要显式添加@ResponseBody才能返回数据
@ResponseBody
@GetMapping("/api/user")
public User getUserData() {
return userService.getCurrentUser();
}
}
// @RestController 直接返回数据
@RestController
public class ApiController {
@GetMapping("/api/user")
public User getUser() {
return userService.getCurrentUser(); // 直接返回User对象,自动转为JSON
}
}
1.3 ViewResolver与HttpMessageConverter的分工
ViewResolver(视图解析器) 是@Controller工作流程中的核心组件,负责将控制器返回的逻辑视图名解析为具体的视图实现。例如,返回的字符串"user"可能被解析为/WEB-INF/views/users.html文件。Spring Boot支持多种视图技术(Thymeleaf、FreeMarker、JSP等),每种技术都有对应的ViewResolver实现。
HttpMessageConverter(消息转换器) 是@RestController工作流程中的核心组件,负责将Java对象序列化/反序列化为特定数据格式(JSON/XML)。当控制器方法返回一个对象时,Spring会根据请求的Accept头和生产能力选择合适的HttpMessageConverter(如MappingJackson2HttpMessageConverter)将对象转换为JSON格式。
1.4 选择策略与应用场景
选择使用@Controller还是@RestController取决于您的应用架构:
-
使用@Controller的场景:需要构建传统的多页面应用(MPA),服务器端负责渲染HTML页面;需要混合使用页面和数据接口的应用;使用JSP、Thymeleaf等服务器端模板引擎的项目。
-
使用@RestController的场景:前后端分离架构,后端仅提供数据接口;构建RESTful API服务;为移动应用或单页面应用(SPA)提供后端数据支持;微服务架构中的服务间通信接口。
在实践中,Spring Boot应用可以同时使用@Controller和@RestController,分别处理页面请求和API请求,以满足不同的需求。
2 DispatcherServlet:Spring MVC的核心引擎
DispatcherServlet是Spring MVC框架的核心组件,扮演着前端控制器(Front Controller)的角色,是所有HTTP请求的统一入口和协调中心。
2.1 DispatcherServlet的定位与作用
在基于Spring Boot的Web应用中,DispatcherServlet作为中央调度器,负责协调请求处理的各个环节。它接收所有HTTP请求,并将这些请求委托给相应的组件(控制器、处理器等)进行处理,最后将处理结果组装成适当的响应返回给客户端。
Spring Boot通过自动配置简化了DispatcherServlet的配置过程。与传统Spring MVC需要在web.xml中手动配置不同,Spring Boot会自动注册和配置DispatcherServlet,并将其映射到默认路径("/")。可以通过配置文件自定义DispatcherServlet的映射路径和其他参数。
2.2 请求处理详细流程
DispatcherServlet处理HTTP请求的过程是一个复杂但精心设计的分工协作流程:
具体来说,DispatcherServlet处理每个请求时都会执行以下关键步骤:
-
请求拦截与接收:DispatcherServlet作为一个标准的Java Servlet,首先拦截所有匹配其URL模式的HTTP请求。在Spring Boot中,默认会拦截所有请求("/")。
-
处理器映射(HandlerMapping):DispatcherServlet使用HandlerMapping来根据请求的URL、HTTP方法和其他条件查找适合处理该请求的控制器方法。HandlerMapping会返回一个HandlerExecutionChain对象,其中包含匹配的控制器方法以及可能配置的拦截器(Interceptor)。如果没找到可以处理当前请求的Handler,DispactherServlet就会返回404.
-
处理器适配(HandlerAdapter):找到控制器方法后,DispatcherServlet会获取一个支持该处理器类型的HandlerAdapter。其本质上是为了适配不同controller的实现,提供统一的调用接口。HandlerAdapter负责实际调用控制器方法(在实际调用方法之前,会按照顺序执行拦截器链(interceptor chain)的prehandle方法),并处理复杂的参数绑定和返回值处理逻辑。
-
控制器执行:HandlerAdapter使用反射机制调用控制器方法,传递已解析和绑定的参数值。控制器方法执行业务逻辑,并返回一个结果(可能是视图名称、ModelAndView对象、数据对象等)。该方法执行完成后会逆序触发postHandle方法。
-
结果处理:根据控制器方法的返回值类型,DispatcherServlet采取不同的处理策略:
-
返回视图名称或ModelAndView对象:使用ViewResolver解析视图,然后渲染视图生成HTML响应。
-
返回数据对象(@ResponseBody或@RestController):使用HttpMessageConverter将对象序列化为JSON/XML等格式。
-
-
执行资源清理:请求处理完成后会触发拦截器链的afterCompletion方法,执行资源清理等动作
-
异常处理:如果在请求处理过程中发生异常,DispatcherServlet会将异常委托给配置的HandlerExceptionResolver处理,生成适当的错误响应。
-
响应返回:最终,处理结果被写入HTTP响应并返回给客户端。
2.3 路由映射的底层实现
Spring Boot中的路由映射机制主要由HandlerMapping组件实现,其核心是在应用启动时构建URL与控制器方法之间的映射关系。
启动阶段的映射表构建:在Spring Boot应用启动时,RequestMappingHandlerMapping组件会扫描所有带有@Controller或@RestController注解的Bean。它会收集这些类和方法上的@RequestMapping及其派生注解(如@GetMapping、@PostMapping)信息,构建一个全局的"URL-方法"映射表。
请求阶段的映射匹配:当请求到达时,DispatcherServlet会调用HandlerMapping的getHandler方法,该方法会在映射表中查找与当前请求URL和HTTP方法匹配的控制器方法。Spring支持多种匹配策略,包括精确路径匹配、路径变量匹配、请求参数匹配等。
匹配优先级:当多个映射模式匹配同一个请求时,Spring会按照最具体优先的原则选择处理器:
-
精确路径匹配(如"/users/list")优先于路径变量匹配(如"/users/{id}")
-
路径变量匹配优先于通配符匹配(如"/users/*")
-
更长的路径模式优先于较短的路径模式
3 请求处理与参数绑定机制
在讲解spring处理参数的方式之前,首先要知道HttpServletRequest (与之前提到的HttpServletResponse ,这俩都是和字节流直接相关的)是Servlet规范的核心接口,封装着客户端发送来的所有HTTP请求信息(可以获取Header、Params、请求Url等信息)。
-
特别的,ResponseEntity是spring为了简化controller返回使用定义的对象,包装了状态码、头部、响应体等信息,其也会被dispatcherServlet解包,并写入到原始的HttpServletResponse 中。
而Spring为了简化使用,通过注解等方式优化了参数和响应的处理流程。其提供了强大而灵活的参数绑定机制,能够自动将HTTP请求中的参数提取并转换为控制器方法的参数。这一过程主要由HandlerMethodArgumentResolver(参数解析器)家族完成。
3.1 参数解析器体系结构
Spring MVC定义了一个丰富的参数解析器体系,每个解析器负责处理特定类型的参数。常见的参数解析器包括:
-
RequestParamMethodArgumentResolver:处理@RequestParam注解的参数
-
PathVariableMethodArgumentResolver:处理@PathVariable注解的参数
-
RequestResponseBodyMethodProcessor:处理@RequestBody注解的参数
-
ServletModelAttributeMethodProcessor:处理使用@ModelAttribute注解或简单POJO类型的参数
-
ServletRequestMethodArgumentResolver:处理原生HttpServletRequest参数
当控制器方法需要被调用时,Spring会遍历已注册的参数解析器,找到支持每个参数类型的解析器,并调用其resolveArgument方法获取参数值。
3.2 ServletModelAttributeMethodProcessor详解
ServletModelAttributeMethodProcessor是处理简单POJO对象参数绑定的核心组件,它负责将请求参数绑定到自定义的Java对象。
工作流程:
-
对象实例化:ServletModelAttributeMethodProcessor首先通过反射创建目标参数类型的实例。
-
数据绑定:遍历目标对象的所有属性,对于每个属性,从请求参数中查找同名的值。
-
类型转换:如果找到对应的请求参数值,使用ConversionService将字符串类型的参数值转换为目标属性所需的类型。
-
属性注入:通过setter方法(默认的,如果没有setter,会保持默认值,很危险,可能影响到逻辑处理)或字段反射(需要额外配置)将转换后的值注入到目标对象中。
-
验证(可选):如果配置了验证器,会对绑定后的对象进行验证,确保数据有效性。
//以POJO对象参数绑定为例
@PostMapping("/users")
public String createUser(User user) {
// ServletModelAttributeMethodProcessor会自动将请求参数绑定到User对象
userService.save(user);
return "redirect:/users/list";
}
public class User {
private String name;
private Integer age;
private String email;
// 篇幅问题,省略getter和setter
}
当接收到创建用户的请求时,ServletModelAttributeMethodProcessor会自动创建User对象,并将请求参数值绑定到对应的属性中。
3.3 其他参数绑定方式
除了POJO对象绑定,Spring Boot还支持多种参数绑定方式:
@RequestParam绑定:用于提取查询参数或表单参数,适用于简单类型的参数:
@GetMapping("/search")
public String search(@RequestParam("keyword") String keyword,
@RequestParam(value = "page", defaultValue = "1") int page) {
return "search-results";
}
@PathVariable绑定:用于从URL路径中提取变量值:
@GetMapping("/users/{userId}")
public String getUserProfile(@PathVariable("userId") Long userId) {
return "user-profile";
}
@RequestBody绑定:用于提取请求体中的JSON/XML数据,并将其转换为复杂对象:
@PostMapping("/api/users")
public User createUser(@RequestBody User user) {
return userService.save(user);
}
这种灵活的参数绑定机制大大简化了Web开发中的数据提取和转换工作,使开发者能够专注于业务逻辑的实现。
4 @RequestMapping与@ResponseBody详解
4.1 @RequestMapping注解
@RequestMapping是Spring MVC中最核心的注解之一,用于将HTTP请求映射到特定的控制器方法。
核心属性:
-
value/path:指定映射的URL路径,支持Ant风格模式(如"/users/*")和路径变量(如"/users/{id}")
-
method:指定处理的HTTP方法(如RequestMethod.GET、RequestMethod.POST)
-
params:要求请求必须包含某些参数
-
headers:要求请求必须包含某些头部信息
-
consumes:指定处理请求的Content-Type
-
produces:指定返回的内容类型
// @RequestMapping使用示例
@RestController
@RequestMapping("/api/users")
public class UserApiController {
// 处理GET /api/users请求
@RequestMapping(method = RequestMethod.GET)
public List<User> getUsers() {
return userService.getAllUsers();
}
// 处理GET /api/users/{id}请求
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
// 处理POST /api/users请求,要求Content-Type为application/json
@RequestMapping(method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE)
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
}
为了简化代码,Spring 引入了派生注解:@GetMapping、@PostMapping、@PutMapping、@DeleteMapping和@PatchMapping。这些注解提供了更简洁的方式来表达HTTP方法特定的映射。
4.2 @ResponseBody注解
@ResponseBody注解用于指示控制器方法的返回值应该直接写入HTTP响应体,而不是被解释为视图名称。它是实现RESTful Web服务的关键注解。
工作原理:
当方法标注了@ResponseBody时,Spring会使用HttpMessageConverter将返回值序列化为适当的格式(如JSON、XML)。具体过程如下:
-
Spring检查返回值的类型和请求的Accept头部信息
-
选择合适的HttpMessageConverter(如MappingJackson2HttpMessageConverter)
-
使用选定的转换器将对象序列化为对应格式(如JSON)
-
将序列化后的数据写入响应体
// @ResponseBody使用示例
@Controller
@RequestMapping("/api")
public class UserController {
@GetMapping("/user/{id}")
@ResponseBody
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
@GetMapping("/users")
@ResponseBody
public List<User> getUsers() {
return userService.getAllUsers();
}
}
内容协商机制:
Spring Boot支持通过内容协商机制根据客户端的需求返回不同格式的数据。客户端可以通过Accept请求头指定期望的响应格式(如application/json、application/xml),Spring会根据这个信息和已注册的HttpMessageConverter选择最合适的转换器。默认是json格式,对于非json格式,需要明确指定,一般在注解中指定就可以,也可以通过手动写入响应头。
// 内容协商示例:支持返回JSON和XML格式
@GetMapping(value = "/users/{id}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@ResponseBody
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
4.3 @RestController的组合特性
@RestController是一个组合注解,它本身包含了@Controller和@ResponseBody两个注解的元数据。这意味着使用@RestController标注的类会自动具备以下特性:
-
被识别为Spring MVC控制器
-
所有方法的返回值都自动带有@ResponseBody语义
-
无需在每个方法上单独添加@ResponseBody注解
从源码角度来看,@RestController的定义明确展示了它的组合特性:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody // 关键组合
public @interface RestController {
String value() default "";
}
这种组合注解的设计体现了Spring Boot的约定优于配置原则,简化了RESTful Web服务的开发流程。
总结
本文详细解析了Spring Boot中Controller的核心机制。通过理解@Controller和@RestController的区别、DispatcherServlet的工作流程、参数绑定原理以及常用注解的工作方式,开发者可以更深入地掌握Spring Boot的Web开发能力,构建出更健壮、可维护的Web应用程序和API服务。
无论是传统的服务端渲染应用还是现代化的前后端分离架构,Spring Boot都提供了强大而灵活的支持,使开发者能够根据具体需求选择最合适的技术方案。

5659

被折叠的 条评论
为什么被折叠?



