**MVC ** 是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
Spring MVC 最核心的思想在于 DispatcherServlet
。在现在的开发模式中,我们主要使用的也是 Spring MVC 的这一核心功能。
浏览器发起一个请求(如:http://localhost:8080/hello), 会经历如下步骤:
springmvc核心组件分为3个,他们分别是Handler、HandlerMapping、HanderAdapter
Handler 是用来做具体事情的,对应的是 Controller 里面的方法,所有有 @RequestMapping 标注的方法都可以看做为一个 Handler。
HandlerMapping 是用来找到 Handler 的,是请求路径与 Handler 的映射关系。
HandlerAdapter 从名字看,可以知道它是一个适配器。它是用来跟具体的 Handler 配合使用的。可以简单理解为各种电子产品与电源适配器(充电器)的关系。
DispatcherServlet 最核心的方法就是 doDispatch ,doDispatch 主要做了四件事:
注解 | 作用域 | 说明 |
---|---|---|
@Controller | 类 | Controller标识 |
@RequestMapping | 类/方法 | URL映射 |
@ResponseBody | 类/方法 | 以Json方式返回 |
@RequestParam | 参数 | 按名字接收参数 |
@RequestBody | 参数 | 接收Json参数 |
@PathVariable | 参数 | 接收URL中的参数 |
@RestController | 类 | 组合注解:@Controller + @ResponseBody |
@GetMapping | 方法 | 组合注解:@RequestMapping(method = RequestMethod.GET) |
@PostMapping | 方法 | 组合注解:@RequestMapping(method = RequestMethod.POST) |
@PutMapping | 方法 | 组合注解:@RequestMapping(method = RequestMethod.PUT) |
@PatchMapping | 方法 | 组合注解:@RequestMapping(method = RequestMethod.PATCH) |
@DeleteMapping | 方法 | 组合注解:@RequestMapping(method = RequestMethod.DELETE) |
从上表我们可以发现组合注解就是具有多个功能的注解,由多个注解或者一个注解 + 一个特定的属性值组成的注解,相当于对注解的一种封装。
例如@RestController 不仅可以标识一个 Controller ,还能让被标识的 Controller 中的所有方法都返回 JSON 格式的数据;@GetMapping 不仅可以映射一个请求路径,还让该路径只响应 GET 请求,对于其他的请求方式不响应。
Spring MVC 的主要工作就是接收外部的请求,然后根据请求去调用相应的服务,最后将处理结果返回。外部发来的请求会以各种形式带着各式各样的参数,以达到不同的目的。Spring MVC共 有四种接收参数的方式:
接下来,我们分别给出示例:
首先,我们需要准备一个接收入参的实体类:
public class User {
private String name;
private int age;
// 此处省略set get方法
// ......
}
@RestController
public class ParamController {
@GetMapping("/noannotation")
public User noAnnotation( User user) {
return user;
}
}
请求示例:
http://localhost:8080/noannotation?name=无注解方式&age=18
@RequestParam 注解有四个属性:
属性 | 类型 | 说明 |
---|---|---|
name | String | 参数名称 |
value | String | name 属性的别名 |
required | boolean | 指定是否为必传参数(为 true 时不传会报错) |
defaultValue | String | 参数默认值 |
@GetMapping("/requestparam")
public User RequestParam(@RequestParam String name, @RequestParam int age) {
User user = new User();
user.setName(name);
user.setAge(age);
return user;
}
请求示例:
http://localhost:8080/requestparam?name=@RequestParam方式&age=4
@PathVariable 注解有三个属性:
属性 | 类型 | 说明 |
---|---|---|
name | String | 参数名称 |
value | String | name 属性的别名 |
required | boolean | 指定是否为必传参数(为 true 时不传会报错) |
@GetMapping("/pathvariable/{name}/{age}")
public User PathVariable(@PathVariable String name,@PathVariable int age) {
User user = new User();
user.setName(name);
user.setAge(age);
return user;
}
请求示例:
http://localhost:8080/pathvariable/@PathVariable方式/2
@RequestBody 只有一个属性:
属性 | 类型 | 说明 |
---|---|---|
required | boolean | 指定是否为必传参数(为 true 时不传会报错) |
@PostMapping("/requestbody")
public User RequestBody(@RequestBody User user) {
return user;
}
请求示例(请自行使用接口调试工具测试,如postMan):
url: http://localhost:8080/requestbody
method: post
body: {"name":"@RequestBody方式","age":12}
书接上回,在这一趴我们一起来学习一下 Spring MVC 中的拦截器。拦截器在我们日常开发当中有着很重要的地位,很多重要的功能需要借助拦截器帮我们完成。我们通常会使用拦截器帮我们完成以下功能:
接下来我们学习如何写一个拦截器。Spring MVC 中所有的拦截器都实现/继承自 HandlerInterceptor
接口。我们想要写一个自定义拦截器的话,需要实现/继承 HandlerInterceptor
或其子接口/实现类。下图是 Spring MVC 中拦截器的类图(还有几个类是 HandlerInterceptorAdapter
的子类,这里没有列出):
HandlerInterceptor
接口的源码如下:
public interface HandlerInterceptor {
// 处理器执行前被调用
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
// 处理器执行后,视图渲染前被调用
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// 视图渲染完成后背调用
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
下面我们自定义一个最简单、纯净的拦截器,也就是直接实现 HandlerInterceptor
接口。
新建一个类 LogInterceptor
并实现 HandlerInterceptor
接口:
并在三个方法中分别添加一条日志打印的代码
@Slf4j
@Component
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion");
}
}
新建一个类 WebConfigurer
并实现 WebMvcConfigurer
接口,用于注册我们自定义的拦截器:
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
@Autowired
private LogInterceptor logInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor);
}
}
在 HelloController
的 hello
方法中添加一条日志打印代码:
@Slf4j
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(@RequestParam(required = false) @ApiParam("名字") String name) {
if (name == null || "".equals(name)) {
name = "Spring Boot";
}
log.info("hello");
return "Hello "+name;
}
}
OK,接下来启动工程,并访问以下hello
方法,控制台会看到如下的输出:
com.imooc.springboot.LogInterceptor : preHandle
com.imooc.springboot.HelloController : hello
com.imooc.springboot.LogInterceptor : postHandle
com.imooc.springboot.LogInterceptor : afterCompletion
如果一切正常,将出现如上结果,这代表我们的自定义拦截器成功了!
从控制台的日志输出,我们可以大概看出拦截器的执行流程。下面我们来更加深入的学习一下拦截器的整个执行流程:
我们可以在 DispatcherServlet
的 doDispatch
方法的源码中进一步验证这个执行逻辑:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
try {
// 返回 HandlerExecutionChain 其中包含了拦截器队列
mappedHandler = getHandler(processedRequest);
//调用拦截器 PreHandle 方法,若返回 false 将直接 return
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 处理 Controller
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 调用拦截器的 PostHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
// 调用拦截器的 afterCompletion 方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
}
本文主要从日常中对springmvc高频使用点做了简单的介绍,包括springmvc的工作原理,核心组件,以及日常编码中常用注解及使用方式,如何传参,拦截器等应用,更多使用技巧还需要大家在日常工作中去磨炼,共同进步。