这是ASM的定义:
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。
ASM的核心原理是,通过读取.class文件并动态修改文件的属性、方法、范围权限等,来替换或生成新的类并加载到运行环境中。
相对于动态代理来说,动态代理的对象必须实现接口,切入的方法必须是接口中的定义的方法,而对于ASM来说就没有这个限制,普通的JAVA类的任何方法都可以切入代码了。动态代理使用了一定的反射,在性能上比ASM要差一些。
原始类Account,要求在operation之前进行安全检查,在operation执行之后记录日志:
/**
* @author Administrator
* 测试账号类
*/
public class Account {
protected String name;
public void operation()
{
System.out.println("operation..."+name);
}
}
进行安全检查和日志记录的类在SecurityChecker中定义,checkSecurity和log方法模拟权限检查和日志记录:
public class SecurityChecker {
public static void checkSecurity(){
System.out.println("checkSecurity.......");
}
public static void log(){
System.out.println("log.......");
}
}
使用ASM对原始类进行改造,首先将新类继承于原始类
Account,定义子类构造方法Adapter
public class ChangeToChildConstructorMethodAdapter extends MethodAdapter {
private String superClassName;
public ChangeToChildConstructorMethodAdapter(MethodVisitor arg0,String superClassName) {
super(arg0);
this.superClassName = superClassName;
}
public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
// 调用父类的构造函数时
if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>")) {
owner = superClassName;
}
super.visitMethodInsn(opcode, owner, name, desc);// 改写父类为 superClassName
}
}
改造operation方法,定义方法Adapter:
public class AddCheckMethodAdapter extends MethodAdapter {
public AddCheckMethodAdapter(MethodVisitor arg0) {
super(arg0);
}
//方法开始前插入代码:权限控制
public void visitCode() {
visitMethodInsn(Opcodes.INVOKESTATIC, "com/zone/asm/SecurityChecker",
"checkSecurity", "()V");
}
//方法返回之前插入代码:日志检查
public void visitInsn(int opcode) {
if (opcode == Opcodes.RETURN) {//在返回之前安插after 代码。
visitMethodInsn(Opcodes.INVOKESTATIC, "com/zone/asm/SecurityChecker", "log", "()V");
}
super.visitInsn(opcode);
}
}
定义工厂类生产新的子类
public class SecureAccountGenerator {
private static AccountGeneratorClassLoader classLoader =
new AccountGeneratorClassLoader();
private static Class secureAccountClass;
public static Account generateSecureAccount() throws ClassFormatError,
InstantiationException, IllegalAccessException,IOException,ClassNotFoundException {
//生成子类
if (null == secureAccountClass) {
ClassReader cr = new ClassReader("com.zone.asm.Account");
ClassWriter cw = new ClassWriter(true);
ClassAdapter classAdapter = new AddSubClassClassAdapter(cw);
cr.accept(classAdapter, true);
byte[] data = cw.toByteArray();
//这里讲子类写到磁盘中,可选
File file = new File("F:/Account$EnhancedByASM.class");
FileOutputStream fout = new FileOutputStream(file);
fout.write(data);
fout.close();
//由字节码定义类
secureAccountClass = classLoader.defineClassFromClassFile(
"com.zone.asm.Account$EnhancedByASM",data);
}
//生成实例
return (Account) secureAccountClass.newInstance();
}
//自定义classLoader
private static class AccountGeneratorClassLoader extends ClassLoader {
public Class defineClassFromClassFile(String className,
byte[] classFile) throws ClassFormatError {
return defineClass(className, classFile, 0, classFile.length);
}
}
public static void main(String [] args) throws Exception{
Account a = SecureAccountGenerator.generateSecureAccount();
a.name = "zhl";
a.operation();
System.out.println(Account.class.getClassLoader());
System.out.println(a.getClass().getClassLoader());
//引导classLoader加载的JAR
URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i].toExternalForm());
}
System.out.println(System.getProperty("java.ext.dirs"));
ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();
System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());
}
使用JD-GUI工具打开新生成的子类,可以看到operate方法里增加了安全检查和日志记录的代码:
public class EnhancedByASM extends Account
{
protected String name;
public void operation()
{
SecurityChecker.checkSecurity();
System.out.println("operation..." + this.name);
SecurityChecker.log();
}
}