Android-注解篇

2023-05-16

1.什么是注解

​ 从JDK 5 开始,Java 增加了注解,注解是代码里的特殊标记,这些标记可以在编译、类加载、运
行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,
在源文件中嵌入一些补充的信息。

2.注解的分类

注解包含:标准注解和元注解。

  • 标准注解

    • @Override:对覆盖超类中的方法进行标记,如果被标记的方法并没有实际覆盖超类中
      的方法,则编译器会发出错误警告。
    • @Deprecated:对不鼓励使用或者已过时的方法添加注解,当编程人员使用这些方法时,
      将会在编译时显示提示信息。
    • @SuppressWarnings:选择性地取消特定代码段中的警告。
    • @SafeVarargs:JDK 7 新增,用来声明使用了可变长度参数的方法,其在与泛型类一起
      使用时不会出现类型安全问题。
  • 元注解: 它用来注解其他注解,从而创建新的注解。

    • @Targe:注解所修饰的对象范围。
      • ElementType.TYPE:能修饰类、接口或枚举类型。
      • ElementType.FIELD:能修饰成员变量。
      • ElementType.METHOD:能修饰方法。
      • ElementType.PARAMETER:能修饰参数。
      • ElementType.CONSTRUCTOR:能修饰构造方法。
      • ElementType.LOCAL_VARIABLE:能修饰局部变量。
      • ElementType.ANNOTATION_TYPE:能修饰注解。
      • ElementType.PACKAGE:能修饰包。
      • ElementType.TYPE_PARAMETER:类型参数声明。
      • ElementType.TYPE_USE:使用类型。
    • @Inherited:表示注解可以被继承。
    • @Documented:表示这个注解应该被 JavaDoc 工具记录。
    • @Retention:用来声明注解的保留策略。
      • RetentionPolicy.SOURCE:源码级注解。注解信息只会保留在.java 源码中,源码在编译
        后,注解信息被丢弃,不会保留在.class 中。
      • RetentionPolicy.CLASS:编译时注解。注解信息会保留在.java 源码以及.class 中。当运
        行 Java 程序时,JVM 会丢弃该注解信息,不会保留在 JVM 中。
      • RetentionPolicy.RUNTIME:运行时注解。当运行 Java 程序时,JVM 也会保留该注解信
        息,可以通过反射获取该注解信息。
    • @Repeatable:JDK 8 新增,允许一个注解在同一声明类型(类、属性或方法)上多次
      使用。

3.注解的使用

使用@interface来申明注解,注解里边可以添加属性,属性默认是public的并且只能是public的,

可以使用default 来设置默认值。

public @interface SoulMaster {
    String name() default "唐三";
    int age() default 24;
}

定义运行时注解

@Retention(RetentionPolicy.RUNTIME)
public @interface SoulMaster {
    String name() default "唐三";
    int age() default 24;
}

如果将@Retention 的保留策略设定为 RetentionPolicy.CLASS,这个注解就是编译时注解

@Retention(RetentionPolicy.CLASS)
public @interface SoulMaster {
    String name() default "唐三";
    int age() default 24;
}

4.注解处理器

如果没有处理注解的工具,那么注解也不会有什么大的作用。对于不同的注解有不同的注解处理器。

  • 针对运行时注解会采用反射机制处理。
  • 针对编译时注解会采用 AbstractProcessor 来处理。

5.运行时注解处理器

下面来通过一个例子演示:获取运行时注解的属性

首先定义一个运行时注解,在方法上使用

@Documented							 //JavaDoc 应该包含这个注解
@Target(ElementType.METHOD)          //在方法上修饰
@Retention(RetentionPolicy.RUNTIME)   //运行时注解
public @interface POST {
    String value();     //带一个参数
}

声明一个类使用这个注解

public class LoginManager {

    @POST(value = "https://login.meishe.com")
    public void login(){

    }
}

测试方法来获取这个注解,并输出注解的value

public class AnnotationTest {

    /**
     * 获取运行时注解
     */
    @Test
    public void testLogin(){
        Method[] declaredMethods = LoginManager.class.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            if (declaredMethod.getName().equals("login")){
                POST annotation = declaredMethod.getAnnotation(POST.class);
                System.out.println("注解的value="+annotation.value());
            }
        }
    }
}

输出:

注解的value=https://login.meishe.com
Process finished with exit code 0

6.编译时注解处理器

编译时在编译的时候也会存在的注解,这类的注解,如果想拿到注解的内容并且执行一定的代码逻辑,就需要借助注解处理器以及poet才行。

定义一个编译时注解,只有一个属性value

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface BindClass {
    String value();
}

定义一个类使用这个注解,这个类没有内容,只是添加了上面定义的注解

@BindClass(value = "注解处理器")
public class BindClassView {
}

准备工作做好了,下面是就是注解处理器的部分了

