Welcome everyone

使用 ByteBuddy 操作字节码

java 汪明鑫 88浏览 0评论

ByteBuddy 是 JavaAgent的一种实现,他的性能比较好,api使用起来也比较方便

本文主要简单介绍如何使用ByteBuddy操作字节码

首先引入依赖

<dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
 </dependency>

上代码

先来看一个方法匹配拦截的例子

@Slf4j
public class ByteBuddyTest {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class<?> loadType = new ByteBuddy()
                .subclass(Object.class)
                .method(ElementMatchers.named("toString"))  // 需要拦截的方法
                .intercept(FixedValue.value("hello world")) //拦截做的事情->FixedValue固定值返回
                .make() // 产生字节码
                .load(ByteBuddyTest.class.getClassLoader()) // 加载类
                .getLoaded(); // 获得class对象

        String result = loadType.newInstance().toString();
        log.info("result:{}", result);
    }
}

上述代码就可以做到修改字节码,让toString()方法固定返回 “hello world”
.method 就是指定需要拦截的方法
.intercept 就是拦截要做的事情
.make 就是产生字节码
.load 就是加载类
.getLoaded 就是获得class对象

再来个例子:

@Slf4j
public class Foo {
    public String aha() { return null; }
    public String foo() { return null; }
    public String foo(String str) { return null; }

    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Foo foo = new ByteBuddy()
                .subclass(Foo.class)
                .method(isDeclaredBy(Foo.class))
                .intercept(FixedValue.value("all method"))
                .method(ElementMatchers.named("foo"))
                .intercept(FixedValue.value("foo call"))
                .method(ElementMatchers.named("foo").and(ElementMatchers.takesArguments(1)))
                .intercept(FixedValue.value("foo call 1 arg"))
                .make()
                .load(Foo.class.getClassLoader())
                .getLoaded()
                .newInstance();

        log.info(foo.aha());
        log.info(foo.foo());
        log.info(foo.foo("nima"));
    }
}

输出:

all method

foo call

foo call 1 arg

还有一个用法,方法委托

public class Source {
    public String hello(String name) {
        return name;
    }
}

public class Target {
    // FIXME: 这个为啥必须得加 static
    public static String hello(String name) {
        return "hello, " + name;
    }
}

public class DelegationTest {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        // 委托方法
        String delegationResult = new ByteBuddy()
                .subclass(Source.class)
                .method(ElementMatchers.named("hello"))
                .intercept(MethodDelegation.to(Target.class))
                .make()
                .load(DelegationTest.class.getClassLoader())
                .getLoaded()
                .newInstance()
                .hello("wmx");
        System.out.println(delegationResult);
    }
}

通过 MethodDelegation 生成代理,代码结果输出 hello, wmx

再来一个基于注解的拦截,实现类似 aop aspect 的功能 =-=

public class Foo {
    public String aha() {
        System.out.println("doing ...");
        return "aha";
    }

整个拦截器

public class LogInterceptor {
    @RuntimeType
    public Object intercept(
            @This Object target,
            @Origin Method method,
            @AllArguments Object[] args,
            @Super Object delegate,
            @SuperCall Callable<?> callable) throws Exception {
        System.out.println("before ... method = " + method.getName());

        // 和Spring AOP 中的 ProceedingJoinPoint.proceed() 好像
        Object result = callable.call();

        System.out.println("after ... method = " + method.getName());
        return result;
    }
}
public class InterceptorTest {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Foo foo = new ByteBuddy()
                .subclass(Foo.class)
                .method(isDeclaredBy(Foo.class))
                .intercept(MethodDelegation.to(new LogInterceptor()))
                .make()
                .load(InterceptorTest.class.getClassLoader())
                .getLoaded()
                .newInstance();

        String result = foo.aha();
        System.out.println(result);
    }
    
}

代码结果输出:

before … method = aha

doing …

after … method = aha

result

ok,有了前面几个例子,我们已经初步学习了ByteBuddy的使用!

转载请注明:汪明鑫的个人博客 » 使用 ByteBuddy 操作字节码

喜欢 (1)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz