注解

Java 注解

什么是注解?

Java 注解(Annotation)又称 Java 标注,用于为代码提供元数据。Java 注解从 Java 5 开始引入,注解不直接影响代码的执行。

什么用处?

  • 生成文档
  • 标识代码,便于查看
  • 格式检查(编译时)
  • 注解处理(编译期生成代码、xml 文件等;运行期反射解析;常用于三方框架)

基本使用

在 Java 中,注解可以应用到包、类、接口、方法、构造方法、变量等多种元素上。以下是一些基本的 Java 注解:

Java内置了三种标准注解,其定义在 java.lang 中。

  • @Override,表示当前的方法定义将覆盖超类中的方法。
  • @Deprecated,被此注解标记的元素表示被废弃,如果程序员使用了注解为它的元素,那么编译器会发出警告。
  • @SuppressWarnings,关闭不当的编译器警告信息。

元注解

元注解是由 Java 提供的基础注解,负责注解其它注解。

元注解 说明 取值
@Target 表示该注解可以用在什么地方 ElementType.ANNOTATION_TYPE 可以应用于注释类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注释。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。
@Retention 表示需要在什么级别保存该注解信息 SOURCE: 只在源码中有效 (编译时抛弃)
CLASS: 在 class 文件中有效(即 class 保留)
RUNTIME: 在运行时有效(即运行时保留)
@Documented 表示将此注解包含在Javadoc中
@Inherited 表示允许子类继承父类中的注解
@Repeatable 指示注解可以在同一个声明上使用多次。

注解的提取

注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然后通过 getAnnotation() 方法来获取 Annotation 对象。

 public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或者是 getAnnotations() 方法。

public Annotation[] getAnnotations() {}

前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解。

如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了,比如

public class ReflectionBinding {
    public static void bind(Activity activity) {
        // 反射方式遍历目标类所有 field
        for (Field field : activity.getClass().getDeclaredFields()) {
            // if(field.isAnnotationPresent(ReflectionBindView.class))
            ReflectionBindView bindView = field.getAnnotation(ReflectionBindView.class);
            if (bindView != null) {
                try {
                    // 目标类 field 可能设置为 privite/protected,修改可访问性
                    field.setAccessible(true);
                    field.set(activity, activity.findViewById(bindView.value()));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

注解处理器(Annotation Processing)

原理

Annotation Processing 原理: 编译过程中读源码,然后⽣成新的代码⽂件,再放在⼀起进⾏编译

基本用法

Annotation Processing ⽤法:

  1. resources/META-INF/services/javax.annotation.processing.Processor(或者使用 AutoService)
  2. 继承 AbstractProcessor
  3. 重写 getSupportedAnnotationTypes() 和 process()
  4. 依赖 annotationProcessor先测试⽣成 java ⽂件的功能:手写或使用 javapoet
  5. 自动生成代码
  6. 添加依赖

使用示例

继承 AbstractProcessor,并重写getSupportedAnnotationTypes() 和 process()

public class BindingProcessor extends AbstractProcessor {

    // 返回用于创建新源、类或辅助文件的文件管理器。
    Filer filer;
    // 返回用于报告错误、警告和其他通知的消息发送器。
    Messager messager;

    /**
     * ProcessingEnvironment :注释处理工具框架将为注释处理器提供实现该接口的对象,以便处理器可以使用框架提供的设施来编写新文件、报告错误消息以及查找其他实用程序。
     * @param processingEnv 访问设施的环境工具框架,提供给处理器
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
        messager = processingEnv.getMessager();
    }

    /**
     * 一个抽象注释处理器,旨在方便大多数具体注释处理器的超类
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Collections.singleton(BindView.class.getCanonicalName());
    }

    /**
     * 注解处理器实际处理方法,一般要求子类实现该抽象方法,你可以在在这里写你的扫描与处理注解的代码,以及生成Java文件。其中参数RoundEnvironment ,
     * 可以让你查询出包含特定注解的被注解元素,后面我们会看到详细的内容。
     *
     * @param set              the annotation types requested to be processed
     * @param roundEnvironment environment for information about the current and prior round
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        double preTime = System.nanoTime();
        for (Element element : roundEnvironment.getRootElements()) {
            String packageStr = element.getEnclosingElement().toString();
            String classStr = element.getSimpleName().toString();
            ClassName className = ClassName.get(packageStr, classStr + "Binding");
            messager.printMessage(Diagnostic.Kind.NOTE, "------ packageStr=" + packageStr + " classStr=" + classStr + " className=" + className.canonicalName());
            MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
                    .addModifiers(Modifier.PUBLIC)
                    .addParameter(ClassName.get(packageStr, classStr), "activity");
            boolean hasBinding = false;
            for (Element enclosedElement : element.getEnclosedElements()) {
                if (enclosedElement.getKind() == ElementKind.FIELD) {
                    BindView bindView = enclosedElement.getAnnotation(BindView.class);
                    if (bindView != null) {
                        hasBinding = true;
                        constructorBuilder.addStatement("activity.$N = activity.findViewById($L)",
                                enclosedElement.getSimpleName(), bindView.value());
                    }
                }
            }
            TypeSpec builtClass = TypeSpec.classBuilder(className)
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(constructorBuilder.build())
                    .build();

            if (hasBinding) {
                try {
                    JavaFile.builder(packageStr, builtClass)
                            .build().writeTo(filer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        messager.printMessage(Diagnostic.Kind.NOTE, "------ 耗时:" + (System.nanoTime() - preTime));
        return false;
    }
}

调试

Java Annotation Processor注解处理器如何 Debug

Android 注解

Android 注解库

support.annotation 是 Android 提供的注解库,与 Android Studio 内置的代码检查工具配合,注解可以帮助检测可能发生的问题,例如 null 指针异常和资源类型冲突等。

使用前配置 在 Module 的 build.gradle 中添加配置 ”implementation 'com.android.support:support-annotations:版本号'“

Null 性注解

  • @Nullable 可以为 null
  • @NonNull 不可为 null

线程注解

线程注解可以检查某个方法是否从特定类型的线程调用。支持以下线程注解:

  • @MainThread
  • @UiThread
  • @WorkerThread
  • @BinderThread
  • @AnyThread

更多 android 注解请参考(利用注解改进代码检查)

参考文档&资料

原文链接

Google 利用注解改进代码检查

Android 注解系列之APT工具(三)

https://github.com/idea007/demo-apt

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容