编译插桩和动态代理

编译插桩技术

image-20210222110944966

Kotlin Symbol Processing (KSP) Alpha 版现已发布

ByteX

https://github.com/bytedance/ByteX

ASM

https://asm.ow2.io/

【Android】函数插桩(Gradle + ASM)

  • ASM代码生成 在Android Studio中安装ASM Bytecode Viewer插件; 2、安装后,在编译器中,点击右键,选择Show Bytecode Viewer;

查看java字节码:

jclasslib插件,View->Show ByteCode with jclasslib

极客时间–android开发高手课07-sample项目

compile group: 'org.ow2.asm', name: 'asm', version: '5.1'
compile group: 'org.ow2.asm', name: 'asm-commons', version: '5.1'

通过反射注入transform

    project.getGradle().getTaskGraph().addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
        @Override
        public void graphPopulated(TaskExecutionGraph taskGraph) {
            for (Task task : taskGraph.getAllTasks()) {
                if ((task.name.equalsIgnoreCase(hackTransformTaskName) || task.name.equalsIgnoreCase(hackTransformTaskNameForWrapper))
                        && !(((TransformTask) task).getTransform() instanceof SystemTraceTransform)) {
                    project.logger.warn("find dex transform. transform class: " + task.transform.getClass() + " . task name: " + task.name)
                    project.logger.info("variant name: " + variant.name)
                    Field field = TransformTask.class.getDeclaredField("transform")
                    field.setAccessible(true)
                    field.set(task, new SystemTraceTransform(project, variant, task.transform))
                    project.logger.warn("transform class after hook: " + task.transform.getClass())
                    break
                }
            }
        }
    })

JavaAssist

Android动态编译技术:Plugin Transform Javassist操作Class文件

可以查看shadow源码中的相关部分: Transform + JavaAssist

运行时生成代码

    /** 
     * 运行时生成业务逻辑 
     * @return 
     * @throws Exception 
     */  
    public static IDBQuery createJavassistBytecodeDynamicProxy() throws Exception{  
        ClassPool mPool = new ClassPool(true);  
        //定义类名  
        CtClass mCtc = mPool.makeClass(IDBQuery.class.getName() + "JavaassistBytecodeProxy");  
        //需要实现接口  
        mCtc.addInterface(mPool.get(IDBQuery.class.getName()));  
        //添加构造函数  
        mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));  
          
        //添加类的字段信息,使用动态Java代码  
        mCtc.addField(CtField.make("public" + IDBQuery.class.getName() + "real;", mCtc));  
        String dbQueryname = DBQuery.class.getName();  
        //添加方法,这里使用动态Java代码指定内部逻辑  
        mCtc.addMethod(CtNewMethod.make("public String request() { if(real==null) real = new " +dbQueryname+"(); return real.request();}", mCtc));  
          
        //基于以上信息,生成动态类  
        Class pc = mCtc.toClass();  
        //生成动态类的实例  
        IDBQuery bytecodeProxy = (IDBQuery) pc.newInstance();  
        return bytecodeProxy;  
    }  

Jdk动态代理:

http://www.cnblogs.com/xiaoluo501395377/p/3383130.html

CGLIB

幸好我们有cglib。“CGLIB(Code Generation Library),是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。”

cglib 创建某个类A的动态代理类的模式是:

  1. 查找A上的所有非final 的public类型的方法定义;
  2. 将这些方法的定义转换成字节码;
  3. 将组成的字节码转换成相应的代理的class对象;
  4. 实现 MethodInterceptor接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)

cglib动态代理

jdk中的动态代理通过反射类Proxy和InvocationHandler回调接口实现,要求委托类必须实现一个接口,只能对该类接口中定义的方法实现代理,这在实际编程中有一定的局限性。

使用[cglibCode Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码,下面通过一个例子看看使用CGLib如何实现动态代理。

参考

编译时插桩的方式,后面我会讲到 Aspect、ASM 和 ReDex

动态代理解释-JDK,CGLIB,JAVASSIST,ASM

Java动态代理:JDK 和CGLIB、Javassist、ASM之间的差别 (详细)