7.如何开发一个Java插件
7.如何开发一个Java插件
分为以下几步:
- 定义插件声明文件
skywalking-plugin.def
- 创建拦截点define类
- 创建插件类
声明文件def
文件名强制是:skywalking-plugin.def
, 存放再 resource
目录下,用于指定插件包的拦截点的全类名
如: mybtis-3.x-plugin 指定了两个拦截点define类
mybatis-3.x=org.apache.skywalking.apm.plugin.mybatis.define.MyBatisInstrumentation
mybatis-3.x=org.apache.skywalking.apm.plugin.mybatis.define.MyBatisShellMethodInstrumentation
skywalking 会加载这两个拦截点类,并实例化,然后读取其中定义的增强规则、拦截规则。
拦截点define类
用于定义插件定义插件支持的拦截规则。
- 拦截增强构造器
ConstructorInterceptPoint
- 拦截增强对象函数
InstanceMethodsInterceptPoint
- 拦截增强静态函数
ClassStaticMethodsEnhancePluginDefine
上面三种拦截规则覆盖了大部分的Java类的实现,这些拦截规则可以通过类名、以及自定义规则针对目标去进行拦截匹配,同时又针对不同的拦截目标指定了增强规则。
基本结构:
- 指定要增强的类
- 可以有多个对象方法的拦截点,每个又指定了要拦截的方法,以及如何增强这个方法
public class MyBatisInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
// 用于指定构造器的拦截增强规则
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return new ConstructorInterceptPoint[0];
}
// 用于指定对象方法的拦截增强规则
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[] {
new InstanceMethodsInterceptPoint() {
// 拦截哪个函数,函数匹配器
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("select").and(takesArguments(4))
.or(named("selectList").and(takesArguments(3)))
.or(named("update").and(takesArguments(2)));
}
// 用哪个类去增强这个函数
@Override
public String getMethodsInterceptor() {
return "org.apache.skywalking.apm.plugin.mybatis.MyBatisInterceptor";
}
@Override
public boolean isOverrideArgs() {
return false;
}
}
};
}
// 要拦截的类,或者说要增强的类
@Override
public ClassMatch enhanceClass() {
return byName("org.apache.ibatis.session.defaults.DefaultSqlSession");
}
}
可以看到,这个插件是拦截增强的类是:org.apache.ibatis.session.defaults.DefaultSqlSession
增强的方法是:
- 拥有四个参数的
select
函数 - 拥有三个参数的
selectList
函数 - 拥有两个参数的
update
函数
对这些函数进行增强的类是:org.apache.skywalking.apm.plugin.mybatis.MyBatisInterceptor
,接下来看下他是如何对这些函数进行增强的.
实例函数如何进行增强?
public class MyBatisInterceptor implements InstanceMethodsAroundInterceptor {
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
// 前置拦截
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
// 后置拦截
return ret;
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
// 异常拦截
}
}
可以看到插件实现了sw针对插件的全生命周期逻辑,前置处理、后置处理、异常处理
可以再不同的生命周期中添加不同的逻辑,实现想要的效果。
静态函数
静态函数增强,以 org.apache.skywalking.apm.plugin.jdk.http.define.HttpClientInstrumentation
为例,他增强的目标是:sun.net.www.http.HttpClient
由于这个 插件对 实例函数和静态函数都有增强,所以只看静态函数的拦截部分。
public class HttpClientInstrumentation extends ClassEnhancePluginDefine {
private static final String ENHANCE_HTTP_CLASS = "sun.net.www.http.HttpClient";
private static final String AFTER_METHOD = "parseHTTP";
private static final String BEFORE_METHOD = "writeRequests";
private static final String NEW_INSTANCE_METHOD = "New";
private static final String INTERCEPT_PARSE_HTTP_CLASS = "org.apache.skywalking.apm.plugin.jdk.http.HttpClientParseHttpInterceptor";
private static final String INTERCEPT_WRITE_REQUEST_CLASS = "org.apache.skywalking.apm.plugin.jdk.http.HttpClientWriteRequestInterceptor";
private static final String INTERCEPT_HTTP_NEW_INSTANCE_CLASS = "org.apache.skywalking.apm.plugin.jdk.http.HttpClientNewInstanceInterceptor";
// 构造器拦截点
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return new ConstructorInterceptPoint[0];
}
// 对象函数拦截点
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
// 删除...
}
// 静态函数拦截点
@Override
public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
return new StaticMethodsInterceptPoint[] {
new StaticMethodsInterceptPoint() {
// 拦截规则
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("New")
.and(takesArguments(5)
.and(takesArgumentWithType(0, "java.net.URL"))
.and(takesArgumentWithType(4, "sun.net.www.protocol.http.HttpURLConnection")));
}
// 声明插件全类名
@Override
public String getMethodsInterceptor() {
return INTERCEPT_HTTP_NEW_INSTANCE_CLASS;
}
@Override
public boolean isOverrideArgs() {
return false;
}
}
};
}
// 要拦截的类,或者说是要增强的类
@Override
protected ClassMatch enhanceClass() {
return NameMatch.byName(ENHANCE_HTTP_CLASS);
}
// 这个好像也重要
@Override
public boolean isBootstrapInstrumentation() {
return true;
}
}
可以看到基本的用法和对象函数的拦截以及增强用法也是一样的,至于构造器拦截点,就当成普通对象函数理解就可以。。用法也是一样的。
这个拦截器,拦截 名为New
的函数,且对参数做了限制,第一个参数和第五个参数的类型必须匹配。