您的位置:首页 > 编程语言 > Java开发

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

2017-01-10 00:34 411 查看

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
@interface
Table{
  String name();
}
 

 

 

/**
 *  自定义注解--数据列
 * @authorshl
 *
 */
@Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD
})
@Retention(RetentionPolicy.RUNTIME)
public
@interface
Column {
    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
static
TableVO 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
class
CompanyEntity 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());

                                     }

                            }

                   }

 

         }

 

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: