本文共 5115 字,大约阅读时间需要 17 分钟。
运动健身、早睡早起、三餐规律、多读好书并保持输入输出,如果你真的想不明白自己要什么,做这些永远不会错。坚持一年,就算你还是没有目标,也能有一个好的身体、博学的脑袋,这些足以让你超越80%的同龄人。
最近因为工作需要,需要使用代码注入的功能,这里简单介绍下代码注入的流程和心得。
Class文件的结构大概如下所示,其中*
表示0个或者多个。
Class字段 | 含义 |
---|---|
B | 基本类型byte |
C | 基本类型char |
D | 基本类型double |
F | 基本类型float |
I | 基本类型int |
J | 基本类型long |
S | 基本类型short |
Z | 基本类型boolean |
V | 特殊类型void |
L | 对象类型,以分号结尾,如Ljava/lang/Object; |
[Ljava/lang/String; | 数组类型,每一位使用一个前置的[ 字符来表示 |
摘录自:
括号中的是参数声明,括号后紧跟着的是返回类型。
Class字段 | Java方法声明 |
---|---|
(IF)V | void m(int i, float f) |
(Ljava/lang/Object;)I | int m(Object o) |
(ILjava/lang/String;)[I | int[] m(int i, String s); |
([I)Ljava/lang/Object; | Object m(int[] i); |
修饰符 | ID | 说明 |
---|---|---|
ACC_PUBLIC | 0X0001 | public类型 |
ACC_FINAL | 0X0010 | 声明为final,只有类可以设置 |
ACC_SUPER | 0X0020 | 使用invokespecial字节码指令的新语意,invokespecial指令的语意在JDK1.0.2发生过改变,为了区别这条指令使用哪种语意,JDK1.0.2之后编译出来的类都为真 |
ACC_INTERFACE | 0X0200 | 接口 |
ACC_ABSTRACT | 0X0400 | abstract类型,对于接口或者抽象类来说,此标志值为真,其他类为假 |
ACC_SYNTHETIC | 0X1000 | 这个类并非由用户代码产生 |
ACC_ANNOTATION | 0X2000 | 注解 |
ACC_ENUM | 0X4000 | 枚举 |
常量池的类型 | 说明 |
---|---|
CONSTANT_Utf8_info | tag标志位为1, UTF-8编码的字符串 |
CONSTANT_Integer_info | tag标志位为3, 整形字面量 |
CONSTANT_Float_info | tag标志位为4, 浮点型字面量 |
CONSTANT_Long_info | tag标志位为5, 长整形字面量 |
CONSTANT_Double_info | tag标志位为6, 双精度字面量 |
CONSTANT_Class_info | tag标志位为7, 类或接口的符号引用 |
CONSTANT_String_info | tag标志位为8,字符串类型的字面量 |
CONSTANT_Fieldref_info | tag标志位为9, 字段的符号引用 |
CONSTANT_Methodref_info | tag标志位为10,类中方法的符号引用 |
CONSTANT_InterfaceMethodref_info | tag标志位为11, 接口中方法的符号引用 |
CONSTANT_NameAndType_info | tag 标志位为12,字段和方法的名称以及类型的符号引用 |
CONSTANT_Method-Handle_info | tag标志位为15,方法句柄 |
CONSTANT_Method-Type_info | tag标志位为16,方法类型 |
CONSTANT_Invoke-Dynamic_info | tag标志位为18,动态方法调用点 |
关于方法的调用,Java 共提供了 5 个指令,来调用不同类型的函数:
调用指令 | 说明 | 绑定类型 |
---|---|---|
invokestatic | 用来调用静态方法。 | 静态绑定 |
invokevirtual | 用于调用非私有实例方法,比如 public 和 protected,大多数方法调用属于这一种。 | 动态绑定 |
invokeinterface | 和上面这条指令类似,不过作用于接口类。 | 动态绑定 |
invokespecial | 用于调用私有实例方法、构造器及 super 关键字等。 | 静态绑定 |
invokedynamic | 用于调用动态方法。 | 动态绑定 |
PS:
相比于静态绑定的方法调用,动态绑定的调用会更加耗时一些。由于方法的调用非常的频繁,JVM 对动态调用的代码进行了比较多的优化,比如使用方法表来加快对具体方法的寻址,以及使用更快的缓冲区来直接寻址( 内联缓存)。
大多数普通方法调用,使用的是invokevirtual
指令,它其实和invokeinterface
是一类的,都属于虚方法调用。很多时候,JVM 需要根据调用者的动态类型,来确定调用的目标方法,这就是动态绑定的过程。
invokevirtual
指令有多态查找的机制,该指令运行时,解析过程如下:
java.lang.IllegalAccessError
。java.lang.AbstractMethodError
异常,这就是 Java 语言中方法重写的本质。如果啃不动英文的文档,可以看这个博主的译文
IBM Developer社区的文章也是非常的不错的,可以作为入门用。
直接使用Sample的例子对照着改就好了,O(∩_∩)O
实践 | 地址 |
---|---|
添加变量 | |
添加方法 | |
删除变量 | |
删除方法 | |
添加耗时统计 | ,还有这部分也是: |
添加注解 | |
删除注解 | |
使用Transformer添加变量 | |
使用Transformer添加方法 | |
使用Transformer删除变量 | |
使用Transformer删除方法 |
org.objectweb.asm.MethodVisitor
参数 | 说明 | 解释 |
---|---|---|
invokevirtual | Invoke instance method; dispatch based on class | 执行一般实例方法,创建完实例对象后,obj.method() 调用的 |
invokespecial | Invoke instance method; special handling for superclass, private, and instance initialization method invocations | 实例初始化方法(构造函数)、父类的方法(super.method() 方式调用)、私有方法 |
invokeinterface | Invoke interface method | 执行接口方法 |
invokestatic | Invoke a class (static) method | 执行静态方法 |
invokedynamic | Invoke dynamic method | jdk1.7新增,执行动态方法,不需要在编译时确定 |
Unexpected static-value type
的Exception
,比如Unexpected static-value type java.lang.Class
。private DexValue getStaticValue(Object value, DexType type) { if (value == null) { return null; } DexItemFactory factory = parent.application.getFactory(); if (type == factory.booleanType) { int i = (Integer) value; assert 0 <= i && i <= 1; return DexValueBoolean.create(i == 1); } if (type == factory.byteType) { return DexValueByte.create(((Integer) value).byteValue()); } if (type == factory.shortType) { return DexValueShort.create(((Integer) value).shortValue()); } if (type == factory.charType) { return DexValueChar.create((char) ((Integer) value).intValue()); } if (type == factory.intType) { return DexValueInt.create((Integer) value); } if (type == factory.floatType) { return DexValueFloat.create((Float) value); } if (type == factory.longType) { return DexValueLong.create((Long) value); } if (type == factory.doubleType) { return DexValueDouble.create((Double) value); } if (type == factory.stringType) { return new DexValueString(factory.createString((String) value)); } throw new Unreachable("Unexpected static-value type " + type);}
转载地址:http://whzrj.baihongyu.com/