概述

ASM 是java字节码操作框架。
由于ASM性能好的原因,所以在动态编译上往往比Javassist上使用的更加广泛。

之前已经写过了Javassist实现动态编译的demo,对动态编译不了解的读者可以看下:动态编译入门(gradle Transform Demo)

本文在前面demo的基础上,将Javassist的实现改为了ASM。
因此对于gradle 插件等重复的点就不多加描述了,本文主要讲解下ASM的使用。

Demo 概述


本demo通过ASM,实现在方法中动态插入代码的功能。
主要代码如下:

public class PluginTestClass {public void init() {System.out.println("PluginTestClass init");//此处将会使用动态编译插入代码//PluginTestClass.testPrint();}public static void testPrint(String s) {System.out.println(s);}
}
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);PluginTestClass pluginTestClass=new PluginTestClass();pluginTestClass.init();}
}

Demo结果

2020-04-05 10:26:53.708 11336-11336/com.example.transformtest I/System.out: PluginTestClass init
2020-04-05 10:26:53.708 11336-11336/com.example.transformtest I/System.out: 我是插入的代码

“代码插入”实现流程

1、自己实现了TestFileUtils.java这个类来递归遍历文件夹下的所有文件。
2、遍历所有文件找到PluginTestClass这个类。
3、使用ClassVisitor 和MethodVisitor 分别来找到init方法和修改init方法。

MethodVisitor中的visitMaxs方法用于返回最大的操作数栈和局部变量表,这两项往往会根据插入的代码而改变。(此demo中插入的代码不会导致这两者改变,因此此demo中没有修改)
笔者自己单独写了一篇文章用于讲解着两个概念,对其有兴趣的读者可以看下:
完全理解 java操作数栈和局部变量表

TestTransform.java

public class TestTransform extends Transform {//用于指明本Transform的名字,也是代表该Transform的task的名字@Override public String getName() {return "TestTransform";}//用于指明Transform的输入类型,可以作为输入过滤的手段。@Override public Set<QualifiedContent.ContentType> getInputTypes() {return TransformManager.CONTENT_CLASS;}//用于指明Transform的作用域@Override public Set<? super QualifiedContent.Scope> getScopes() {return TransformManager.SCOPE_FULL_PROJECT;}//是否增量编译@Override public boolean isIncremental() {return false;}@Override public void transform(TransformInvocation invocation) {System.out.println("TestTransform transform");for (TransformInput input : invocation.getInputs()) {//遍历jar文件 对jar不操作,但是要输出到out路径input.getJarInputs().parallelStream().forEach(jarInput -> {File src = jarInput.getFile();System.out.println("input.getJarInputs fielName:" + src.getName());File dst = invocation.getOutputProvider().getContentLocation(jarInput.getName(), jarInput.getContentTypes(), jarInput.getScopes(),Format.JAR);try {FileUtils.copyFile(src, dst);} catch (IOException e) {throw new RuntimeException(e);}});//遍历文件,在遍历过程中input.getDirectoryInputs().parallelStream().forEach(directoryInput -> {File src = directoryInput.getFile();System.out.println("input.getDirectoryInputs fielName:" + src.getName());File dst = invocation.getOutputProvider().getContentLocation(directoryInput.getName(), directoryInput.getContentTypes(),directoryInput.getScopes(), Format.DIRECTORY);try {scanFilesAndInsertCode(src);FileUtils.copyDirectory(src, dst);} catch (Exception e) {System.out.println(e.getMessage());}});}}private void scanFilesAndInsertCode(File file) throws Exception {TestFileUtils.scanFileInDir(file,new TestFileUtils.ScanFileCallback() {@Override public void action(File file) {if (file.getAbsolutePath().contains("PluginTestClass")) {insertTestCode(file);}}});}private void insertTestCode(File file) {try {//读取class文件并且插入代码InputStream inputStream = new FileInputStream(file);ClassReader cr = new ClassReader(inputStream);ClassWriter cw = new ClassWriter(cr, 0);ClassVisitor cv = new MyClassVisitor(Opcodes.ASM5, cw);cr.accept(cv, ClassReader.EXPAND_FRAMES);//生成新的class文件FileOutputStream fileOutputStream = new FileOutputStream(file);fileOutputStream.write(cw.toByteArray());fileOutputStream.close();} catch (Exception e) {e.printStackTrace();}}private static class MyClassVisitor extends ClassVisitor {MyClassVisitor(int api, ClassVisitor cv) {super(api, cv);}public void visit(int version, int access, String name, String signature,String superName, String[] interfaces) {super.visit(version, access, name, signature, superName, interfaces);}@Overridepublic MethodVisitor visitMethod(int access, String name, String desc,String signature, String[] exceptions) {MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);//generate code into this methodif (name.equals("init")) {mv = new MyMethodVisitor(Opcodes.ASM5, mv);}return mv;}}private static class MyMethodVisitor extends MethodVisitor {MyMethodVisitor(int api, MethodVisitor mv) {super(api, mv);}@Overridepublic void visitInsn(int opcode) {if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {//添加方法mv.visitLdcInsn("我是插入的代码");mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/example/testplugin/PluginTestClass","testPrint","(Ljava/lang/String;)V", false);}super.visitInsn(opcode);}@Override public void visitMaxs(int maxStack, int maxLocals) {super.visitMaxs(maxStack, maxLocals);}}
}

TestFileUtils.java

