JAVA进阶——注解


前言

JAVA中的注解我们每个人都用过,通常是以@开头,最常见的就是@Ovrride注解。那么注解到底是什么,又有啥用呢?本文准备做一个专题讨论。


一、注解是什么?

注解汉语中的意思是“解释字句的文字”,在很多书中,都会对某句话或者某个词语添加一个注解,详情解释这段文字的内容。类似地,在JAVA中,注解可以理解为解释代码的“符号/标签”。JAVA中注解一个比较官方的定义是:

Java 注解( Annotation )又称 Java 标注,是 JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。

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之外,还有其他作用范围可供选择:

ElementType.ANNOTATION_TYPE 注解 可以应用于注解类型。
ElementType.CONSTRUCTOR 注解 可以应用于构造函数。
ElementType.FIELD 注解 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 注解可以应用于局部变量。
ElementType.METHOD 注解可以应用于方法级注解。
ElementType.PACKAGE 注解可以应用于包声明。
ElementType.PARAMETER 注解可以应用于方法的参数。
ElementType.TYPE 注解可以应用于类的任何元素

2.1.3 @Retention注解

@Retention注解,中文翻译是“保留”,这里是用于指定被@Retention注解标记的注解保留到何时:

RetentionPolicy.SOURCE   仅保留在源阶段,编译后得到的字节码中就被忽略。在源码阶段的注解,可以被APT使用,并执行我们需要的逻辑。如Butterknife框架中,用的就只是源码级的注解,Butterknife框架会通过自定义的APT生成一些辅助类,就可以直接在声明好的控件上添加@BindView注解,快速实现绑定,避免了大量的findViewbyId。说句题外话,DataBinding问世后,个人认为Butterknife已经out了,毕竟曾经被Butterknife各种R文件报错支配过。。。通过Butterknife的源码学习一下注解的知识尚有益处,但如果在项目中,还是使用DataBinding比较好。
RetentionPolicy.CLASS   编译后 保留到字节码阶段,但在运行时被 JVM忽略
RetentionPolicy.RUNTIME    运行阶段也能 保留这个注解,我们可以用反射获取到注解的信息并使用。
根据上面的描述, CLASS肯定是 包含了的 SOURCE ,同理, RUNTIME是 包含SOURCE和CLASS。因为运行时注解都还保留着,源码和编译后当然也是保留着的。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值