> 文档中心 > Java注解浅析一二

Java注解浅析一二

文章目录

  • Java注解
    • 1. 内置注解
    • 2. 通过内置注解了解元注解
    • 3. 元注解
      • 3.1 @Target
      • 3.2 @Retention
      • 3.3 @Documented
      • 3.4 @Inherited
      • 3.5 @Repeatable (Java8)
      • 3.6 @Native (Java8)
    • 4.通过发射获取注解内容
    • 4.1 关键方法
    • 4.2 获取注解信息
    • 5 自定义注解
      • 5.1 创建自定义注解类
      • 5.2 创建测试类
      • 5.3 创建框架实现注解类
      • 5.4 运行结果
  • 总结

Java注解

注解的作用很神奇,平时@RequestMapping用得6,但是我们还是需要了解其本质和原理。

1. 内置注解

  • 1.1 @Override 重写某个方法
  • 1.2 @Deprecated 标明方法或类过时
  • 1.3 @SuppressWarnings

2. 通过内置注解了解元注解

  • 2.1 @Override
    这个注解可以被用来修饰方法,并且它只在编译时有效.
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}
  • 2.2 @Deprecated
    这个注解它会被文档化,能够保留到运行时,能够修饰构造方法、属性、局部变量、方法、包、参数、类型。
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})public @interface Deprecated {}
  • 2.3 @SuppressWarnings
    它能够修饰的程序元素包括类型、属性、方法、参数、构造器、局部变量,只能存活在源码时,取值为String[]。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {    String[] value();}

3. 元注解

元注解是注解的注解。
JDK1.5 提供了4个 @Target,@Retention,@Documented,@Inherited,
JDK 1.8提供了2个 @Repeatable和@Native

3.1 @Target

描述注解的使用范围(即:被修饰的注解可以用在什么地方)
在定义注解类时使用了@Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在ElementType 枚举中。
@Target注解可以用于修饰 packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)

@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Target {    ElementType[] value();}

ElementType 是枚举类,指定位置包括:

public enum ElementType {    / Class, interface (including annotation type), or enum declaration */    TYPE,    FIELD,    METHOD,    PARAMETER,    CONSTRUCTOR,    LOCAL_VARIABLE,    ANNOTATION_TYPE,    PACKAGE,    TYPE_PARAMETER,    TYPE_USE}

3.2 @Retention

描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时) 。

@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Retention {    /     * Returns the retention policy.     * @return the retention policy     */    RetentionPolicy value();}

RetentionPolicy 枚举定义了保留策略。

public enum RetentionPolicy {    SOURCE,    CLASS,    RUNTIME}

做一个实验

public class RetentionTest {    @souceAnnotation    public void m1(){    }    @classAnnotation    public void m2(){    }    @runtimeAnnotation    public void m3(){    }}@Retention(RetentionPolicy.SOURCE)@interface souceAnnotation{}@Retention(RetentionPolicy.CLASS)@interface classAnnotation{}@Retention(RetentionPolicy.RUNTIME)@interface runtimeAnnotation{}

使用javap -verbose RetentionTest查看字节码。发现@souceAnnotation注解在字节码不存在;@classAnnotation@runtimeAnnotation在字节码存在,状态分别是RuntimeInvisibleAnnotations,RuntimeVisibleAnnotations
在这里插入图片描述

3.3 @Documented

描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。

3.4 @Inherited

被它修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。

3.5 @Repeatable (Java8)

允许在同一申明类型(类,属性,或方法)的多次使用同一个注解

3.6 @Native (Java8)

使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用.

4.通过发射获取注解内容

4.1 关键方法

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

//判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回falseboolean isAnnotationPresent(Class<?extends Annotation> annotationClass)//返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。<T extends Annotation> T getAnnotation(Class<T> annotationClass)//返回该程序元素上存在的所有注解Annotation[] getAnnotations()//返回该程序元素上存在的、指定类型的注解数组。<T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)//返回直接存在于此元素上的所有注解<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)//返回直接存在于此元素上的所有注解。<T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)//返回直接存在于此元素上的所有注解及注解对应的重复注解容器。Annotation[] getDeclaredAnnotations()

4.2 获取注解信息

创建自定义注解SimpleAnnotation

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface SimpleAnnotation {    public String title() default "";    public String description() default "";}

在Main类中标记注解

public class Main {    @Override    @SimpleAnnotation(title = "toString",description = "override toString")    public String toString() { return "Main{}";    }    @Deprecated    @SimpleAnnotation(title = "oldmethod",description = "do not use it")    public static void old(){ System.out.println("old method call ");    }    @SuppressWarnings({"unchecked","desc"})    @SimpleAnnotation(title = "raw method",description = "suppress warning")    public static void raw(){ List l1 = new ArrayList<>(); l1.add("ab"); old();    }    public static void main(String[] args) { Method[] methods = Main.class.getMethods(); for(Method method:methods) {     if(method.isAnnotationPresent(SimpleAnnotation.class)){  for(Annotation annotation:method.getDeclaredAnnotations()) {      System.out.println("method "+ method.getName() +"() has an annotation : "+ annotation.annotationType()+",title="+method.getAnnotation(SimpleAnnotation.class).title());  }     } }    }}

运行结果:

method toString() has an annotation : interface com.annotation.reflection.SimpleAnnotation,title=toStringmethod old() has an annotation : interface java.lang.Deprecated,title=oldmethodmethod old() has an annotation : interface com.annotation.reflection.SimpleAnnotation,title=oldmethodmethod raw() has an annotation : interface com.annotation.reflection.SimpleAnnotation,title=raw method

5 自定义注解

5.1 创建自定义注解类

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface MyBefore {}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface MyAfter {}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface MyTest {}

5.2 创建测试类

public class TestCase01 {@MyBeforepublic void init() {System.out.println("初始化...");}@MyAfterpublic void destroy() {System.out.println("销毁...");}@MyTestpublic void testSave() {System.out.println("save...");}@MyTestpublic void testDelete() {System.out.println("delete...");}}

5.3 创建框架实现注解类

public class MyJunitFrameWork {public static void main(String[] args) throws Exception {// 1.先找到测试类的字节码:TestClass clazz = TestCase01.class;Object obj = clazz.newInstance();// 2.获取EmployeeDAOTest类中所有的公共方法Method[] methods = clazz.getMethods();/* 3.迭代出每一个Method对象 判断哪些方法上使用了@MyBefore/@MyAfter/@MyTest注解  */List<Method> mybeforeList = new ArrayList<>();List<Method> myAfterList = new ArrayList<>();List<Method> myTestList = new ArrayList<>();for (Method method : methods) {if(method.isAnnotationPresent(MyBefore.class)){//存储使用了@MyBefore注解的方法对象mybeforeList.add(method);}else if(method.isAnnotationPresent(MyTest.class)){//存储使用了@MyTest注解的方法对象myTestList.add(method);}else if(method.isAnnotationPresent(MyAfter.class)){//存储使用了@MyAfter注解的方法对象myAfterList.add(method);}}// 执行方法测试for (Method testMethod : myTestList) {// 先执行@MyBefore的方法for (Method beforeMethod : mybeforeList) {beforeMethod.invoke(obj);}// 测试方法testMethod.invoke(obj);// 最后执行@MyAfter的方法for (Method afterMethod : myAfterList) {afterMethod.invoke(obj);}}}}

5.4 运行结果

初始化...save...销毁...初始化...delete...销毁...

总结

注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它是框架学习和设计者必须掌握的基础。

java基础 系列在github上有一个开源项目,主要是本系列博客的demo代码。https://github.com/forestnlp/javabasic
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。