0%

注解

前言:平时用的最多是Spring注解等框架注解,发现自己对底层不是很了解,所以来复习一下java基础之注解

概述

java注解是一种特殊的接口,可以看做是extends Annotation接口,注解可分为自定义注解、JDK内置注解、第三方框架注解。注解的使用分3步,1定义注解,2使用注解,3解析注解(如反射等)

1
2
3
4
5
6
/**
* @author shency
* @description: 注解学习
*/
public @interface myAnnotation {
}
1
2
3
4
5
6
/**
* @author shency
* @description: 注解学习
*/
public interface myAnnotation extends Annotation {
}

元注解

修饰注解的注解,通常用在注解的定义

1
2
3
4
5
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {

}
  • @Target:注解的作用目标
  • @Retention:注解的生命周期
  • @Documented:注解是否应当被包含在 JavaDoc 文档中
  • @Inherited:是否允许子类继承该注解
@Target
1
2
3
4
5
6
7
8
9
10
11
12
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}

image-20200102225955724

  • ElementType.ANNOTATION_TYPE:允许作用在注解上
  • ElementType.CONSTRUCTOR:允许作用在构造器上
  • ElementType.FIELD:允许作用在属性字段上
  • ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
  • ElementType.METHOD:允许作用在方法上
  • ElementType.PACKAGE:允许作用在包上
  • ElementType.PARAMETER:允许作用在方法参数上
  • ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
  • ElementType.TYPE_PARAMETER:表示该注解能写在类型变量的声明语句中(Java8)
  • ElementType.TYPE_USE:表示该注解能写在使用类型的任何语句中(Java8)
@Retention

解释说明了注解的的存活时间

image-20200102230007028

  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被忽略去除。(除了反射,还有其他方式读取注解,比如直接使用IO读取java文件然后正则、字符串处理等方式,以RetentionPolicy.CLASS注解的@Override为例,编译器就会读取该注解,然后在编译时才会忽略去除)
  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

image-20200102230016128

注解主要被反射读取,反射只能读取内存中的字节码信息

RetentionPolicy.SOURCE只有源码有,编译为class就会去除。RetentionPolicy.CLASS指的是保留到字节码文件(.class),它在磁盘内,而不是内存中。虚拟机将字节码文件加载进内存后注解会消失,要想被反射读取,保留策略只能用RUNTIME,即运行时仍可读取

@Documented

文档。将注解中的元素包含到Javadoc中

@Inherited

继承,使用了该注解的类,子类会其注解

@Repeatable

可重复(Java8), 使用了该注解的注解可使用多次

属性

注解中没有方法,只有属性。

注解的属性在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该属性的名字,其返回值定义了该属性的类型

  • 八种基本数据类型
  • String
  • 枚举
  • Class
  • 注解
  • 数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author shency
* @description: 注解学习
*/
@Target(ElementType.METHOD)
public @interface myAnnotation {
// 八大基本数据类型
byte byteValue();
short shortValue();
int intValue();
long longValue();
float floatValue();
double doubleValue();
char charValue();
boolean booleanValue();
// 类
String stringValue();
// 数组
int[] intArray();
}

实例

MyAnnotation
1
2
3
4
5
6
7
8
9
/**
* @author shency
* @description: MyAnnotation
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
String name();
}
AnnotationTest
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
/**
* @author shency
* @description: AnnotationTest
*/
@MyAnnotation(value = "1", name = "AnnotationTest")
public class AnnotationTest {
@MyAnnotation(value = "2", name = "myField")
private String myField;

@MyAnnotation(value = "3", name = "myMethod")
public void myMethod() {
System.out.println("intMethod");
}

public static void main(String[] args) throws Exception{
// 类的注解
Class<AnnotationTest> clazz = AnnotationTest.class;
MyAnnotation myAnnotationClass = clazz.getAnnotation(MyAnnotation.class);
System.out.println("name: " + myAnnotationClass.name() + " value:" + myAnnotationClass.value());

// 属性的注解
// 注意myField是私有属性,需要使用getDeclaredField方法,getField只能获得公共属性
Field myField = clazz.getDeclaredField("myField");
myField.setAccessible(true);
MyAnnotation myAnnotationField = myField.getAnnotation(MyAnnotation.class);
System.out.println("name: " + myAnnotationField.name() + " value:" + myAnnotationField.value());

// 方法的注解
Method myMethod = clazz.getMethod("myMethod");
MyAnnotation myAnnotationMethod = myMethod.getAnnotation(MyAnnotation.class);
System.out.println("name: " + myAnnotationMethod.name() + " value:" + myAnnotationMethod.value());
}
}
运行结果

image-20200102230032976

注解属性赋值

有多个属性时,需要写明属性与值的对应关系

1
2
3
4
5
// 多个属性
@MyAnnotation(value = "3", name = "myMethod")

// 单属性
@MyAnnotation("myMethod")

实例2 简单的单元测试

MyTest
1
2
3
4
5
6
7
8
/**
* @author shency
* @description: MyTest
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
MyBefore
1
2
3
4
5
6
7
8
/**
* @author shency
* @description: MyBefore
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBefore {
}
MyAfter
1
2
3
4
5
6
7
8
/**
* @author shency
* @description: MyAfter
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAfter {
}
ServiceTest
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
/**
* @author shency
* @description: ServiceTest
*/
public class ServiceTest {
@MyBefore
public void init(){
System.out.println("init...");
}
@MyAfter
public void destroy(){
System.out.println("destroy...");
}
@MyTest
public void serviceOneTest(){
System.out.println("serviceOneTest...");
}
@MyTest
public void serviceTwoTest(){
System.out.println("serviceTwoTest...");
}
@MyTest
public void serviceThreeTest(){
System.out.println("serviceThreeTest...");
}
}
AnnotationTest
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
/**
* @author shency
* @description: AnnotationTest
*/
@MyAnnotation(value = "1", name = "AnnotationTest")
public class AnnotationTest {
public static void main(String[] args) throws Exception {
Class clazz = ServiceTest.class;
Object obj = clazz.newInstance();
// 获取ServiceTest中的所有公共方法
Method[] methods = clazz.getMethods();
List<Method> myBeforeList = new ArrayList<>();
List<Method> myAfterList = new ArrayList<>();
List<Method> myTestList = new ArrayList<>();
for (Method method : methods) {
if (method.isAnnotationPresent(MyBefore.class)) {
myBeforeList.add(method);
} else if (method.isAnnotationPresent(MyAfter.class)) {
myAfterList.add(method);
} else if (method.isAnnotationPresent(MyTest.class)) {
myTestList.add(method);
}
}
for(Method testMethod : myTestList){
for(Method beforeMethod : myBeforeList){
beforeMethod.invoke(obj);
}
testMethod.invoke(obj);
for(Method afterMethod : myAfterList){
afterMethod.invoke(obj);
}
}
}
}
运行结果

image-20200102230048284

-------------本文结束感谢您的阅读-------------