Java 注解
基本注解
- 内置标准注解
1 2 3 4 5
| @Override @Deprecated @SuppressWarnings @SafeVarargs @FunctionalInterface
|
- 元注解(用于注解其他注解)
1 2 3 4 5
| @Target @Retention @Documented @Inherited @Repeatable
|
注解分类
- 按保留策略(即声明注解的生命周期,@Retention)
- SOURCE:仅保留在源码中,编译时丢弃
- CLASS:保留到 class 文件,但 JVM 不加载(默认)
- RUNTIME:保留到运行时,可通过反射读取
- 按作用目标(即声明可以注解在哪些元素之上,@Target)
元素 |
说明 |
ElementType.TYPE |
注解在类、接口、枚举上 |
ElementType.FIELD |
注解在类中的字段上 |
ElementType.METHOD |
注解在类中的方法上 |
ElementType.PARAMETER |
注解在方法的参数前 |
ElementType.CONSTRUCTOR |
注解在类中构造方法上 |
ElementType.LOCAL_VARIABLE |
注解在局部变量前 |
ElementType.ANNOTATION_TYPE |
注解在注解上 |
ElementType.PACKAGE |
注解在包名前 |
组合注解
@Controller 注解用来配置访问路径等,@ResponseBody 注解用来表明不做视图渲染,直接展示方法的运行结果(一般是转成 json 返回),而@RestController 组合了两者的功能,可以配置访问路径,同时也可以直接展示方法的运行结果
1 2 3 4 5 6 7 8 9 10
| @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { @AliasFor(annotation = Controller.class) String value() default ""; }
|
使用案例
简化 Web 层配置,需求:统一 Controller 层的公共配置(如日志、权限校验)。
1 2 3 4 5 6
| @RestController @RequestMapping("/api/v1") @CrossOrigin @Tag(name = "API接口") public @interface RestApiController { }
|
1 2 3 4 5 6 7 8
| @RestApiController public class UserController { @GetMapping("/users") public List<User> getUsers() { } }
|
@AliasFor
显式别名
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { @AliasFor("path") String[] value() default {}; @AliasFor("value") String[] path() default {}; }
|
value 和 path 互为别名,互为别名的限制条件如下:
- 互为别名的属性其属性值类型、默认值,都是相同的。
- 互为别名的属性必须定义默认值。
- 互为别名的注解必须成对出现。
隐式别名
1 2 3 4 5 6 7 8 9 10 11 12
| @ContextConfiguration public @interface MyTestConfig {
@AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] value() default {};
@AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] groovyScripts() default {};
@AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] xmlFiles() default {}; }
|
因为 value、groovyScripts、xmlFiles 都定义了别名@AliasFor(annotation = ContextConfiguration.class, attribute = “locations”),所以 value、groovyScripts 和 xmlFiles 也互为别名。
传递式隐式别名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @MyTestConfig public @interface GroovyOrXmlTestConfig {
@AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts") String[] groovy() default {};
@AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] xml() default {}; }
@ContextConfiguration public @interface MyTestConfig {
@AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] groovyScripts() default {}; }
|
@AliasFor 注解是允许别名之间的传递的,简单理解,如果 A 是 B 的别名,并且 B 是 C 的别名,那么 A 是 C 的别名
因为 MyTestConfig 中的 groovyScripts 属性是 ContextConfiguration 中的 locations 属性的别名;所以 xml 属性和 groovy 属性也互为别名
自定义注解
继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class SynthesizedAnnotationTest { @Target({ ANNOTATION_TYPE, FIELD, TYPE }) @Retention(RUNTIME) @interface Test1 { String test1() default "test1"; } @Target({ ANNOTATION_TYPE, FIELD, TYPE }) @Retention(RUNTIME) @interface Test2 { String test2() default "test2"; } @Target({ ANNOTATION_TYPE, FIELD, TYPE }) @Retention(RUNTIME) @Test2 @interface Test3 { String test3() default "test3"; }
@Test3 static class Element {} public static void main(String[] args) { Test2 test2 = AnnotatedElementUtils.getMergedAnnotation(Element.class, Test2.class); System.out.println(test2); } }
|
覆盖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class SynthesizedAnnotationTest { @Target({ ANNOTATION_TYPE, FIELD, TYPE }) @Retention(RUNTIME) @interface Test1 { String test1() default "test1"; } @Target({ ANNOTATION_TYPE, FIELD, TYPE }) @Retention(RUNTIME) @interface Test2 { String test2() default "test2"; } @Target({ ANNOTATION_TYPE, FIELD, TYPE }) @Retention(RUNTIME) @Test2 @interface Test3 {
@AliasFor(annotation = Test2.class, attribute = "test2") String test3() default "test3"; }
@Test3(test3 = "覆盖Test2属性中的test2方法") static class Element {}
public static void main(String[] args) { Test2 test2 = AnnotatedElementUtils.getMergedAnnotation(Element.class, Test2.class); System.out.println(test2.test2()); } }
|
合并
1 2 3 4 5 6 7 8 9 10 11 12
| @Documented @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @A @B public @Interface C{ @AliasFor(annotation = A.class, attribute = "value") String aValue(); @AliasFor(annotation = B.class, attribute = "value") String bValue(); }
|