注解处理器部分需要添加gradle依赖

// 这是Google对注解处理器所提供的服务,目的是为了生成注解配置,并 可以生成java文件代码等
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'

// 此JavaPoet可以去生成Java文件  以面向对象的思维,生成代码
implementation 'com.squareup:javapoet:1.9.0'

注解处理器源码

/**
 * Google的这个AutoService可以去生成配置文件
 */
@AutoService(Processor.class)
/**
 * 配置版本(Java编译时的版本)
 */
@SupportedSourceVersion(SourceVersion.RELEASE_7)
/**
 * 允许注解处理器  可以去处理的注解,不是所有的注解处理器都可以去处理
 */
@SupportedAnnotationTypes({"com.meishe.annotation.BindClass"})
/**
 *  注解处理器能够接收的参数(例如:如果想把AndroidApp信息传递到这个注解处理器(Java工程),是没法实现的,所以需要通过这个才能接收到)
 */
@SupportedOptions("value")
public class BindClassProcess extends AbstractProcessor {

    /**
     * 注解节点
     */
    Elements mElementTool;

    /**
     * 类信息
     */
    Types mTypesTool;

    /**
     * 专用日志
     */
    Messager mMessager;

    /**
     * 过滤器
     */
    Filer mFiler;

    /**
     * 用于做一些初始化的操作
     * @param processingEnv
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mElementTool =processingEnv.getElementUtils();
        mTypesTool =processingEnv.getTypeUtils();
        mMessager =processingEnv.getMessager();
        mFiler =processingEnv.getFiler();
        String value = processingEnv.getOptions().get("value");
        mMessager.printMessage(Diagnostic.Kind.NOTE,"init---->从Android App Gradle中传递过来的value="+value);
    }


    
  	/**
     * 只要使用了定义好的注解,可以统一的执行一定的代码逻辑
     * @param annotations 使用注解的节点数组
     * @param roundEnv 封装的环境内容,使用注解的类的包名字 类名等信息都可以在这个上面拿到
     * @return 
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        for (TypeElement element : annotations) {
            String packageName = mElementTool.getPackageOf(element).getQualifiedName().toString();
            // 获取简单的 类名
            String className = element.getSimpleName().toString();
            mMessager.printMessage(Diagnostic.Kind.NOTE, "packageName---"+packageName+"  className---"+className);
            // 打印一下信息

            // 最终要生成的类名
            String finalResultClassNmae = className + "$BinderViewClass";
            mMessager.printMessage(Diagnostic.Kind.NOTE,"finalResultClassNmae----"+finalResultClassNmae);


            // 开始真正的使用JavaPoet的方式来生成 Java代码文件
            MethodSpec methodSpec = MethodSpec.methodBuilder("main")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(void.class)
                    .addParameter(String[].class, "args")
                    .addStatement("$T.out.println($S)", System.class, "刘恬希 is my daughter")
                    .build();

            TypeSpec typeSpec = TypeSpec.classBuilder(finalResultClassNmae)
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(methodSpec)
                    .build();

            JavaFile javaFile=JavaFile.builder(packageName,typeSpec).build();

            try {
                javaFile.writeTo(mFiler);
            } catch (IOException e) {
                e.printStackTrace();
                mMessager.printMessage(Diagnostic.Kind.NOTE,e.getMessage());
            }
            mMessager.printMessage(Diagnostic.Kind.NOTE, "代码注入完成...");
        }

        return true;
    }
}
  • init()方法一般就是进行一些初始化的操作。
  • process 这个方法是关键的部分,用于根据使用注解的类,生成相应的代码部分。使用javaPoet输出代码非常友好方便。

然后进行项目构建,在build->intermediates->javac->debug->classes->包名下面就能看到通过注解处理期生成的class源码。

public class BindClass$BinderViewClass {
  public static void main(String[] args) {
    System.out.println("刘恬希 is my daughter");
  }
}

这个方式进行的源码注入,仅仅是在编译的时候进行的,不影响运行时的运行速度,越来越多的开源框架采用这个方式替代反射的方式。

源码链接

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android-注解篇 的相关文章

随机推荐

  • linux下单独编译安装Apache rewrite_module

    转自 xff1a http blog chinaunix net uid 25266990 id 145266 html Apache已经编译安装完了 xff0c 上传程序 xff0c 发现必须得用rewrite xff0c 重新上载模块
  • linux 下安装php的openssl扩展

    1 安装openssl 解压php的源码包 tar zxvf php 5 3 8 tar gz cd soft php 5 3 8 ext openssl mv config0 m4 config m4 否则报错 xff1a 找不到conf
  • php安装 出现Sorry, I cannot run apxs. ***错误解决方法

    tar zvxf php 5 1 2 tar gz cd php 5 1 2 configure prefix 61 usr local php with mysql 61 usr local mysql with apxs2 61 usr
  • linux下mysql的root密码忘记解决方

    1 xff0e 首先确认服务器出于安全的状态 xff0c 也就是没有人能够任意地连接MySQL数据库 因为在重新设置MySQL的root密码的期间 xff0c MySQL数据库完全出于没有密码保护的 状态下 xff0c 其他的用户也可以任意
  • Pandas DataFrame

    http pandas pydata org pandas docs stable api html dataframe 构造函数 方法描述DataFrame data index columns dtype copy 构造数据框 属性和数
  • Apache域名自动指向虚拟主机目录

    用apache做项目时 xff0c 是经常需要在httpd vhost conf文件中配置虚拟主机 xff0c 用于单独的项目 xff0c 每新开一个项目就需要重新加一个配置 xff0c 着实麻烦 xff0c apache2其实是支持在配置
  • CentOS上升级PHP5.3至PHP5.5

    最近想玩玩儿symfony2 发现自己CentOS上的PHP版本还是5 3不符合要求 xff0c 所以想升级至5 5 但是 CentOS通过yum更新的源最新版本是5 3 无法升级 xff0c 通过网上查找资料 发现 更新下yum的源后 可
  • 启动docker web服务时 虚拟机端口转发 外部无法访问

    centos 7 docker 启动了一个web服务 但是启动时 报 WARNING IPv4 forwarding is disabled Networking will not work 网上查询了下 需要做如下配置 解决办法 xff1
  • CentOS7下docker启动报Error starting daemon: Error initializing network controller: could not delete ...v

    前一天晚上强制关闭了虚拟机 CentOS7 导致早上启动docker 报Error starting daemon Error initializing network controller could not delete ve endp
  • 在ubuntu1404下安装PHP AMQP拓展 并进行docker容器封装

    有项目用到过rabbitmq 是技术负责人搭建好环境 xff0c 今天自己在本地 ubuntu 开发 xff0c 重新搞了下 xff0c 在网上查了下资料 xff0c 把安装重点记录下来 项目开发语言PHP 需要与一台rabbitmqser
  • centos7下将docker的storage 由loop-lvm 更改为 direct-lvm

    本机服务器环境是centos7 xff08 vbox虚拟机 xff09 安装docker后 运行正常 xff0c 某次强行关闭了虚拟机 导致后续docker运行不起来 xff0c 发现报错是与storage有关 xff0c 所以网上查原因是
  • ubuntu1604下添加自启动项 开机启动docker开发环境

    在win7下安装的vbox虚拟机 xff0c 虚拟机起了一个ubuntu1604作为宿主机 xff0c 安装的docker服务 通过vbox的挂载工具将win7下的程序开发目录挂载到了ubuntu下 作为共享目录 docker安装的开发环境
  • apache2自签名证书开启ssl

    生成私钥文件 需要输入密码 openssl genrsa des3 out apache key 1024 防止APACHE启动 读取私钥文件也需要输入密码 去除密码输入 openssl rsa in apache key out apac
  • alpine 下编译php5.4的源码报Invalid configuration `x86_64-linux-musl'错误

    Invalid configuration 96 x86 64 linux musl 39 system 96 musl 39 not recognized configure error bin bash 在alpine3 7上编译php
  • golang GRPC安装

    1 下载protoc编译器 https github com protocolbuffers protobuf releases 将protoc exe放到系统环境变量设置的目录下 2 安装golang相关的package go get g
  • reStructuredText

    段落 段落必须由空行代替 段落1 段落2 内联标记 span class hljs emphasis 斜体 span span class hljs strong 粗体 span span class hljs code 96 96 代码块
  • win10下安装kubernets

    win10下安装docker for windows后 xff0c 新版是有一个kubernets选项 xff0c 选择启动后 xff0c 一直报 kubernets is starting 的错误 xff0c 原因是 xff0c kube
  • 嵌入式工程师的自我修养

    文章目录 前言一 认知的四个阶段1 不知不知2 知道不知3 知道己知3 1 软硬件3 2 网络3 3 安全技术 xff08 换成你自己的领域 xff09 3 4 真正知道的三个阶段3 4 1 会用3 4 2 了解怎么实现3 4 3 明白为什
  • 利用uORB机制实现数据在不同进程中通信

    uORB实际上是一种设计模式中的观察者模式 xff0c 用于实现一种一对多的依赖关系 xff0c 让多个观察者 xff08 Observer xff09 同时监听某一主题对象 xff08 Subject xff09 当这个主题对象 xff0
  • Android-注解篇

    1 什么是注解 从JDK 5 开始 xff0c Java 增加了注解 xff0c 注解是代码里的特殊标记 xff0c 这些标记可以在编译 类加载 运 行时被读取 xff0c 并执行相应的处理 通过使用注解 xff0c 开发人员可以在不改变原