元数据(metadata)
元数据就是描述数据的数据,对数据及信息资源的描述性信息.比如说一个文本文件,有创建时间,创建人,文件大小等数据,这都可以理解为是元数据.
在java中,元数据以标签的形式存在java代码中,它的存在并不影响程序代码的编译和执行,通常它被用来生成其它的文件或运行时知道被运行代码的描述信息。java当中的javadoc和注解都属于元数据.
元注解
元注解就是定义注解的注解,是java提供给我们用于定义注解的基本注解。位于java.lang.annotation包下。
@interface
@interface是java中用于声明注解类的关键字.使用该注解表示将自动继承java.lang.annotation.Annotation类,该过程交给编译器完成.
1 | public @interface Override { |
注意:在定义注解时,不能继承其他注解或接口.
@Retention
@Retention:该注解用于定义注解保留策略,即定义的注解类在什么时候存在(源码阶段 or 编译后 or 运行阶段).
1 | @Retention(RetentionPolicy.SOURCE):注解仅在源码中保留,class文件中不存在。 |
@Target
该注解用于定义注解的作用目标,即注解可以用在什么地方,比如是用于方法上还是用于字段上。
1 | @Target(ElementType.TYPE):用于接口(注解本质上也是接口),类,枚举。 |
完整的@Override定义:
1 | @Target(ElementType.METHOD) |
@Inherited
默认情况下,我们自定义的注解用在父类上不会被子类所继承。如果想让子类也继承父类的注解,即注解在子类也生效,需要在自定义注解时设置@Inherited。一般情况下该注解用的比较少。
@Documented
该注解用于描述其它类型的annotation应该被javadoc文档化,出现在api doc中。
1 | @Documented |
@Deprecated和@deprecated
@Deprecated被javac识别和处理,而@deprecated则是被javadoc工具识别和处理.因此当我们需要在源码标记某个方法已经过时应该使用@Deprecated,如果需要在文档中说明则使用@deprecated。
1 | public class SimpleCalculator { |
@SuppressWarnning
关闭编译器对类的警告。
该注解被用于有选择的关闭编译器对类,方法,成员变量即变量初始化的警告。
可选参数:1
2
3
4
5
6
7
8
9
10
11
12
13deprecated:使用已过时类,方法,变量。
unchecked:执行了未经检查的转换时的警告,如使用集合时,使用泛型来作为集合保存时的类型。
fallthrough:使用switch,但是没有break时。
path:类路径,源文件路径等有不存在的路径。
serial:可序列化的类上缺少serialVersionUID定义时的警告。
finally:任何finally字句不能正常完成时的警告。
all:以上所有情况的警告。
Java定义注解
1 | @Documented |
使用:
1 | @Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true, priority = 100) |
运行时注解(Kotlin版)
运行时注解通过反射实现代码插入。
注解处理器实现为Model赋值1
2
3
4
5
6
7
8
9
10
11
12
13
14object 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 | class Fingerlings @PersonMeta("hsh") constructor(var id: String,var name: String,var age: Int){ |
注解1
2
3@Target(AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.RUNTIME)
annotation class PersonMeta(val name:String = "fingerlings",val age:Int = 20)
使用
1 | Fingerlings().apply { |
自定义ButterKnife
1 | @Target(AnnotationTarget.FIELD) |
1 | object FishKnife { |
1 | class MainActivity : AppCompatActivity() { |
运行时注解通过反射实现,在运行效率上肯定会有所缺失。
编译时注解(Jar形式)
apt在新版的gradle中已经被抛弃了,新版的gradle无法生成对应的编译代码,在Gradle2.2中可以支持。
java文件夹下分别创建resources>META-INF>services文件夹,再创建javax.annotation.processing.Processor文件:
在文件中写出处理器的包路径:1
2com.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
50public 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
71public 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 | @Target(ElementType.METHOD) |
编译时注解需要使用Gradle的jar task进行编译,编译完成后的jar文件即可作为库引入使用。
1 | public class MainActivity extends AppCompatActivity { |
apt、annotationProcessor
上面直接引用jar包的形势,最终注解处理器的代码也会连带被打包进apk中。如果不想这样,可以将注解处理器作为单独的模块进行额外引用,并且在引用时,使用apt或者其替代者annotationProcessor 来引用该模块。
以EventBus为例,其注解处理器就是单独引入的。
apt形式:
annotationProcessor形式:
参考:
https://blog.csdn.net/dd864140130/article/details/53875814
https://blog.csdn.net/dd864140130/article/details/53957691