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)

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

 

 

 

 

 

 

 

posted @ 2013-05-19 08:16  丁丁木木  阅读(381)  评论(0编辑  收藏  举报