Spring最深处——ASM

1.ASM

ASM官网—官方文档

ASM是什么

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

ASM能做什么

用一句话总结:ASM能够修改.class文件,从而动态生成类或者增强现有类的功能

查看一个.class文件

打开一个class文件,所有Java编译后的文件都是cafe babe开头,只要看到这个开头

都是Java class编译后的文件,可以理解为Java的字节码文件

Java虚拟机规范 里面详细介绍了每一个Java字节码意思

01![](/home/lks/文档/Spring_ASM/01.jpg)

可使用IDEA下的一个Plugins(插件)下安装一个jclasslib Bytecode viewer插件

安装完成后,点击菜单栏中View下的Show Bytecode With jclasslib

如下图所示这10项是一个class文件中所有包含的内容

02![](/home/lks/文档/Spring_ASM/02.jpg)

2.使用ASM读取一个类

在官方文档 2.2.2中

如何解析一个类呢,我们需要自定义一个ClassPrinter继承ClassVisitor这个类

ASM给我们提供一个接口,我们可以写自定义一个ClassVistor的访问者,访问Class中的每一个节点,如:版本、名称、方法等,可以访问的很多,这里只是一部分

我们去重写visit方法,程序一但执行,当我们去访问class文件的时候,在visit中就会打印出其内容,

当我们把一个类都访问了一遍,执行visitEnd()方法;例:

在这里插入图片描述

自建一个测试类

如:

package com.mashibing.dp.ASM
public class T1{
	int i = 0;
	public void m(){
	int j = 1;
	}

}
ClassReader cr = new 
    ClassReder(ClassPrinter.class.getClassLoader().getResourceAsSteream(
        "com/mashibing/db/ASM/T1.class"));//读取测试类内容

执行结果

在这里插入图片描述

可以看到把T1这个类的内容读取了出来

3.使用ASM写一个类

参考官方文档 2.2.3

在这里插入图片描述
我们自己将其进行一些修改

在这里插入图片描述

/*自定义一个ClassWriter对象*/
ClassWriter cw = new ClassWriter(0);
/*生成1.5版本,名为comparable,父类为Object*/
cw.visit(V1_5,ACC_PUBLIC+ACC_ABSTRACT+ACC_INTERFACE,"pkg/comparable",null,"java/long/Object",null);
/*生成Field,名字是"LESS",类型是int,值是-1*/
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC,"LESS","I",null,-1).visitEnd();
...
/*最后生成一个方法,方法名为"compareTo",参数是java/lang/Object,*/
cw.visitMethod(ACC_PUBLIC+ACC_ABSTRACT,"compareTo","(Ljava/lang/Object;)I",null,null).visitEnd();
/*将其转换为二进制数组*/
byte[] b = cw.toByteArray();
/*使用这个字节数组*/
MyclassLoader myClassLoader = new MyClassLoader();//自定义ClassLoader
/*load为一个Class对象,这里会涉及到Java的反射*/
Class c = myClassLoader.defineClass("pkg.Comparable",b);
/*然后将其打印出来,由于是一个这是一个接口,所以他没有构造方法,第0个方法就是"comparable"*/
System.out.println(c.getMethods()[0].getName());

Java虚拟机规范里指定了返回值为int类型,所以我们必须要写成"Int"类型

运行结果:

在这里插入图片描述

4.使用ASM转换一个类

当我们学会如何使用ASM读和写后,把原来的类读出来,读出来后的内容加上自己写的动态代理生成新的内容,直接生成在内存中.

  1. 参考官方文档2.2.4 Transforming classes(改变一个类)

/*
*如果我们直接使用,就是将Reader复制到Writer
*/
byte[] b1 = ...;//被改变的类
ClassWriter cw = new ClassWriter(0);
ClassReader cr = new ClassReader(b1);
cr.accept(cv, 0);
byte[] b2 = cw.toByteArray();//将其写出来的转变为字节数字进行修改
/*所以在其中间添加一个ClassVisitor*/
byte[] b1 = ...;
ClassWriter cw = new ClassWriter(0);
ClassReader cr = new ClassReader(b1);
ClassVisitor cv = new ClassVisitor(ASM4, cw) {};
cr.accept(cv, 0);
byte[] b2 = cw.toByteArray();

我们需要在Reader传给Writer中添加一个Adapter,将Reader传过来的经过Adapter修改一下,在传给Writer

如下图:

在这里插入图片描述

让我们自己来试一下

如下图所示,首先是读取一个类,图中所读取的类是Tank.class

在这里插入图片描述

Tank类中非常简单,注意的是它没有实现任何接口

package com.mashibing.dp.ASM
/*Tank类*/
public class Tank{
	public void move(){
		System.out.println("Tank Moving ClaClaCla...");
	}
}

我们在回到图一,当程序执行第14行代码时,Tank类就被load至内存,

然后看第24行这个方法visitCode(),这个方法是访问它的机器码

/*图一摘取*/
public void visitCode() {
    //添加的指令,静态调用,调用了TimeProxy这个类里的before方法
    visitMethodInsn(INVOKESTATIC,"com/mashibing/dp/ASM/TimeProxy"
        ,"before","()V",false);

    super.visitCode();//如果只调用此方法相当于直接复制
}

一个类的Visitor去访问类,一个方法的Visitor去访问方法

从图1可以看出,这是Visitor套Visitor我们把一个class load到内存,里面有一方法叫move(),写一个Class Visitor 去访问这个Class 如图中:Tank.class.

当我们访问到这个方法的时候,又使用访问方法的Visitor去访问这个方法:move(),访问这个方法的Visitor 里面重写了它的VisitorCode()方法,当访问到VisitorCode()这个方法里面

在super.visitCode()前加了一个指令让他静态的去访问TImeProxy类里的before()这个方法.最后生成到Tank_0.class这个类中,

在这里插入图片描述
然后我们运行这个程序,就会看到before…

在到Tank_0类中,可以看到move()这个方法中多了一句话:TimeProxy.before()

在这里插入图片描述

而TimeProxy类就是代理,它里面有一个before()方法

package com.mashibing.dp.ASM
/*TimeProxy类*/
public class TimeProxy{
	public static void before() {
		System.out.println("before...");
	}
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值