Java注解开发及ICOP平台中的应用

来源:互联网 发布:in软件客服 编辑:程序博客网 时间:2024/05/22 15:44

Java注解开发及ICOP平台中的应用


一.什么是Java注解

从JDK5开始,Java增加了Annotation(注解),Annotation是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证、处理或者进行部署。

Annotation提供了一种为程序元素(包、类、构造器、方法、成员变量、参数、局域变量)设置元数据的方法。Annotation不能运行,它只有成员变量,没有方法。Annotation跟public、final等修饰符的地位一样,都是程序元素的一部分,Annotation不能作为一个程序元素使用。

 

二.注解的作用

1.   为编译器提供辅助信息 — Annotations可以为编译器提供而外信息,以便于检测错误,抑制警告等.(@Override)

2.   编译源代码时进行而外操作 — 软件工具可以通过处理Annotation信息来生成原代码,xml文件等等.(JPA工具利用entity类的@Table和@Column)

3.   运行时处理 — 有一些annotation甚至可以在程序运行时被检测,使用.(@EnableCache,@ Component,@Controller)

4.   总之,注解是一种元数据,起到了”描述,配置“的作用。

三. 注解的开发规范

3.1定义注解(Annotation

定义新的Annotation类型使用@interface关键字(在原有interface关键字前增加@符号)。定义一个新的Annotation类型与定义一个接口很像,例如:

public @interface MyTag{

}

定义完该Annotation后,就可以在程序中使用该Annotation。使用Annotation,非常类似于public、final这样的修饰符,通常,会把Annotation另放一行,并且放在所有修饰符之前。例如:

@MyTag

public class MyClass{

....

}

3.2 定义成员变量

Annotation只有成员变量,没有方法。Annotation的成员变量在Annotation定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型,还可以通过default关键字定义注解成员变量的默认值,如果Annotation的成员变量已经指定了默认值,使用该Annotation时可以不为这些成员变量指定值,而是直接使用默认值。例如:

public @interface MyTag{
    string name() default "张三";
    int age() default 18;
}

3.2 元注解

在定义Annotation时,也可以使用JDK提供的元注解来修饰Annotation定义。JDK提供了以下4个元注解。

3.2.1 @Retention

@Retention用于指定Annotation可以保留多长时间。@Retention包含一个名为“value”的成员变量,该value成员变量是RetentionPolicy枚举类型。使用@Retention时,必须为其value指定值。value成员变量的值只能是如下3个:

1)     RetentionPolicy.SOURCE:Annotation只保留在源代码中,编译器编译时,直接丢弃这种Annotation。(@Override注解,用于注释该方法为重写父类的方法)

2)     RetentionPolicy.CLASS:编译器把Annotation记录在class文件中。当运行Java程序时,JVM中不再保留该Annotation。(用于注解处理器,根据注解生产java源文件及xml文件等。)

3)     RetentionPolicy.RUNTIME:编译器把Annotation记录在class文件中。当运行Java程序时,JVM会保留该Annotation,程序可以通过反射获取该Annotation的信息。

3.2.2@Target

@Target指定Annotation用于修饰哪些程序元素。@Target也包含一个名为”value“的成员变量,该value成员变量类型为ElementType[ ],ElementType为枚举类型,值有如下几个:

1)     ElementType[ ],ElementType为枚举类型,值有如下几个:

2)     ElementType.TYPE:能修饰类、接口或枚举类型

3)     ElementType.FIELD:能修饰成员变量

4)     ElementType.METHOD:能修饰方法

5)     ElementType.PARAMETER:能修饰参数

6)     ElementType.CONSTRUCTOR:能修饰构造器

7)     ElementType.LOCAL_VARIABLE:能修饰局部变量

8)     ElementType.ANNOTATION_TYPE:能修饰注解

9)     ElementType.PACKAGE:能修饰包

示例:

package com.cn;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
 
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface FieldTag {
     int type() default 1;
}

3.2.3 @Documented

如果定义注解A时,使用了@Documented修饰定义,则在用javadoc命令生成API文档后,所有使用注解A修饰的程序元素,将会包含注解A的说明。

@Documented
public @interface MyTag {
}
public class Test {
         @ MyTag 
         public void info() {
         }
}

3.2.4 @Inherited

@Inherited指定Annotation具有继承性。

package com.cn.annotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Inherited;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Inherited

public @interface MyTag{

}

 

