字节开源动态代理技术ByteBuddy

架构小魔方 2024-08-20 19:37:01

最早大家听说JAVA的动态代理技术可能是从Spring 的框架开始的,在Spring 框架使用了大量的动态代理技术去使用,主要用在Spring 的AOP切面,比如日志、事务、参数检验等和业务无关性的场景下使用,而常见的使用动态代理的框架有Cglib、Javassist等

本文今天带来另外一种基于字节码增强的技术ByteBuddy,它是字节开源出来的一种字节码技术,相较于Cglib、Javassist等传统的框架,使用起来更简单,性能层面也更具有优势。

ByteBuddy简单使用

一、如何创建一个类

先引入ByteBuddy包

<dependencies> <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy</artifactId> <version>1.14.19</version> </dependency> <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy-agent</artifactId> <version>1.14.19</version> </dependency></dependencies>DynamicType.Unloaded<?> dynamicType = new ByteBuddy() .subclass(Object.class) .name("example.Type") .make();

上面的代码示例是创建了一个继承了Object的类的一个子类, 这个动态创建的类相当于一个只继承Object而没有显式的实现任何方法,字段或构造器的类。类的命名通过name方法去现实的,当然也可以不显式命名,那么就会更加框架的命名规则自动生成。

二、类的加载

Class<?> type = new ByteBuddy() .subclass(Object.class) .make() .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) .getLoaded();

当我们创建完类之后,还需要将所创建的类加载到java的虚拟机中去,使用load方法进行类的加载,类的加载策略有三种,上面的例子采用的是WRAPPER

WRAPPER 策略:创建一个新的 ClassLoader 来加载动态生成的类型。

CHILD_FIRST 策略:创建一个子类优先加载的 ClassLoader,即打破了双亲委派模型。

INJECTION 策略:使用反射将动态生成的类型直接注入到当前 ClassLoader 中。

ByteBuddy实现一个日志拦截器

一、定义一个Service类,针对于这个类进行日志拦截

public UserService { public void addUser(String name,String code){ System.out.println("执行方法:name:"+name+",code:"+code); }}

二、定义切面拦截器,用于拦截进行一些逻辑的处理

public LogFilter{ @RuntimeType //将返回值转换成具体的方法返回值类型,加了这个注解 Filter 方法才会被执行 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="+method.getName()); // 调用目标方法 Object result = callable.call(); //目标方法执行后执行日志记录 System.out.println("执行后:"+method.getName()); return result; }}

三、动态创建类,进行测试

public Test { public static void main(String[] args) throws IllegalAccessException, InstantiationException { Class<? extends UserService> aClass = new ByteBuddy() // 创建一个UserService 的子类 .subclass(UserService.class) //指定类的名称 .name("UserServiceImpl") // 指定要拦截的方法 .method(ElementMatchers.named("addUser")) // 为方法添加拦截器 如果拦截器方法是静态的 这里可以传 LogInterceptor.class .intercept(MethodDelegation.to(new LogFilter())) // 动态创建对象,但还未加载 .make() // 设置类加载器 并指定加载策略(默认WRAPPER) .load(ByteBuddy.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) // 开始加载得到 Class .getLoaded(); UserService userService = aClass.newInstance(); userService.addUser("张三", "123456"); }}

五、测试运行结果

执行前:Method=addUser执行方法:name:张三,code:123456执行后:addUser写在最后

上述是利用ByteBuddy实现一个简单的动态代理的例子,当然实际情况,功能更加丰富,能实现的东西也更多,比如可以基于此现实一个类似于Lombok的框架,也可以基于此去实现Dubbo的动态调用等,尤其是一些基于中台化定义插件扩展点的场景,可以尝试去实现,能让我们的系统变得更加的灵活。

ByteBuddy的官网http://bytebuddy.net/#/tutorial-cn,在这上面可以获取更详细的资料,可以更加深入的了解,如果有这方面的需求不妨试试。

0 阅读:20

架构小魔方

简介:感谢大家的关注