public class TestFileUtils {public static void scanFileInDir(File rootFile, ScanFileCallback callback) {if (rootFile == null) {return;}if (rootFile.isDirectory()) {if (rootFile.listFiles() == null) {return;}for (File file : rootFile.listFiles()) {scanFileInDir(file, callback);}} else {callback.action(rootFile);}}public interface ScanFileCallback {void action(File file);}
}

动态编译 java ASM入门相关推荐

  1. eclipse编译java项目class文件_动态编译 Java 代码以及生成 Jar 文件

    导读: 最近在看 Flink 源码的时候发现到一段实用的代码,该代码实现了 java 动态编译以及生成 jar 文件.将其进行改进后可以应用到我们的平台上,实现在平台页面上编写 java 代码语句,提 ...

  2. java 反射 动态编译_动态编译java源代码和反射调用问题

    我从教程中得到了以下代码: package com.tom.labs; import java.io.IOException; import java.lang.reflect.Method; imp ...

  3. 菜鸟学习笔记:Java提升篇12(Java动态性2——动态编译、javassist字节码操作)

    菜鸟学习笔记:Java提升篇12(Java动态性2--动态编译.javassist字节码操作) Java的动态编译 通过脚本引擎执行代码 Java字节码操作 JAVAssist的简单使用 常用API ...

  4. java动态编译类文件并加载到内存中

    如果你想在动态编译并加载了class后,能够用hibernate的数据访问接口以面向对象的方式来操作该class类,请参考这篇博文-http://www.cnblogs.com/anai/p/4270 ...

  5. kan-java, 一个能裁剪语法特性的java动态编译工具

    'kan-java' 就是 '砍-java' 这是一个java代码动态编译工具,也就是能够把String形式的java代码实时地编译为字节码的工具: "动态编译"工具,其实自jdk ...

  6. JAVA1.6实现动态编译加载运行

    一直以为我们写好的程序都需要预先编译好,然后再运行,直到今天在看书的时候才看到书上说JAVA1.6之后可以动态的编译JAVA文件,这着实让我高兴了一把,为什么这么兴奋呢,我一心想着能用技术实现动态更新 ...

  7. java compiler.run_动态的Java JavaCompilerAPI中文指南

    JavaCompiler API 1.6之后JDK提供了一套compiler API,定义在JSR199中, 提供在运行期动态编译java代码为字节码的功能 简单说来,这一套API就好比是在java程 ...

  8. 【java】深入理解Java的动态编译

    文章目录 1.概述 2. 前提 3. 基本原理# 4. JDK动态编译 4.1 实现JavaFileObject 4.2 实现ClassLoader 4.3 实现JavaFileManager 4.4 ...

  9. 趁周末,来学点进阶知识:Java 动态编译

    来源 | https://zhenbianshu.github.io 问题 之前的文章从Spring 的环境到 Spring Cloud 的配置中提到过,我们在使用 Spring Cloud 进行动态 ...

  10. java 动态编译 canino_java动态编译

    在HotSpot虚拟机中,有两个技术是至关重要的,即动态编译(Dynamic compilation)和Profiling. HotSpot是如何动态编译Javad的bytecode呢?Java by ...

最新文章

  1. android端使用http2.0,android Retrofit2+okHttp3使用总结
  2. springcloud19---springCloudConfig
  3. 车小米O2O保养平台搭建完毕
  4. Git基础-获取仓库、提交、查看历史、撤销
  5. Android编译错误: The project cannot be built until build path errors are resolved
  6. 4代hiv检测50元_别瞧不起国货!这4个姥姥辈的护肤品,真心好用,还不到50元
  7. python大写字母怎么表示_python – 如何在字符串中搜索大写字母并返回带有和不带大写字母的单词列表...
  8. php中文件读写总结,PHP中文件读写操作
  9. 提取数据_Origin如何提取图片数据
  10. Java 数据库添加,修改和删除
  11. adguard home上网慢_如何正确使用smartdns搭配adguardhome, 优选dns并去除广告
  12. 日系插画学习笔记(十):色彩基础
  13. Unity UGUI Inputfield 回车submit 按下Enter回车完成
  14. 亚马逊热销爆款产品货源有哪些?亚马逊无货源怎么做?
  15. 未来两周目标计划---C++ and Disassembly(不积跬步无以至千里,不积小流无以成江海)
  16. Win10应用卸载(针对控制面板的卸载程序页面找不到的那些应用)
  17. 电脑硬件:cpu后边数字及字母的意思
  18. 加勒比海盗1英文剧本
  19. CLIP与CoOp代码分析
  20. Android面试题-解决字体适配

热门文章

  1. 学地质灾害还是学计算机专业,地质灾害与防治技术专科专业介绍
  2. [渝粤教育] 深圳职业技术学院 安全教育与应急处理训练 参考 资料
  3. C语言字母O和数字0怎么区分,车牌数字“0”和字母“O”究竟如何区分?看完终于弄明白了...
  4. boost升压电路解析
  5. 【2016年第4期】国务院批复建立促进大数据 发展部际联席会议制度
  6. 苹果11显示服务器,苹果11肿么显示网络速度
  7. 空号检测(电话号码状态实时分析)freeswitch模块
  8. 数据库的常用操作语句
  9. java程序员 英文简历_Java程序员英文简历范文
  10. 企业申请CMMI3-CMMI5必经的六个阶段