什么是注解

本文介绍了Java注解的基本概念、用途,如代码文档生成、编译时处理和运行时反射,重点讲解了元注解、注解的提取方法以及AnnotationProcessing的工作原理和使用示例。同时提及了Android注解库及其在AndroidStudio中的应用,以及如何利用注解改进代码检查。
摘要由CSDN通过智能技术生成

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值