package com.cn.annotation;
@MyTag
public class Base {
}

 

package com.cn;
 
//SubClass只是继承了Base
//并未直接使用@MyTag注解修饰
public class SubClass extends Base {
         public static void main(String[] args) {
                 System.out.println(SubClass.class.isAnnotationPresent(MyTag.class));
         }
}
//执行后控制台输出true

3.3 JDK中的基本注解

3.3.1 @Override

限定重写父类方法。对于子类中被@Override 修饰的方法,如果存在对应的被重写的父类方法,则正确;如果不存在,则报错。@Override 只能作用于方法,不能作用于其他程序元素。

3.3.2 @Deprecated

用于表示某个程序元素(类、方法等)已过时。如果使用被@Deprecated修饰的类或方法等,编译器会发出警告。

3.3.3 @SuppressWarning

抑制编译器警告。指示被@SuppressWarning修饰的程序元素(以及该程序元素中的所有子元素,例如类以及该类中的方法.....)取消显示指定的编译器警告。例如,常见的@SuppressWarning(value="unchecked")

3.4 提取Annotation信息

当开发者使用了Annotation修饰了类、方法、Field等成员之后,这些Annotation不会自己生效,必须由开发者提供相应的代码来提取并处理Annotation信息。这些处理提取和处理Annotation的代码统称为APT(AnnotationProcessing Tool)。

JDK主要提供了两个类,来完成Annotation的提取:

3.4.1  java.lang.annotation.Annotation

java.lang.annotation.Annotation接口:这个接口是所有Annotation类型的父接口(后面会分析Annotation的本质,Annotation本质是接口,而java.lang.annotation.Annotation接口是这些接口的父接口)。

package java.lang.annotation;
 
public interface Annotation {
 
    boolean equals(Object obj);
 
    int hashCode();
 
    String toString();
 
    Class<? extends Annotation> annotationType();
}

java.lang.annotation.Annotation接口的主要方法是annotationType(),用于返回该注解的java.lang.Class。

3.4.2  java.lang.reflect.AnnotatedElement

ackage java.lang.reflect;
import java.lang.annotation.Annotation;
 
public interface AnnotatedElement {
     boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
    <T extends Annotation> T getAnnotation(Class<T> annotationClass);
    Annotation[] getAnnotations();
    Annotation[] getDeclaredAnnotations();
}

主要方法有:

·        isAnnotationPresent(Class<? extends Annotation> annotationClass)判断该程序元素上是否存在指定类型的注解,如果存在则返回true,否则返回false。

·        getAnnotation(Class<T> annotationClass)返回该程序元素上存在的指定类型的注解,如果该类型的注解不存在,则返回null

·        Annotation[] getAnnotations()返回该程序元素上存在的所有注解。

java.lang.reflect.AnnotatedElement接口是所有程序元素(例如java.lang.Class、java.lang.reflect.Method、java.lang.reflect.Constructor等)的父接口。

·        Annotation[] getDeclaredAnnotations ()返回该程序元素上存在的所有注解,该方法将忽略继承的注释

java.lang.reflect.AnnotatedElement接口是所有程序元素(例如java.lang.Class、java.lang.reflect.Method、java.lang.reflect.Constructor等)的父接口。

3.4.3 注解处理器

JDK6中提供的注解处理工具框架的主要类包javax.annotation.processing。处理器的核心接口为:javax.annotation.processing.Processor,还提供了一个此接口的实现类:javax.annotation.processing.AbstractProcessor。处理接口提供了一个核心处理方法process(),用于开发者实现自己的处理逻辑(用于处理注解)。

public abstract boolean process(Set<? extends TypeElement> annotations,
                                    RoundEnvironment roundEnv);

process()方法有一个boolean类型的返回值,若返回false,表示本轮注解未声明并且可能要求后续其它的Processor处理它们;若返回true,则代表这些注解已经声明并且不要求后续Processor来处理它们。

 

3.4.4 提取Annotation示例

/**

 * 自定义注解--数据表

 * @authorshl

 *

 */

@Retention(RetentionPolicy.RUNTIME)

@Target({java.lang.annotation.ElementType.TYPE })

 

public @interfaceTable{

  String name();

}

 

 

 

/**

 *  自定义注解--数据列

 * @authorshl

 *

 */

@Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD })

@Retention(RetentionPolicy.RUNTIME)

