Spring 03 Web springMVC
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 都不会执行。