Byte Buddy简单学习

Java中的代码生成库
Java Proxy -- Jdk自带,目标类必须实现接口
Cglib -- 是一个非常强大的库,但是也变得越来越复杂
Javassist -- 使用简单,有自己的编译器,但是性能比不上Javac,而且在实现复杂的逻辑的时候容易出错
Byte Buddy -- 灵活且强大,编写简单,能够应对不同复杂度的需求 主要侧重点在于生成快速的代码
1、Byte Buddy一些API介绍
package com.fh;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;

public class ByteBuddyTest {

    public static void agentmain(String agentArgs,
                                 Instrumentation inst) throws UnmodifiableClassException, IllegalAccessException, InstantiationException {

        String s = new ByteBuddy()//创建对象
                .subclass(Object.class)//subclass增强方式
                .name("com.fh.Demo")//新类型的类名
                //拦截其中的toString方法
                //如果存在重载方法,可以使用其他的API去匹配,例如指定方法的返回值,指定方法的参数等
                .method(ElementMatchers.named("toString"))
                .intercept(FixedValue.value("Hello world"))
                .make()
                //加载新类型,默认使用WRAPPER策略
                .load(ByteBuddy.class.getClassLoader())
                .getLoaded()
                .newInstance()//通过Java反射创建实例
                .toString();//调用方法
        System.out.println(s);

    }

    public static void agentmain(String agentArgs){
//      表示为一个未加载的类型,可以使用 ClassLoadingStrategy加载此类型
//      提供了几种加载策略
//        ClassLoadingStrategy.Default:
//        WRAPPER -- 创建一个新的ClassLoader来加载动态生成的类型
//        CHILD_FIRST -- 创建一个子类优先加载的ClassLoader,即打破双亲委派模型
//        INJECTION -- 使用反射将动态生成的类型直接注入到当前的ClassLoader
        DynamicType.Unloaded<Object>
                make = new ByteBuddy()
                .subclass(Object.class) //生成Object的自雷
                .name("com.fh.Demo") //生成的类名
                .make();
        //加载策略
//                .load(ByteBuddyTest.class.getClassLoader(),
//                        ClassLoadingStrategy.Default.WRAPPER)
//                .getLoaded();
//        为目标类生成一个子类,在子类中插入动态代码
//        new ByteBuddy().subclass()
//        会保存目标类中所有目标类的实现,遇到冲突的字段或者方法,会将字段或者方法复制到具有兼容签名的私有方法中,
//        不会抛弃方法或者字段,从而达到不丢失实现的目的
//        new ByteBuddy().rebase()
//        可以对一个已有的类添加属性和方法,或者删除已有的方法或者实现,如果使用其他方法替换已有的方法,则原来的方法自会消失
//        new ByteBuddy().redefine()

    }
}
View Code

2、修改类  

package com.fh;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;

import static net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default.INJECTION;

/**
 *  动态生成子类,且修改其中的方法
 *
 *  Byte Buddy 会将先定义的放到栈底,后定义的放到栈顶,然后按照出栈流程逐一匹配
 */
public class ByteBuddyUpdate {

    public static void agentmain(String agentArgs) throws IllegalAccessException, InstantiationException {
        Foo foo = new ByteBuddy()
                .subclass(Foo.class)
                .method(ElementMatchers.isDeclaredBy(Foo.class))
                .intercept(FixedValue.value("One!"))
                .method(ElementMatchers.named("foo"))
                .intercept(FixedValue.value("two!"))
                .method(ElementMatchers.named("foo").and(ElementMatchers.takesArguments(1)))
                .intercept(FixedValue.value("three!"))
                .make()
                .load(ByteBuddy.class.getClassLoader(),INJECTION)
                .getLoaded()
                .newInstance();
        System.out.println(foo.bar());
        System.out.println(foo.foo());
        System.out.println(foo.foo(null));
    }
}
View Code

3、委托给其他类做处理

package com.fh;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.implementation.bind.annotation.*;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

import static net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default.INJECTION;

public class ByteBuddyDelegation {

