文章目录
目录
前言
JAVA中的注解我们每个人都用过,通常是以@开头,最常见的就是@Ovrride注解。那么注解到底是什么,又有啥用呢?本文准备做一个专题讨论。
一、注解是什么?
注解汉语中的意思是“解释字句的文字”,在很多书中,都会对某句话或者某个词语添加一个注解,详情解释这段文字的内容。类似地,在JAVA中,注解可以理解为解释代码的“符号/标签”。JAVA中注解一个比较官方的定义是:
1.1 注释和注解的区别
有人可能会说,我们平时使用//或者/* */添加的注释不是同样可以解释某段代码的含义吗?这确实没错,不过注释的部分,完全是给我们人自己看的,编译器会自动忽略这一部分代码。而使用@开头的注解,主要是给编译器看的,比如@Ovrride表示这个方法重写了父类中的方法,所以编译器看到这个注解,就会去检查你的方法名是否和父类一样,如果不一样,就会报错。
二、注解的使用
2.1 如何声明一个注解
还是拿我们最常见使用的@Override注解来举例,我们先来看看@Override注解是怎样声明的,依葫芦画瓢,就能大概知道其他注解应该怎样声明。
package java.lang;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
上述代码段就是@Override注解的声明,我们可以看到,声明一个注解类型和声明一个接口的很相似,只不过在interface关键字前面又加了一个@符号,然后在注解名@interface的上方,还多了两个注解@Target({ElementType.METHOD}) 以及@Retention(RetentionPolicy.SOURCE)。
2.1.1 元注解
上面说的@Target和@Retention,这种注解上的注解,我们称之为元注解,一般我们在定义自己的注解时,需要指定这两个注解 。接下来我们来定义一个我们自己的注解,声明的方法也比较简单,我们在new一个JAVA Class的时候,输入注解名,选择类型为Annotation即可,IDE会自动为我们创建好一个注解。
这时候我们自定义的注解是没有添加元注解的,也就意味着没有给这个注解任何限制,我们可以将其添加在各种地方,比如类上、方法上、属性上等等,都是没问题的。
package com.example.libjava;
@BeiWeiLittleYang
public class MainClass {
@BeiWeiLittleYang
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
@BeiWeiLittleYang
String mainString;
}
}
那上文中提到的几种元注解,又是干啥的咧?
2.1.2 @Target注解
@Target注解,中文翻译是“目标”,用于限制注解能够作用在何处,@Target({ElementType.METHOD})括号中的ElementType.METHOD,意思是制定注解只能作用在方法上,除ElementType.METHOD之外,还有其他作用范围可供选择:
2.1.3 @Retention注解
@Retention注解,中文翻译是“保留”,这里是用于指定被@Retention注解标记的注解保留到何时:
2.1.4 给注解添加参数
用过EventBus的肯定都知道@Subscribe注解,我们可以在这个注解后面传递参数,比如
threadMode = ThreadMode.MAIN表示线程模型为主线程,sticky = true表示是否接收黏性事件:
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void XXX(MessageEvent messageEvent) {
...
}
我们自定义的注解,也可以添加参数,添加的方式类似于接口中声明方法:
package com.example.libjava;
public @interface BeiWeiLittleYang {
String text();
int num() default 0;
}
上述代码中,我们给自定义的注解使用“键=值”的方式添加了两个参数,一个是String类型的text(),一个是int类型的num(),至于num()后面的default,表示给这个参数设置一个默认值,如果没有设置参数的默认值,就必须在使用该注解的时候为其设置一个值,否则会报错:
package com.example.libjava;
@BeiWeiLittleYang(text = "yang")
public class MainClass {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
@BeiWeiLittleYang(text = "yang",num = 666)
String mainString;
}
}
这样一来,我们就把参数传递到了注解中。最后有一个小提示,当参数名是value,并且只传value的时候,可以直接传值,“value=xxx”可以简写为“xxx”:
package com.example.libjava;
public @interface BeiWeiLittleYang {
String value();//参数名为value
int num() default 0;
}
package com.example.libjava;
@BeiWeiLittleYang("yang")//此处参数名为value,并且必须传的参数只有value,可使用简写,只需要传值
public class MainClass {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
@BeiWeiLittleYang(value = "yang",num = 666)//此处需要传多个参数,不能简写,必须写成 键=值 的格式
String mainString;
}
}
三、注解处理器
上文中,我们自己定义了注解,并穿了参数,但是并没有看出注解有多么明显的作用,难道真的仅仅只是标记/注释而已吗?并不是,如果我们将注解配合注解处理器(Annotation Processor)来使用,就能够完成许多事情。不管是运行时注解还是编译时注解,注解处理器都会对其进行处理,
Java中有默认的注解处理器,我们也可以自定义注解处理器来实现更加丰富的功能。
3.1 创建自己的注解处理器
我们创建了一个Annotation的module,用来放注解处理器,创建了一个LittleYangAnnotationProcessor的module用来放注解,另一个MainLib用来测试,mainLib和LittleYangAnnotationProcessor需要依赖Annotation。创建方法不再赘述,鼠标右键new Module即可:
自定义注解还是使用了上文中的BeiWeiLittleYang:
package com.example.libAnnotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface BeiWeiLittleYang {
String value();
int num() default 0;
}
接下来就是创建自定义的注解处理器。我们通常继承AbstractProcessor来实现自己的注解处理器,通常有几个方法需要重写:
1、init (ProcessingEnvironment processingEnvironment):
初始化注解处理器。ProcessingEnviroment参数提供了很多有用的工具类,如Elements, Types和Filer等等。
2、process(Set<? extends TypeElement> annotations, RoundEnvironment env):
该方法通常被认为是注解处理器的核心方法。在这里主要是写扫描、处理注解的逻辑,也可以生成文件。
3、getSupportedAnnotationTypes():
该方法是用于指定这个注解处理器是处理哪些注解的,不重写这个方法也没问题,我们也可以在注解处理器上使用@SupportedAnnotationTypes(“注解的全限定名”)来指定。
4、getSupportedSourceVersion():
该方法用于指定你使用的Java版本,通常这里返回SourceVersion.latestSupported()。同样,我们也可以使用注解@SupportedSourceVersion(“版本号,如SourceVersion.RELEASE_8”)来代替。
我们创建的注解处理器代码如下:
package com.example.littleyangannotationprocessor;
import com.example.libAnnotation.BeiWeiLittleYang;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
public class YangProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.ERROR, "yang的注解处理器初始化了=================================================");
System.out.println("yang的注解处理器初始化了=================================");
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.ERROR, "yang的注解处理器=================================================");
System.out.println("yang的注解处理器=================================");
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BeiWeiLittleYang.class);
for (int i = 0; i < elements.size(); i++) {
System.out.println("使用了该注解的方法共有" + elements.size() + "个");
}
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotataions = new LinkedHashSet<String>();
annotataions.add(BeiWeiLittleYang.class.getCanonicalName());
return annotataions;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
3.2 注册注解处理器
3.2.1 手动注册注解处理器
如果是手动注册注解处理器,我们需要创建一个资源文件夹和配置文件,并将注解处理器的全限定名写入配置文件,具体文件名如下,注意不要打错:
配置文件内容如下:
com.example.littleyangannotationprocessor.YangProcessor
配置好后,重新build一下,我们可以看到自定义的注解处理器类名不再是灰色,鼠标左键+ctrl可以跳转到配置文件,说明自定义的注解处理器被识别到了,配置正确,注册成功。
3.2.2 自动注册注解处理器
有人可能觉得上面还需要手动创建文件夹和文件,过于麻烦,于是再介绍一种自动注册的方式。自动注册注解处理器,需要引入一个google的插件:
implementation 'com.google.auto.service:auto-service:1.0-rc6'
然后我们就可以直接在注解处理器上使用@AutoService(Processor.class)注解来注册我们的注解处理器了,就像下面这样:
@AutoService(Processor.class)
public class YangProcessor extends AbstractProcessor {
}
其实上面的插件就是自动帮我们创建了3.2.1中提到的配置文件,重新编译一下,就可以在目录中查看到熟悉的东西。
3.3 使用注解处理器
注解处理器的功能也是十分强大的,本文中,我们只是简单打印了几条日志。开发中,我们可以根据实际需求在自己的注解处理器中写相应的逻辑,比较常见的用法,是利用注解处理器来生成一些辅助类、资源类等等,例如Arouter、Butterknife、Retrofit等框架之所以使用简单,容易上手,都离不开注解处理器,相关开源框架的源码今后将会专题讨论。
总结
以上是我学习注解后整理的部分内容,如有错误还请各位大神指出,同时也欢迎各位大佬来讨论和交流技术,本人邮箱hbutys@vip.qq.com