@RestController 源码解读:解决 Web 开发中 REST 服务的疑难杂症
目录
一、@RestContrller注解
1.1 查看底层源码
1.2 @AliasFor注解说明
1.2.1 注解别名
1.2.2 元数据别名
1.3 value() 方法的作用
一、@RestContrller注解
1.1 查看底层源码
首先编写如下内容:
@RestController
public class TestController {
}
按住 Ctrl , 鼠标点击 @RestController 进入源码:
-
@Target注解说明:查看底层源码发现
-
@Documented:标明这个注解会包含在JavaDoc文档中
-
@Retention:表示这个注解在运行时仍然有效,可以通过反射机制读取。
1.2 @AliasFor注解说明
@AliasFor是Spring框架的一个注解,用于声明注解属性的别名。它有两种不同的应用场景。
-
注解内的别名
-
元数据的别名
1.2.1 注解别名
查看@AliasFor注解源码:
通过该注解可以知道@AliasFor(value="xxx")和@AliasFor(attribute="xxx")的作用是相同的,接下来我们举例说明:
-
首先我需要自定义一个注解
@Retention(value = RetentionPolicy.RUNTIME) @Documented @Target(ElementType.TYPE) public @interface MyInterface1 { String value1() default ""; }
当我们使用到该注解如 @MyInterface1(value1="number1"),表示 value1 的属性传入了 number1的值。现在如果自定义的注解改变为如下方式,但我希望通过 @MyInterface1(value2="number1") 达到上述相同的效果:
@Retention(value = RetentionPolicy.RUNTIME) @Documented @Target(ElementType.TYPE) public @interface MyInterface1 { String value1() default ""; String value2() default ""; }
就可以添加@Alias()注解为注解内方法(因为@Alias作用域是方法注解)起别名:
@Retention(value = RetentionPolicy.RUNTIME) @Documented @Target(ElementType.TYPE) public @interface MyInterface1 { @AliasFor(value = "value2") String value1() default ""; @AliasFor(value = "value1") String value2() default ""; }
达到 @MyInterface1(value2="number1") 和 @MyInterface1(value1="number1") 的效果相同。
注意事项:
- 组成别名对的每个属性都必须加上注释 @AliasFor,attribute()或value() 属性必须引用该对中另一个属性
- 别名属性必须声明相同的返回类型
- 别名属性必须声明一个默认值
- 别名属性必须声明相同的默认值
1.2.2 元数据别名
-
自定义第一个注解:
@Retention(value = RetentionPolicy.RUNTIME) @Documented @Target(ElementType.TYPE) public @interface MyInterface1 { String value() default ""; }
-
自定义第二个注解:
@Retention(value = RetentionPolicy.RUNTIME) @Documented @Target(ElementType.TYPE) @MyInterface1 public @interface MyInterface2 { @AliasFor(annotation = MyInterface1.class,value = "value") String value() default ""; }
此时@MyInterface1 (“number”)就和@MyInterface2(“number”)等价。这里可以理解成,注解MyInterface2的value属性重写了注解MyInterface1的value属性,但重新的属性的返回类型必须相同。
此时再来查看@RestController 的@Alias:
@RestController(value="xxxx")和@Controller(value="xxx")等价。
类似的使用包括:
可以看出对于value这个属性来说,@Configuration注解中的值会重写@Component的value属性值,这有点像类之间的继承,子类可以重父类的方法。我们也可以将@Configuration注解看成@Component的子注解
1.3 value() 方法的作用
在@RestController
的定义中,value()
方法使用@AliasFor
注解将其与@Controller
的value
属性关联起来。这意味着当在@RestController
上使用value
属性时,它实际上等同于在@Controller
上使用value
属性,用于指定控制器的基本路径等相关信息,例如@RestController("myControllerPath")
这种用法
也就是说value
属性用于指定控制器的基本路径,那么我们进行接下来的测试:
-
首先编写Controller类代码:运行项目
@RestController(value = "user") public class TestController { @RequestMapping("/test") public String test(){ return "测试成功!"; } }
结果程序并没有识别到,发生404错误:
-
接下来测试/test路径:发现可以访问成功
- 所以如果是要填写路径的话,还是需要使用 @RequestMapping("/user/test")才是正确的,代码修改后测试:
@RestController(value = "user") @RequestMapping("/user") public class TestController { @RequestMapping("/test") public String test(){ return "测试成功!"; } }
此时就可以访问成功了:
-
一般情况下,我们修改为如下的规范:
@RestController @RequestMapping("/user") public class TestController { @RequestMapping("/test") public String test(){ return "测试成功!"; } }
所以value()方法的解释仅仅是 “建议”。
JDK 17 引入 records 新特性