python 注解annotation_Annotation注解(一)- 基础

这篇博客,主要讲解关于注解的一些基本知识,包括注解的概念、分类、作用,常见注解的定义及其解析方式等。

Annotation的概念

1. 概念

关于Annotation注解的概念,我们可以看下官方的解释:

Annotations, a form of metadata;

provide data about a program that is not part of the program itself.

Annotations have no direct effect on the operation of the code they annotate

Annotation注解是Java中的一种元数据,它可以往源代码中添加额外的数据,并且对注解的代码无直接影响;包名、类、成员方法、成员属性、参数都可以被注解。

2. 作用

注解可以让我们的代码更加简洁,并且增加代码的复用性,避免冗余代码。

如果细分起来,注解主要有3个作用:

编译时标记:在编译时,通过标记代码,来提示信息、检查错误

编译时处理:在编译时,通过构建代码,来动态生成一些额外的代码、或者Java/XML等文件

运行时处理:在运行时,我们可以动态的获取注解信息

3. 分类

1. 标准Annotation

标准Annotation是指Java自带的Annotation。

eg:@Override[重写]、@Deprecated[不鼓励使用]、@SuppressWarnings[忽略警告]等。

2. 元Annotation

元Annotation是指注解Annotation的Annotation。

eg:@Retention, @Target, @Inherited, @Documented等。

3. 自定义Annotation

我们可以利用元Annotation来自定义一些自己的Annotation。

元Annotation

元Annotation是指注解Annotation的Annotation。

@Documented:Annotation是否会保存到JavaDoc文档中

@Inherited:Annotation是否可以被继承,默认是false

@Target(ElementType[] types):Annotation可以用来注解哪些元素

ElementType:元素类型,比如类、方法、变量、参数等

@Retention(RetentionPolicy ploicy):Annotation的保留策略

RetentionPolicy.SOURCE:Annotation仅保留在java源码中

- 大都为Mark Annotation,做一个警示、校验的作用

RetentionPolicy.CLASS:Annotation保留在java源码、class文件中

- 默认的保留策略,一般配合Processor注解处理器,在构建时动态生成代码

RetentionPolicy.RUNTIME:Annotation保留在java源码、class文件、运行时

- 与CLASS的区别是,这类Annotation在运行时会被加载到JVM中,因此我们可以在程序运行的过程中动态获取到Annotation的相关信息

自定义Annotation

我们先来看一下一个最为基础的Annotation示例代码(这是一个自动生成类头信息的一个注解)

调用

@ClassInfo (

author = "xzqbetter@163.com",

date = "11/17/2017",

currentRevision = 6,

reviewers = {"Alice", "Bob", "Cindy"} // Note array notation

)

public class Test1 {

// class code goes here

}

定义

public @interface ClassInfo {

String author();

String date();

int currentRevision() default 1;

String[] reviewers(); // Note use of array

}

解释

1.注解的定义:可以看到,我们利用@interface,定义了一个注解类,之后就可以在代码中使用这个注解

@interface是interface的一种变异形式

2.参数的定义:在注解类内部,我们声明了很多抽象方法,来定义注解所需的参数

对应关系:方法名 - 参数名,方法的返回值 - 参数类型

返回值的限制:只能是基本类型、String、Class、Enumeration、Annotation及其一维数组

方法的限制:这些方法没有方法体、没有参数、不能抛异常,并且只能用public abstract进行修饰

可以用default来指定默认值

如果只有一个参数,可以用value()代替(在使用的时候无需写参数名)

可以利用classInfo.author()来获取参数值

解析Annotation

完成自定义Annotation后,我们还需要知道,针对这些注解,我们要做哪些相关的处理,这就涉及到了Annotation的解析操作。

解析Annotation,通常分为:对运行时Annotation的解析、对编译时Annotation的解析;

解析Annotation,其实就是如何从代码中找到Annotation,通常我们的做法是:

用反射的方式获取Annotation,运行时Annotation的解析方式

借助apt工具获取Annotation,编译时Annotation的解析方式

另外如果我们需要生成额外的代码、文件,则还需要借助JavaPoet API

反射

反射的解析方式,通常运用在运行时Annotation的解析。

反射是指:利用Class、Field、Method、Construct等reflect对象,获取Annotation:

field.getAnnotation(Annotation.class):获取某个Annotation

field.getAnnotations():获取所有的Annotation

field.isAnnotationPresent(Annotation.class):是否存在该Annotation

这样,我们就可以在程序运行过程中,动态的获取Annotation的信息。

借助反射进行解析,这在一定程度上会影响程序性能

APT工具

概念

APT:是一个注解处理工具 Annotation Processing Tool

作用:利用apt,我们可以找到源代中的注解,并根据注解做相应的处理

根据注解,生成额外的源文件或其他文件

编译生成的源文件和原来的源文件,一起生成class文件

利用APT,在编译时生成额外的代码,不会影响性能,只是影响项目构建的速度

Android Studio配置

使用apt工具前,需要对gradle做一些基本的配置:

1.在project的build.gradle中,添加apt的依赖

dependencies {

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

}

2.在module的build.gradle中,使用apt插件

apply plugin: 'com.neenbedankt.android-apt'

JavaPoet

JavaPoet:是一个自动生成Java代码的Java API。

在使用前,我们需要导入相关的依赖:

compile 'com.squareup:javapoet:1.7.0'

JavaPoet主要有以下几个关键类:

JavaFile:生成Java文件

TypeSpec:定义类

