注解和注解生成器
如果没有处理注解的工具,那么注解也不会有太大的作用。对于不同的注解有不同的注解处理器。虽然注解处理器的编写千变万化,但是也有处理标准。
参考文献:https://blog.csdn.net/jdfkldjlkjdl/article/details/110937447?utm_term=java%E6%B3%A8%E8%A7%A3%E5%A4%84%E7%90%86%E5%99%A8%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allsobaiduweb~default-2-110937447&spm=3001.4430
参考文献:https://blog.csdn.net/yamadeee/article/details/96479009
1.反射基本知识
类和类加载器:Java虚拟机是通过类加载器将类信息加载到Java虚拟机中的。
a.类信息中包含类中的所有信息,可以通过反射拿到类中所有的信息。
//待反射拿到的类
public class ReflectDemo {
public String name;
private int age;
public ReflectDemo(String name, int age) {
this.name = name;
this.age = age;
}
private ReflectDemo() {
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return showString();
}
private String showString() {
return "Demo{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
反射操作:
public class Demo {
public static void main(String[] args) {
//1.获取类中声明的构造函数,私有&公开
Constructor<?>[] declaredConstructors = ReflectDemo.class.getDeclaredConstructors();
for (Constructor<?> constructor : declaredConstructors) {
show("declaredConstructors", constructor);
}
//2.获取类中公开构造函数
Constructor<?>[] constructors = ReflectDemo.class.getConstructors();
for (Constructor<?> constructor : constructors) {
show("constructor", constructor);
}
//3.获取类中所有声明的函数,私有&公开
Field[] declaredFields = ReflectDemo.class.getDeclaredFields();
for (Field declaredField : declaredFields) {
show("declaredField", declaredField);
}
//4.获取类中所有的共有函数,包括继承的
Field[] fields = ReflectDemo.class.getFields();
for (Field field : fields) {
show("field", field);
}
Method[] declaredMethods = ReflectDemo.class.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
show("declaredMethod", declaredMethod);
}
Method[] methods = ReflectDemo.class.getMethods();
for (Method method : methods) {
show("method", method);
}
}
private static void show(String tag, Object object) {
System.out.println(String.format("Demo tag : %s output : %s", tag, object.toString()));
}
}
b.调用类中的方法
//反射调用对象中的toString方法
public class Demo {
public static void main(String[] args) {
try {
ReflectDemo reflectDemo = new ReflectDemo("JC", 23);
//1.通过反射调用对象的toString方法
Method toString = ReflectDemo.class.getMethod("toString");
show("toString", toString);
String ret = (String) toString.invoke(reflectDemo);
show("ret", ret);
//2.通过反射调用对象的field
Field name = ReflectDemo.class.getField("name");
String nameField = (String) name.get(reflectDemo);
show("nameField", nameField);
//3.通过反射调用构造函数
Constructor<ReflectDemo> constructor = ReflectDemo.class.getConstructor(String.class, int.class);
ReflectDemo reflectDemoConstructed = constructor.newInstance("luogou", 24);
show("constructor", reflectDemoConstructed.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
private static void show(String tag, Object object) {
System.out.println(String.format("Demo tag : %s output : %s", tag, object.toString()));
}
}
2.反射拿到注解信息
注解根据Retention属性,可表明是保留在source、class还是runtime三个阶段。
//注解定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstAnno {
String name();
int age();
String time();
}
//注解的使用
@FirstAnno(name = "JC", age = 27,time = "2021-8-7")
public class FirstClass {
}
由于,注解信息只在源码阶段存在,所以查看编译的字节码如下:
//1.保留策略为源码,所以编译的字节码没有注解信息了
public class FirstClass {
public FirstClass() {
}
}
//2.保留策略为class类,字节码就会有注解信息。但是注意,在运行期(代码中反射)是无法获取到的!!!字节码文件如下:
@FirstAnno(
name = "JC",
age = 27,
time = "2021-8-7"
)
public class FirstClass {
public FirstClass() {
}
}
//3.保留策略为runtime类,字节码会有注解信息,并且还是可以在运行期获取到。字节码为文件同保留策略为class时候。
public class Demo {
public static void main(String[] args) {
Annotation[] annotations = FirstClass.class.getAnnotations();
for (Annotation annotation : annotations) {
show("annotation", annotation);
}
Annotation[] declaredAnnotations = FirstClass.class.getDeclaredAnnotations();
for (Annotation declaredAnnotation : declaredAnnotations) {
show("declaredAnnotation", declaredAnnotation);
}
FirstAnno[] annotationsByTypes = FirstClass.class.getAnnotationsByType(FirstAnno.class);
for (FirstAnno annotationsByType : annotationsByTypes) {
show("annotationsByType", annotationsByType);
}
FirstAnno[] declaredAnnotationsByTypes = FirstClass.class.getDeclaredAnnotationsByType(FirstAnno.class);
for (FirstAnno declaredAnnotationsByType : declaredAnnotationsByTypes) {
show("declaredAnnotationsByType", declaredAnnotationsByType);
}
}
private static void show(String tag, Object object) {
System.out.println(String.format("Demo tag : %s output : %s", tag, object.toString()));
}
}
3.三种保留策略的补充说明
- 如果保留策略为source,那么注解信息只会保存在.java源文件中;
- 如果保留策略为class,那么注解信息只会保存在.java源文件和.class字节码文件中;
- 如果保留策略为runtime,那么注解信息会保存在.java源文件、.class字节码文件和在类加载后。注意,类加载后保留了注解信息,那么就可以在代码中通过反射拿到相应的注解信息了!
通常有两种,一是编译时注解处理器,一是运行时注解处理器。上面“2.反射拿到注解信息”其实就是一种运行时注解处理器的实现了。而编译时注解处理器,在工程编译过程中对注解进行处理,也就是执行操作的期间只在编译期间内,这会在“6.Android Studio中的注解处理器”中进行举例。
4.获取泛型参数
@FirstAnno(name = "JC", age = 27,time = "2021-8-7")
public class FirstClass<T, R> {
T name;
R info;
}
public class Demo {
public static void main(String[] args) {
FirstClass<Integer, String> demo = new FirstClass<>();
TypeVariable<? extends Class<? extends FirstClass>>[] typeParameters = demo.getClass().getTypeParameters();
for (TypeVariable<? extends Class<? extends FirstClass>> typeParameter : typeParameters) {
show("typeParameter", typeParameter);
}
}
private static void show(String tag, Object object) {
System.out.println(String.format("Demo tag : %s output : %s", tag, object.toString()));
}
}
5.注解处理器
如果没有处理注解的工具,那么注解也只会在source、class或者runtime的代码中增加部分信息,是不会有太大的作用。想要注解生效,就需要针对这些信息进行解析的。
自定义注解处理器,其实就是反射获取注解的属性,例如运行时注解的获取在类型2中已经展示了。其实,Java中有更好的方式,通过继承实现AbstractProcessor即可。
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.example.processors.DemoAnno")
public class CustomProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager();
for (Element ele : roundEnvironment.getElementsAnnotatedWith(DemoAnno.class)) {
messager.printMessage(Diagnostic.Kind.NOTE, "printMessage:" + ele.toString() + ", kind: " + ele.getKind());
// 获取注解的元数据
DemoAnno annotation = ele.getAnnotation(DemoAnno.class);
String annoInfo = String.format("annotation class: %s, value: %d, name: %s", annotation.getClass(), annotation.value(), annotation.name());
messager.printMessage(Diagnostic.Kind.NOTE, annoInfo);
}
return true;
}
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotations = new LinkedHashSet<>();
annotations.add(DemoAnno.class.getCanonicalName());
return annotations;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
6.Android Studio中的注解处理器
在Android Studio中使用自定义注解处理器的方法,是无法通过继承实现AbstractProcessor的,这个在Android app或者library模块中都是无法使用的,Android中应该是有更加好的方式实现注解处理器功能的(可能是apt依赖的使用,后续研究补充)。
所以,想要在Android Studio中使用JDK自带的AbstractProcessor方式,那么就只能够在Android中创建Java或者Kotlin原生库,然后在内部实现相应功能,再被上层Android的app或者library模块使用。注意:Java原生库也无法通过依赖,然后使用Android的app或者library模块。
所以,通过在Android Studio中“File-New-New Module-Java or Kotlin Library”,创建原生的Java库。
项目结构如下所示,annotation和processors模块都是原生Java模块,而app就是Android的模块。
- annotation模块是一个Java库,定义了各种注解内容,以底层api工具包被外部依赖。内容如下:
java
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface DemoAnno {
int value();
String name();
}
- processors模块也是一个Java库,定义了各种注解处理器,被外部以注解处理器方式依赖。内容见“5.注解处理器”。另外,gradle文件需要依赖annotation模块:
dependencies {
implementation project(':annotation')
// auto service
implementation 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
}
- app模块,直接使用了processors模块,并以来annotation模块,其gradle文件如下:
dependencies {
//依赖注解接口模块
implementation project(path: ':annotation')
//使用注解处理器
annotationProcessor project(':processors')
}
//MainActivity.java
@DemoAnno(value = 123, name = "JC")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
//MyService.java
@DemoAnno(value = 666, name = "GoGoGO")
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
那么编译项目的时候便会在“build”窗口中看到如下日志输出:
.......
> Task :app:compileDebugJavaWithJavac
The following annotation processors are not incremental: processors.jar (project :processors).
Make sure all annotation processors are incremental to improve your build speed.
注: printMessage:com.example.annotationdemo.MainActivity, kind: CLASS
注: annotation class: class com.sun.proxy.$Proxy197, value: 123, name: JC
注: printMessage:com.example.annotationdemo.MyService, kind: CLASS
注: annotation class: class com.sun.proxy.$Proxy197, value: 666, name: GoGoGO
.......
BUILD SUCCESSFUL in 4s
31 actionable tasks: 31 executed
Build Analyzer results available