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() } }
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)); } }
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); } }
package com.fh; public class Foo { public String bar(){ return null; } public String foo(){ return null; } public String foo(Object obj){ return null; } }
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; } }
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(); } }
package com.fh; public interface DemoInterface { String get(); void set(String name); }
https://bytebuddy.net/#/tutorial -- 官网教程