zoukankan      html  css  js  c++  java
  • 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 -- 官网教程
  • 相关阅读:
    SpringBoot | Thymeleaf | 局部更新
    面试 | 冒泡排序优化
    JSP && Servlet | AXIS 0配置 入门
    155. 最小栈
    idea | 命名空间改过后重新导入项目方法
    Java | 基础归纳 | Map.Entry<String, String>
    08_Azkaban案例实践1_Command单一job示例
    07_Azkaban工作流调度器简介及其安装
    06_工作流调度器概述
    05_ Flume多级Agent之间串联案例
  • 原文地址:https://www.cnblogs.com/nihaofenghao/p/14828350.html
Copyright © 2011-2022 走看看