注解

元数据(metadata)

元数据就是描述数据的数据,对数据及信息资源的描述性信息.比如说一个文本文件,有创建时间,创建人,文件大小等数据,这都可以理解为是元数据.

在java中,元数据以标签的形式存在java代码中,它的存在并不影响程序代码的编译和执行,通常它被用来生成其它的文件或运行时知道被运行代码的描述信息。java当中的javadoc和注解都属于元数据.

元注解

元注解就是定义注解的注解,是java提供给我们用于定义注解的基本注解。位于java.lang.annotation包下。

@interface

@interface是java中用于声明注解类的关键字.使用该注解表示将自动继承java.lang.annotation.Annotation类,该过程交给编译器完成.

1
2
public @interface Override {
}

注意:在定义注解时,不能继承其他注解或接口.

@Retention

@Retention:该注解用于定义注解保留策略,即定义的注解类在什么时候存在(源码阶段 or 编译后 or 运行阶段).

1
2
3
4
5
6
7
8
9
10
11
@Retention(RetentionPolicy.SOURCE):注解仅在源码中保留,class文件中不存在。

@Retention(RetentionPolicy.CLASS):注解在源码和class文件中都存在,但运行时不存在,即运行时无法获得,该
策略也是默认的保留策略。

@Retention(RetentionPolicy.RUNTIME):注解在源码,class文件中存在且运行时可以通过反射机制获取到。


@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Target

该注解用于定义注解的作用目标,即注解可以用在什么地方,比如是用于方法上还是用于字段上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Target(ElementType.TYPE):用于接口(注解本质上也是接口),类,枚举。

@Target(ElementType.FIELD):用于字段,枚举常量。

@Target(ElementType.METHOD):用于方法。

@Target(ElementType.PARAMETER):用于方法参数。

@Target(ElementType.CONSTRUCTOR):用于构造参数。

@Target(ElementType.LOCAL_VARIABLE):用于局部变量。

@Target(ElementType.ANNOTATION_TYPE):用于注解。

@Target(ElementType.PACKAGE):用于包。

完整的@Override定义:

1
2
3
4
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Inherited

默认情况下,我们自定义的注解用在父类上不会被子类所继承。如果想让子类也继承父类的注解,即注解在子类也生效,需要在自定义注解时设置@Inherited。一般情况下该注解用的比较少。

@Documented

该注解用于描述其它类型的annotation应该被javadoc文档化,出现在api doc中。

1
2
3
4
5
6
7
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {

ElementType[] value();
}

image

@Deprecated和@deprecated

@Deprecated被javac识别和处理,而@deprecated则是被javadoc工具识别和处理.因此当我们需要在源码标记某个方法已经过时应该使用@Deprecated,如果需要在文档中说明则使用@deprecated。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SimpleCalculator {
/**
* @param x
* @param y
* @return
*
* @deprecated deprecated As of version 1.1,
* replace by <code>SimpleCalculator.add(double x,double y)</code>
*/
@Deprecated
public int add(int x, int y) {
return x+y;
}

public double add(double x,double y) {
return x+y;
}

}

@SuppressWarnning

关闭编译器对类的警告。

该注解被用于有选择的关闭编译器对类,方法,成员变量即变量初始化的警告。

可选参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
deprecated:使用已过时类,方法,变量。

unchecked:执行了未经检查的转换时的警告,如使用集合时,使用泛型来作为集合保存时的类型。

fallthrough:使用switch,但是没有break时。

path:类路径,源文件路径等有不存在的路径。

serial:可序列化的类上缺少serialVersionUID定义时的警告。

finally:任何finally字句不能正常完成时的警告。

all:以上所有情况的警告。

Java定义注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {

// 类似于定义方法
// 返回值指定了参数类型
// 通过default为其设置默认值
ThreadMode threadMode() default ThreadMode.POSTING;

/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;

/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}


@Retention(SOURCE)
@Target({ANNOTATION_TYPE})
public @interface IntDef {
/** Defines the allowed constants for this element */
long[] value() default {};

/** Defines whether the constants can be used as a flag, or just as an enum (the default) */
boolean flag() default false;
}

使用:

1
2
3
4
5
6
7
@Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true, priority = 100)
public void test(String str) {}


@IntDef({RED, BLUE, YELLOW, GREEN, PURPLE, WHITE, BLACK})
@Retention(RetentionPolicy.SOURCE)
public @interface Color{};

运行时注解(Kotlin版)

运行时注解通过反射实现代码插入。

注解处理器实现为Model赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
object AnnotationProcessor {
fun init(any: Any) {
any.javaClass.declaredConstructors.forEach {
if (it.isAnnotationPresent(PersonMeta::class.java)) {
val personMeta = it.getAnnotation(PersonMeta::class.java)
(any as? Fingerlings)?.let {
it.age = personMeta.age
it.name = personMeta.name
it.id = "110"
}
}
}
}
}

实验类

1
2
3
4
5
6
7
8
9
10
11
class Fingerlings @PersonMeta("hsh") constructor(var id: String,var  name: String,var  age: Int){
init {
println("id = $id , name =$name , age = $age")
}

constructor() : this("", "", 0)

override fun toString(): String {
return "Fingerlings(id='$id', name='$name', age=$age)"
}
}

