ByteBuddy 实战 3 自定义拦截逻辑

360 阅读3分钟

方法拦截实现

1、静态方法拦截

@Test
public void test2() throws IOException {
    DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy()
            .redefine(UserManager.class)// 继承自 Object 类
            .name("a.b.SubObj")
            .method(ElementMatchers.named("selectUserName"))
            //委托给UserManagerInterceptor 中于被拦截方法同签名的静态方法
            .intercept(MethodDelegation.to(UserManagerInterceptor.class))
            .make();// 创建类型
    unloaded.saveIn(new File(path));
}

拦截类

public class UserManagerInterceptor {
    public static String selectUserName(){
        return "hello world";
    }
}

拦截生成的类

image.png

2、也可以代理到对象的成员方法

@Test
public void test2() throws IOException {
    DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy()
            .redefine(UserManager.class)// 继承自 Object 类
            .name("a.b.SubObj")
            .method(ElementMatchers.named("selectUserName"))
            //委托给UserManagerInterceptor 中于被拦截方法同签名的静态方法
            .intercept(MethodDelegation.to(new UserManagerInterceptor()))
            .make();// 创建类型
    unloaded.saveIn(new File(path));
}

拦截类

public class UserManagerInterceptor {
    public String selectUserName(){
        return "hello world";
    }
}

生成的类

image.png

3、通过注解

@Test
public void test2() throws IOException {
    DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy()
            .redefine(UserManager.class)// 继承自 Object 类
            .name("a.b.SubObj")
            .method(ElementMatchers.named("selectUserName"))
            //通过bytebuddy的注解指定增强的方法是谁
            .intercept(MethodDelegation.to(new UserManagerInterceptor()))
            .make();// 创建类型
    unloaded.saveIn(new File(path));
}
public class UserManagerInterceptor {
    @RuntimeType
    public Object selectUserName(
            //表示被拦截的目标对象,只有拦截实力方法时可用
            @This Object targetObject,
            //表示被拦截的目标方法,只有拦截实例方法,和静态方法时可用
            @Origin Method targetMethod,
            //目标方法的参数
            @AllArguments Object[] args,
            @Super Object targetObject2,
            @SuperCall Callable<?> supCall
    ){
        Object call = null;
        try {
            call = supCall.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return call;
    }
}

生成的类

image.png 注解说明

注解名功能描述
@This获取当前被调用对象的引用。对于实例方法,访问调用方法的对象本身。
@AllArguments获取被拦截方法的所有参数。应用于数组类型的参数。
@Argument获取特定位置的参数值。通过 value 属性指定参数的索引。
@Super获取一个代理对象,用于调用父类的方法。
@SuperCall获取一个 Callable 或 Runnable,用于调用原始方法的实现。
@StubValue获取一个方法的默认返回值。
@DefaultCall类似于 @SuperCall,但用于接口的默认方法调用。
@FieldValue访问或修改一个字段的值,通过 value 属性指定字段的名称。
@Morph创建一个可变的方法调用代理,允许改变方法调用的参数或目标。
@Pipe将方法调用的结果传递给另一个方法,通常用于链式调用或流式处理。

动态修改方法入参

0、待增强的类

public class UserManager {
    public String selectUserName(Long id){
        return "hello"+id;
    }
}

1、自定义一个接口

public interface MyCallable {
    Object call(Object[] args);
}

2、用@Morph代替@SuperCall

public class UserManagerInterceptor {
    @RuntimeType
    public Object selectUserName( @AllArguments Object[] args,@Morph MyCallable supCall) {
        Object call = null;
        try {
            if (args != null && args.length > 0) {
                args[0] = Long.valueOf(args[0].toString())+1;
            }
            call = supCall.call(args);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return call;
    }
}

3、使用MyCallable之前要告诉bytebuddy参数的类型是什么

@Test
public void test2() throws IOException {
    DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy()
            .redefine(UserManager.class)// 继承自 Object 类
            .name("a.b.SubObj")
            .method(ElementMatchers.named("selectUserName"))
            //通过bytebuddy的注解指定增强的方法是谁
            .intercept(
                    MethodDelegation.withDefaultConfiguration()
                    //使用MyCallable之前要告诉bytebuddy参数的类型是什么?MyCallable
                    .withBinders(Morph.Binder.install(MyCallable.class))
                    .to(new UserManagerInterceptor()))
            .make();// 创建类型
    unloaded.saveIn(new File(path));
}

构造方法增强

0、待增强的类

public class UserManager {

    public UserManager() {
        System.out.println("UserManager init");
    }

    public String selectUserName(Long id) {
        return "hello" + id;
    }
}

1、生成代理方法

@Test
public void test2() throws IOException {
    DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy()
            .redefine(UserManager.class)// 继承自 Object 类
            .name("a.b.SubObj")
            .constructor(ElementMatchers.any())
            //构造方法执行完
            .intercept(SuperMethodCall.INSTANCE.andThen(
                    MethodDelegation.to(new UserManagerInterceptor())))
            .make();// 创建类型
    unloaded.saveIn(new File(path));
}

3、代理方法

public class UserManagerInterceptor {
    @RuntimeType
    public void init( @This Object targetObj) {
        System.out.println(targetObj+"实例化了");
    }
}

4、生成的类

image.png

@SuperCall 与 redefine rebase subClass 关系

1、@SuperCall 调用的目标方法(指的是原始的方法)
2、静态方法拦截器不能使用redefine,因为redefine不会保留原始方法,在拦截器中就没办法调过@SuperCall调用。
3、静态方法不能被继承的,用subClass 也无法实现拦截

下一篇:ByteBuddy 实战 4 TypePool详解 参考:
# bytebuddy核心教程