java–字节码增强–1.3–ByteBuddy–ByteBuddy常用注解
1、介绍
Byte Buddy 还提供了一些预定义的注解,通过这些注解我们可以告诉 Byte Buddy 将哪些需要的数据注入到 Interceptor 中
2、常用注解
2.1、@RuntimeType
- 告诉 Byte Buddy 不要进行严格的参数类型检测,在参数匹配失败时,尝试使用类型转换方式(runtime type casting)进行类型转换,匹配相应方法。
- 可以用在返回值、参数上,提示ByteBuddy禁用严格的类型检查
public class Target {
@RuntimeType
public static Object intercept(@RuntimeType Object value) {
System.out.println("Invoked method with: " + value);
return value;
}
}
2.2、@This
注入被拦截的目标对象。
2.3、@AllArguments
注入目标方法的全部参数
2.4、@Origin
- 注入被拦截的源方法
- 如果拦截的是字段的话,该注解应该标注到 Field 类型参数。
2.5、@Super
- 注入当前被拦截的、动态生成的那个对象的父类对象
- 通过该对象可以调用目标对象的所有方法。
2.6、@SuperCall
- 这个注解比较特殊,我们要在 intercept() 方法中用于调用父类版本的方法,需要通过这种方式注入,
- @SuperCall与 Spring AOP 中的 ProceedingJoinPoint.proceed() 方法有点类似,需要注意的是,这里不能修改调用参数,
- @SuperCall 注解还可以修饰 Runnable 类型的参数,只不过目标方法的返回值就拿不到了。
2.7、@Argument
- 绑定单个参数
- 可以在拦截器(Target)的拦截方法,intercept 中使用注解注入参数,ByteBuddy 会根据注解给我们注入对于的参数值。比如
void intercept(Object o1, Object o2)
// 等同于
void intercept(@Argument(0) Object o1, @Argument(1) Object o2)
2.8、@DefaultCall
调用默认方法而非super的方法
2.9、@FieldValue
注入被拦截对象的一个字段的值
3、案例
3.1、代码结构
3.2、代码
LogInterceptor
package fei.zhou.demo1.business.ByteBuddy2.demo8;
import net.bytebuddy.implementation.bind.annotation.*;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
public class LogInterceptor {
@RuntimeType //将返回值转换成具体的方法返回值类型,加了这个注解 intercept 方法才会被执行
public Object intercept(
// 被拦截的目标对象 (动态生成的目标对象)
@This Object target,
// 正在执行的方法Method 对象(目标对象父类的Method)
@Origin Method method,
// 正在执行的方法的全部参数
@AllArguments Object[] argumengts,
// 目标对象的一个代理
@Super Object delegate,
// 方法的调用者对象 对原始方法的调用依靠它
@SuperCall Callable<?> callable) throws Exception {
//目标方法执行前执行日志记录
System.out.println("执行目标方法 前:" + method.getName());
// 调用目标方法
Object result = callable.call();
//目标方法执行后执行日志记录
System.out.println("执行目标方法 后:" + method.getName());
return result;
}
}
UserService
package fei.zhou.demo1.business.ByteBuddy2.demo8;
public class UserService {
//方法1
public String method01() {
System.out.println("我是方法1");
return "method01";
}
//方法2
public String method02(String str1) {
System.out.println("我是方法2,参数1");
return str1;
}
//方法3
public String method02(String str1, String str2) {
System.out.println("我是方法2,参数2");
return str1 + str2;
}
}
Test
package fei.zhou.demo1.business.ByteBuddy2.demo8;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
public class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Class<? extends UserService> aClass = new ByteBuddy()
// 创建一个UserService 的子类
.subclass(UserService.class)
//指定类的名称
.name("fei.zhou.UserServiceImpl")
// 指定要拦截的方法
//.method(ElementMatchers.isDeclaredBy(UserService.class))
.method(ElementMatchers.named("method02").and(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(1))))
// 为方法添加拦截器 如果拦截器方法是静态的 这里可以传 LogInterceptor.class
.intercept(MethodDelegation.to(new LogInterceptor()))
// 动态创建对象,但还未加载
.make()
// 设置类加载器 并指定加载策略(默认WRAPPER)
.load(ByteBuddy.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
// 开始加载得到 Class
.getLoaded();
UserService userService = aClass.newInstance();
System.out.println("---------method01----------");
System.out.println(userService.method01());
System.out.println("---------method02 参数1----------");
System.out.println(userService.method02("11111"));
System.out.println("---------method02 参数2----------");
System.out.println(userService.method02("2222", "3333"));
}
}
3.3、结果
---------method01----------
我是方法1
method01
---------method02 参数1----------
执行目标方法 前:method02
我是方法2,参数1
执行目标方法 后:method02
11111
---------method02 参数2----------
我是方法2,参数2
22223333