版权申明:转载请注明出处。
文章来源:玄涧思库
Java注解出现的初衷是为了解决大量配置文件带来的问题,现在注解已经变的非常的有用和强大。它可以在你编写源码时提示错误比如常见的@Override,也可以在编译时生成代码,更能在运行时结合反射机制获取被标注的对象,然后进行逻辑处理。有了这些功能,我们就可以造出非常易用的轮子,别人使用我们的注解就可以轻松的完成一堆代码所表达的逻辑。
1.注解可以用在什么地方?
在Java8中注解几乎可以用到任何地方,在java低版本中注解可以被用到类、方法、参数、属性、局部变量、接口、甚至是注解上。本次系列的文章以jdk1.7为例,不过我们可以先目睹一下注解在java8中的使用。
myString = (@NonNull String) str;
class UnmodifiableList<T> implements @Readonly List<@Readonly T> { ... }
new @Interned MyObject();
2.注解的类型及可标注对象
首先看一下常见的注解@Override是怎么实现的。
package java.lang;
import java.lang.annotation.*;
/**
* Indicates that a method declaration is intended to override a
* method declaration in a supertype. If a method is annotated with
* this annotation type compilers are required to generate an error
* message unless at least one of the following conditions hold:
* @author Peter von der Ahé
* @author Joshua Bloch
* @jls 9.6.1.4 Override
* @since 1.5
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
可以看到注解使用@interface来声明,同时使用@Target和@Retention两个注解来标注,这两个注解是jdk自带的,我们可以将它们理解为元注解,即标注注解的注解。这样的注解有以下几种:
(1) Target 用来说明哪些元素可以被标注,可以为多个值。当为多个值的时候写法为@Target({ElementType.FIELD,ElementType.LOCAL_VARIABLE})。这些可以被标注的元素有以下几种:
<1> TYPE 表示类、接口(包含注解类型)或者枚举Enum。
<2> FIELD 表示字段、包含枚举中的常量
<3> METHOD 表示方法
<4> PARAMETER 表示参数
<5> CONSTRUCTOR 表示构造方法
<6> LOCAL_VARIABLE 表示局部变量
<7> ANNOTATION_TYPE 表示注解类型
<8> PACKAGE 表示包
(2) Retention 用来说明注解的有效时间,它的取值只能是一个,可选值有如下几个(默认为RetentionPolicy.CLASS)。
<1> SOURCE 源码时
<2> CLASS 编译时
<3> RUNTIME 运行时
(3) Documented 表示会生成doc文档,一般小项目中不需要。
(4) Inherited 表示是否可以被继承,默认为false。
针对@Retention取值的几点说明:
<1> 如果是SOURCE,注解保留范围为源代码,在编译时将会被编译器丢弃。这类 Annotation 大都用来校验,比如 Override, Deprecated, SuppressWarnings。
<2> 如果是CLASS,这个注解保留范围是源代码和类文件中,但并非作用于运行时,所以JVM不会识别此。如果你在自定义注解时,不写@Retention,默认就是CLASS的。这类的注解和SOURCE的注解都可以配合AbstractProcessor进行使用,用于在编译时进行自动处理一些事物或者生成一些文件。
<3> 如果是RUNTIME,这个注解的保留范围是源代码、类文件和运行时,这类的注解一般会和反射配合使用。可以在运行时通过反射查看被这个注解标识的方法,然后得到被标识的元素,接着进行处理。
3.注解处理器
我们自定义了注解,但是这个注解有什么作用,该何时干何事这就是注解处理器需要关注的。在自定义注解的同时我们要有相应的自定义注解处理器来处理被注解标注的元素。
在JDK1.6之后提供了抽象的注解处理器AbstractProcessor,我们的自定义注解处理器需要继承它去实现自己的逻辑。
public class MyProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment env){ }
@Override
public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }
@Override
public Set<String> getSupportedAnnotationTypes() { }
@Override
public SourceVersion getSupportedSourceVersion() { }
}
以上代码如果在你的开发环境上有报错请参考我的另外一篇文章Idea下Usage of API documented as @since1.6+问题及解决
接下来的文章会分别给出source,class,runtime不同阶段的自定义注解及注解处理器的实现。
源码时demo