对于java注解我之前就是仅仅拿来使用,也没有深究其背后的细节。但新工作使用新框架,原本一些清晰透明的配置信息,被公司公共平台开发的组件进行了抽离,并通过注解的方式进行了简化。虽然结果上是简化了开发,但却进一步强化了我内心对技术成长的焦虑。
因为简化了开发,让我觉得不可捉摸,所以我觉得自己有必要带着疑问首先要把注解搞清楚,然后后面再抽时间把组件背后也即是APT的部分搞明白。这样关于新公司简化开发省去的部分自己才能真正做到心知肚明。我相信如此一步步来,才能帮助自己实现技术的成长与提升。
1 Java注解核心要义概述
Java注解是从jdk5开始出现的,它是代码里面的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的信息,比如代码分析工具、开发工具和部署工具等。
Java注解作为一种修饰符,它可以用来修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,在修饰的同时也可以设置元数据(name=value)。
Java注解(Annotation)是一个接口,程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注解里的元数据。
值得指出的是,Annotation不影响程序代码的执行,无论增加、删除Annotation,代码都会始终如一地执行。如果希望让程序中的Annotation在运行时起一定的作用,只有通过某种配套的工具对Annotation中的信息进行访问和处理,访问和处理Annotation的工具统称APT(Annotation Processing Tool)。
2 注解分类
根据Annotation是否包含成员变量,可以把Annotation分为两类:
- 标记Annotation:没有定义成员变量,是仅利用自身的存在与否提供信息,比如注解@Test、@Override。
- 元数据Annotation:包含成员变量。
下面介绍常见的注解:
2.1 java.lang下5个基本标记Annotation
- @Override:限定重写父类方法,作用就是告诉编译器检查被修饰的这个方法,保证父类要包含一个被该方法重写的方法,否则就会编译出错。主要就是帮助程序员避免一些低级错误。
- @Deprecated:标示已过时。
- @SuppressWarnings:抑制编译器警告。
- @SafeVarargs:java7的堆污染警告。当把一个不带泛型的对象赋给一个带泛型的变量时,往往会发生这种堆污染;对于形参个数可变的方法,如果形参类型又是泛型的话,就更容易导致堆污染。
- @FunctionalInterface:java8的函数式接口,用来指定某个接口必须是函数式接口。也就是告诉编译器检查这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错。
2.2 jdk的元Annotation
- @Retention:只能用于修饰Annotation定义,用于指定被修饰的Annotation可以保留多长时间。
@Retention包含一个RetentionPolicy类型的value成员变量,value成员变量的值只能是如下三个:
(1) RetentionPolicy.CLASS(注解记录在class文件中,程序运行时,jvm不可获取注解信息);
(2)RetentionPolicy.RUNTIME(注解记录在class文件中,程序运行时,jvm可获取注解信息) ;
(3)RetentionPolicy.SOURCE(只保留在源代码中,编译器直接丢弃这种注解)。
- @Target:只能用于修饰Annotation定义,用于指定被修饰的Annotation可以用于修饰那些程序单元。
该成员变量的值只能是如下几个:
(1)ElementType.ANNOTATION_TYPE(只能修饰注解);
(2)ElementType.CONSTRUCTOR(只能修饰构造器);
(3)ElementType.FIELD(只能修饰成员变量);
(4)ElementType.LOCAL_VARIABLE(只能修饰局部变量);
(5)ElementType.METHOD(只能修饰方法);
(6)ElementType.PACKAGE(只能修饰包定义);
(7)ElementType.PARAMETER(只能修饰参数);
(8)ElementType.TYPE(只能修饰类、接口或枚举定义)。
- @Documented:用于指定被该元Annotation修饰的Annotation类将被javadoc工具提取成文档。
- @Inherited:用于指定被它修饰的注解具有继承性,父类被该注解修饰,子类也自动被该注解修饰。
3 自定义注解
- (1)定义新的注解类型使用@interface关键字;
- (2)新定义的注解可以带成员变量,成员变量以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型。
-(3) 可以为注解的成员变量指定初始值即默认值,指定成员变量的初始值可以使用default关键字。
public @interface Mytag{ String name() default "xiaoming"; int age(); }
4 提取注解信息
使用Annotation修饰了类、方法、成员变量等成员之后,这些注解不会自己生效,必须由开发者提供相应的工具来提取并处理注解信息。从java5开始,java.lang.reflect包所提供的反射API增加了读取运行时注解的能力。
至于APT工具的开发实例,后面深挖学习之后,再做进一步分享。
提取注解信息-后话:
作为一个开发人员,我之前很少使用自定义注解,所以对于读取注解之前是很不熟悉。而经过一段时间的学习之后,我对于读取使用注解有了新的思考。尤其是对于使用Spring开发框架的我们,有着很好的优势,因为Spring给我们提供了很多方式可以来读取运行时中注解。
比如,web接口中对于加在方法上的注解,我们可以通过使用拦截器的方式进行读取并进行相应操作。另外对于加在非私有方法上的注解,我们可以使用Spring AOP切面编程的方式进行读取。对于加载类上的注解,我们可以在对象完成初始化之后,通过借助处理器BeanPostProcessor来读取注解。