注解练习
# 注解练习
前面我们讲反射的时候,并没有将如何获取注解,这里补充下:Field,Constructor 和 Method 类对象都可以调用下面这些方法获取标注在它们之上的注解。
Annotation[] getAnnotations()
:获取该对象上的所有注解Annotation getAnnotation(Class annotaionClass)
:传入注解类型
,获取该对象上的特定一个注解Annotation[] getDeclaredAnnotations()
:获取该对象上的显式标注的所有注解,无法获取继承
下来的注解Annotation getDeclaredAnnotation(Class annotationClass)
:根据注解类型
,获取该对象上的特定一个注解,无法获取继承
下来的注解
# 案例:解析注解
在程序使用注解:获取注解中定义的属性值。
需求:之前用反射做了个小型的 Spring 框架,如今改成注解来配置。注解大多数时候都是用来替换配置文件的
定义注解:
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno5 {
String className();
String methodName();
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
定义 Person 类:
package com.peterjxl;
public class Person {
public void eat(){
System.out.println("Person eating......");
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
定义测试类 来测试
//不使用配置文件,通过注解和反射来创建对象、执行方法
import java.lang.reflect.Method;
@MyAnno5(className = "com.peterjxl.Person", methodName = "eat")
public class AnnoDemo4 {
public static void main(String[] args) throws Exception {
// 1. 解析注解
// 1.1 获取该类的字节码文件对象
Class<AnnoDemo4> annoDemo4 = AnnoDemo4.class;
// 1.2 获取上边的注解对象
MyAnno5 an = annoDemo4.getAnnotation(MyAnno5.class);// 指定获取什么注解
// 2.调用注解对象中定义的抽象方法,获取返回值
String className = an.className();
String methodName = an.methodName();
Class cls = Class.forName(className);// 3.加载该类进内存
Object obj = cls.newInstance();// 4.创建对象
Method method = cls.getMethod(methodName);// 5.获取方法对象
method.invoke(obj); // 6.执行方法
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
此时的文件目录结构:
├── AnnoDemo4.java
└── MyAnno5.java
├── com
│ └── peterjxl
│ └── Person.java
1
2
3
4
5
2
3
4
5
运行结果:
javac ./com/peterjxl/Person.java
javac AnnoDemo4.java -encoding utf8
java -cp . AnnoDemo4
Person eating......
1
2
3
4
2
3
4
为什么能直接调用注解对象中定义的抽象方法,获取返回值? 因为实际上在内存中,生成了一个该注解的子类实现对象,即 an。当于用以下类新建的的对象:
public class ProImpl implements Pro{
public String className(){
return "com.peterjxl.anno.Person";
}
public String methodName(){
return "eat";
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
使用步骤总结:
- 获取注解定义的位置的对象 (Class,Method, Field)
- 获取指定的注解
getAnnotation(Class)
, 其实就是在内存中生成了一个该注解接口的子类实现对象。 - 调用注解中的抽象方法获取配置的属性值
注意:有必检异常,需要抛出或 try-catch。
# 案例:简单的测试框架
需求:我们要测试一个类的方法,如果每个方法都测试的话,非常麻烦,需要一个个在 main 方法里调用。
例如我们定义了一个计数器类:
public class Calculator {
public void add(){
System.out.println("1 + 0 = " + (1 + 0));
}
public void sub(){
System.out.println("1 - 0 = " + (1 - 0));
}
public void mul(){
System.out.println("1 * 0 = " + (1 * 0));
}
public void div(){
System.out.println("1 / 0 = " + (1 / 0));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
我们希望能在方法上加一个注解,就能实现方法的调用;如果有异常则记录到一个 txt 里
整体思路并不难:
- 定义注解
- 创建计数器对象,获取字节码
- 获取所有方法
- 判断方法上是否有注解
- 有则执行,并捕获异常
我们先定义 Check 注解
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}
1
2
3
4
5
6
7
2
3
4
5
6
7
然后在计数器类里加上注解(注意 show 方法我们没有添加,这是测试没有注解的情况下是否会被执行)
public class Calculator {
@Check
public void add(){
System.out.println("1 + 0 = " + (1 + 0));
}
@Check
public void sub(){
System.out.println("1 - 0 = " + (1 - 0));
}
@Check
public void mul(){
System.out.println("1 * 0 = " + (1 * 0));
}
@Check
public void div(){
System.out.println("1 / 0 = " + (1 / 0));
}
public void show(){
System.out.println("永无bug...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
现在我们可以开始写代码,执行所有加了 Check 注解的方法,判断方法是否有异常,记录到 bug.txt 文件中
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.lang.reflect.Method;
public class AnnoDemo5 {
public static void main(String[] args) throws Exception{
Calculator calculator = new Calculator(); //1.创建计算器对象
Class cls = Calculator.class; //2.获取字节码文件对象
Method[] methods = cls.getMethods(); //3.获取所有方法
int exceptionNum = 0; //出现异常的次数
BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
for (Method method : methods) {
//4.判断方法上是否有Check注解
if(method.isAnnotationPresent(Check.class)){
try {
method.invoke(calculator); //5.有,执行
} catch (Exception e) {
//6.捕获异常,记录到文件中
exceptionNum ++;
bw.write(method.getName() + "方法出现异常了");
bw.newLine();
bw.write("异常的名称:" + e.getCause().getClass().getSimpleName());
bw.newLine();
bw.write("异常的原因:" + e.getCause().getMessage());
bw.newLine();
}
}
}
bw.write("本次测试一共出现 " + exceptionNum + " 次异常");
bw.flush();
bw.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
上次更新: 2024/10/1 18:45:09