博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
自定义注解的理解及其应用
阅读量:4310 次
发布时间:2019-06-06

本文共 9193 字,大约阅读时间需要 30 分钟。

   在项目中经常会用到自定义注解,下面讲解一下自定义注解的理解及其应用。

一、元注解

  元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。

  Java5.0定义的元注解:

  • @Target
  • @Retention
  • @Documented
  • @Inherited

@Target

   作用:描述该注解修饰的范围,可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。

   取值(ElementType):

       CONSTRUCTOR:用于描述构造器;

       FIELD:用于描述域;

       LOCAL_VARIABLE:用于描述局部变量;

       METHOD:用于描述方法;

       PACKAGE:用于描述包;

       PARAMETER:用于描述参数;

      TYPE:用于描述类、接口(包括注解类型) 或enum声明;

@Retention

   作用:描述该注解的生命周期,表示在什么编译级别上保存该注解的信息。Annotation被保留的时间有长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)

  取值(RetentionPoicy):

      SOURCE:在源文件中有效(即源文件保留)

      CLASS:在class文件中有效(即class保留)     

      RUNTIME:在运行时有效(即运行时保留)

@Documented

    @Documented Annotation的作用是在生成javadoc文档的时候将该Annotation也写入到文档中

@Inherited

  作用:@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

二、自定义注解

  使用@interface自定义注解,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

定义注解格式:public @interface 注解名 {定义体}

注解参数的可支持数据类型:

  • 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
  • String类型
  • Class类型
  • enum类型
  • Annotation类型
  • 以上所有类型的数组

参数定义要点

  •  只能用public或默认(default)这两个访问权修饰;
  • 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组;
  • 如果只有一个参数成员,建议参数名称设为value();
  • 注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或负数作为默认值是一种常用的做法。

简单的自定义注解实例:

/** *自定义注解MyAnnotation */@Target(ElementType.TYPE) //目标对象是类型@Retention(RetentionPolicy.RUNTIME) //保存至运行时@Documented //生成javadoc文档时,该注解内容一起生成文档@Inherited //该注解被子类继承public @interface MyAnnotation {    public String value() default ""; //当只有一个元素时,建议元素名定义为value(),这样使用时赋值可以省略"value="    String name() default "devin"; //String    int age() default 18; //int    boolean isStudent() default true; //boolean    String[] alias(); //数组    enum Color {GREEN, BLUE, RED,} //枚举类型    Color favoriteColor() default Color.GREEN; //枚举值}@MyAnnotation(        value = "info",        name = "myname",        age = 99,        isStudent = false,        alias = {"name1", "name2"},        favoriteColor = MyAnnotation.Color.RED)public class MyClass {    //使用MyAnnotation注解,该类生成的javadoc文档包含注解信息如下:    /*    @MyAnnotation(value = "info", name = "myname", age = 99, isStudent = false, alias = {"name1","name2"}, favoriteColor = Color.RED)    public class MyClass    extends Object     */}public class MySubClass extends MyClass{    //子类MySubClass继承了父类MyClass的注解}

 解析注解信息

   Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。相应地,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素。

   实际上,java.lang.reflect 包所有提供的反射API扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

   AnnotatedElement接口是所有程序元素(Field、Method、Package、Class和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下七个方法来访问Annotation信息:

  1. <T extends Annotation> T getAnnotation(Class<T> annotationClass) :返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null;
  2. Annotation[] getDeclaredAnnotation(Class<T>):返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null;与此接口中的其他方法不同,该方法将忽略继承的注解;
  3. Annotation[] getAnnotations():返回该程序元素上存在的所有注解;
  4. Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注解;
  5. Annotation[] getAnnotationsByType(Class<T>):返回直接存在于此元素上指定注解类型的所有注解;
  6. Annotation[] getDeclaredAnnotationsByType(Class<T>):返回直接存在于此元素上指定注解类型的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注解;
  7. boolean isAnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false;
/***********注解声明***************//** * 水果名称注解 */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface FruitName {    String value() default " ";}/** * 水果颜色注解 */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface FruitColor {    /**     * 颜色枚举     */    public enum Color{BLUE, RED, GREEN};    /**     * 颜色属性     * @return     */    Color fruitColor() default Color.GREEN;}/** * 水果供应商注解 */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface FruitProvider {    /**     * 供应商编号     * @return     */    public int id() default -1;    /**     * 供应商名称     * @return     */    public String name() default " ";    /**     * 供应商地址     * @return     */    public String address() default " ";}/***********注解使用***************/public class Apple {    @FruitName("Apple")    private String appleName;    @FruitColor(fruitColor = FruitColor.Color.RED)    private String appleColor;    @FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西红富士大厦")    private String appleProvider;    public String getAppleProvider() {        return appleProvider;    }    public void setAppleProvider(String appleProvider) {        this.appleProvider = appleProvider;    }    public String getAppleName() {        return appleName;    }    public void setAppleName(String appleName) {        this.appleName = appleName;    }    public String getAppleColor() {        return appleColor;    }    public void setAppleColor(String appleColor) {        this.appleColor = appleColor;    }    public void displayName(){        System.out.println(getAppleName());    }}/***********注解信息获取***************/public class AnnotationParser {    public static void main(String[] args) {        Field[] fields = Apple.class.getDeclaredFields();        for (Field field : fields) {            //System.out.println(field.getName().toString());            if (field.isAnnotationPresent(FruitName.class)){                FruitName fruitName = field.getAnnotation(FruitName.class);                System.out.println("水果的名称:" + fruitName.value());            }else if (field.isAnnotationPresent(FruitColor.class)){                FruitColor fruitColor = field.getAnnotation(FruitColor.class);                System.out.println("水果的颜色:"+fruitColor.fruitColor());            }else if (field.isAnnotationPresent(FruitProvider.class)){                FruitProvider fruitProvider = field.getAnnotation(FruitProvider.class);                System.out.println("水果供应商编号:" + fruitProvider.id() + " 名称:" + fruitProvider.name() + " 地址:" + fruitProvider.address());            }        }    }}/***********输出结果***************/水果的名称:Apple水果的颜色:RED水果供应商编号:1 名称:陕西红富士集团 地址:陕西红富士大厦

JDK8注解新特性

   JDK 8 主要有两点改进:类型注解和重复注解

  • 类型注解:类型注解在@Target中增加了两个ElementType参数:

               1、ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中;

               2、ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中(例如声明语句、泛型和强制转换语句中的类型);

从而扩展了注解使用的范围,可以使用在创建类实例、类型映射、implements语句、throw exception声明中的类型前面。例如:

1、创建类实例: new @Interned MyObject();

2、类型映射:myString = (@NonNull String) str;

3、implements 语句中 :class UnmodifiableList<T> implements @Readonly List<@Readonly T> { ... }

4、throw exception声明:void monitorTemperature() throws @Critical TemperatureException { ... }

简单示例:

@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})public @interface Encrypted {}public class MyTypeAnnotation {    @Encrypted String data;    List<@Encrypted String> strings;}

 类型注解的作用:

   首先,局域变量声明中的类型注解也可以保留在类文件中,完整泛型被保留,并且在运行期可以访问,从而有助于我们获取更多的代码信息;其次,类型注解可以支持在的程序中做强类型检查。配合第三方工具check framework,可以在编译的时候检测出runtime error,以提高代码质量;最后,代码中包含的注解清楚表明了编写者的意图,使代码更具有表达意义,有助于阅读者理解程序,毕竟代码才是“最根本”的文档、“最基本”的注释。

