zoukankan      html  css  js  c++  java
  • java注解综合应用总结

    1.注解的一些基本概念

    Java从1.5开始引入注解。注解的功能类似于代码中的注释,所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。在使用注解时候的配置参数的值必须是编译时刻的常量(java基本类型和String类型,Class类型,Enum类型,Anotation类型,数组类型)从某种角度来说,可以把注解看成是一个XML 元素,该元素可以有不同的预定义的属性。而属性的值是可以在声明该元素的时候自行指定的。在代码中使用注解,就相当于把一部分元数据从XML 文件移到了代码本身之中,在一个地方管理和维护。

     1 @Retention(RetentionPolicy.SOURCE)
     2 @Target({ElementType.FIELD,ElementType.METHOD,ElementType.ANNOTATION_TYPE})
     3 public @interface MyAnotation {
     4     public int intTest() default 1;
     5     public long longTest() default 1l;
     6     public String stringTest() default "name";
     7     public MyEnum enumTest();
     8     public String[] arrayTest();
     9     public MyAnotation2 anotationTest();
    10 }
    11 
    12 
    13 enum MyEnum{
    14     TestAnotation1,TestAnotation2;
    
    15 }
    16 
    17 
    18 @interface MyAnotation2{
    19     public String value()default "myAnotation2"; 
    20 }
    一个anotation定义实例

    @interface 用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型。可以通过default来声明参数的默认值@Retention用来声明注解的保留策略,有SOURCE(仅存在于源码中,在class字节码文件中不包含)、CLASS(会在class字节码文件中存在,但运行时无法获得)、RUNTIME(在class字节码文件中存在,在运行时可以通过反射获取到)这三种。只有当声明为RUNTIME的时候,才能够在运行时刻通过反射API来获取到注解的信息。

    @Target用来声明注解可以被添加在哪些类型的元素上,其元素包括(TYPE(指一般的类,接口,枚举,注解), FIELD(属性,枚举常量), METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE)。

    一般我们最常用的一些注解有

    @Override(声明重写),@Deprecated(表示方法或类等是不被建议使用的)

    @SupressWarnings(用于抑制警告常用值有“unchecked”,"unused"),@Documented(声明注解将被javaDoc中)

    使用刚才定义的注解:

    //当自定义注解的方法有默认值时可以不提供,也可以覆盖它
    //当自定义注解只一个方法value或values(返回一个数组)时,在使用时可以不指
    //定属性值,直接写如@SupressWarnings注解
    @MyAnnotation(anotationTest = @MyAnotation2,intTest=1, arrayTest = { "1","2" }, enumTest = MyEnum.TestAnotation1)
    public class TestMyAnotation {
    
    }

    2.实现一个自定义在Source或Class的注解

    定义为Source或Class的注解,影响的就是程序的编译时刻,只有在程序的编译时候才能对它们作处理,只是定义在Source策略的在编译之后不会进入到Class文件中,而定义为Class策略的编译后会保留在class文件中。

    在编译时刻处理的时候,是分成多趟来进行的。如果在某趟处理中产生了新的Java源文件,那么就需要另外一趟处理来处理新生成的源文件。如此往复,直到没有新文件被生成为止。在完成处理之后,再对Java代码进行编译。

    在JDK 6 中,通过JSR269 把自定义注解处理器这一功能进行了规范化, 有了新的javax.annotation.processing这个新的API。对Mirror API(之前jdk1.5中)也进行了更新,形成了新的javax.lang.model包。注解处理器的使用也进行了简化,不需要再单独运行apt(在jdk下tools.jar中)这样的命令行工具,Java编译器本身就可以完成对注解的处理。
    于JDK 6中通过元注解@SupportedAnnotationTypes来声明所支持的注解类型。另外描述程序静态结构的javax.lang.model包使用了不同的类型名称。使用的时候也更加简单,只需要通过javac -processor Processor Demo.java这样的方式即可。

    假设我们想在源代码编译的时候为一些加了@Property注解的属性生成setter 和getter方法

    定义注解类和测试类放到一个文件中如下:

     1 public class PropertyAnnotationTest {
     2     @Property
     3     private String username;
     4     @Property(PropertyType.Set)
     5     private String password;
     6 
     7 }
     8 
     9 @Retention(RetentionPolicy.SOURCE)
    10 @Target(ElementType.FIELD)
    11  @interface Property {
    12     public PropertyType value()default PropertyType.GetAndSet;
    13 }
    14 
    15 enum PropertyType{
    16     Get,Set,GetAndSet;
    17 }

    编写自己的注解处理器类如下:

    @SupportedAnnotationTypes(value = { "Property" })//用元注解声明些注解处理器要处理的注解类型
    @SupportedSourceVersion(SourceVersion.RELEASE_7)//元注解声明源代码的版本
    public class MyPropertyAnnotationProcessor extends AbstractProcessor {
    
        @Override
        public boolean process(Set<? extends TypeElement> set,RoundEnvironment roundenvironment) {
            if (!roundenvironment.processingOver()) {
                for (Element e : roundenvironment.getRootElements()) {
                    
                    TypeElement te = findEnclosingTypeElement(e);//找到最底层的元素
                    System.out.printf("\n Scanning Type %s\n\n ",te.getQualifiedName());
                    
                    for (ExecutableElement ee : ElementFilter.methodsIn(te.getEnclosedElements())) {
                        Property property = ee.getAnnotation(Property.class);//从元素上获得注解
    
                        System.out.printf("%s property value = %s\n", 
                                ee.getSimpleName(), //获得注解所在的方法,类或属性等的简单不带修饰符的名字
                                property == null ? null: property.value());//获得此注解在此元素上的值
                    }
                }
            }
            return false;
        }
    
        public static TypeElement findEnclosingTypeElement(Element e) {
            while (e != null && !(e instanceof TypeElement)) {
                e = e.getEnclosingElement();
            }
            return TypeElement.class.cast(e);
        }
    
        @Override
        public synchronized void init(ProcessingEnvironment processingenvironment) {
            super.init(processingenvironment);
        }
    }
    
    // 参考<http://blog.retep.org/2009/02/13/getting-class-values-from-annotations-in-an-annotationprocessor/>

    把cmd目录指向MyPropertyAnnotationProcessor(处理器)的bin目录下,执行javac -verbose  -g -processor   annotationTest.MyPropertyAnnotationProcessor  D:\myworkspace\test\src\annotationTest\PropertyAnnotationTest.java
    但是并没有看到输出相关的信息,但处理器的确调用了,至于如何在字节码中构造setter 和getter方法是处理字节码的事了,现在已经获得了属性的名称和注解的值,应该没有问题了

    2.实现一个runtime策略的注解

    运行时刻处理注解,一般通过java反射api来处理。反射API提供了在运行时刻读取注解信息的支持。不过前提是注解的保留策略声明的是运行时(runtime)

    举一个例子,简单模仿hibernate的自动建表操作,首先定义一些注解,这些注解可以指定表名,属性名有ID

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Table {
        public String tableName();
    
    }
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @interface Id{
        public String IdName();
    }
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @interface Column{
        public String ColumnName();
    }

    再建一个测试类应用自己建的注解

     1 @Table(tableName="testTabel")
     2 public class PoJoClass {
     3     
     4     @Id(IdName="ID")
     5     private int Id;
     6     
     7     @Column(ColumnName="username")
     8     private String username;
     9     
    10     @Column(ColumnName="password")
    11     private String password;
    12     
    13     public int getId() {
    14         return Id;
    15     }
    16     public void setId(int id) {
    17         Id = id;
    18     }
    19     public String getUsername() {
    20         return username;
    21     }
    22     public void setUsername(String username) {
    23         this.username = username;
    24     }
    25     public String getPassword() {
    26         return password;
    27     }
    28     public void setPassword(String password) {
    29         this.password = password;
    30     }
    31     
    32 
    33 }
    注解测试类

    然后再建一个处理我们注解的类,此处为简单起见就直接传Class文件进去到处理类中,在实际中应该传配置进去或是用动态代理方式处理拿到类来处理。

     1 public class ProcessorTest {
     2     
     3     
     4     public static void main(String[] args) {
     5         
     6         filter(PoJoClass.class);
     7     
     8     }
     9 
    10     public static String filter(Class clzz){
    11         StringBuffer Sql = new StringBuffer();
    12         Annotation[] annotations = clzz.getAnnotations();//获得类上的所有注解
    13         for(Annotation anno:annotations){
    14             if(anno instanceof Table){
    15                 String tableName = ((Table) anno).tableName();//获取注解的值
    16                 Sql.append("creat Table ").append(tableName+"(");
    17             }
    18             
    19         }
    20         
    21         Field[] fields = clzz.getDeclaredFields();
    22 
    23         for(Field field:fields){
    24             Annotation[] annotations2 = field.getDeclaredAnnotations();//获得属性上的所有注解
    25             
    26             for(Annotation anno:annotations2){
    27                 if(anno instanceof Id)
    28                 {
    29                     String idName = ((Id) anno).IdName();//获得注解的值
    30                     Sql.append(idName +" primary key, ");
    31                 }
    32                 else if(anno instanceof Column)
    33                 {
    34                     String columnName = ((Column) anno).ColumnName();//获得注解的值
    35                     Sql.append(columnName +",");
    36                 }
    37             }
    38         }
    39         
    40         String sql = Sql.substring(0, Sql.length()-1)+")";
    41         System.out.println(sql);
    42         return sql;
    43         
    44     }
    45 
    46 }

    为测试方便直接在处理注解类中调用处理注解的方法,返回结果为:
    creat Table testTabel(ID primary key, username,password)

    注解一般在框架中用的比较多,所以熟悉注解的知识对学习框架作用是比较大的。

  • 相关阅读:
    Linux多线程Pthread学习小结
    TCP三次握手/四次挥手
    内存管理内幕
    Delphi 中分发设计时包
    一个小的算法问题解决
    写了一个验证数字范围的正则表达式
    用 XML 文件持久化和恢复图片信息
    string.Empty 和 "" 并不总是可以互换的
    博客园用的 FreeTextBox 有 bug
    乱花渐欲迷人眼。。。
  • 原文地址:https://www.cnblogs.com/wn398/p/3086486.html
Copyright © 2011-2022 走看看