Java的注解应用广泛,且使代码结构看起来整洁明了,著名的JUnit和TestNG测试框架中就大量应用了注解。
一、注解说明
1、什么是注解?
注解是在jdk 1.5开始提供的功能,目前被广泛使用。
注解的描述: “Annotation其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行响应的处理。通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。“
2、定义注解:
一个自定义注解主要包含了三个部分:
a.注解文件使用@interface来声明,格式:public @interface AnnotationName
b.在注解外层有元注解声明(注解的注解),主要的作用就是对我们自定义注解的执行范围、时期进声明,一个自定义注解至少要声明@Target和@Retention元注解;
C.注解内部可以定义变量和方法,其中方法名就是引用注解时的参数名,返回值类型就是参数的类型,通过default来设置方法的默认值。
3、元注解:
元注解的作用是负责注解其他注解。 Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
1.@Target
2.@Retention
3.@Documented
4.@Inherited
这些类型和它们所支持的类java.lang.annotation包中可以找到。下面我们可以看一下每种元注解的作用和相应参数的使用。
@Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方):
1.ElementType.CONSTRUCTOR : 用于描述构造器
2.ElementType.FIELD : 用于描述属性
3.ElementType.LOCAL_VARIABLE:用于描述局部变量
4.ElementType.METHOD : 用于描述方法
5.ElementType.PACKAGE : 用于描述包
6.ElementType.PARAMETER : 用于描述参数
7.ElementType.TYPE : 用于描述类、接口(包括注解类型) 或enum声明
@Retention:表示需要在什么级别保存注解信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效):
1.RetentionPoicy.SOURCE : 在源文件中有效(即源文件保留)
2.RetentionPoicy.CLASS : 在class文件中有效(即class保留)
3.RetentionPoicy.RUNTIME : 在运行时有效(即运行时保留)
@ Documented:用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Inherited:指示注释类型被自动继承。
二、注解处理器
注解处理器类库(Java.lang.reflect.AnnotatedElement):
Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类:
Class:类定义 - Constructor:构造器定义
Field:类的成员变量定义
Method:类的方法定义
Package:类的包定义
java.lang.reflect 包下主要包含一些实现反射功能的工具类,实际上,java.lang.reflect 包所有提供的反射API扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个方法来访问Annotation信息:
方法1: T getAnnotation(Class annotationClass): 返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
方法3:boolean is AnnotationPresent(Class< ?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
三、代码实例
1、定义个Description注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Description {
int P1 = 1; //变量
int P2 = 2;
int P3 = 3;
int P4 = 4;
String steps() default ""; //方法
String expectation() default "";
int priority();
}
2、注解解析类:
public class AnnotationParser {
private String className;
private String methodName;
public AnnotationParser(String className, String methodName){
this.className = className;
this.methodName = methodName;
}
public Description getDescription(){
try {
Class clazz = Class.forName(this.className);
for(Method method : clazz.getDeclaredMethods()){
if(method.isAnnotationPresent(Description.class) && this.methodName.equals(method.getName())){
return method.getAnnotation(Description.class);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String parser(String annotationName) {
Description description = getDescription();
if(description == null){
return "";
}
switch (annotationName) {
case "steps":
return description.steps();
case "expectation":
return description.expectation();
case "priority":
return String.valueOf(description.priority());
default:
break;
}
return "";
}
}
3、测试类:
public class AnnotationTest {
@Description(steps="test1", expectation="print something", priority=Description.P1)
public void test1(){
System.out.println("test1.test");
}
@Description(steps="test2", expectation="print something by static method", priority=Description.P2)
public static void test2(){
System.out.println("test2.test");
}
public static void main(String[] args) {
new AnnotationTest().test1();
AnnotationParser aParser = new AnnotationParser("com.yag.demo.AnnotationTest","test1");
System.out.println("steps=\"" + aParser.parser("steps") + "\", expectation=\"" + aParser.parser("expectation") +
"\", priority=" + aParser.parser("priority"));
test2();
aParser = new AnnotationParser("com.yag.demo.AnnotationTest","test2");
System.out.println("steps=\"" + aParser.parser("steps") + "\", expectation=\"" + aParser.parser("expectation") +
"\", priority=" + aParser.parser("priority"));
}
}
测试类执行效果:
test1.test
steps="test1", expectation="print something", priority=1
test2.test
steps="test2", expectation="print something by static method", priority=2
PS: 更多原创技术好文和资料,请关注下方公众号:“测试开发栈”公众号是由具有多年测试、开发经验的老兵们共同管理和运营,旨在分享原创测试、开发相关技术,包括但不限于:测试方向:Web自动化测试、移动端自动化测试、Web服务端测试、接口测试等;开发方向:Java开发、Android开发、前端开发等;期望我们的经验和技术分享能让你每天都成长和进步,早日成为技术大牛~欢迎大家分享和转发我们的文章(分享转发请保留文章出处),以便让更多的朋友关注我们,同时也欢迎加入我们的QQ群交流和提问:427020613
网友评论