ASM的基础用法
本文转载自ASM的基础用法
导语
新闻里使用的热补丁修复方案是基于AspectJ,AspectJ是AOP的一种实现。
无意接触到一种小巧轻便的Java字节码操控框架ASM,它也能方便地生成和改造Java代码。
本文主要分为几个部分:
- 什么是ASM;
- 为什么要动态生成Java类;
- 为什么选择ASM;
- ASM中的核心类和核心方法;
- ASM示例;
什么是ASM?
ASM是一个Java字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制 class文件,也可以在类被加载入Java虚拟机之前动态改变类行为。
如果想了解Java虚拟机的工作过程可参考JVM原理浅析
为什么要动态生成Java类?
举个例子,目前有一个既有的银行管理系统,包括Bank、Customer、Account、Invoice等对象,现在要加入一个安全检查模块,对已有类的所有操作之前都必须进行一次安全检查。

然而 Bank、Customer、Account、Invoice 是代表不同的事务,派生自不同的父类,很难在高层上加入关于Security Checker的共有功能。对于没有多继承的Java来说,更是如此。
传统解决方案是使用装饰器模式,装饰器模式动态的将责任链附加到对象上,若要扩展功能,装饰者提供了比继承更加富有弹性的代替方案。
装饰器模式可以在一定程度上改善耦合,而功能仍旧是分散的,每个需要Security Checker的类都必须派生一个Decorator,每个需要Security Checker的方法都要被包装(wrap)。
下面我们以 Account类为例看一下Decorator:
首先,有一个SecurityChecker类,其静态方法checkSecurity执行安全检查功能:
public class SecurityChecker {
public static void checkSecurity() {
System.out.println("SecurityChecker.checkSecurity ...");
}
}
另一个是Account类:
public class Account {
public void operation() {
System.out.println("operation...");
}
}
若想对operation加入对SecurityCheck.checkSecurity()调用,标准的Decorator需要先定义一个 Account类的接口:
public interface IAccount {
void operation();
}
然后定义一个实现类:
public class AccountImpl implements IAccount {
@Override
public void operation() {
System.out.println("operation...");
}
}
定义一个AccountImpl类的Decorator,并包装operation方法:
public class AccountWithSecurityCheck implements IAccount {
private IAccount account;
public AccountWithSecurityCheck(IAccount IAccount) {
this.account = IAccount;
}
public void operation() {
SecurityChecker.checkSecurity();
account.operation();
}
}
最后的调用方式为:
public class Test {
public static void main(String[] args) throws Exception {
// 1.使用包装类
AccountWithSecurityCheck account = new AccountWithSecurityCheck(new AccountImpl());
account.operation();
}
}
在这个简单的例子里,改造一个类的一个方法还好,如果是变动整个模块,Decorator很快就会演化成另一个噩梦。动态改变Java类就是要解决AOP的问题,提供一种得到系统支持的可编程的方法,自动化地生成或者增强Java代码。
为什么选择ASM?
最直接的改造Java类的方法莫过于直接改写class文件。Java 规范详细说明了class文件的格式,直接编辑字节码确实可以改变Java类的行为。
还有一种比较理想且流行的方式是是使用java.lang.reflect.Proxy。我们仍旧使用以上的例子,给Account类加上checkSecurity功能。
首先,Proxy编程是面向接口的,Proxy并不负责实例化对象,和Decorator模式一样,要把Account定义成一个接口,然后在AccountImpl里实现Account接口,接着实现一个InvocationHandlerAccount方法被调用的时候,虚拟机都会实际调用这个InvocationHandler的invoke方法:
public class SecurityProxyInvocationHandler implements InvocationHandler {
private Object proxyedObject;
public SecurityProxyInvocationHandler(Object o) {
proxyedObject = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (proxy instanceof IAccount && "operation".equals(method.getName())) {
SecurityChecker.checkSecurity();
}
return method.invoke(proxyedObject, args);
}
}
最后,在应用程序中指定InvocationHandler生成代理对象:
public class Test {
public static void main(String[] args) throws Exception {
// 2.使用代理
IAccount account = (IAccount) Proxy.newProxyInstance(
IAccount.class.getClassLoader(),
new Class[]{IAccount.class},
new SecurityProxyInvocationHandler(new AccountImpl()));
account.operation();
}
}
其不足之处在于:
- Proxy是面向接口的,所有使用Proxy的对象都必须定义一个接口,而且用这些对象的代码也必须是对接口编程的:Proxy生成的对象是接口一致的而不是对象一致的:例子中Proxy.newProxyInstance生成的是实现IAccount接口的对象而不是AccountImpl的子类。这对于软件架构设计,尤其对于既有软件系统是有一定掣肘的。
- Proxy毕竟是通过反射实现的,必须在效率上付出代价:有实验数据表明,调用反射比一般的函数开销至少要大10倍。而且,从程序实现上可以看出,对proxy class的所有方法调用都要通过使用反射的invoke方法。因此,对于性能关键的应用,使用proxy class是需要精心考虑的,以避免反射成为整个应用的瓶颈。
ASM能够通过改造既有类,直接生成需要的代码。增强的代码是硬编码在新生成的类文件内部的,没有反射带来性能上的付出。同时,ASM与Proxy编程不同,不需要为增强代码而新定义一个接口,生成的代码可以覆盖原来的类,或者是原始类的子类。它是一个普通的Java类而不是Proxy类,甚至可以在应用程序的类框架中拥有自己的位置,派生自己的子类。
ASM使用
使用javap -c命令查看Account类的字节码

