Javaagent提供的能力可以让我们在main之前(premain)、之后(agentmain)做一些事情,向Skywalking这类监控就是基于javaagent实现的。
Javassist提供的能力可以动态修改字节码,可以在不修改源码的基础上动态编译class文件 参考2 参考3。
我们尝试基于现有服务通过javaagent+javassist的方式对原类做功能增强,开始!
源码地址参考
javaagent工程
-
类图
-
MANIFEST.MF配置Premain-Class
为防止package后被覆盖,pom增加插件
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<!--自动添加META-INF/MANIFEST.MF -->
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Premain-Class>com.dragon.hei.javaagent.premain.MethodTimePreMain</Premain-Class>
<!--<Agent-Class>com.dragon.hei.javaagent.premain.MethodTimePreMain</Agent-Class>-->
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
- 增加premain函数
import com.dragon.hei.javaagent.enhancer.ClassEnhancer;
import com.dragon.hei.javaagent.enhancer.javassist.LogEnhancer;
import com.dragon.hei.javaagent.enhancer.javassist.TimeCostEnhancer;
import com.dragon.hei.javaagent.transformer.MyClassTransformer;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
public class MethodTimePreMain {
public static void premain(String agentArgs, Instrumentation inst) {
//System.out.println("自定义premain:" + agentArgs);
inst.addTransformer(myTransformer(), true);
}
private static ClassFileTransformer myTransformer(){
ClassEnhancer timeCostEnhancer = new TimeCostEnhancer();
ClassEnhancer logEnhancer = new LogEnhancer();
return new MyClassTransformer(logEnhancer, timeCostEnhancer);
}
}
- 增强器链
详见 javagent
LogEnhancer:针对被代理类的方法入口出口打印参数
TimeCostEnhancer:针对被代理类的方法结束时打印接口耗时
wsth工程
- wsth启动类或main函数配置jvm启动参数:-javaagent:/xxx/javagent/target/javagent-1.0-SNAPSHOT.jar
- main函数,本次仅针对DefaultRuleChain做增强
public static void main(String[] args) {
// aviator上下文
AviatorContext context = buildContext();
// 存储表达式执行结果
List<AbstractAviatorRule> response = new ArrayList<>();
// 规则chain
RuleChain ruleChain = new DefaultRuleChain(rules());
ruleChain.matchRule(context, response);
System.out.println("规则链执行结果:"+JSON.toJSONString(response));
}
- 执行结果
遇到的问题
- 如果main函数或应用启动或执行失败,可能是agent的依赖在应用中不存在,可以在应用war包中增加对应的jar或修改pom
- javassist类获取不到,可能是className格式问题,"/“替换成”."
- LogEnhancer打印参数无法识别"$$",暂时写死"$1","$2"
小结
本篇仅在不修改源码情况下增强字节码功能的一次尝试,增强器代码结构性、完整性、可通用性比较差,慢慢完善增加一些实用、可复用的功能。