重复注解

  重复注释就是运行在同一元素上多次使用同一注解,使用@Repeatable注解。之前也有重复使用注解的解决方案,但可读性不是很好,例如:

public @interface Authority {     String role();}public @interface Authorities {    Authority[] value();}public class RepeatAnnotationUseOldVersion {        @Authorities({@Authority(role="Admin"),@Authority(role="Manager")})    public void doSomeThing(){    }}

 而现在的实现如下:

@Repeatable(Authorities.class)public @interface Authority {     String role();}public @interface Authorities {    Authority[] value();}public class RepeatAnnotationUseNewVersion {    @Authority(role="Admin")    @Authority(role="Manager")    public void doSomeThing(){ }}

  不同的地方是,创建重复注解Authority时,加上@Repeatable,指向存储注解Authorities,在使用时候,直接可以重复使用Authority注解。从上面例子看出,java 8里面做法更适合常规的思维,可读性强一点。

参见:

 三、自定义注解的应用

(1)利用自定义注解打印接口调用时长日志

#创建ServiceLog类用来自定义注解@Retention(RetentionPolicy.Runtime)@Target(ElementType.METHOD)public @interface ServiceLog {}#定义ServiceLogAspect类用来定义日志打印信息@Component@Aspect public class ServiceLogAspect {   public ThreadLocal
local = new ThreadLocal
(); @Pointcut("@annotation(com.test.XXX.ServiceLong)") public void pointCut() { } @Before("pointCut()") public void before(JoinPoint point) { String methodName = point.getTarget().getClass().getName()+"."+point.getSignature().getName(); local.set(System.currentTimeMillis()); } @After("pointCut()") public void after(JoinPoint point) { long start = local.get(); String methodName = point.getTarget().getClass().getName()+"."+point.getSignature().getName(); System.out.println(System.currentTimeMillis()-start)); } @AfterThrowing(pointcut="pointCut()",throwing="error") public void throwing(JoinPoint point,Throwable error) { System.out.println("error"); }}

 完成上述定义,如果需要记录方法调用时长时,可以直接使用@ServiceLog注解。

转载于:https://www.cnblogs.com/moonandstar08/p/5975156.html

你可能感兴趣的文章
完美解决 error C2220: warning treated as error - no ‘object’ file generated
查看>>
使用SQL*PLUS,构建完美excel或html输出
查看>>
SQL Server数据库笔记
查看>>
X-Forwarded-For伪造及防御
查看>>
android系统平台显示驱动开发简要:LCD驱动调试篇『四』
查看>>
Android 高仿微信头像截取 打造不一样的自定义控件
查看>>
Jenkins的初级应用(1)-Publish Over SSH
查看>>
利用正则表达式群发定制邮件
查看>>
【原】RDD专题
查看>>
第三周——构建一个简单的Linux系统MenuOS
查看>>
Docker 的两类存储资源 - 每天5分钟玩转 Docker 容器技术(38)
查看>>
Codeforces 257D
查看>>
常用的20个强大的 Sublime Text 插件
查看>>
ajaxfileupload.js在IE中的支持问题
查看>>
tensorflow学习之(十)使用卷积神经网络(CNN)分类手写数字0-9
查看>>
当document.write里含有script标签时
查看>>
工作中常见问题
查看>>
JAVA 从一个List里删除包含另一个List的数据
查看>>
外国的月亮比较圆吗?外籍团队工作有感
查看>>
CentOS 关闭烦人的屏保
查看>>