前言:平时用的最多是Spring注解等框架注解,发现自己对底层不是很了解,所以来复习一下java基础之注解
概述 java注解是一种特殊的接口,可以看做是extends Annotation接口,注解可分为自定义注解、JDK内置注解、第三方框架注解。注解的使用分3步,1定义注解,2使用注解,3解析注解(如反射等)
1 2 3 4 5 6 public @interface myAnnotation {}
1 2 3 4 5 6 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 { ElementType[] value(); }
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 解释说明了注解的的存活时间
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被忽略去除。(除了反射,还有其他方式读取注解,比如直接使用IO读取java文件然后正则、字符串处理等方式,以RetentionPolicy.CLASS注解的@Override为例,编译器就会读取该注解,然后在编译时才会忽略去除)
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
注解主要被反射读取,反射只能读取内存中的字节码信息
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 @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 @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 @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()); 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()); } }
运行结果
注解属性赋值 有多个属性时,需要写明属性与值的对应关系
1 2 3 4 5 @MyAnnotation (value = "3" , name = "myMethod" )@MyAnnotation ("myMethod" )
实例2 简单的单元测试 MyTest 1 2 3 4 5 6 7 8 @Target (ElementType.METHOD)@Retention (RetentionPolicy.RUNTIME)public @interface MyTest {}
MyBefore 1 2 3 4 5 6 7 8 @Target (ElementType.METHOD)@Retention (RetentionPolicy.RUNTIME)public @interface MyBefore {}
MyAfter 1 2 3 4 5 6 7 8 @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 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 @MyAnnotation (value = "1" , name = "AnnotationTest" )public class AnnotationTest { public static void main (String[] args) throws Exception { Class clazz = ServiceTest.class ; Object obj = clazz.newInstance(); 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); } } } }
运行结果