【JavaAgent】字节码编程 - 使用Byte-buddy工具实现插件化开发

简介

假设我们有需求:所有的控制器(Controller)添加耗时,对所有的服务(Service)添加调用信息,使用一个agent代理,也就是所有实现在一个jar包中,该如何处理?

此类问题,最好采用插件化开发:定义一个插件接口,各个功能实现接口即可。本文讲解如何定义接口,接口与byte-buddy集成,不涉及具体功能实现。

使用版本

  • JDK 11
  • byte-buddy 1.12.3

开发项目

要定义接口,必须先了解byte-buddy如何使用,下面是一个简单的例子:

        AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
            return builder
                    // 拦截任意方法
                    .method(ElementMatchers.any())                                <<1>>
                    // 委托
                    .intercept(MethodDelegation.to(ElapseOfTimeWriter.class));    <<2>>
        };


        new AgentBuilder.Default()
                // 指定需要拦截的类
                .type(ElementMatchers.named(TARGET_CLASS))                         <<3>>
                .transform(transformer)                                            <<4>>
                .with(listener)                                                    <<5>>
                .installOn(inst);

首先创建Transformer实例:

<<1>> 定义需要拦截的方法,参数是ElementMatcher<MethodDescription> 实例。本需求就是所有的方法

<<2>> 拦截功能实现类,参数是Class<?>实例,也就是一个普通的类,但这个类必须使用@Advice注解

然后创建AgentBuilder实例:

<<3>>定义需要拦截的类,参数是ElementMatcher<TypeDescription>实例。本需求有两个,一个是Controller,一个是Service类

<<4>> Transformer实例

<<5>> 监听,参数是Listener实例。监听是全局的,只需要实例化一次就可以了,所以不应该包含在接口中

由以上分析可知,<<1>>,<<2>>, <<3>> 都是接口必须有的,子类必须实现的。经过探索,定义如下两个接口:

/**
 * 目标提供者,需要处理的类及方法
 * 
 * @author PinWei Wan
 * @since 1.0.1
 */
public interface TargetElementProvider {
    // 监控方法描述
    ElementMatcher<MethodDescription> methodDescription();

    // 监控类型描述
    ElementMatcher<TypeDescription> typeDescription();
}
/**
 * 代理插件
 * 
 * @author PinWei Wan
 * @since 1.0.1
 */
public interface Plugin {
        // 名称
        String name();

        // 提供者
        TargetElementProvider[] elementMatchers();

        // 织入类
        Class<?> advice();
}

在插件接口中,elementMatchers返回是数组,也就是插件子类可以提供多个匹配规则。

那么与byte-buddy集成就是简单了,代码如下:

public static void premain(String agentArgs, Instrumentation inst) {
        log.info("Agent called - {}", PremainAgent.class.getName());

        AgentBuilder agentBuilder = new AgentBuilder.Default();

        // 扫描所有的插件
        List<Plugin> plugins = PluginFactory.pluginGroup();
        for (Plugin plugin : plugins) {
            // 需要处理的类及方法
            TargetElementProvider[] providers = plugin.elementMatchers();
            for (TargetElementProvider provider : providers) {
                AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
                    builder = builder.visit(Advice.to(plugin.advice()).on(provider.methodDescription()));
                    return builder;
                };
                agentBuilder = agentBuilder.type(provider.typeDescription()).transform(transformer);
            }

        }

        // 监听
        AgentBuilder.Listener listener = new AgentBuilder.Listener() {
            @Override
            public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }

            @Override
            public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader,
                    JavaModule javaModule, boolean b, DynamicType dynamicType) {
                System.out.println("onTransformation - " + typeDescription);
            }

            @Override
            public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule,
                    boolean b) {

            }

            @Override
            public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b,
                    Throwable throwable) {

            }

            @Override
            public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }

        };

        agentBuilder.with(listener).installOn(inst);
    }

具体功能实现,不是本文重点。如需要,参考文章 【JavaAgent】字节码编程 - 方法运行时间实现

最后 

全部的代码,可以在这里查看 opentrace,欢迎顶赞,感谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值