java asm、cglib

简介: java asm、cglib

一、ASM

ASM 官方 的定义是:ASM是一个通用的 Java 字节码操控和分析框架。它可以用于修改已有的类也可以直接生成类。ASM 提供了一些常用的字节码转换和分析算法,从中可以构建自定义的复杂转换和源码分析工具。ASM提供了与其他 Java 字节码框架类似的方法,但是更注重性能。因为它被设计和实现成尽可能小和快,所以非常适用于动态系统(当然也可以用于静态的方式,例如在编译器中)。

总之ASM让我们可以对 Java 字节码做几乎任何事情。例如获取类文件的详细信息、动态修改或生成一个类等。

Java 是一门静态语言,所有的类在编译时就已经给定了,但是有很多情况下,我们需要在运行时动态地生成或者增强一些类。这时候 ASM 就派上用场了。

为什么要用 ASM

最典型的应用场景是 AOP(面向切面编程)。实际开发中我们会发现,项目中经常存在这么一类零散而又耦合的代码。例如打印方法耗时、方法权限控制等。

假设这样的场景,团队规定对所有类的关键的方法都必须打印方法执行耗时,如果方法执行抛出异常则必须把参数也打印出来。

而对这类需求,用传统的做法我们会发现整个项目充斥着下面这段重复的模板代码:

try {
    long start = System.currentTimeMillis();
    // 业务代码
    ...
    long timeConsumed = start - System.currentTimeMillis();
    log.info("方法 {} 耗时: {}", "doSomething", timeConsumed)
} catch(Exception e){
    log.warn("方法 {} 执行抛出异常:{}, 参数: {}", "doSomething", e.getMessage());
    throw e;
}

根据 DRY(Don’t Repeat Yourself) 原则,当我们发现项目中不断出现重复代码时,肯定是哪里出了问题,该优化了。就这个案例来说,如果我们可以在编译或者运行时在业务代码的前面和后面自动加上我们定义好的几行日志代码,那么这些重复的代码就能统统消灭掉了。

AOP 就是为了解决这类问题而生的,它能让我们在方法的执行前后添加我们需要添加的代码,甚至改变方法的执行流程。AOP 的实现跟字节码操控密切相关,SpringAOP 是基于动态代理和CGLIB实现的,而CGLIB是基于ASM实现的。所以我们非常有必要学习字节码操控技术。这个领域使用最广泛的框架就是 ASM。

从 HelloWorld 开始

HelloWorld 是所有人在学习一门语言时写的第一个程序,而学习任何一项新技术时,HelloWorld 也是最好的敲门砖。所以让我们用 ASM 来写一个 HelloWorld 程序吧!

还记得这个 Java 的 HelloWorld 吗?

public class HelloWorld{
    public static void main(){
        System.out.println("HelloWorld!");
    }
}

我们不直接写上面这个类,而是使用 ASM 框架来动态生成

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
 * @author dadiyang
 * @date 2019/1/13
 */
public class HelloWorldByAsm {
    public static void main(String[] args) throws Exception {
        // 生成二进制字节码
        byte[] bytes = generate();
        // 使用自定义的ClassLoader
        MyClassLoader cl = new MyClassLoader();
        // 加载我们生成的 HelloWorld 类
        Class clazz = cl.defineClass("com.dadiyang.asm.HelloWorld", bytes);
        // 反射获取 main 方法
        Method main = clazz.getMethod("main", String[].class);
        // 调用 main 方法
        main.invoke(null, new Object[]{new String[]{}});
    }
    private static byte[] generate() {
        ClassWriter cw = new ClassWriter(0);
        // 定义对象头:版本号、修饰符、全类名、签名、父类、实现的接口
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "com/dadiyang/asm/HelloWorld",
                null, "java/lang/Object", null);
        // 添加方法:修饰符、方法名、描述符、签名、抛出的异常
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main",
                "([Ljava/lang/String;)V", null, null);
        // 执行指令:获取静态属性
        mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        // 加载常量 load constant
        mv.visitLdcInsn("HelloWorld!");
        // 调用方法
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        // 返回
        mv.visitInsn(Opcodes.RETURN);
        // 设置栈大小和局部变量表大小
        mv.visitMaxs(2, 1);
        // 方法结束
        mv.visitEnd();
        // 类完成
        cw.visitEnd();
        // 生成字节数组
        return cw.toByteArray();
    }
}
/**
 * 自定义ClassLoader以支持加载字节数组形式的字节码
 * @author dadiyang
 */
class MyClassLoader extends ClassLoader {
    public Class defineClass(String name, byte[] b) {
        // ClassLoader是个抽象类,而ClassLoader.defineClass 方法是protected的
        // 所以我们需要定义一个子类将这个方法暴露出来
        return super.defineClass(name, b, 0, b.length);
    }
}

这时执行程序就会输出 HelloWorld 了。是不是很神奇?我们并没有写一个真正的 HelloWorld.java 文件,也没有在运行之前对其进行编译,但是 ASM 让我们有能力在运行的过程中动态生成一个新的类并加载和运行它。

二、cglib

 

。。。。


相关文章
|
5月前
|
安全 算法 Java
从零开发基于ASM字节码的Java代码混淆插件XHood
因在公司负责基础框架的开发设计,所以针对框架源代码的保护工作比较重视,之前也加入了一系列保护措施,例如自定义classloader加密保护,授权license保护等,但都是防君子不防小人,安全等级还比较低,经过调研各类加密混淆措施后,决定自研混淆插件,自主可控,能够贴合实际情况进行定制化,达到框架升级后使用零感知,零影响
70 1
从零开发基于ASM字节码的Java代码混淆插件XHood
|
3月前
|
设计模式 Java
java中的jdk代理和cglib代理
在Java中,代理是一种设计模式,它允许一个对象(代理)控制对另一个对象(真实对象)的访问。Java中的代理主要分为两种类型:JDK(Java Dynamic Proxy)代理和CGLIB(Code Generation Library)代理。
|
4月前
|
Arthas 缓存 Java
[Java] 模拟Jdk 以及 CGLib 代理原理
[Java] 模拟Jdk 以及 CGLib 代理原理
|
4月前
|
Java
[Java]轻松掌握JDK和CGlib代理的使用技巧,让你的Java程序性能更卓越!
[Java]轻松掌握JDK和CGlib代理的使用技巧,让你的Java程序性能更卓越!
|
5月前
|
Java Spring
探究Java spring中jdk代理和cglib代理!
探究Java spring中jdk代理和cglib代理!
|
8月前
|
缓存 Java
探索Java动态代理:深入了解CGLib
了解什么是CGLib,它如何实现动态代理和代码增强,以及它与Java标准动态代理的比较。
311 0
|
10月前
java202304java学习笔记第六十四天-ssm-基于cglib的动态代理4
java202304java学习笔记第六十四天-ssm-基于cglib的动态代理4
27 0
|
11月前
java202304java学习笔记第六十四天-ssm-基于cglib的动态代理2
java202304java学习笔记第六十四天-ssm-基于cglib的动态代理2
37 0
|
11月前
java202304java学习笔记第六十四天-ssm-基于cglib的动态代理3
java202304java学习笔记第六十四天-ssm-基于cglib的动态代理3
23 0
|
设计模式 Java 数据安全/隐私保护
剖析代理模式及Java两种动态代理(JDK动态代理和CGLIB动态代理)
本文详述了代理模式以及我们经常接触到的两种具体实现(JDK动态代理和CGLIB动态代理),为读者理解代理模式、JDK动态代理和CGLIB动态代理提供帮助
132 0
剖析代理模式及Java两种动态代理(JDK动态代理和CGLIB动态代理)