当前位置: 首页 > news >正文

java中的注解

java中的注解

注解的作用

起初是用来标注程序的辅助信息,后面框架越来越多,生态越来越好,主要有如下作用:

  1. 代替xml中的配置作用(也不能完全替代),用注解做为配置更方便一些
  2. 通过注解设计一些通用的、增强原有逻辑的功能。自定义注解有个作用:被其他代码读到,所以经常需要配置【扫描的包】
  3. 自解释:所有的代码都有解释自己的功能,良好的命名规范可以少写或不写注释。
  • 注解的英文是 annotation,注解和泛型一样,是对代码添加的标记,注解作为标记的作用更加强大,注解可以被其它程序读取(这是区别于注释最直观的特点),以实现各种各样的功能和业务逻辑;
  • 随着 JDK 的演进,可以在几乎所有的地方使用注解。使用注解我们可以把一些附加的通用的方法「动态地」加在原有的代码上,而 不用修改原来的代码。这些附加的通用的方法可能是和原有的业务无关的方法,例如:统计代码的执行时间、记录日志等;也可能与原有业务有关,例如:自动化生成get,set方法,构造器等。

注解的标记功能

我们最早接触的注解可能是 @Override ,它修饰在方法上,被注解 @Override 修饰的方法表示:此方法覆盖了父类(或者父类的父类)的某个方法。有了 @Override 注解,如果父类的方法名、参数个数、参数类型、返回值类型和原方法不一致,编译器就会报错,如果不加@Override 注解且参数个数、参数类型、返回值类型和原方法不一致,那编译器就认为这是一个方法重载。所以注解在这里有两个作用:

  • 给人看:父类也有这个方法,在子类被重写了;
  • 给编译器看:帮我检查一下,我是不是重写对了。

因此,注解的一个作用是给代码加上的标记,是对代码内容的说明。设计注解是为了设计更通用的功能,常见的框架的设计有很大一部分是通过==「自定义注解 + 反射」==实现强大功能的。

JDK提供的常用注解

  • @Override,标记方法重写
  • @Deprecated,标记弃用,表示不再建议使用的方法或类
  • @SuppressWarnings,用于抑制编译器产生特定类型的警告信息
  • @FunctionlInterface,声明函数式接口,标注在接口上,表示这是一个【函数式接口】(即只有一个待实现的抽象方法的接口)

元注解

元注解就是解释注解的注解,一共有5个:TargetRetention,DocumentedInheritedRepeatable,最常用的是前两个,但掌握所有的元注解用法是高阶玩家的必修课

@Target

作用:声明被修饰的注解在哪些位置使用,它可以传入值来具体指定使用位置,取值定义在ElementType 枚举中

  • ElementType.TYPE:可以应用于类、接口(包括注解类型)、枚举。
  • ElementType.FIELD:可以应用于字段(成员变量)。
  • ElementType.METHOD:可以应用于方法。
  • ElementType.PARAMETER:可以应用于方法参数。
  • ElementType.CONSTRUCTOR:可以应用于构造器。
  • ElementType.LOCAL_VARIABLE:可以应用于局部变量。
  • ElementType.ANNOTATION_TYPE:可以应用于其他注解。
  • ElementType.PACKAGE:可以应用于包声明。

为显式声明,这可在任意元素上标注。

@Retention

作用:指定被修饰的注解的保留级别,即注解的存活周期。它有三个取值,定义在RetentionPolicy 枚举中。

  • RetentionPolicy.SOURCE:注解只在源文件(.java)中存在,编译时会被丢弃,不会保留在字节码文件(.class)中。
  • RetentionPolicy.CLASS:默认值,注解会保留在字节码文件中,但在运行时 JVM 不会读取。
  • RetentionPolicy.RUNTIME:注解不仅会保留在字节码文件中,在运行时 JVM 也能够读取到注解的信息。这使得我们可以在运行时通过反射机制获取注解并进行相应的处理。例如,Spring 框架中用于依赖注入的@Autowired注解就使用了这个策略,以便在运行时根据注解信息进行依赖注入操作。

@Repeatable

作用:标识一个注解可以在同一个元素(类、方法、字段等)上重复使用

