最新消息:深度思考

Java注解之用于源码时的注解(2)

Java liuxuecheng 4405浏览 0评论

版权申明:转载请注明出处。
文章来源:大数据随笔

前面一篇文章Java注解之基本知识已经介绍了什么是Java注解,以及相关的知识,本篇文章将介绍如何自定义一个用于源码时的注解,并自定义注解处理器来处理这个注解。

1.背景

源码时注解我们比较熟悉的有@Override,用于在源码编译前提示错误等。本篇文章将自定义一个简单的注解,它用在方法上,作用在编译前,注解使用时需传入两个参数,一个major_version一个minor_version,若minor_version的值大于major_version的值,则提示错误。

2.注解

创建一个名为VersionCheckAnnotation的注解,Target为ElementType.METHOD,Retention为RetentionPolicy.SOURCE。有两个字段major_version代表主版本,minor_version代表次版本,默认值分别为1和0。

@Target(ElementType.METHOD) //可标注元素为方法
@Retention(RetentionPolicy.SOURCE) //作用于源码时
public @interface VersionCheckAnnotation {
    int major_version() default 1;
    int minor_version() default 0;
}

3.注解处理器

3.1 自定义注解处理器就是一个普通的Java类,因此也可以在注解处理器上面添加注解。这里我们需要添加两个注解,一个用于标注注解处理器能支持的注解类型的@SupportedAnnotationTypes,一个用于标识次注解处理器支持的Java版本的@SupportedSourceVersion。其实这两个注解对应的就是public Set<String> getSupportedAnnotationTypes()public SourceVersion getSupportedSourceVersion()这两个方法。在Java7以下的版本以及安卓开发中最好使用重载这两个方法来实现,在Java7及以上的非安卓开发中可以使用注解。注解使用起来这么方便,为何不用呢?@SupportedAnnotationTypes的参数可以有多个,此处我们指定它来处理我们之前定义的注解net.bigdataer.demo.annotation.VersionCheckAnnotation。如下:

@SupportedAnnotationTypes("net.bigdataer.demo.annotation.VersionCheckAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class VersionCheckProcessor extends AbstractProcessor {
 ……
}

3.2 注解处理器继承自AbstractProcessor,此处我们重写两个方法public synchronized void init(ProcessingEnvironment processingEnv)public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)。其中init方法主要是从processingEnv中初始化一些有用的工具类,process是对注解处理的核心逻辑。此处我们在init中初始化一个用于打印信息的Messager,它不是用于注解开发着打印日志的工具而是用于给注解使用者返回信息的工具。

@Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager = processingEnv.getMessager();
    }

3.3 现在来看核心方法process,第一个参数表示此注解处理器需要处理的所有注解,在本案例中只有一个VersionCheckAnnotation,roundEnv包含了一些有用的信息和工具,我们可以通过roundEnv获取到被当前注解标注的元素。获取到元素以后也可以调用具体的元素api获取元素上对应的注解实例,获取到了注解实例就能得到注解的参数。比如我们这里可以获取到所有被VersionCheckAnnotation标注的所有方法,获取到方法以后我们就可以知道标注在这个方法上的具体注解,进而获取注解的参数major_version和minor_version,然后判断若minor_version>major_version则使用Messager提示错误。代码如下:

 @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //遍历被VersionCheckAnnotation标记的元素
        for(Element element:roundEnv.getElementsAnnotatedWith(VersionCheckAnnotation.class)){
            //获取当前元素上的注解
            VersionCheckAnnotation versionCheckAnnotation = element.getAnnotation(VersionCheckAnnotation.class);
            int major_version = versionCheckAnnotation.major_version();
            int minor_version = versionCheckAnnotation.minor_version();
            if(major_version<minor_version){
                messager.printMessage(Diagnostic.Kind.ERROR,"major_version should bigger than minor_version",element);
            }
        }
        //此处布尔值表示传入的一组注解是否是本注解处理器声明的,若是返回true,其他的处理器将不再对这些注解进行处理
        return true;
    }

3.4 完整的注解处理器代码如下:

package net.bigdataer.demo.processor;

import net.bigdataer.demo.annotation.VersionCheckAnnotation;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;

/**
 * Created by liuxuecheng on 2017/6/26.
 */
@SupportedAnnotationTypes("net.bigdataer.demo.annotation.VersionCheckAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class VersionCheckProcessor extends AbstractProcessor {
    private Messager messager;
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //遍历被VersionCheckAnnotation标记的元素
        for(Element element:roundEnv.getElementsAnnotatedWith(VersionCheckAnnotation.class)){
            //获取当前元素上的注解
            VersionCheckAnnotation versionCheckAnnotation = element.getAnnotation(VersionCheckAnnotation.class);
            int major_version = versionCheckAnnotation.major_version();
            int minor_version = versionCheckAnnotation.minor_version();
            if(major_version<minor_version){
                messager.printMessage(Diagnostic.Kind.ERROR,"major_version should bigger than minor_version",element);
            }
        }

        //此处布尔值表示传入的一组注解是否是本注解处理器声明的,若是返回true,其他的处理器将不再对这些注解进行处理
        return true;
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager = processingEnv.getMessager();
    }
}

4.打包

注解及注解处理器打包时需要在resource目录下添加META-INF/services文件夹,并新建javax.annotation.processing.Processor 增加自定义处理类的全名net.bigdataer.demo.processor.VersionCheckProcessor。如有多个处理器则换行添加。对应的maven配置如下:

 <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArgument>-proc:none</compilerArgument>
                </configuration>
            </plugin>
        </plugins>
    </build>

然后运行打包命令,将class文件和资源文件打包在一起。

5.测试

新建一个工程,将上面的包的jar以外部依赖包的方式导入,并创建如下测试类:

package net.bigdataer.demo.annotation.test;

import net.bigdataer.demo.annotation.VersionCheckAnnotation;

public class VersionCheckTest {
    public static void main(String[] args){
        version();
    }
    @VersionCheckAnnotation(major_version = 2,minor_version = 3)
    private static void version(){
        System.out.println();
    }
}

运行,结果如下图:
Java注解

关于注解和注解处理器的代码都已上传github,地址https://github.com/bigdataer01/annotation/

转载请注明:大数据随笔 » Java注解之用于源码时的注解(2)

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址