Springboot 常用 Spring MVC 实现 web 服务。
Spring MVC 请求处理流程
图片来自《Spring 实战第四版》
浏览器请求首先被交给 DispatcherServlet 前端控制器。
DispatcherServlet 查询处理器映射以决定将请求发送给哪个控制器。控制器处理业务逻辑后,向 DispatcherServlet 返回处理结果(被称为模型)和逻辑视图名。DispatcherServlet 使用视图解析器为逻辑视图名匹配特定视图。最后视图渲染结果,交给响应对象返回浏览器。
MVC 启动
Springboot 启动 Spring MVC 时,动态加载\org\springframework\web\servlet\DispatcherServlet.properties
文件定义的组件。
控制器
@Controller 注解定义控制器。它与 @Component 注解功能相同,因此Spring 会创建 MyController bean。
@RequestMapping 注解定义请求路径(value 属性)和控制器方法(hello())的映射关系。映射关系被存储到HandlerMapping(处理器映射)对象。DispatcherServlet 将请求发送给处理器映射,处理器映射返回的不是 Controller,而是 HandlerExecutionChain 对象。
@Controller
public class MyController {@RequestMapping(value = "/", method=GET)public String hello() {return "hello";}
}
HandlerExecutionChain 对象包含处理器 handler 和拦截器 interceptorList 。处理器是对控制器 controller 的封装。拦截器增强处理器功能。
public class HandlerExecutionChain {private final Object handler;private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
HandlerAdapter 实现类执行 HandlerExecutionChain 对象的内容。即执行控制器并返回结果。
控制器返回的结果被称为模型。视图解析器根据逻辑视图名称定位视图,视图渲染模型给响应体。前后端分离场景下一般用 json 格式响应体,此时不需要视图解析器和视图。
@RequestMapping
@RequestMapping 注解有主要有两个参数,一个是路径 value,一个是请求方法 method。它的简化版本 @GetMapping 注解表示 get 请求类型,@PostMapping 注解表示 Post 请求类型。
获取控制器参数
@RequestParam 注解可以定义HTTP参数和方法参数的映射关系。
@RequestMapping(value = "/param", method=GET)
public String param(@RequestParam("user") String user,@RequestParam("age") int age) {return service(user, age);
}
@RequestBody 注解可以将 json 参数转换为对应 java 对象。
@RequestMapping(value = "/param", method=GET)
public String param(@RequestBody User user) {return service(user);
}
@PathVariable 注解可以获取路径参数。Restful 风格要求用路径,而不是用参数表示资源。
@RequestMapping(value = "/param/{id}", method=GET)
public String param(@PathVariable("id") String id) {return service(id);
}
@DataTimeFormat 注解和 @NumberFormat 注解可以定义格式化参数。
自定义获取控制器参数的规则
SpringMVC 通过 WebDataBinder 机制来获取参数。它解析 HTTP 请求上下文,转换参数并且提供验证功能。转换参数的接口有三个:Converter,Formatter 和 GenericConverter。第一个是转换类型,第二个是转换格式,第三个是转换数组。
SpringMVC 将三个接口的默认实现类注册到注册机,这就是大部分类型转换无需开发者开发的原因。
我们可以定义自己的转换器,只需实现接口,SpringMVC自动注册到注册机。
@Component
public class MyConvrter implements Converter<String, User> {@Overridepublic User convert(String str) {...}
}
参数验证
SpringMVC 支持参数验证。通过 @Valid 注解启动验证机制,通过 @NotNull, @Max, @Range, @Email 等注解验证字段。
用户也可以在 WebDataBinder 注册验证器自定义验证机制。
public class MyValidator implements Validator {// 指定验证类型@Overridepublic boolean supports(Class<?> clazz) {return clazz.equals(User.class);}// 执行验证方法@Overridepublic void validate(Object target, Errors erros) {...}
}
MyValidator 类没有 @Component 注解。在控制器类用 @InitBinder 注解注册验证类。
@RequestMapping("/user")
public class UserController {@InitBinderpublic void initBinder(WebDataBinder binder) {binder.setValidator(new MyValidator()); // 绑定验证器}@GetMapping("/validate")public Map<String, Object> validator(@Valid User user, Errors erros) {if (errors.hasErrors()) {// 没通过验证,返回错误结果}}
}
视图
SpringMVC 视图分为逻辑视图和非逻辑视图。逻辑视图需要视图解析器进一步定位视图,比如 JSP。非逻辑视图不需定位,直接渲染,比如 json。
拦截器
拦截器用于拦截处理器,增强处理器功能。preHandle 方法在处理器执行前执行,postHandle 方法在处理器执行后执行,afterCompletion 方法在处理器完成且视图渲染后后执行。
public class MyInterceptor extends HandlerInterceptor {@Overridepublic boolean preHandle(HttpServltRequest request, HttpServletResponse response, Object handler) {...}@Overridepublic boolean postHandle(HttpServltRequest request, HttpServletResponse response, Object handler) {...}@Overridepublic boolean afterCompletion(HttpServltRequest request, HttpServletResponse response, Object handler) {...}
}
开发拦截器后需要注册拦截器。
@Configuration
public class MyApplication implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {InterceptorRegistry ir = registry.addInterceptor(new MyInterceptor());ir.addPathPatterns("/user/*");}
}
多个拦截器按照责任链模式装配。对于 preHandle 方法,先注册先执行。对于 postHandle 和 afterCompletion 方法,先注册后执行。如果某个拦截器的 preHandle 为 false,后续的 preHandle 和 postHandle 都不会执行。