public @interfaceColumn {

    Stringname() default"";

   String type() default"varchar";

   booleannullable()defaulttrue;

    int length()default 10;

    int precision()default 0;

    int scale()default 0;

}

 

 /**

* 根据注解生成table的描述信息

* @param entityClass entity Class

* @return

*/

 private staticTableVO generateTableVO(Class<?> entityClass) {

         String tableName = null;

         Map<String, ColumnVO> columnMap =new HashMap<String,ColumnVO>();

         TableVO tableVO = new TableVO();

         //判断该类是否声明了table注解

         if (entityClass.isAnnotationPresent(Table.class)) {

             //获取table注解

         Table tableAnnotation = (Table)entityClass.getAnnotation(Table.class);

             tableName = tableAnnotation.name();       

             tableVO.setTablename(tableName);

             Field[] fields = entityClass.getDeclaredFields();

             for (Fieldfield:fields){

                 Column columnAnnotation= field.getAnnotation(Column.class);

                 ColumnVO columnVO =new ColumnVO();

                 columnVO.setName(columnAnnotation.name());

                 columnVO.setLength(columnAnnotation.length());

                 columnVO.setNullable(columnAnnotation.nullable());

                 columnVO.setPrecision(columnAnnotation.precision());

                 columnVO.setScale(columnAnnotation.scale());

                 columnVO.setType(columnAnnotation.type());

                 columnMap.put(field.getName(),columnVO);

             }

             tableVO.setColumnMap(columnMap);

         }

         returntableVO;

     }

 

3.5 注解的本质

输出注解类为字节码文件:

javap -verbose -c Column.class > d.txt

 

 

Classfile/D:/eclipse_ownspace/ibop-springframework/target/classes/com/cn/annotation/Column.class

 Last modified 2017-1-9; size 635 bytes

  MD5checksum 9b1539aa17dc8983dc5d2de6ffa95e99

 Compiled from "Column.java"

public interface com.cn.annotation.Columnextends java.lang.annotation.Annotation

 SourceFile: "Column.java"

 RuntimeVisibleAnnotations:

   0: #25(#26=[e#27.#28,e#27.#29])

    1: #30(#26=e#31.#32)

 minor version: 0

 major version: 49

 flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION

Constant pool:

   #1= Class              #2             // com/cn/annotation/Column

   #2= Utf8               com/cn/annotation/Column

   #3= Class              #4             // java/lang/Object

   #4= Utf8               java/lang/Object

   #5= Class              #6             // java/lang/annotation/Annotation

   #6= Utf8              java/lang/annotation/Annotation

   #7= Utf8               name

   #8= Utf8               ()Ljava/lang/String;

   #9= Utf8               AnnotationDefault

  #10= Utf8              

  #11= Utf8               type

  #12= Utf8               varchar

  #13= Utf8               nullable

  #14= Utf8               ()Z

  #15= Integer            1

  #16= Utf8               length

  #17= Utf8               ()I

  #18= Integer            10

  #19= Utf8               precision

  #20= Integer            0

  #21= Utf8               scale

  #22= Utf8               SourceFile

  #23= Utf8               Column.java

  #24= Utf8              RuntimeVisibleAnnotations

  #25= Utf8              Ljava/lang/annotation/Target;

  #26= Utf8               value

  #27= Utf8               Ljava/lang/annotation/ElementType;

  #28= Utf8               METHOD

  #29= Utf8               FIELD

  #30= Utf8              Ljava/lang/annotation/Retention;

  #31= Utf8              Ljava/lang/annotation/RetentionPolicy;

  #32= Utf8               RUNTIME

