什么是办法id
咱们日常开发中,可能会遇到这么一个需求,便是想要把每一个办法,都转换为一个个Int类型/Long类型(推荐)来标明,这样的优点是效率非常高,比方在慢办法插桩里边,咱们假如直接记录一个办法耗时,同时也要记录是哪个办法发生的耗时对吧,咱们一般会以String类型办法名称去记录这么一条信息。但是以String可能会比较麻烦
原因在于String长度是不固定的,假如在插桩场景之下,假如需求刺进的办法许多,那么它的耗费也是非常巨大的。
所以程序员们想啊想,既然字符串那么不可靠,那么假如咱们能用一个数字代表一个办法,那么不管怎么变化,只需这个办法本身没有改动,那么是不是永远也能找到固定的值?这也通常被称为办法id。
办法id生成
生成办法ID,最主要的是寻找一个映射联系,用于把办法映射成咱们界说的ID,这儿的ID能够是一个递增的数字,比方咱们能够采用一个原子类型,比方AtomicInteger,每次遇到一个新办法的时候,这个数就递增即可,原子类型确保了咱们能够在多线程环境下处理办法ID的正确生成,比方咱们只需调用incrementAndGet即可处理并发问题。
那么还有一个问题,便是办法,咱们怎么把一个办法跟一个id绑定起来呢?处理这个问题前,还有一个问题需求解决,怎么判别办法是不是同一个办法?
这儿能够总结成一条公式:办法仅有值 = 办法名+办法签名+类特征
下面咱们一一说明这几点:
办法名
一个办法的办法名,比方java/kotlin,便是一个办法的重要特征,当然,咱们也不能仅仅依托办法名去判别,因为java虚拟机是支持重载的,比方fun1(a:Int),跟fun1(a:Int,b:Int),虽然办法名共同,但是也不是同一个办法。因此需求另一个确保,办法签名
办法签名
代表着一个办法参数和返回值的特定字符串,比方一个fun1(param:String),它的签名便是(Ljava/lang/String;)V,括号中心代表着参数,右侧是返回值类型
以上两个,换算成字节码标明,以(ASM 中 MethodNode 为例子,办法名便是name特点,办法签名便是desc)
public class MethodNode extends MethodVisitor {
public int access;
办法名
public String name;
办法签名
public String desc;
public String signature;
public List<String> exceptions;
public List<ParameterNode> parameters;
public List<AnnotationNode> visibleAnnotations;
public List<AnnotationNode> invisibleAnnotations;
public List<TypeAnnotationNode> visibleTypeAnnotations;
public List<TypeAnnotationNode> invisibleTypeAnnotations;
public List<Attribute> attrs;
public Object annotationDefault;
public List<AnnotationNode>[] visibleParameterAnnotations;
public List<AnnotationNode>[] invisibleParameterAnnotations;
public InsnList instructions;
public List<TryCatchBlockNode> tryCatchBlocks;
public int maxStack;
public int maxLocals;
public List<LocalVariableNode> localVariables;
public List<LocalVariableAnnotationNode> visibleLocalVariableAnnotations;
public List<LocalVariableAnnotationNode> invisibleLocalVariableAnnotations;
private boolean visited;
经过办法名+办法签名,咱们能够判别一个在类内的办法仅有值,留意这儿特别标明是类内,因为假如同样的办法界说,在不同的类中,办法名与函数签名也是共同的,因此咱们需求参加第三个维度,类特征
类特征
这儿能够指代表一个类的仅有特征,比方咱们常见的类名便是,由类加载验证进程,确保了类的仅有性,也能够是其他特有的信息。经过类特征,咱们就能区别开不同类的办法
生成代码
经过上面的公式,咱们就知道了一个办法仅有值,咱们能够取类名+办法名+函数签名的办法拼凑而成,这个时候咱们还需求一个容器,存储着两者的映射联系,这儿咱们能够用一个ConcurrentHashMap<String,Int>存储即可,这也咱们能够在多线程环境下,也能收集到正确的办法id。
methodMap 是 ConcurrentHashMap<String,Int>,key是办法仅有值,value便是办法id类型
methodId 是一个AtomicInteger类型,用于原子的递增
fun methodCreate(){
classNode.methods.forEach {
if (!methodMap.containsKey(办法仅有值)) {
设置办法id
val id = methodId.incrementAndGet()
methodMap[办法仅有值] = id
}
}
}
生成办法优劣
经过上文的生成办法,咱们简略概括一下优点与坏处
优点 | 坏处 |
---|---|
能够直接在编译时简略生成,上面咱们讲到的信息,都是能够在编译时获取到,比方 classNode对应着一个Transform/Transform Action进程的一个类,能够用ASM获取,生成也比较简略,能够支持多线程 | 只能处理编译时就确认的办法,因为依赖编译时生成id,关于动态生成的办法就没有办法 |
办法id生成实践
这种办法id生成思维,因为简略易用,所以也被许多大厂开源所采用,比方
MethodCollector
总结
经过本文,咱们了解到了办法id以及常见的办法id生成进程,假如大家有多办法插桩的需求,无妨采用办法id的办法代替办法名等信息,希望对你有帮助!