注解

1
2
3
@Target(AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.RUNTIME)
annotation class PersonMeta(val name:String = "fingerlings",val age:Int = 20)

使用

1
2
3
4
5
Fingerlings().apply {
AnnotationProcessor.init(this)
}.toString().run {
println(this)
}
自定义ButterKnife
1
2
3
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class FishInject(val id: Int, val clickable: Boolean)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
object FishKnife {
fun init(obj: Activity, root:View) {
obj.javaClass.declaredFields.forEach {
it.getAnnotation(FishInject::class.java)?.run {
it.isAccessible = true
it.set(obj, root.findViewById(this.id))
root.findViewById<View>(this.id).isClickable = this.clickable
}
}
}

fun inject(activity: Activity) {
init(activity,activity.window.decorView)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
class MainActivity : AppCompatActivity() {

@FishInject(R.id.test, false)
private lateinit var test: Button

@Print
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
FishKnife.inject(this)
}
}

运行时注解通过反射实现,在运行效率上肯定会有所缺失。

编译时注解(Jar形式)

apt在新版的gradle中已经被抛弃了,新版的gradle无法生成对应的编译代码,在Gradle2.2中可以支持。

java文件夹下分别创建resources>META-INF>services文件夹,再创建javax.annotation.processing.Processor文件:
image
在文件中写出处理器的包路径:

1
2
com.example.injectdemo2.CodeProcessor
com.example.injectdemo2.PrintProcessor

编译时注解除了处理器不同外,其他与运行时注解相同。

打印编译信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class PrintProcessor extends AbstractProcessor {

// 消息输出类,此处是输出的是控制台
private Messager messager;

/**
* 实际处理注解的地方,需要在这里编写扫描、处理注解的代码,
* 以及最终生成的java文件
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.out.println("PrintProcessor === >processing ........");
for (TypeElement te :
annotations) {
for (Element e :
roundEnv.getElementsAnnotatedWith(te)) {
messager.printMessage(Diagnostic.Kind.NOTE, e.toString());
}
}
return true;
}

/**
* 由注解处理器自动调用
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
messager = processingEnv.getMessager();
}

/**
* 返回要处理的注解集合
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
LinkedHashSet<String> annotations = new LinkedHashSet<>();
annotations.add(Print.class.getCanonicalName());
return super.getSupportedAnnotationTypes();
}

/**
* 用来指定支持的Java版本,一般为最新的即可
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}

动态编译生成一个Java类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class CodeProcessor extends AbstractProcessor {


private Messager messager;
// 用来创建新的源文件,class文件以及辅助文件
private Filer filer;
// 提供用于操作TypeMirror的工具方法
// 代表了Java中各种数据类型
private Types typeUtils;

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

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element e : roundEnv.getElementsAnnotatedWith(Code.class)
) {
Code annotation = e.getAnnotation(Code.class);
TypeElement te = (TypeElement) e.getEnclosingElement();
generateCode(e, annotation, te);
}
return true;
}

// 通过String拼接,生成一个Java文件
private void generateCode(Element e, Code annotation, TypeElement te) {
try {
JavaFileObject sourceFile = filer.createSourceFile(te.getQualifiedName() + "_Info");
Writer writer = sourceFile.openWriter();
messager.printMessage(Diagnostic.Kind.NOTE, "Create Java File: " + sourceFile.toUri());

//输出Java类信息
try {
String pack = te.getQualifiedName().toString();
PrintWriter pw = new PrintWriter(writer);
pw.println("package " + pack.substring(0, pack.lastIndexOf('.')) + ";"); //create package element
pw.println("\n class " + te.getSimpleName() + "_Info_Autogenerate {");//create class element
pw.println("\n protected " + te.getSimpleName() + "_Info_Autogenerate() {}");//create class construction
pw.println(" protected final void message() {");//create method
pw.println("\n//" + e);
pw.println("//" + annotation);
pw.println("\n System.out.println(\"author:" + annotation.author() + "\");");
pw.println("\n System.out.println(\"date:" + annotation.date() + "\");");
pw.println(" }");
pw.println("}");
pw.flush();
} finally {
writer.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}

@Override
public Set<String> getSupportedAnnotationTypes() {
LinkedHashSet<String> annotations = new LinkedHashSet<>();
annotations.add(Code.class.getCanonicalName());
return annotations;
}

@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Code {
public String author();

public String date() default "";
}


@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface Print {
}

编译时注解需要使用Gradle的jar task进行编译,编译完成后的jar文件即可作为库引入使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainActivity extends AppCompatActivity {

@Override
@Print
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
process();
}

@Code(author = "closedevice", date = "20161225")
private void process() {
new MainActivityAutogenerate().message();
}


}

apt、annotationProcessor

上面直接引用jar包的形势,最终注解处理器的代码也会连带被打包进apk中。如果不想这样,可以将注解处理器作为单独的模块进行额外引用,并且在引用时,使用apt或者其替代者annotationProcessor 来引用该模块。

以EventBus为例,其注解处理器就是单独引入的。

apt形式:
image

annotationProcessor形式:
image

参考:
https://blog.csdn.net/dd864140130/article/details/53875814
https://blog.csdn.net/dd864140130/article/details/53957691