5-9行表示的是一个的默认构造方法,是编译器为我们自动添加的。参考来自深入字节码 – 使用 ASM 实现 AOP
11-结束表示我们编写的operation方法。
aload_0:这个指令是LOAD系列指令中的一个,它的意思表示装载当前第0个元素到堆栈中。代码上相当于“this”。
invokespecial:这个指令是调用系列指令中的一个。其目的是调用对象类的方法。后面需要给上父类的方法完整签名。“#1”的意思是class文件常量表中第1个元素。值为:“java/lang/Object.””
ASM的基础用法的更多相关文章
- Golang 汇编asm语言基础学习
Golang 汇编asm语言基础学习 一.CPU 基础知识 cpu 内部结构 cpu 内部主要是由寄存器.控制器.运算器和时钟四个部分组成. 寄存器:用来暂时存放指令.数据等对象.它是一个更快的内存. ...
- PropertyGrid控件由浅入深(二):基础用法
目录 PropertyGrid控件由浅入深(一):文章大纲 PropertyGrid控件由浅入深(二):基础用法 控件的外观构成 控件的外观构成如下图所示: PropertyGrid控件包含以下几个要 ...
- logstash安装与基础用法
若是搭建elk,建议先安装好elasticsearch 来自官网,版本为2.3 wget -c https://download.elastic.co/logstash/logstash/packag ...
- elasticsearch安装与基础用法
来自官网,版本为2.3 注意elasticsearch依赖jdk,2.3依赖jdk7 下载rpm包并安装 wget -c https://download.elastic.co/elasticsear ...
- BigDecimal最基础用法
BigDecimal最基础用法 用字符串生成的BigDecimal是不会丢精度的. 简单除法. public class DemoBigDecimal { public static void mai ...
- Vue组件基础用法
前面的话 组件(Component)是Vue.js最强大的功能之一.组件可以扩展HTML元素,封装可重用的代码.根据项目需求,抽象出一些组件,每个组件里包含了展现.功能和样式.每个页面,根据自己所需, ...
- Smarty基础用法
一.Smarty基础用法: 1.基础用法如下 include './smarty/Smarty.class.php';//引入smarty类 $smarty = new Smarty();//实例化s ...
- 前端自动化测试神器-Katalon的基础用法
前言 最近由于在工作中需要通过Web端的功能进行一次大批量的操作,数据量大概在5000左右,如果手动处理, 完成一条数据的操作用时在20秒左右的话,大概需要4-5个人/天的工作量(假设一天8小时的工作 ...
- Bootstrap fileinput:文件上传插件的基础用法
官网地址:http://plugins.krajee.com/ 官网提供的样例:http://plugins.krajee.com/file-input/demo 基础用法一 导入核心CSS及JS文件 ...
随机推荐
- nginx常用功能和配置
nginx常用功能和配置 1.nginx常用功能和配置 1.1 限流 1.2 压力测试工具--Ab 1.2.1安装 1.2.2 测试 1.2.3 返回值 1.3 limit_conn_zone 1.4 ...
- Spark 将DataFrame所有的列类型改为double
Spark 将DataFrame所有的列类型改为double 1.单列转化方法 2.循环转变 3.通过:_* 1.单列转化方法 import org.apache.spark.sql.types._ ...
- Redis4.0.1的安装及哨兵模式的配置
https://blog.csdn.net/u014182745/article/details/76294146
- maven高级笔记
Maven高级 1.maven基础知识回顾 1.1 maven介绍 maven 是一个项目管理工具,主要作用是在项目开发阶段对Java项目进行依赖管理和项目构建. 依赖管理:就是对jar包的管理.通过 ...
- Windows10与虚拟机中CentOS-7.2进行telnet通信 出现在端口23处失败【解决】
(telnet服务是由xinetd守护,所以安装和启动都要用到xinetd) 1.先检查CentOS7.0是否已经安装以下几个安装包:telnet-server.telnet.xinetd.命令如下: ...
- Codeforces Round #296 (Div. 2B. Error Correct System
Ford Prefect got a job as a web developer for a small company that makes towels. His current work ta ...
- python对csv文件读写的两种方式 和 读写文件编码问题处理
''' 如果文件读取数据出错,可以考虑加一个encoding属性,取值可以是:utf-8,gbk,gb18030 或者加一个属性error,取值为ignore,例如 open(path, encodi ...
- UVALive 7146
Long long ago there is a strong tribe living on the earth. They always have wars and eonquer others. ...
- Python——控制鼠标键盘
一.安装包 pip install pynput 二.引用包 from pynput import mouse,keyboard 三.控制鼠标 from pynput.mouse import But ...
- 弹性伸缩 AS(Auto Scaling)
根据业务需求和策略设置伸缩规则,在业务需求增长时自动为您增加 ECS 实例以保证计算能力,在业务需求下降时自动减少 ECS 实例以节约成本,弹性伸缩不仅适合业务量不断波动的应用程序,同时也适合业务量稳 ...