MethodSpec:定义方法

MethodSpec

MethodSpec主要用来定义方法。

MethodSpec mainSpec = MethodSpec.methodBuilder("main") // 方法名

.addModifiers(Modifier.PUBLIC, Modifier.STATIC) // 修饰符

.addParameter(String[].class, "args") // 参数

.returns(TypeName.VOID) // 返回值

.addStatement("$T.out.println($S)", System.class, "Hello,World!!") // 具体代码

.build();

// 添加代码的方法

mainBuilder.addCode("System.out.println(\"Hello,World!\")");

mainBuilder.addStatement("$T.out.println($S)",System.class,"Hello, World!");

mainBuilder.addStatement("activity.$L= ($T) activity.findViewById($L)",

element, ClassName.get(member.asType()), injectViewAnno.value() );

// 这里的element表示一个成员变量, injectViewAnno是一个Annotation, value是注解的参数值

方法名:methodBuilder()

修饰符:addModifiers(Modifier)

返回值:return(TypeName)

参数:addParameter(Class, name)

添加代码:addStatement末尾会自动添加换行符,addCode末位不会自动添加换行符

-$T:表示需要import的类

-$S:表示字符串,会自动添加双引号

-$L:表示变量,不带双引号

TypeSpec

TypeSpec主要用来定义类。

// 外部类

TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld") // 类名

.addModifiers(Modifier.PUBLIC, Modifier.FINAL) // 修饰符

.addMethod(mainSpec) // 方法

.build();

// 内部类

TypeSpec typeSpec = TypeSpec.anonymousClassBuilder("innerClass") // 内部类的类名

.addSuperinterface(OutClass.class) // 外部类的Class

.build();

JavaFile

JavaFile是真正生成Java文件的核心类。

try {

JavaFile javaFile = JavaFile.builder("com.example.seven", typeSpec).build(); // 指定包名+类

javaFile.writeTo(processingEnv.getFiler()); // 固定写法

} catch (IOException e) {

e.printStackTrace();

}

运行时Annotation

运行时Annotation,是指@Retention为RetentionPolicy.RUNTIME的注解。

对于这类注解,我们通常配合反射进行解析。

运行时Annotation的解析,需要借助反射进行解析,这在一定程度上会影响程序性能。

早期的依赖注入框架,大都属于运行时处理。

示例代码

public static void main(String[] args) {

try {

Class clazz = Class.forName("com.example.Runtime");

for (Method method : clazz.getMethods()) {

MethodAnno methodAnno = method.getAnnotation(

MethodAnno.class);

if (methodAnno != null) {

System.out.println("method name:" + method.getName());

System.out.println("method author:" + methodAnno.author());

System.out.println("method version:" + methodAnno.version());

System.out.println("method date:" + methodAnno.date());

}

}

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

编译时Annotation

编译时Annotation,是指@Retention为RetentionPolicy.CLASS的注解。

对于这类注解,我们通常借助apt工具、注解处理器Processor进行解析。

编译时Annotation,利用apt工具,在编译时,自动生成一些额外的代码,相比运行时Annotation更加高效。

定义一个编译时Annotation的标准流程如下:

定义注解:@interface

定义注解处理器:重写AbstractProcessor的2个方法

实现注解处理器:JavaPoet

注册注解处理器:@AutoService

使用注解,build工程

下面,我们将一一进行讲解

1. 定义注解

参考,上面的【自定义注解】部分

2. 定义注解处理器

概念

注解处理器是一个继承自AbstractProcessor的一个Java类,内部主要定义了:

要处理的注解是谁:通过getSupportedAnnotationTypes方法进行指定

如何处理这个注解:通过process方法进行指定

注意:AbstractProcessor只能用在Java项目中,在Android项目中无法使用,因为Android内部删除了这个类;因此如果想要在Android项目中,使用这个类,需要新建一个Java Library库。

示例代码

@AutoService(Processor.class)

public class AutoCreateProcessor extends AbstractProcessor {

@Override

public Set getSupportedAnnotationTypes() {

// 返回的Set集合,是注解类ClassName的集合

// AutoCreate是一个自定义的Annotation

return Collections.singleton(AutoCreate.class.getCanonicalName());

}

@Override

public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {

// 利用JavaPoet进行相关处理

return false;

}

}

解释

getSupportedAnnotationTypes

这个方法主要用来指定,当前的注解处理器,需要处理哪些Annotation

这些Annotation,将会通过一个Set集合的形式返回

Set集合内部存储的是,所有注解的类名className

process

这个方法主要用来定义,针对这些注解,我们要做哪些相应的处理

通常,我们会在方法内部,利用JavaPoet创建相关的Java类、或是XML文件

返回值表示,当前Processor是否接收这组Annotation,如果接收,后续其他的Processor将不会继续处理

3. 实现注解处理器

实现注解处理器,主要是利用JavaPoet对process部分的代码进行完善补充,在下一个博客中,我们将会详细介绍。

4. 注册注解处理器

注册注解处理器,就是告诉APT这个类是一个Annotation Processor

我们可以借助AutoService进行快速注册:

添加AutoService的依赖

在类上注解 @AutoService(Processor.class),完成注册

compile 'com.google.auto.service:auto-service:1.0-rc2'

@AutoService(Processor.class)

5. build工程

完成以上步骤,我们build下工程,就会在build目录下自动生成对应的java/class文件

java文件在build/generated文件夹下

class文件在build/intermediates文件夹下

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值