    public static void agentmain(String agentArgs) throws IllegalAccessException, InstantiationException, NoSuchMethodException {
        String hello = new ByteBuddy()
                .subclass(DB.class)
                //拦截Db.hello方法,委托给Interceptor中的静态类处理
                .method(ElementMatchers.named("hello"))
                .intercept(MethodDelegation.to(Interceptor.class))
                .make()
                .load(ClassLoader.getSystemClassLoader(), INJECTION)
                .getLoaded()
                .newInstance()
                .hello("World");
        System.out.println(hello);


        //修改参数
        new ByteBuddy()
                .subclass(DB.class)
                .method(ElementMatchers.named("hello"))
                .intercept(MethodDelegation.withDefaultConfiguration()
                .withBinders(
                        //要用Morph之前,需要通过Morph.Binder告诉Byte Buddy要注入的参数类型是什么
                        Morph.Binder.install(OverrideCallable.class)
                    )
                        .to(new Interceptor())
                )
                .make()
                .load(Main.class.getClassLoader(),INJECTION)
                .getLoaded()
                .newInstance()
                .hello("World");

        //拦截构造器
        new ByteBuddy()
                .subclass(DB.class)
                //通过constructor()方法拦截所有构造方法
                .constructor(ElementMatchers.any())
                //拦截的操作:首先调用目标对象的构造方法,根据前面自动匹配,
                //这里直接匹配到参数为String.class的构造方法
                .intercept(SuperMethodCall.INSTANCE.andThen(
                        //执行完原始构造方法,再开始执行interceptor的代码
                        MethodDelegation.withDefaultConfiguration()
                        .to(new Interceptor())
                )).make().load(Main.class.getClassLoader(),INJECTION)
                .getLoaded()
                .getConstructor(String.class);

    }

   static class Interceptor{
        public static String intercept(String name){
            return "name";
        }

        public static String intercept(int i){
            return "int";
        }

        public static String intercept(Object o){
            return "Object";
        }
        //这里是非静态方法,所以委托的时候就不是类,而是实例
        @RuntimeType //不要进行严格的参数效验,匹配失败的时候,使用类型转换的方式进行转换,匹配对应的方法
        public Object intercept(
                @This Object object,//目标对象 -- 注入被拦截的目标对象
                @AllArguments Object[] allArguments, // 注入目标类中的所有的参数
                @SuperCall Callable zuper,//调用目标方法
                @Origin Method method,//目标方法
                @Super DB db ,//目标对象
                @Morph OverrideCallable callable
        ){

            try {
//                调用目标方法需要传递参数
//                callable.call(allArguments);
               return zuper.call();
          } catch (Exception e) {
                e.printStackTrace();
                return null;
            } finally {

          }
        }
    }


    interface OverrideCallable{
        Object call(Object[] args);
    }


}
View Code
package com.fh;

public class Foo {

    public String bar(){
        return null;
    }

    public String foo(){
        return null;
    }

    public String foo(Object obj){
        return null;
    }
}
View Code
package com.fh;

public class DB {

    public DB(String name){
        System.out.println("Db:"+name);
    }

    public String hello(String name){
        System.out.println("Db:"+name);
        return null;
    }
}
View Code

4、新增方法,新增字段

package com.fh;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.FixedValue;

import java.lang.reflect.Modifier;

/**
 * 新增方法
 * 新增字段
 * 实现一个接口
 */
public class ByteBuddyAddAndImpl {

    public static void agentmain(String agentargs) throws IllegalAccessException, InstantiationException {
        Class<? extends Foo> loaded = new ByteBuddy()
                .subclass(Foo.class)
                .defineMethod("moon",//定义方法的名称
                        String.class,//方法的返回值
                        Modifier.PUBLIC)//public修饰
                .withParameter(String.class, "s")//新增方法的参数
                .intercept(FixedValue.value("Zero!"))//方法的具体实现,返回固定值
                //新增一个字段,字段名字为name,类型为String,且public修饰
                .defineField("name", String.class, Modifier.PUBLIC)
                .implement(DemoInterface.class) //实现DemoInterface接口
                //实现接口的方式是读写name字段
                .intercept(FieldAccessor.ofField("name"))
                .make().load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                .getLoaded();//获取加载后的class

        Foo foo = loaded.newInstance();


    }
}
View Code
package com.fh;

public interface DemoInterface {

    String get();

    void set(String name);
}
View Code
https://bytebuddy.net/#/tutorial -- 官网教程
原文地址:https://www.cnblogs.com/nihaofenghao/p/14828350.html