Java注解浅析一二
文章目录
- Java注解
-
- 1. 内置注解
- 2. 通过内置注解了解元注解
- 3. 元注解
-
- 3.1 @Target
- 3.2 @Retention
- 3.3 @Documented
- 3.4 @Inherited
- 3.5 @Repeatable (Java8)
- 3.6 @Native (Java8)
- 4.通过发射获取注解内容
- 4.1 关键方法
- 4.2 获取注解信息
- 5 自定义注解
-
- 5.1 创建自定义注解类
- 5.2 创建测试类
- 5.3 创建框架实现注解类
- 5.4 运行结果
- 总结
Java注解
注解的作用很神奇,平时@RequestMapping用得6,但是我们还是需要了解其本质和原理。
1. 内置注解
- 1.1 @Override 重写某个方法
- 1.2 @Deprecated 标明方法或类过时
- 1.3 @SuppressWarnings
2. 通过内置注解了解元注解
- 2.1 @Override
这个注解可以被用来修饰方法,并且它只在编译时有效.
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}
- 2.2 @Deprecated
这个注解它会被文档化,能够保留到运行时,能够修饰构造方法、属性、局部变量、方法、包、参数、类型。
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})public @interface Deprecated {}
- 2.3 @SuppressWarnings
它能够修饰的程序元素包括类型、属性、方法、参数、构造器、局部变量,只能存活在源码时,取值为String[]。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings { String[] value();}
3. 元注解
元注解是注解的注解。
JDK1.5 提供了4个 @Target,@Retention,@Documented,@Inherited,
JDK 1.8提供了2个 @Repeatable和@Native
3.1 @Target
描述注解的使用范围(即:被修饰的注解可以用在什么地方)
在定义注解类时使用了@Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在ElementType 枚举中。
@Target注解可以用于修饰 packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Target { ElementType[] value();}
ElementType 是枚举类,指定位置包括:
public enum ElementType { / Class, interface (including annotation type), or enum declaration */ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE}
3.2 @Retention
描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时) 。
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Retention { / * Returns the retention policy. * @return the retention policy */ RetentionPolicy value();}
RetentionPolicy 枚举定义了保留策略。
public enum RetentionPolicy { SOURCE, CLASS, RUNTIME}
做一个实验
public class RetentionTest { @souceAnnotation public void m1(){ } @classAnnotation public void m2(){ } @runtimeAnnotation public void m3(){ }}@Retention(RetentionPolicy.SOURCE)@interface souceAnnotation{}@Retention(RetentionPolicy.CLASS)@interface classAnnotation{}@Retention(RetentionPolicy.RUNTIME)@interface runtimeAnnotation{}
使用javap -verbose RetentionTest
查看字节码。发现@souceAnnotation
注解在字节码不存在;@classAnnotation
、@runtimeAnnotation
在字节码存在,状态分别是RuntimeInvisibleAnnotations
,RuntimeVisibleAnnotations
。
3.3 @Documented
描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
3.4 @Inherited
被它修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。
3.5 @Repeatable (Java8)
允许在同一申明类型(类,属性,或方法)的多次使用同一个注解
3.6 @Native (Java8)
使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用.
4.通过发射获取注解内容
4.1 关键方法
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的方法来访问Annotation信息。关键方法如下:
//判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回falseboolean isAnnotationPresent(Class<?extends Annotation> annotationClass)//返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。<T extends Annotation> T getAnnotation(Class<T> annotationClass)//返回该程序元素上存在的所有注解Annotation[] getAnnotations()//返回该程序元素上存在的、指定类型的注解数组。<T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)//返回直接存在于此元素上的所有注解<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)//返回直接存在于此元素上的所有注解。<T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)//返回直接存在于此元素上的所有注解及注解对应的重复注解容器。Annotation[] getDeclaredAnnotations()
4.2 获取注解信息
创建自定义注解SimpleAnnotation
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface SimpleAnnotation { public String title() default ""; public String description() default "";}
在Main类中标记注解
public class Main { @Override @SimpleAnnotation(title = "toString",description = "override toString") public String toString() { return "Main{}"; } @Deprecated @SimpleAnnotation(title = "oldmethod",description = "do not use it") public static void old(){ System.out.println("old method call "); } @SuppressWarnings({"unchecked","desc"}) @SimpleAnnotation(title = "raw method",description = "suppress warning") public static void raw(){ List l1 = new ArrayList<>(); l1.add("ab"); old(); } public static void main(String[] args) { Method[] methods = Main.class.getMethods(); for(Method method:methods) { if(method.isAnnotationPresent(SimpleAnnotation.class)){ for(Annotation annotation:method.getDeclaredAnnotations()) { System.out.println("method "+ method.getName() +"() has an annotation : "+ annotation.annotationType()+",title="+method.getAnnotation(SimpleAnnotation.class).title()); } } } }}
运行结果:
method toString() has an annotation : interface com.annotation.reflection.SimpleAnnotation,title=toStringmethod old() has an annotation : interface java.lang.Deprecated,title=oldmethodmethod old() has an annotation : interface com.annotation.reflection.SimpleAnnotation,title=oldmethodmethod raw() has an annotation : interface com.annotation.reflection.SimpleAnnotation,title=raw method
5 自定义注解
5.1 创建自定义注解类
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface MyBefore {}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface MyAfter {}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface MyTest {}
5.2 创建测试类
public class TestCase01 {@MyBeforepublic void init() {System.out.println("初始化...");}@MyAfterpublic void destroy() {System.out.println("销毁...");}@MyTestpublic void testSave() {System.out.println("save...");}@MyTestpublic void testDelete() {System.out.println("delete...");}}
5.3 创建框架实现注解类
public class MyJunitFrameWork {public static void main(String[] args) throws Exception {// 1.先找到测试类的字节码:TestClass clazz = TestCase01.class;Object obj = clazz.newInstance();// 2.获取EmployeeDAOTest类中所有的公共方法Method[] methods = clazz.getMethods();/* 3.迭代出每一个Method对象 判断哪些方法上使用了@MyBefore/@MyAfter/@MyTest注解 */List<Method> mybeforeList = new ArrayList<>();List<Method> myAfterList = new ArrayList<>();List<Method> myTestList = new ArrayList<>();for (Method method : methods) {if(method.isAnnotationPresent(MyBefore.class)){//存储使用了@MyBefore注解的方法对象mybeforeList.add(method);}else if(method.isAnnotationPresent(MyTest.class)){//存储使用了@MyTest注解的方法对象myTestList.add(method);}else if(method.isAnnotationPresent(MyAfter.class)){//存储使用了@MyAfter注解的方法对象myAfterList.add(method);}}// 执行方法测试for (Method testMethod : myTestList) {// 先执行@MyBefore的方法for (Method beforeMethod : mybeforeList) {beforeMethod.invoke(obj);}// 测试方法testMethod.invoke(obj);// 最后执行@MyAfter的方法for (Method afterMethod : myAfterList) {afterMethod.invoke(obj);}}}}
5.4 运行结果
初始化...save...销毁...初始化...delete...销毁...
总结
注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它是框架学习和设计者必须掌握的基础。
java基础 系列在github上有一个开源项目,主要是本系列博客的demo代码。https://github.com/forestnlp/javabasic
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。