003、从零开始写调用链监控-引入byteBuddy

1、byteBuddy介绍

官网教程:http://bytebuddy.net/#/tutorial
中文翻译教程:https://notes.diguage.com/byte-buddy-tutorial/#creating-java-agents

Byte Buddy是一个代码生成和操作库,用于在Java应用程序的运行时期间创建和修改Java类,而无需编译器的帮助。除了Java类库附带的代码生成实用程序之外,Byte Buddy允许创建任意类,并且不限于实现用于创建运行时代理的接口。此外,Byte Buddy提供了一个方便的API,可以手动,使用Java代理或在构建期间更改类。

官网对其他字节码框架进行了点评:Java proxies 、CGLIB、Javassist,有兴趣的可以了解下,看看英文,不懂就google翻译一下,不外乎就是吹吹自己,找找其他框架的问题。我们引入byteBuddy主要要解决的问题就是和javaagent的Instrumentation衔接,更加方便的对我们需要加强的类进行拦截和加强,这也是调用链采集框架的核心所在。

下面基于前面002章节搭建好的框架,引入byteBuddy,实现一个对类的实例方法进行拦截和加强的DEMO。

2、项目扩展

项目结构:

003、从零开始写调用链监控-引入byteBuddy_第1张图片
1566691341391.png

DEMO说明:

SnifferAgent实现对TestByteBuddy的print方法进行拦截加强。

TestSnifferAgent调用TestByteBuddy里面的两个方法:print和printWithoutInterceptor,测试SnifferAgent的拦截效果。

关键代码:

gy4j-monitor-sniffer的pom.xml

引入byteBuddy的依赖

    
        
            net.bytebuddy
            byte-buddy
            ${bytebuddy.version}
        
    

SnifferAgent.java:

public class SnifferAgent {
    /**
     * 在方法在main方法之前执行,和main方法同Jvm、ClassLoader、Security policy和Context
     *
     * @param agentOps javaagent入参
     * @param inst     对class进行字节码加强的代理实例
     */
    public static void premain(String agentOps, Instrumentation inst) {
        System.out.println("hello javaagent!this is premain!");
        // 基于ByteBuddy建立agent规则
        final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.ENABLED);
        new AgentBuilder.Default(byteBuddy)
                // 忽略不需要拦截的类
                .ignore(initIgnoreElementMatcher())
                // 对类名为cn.gy4j.monitor.test.sniffer.agent.TestByteBuddy的类进行拦截
                .type(ElementMatchers.named("cn.gy4j.monitor.test.sniffer.agent.TestByteBuddy"))
                // 对拦截类进行加强
                .transform(new AgentTransformer())
                // 类加强的侦听器:类加强过程中的事件侦听
                .with(new AgentListener())
                // 基于inst
                .installOn(inst);
    }

    /**
     * 忽略规则构建
     *
     * @return
     */
    private static ElementMatcher initIgnoreElementMatcher() {
        // synthetic总的来说,是由编译器引入的字段、方法、类或其他结构,主要用于JVM内部使用
        // 参考:https://blog.csdn.net/a327369238/article/details/52608805
        return nameStartsWith("net.bytebuddy.").or(ElementMatchers.isSynthetic());
    }

    /**
     * 转换规则构建
     */
    static class AgentTransformer implements AgentBuilder.Transformer {
        @Override
        public DynamicType.Builder transform(DynamicType.Builder builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
            // 对拦截类的print方法进行拦截加强,加强的规则为TestByteBuddyPrintInterceptor
            return builder.method(ElementMatchers.named("print"))
                    .intercept(MethodDelegation.withDefaultConfiguration().to(new TestByteBuddyPrintInterceptor()));
        }
    }

    /**
     * 侦听器
     */
    static class AgentListener implements AgentBuilder.Listener {
        @Override
        public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
        }

        @Override
        public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
            System.out.println("onTransformation:" + typeDescription);
        }

        @Override
        public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded) {
        }

        @Override
        public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("onError:" + typeName);
        }

        @Override
        public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
        }
    }
}

TestByteBuddyPrintInterceptor.java

public class TestByteBuddyPrintInterceptor {
    @RuntimeType
    public Object intercept(@This Object obj,
                            @AllArguments Object[] allArguments,
                            @SuperCall Callable zuper,
                            @Origin Method method) throws Throwable {
        try {
            // 原方法执行前
            System.out.println(" before method:" + method.getName());
        } catch (Throwable t) {
            t.printStackTrace();
            System.out.println("class[" + obj.getClass() + "] before method[" + method.getName() + "] intercept failure");
        }

        Object ret = null;
        try {
            // 原方法调用
            ret = zuper.call();
        } catch (Throwable t) {
            try {
                // 原方法调用异常
                System.out.println("exception method:" + method.getName());
            } catch (Throwable t2) {
                System.out.println("class[" + obj.getClass() + "] handle method[" + method.getName() + "] exception failure");
            }
            throw t;
        } finally {
            try {
                // 原方法执行后
                System.out.println("after method:" + method.getName());
            } catch (Throwable t) {
                System.out.println("class[" + obj.getClass() + "] after method[" + method.getName() + "] intercept failure");
            }
        }
        return ret;
    }
}

TestSnifferAgent.java

public class TestSnifferAgent {
    /**
     * 测试SnifferAgent的main方法
     * jvm配置:-javaagent:G:\gy4j\git_gy4j\gy4j-monitor\gy4j-monitor-sniffer\target\sniffer-agent.jar
     *
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("hello javaagent!this is main!");
        TestByteBuddy testByteBuddy = new TestByteBuddy();
        testByteBuddy.print();
        testByteBuddy.printWithoutInterceptor();
    }
}

TestByteBuddy.java

public class TestByteBuddy {
    public void print() {
        System.out.println("this is the  method:print");
    }

    public void printWithoutInterceptor() {
        System.out.println("this is method:printWithoutInterceptor");
    }
}

3、测试验证

第一步:编译打包

mvn clean package

获取路径:G:\gy4j\git_gy4j\gy4j-monitor\gy4j-monitor-sniffer\target\sniffer-agent.jar

第二步:测试验证

加入jvm参数(使用前面copy的jar路径):
-javaagent:G:\gy4j\git_gy4j\gy4j-monitor\gy4j-monitor-sniffer\target\sniffer-agent.jar
003、从零开始写调用链监控-引入byteBuddy_第2张图片
1566529887800.png
执行TestSnifferAgent的main方法:
003、从零开始写调用链监控-引入byteBuddy_第3张图片
1566694144998.png

4、源码地址

https://github.com/gy4j/gy4j-monitor/tree/003-byteBuddy

你可能感兴趣的:(003、从零开始写调用链监控-引入byteBuddy)