Skip to main content

Spring-Web有哪些常用注解?

作者:程序员马丁

在线博客:https://open8gu.com

note

大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。

由于相关的注解是在太多,因此这里按照注解所属的模块和涉及的功能大致拆成了几篇文章:

问题详解

1. @Controller & @Service & @Repository

在 SpringMVC 中,我们会比较经常用到三个注解:

  • @Controller:表示一个控制器,通常与 @RequestMapping 一起使用。
  • @Service:表示一个服务层对象。
  • @Repository:表示一个数据访问层对象,即一般我们俗称的 DAO (Data Access Object)。

它们实际上都是 @Component 的扩展注解。

顺带一提,如果我们用的是 Mybatis 而不是 JPA,那么比较常用的就是 @Mapper而不是 @Repository ,不过,@Mapper@Repository 实际上一点关系都没有,@Mapper 注解的解析逻辑是完全由 MyabtisMapperScannerRegistrar实现的。

关于 Mybatis 的 @MapperScan 的运行原理,请参见:Mybatis 的 Mapper 是怎么变成 Bean 的?

2. @RequestMapping

@RequestMapping 是 Spring 中用于映射 Web 请求到控制器中方法的注解,它可以用在方法上,表示该方法用于响应指定路径的请求。

比如:

@Controller
@RequestMapping("/example") // 当放在类上时,等同于定义公共路径前缀
public class MyController {

@RequestMapping(
path = {"/method1", "/method"}, // 支持同时映射多个路径
method = RequestMethod.GET
)
public String method1() {
// 处理 /example/method1 或 /example/method 的 get 请求
return "view1";
}
}

它还有一批组合注解,比如:@GetMapping@PostMapping@PutMapping 还有@DeleteMapping,它们实际上就是指定了请求方式的 @RequestMapping

此外,除按请求方法与请求匹配外,@RequestMapping 还支持按 header 或参数进行匹配,比如:

@RequestMapping(
path = {"/method1", "/method"},
headers = "allow-request-method1" // 仅当 headler 中存在该值时才响应
method = RequestMethod.GET
)
public String method1() {
// 处理 /example/method1 或 /example/method 的 get 请求
return "view1";
}

关于 @RequestMapping 的原理,请参见:@RequestMapping 注解是如何生效的?

3. @RequestParam

@RequestParam 它用于将请求的 param 参数的值绑定到控制器方法的参数上。

一般来说,它相当于代替我们我们完成了从 HttpServlertRequest.getAttribute 取值的工作。

比如:

@GetMapping("/method")
public String method(@RequestParam String param) {
// 将请求中名为 "param" 的参数绑定到 param 上
return "view";
}

一般情况下,Spring 会根据参数名自动寻找请求中的同名参数,不过你也可以通过指定 name 属性显式的指定要绑定的参数名称:

@GetMapping("/method")
public String method(@RequestParam(name = "arg") String arg) {
// 将请求中名为 "param" 的参数绑定到 param 上
return "view";
}

具体参见:@RequestParam 和@RequestBody 注解是如何生效的?

4. @PathVariable

@PathVariable 用于将 URI 模板中的变量绑定到控制器方法的参数上。

比如:

@Controller
@RequestMapping("/example")
public class MyController {

@GetMapping("/method/{id}")
public String method(@PathVariable Long id) {
// 将 URI 模板中的变量值绑定到方法参数 id 上
return "view";
}
}

@RquestParam 一样,Spring 会根据参数名自动寻找请求中的同名参数,或者也可以手动指定 name 属性从而绑定到指定的参数上。

具体参见:@RequestParam 和@RequestBody 注解是如何生效的?

5. @RequestBody

@RequestBody 注解用于将 HTTP 请求中的 Body 与方法参数进行绑定。

由于 Spring 默认使用 Jackson 作为 JSON 库,所以你可以在参数类型中使用 Jackson 提供的一些注解, 比如 @JsonAlias 或者 @JsonFormat之类的注解来调整序列化配置。

具体参见:@RequestParam 和@RequestBody 注解是如何生效的?

6. @ResponseBody

@ResponseBody 注解用于标记在带有 @Controller 注解的控制器类中的方法上,表示返回值应该直接作为 HTTP 响应的 Body,而不是需要通过视图解析器进行渲染

简单来说,就是直接将数据写入 ResponseBody,用于返回 JSON 数据,而不是像传统 MVC 项目那样返回一个通过模板引擎渲染好的页面。

当它注解在控制器类上时,则表示所有的方法均返回 JSON 数据,当然,这种情况下我们一般会直接使用 @RestController代替 @Controller + @ResponseBody

关于 @ResponseBody 的原理,请参见:@ResponseBody 注解是怎么生效的?

7. @RestController

@RestController@ResponseBody@Controller 组合而来,表示一个 RESTFul 风格的控制器,它的所有方法返回值都直接写入响应体中

@RestContorller
public class FooController {

@GetMapping()
public Foo getFoo();
}

// 上面的写法等价于下面

@Controller
public class FooController {
@ResponseBody
@GetMapping()
public Foo getFoo();
}

8. @ControllerAdvice

@ControllerAdvice 注解相当于官方提供的仅针对控制器的切面注解,它一般配合其他的方法级注解使用,从而向控制器中的方法统一的添加前置或后置操作:

@ControllerAdvice
public class GlobalControllerAdvice {

// 拦截方法抛出的 Exception 异常,并返回错误响应体
@ExceptionHandler(Exception.class)
@ResponseBody
public Result<String> handleException(Exception ex) {
return new Result(500, "服务器内部错误");
}

// 在入参时,统一添加 globalAttribute 参数
@ModelAttribute
public void globalAttributes(Model model) {
model.addAttribute("globalAttribute", "someValue");
}

// 进行数据绑定时,忽略 unwantedField 属性
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields("unwantedField");
}
}

目前比较主流的用法还是结合 @ExceptionHandler 实现异常拦截器。

9. @ExceptionHandler

@ExceptionHandler注解用于配合 @ControllerAdvice 拦截控制器方法抛出的特定类型的异常。

比如:

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(MyCustomException.class)
public Result<String> handleException(Exception ex) {
return new Result(500, "业务异常");
}

@ExceptionHandler(Exception.class)
public Result<String> handleException(Exception ex) {
return new Result(500, "未知错误");
}
}

值得注意的是,当针对某个类型的异常同时有多个 @ExceptionHandler 支持处理时,Spring 将按照拦截方法上的异常类型与实际异常类型在继承树中的距离判断要应用哪个,即拦截的异常类型越接近实际类型,就越优先。

比如:

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(NullPointerException.class) // 当抛出 NullPointerException 时走这里
public Result<String> handleException(Exception ex) {
return new Result(500, "业务异常");
}

@ExceptionHandler(RuntimeException.class) // 当抛出 IllegalArgumentException 时走这里
public Result<String> handleException(Exception ex) {
return new Result(500, "业务异常");
}

@ExceptionHandler(Exception.class) // 当抛出 IllegalAccessException 时走这里
public Result<String> handleException(Exception ex) {
return new Result(500, "未知错误");
}
}

具体参见:SpringMVC 怎么做统一异常处理?