在 Java 8 之前,同一个注解默认不能在同一个元素上重复标注。如果需要对一个元素多次使用同类型注解,通常需要借助一个 “容器注解” 来包裹多个同类型注解,使用起来不够直观

@Repeatable 的出现解决了这个问题,它的作用是声明某个注解是可重复的,并指定该注解对应的 “容器注解”(用于在底层存储重复的注解实例)。

  1. 定义可重复的注解,并通过 @Repeatable 指定容器注解:

    import java.lang.annotation.Repeatable;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;// 声明该注解可重复,容器注解为 Roles.class
    @Repeatable(Roles.class)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Role {String value();
    }
    
  2. 定义容器注解(用于存储重复的 @Role 注解):

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)
    public @interface Roles {// 容器注解必须包含一个返回该可重复注解数组的方法Role[] value();
    }
    
  3. 重复使用注解

    // 直接重复使用 @Role 注解(无需显式包裹在 @Roles 中)
    @Role("admin")
    @Role("user")
    public class User {
    }
    
  4. 获取重复的注解

    运行时通过反射获取注解时,会自动封装到容器注解中,可通过容器注解获取所有重复的实例:

    public class Main {public static void main(String[] args) {Roles roles = User.class.getAnnotation(Roles.class);for (Role role : roles.value()) {System.out.println(role.value()); // 输出 "admin" 和 "user"}}
    }
    

核心要点

  • @Repeatable参数是 “容器注解” 的 Class 对象,容器注解必须包含一个返回可重复注解数组的方法(通常命名为 value())。
  • 重复标注的注解在编译后会被自动封装到容器注解中,反射获取时需通过容器注解获取所有实例。

@Documented

作用:表示被修饰的注解会被javadoc工具提取成文档。如果一个注解a标注了该注解,那么在生成API文档时,使用了注解a的元素的文档会包含这个注解的信息。

@Inherited

作用:指定被修饰的注解具有继承性。如果一个类使用了被Inherited修饰的注解,那么它的子类也会自动继承这个注解

需要注意的是,该注解仅对类有效,对接口无效

自定义注解

基本定义与使用

格式

public @interface 注解名{public 类型 属性名() default 默认值;
}
  • 注解本质上也是一个类,每次使用注解都将产生对应注解类的实例

  • 注解这个特殊类使用@interface修饰,命名规则和类的命名规则一致,使用时在前面加上@号,编译后的文件是.class文件。

示例

定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {// 注解元素,带有默认值String value() default "";int count() default 1;
}

使用注解

  1. 使用注解:

    在符合注解目标(如上述示例中的方法)上使用自定义注解,并可以为注解元素赋值(如果没有使用默认值)。

public class AnnotationUsageExample {@MyAnnotation(value = "example", count = 3)  //以键值对的方式为注解属性赋值public void myMethod() {System.out.println("This is my method.");}
}

当注解只有一个value属性时,使用注解时,value的名称可以省略,如上,当MyAnnotation注解只有value属性时,使用注解时,可以直接写成:@MyAnnotation("example")

注解的解析

注解是程序的标记,要想获得注解信息,必须先获得被注解的程序对象。java的反射机制提供了这样的功能。

运行时解析注解

当注解的@Retention的为RetentionPolicy.RUNTIME时(存活到运行时),可以使用反射机制在运行时获取和解析注解。

要想解析谁身上的注解,就应该先拿到谁Class,Method,Field,Constructor都实现了AnnotatedElement接口,都拥有解析注解的能力。

解析注解API

AnnotatedElement接口提供的常用解析注解的方法:

方法描述
isAnnotationPresent(Class<? extends Annotation> annotationClass)判断当前元素上是否存在指定注解实例
Annotation[] getAnnotations()返回当前元素上存在的所有注解实例(重复注解返回的是容器注解实例)
Annotation[] getDeclaredAnnotations()返回当前元素上存在的所有显式声明的注解实例(重复注解返回的是容器注解实例)
<T extends Annotation>T getAnnotation(Class<T> annotationClass)返回当前元素上存在的指定注解实例,不存在返回null
<T extends Annotation>T getDeclaredAnnotation(Class<T> annotationClass)返回当前元素上存在的显式声明的指定注解实例,不存在返回null
<T extends Annotation>T[] getAnnotationsByType(Class<T> annotationClass)返回当前元素上存在的所有注解实例,包括重复注解的所有实例
<T extends Annotation>T[] getDeclaredAnnotationsByType(Class<T> annotationClass)返回当前元素上存在的所有显式声明的注解实例,包括重复注解的所有实例

Annotation实现类的实例方法:

方法描述
Class<? extends Annotation> annotationType()返回此注解实例的类型
equals(Object obj)逻辑判断
int hashCode()返回hash值
String toString()返回字符串表示
案例上手
import java.lang.reflect.Method;public class AnnotationParserExample {@MyAnnotation(value = "example", count = 3)  //以键值对的方式为注解实例属性赋值public void myMethod() {System.out.println("This is my method.");}public static void main(String[] args) {try {Class<?> clazz = AnnotationUsageExample.class;  //获取当前类的Class对象Method method = clazz.getMethod("myMethod");  //获取myMethod方法//获取方法上的@MyAnnotation注解MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);  if (myAnnotation!= null) {System.out.println("Value: " + myAnnotation.value());  // 访问注解的value属性System.out.println("Count: " + myAnnotation.count());  // 访问注解的Count属性}} catch (NoSuchMethodException e) {e.printStackTrace();}}
}
深入探究

是否获取继承性注解的实例

可以发现上述AnnotatedElement接口提供的常用解析注解的方法中,核心是通过程序元素(类,方法,字段)获取其标注的注解实例,每个获取注解实例的方法,都有对应的获取显式声明的注解实例的方法(方法名特点是:Declared),它们的核心区别是是否包含继承的注解(仅限标注在类上的注解,且该注解要用元注解@Inherited修饰)。

getAnnotations()getDeclaredAnnotations()区别为例

前面提到@Inherited可以让注解随着类的继承而继承,getAnnotations()可以获取到所有注解实例(包括继承而来的),getDeclaredAnnotations()获取到所有显式声明的注解实例(不包括继承而来的

//没有继承性的注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Capacity{String value();
}//有继承性的注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {String value();
}
@MyAnnotation("fun")
public class TestUser {
}@Capacity("son")
public class TestUserSon extends TestUser {
}
public class Main {public static void main(String[] args) throws CloneNotSupportedException {Annotation[] anns1 = TestUserSon.class.getAnnotations();Annotation[] anns2 = TestUserSon.class.getDeclaredAnnotations();for(Annotation ann : anns1){System.out.println(ann.annotationType());}System.out.println("-----------------");for(Annotation ann : anns2){System.out.println(ann.annotationType());}}
}
/*输出:
interface com.gezishan.analysis.MyAnnotation
interface com.gezishan.analysis.Capacity
-----------------
interface com.gezishan.analysis.Capacity
*/

是否获取全部重复性注解实例

jdk8之后对重复注解做了有力支持,通过getAnnotationsByTypegetDeclaredAnnotationsByType(方法名的特点是:后缀为ByType)可获取重复性注解的所有实例。

getAnnotations()getAnnotationsByType()区别为例

同样是返回所有注解实例

  • getAnnotations(),返回所有标注在此元素上的注解,对于重复注解,返回的是重复性注解的容器注解的实例(对于容器注解的内容见上:@Repeatable)。
  • getAnnotationsByType返回指定重复性注解的所有实例。
// 可重复的注解
@Repeatable(Roles.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Role {String value();
}// 容器注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Roles {Role[] value();
}//
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Capacity{String value();
}
@Role("admin")
@Role("user")
@Capacity("stu")
public class TestUser {
}
public class Main {public static void main(String[] args) throws CloneNotSupportedException {Annotation[] anns1 = TestUser.class.getAnnotations();Annotation[] anns2 = TestUser.class.getAnnotationsByType(Role.class);  //指定重复性注解for(Annotation ann : anns1){System.out.println(ann.annotationType());}System.out.println("-----------------");for(Annotation ann : anns2){System.out.println(ann.annotationType());}}
}
/*输出:
interface com.gezishan.analysis.Roles
interface com.gezishan.analysis.Capacity
-----------------
interface com.gezishan.analysis.Role
interface com.gezishan.analysis.Role
编译时注解解析

编译时注解处理允许在编译期间根据注解生成代码进行检查。这需要创建一个实现 javax.annotation.processing.Processor 接口的处理器类,并注册该处理器。

相关概念

  • 注解处理器(Processor):实现 javax.annotation.processing.Processor 接口的类,负责处理特定的注解。处理器会在编译期间被调用,对使用了特定注解的代码元素进行处理
  • 抽象处理器(AbstractProcessor):一个抽象类,实现了 Processor 接口,为开发者提供了一个更方便的基础类来编写自定义处理器。通常,自定义处理器会继承 AbstractProcessor 类并实现其 process 方法(处理源自前一轮的类型元素的一组注解类型,并返回此处理器是否声明了这些注解类型。)。
  • 处理轮次(Round):编译过程中,注解处理器可能会被多次调用,每一次调用称为一个处理轮次。在每一轮处理中,处理器可以处理新发现的注解。如果在某一轮处理中生成了新的源文件,那么可能会触发新的一轮处理。

定义注解处理器

  • 继承 AbstractProcessor 类。
  • 使用 @SupportedAnnotationTypes 注解指定该处理器处理的注解类型。
  • 使用 @SupportedSourceVersion 注解指定支持的 Java 源版本。

以下是一个简单的编译时注解处理器示例框架:

@SupportedAnnotationTypes("MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {for (Element element : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {MyAnnotation myAnnotation = element.getAnnotation(MyAnnotation.class);// 在这里进行编译时的处理逻辑,如生成代码或检查processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Processing MyAnnotation on " + element);}return true;}
}

编译时的注解解析的内容还是比较多的,包括:

  • 获取处理环境ProcessingEnvironment 提供了很多有用的工具和信息,例如 Messager 用于报告错误、警告和其他信息,Filer 用于生成新的源文件或资源文件或代码等。

  • 处理注解方法,process 方法是注解处理器的核心。它接收两个参数:annotations 是这一轮要处理的注解类型集合,roundEnv 提供了对当前处理轮次中使用了这些注解的元素的访问。在 process 方法中,可以遍历使用了特定注解的元素,并根据注解的信息进行相应的处理。

  • 服务加载机制。

  • 构建工具集成。


如果说运行时通过反射解析注解是通过程序对象找到注解,那编译时通过process方法解析注解就是通过注解找到被注解的程序对象

更多详情就不在这里叙述了(作者自己还没完全搞懂…),后面有机会再研究。

http://www.dtcms.com/a/488690.html

相关文章:

  • 手撕大模型 | MQA 和 GQA 原理解析
  • 爱站网长尾关键词挖掘工具电脑版wordpress链接的图片保存到本地
  • 站长平台怎么做网站漂亮的学校网站模板下载
  • 户型图在哪个网站找免费发外链的网站
  • .net 大型网站开发合肥seo优化公司
  • 效果图网站推荐大全面包砖dedecms怎么制作网站
  • 网站的流量是什么意思wordpress去除更新
  • 制作一个网页游戏需要多少钱成都企业网站优化服务
  • 学ui设计网站如何变更网站备案信息查询
  • 虚拟产品货源网站新浪网页编辑器
  • php与mysql网站开发免费永久个人服务器
  • 黄冈网站网站设计要多久
  • 网站标题优化工具建e网手机app
  • ssc网站建设教程网站怎么做伪静态iis7.0
  • 网站建设企业服务两台wordpress共享mysql
  • 做网站合同山东手机网站建设
  • 学校网站制作代码购物网站建设项目可研报告
  • 网站描述修改网站开发工作介绍
  • 网站一直维护意味着什么网站建立费用多少钱
  • asp.net 4.0网站开发与项目实战 pdf怎么做动漫网站
  • wordpress+随机播放河南网站优化要多少钱
  • 什么样的网站高大上网站建设有多少公司
  • 孟村做网站价格祁县建设局网站
  • 建设网站有哪些公司西宁市建设局官方网站
  • QT登陆界面
  • 网站设计费用一览表企业网站属于广告吗
  • 小说网站怎么做空间小大连图书馆网站建设要求
  • 网站导航页面制作手表网站欧米茄价格
  • 网站建设丽水wordpress图集功能
  • 赣州制作网站企业wordpress设置新窗口打开