20.自定义注解

本章目标

  1. 简介
  2. 自定义注解(掌握)

本章内容

一、简介

注解(Annotation)是Java SE 5.0 版本开始引入的概念,它是对 Java 源代码的说明,是一种元数据(描述数据的数据)

1、Java注解定义

Java注解,是Java语言中引入的一种元数据,是附加在代码中的一些一些特殊标记。它提供了一种将元数据和程序代码组合在一起的方式。用于在编译、类加载、运行时进行解析和使用,并执行相应的处理,起到说明、校验、配置的功能。

元数据从 metadata 一词译来,是描述数据的数据,它不会影响程序的运行,但可以提供额外的信息,例如Java类的版本号、作者、方法的参数名称和类型等。

Java注解有助于将程序元数据与源代码分离开来,从而简化程序设计,并提供了一种在程序运行时动态处理元数据的方式。Java注解就像修饰符一样被使用,并应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在注解的 “name=value” 结构对中,从而为程序的设计和实现提供了更多的灵活性和可扩展性。

如果说注释是写给人看的,那么注解就是写给程序看的。它更像一个标签,贴在一个类、一个方法或者字段上。它的目的是为当前读取该注解的程序提供判断依据。

2、Java注解的作用

  1. 提供更多的程序元数据。Java注解提供了一种将元数据与程序代码组合在一起的方式,从而可以为程序提供更多的信息,例如Java类的版本号、作者、方法的参数名称和类型等。
  2. 简化程序设计。Java注解可以使程序的设计更加简单,因为它实现了将元数据和程序代码分离开来的目的,从而程序员可以专注于程序代码的设计和实现。
  3. 提供更好的可读性。通过使用Java注解,程序员可以更加清晰地表达程序的意图和目的,从而增强程序的可读性。
  4. 实现自动化的代码生成。Java注解可以为程序自动生成一些代码,从而减少程序员的工作量,提高程序的开发效率。
  5. 提供更加灵活的程序实现。Java注解可以应用到Java类、方法、属性、参数等元素上,从而为程序的设计和实现提供了更加灵活和可扩展的方式

3、Java注解应用

  1. 生成文档这是最常见的,也是java 最早提供的注解;
  2. 在编译时进行格式检查,如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;
  3. 跟踪代码依赖性,实现替代配置文件功能,比较常见的是spring 2.5 开始的基于注解配置,作用就是减少配置;
  4. 在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口,可以在反射中解析并使用 Annotation。
范围 作用 常见注解
类和属性 描述版本号、作者、属性的名称和类型的元数据 @param、@return、@author、@version
方法和参数 方法的返回值类型、参数的名称和类型等元数据 @Override、@Deprecated
包和模块 模块的名称、版本号、依赖关系等元数据 @Service、@Component
框架和插件 框架的配置文件、插件的依赖关系等元数据 @Configuration
测试和文档 测试用例的名称、文档的标题等元数据 @Test

4、Java注解分类

  1. Java自带的标准注解:包括@Override、@Deprecated(用来表示不赞成使用,标记已经过时)、@SuppressWarnings(抑制警告)等,使用这些注解后编译器就会进行检查。

  2. 元注解:元注解是用于定义注解的注解,包括@Retention、@Target、@Inherited、@Documented、@Repeatable 等。

  3. 自定义注解:用户可以根据自己的需求定义注解。

    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());
             }
         }
 
     }
 
 }

思维导图

image

posted @ 2025-04-09 14:32  icui4cu  阅读(23)  评论(0)    收藏  举报