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

 

。。。。


相关文章
|
3月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
159 0
|
3月前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
154 0
|
4月前
|
Java 数据库 Spring
Java编程问题之在测试中使用CGLIB创建代理类如何解决
Java编程问题之在测试中使用CGLIB创建代理类如何解决
|
4月前
|
缓存 Java 应用服务中间件
Java编程问题之重试机制问题之在CGLIB中设置目标对象类并创建代理类如何解决
Java编程问题之重试机制问题之在CGLIB中设置目标对象类并创建代理类如何解决
|
安全 算法 Java
从零开发基于ASM字节码的Java代码混淆插件XHood
因在公司负责基础框架的开发设计,所以针对框架源代码的保护工作比较重视,之前也加入了一系列保护措施,例如自定义classloader加密保护,授权license保护等,但都是防君子不防小人,安全等级还比较低,经过调研各类加密混淆措施后,决定自研混淆插件,自主可控,能够贴合实际情况进行定制化,达到框架升级后使用零感知,零影响
127 1
从零开发基于ASM字节码的Java代码混淆插件XHood
|
5月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
30 0
|
5月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
40 0
|
5月前
|
存储 Java 编译器
使用ASM来书写Java代码
使用ASM来书写Java代码
39 0
|
6月前
|
设计模式 Java
java中的jdk代理和cglib代理
在Java中,代理是一种设计模式,它允许一个对象(代理)控制对另一个对象(真实对象)的访问。Java中的代理主要分为两种类型:JDK(Java Dynamic Proxy)代理和CGLIB(Code Generation Library)代理。
|
6月前
|
Arthas 缓存 Java
[Java] 模拟Jdk 以及 CGLib 代理原理
[Java] 模拟Jdk 以及 CGLib 代理原理