20.自定义注解
本章目标
- 简介
- 自定义注解(掌握)
本章内容
一、简介
注解(Annotation)是Java SE 5.0 版本开始引入的概念,它是对 Java 源代码的说明,是一种元数据(描述数据的数据)
1、Java注解定义
Java注解,是Java语言中引入的一种元数据,是附加在代码中的一些一些特殊标记。它提供了一种将元数据和程序代码组合在一起的方式。用于在编译、类加载、运行时进行解析和使用,并执行相应的处理,起到说明、校验、配置的功能。
元数据从 metadata 一词译来,是描述数据的数据,它不会影响程序的运行,但可以提供额外的信息,例如Java类的版本号、作者、方法的参数名称和类型等。
Java注解有助于将程序元数据与源代码分离开来,从而简化程序设计,并提供了一种在程序运行时动态处理元数据的方式。Java注解就像修饰符一样被使用,并应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在注解的 “name=value” 结构对中,从而为程序的设计和实现提供了更多的灵活性和可扩展性。
如果说注释是写给人看的,那么注解就是写给程序看的。它更像一个标签,贴在一个类、一个方法或者字段上。它的目的是为当前读取该注解的程序提供判断依据。
2、Java注解的作用
- 提供更多的程序元数据。Java注解提供了一种将元数据与程序代码组合在一起的方式,从而可以为程序提供更多的信息,例如Java类的版本号、作者、方法的参数名称和类型等。
- 简化程序设计。Java注解可以使程序的设计更加简单,因为它实现了将元数据和程序代码分离开来的目的,从而程序员可以专注于程序代码的设计和实现。
- 提供更好的可读性。通过使用Java注解,程序员可以更加清晰地表达程序的意图和目的,从而增强程序的可读性。
- 实现自动化的代码生成。Java注解可以为程序自动生成一些代码,从而减少程序员的工作量,提高程序的开发效率。
- 提供更加灵活的程序实现。Java注解可以应用到Java类、方法、属性、参数等元素上,从而为程序的设计和实现提供了更加灵活和可扩展的方式
3、Java注解应用
- 生成文档这是最常见的,也是java 最早提供的注解;
- 在编译时进行格式检查,如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;
- 跟踪代码依赖性,实现替代配置文件功能,比较常见的是spring 2.5 开始的基于注解配置,作用就是减少配置;
- 在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口,可以在反射中解析并使用 Annotation。
范围 | 作用 | 常见注解 |
---|---|---|
类和属性 | 描述版本号、作者、属性的名称和类型的元数据 | @param、@return、@author、@version |
方法和参数 | 方法的返回值类型、参数的名称和类型等元数据 | @Override、@Deprecated |
包和模块 | 模块的名称、版本号、依赖关系等元数据 | @Service、@Component |
框架和插件 | 框架的配置文件、插件的依赖关系等元数据 | @Configuration |
测试和文档 | 测试用例的名称、文档的标题等元数据 | @Test |
4、Java注解分类
-
Java自带的标准注解:包括@Override、@Deprecated(用来表示不赞成使用,标记已经过时)、@SuppressWarnings(抑制警告)等,使用这些注解后编译器就会进行检查。
-
元注解:元注解是用于定义注解的注解,包括@Retention、@Target、@Inherited、@Documented、@Repeatable 等。
-
自定义注解:用户可以根据自己的需求定义注解。
package com.woniuxy.annotation; import java.io.Serializable; @SuppressWarnings(“serial”) public class Person implements Serializable{ //*@Override public void eat() { }* @Override public String toString() { return “hello”; } @Deprecated public void eat() { System.out.println(); } public static void main(String[] args) { Person person = new Person(); person.eat(); } }
5、元注解
定义注解时使用的注解,称为元注解
@Target,@Retention,@Documented,@Inherited
5.1、@Target
@Target 表示该注解用于什么地方,可能的 ElemenetType 参数包括:
java.lang.annotation.ElementType
参数 | 说明 |
---|---|
CONSTRUCTOR | 构造器声明 |
FIELD | 域声明(包括 enum 实例) |
LOCAL_VARIABLE | 局部变量声明 |
METHOD | 方法声明 |
PACKAGE | 包声明 |
PARAMETER | 参数声明 |
TYPE | 类,接口(包括注解类型)或enum声明 |
5.2、@Retention(保留)
表示在什么级别保存该注解信息。可选的 RetentionPolicy 参数包括:
参数 | 说明 |
---|---|
SOURCE | 源码时保留,将被编译器丢弃 |
CLASS | 编译时保留,将由编译器记录在类文件中,但JVM不需要在运行时保留 |
RUNTIME | 运行时保留,运行中可以处理,因此可以反射性地读取 |
5.3、@Documented
如果用javadoc生成文档时,想把注解也生成文档,就带这个
5.4、 @Inherited
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。
如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
二、自定义注解
1、语法
Annotation是所有注解类型的公共扩展接口。
自定义注解通过使用@interface
来实现,它自动继承了java.lang.annotation.Annotation接口,语法如下:
修饰符 @interface 注解名 {
type elementName();
type elementName() default value;
}
- 修饰符: 访问修饰符必须为public,不写默认为pubic
- 关键字: 关键字为@interface,并且该注解不能再去继承别的类或是接口
- 注解名称: 要求合法的Java标识符,注解名称为自定义注解的名称,使用时还会用到
- 成员属性:定义为为无参方法的形式
- 成员类型:成员类型只能用基本类型 byte、short、char、int、long、float、double、boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组,不能为void类型。
- 成员调用:必须要为给所有成员赋值,除非属性指定了默认值
- value属性:当只有value属性时,调用时可以省略value属性,直接赋值
2、自定义案例
2.1、声明一个注解MyAnnotation
没有加Target之前 ,该注解可以放到各类的各个地方,可以单独创建一个类体现一下,如果只有一个值时{}可省略
Retention保留的生命周期:源码→Class 文件→运行时
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation {
String name() default "tom";
int age();
}
底层实现原理:实际上建了一个接口MyAnnotation继承自Annotation,并在接口中定义了两个抽象方法name(),age()
public interface com.woniuxy.annotation.MyAnnotation extends java.lang.annotation.Annotation
2.2、给Employee类添加注解
import com.woniuxy.annotation.MyAnnotation;
@MyAnnotation(name="merry",age=22)
public class Employee implements Serializable {
/**
* 0
*/
private static final long serialVersionUID = 1L;
private int empId;
private String name;
private int age;
public Employee() {
// TODO Auto-generated constructor stub
}
@MyAnnotation(age=20)
public int getEmpId() {
return empId;
}
public int setName(@MyAnnotation String name) {
this.name = name;
}
……
}
2.3、测试
可以分别@Retention为不同值的时候的情况,通过命令javap -v xxx.class可以查看字节码文件的变化情况
-
SOURCE
-
CLASS
三、获取注解中内容
如何获取程序中注解中的内容
RetentionPolicy
必须为RUNTIME
1、前提
在Java程序中可以通过反射机制来获取指定程序元素的 Annotation对象,然后通过 Annotation对象 来获取注解里面的元数据。
要获得程序中注解的内容需要注意:
- 某个类标注了注解,那么这个类就是Annotation的一个对象
- 通过反射来获取注解的值时,要求该注解的
RetentionPolicy
必须为RUNTIME,因为java反射是在程序运行时获取字节码的内容。
2、AnnotatedElement
接口
表示当前在此JVM中运行的程序的注解元素, 该界面允许反射读取注解。
Java在java.lang.reflect
包下有一个接口AnnotatedElement
,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类:
- Class:类定义
- Constructor:构造器定义
- Field:累的成员变量定义
- Method:类的方法定义
- Package:类的包定义
AnnotatedElement
接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement
对象之后,程序就可以调用该对象的如下方法来访问Annotation信息:
3.2、常用方法
返回类型 | 方法 | 描述 |
---|---|---|
<T extends Annotation>T |
getAnnotation(类<T> annotationClass) |
返回该元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null |
Annotation[] |
getAnnotations() |
返回该程序元素上存在的所有注解 |
default <T extends Annotation>T |
getDeclaredAnnotation(类<T> annotationClass) |
返回该元素上的指定类型的注解,忽略继承的注解 |
Annotation[] |
getDeclaredAnnotations() |
返回直接存在于此元素上的所有注解,忽略继承的注解 |
default boolean |
isAnnotationPresent(类<? extends Annotation> annotationClass) |
如果此元素上 存在指定类型的注解,则返回true,否则返回false。 |
3.3、示例
分别得到类和方法上的注解元素上的值
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Class cl = Class.forName("com.woniuxy.entity.Employee");
//得到类上的注解信息
//如果此元素上 存在指定类型的注释,则返回true,否则返回false
if(cl.isAnnotationPresent(MyAnnotation.class)) {
//返回该元素的,如果这样的注释 ,否则返回null指定类型的注释
MyAnnotation ma = (MyAnnotation)cl.getAnnotation(MyAnnotation.class);
System.out.println(ma.age());
System.out.println(ma.name());
}else {
System.out.println("该类上没有这种类型的注解");
}
//得到方法上的注解信息
Method[] methods = cl.getMethods();
for (Method method : methods) {
if(method.isAnnotationPresent(MyAnnotation.class)) {
System.out.println(method.getName());
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println(annotation.age());
System.out.println(annotation.name());
}
}
}
}
思维导图
本文来自博客园,作者:icui4cu,转载请注明原文链接:https://www.cnblogs.com/icui4cu/p/18816575