{

  publicabstract java.lang.String name();

   flags: ACC_PUBLIC, ACC_ABSTRACT

   AnnotationDefault:

     default_value: s#10

 public abstract java.lang.String type();

   flags: ACC_PUBLIC, ACC_ABSTRACT

   AnnotationDefault:

     default_value: s#12

 public abstract boolean nullable();

   flags: ACC_PUBLIC, ACC_ABSTRACT

   AnnotationDefault:

     default_value: Z#15

 public abstract int length();

   flags: ACC_PUBLIC, ACC_ABSTRACT

   AnnotationDefault:

     default_value: I#18

 public abstract int precision();

   flags: ACC_PUBLIC, ACC_ABSTRACT

   AnnotationDefault:

     default_value: I#20

 public abstract int scale();

   flags: ACC_PUBLIC, ACC_ABSTRACT

   AnnotationDefault:

     default_value: I#20}

 

 

通过分析字节码可知:

·        注解实质上会被编译器编译为接口,并且继承java.lang.annotation.Annotation接口。

·        注解的成员变量会被编译器编译为同名的抽象方法。

·        根据Java的class文件规范,class文件中会在程序元素的属性位置记录注解信息。例如,RuntimeVisibleAnnotations属性位置,记录修饰该类的注解有哪些;flags属性位置,记录该类是不是注解;在方法的AnnotationDefault属性位置,记录注解的成员变量默认值是多少。

 

四.注解在ICOP平台中的应用

4.1 高性能的基于注解的参照缓存服务实现

4.1.1 创建参照注解

package com.yyjz.icop.refer.annotation;

 

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

importjava.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

 

import org.springframework.stereotype.Component;

 

/**

 * 参照实体

 *

 *@author hupeng 2016年9月22日

 *

 */

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Component

public @interface Refer {

 

         Stringvalue() default "";

 

         /**

          * 参照注册节点对应的参照编码

          *

          * @return

          */

         publicString referCode() default "";

 

         /**

          * 档案ID

          *

          * @return

          */

         publicString id() default "id";

 

         /**

          * 档案CODE

          *

          * @return

          */

         publicString code() default "code";

 

         /**

          * 档案名称

          *

          * @return

          */

         publicString name() default "name";

        

        

        

         publicString parentId() default  "";

}

4.1.2 在实体类中声明@Refer注解

@Entity

@Table(name="bd_company")

@Refer(id="id",code="companyCode",name="companyName")

public classCompanyEntity extendsAbsIdEntity{

    private static final long serialVersionUID= 1L;

    @Column(name="company_code")

    protected StringcompanyCode;

    @Column(name="company_name")

    protected StringcompanyName;

    @Column(name="company_sh_name")

    protected StringcompanyShName;

    。。。。。。。省略部分代码。

}   

 

4.1.3 在容器启动时,将声明@Rerfer注解的实体放入缓存

package com.yyjz.icop.refer.listenner;

 

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.beans.factory.annotation.Value;

import org.springframework.context.ApplicationListener;

importorg.springframework.context.event.ApplicationContextEvent;

importorg.springframework.stereotype.Component;

importorg.springframework.util.StringUtils;

importcom.yyjz.icop.database.entity.SuperEntity;

import com.yyjz.icop.database.repository.EntityNativeQuery;

importcom.yyjz.icop.exception.BusinessException;

importcom.yyjz.icop.refer.annotation.Refer;

importcom.yyjz.icop.refer.context.ContextUtils;

importcom.yyjz.icop.refer.utils.ReferCacheTool;

 

@Component

public class ReferListenner implementsApplicationListener<ApplicationContextEvent> {

         @Autowired

         privateEntityNativeQuery<? extends SuperEntity> query;

         /**

          * Y的时候初始化

          */

         @Value("${refer.init:N}")

         privateString referInit;

 

         @Override

         publicvoid onApplicationEvent(ApplicationContextEvent event) {

                   if(event.getApplicationContext().getParent() != null &&referInit.equals("Y")) {

                            Map<String,Object> beansWithAnnotationMap = ContextUtils.getApplicationContext()

                                               .getBeansWithAnnotation(Refer.class);

 

                            for(String key : beansWithAnnotationMap.keySet()) {

                                     System.out.println("beanName=" + key);

                                     Objectbean = beansWithAnnotationMap.get(key);

                                     Referrefer = bean.getClass().getAnnotation(Refer.class);

                                     if(refer != null) {

                                               List<String> filednames= new ArrayList<>();

                                               filednames.add(refer.id());

                                               filednames.add(refer.code());

                                               filednames.add(refer.name());

                                               if(!StringUtils.isEmpty(refer.parentId())){

                                                        filednames.add(refer.parentId());

                                               }

                                               List<SuperEntity>lst = null;

                                               try{

                                                        lst= (List<SuperEntity>) query.query((Class<? extends SuperEntity>)bean.getClass(),

                                                                           filednames,null,null);

                                               }catch (BusinessException e) {

                                                        thrownew RuntimeException(e);

                                               }

                                               ReferCacheTool.putBatchReferCache(lst,bean.getClass());

                                     }

                            }

                   }

 

         }

 

}


0 0
原创粉丝点击