企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # 什么是注解 注解就是符合一定格式的语法 @xxxx 注解作用: 注释:在阅读程序时清楚----给程序员看的 注解:给jvm看的,给机器看的 注解在目前而言最主流的应用:代替配置文件 关于配置文件与注解开发的优缺点: 注解优点:开发效率高 成本低 注解缺点:耦合性大 并且不利于后期维护 # 注解的应用结构图 注解就相当于一个你的源程序中要调用的一个类,要在源程序中应用某个注解,得先准备好了这个注解类,就像你要调用某个类,得先要开发好这个类 ![](https://box.kancloud.cn/daa3919053b55de2763885e274800e50_614x247.png) # jdk5提供的注解 ~~~ @Override:告知编译器此方法是覆盖父类的 @Deprecated:标注过时 @SuppressWarnings:压制警告 deprecation, 忽略过时 rawtypes 忽略类型安全 unused 忽略不使用 unchecked 忽略安全检查 null 忽略空指针 all 忽略所有 ~~~ # 自定义注解 ## 编写一个注解 关键字:@interface 注解的属性: 语法:返回值 名称(); 注意:如果属性的名字是value,并且注解的属性值有一个 那么在使用注解时可以省略value ~~~ public @interface MyAnno{ String value(); int age() default 28; } ~~~ 注解属性类型只能是以下几种 1. 基本类型 2. String 3. 枚举类型 4. 注解类型 5. Class类型 6. 以上类型的一维数组类型 ## 使用注解 在类/方法/字段 上面是@XXX ~~~ @MyAnno("zhansan") public class TestAnno { } ~~~ ## 解析使用了注解的类 介入一个概念:元注解:代表修饰注解的注解,作用:限制定义的注解的特性 ### `@Retention` ~~~ SOURCE: 注解在源码级别可见 CLASS:注解在字节码文件级别可见 RUNTIME:注解在整个运行阶段都可见 ~~~ 如果`@Retention`不存在,则该`Annotation`默认为`CLASS` ![](https://box.kancloud.cn/16e2af0406570f1dd7f2b295184e7c9c_838x334.png) ### `@Target` ~~~ 代表注解修饰的范围:类上使用,方法上使用,字段上使用 FIELD:字段上可用此注解 METHOD:方法上可以用此注解 TYPE:类/接口上可以使用此注解 CONSTRUCTOR: 构造方法上使用 PARAMETER: 方法参数上使用 ~~~ ### `@Repeatable` 使用`@Repeatable`这个元注解可以定义`Annotation`是否可重复。这个注解应用不是特别广泛。 ~~~ @Repeatable @Target(ElementType.TYPE) public @interface Report { int type() default 0; String level() default "info"; String value() default ""; } ~~~ 经过`@Repeatable`修饰后,在某个类型声明处,就可以添加多个`@Report`注解: ~~~ @Report(type=1, level="debug") @Report(type=2, level="warning") public class Hello { } ~~~ ### `@Inherited` 使用`@Inherited`定义子类是否可继承父类定义的`Annotation`。`@Inherited`仅针对`@Target(ElementType.TYPE)`类型的`annotation`有效,并且仅针对`class`的继承,对`interface`的继承无效: ~~~ @Inherited @Target(ElementType.TYPE) public @interface Report { int type() default 0; String level() default "info"; String value() default ""; } ~~~ 在使用的时候,如果一个类用到了`@Report`: ~~~ @Report(type=1) public class Person { } ~~~ 则它的子类默认也定义了该注解: ~~~ public class Student extends Person { } ~~~ ### 例子 ~~~ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnno { //注解的属性 String name(); int age() default 28; } ~~~ 注意:要想解析使用了注解的类 , 那么该注解的Retention必须设置成Runtime 关于注解解析的实质:从注解中解析出属性值 字节码对象存在于获得注解相关的方法 ~~~ isAnnotationPresent(Class<? extends Annotation> annotationClass) : 判断该字节码对象身上是否使用该注解了 getAnnotation(Class<A> annotationClass) :获得该字节码对象身上的注解对象 ~~~ ~~~ @MyAnno(name = "zhangsan") public class MyAnnoTest { @SuppressWarnings("all") @MyAnno(name = "zhangsan") //@MyAnno({ "aaa","bbb","ccc"}) public void show(String str){ System.out.println("show running..."); } } ~~~ ~~~ //解析show方法上面的@MyAnno //直接的目的是 获得show方法上的@MyAnno中的参数 //获得show方法的字节码对象 Class clazz = MyAnnoTest.class; Method method = clazz.getMethod("show", String.class); //获得show方法上的@MyAnno MyAnno annotation = method.getAnnotation(MyAnno.class); //获得@MyAnno上的属性值 System.out.println(annotation.name());//zhangsan System.out.println(annotation.age());//28 //根据业务需求写逻辑代码 ~~~ # `@NonNull和@Nullable` 用 `@Nullable` 和 `@NotNull` 注解来显示表明可为空的参数和以及返回值。 这样就够在编译的时候处 理空值而不是在运行时抛出 NullPointerExceptions。 # 处理注解 因为注解定义后也是一种`class`,所有的注解都继承自`java.lang.annotation.Annotation`,因此,读取注解,需要使用反射API。 Java提供的使用反射API读取`Annotation`的方法包括: 判断某个注解是否存在于`Class`、`Field`、`Method`或`Constructor`: * `Class.isAnnotationPresent(Class)` * `Field.isAnnotationPresent(Class)` * `Method.isAnnotationPresent(Class)` * `Constructor.isAnnotationPresent(Class)` 例如: ~~~ // 判断@Report是否存在于Person类: Person.class.isAnnotationPresent(Report.class); ~~~ 使用反射API读取Annotation: * `Class.getAnnotation(Class)` * `Field.getAnnotation(Class)` * `Method.getAnnotation(Class)` * `Constructor.getAnnotation(Class)` 例如: ~~~ // 获取Person定义的@Report注解: Report report = Person.class.getAnnotation(Report.class); int type = report.type(); String level = report.level(); ~~~ 使用反射API读取`Annotation`有两种方法。方法一是先判断`Annotation`是否存在,如果存在,就直接读取: ~~~ Class cls = Person.class; if (cls.isAnnotationPresent(Report.class)) { Report report = cls.getAnnotation(Report.class); ... } ~~~ 第二种方法是直接读取`Annotation`,如果`Annotation`不存在,将返回`null`: ~~~ Class cls = Person.class; Report report = cls.getAnnotation(Report.class); if (report != null) { ... } ~~~ 读取方法、字段和构造方法的`Annotation`和Class类似。但要读取方法参数的`Annotation`就比较麻烦一点,因为方法参数本身可以看成一个数组,而每个参数又可以定义多个注解,所以,一次获取方法参数的所有注解就必须用一个二维数组来表示。例如,对于以下方法定义的注解: ~~~ public void hello(@NotNull @Range(max=5) String name, @NotNull String prefix) { } ~~~ 要读取方法参数的注解,我们先用反射获取`Method`实例,然后读取方法参数的所有注解: ~~~ // 获取Method实例: Method m = ... // 获取所有参数的Annotation: Annotation[][] annos = m.getParameterAnnotations(); // 第一个参数(索引为0)的所有Annotation: Annotation[] annosOfName = annos[0]; for (Annotation anno : annosOfName) { if (anno instanceof Range) { // @Range注解 Range r = (Range) anno; } if (anno instanceof NotNull) { // @NotNull注解 NotNull n = (NotNull) anno; } } ~~~