Bootstrap

Java字节码技术(二)字节码增强之ASM、JavaAssist、Agent、Instrumentation

前言

在上篇文章Java字节码技术(一)中已经介绍了Java中字节码相关的基础概念。我们知道,Java代码转换后的JVM指令存在Code区中。如果能对Code区的指令进行新增、修改,即能达到增强字节码的效果。字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术

先贴上文中Demo的代码地址

https://github.com/hosaos/JavaLearningDemo/tree/master/aop-demo

部分内容借鉴自 字节码增强技术探索

从AOP说起

用过Spring的同学肯定对AOP的概念不陌生,AOP能使得程序能在代码前后进行一些功能织入(如日志记录,执行耗时统计等)。 其特点是通过代理方式对目标类在运行期间织入功能。

AOP的实现技术有如下几种
在这里插入图片描述

AOP的实现离不开代理这个概念,什么是代理?我的理解就是:把对目标类的方法A的调用交由代理类来执行,在代理类的B方法中对目标类的方法A进行调用,同时在B方法中加入代理(增强)逻辑

以实例来说明,假设有如下接口AopDemoService,sayHello方法简单输出一句话,要在原有输出的前后增加输出"start","end"


静态代理

程序运行前就已经存在代理类的字节码文件,代理类和原始类的关系在运行前就已经确定

public interface AopDemoService {
   
    void sayHello();
}
public class AopDemoServiceImpl implements AopDemoService  {
   
    @Override
    public void sayHello() {
   
        System.out.println("hello this is aop demo service");
    }
}

AopDemoProxy即为静态代理类

public class AopDemoProxy implements AopDemoService {
   
    private AopDemoService targetService = new AopDemoServiceImpl();
    @Override
    public void sayHello() {
   
        System.out.println("start");
        targetService.sayHello();
        System.out.println("end");
    }
}

缺点:不灵活,每一个需要被代理的目标类,都需要实现一个代理类,增加类、增加方法都需要对代理类代码进行修改


动态代理

JavaProxy

动态代理解决了静态代理的问题,程序运行期间通过JVM反射等机制动态生成代理类,代理类和目标类的关系是运行时才确定的

Java中的动态代理,需要自定义实现一个InvocationHandler类,在其invoke方法中原有逻辑进行增强

public class DynamicHandler implements InvocationHandler {
   
    private Object target;

    public DynamicHandler(Object target) {
   
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
    	//编写增强逻辑
        System.out.println("start");
        Object result = method.invoke(target, args);
        System.out.println("end");
        return result;
    }
}

再通过Proxy.newProxyInstance创建代理类

public class JavaproxyDemo {
   

    public static void main(String[] args) {
   
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        //被代理的接口
        Class[] classes = new Class[]{
   AopDemoService.class};
        InvocationHandler handler = new DynamicHandler(new AopDemoServiceImpl());

        AopDemoService proxyService = (AopDemoService) Proxy.newProxyInstance(classLoader, classes, handler);
        proxyService.sayHello();
    }

}

缺点:实现类必须实现接口,Java中的动态代理通过传入的接口来反射生成一个新的类,在新的类中调用InvocationHandler.invoke对方法进行代理


CGLIB

当某个类没有实现某个接口时,可以通过CGLIB来创建一个继承实现类的子类,用Asm库动态修改子类的代码来实现AOP效果

先定义一个类,不实现任何接口,其中有2个方法,一个普通方法,一个final修饰的方法

@Service
public class AopDemoServiceWithoutInterface {
   
    public void sayHello() {
   
        System.out.println
;