Spring Controller深入解析

本文将深入解析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处理每个请求时都会执行以下关键步骤:

  1. 请求拦截与接收​:DispatcherServlet作为一个标准的Java Servlet,首先拦截所有匹配其URL模式的HTTP请求。在Spring Boot中,默认会拦截所有请求("/")。

  2. 处理器映射(HandlerMapping)​​:DispatcherServlet使用HandlerMapping来根据请求的URL、HTTP方法和其他条件查找适合处理该请求的控制器方法。HandlerMapping会返回一个HandlerExecutionChain对象,其中包含匹配的控制器方法以及可能配置的拦截器(Interceptor)。如果没找到可以处理当前请求的Handler,DispactherServlet就会返回404.

  3. 处理器适配(HandlerAdapter)​​:找到控制器方法后,DispatcherServlet会获取一个支持该处理器类型的HandlerAdapter。其本质上是为了适配不同controller的实现,提供统一的调用接口。HandlerAdapter负责实际调用控制器方法(在实际调用方法之前,会按照顺序执行拦截器链(interceptor chain)的prehandle方法),并处理复杂的参数绑定和返回值处理逻辑。

  4. 控制器执行​:HandlerAdapter使用反射机制调用控制器方法,传递已解析和绑定的参数值。控制器方法执行业务逻辑,并返回一个结果(可能是视图名称、ModelAndView对象、数据对象等)。该方法执行完成后会逆序触发postHandle方法

  5. 结果处理​:根据控制器方法的返回值类型,DispatcherServlet采取不同的处理策略:

    • 返回视图名称ModelAndView对象​:使用ViewResolver解析视图,然后渲染视图生成HTML响应。

    • 返回数据对象​(@ResponseBody或@RestController):使用HttpMessageConverter将对象序列化为JSON/XML等格式。

  6. 执行资源清理:请求处理完成后会触发拦截器链的afterCompletion方法,执行资源清理等动作

  7. 异常处理​:如果在请求处理过程中发生异常,DispatcherServlet会将异常委托给配置的HandlerExceptionResolver处理,生成适当的错误响应。

  8. 响应返回​:最终,处理结果被写入HTTP响应并返回给客户端。

2.3 路由映射的底层实现

Spring Boot中的路由映射机制主要由HandlerMapping组件实现,其核心是在应用启动时构建URL与控制器方法之间的映射关系。

启动阶段的映射表构建​:在Spring Boot应用启动时,RequestMappingHandlerMapping组件会扫描所有带有@Controller或@RestController注解的Bean。它会收集这些类和方法上的@RequestMapping及其派生注解(如@GetMapping、@PostMapping)信息,构建一个全局的"URL-方法"映射表。

请求阶段的映射匹配​:当请求到达时,DispatcherServlet会调用HandlerMapping的getHandler方法,该方法会在映射表中查找与当前请求URL和HTTP方法匹配的控制器方法。Spring支持多种匹配策略,包括精确路径匹配、路径变量匹配、请求参数匹配等。

匹配优先级​:当多个映射模式匹配同一个请求时,Spring会按照最具体优先的原则选择处理器:

  1. 精确路径匹配(如"/users/list")优先于路径变量匹配(如"/users/{id}")

  2. 路径变量匹配优先于通配符匹配(如"/users/*")

  3. 更长的路径模式优先于较短的路径模式

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对象。

工作流程​:

  1. 对象实例化​:ServletModelAttributeMethodProcessor首先通过反射创建目标参数类型的实例。

  2. 数据绑定​:遍历目标对象的所有属性,对于每个属性,从请求参数中查找同名的值。

  3. 类型转换​:如果找到对应的请求参数值,使用ConversionService将字符串类型的参数值转换为目标属性所需的类型。

  4. 属性注入​:通过setter方法(默认的,如果没有setter,会保持默认值,很危险,可能影响到逻辑处理)或字段反射(需要额外配置)将转换后的值注入到目标对象中。

  5. 验证​(可选):如果配置了验证器,会对绑定后的对象进行验证,确保数据有效性。

//以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)。具体过程如下:

  1. Spring检查返回值的类型和请求的Accept头部信息

  2. 选择合适的HttpMessageConverter(如MappingJackson2HttpMessageConverter)

  3. 使用选定的转换器将对象序列化为对应格式(如JSON)

  4. 将序列化后的数据写入响应体

// @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标注的类会自动具备以下特性:

  1. 被识别为Spring MVC控制器

  2. 所有方法的返回值都自动带有@ResponseBody语义

  3. 无需在每个方法上单独添加@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都提供了强大而灵活的支持,使开发者能够根据具体需求选择最合适的技术方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值