Java:注解、反射和动态代理
注解反射和动态代理
注解
注解的定义
注解的作用或者意义是什么?
注解本身没有任何含义,单独的注解就是一种或注解,它需要结合其他如反射、插桩等技术才有意义
Java注解(Annotation)又称Java标注,是JDK1.5引入的一种注解机制。是元数据的一种形式,提供关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有用直接影响
自定义注解
使用@interface自定义注解
\x7e~~ Java
@Retention(RetentionPolicy.ROUTIME) //保留时
@Target({ElementType.TYPE}) //作用目标
public @interface Lance {
int a();
String b();
}
1 |
|
创建实现类
在compile模块下创建注解处理器的实现类
1 | public class TestProcessor extends AbstractProcessor { |
在process方法,由javac调用,写什么代码就做什么事情
注册
在compile模块的main文件夹下创建:resource -> META-INF -> services -> javax.annotation.prrocessing.Processor中进行注册
写上APT实现类
1
com.wuliner.compile.TestProcessor
配置允许APT处理的注解
以下两种方法选一种就可以了
- 重写该实现类的
getSuppotedAnnotationTypes方法
1 | /** |
使用
@SupportedAnnotationTypes()注解括号中写注解类的全类名,如
@SupportedAnnotationTypes({"com.wuliner.annotation.MyClass"})
1 |
|
引入APT所在模块
在build.gradle
Java中使用
annotationProcessor,如annotationProcessor project('compiler')kolin中使用kapt
注意:apt不会打包进入APK,只会在编译时参与编译
APT的使用
在类名前添加注解,如
@MyClass在Processor实现类中
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
public class CwProcessor extends AbstractProcessor {
/**
* javac 调用此方法
* @param set
* @param roundEnvironment
* @return
*/
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//写什么代码就做什么事情
//process:javac编译时的一个回调
Messager messager = processingEnv.getMessager();
//打印日志
messager.printMessage(Diagnostic.Kind.NOTE, "=========>");
return false;
}
/**
* 允许此注解处理器处理的注解
* @return
*/
// @Override
// public Set<String> getSupportedAnnotationTypes() {
// return super.getSupportedAnnotationTypes();
// }
}注:
使用Message打印日志:
1
2
3Messager messager = processingEnv.getMessager();
//打印日志
messager.printMessage(Diagnostic.Kind.NOTE, "=========>");引入注解模块
使用
annotationProcessor引入注解处理器模块, 若使用kotlin,则使用kapt1
annotationProcessor(project(":compile"))
apt不会打包进入apk,只会在编译时参与编译
在Build窗口中查看日志
为什么APT中process方法会执行多次?
Java将源码编译成class文件的过程:
java编译过程:词法分析、语法分析、填充符号表、注解处理器处理注解、语义分析、解语法糖、生成字节码。
注解处理器可以增删改抽象语法树的任意元素。执行到注解处理器,都会重新执行词法分析、语法分析、填充符号表步,直到注解处理器不再对语法树进行修改为止,每一次的循环过程都称为一次Round。
process第一个参数set集合是要处理的注解集合,如果这个集合为null了,就不需要处理了。写代码就这样去做。
1
2
3if(!set.isEmpty()){
//…执行处理
}或者通过
roundEnvironment.processingOver()将round结束
字节码增强(插桩)
字节码增强技术相当于是一把打开运行时JVM的钥匙,利用它可以动态地对运行中的程序做修改,也可以跟踪JVM运行中程序的状态。
此外,我们平时使用的动态代理、AOP也于字节码增强密切相关,它们实质上还是利用各种手段生成或修改符合规范的字节码文件。
综上所述,掌握字节码增强后可以高效的定位并快速修复一些棘手的问题(如线上性能问题,方法出现不可控的出入参需要紧急加日志等问题),也可以在开发中减少冗余代码,大大提高开发效率
反射
反射实现findViewById
通过反射获取类的成员
1 | public static void injectView(Activity activity) { |
注意:
Filed获得自己+父类的成员(不包括private,只能是public)
DecleredFile只能获得自己的成员(不包括父类,所有作用域)
可以通过getSuperClass()获取父类的class文件
创建注解
创建注解用来标识,用于筛选
1 |
|
在需要标识的属性上使用注解
这一传入了TextView对应的id,用来实现findViewById的功能
1 |
|
筛选Field
1 | public class InjectUtils { |
先通过
Filed的isAnnotationPresent()方法,传入自定义注解的class文件用来判断属性是否被InjectView注解声明再通过
getAnnotation()方法,传入注解的class文件,获得对应的注解类再通过这个类获取对应的属性,获得注解中设置的id后,获取对应的View
然后通过反射设置属性的值
先用
filed.setAccessible(true)设置访问权限,允许操作private的属性通过
filed.set(activity, view),将TextView值设置为对应的View
调用方法
通过反射获取泛型的真实类型
Type类
TypeVariable泛型类型变量。可以泛型上下限等信息
ParameterType具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
GenericArrayType当需要描述的类型是泛型类的数据时,比如List[],Map[],此接口会作为Type的实现
WildcardType通配符泛型,获得上下限信息
1 | Type type = new TypeRefrence<Response<Data>>().getType(); //不行正常运行 |
有花括号,代表是匿名内部类,没有添加的话就代表一个对象


