Java中的注解

一.注解(Annotation):
    1.解释:其实就是代码的特殊标记,这些标记可以在编译、运行时、类加载时被读取,并执行相应的操作。    
    2.代码中常常出现的注解,java提供的基本注解:@Override、@Deprecated、@SuppressWarnings、@SafeVarargs、@FunctionalInterface。其中ov是最常见用来指定方法的覆盖,强制一些子类必须覆盖父类的一些方法,编译器会检查是否重写,避免类名写错等低级的错误。Deprecated标示已过时,使用过时的类或者方法会给出警告。这两个是最常见的注解。
    3.元Annotation:
       在java.lang.annotation包下提供了6个Meta Annotation,这些注解用于修饰自定义的注解,即元注解;
       1.@Retention:标示注解可以保留多长时间 
         @Retention((RetentionPolicy.CLASS))  记录在class文件中 运行java程序时jvm获取不到注解信息 这个是默认值;
         @Retention((RetentionPolicy.SOURCE))    只保留在源代码中 编译器直接丢弃这种注解;
         @Retention(RetentionPolicy.RUNTIME) 这个可以保留到运行时JVM可以可以获取注解信息 可以通过反射获取信息;
       2.@Target: 这个元注解可修饰注解标示用于哪种程序单元 (即类、方法、成员变量)  
          @Target(ElementType.METHOD)  这里只能修饰方法;
          @Target(ElementType.FIELD)   成员变量;
          PACKAGE包、TYPE类或接口或枚举定义、LOCAL_VARIABLE局部变量。
      3.@Documented 被该元Annotation修饰的Annotation类将被javadoc工具提取成文档 ;
      4.@Inherited:被修饰的注解具有集成性,父类中使用该注解则子类自动被该自定义注解修饰;
   4.自定义注解:
@Retention(RetentionPolicy.RUNTIME)
     @Target(ElementType.METHOD)
     public @interface Test {
    String name() default "noName";
    int age();
    }
上面是一个名为Test的自定义注解,用@interface声明,注解中的成员变量以方法的形式来定义,可以通过default来定义默认值。ElementType.METHOD标示该注解只能用于修饰方法,RetentionPolicy.RUNTIME指注解可以在运行时获取。
  使用该注解:
public class MyTest {
    @Test(age = 1)
    publicstatic void info() {
     
    }

    @Test(age = 2)
    publicstatic void test() {
        
    }

    publicstatic void test2(){
    }

    @Test(age = 3)
    public void test3(){
     
    }
}

在MyTest类中定义了四个方法,其中三个使用了Test注解,使用注解的方法都抛出了一个参数错误的异常。
那么定义的这些注解怎么使用呢?首先要知道的是仅仅使用注解来标记 程序元素对程序是不会有任何影响的, 为了让这些注解起作用必须为这些注解提供一个注解处理工具,使用的场景受@Retention的限制。下面定义一个类用于处理注解:

public class AnnatationUtil {
    public static void process(String clazz)throws ClassNotFoundException{
        int passed=0;
        int failed=0;
        //遍历clazz对应类里的所有方法
        for (Method method : Class.forName(clazz).getDeclaredMethods()) {
            //如果这个方法使用了@Test注解
            if (method.isAnnotationPresent(Test.class)) {
                try {
                    method.invoke(null);
                    passed++;
                    System.out.println("age"+method.getAnnotation(Test.class).age());
                    System.out.println("方法"+method+"运行成功");
                } catch (Exception e) {
                    System.out.println("方法"+method+"运行失败,异常");
                    failed++;
                    e.printStackTrace();
                }
            }
        }
        System.out.println("总"+passed+failed);
    }
}

其中process方法会对clazz进行反射操作,这个处理场景是在运行时,通过反射获取类中的方法集合进行遍历,通过isAnnotationPresent方法判断是否被Test注解修饰了,如果被修饰了则调用此方法,下面以MyTest方法为例:

public static void main(String[] args) {
    try {
        AnnatationUtil.process("annotation.MyTest");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

在主方法中调用process方法并出入MyTest的类路径,执行后的日志:

java 定义 注解 java的注解有几种_Test


二.什么是APT:即注解处理工具或者叫注解处理器,ButterKnife,dagger等常用的项目框架中都有使用。

1.怎么工作的:对元代码文件进行检测,并找出源文件包含的Annotation信息,针对Annotation做出特别的处理。

2.为什么使用:主要目的是为了简化开发者的工作量,APT可以在编译程序源代码的同时生成一些附属文件,可以代替传统的对代码信息和附属文件的维护工作。在JAVA中的运行很广泛。

3.注解处理器:每个出列器都要实现javax.annotation.processing包下的Processor接口,通常是通过继承AbstractProcessor,一个处理器可以处理一种或多种Annotation类型。

实现一个生成XML文件的注解处理器:

@SupportedAnnotationTypes({"Persistent","Id","Property"})
public class MyAnnotationProcessor extends AbstractProcessor {
    //循环处理每个需要处理的程序对象
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //文件输出流
        PrintStream ps=null;
        //RoundEnvironment获取Annotation信息   getElementsAnnotatedWith可根据Annotation获取需要处理的程序单元由Element代表,
        //Element中包含一个getKind()方法 该方法返回Element所代表的程序单元,
        // getEncloseElements()获取该Element中定义的所有程序单元包括类、方法、构造器、内部类等;
        for (Element t : roundEnv.getElementsAnnotatedWith(Persistent.class)) {
            //正在处理的类名
            Name clazzName=t.getSimpleName();
            Persistent per = t.getAnnotation(Persistent.class);
            try {
                ps=new PrintStream(new FileOutputStream(clazzName+".hbm.xml"));
                ps.println("<?xml version=\"1.0\"?>");
                ///.....................................//

                for (Element f : t.getEnclosedElements()) {
                    //只处理成员变量上的Annotation
                    if (f.getKind() == ElementKind.FIELD) {
                        Id id = t.getAnnotation(Id.class);
                        if (id != null) {
                            ps.println("<id name=\""+f.getSimpleName()+">");
                            //......................................//

                        }
                        Property p = f.getAnnotation(Property.class);
                        if (p != null) {
                            //.............................//
                        }
                    }
                }

                ps.println("    </class>");
                //...............

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }finally {
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }

            }
        }
        return false;
    }
}

头部的SupportedAnnotationTypes表示该注解器可以处理的注解有哪些。java中的使用配置和Android中是不同的。