注解

概述,自定义注解

  • 充分认识
  • 应用场景

注解(Annotation)

  • 就是java代码里的特殊标记,比如@Override,@Test等,作用是:让其他程序根据注解信息来决定怎么执行该程序.
  • 注意: 注解可以用在类上,构造器上,方法上,成员变量上,参数上,等位置处.

自定义注解

  • 就是自己定义注解
1
2
3
public @interface 注解名称 {
public 属性类型 属性名() default 默认值;
}

使用:

1
2
3
4
5
6
7
8
9
10
11
package _annotation;

/**
* 自定义注解
*/
public @interface Mystest1 {
String aaa();
boolean bbb() default true;
String[] ccc();
}

1
2
3
4
5
6
7
8
9
10
11
package _annotation;

@Mystest1(aaa="牛魔王",ccc={"HTML","JAVA"})
public class AnnotationTest1 {

@Mystest1(aaa="铁扇公主",bbb=false,ccc={"Python","前端"})
public void test1(){

}
}

特殊属性名: value

如果注解中只有一个value属性,使用注解时,value名称可以不写!

1
2
3
4
5
package _annotation;

public @interface Mystest1 {
String value();//特殊属性
}
1
2
3
4
5
6
7
8
9
@Mystest1(aaa="牛魔王",ccc={"HTML","JAVA"})
@Mytest2("孙悟空")
public class AnnotationTest1 {

@Mystest1(aaa="铁扇公主",bbb=false,ccc={"Python","前端"})
public void test1(){

}
}

注解的原理

先编译为class,反编译成真正的源码

1
2
3
4
5
public @interface Mystest1 {
String aaa();
boolean bbb() default true;
String[] ccc();
}

​ |

​ |

​ /

1
2
3
4
5
public interface Mystest1 extends Annotation{
public abstract String aaa();
public abstract boolean bbb();
public abstract String[] ccc();
}

当我们这样写的时候:

1
2
3
4
@Mystest1(aaa="李四",bbb=true,ccc={"Go","Python"}) //这里是实现类对象
public void test(){

}
  • 注解本质是一个接口,java中所有的注解都是继承了Annotation接口的.
  • @注解(…) 其实就是一个实现类对象,实现了该注解以及Annotation接口

元注解

指的是: 修饰注解的注解.

1
2
3
4
5
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Test {
}

image-20250322105534740.png

注解的解析

什么是注解的解析?

  • 就是判断类上,方法上,成员变量上是否存在注解,并把注解里面的内容给解析出来.

如何解析注解?

  • 指导思想: 要解析谁上面的注解,就应该先拿到谁
  • 比如要解析类上面的注解,则应该先获取该类的class对象,再获取Class对象解析其上面的注解
  • Class,Method,Field,Constructor,都实现了AnnotatedElement接口,它们都拥有解析注解的能力.
AnnotatedElement接口提供了解析注解的方法 说明
public Annotation[] getDeclareAnnotations() 获取当前对象上面的注解
public T getDeclaredAnnotation(Class<T>annotationClass) 获取指定的注解对象
public boolean isAnnotatuibPresent(Class<Annotation>annotationClass) 判断当前对象上是否存在某个注解

下面我们进行一个案例:

  1. 定义注解MyTest4,要求如
    • 包含属性:String value()
    • 包含属性:double aaa() 默认值为100
    • 包含属性:String[] bbb()
    • 限制注解使用的位置:类和成员方法上
    • 指定注解的有效范围: 一直到运行时
  2. 定义一个类:Demo 在类中定义一个test1方法,并在该类和其方法上使用MyTest4注解
  3. 定义AnnotationTest3测试类,解析Demo类的全部注解.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package _annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4 {
String value();
double aaa() default 100;
String[] bbb();
}
1
2
3
4
5
6
7
8
9
package _annotation;
@MyTest4(value="1",aaa=99.9,bbb={"PYTHON","JAVA"})
public class demo {
@MyTest4(value="2",aaa=199.9,bbb={"Go","Linux"})
public void test1(){

}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.junit.Test;

import java.util.Arrays;

/**
* 目标:掌握注解的解析
*/
public class AnnotationTest3 {
@Test
public void parseClass(){
//1.先得到class对象
Class c = demo.class;
//2.解析类上的注解
//判断这个类上包含了某个注解
if(c.isAnnotationPresent(MyTest4.class)){
MyTest4 myTest4 = (MyTest4) c.getDeclaredAnnotation(MyTest4.class);
System.out.println(myTest4.value());
System.out.println(myTest4.aaa());
System.out.println(Arrays.toString(myTest4.bbb()));
}
}
}

结果:

1
2
3
1
99.9
[PYTHON, JAVA]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void parseMethod() throws Exception {
//1.先得到class对象
Class c = demo.class;
Method m = c.getDeclaredMethod("test1");
//2.解析方法上的注解
//判断方法上包含了某个注解
if(m.isAnnotationPresent(MyTest4.class)){
MyTest4 myTest4 = (MyTest4) m.getDeclaredAnnotation(MyTest4.class);
System.out.println(myTest4.value());
System.out.println(myTest4.aaa());
System.out.println(Arrays.toString(myTest4.bbb()));
}
}
1
2
3
2
199.9
[Go, Linux]

应用场景

模拟一个简易版的junit框架

需求:

  • 定义若干方法,只要加了MyTest注解,就会触发该方法执行
  1. 定义一个自定义注解MyTest,只能注解方法,存活范围是一直都在
  2. 定义若干个方法,不分方法加上@MyTest注解修饰,部分方法不加.
  3. 模拟一个junit程序,可以触发加了@MyTest注解的方法执行.
1
2
3
4
5
6
7
8
9
10
11
package _annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Mytest {
}
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
37
38
39
40
41
42
43
44
45
46
47
48
package _annotation;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* 目标: 模拟junit框架的设计.
*/
public class AnnotationTest4 {

// @Mytest
public void test1(){
System.out.println("-----test1------");
}

@Mytest
public void test2(){
System.out.println("-----test2------");
}

// @Mytest
public void test3(){
System.out.println("-----test3------");
}

@Mytest
public void test4(){
System.out.println("-----test4------");
}

public static void main(String[] args) throws Exception {
AnnotationTest4 a = new AnnotationTest4();
// 启动程序!
//把这个类的所有方法拿到判断是否有注解 有就跑
//1.得到Class对象
Class c = AnnotationTest4.class;
//2.提取这个类中的全部成员方法
Method[] methods = c.getDeclaredMethods();
//3.遍历这个数组中的每一个方法,看看方法上是否存在@MyTest注解,存在
for (Method method : methods) {
if(method.isAnnotationPresent(Mytest.class)){
//说明存在 赶紧运行
method.invoke(a,null);
}
}
}
}

结果:

1
2
-----test4------
-----test2------