神器之ByteBuddy,字节码注入分析代码执行性能

前文

一路上看见晦涩难懂的ASM,小巧可人的Javassist。直到遇见了ByteBuddy才知世上竟有如此的冷艳简洁。

JavaAgent

从Jdk1.5开始Java开始支持Java Agent特性,可以通过premain方法,在Class字节码加载进虚拟机之前对底层的字节码进行修改。从而达到可以自定义特性的功能。给Aop的实现提供了一种更加简洁的方式。

ByteBuddy

字节码修改工具貌似从Java的诞生就一直存在,一开始的ASM,后来可以通过人类可以理解的方式修改字节码的Javassist,到现在的ByteBuddy,甚至不需要了解Class文件的构成就可以修改字节码,实现自己需求,简直神器。对于Class文件结构构成不在此赘述,有兴趣可以去之前摹写的一个加载Class文件的工具了解一下GOM,ByteBuddy官方地址。

通过ByteBuddy修改字节码,计算方法耗时

直接上代码

入口文件

package com.langel.anal.agent;

import com.langel.anal.agent.constant.Const;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;

/**
 * @author [email protected]
 * @date 2019-07-04
 */
public class AnalAgent {

    public static void premain(String agentOps, Instrumentation inst) {
        System.out.println(Const.LOG_PREFIX + "This is an perform monitor agent.");
        new Param(agentOps);
        Param.print();
        AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
            @Override
            public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
                                                    TypeDescription typeDescription,
                                                    ClassLoader classLoader) {
                return builder
                        .method(ElementMatchers.<MethodDescription>any()) // 拦截任意方法
                        .intercept(MethodDelegation.to(TimeInterceptor.class)); // 委托
            }
        };

        AgentBuilder.Listener listener = new AgentBuilder.Listener() {
            @Override
            public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, DynamicType dynamicType) {
            }

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

            @Override
            public void onError(String typeName, ClassLoader classLoader, JavaModule module, Throwable throwable) {
            }

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

        String packagePrefix = Param.get("prefix") == null ? "com" : Param.get("prefix");
        new AgentBuilder
                .Default()
                .type(ElementMatchers.nameStartsWith(packagePrefix)
                        .and(ElementMatchers.not(ElementMatchers.isAnnotation()))
                        .and(ElementMatchers.not(ElementMatchers.isInterface()))
                        .and(ElementMatchers.not(ElementMatchers.isEnum()))) // 指定需要拦截的类
                .transform(transformer)
                .with(listener)
                .installOn(inst);
    }

}

拦截器文件

package com.langel.anal.agent;

import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;

import java.lang.reflect.Method;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.Callable;

/**
 * @author [email protected]
 * @date 2019-07-05
 */
public class TimeInterceptor {

    @RuntimeType
    public static Object intercept(@Origin Method method,
                                   @SuperCall Callable<?> callable) throws Exception {
        String methodName = method.getName();
        if (methodName.startsWith("equals")
                || methodName.startsWith("hashCode")
                || methodName.startsWith("toString")) {
            return callable.call();
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");


        long start = System.currentTimeMillis();
        try {
            // 原有函数执行
            return callable.call();
        } finally {
            Long elpse = System.currentTimeMillis() - start;
            LogCachePool.putInstant(method.getDeclaringClass().getName(), methodName, elpse);
        }
    }
}

然后在JVMOptions加入下面配置

-javaagent:/Users/rick/Workspaces/sourcecode/anal/target/anal-agent.jar=file=log.txt,prefix=com.langel.kmp

这只是ByteBuddy 的API的一个简单使用。更多特性值得去探索

测试

测试代码

package com.langel.kmp;

import com.langel.util.PrintUtils;

/**
 * @Author: rick
 * @Date: 2018/4/15 上午1:55
 * @Description:
 */
public class KMP {
    private static final String searchStr = "bacbababadababacambabacaddababacasdsd";
    private static final String ptrStr = "ababaca";//-1,-1,0,1,2,-1,0

    private static int[] calNext(String pStr) {
        int[] next = new int[pStr.length()];
        next[0] = -1;
        int k = -1;
        for (int i = 1; i < next.length; i++) {
            while (k > -1 && pStr.charAt(i) != pStr.charAt(k + 1)) {
                k = next[k];
            }
            if (pStr.charAt(k + 1) == pStr.charAt(i)) {
                next[i] = k = k + 1;
            }
        }
        return next;
    }

    private static int search(String sStr, String pStr) {
        int[] next = calNext(pStr);
        PrintUtils.println(next);
        int k = -1;
        for (int i = 0; i < sStr.length(); i++) {
            while (k > -1 && sStr.charAt(i) != pStr.charAt(k + 1)) {
                k = next[k];
                System.out.println(k);
            }
            if (pStr.charAt(k + 1) == sStr.charAt(i)) {
                k = k + 1;
                System.out.println("k + 1 : " + k);
            }
            if (k == pStr.length() - 1) {
                return i - pStr.length() + 1;
            }
            System.out.println();
        }
        return -1;
    }

    public static void main(String[] args) throws InterruptedException {
        for (int idx = 0; idx < 1000; idx++) {
            Thread.sleep(1000);
            System.out.println(search(searchStr, ptrStr));
        }

    }
}

日志

Anal monitor : time - 2019-07-11T01:14:30.785
com.langel.kmp.KMP
    -- search : 27ms
    -- calNext : 0ms

个人博客导流(L-Angel)http://l-angel.fun

你可能感兴趣的:(Java,Tools,架构)