核心思想
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" ......
很熟悉吧,我们要插桩的这个对象就是 ComboPooledDataSource ,
如何很直观的展示出来的
- 实时获取ComboPooledDataSource的属性信息
- 简洁直观的展示相关属性信息
实现第一个,那就在ComboPooledDataSource构造函数后下手,将ComboPooledDataSource放到System Properties 中, 然后开启一个HTTP服务,对外提供服务访问即可。
编码实现
package com.artisan.agent.collect.c3p0; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import javassist.ClassPool; import javassist.CtClass; import javassist.LoaderClassPath; import java.io.IOException; import java.io.OutputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.net.InetSocketAddress; import java.security.ProtectionDomain; import java.util.concurrent.Executors; /** * @author 小工匠 * @version 1.0 * @description: C3P0插桩 * @date 2020/8/29 9:15 * @mark: show me the code , change the world */ public class C3P0Agent { // 要插装的类 static String targetClass = "com.mchange.v2.c3p0.ComboPooledDataSource"; public static void premain(String args , Instrumentation instrumentation){ // 类转换器处理插桩逻辑 instrumentation.addTransformer(new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { // 插桩后的对象 byte[] result = null; // 条件判断 if (className != null && className.replace("/", ".").equals(targetClass)){ // 实例化pool , 将当前的ClassLoader设置到pool ClassPool pool = new ClassPool(); pool.insertClassPath(new LoaderClassPath(loader)); try { // 获取目标对象 CtClass ctl = pool.get(targetClass); // 获取构造函数,插桩 将对象放入系统属性中 ctl.getConstructor("()V") //构造函数 .insertAfter("System.getProperties().put(\"c3p0Source$agent\", $0);"); // $0 this本身 // 转成class result = ctl.toBytecode(); // 暴漏HTTP服务 new C3P0Agent().openHttpServer(); } catch (Exception e) { e.printStackTrace(); } } return result; } }); } /** * 对外提供Http 服务展示DataSource当前状态 * @throws IOException */ private void openHttpServer() throws IOException { InetSocketAddress addr = new InetSocketAddress(7777); HttpServer server = HttpServer.create(addr, 0); // 设置上下文 server.createContext("/", new MyHttpHandler()); server.setExecutor(Executors.newCachedThreadPool()); server.start(); } private class MyHttpHandler implements HttpHandler { @Override public void handle(HttpExchange exchange) throws IOException { Headers responseHeaders = exchange.getResponseHeaders(); responseHeaders.set("Content-Type", "text/plain;charset=UTF-8"); exchange.sendResponseHeaders(200, 0); OutputStream responseBody = exchange.getResponseBody(); // 输出c3p0状态 responseBody.write(C3P0Agent.this.getStatus().getBytes()); responseBody.flush(); responseBody.close(); } } public String getStatus() { Object source2 = System.getProperties().get("c3p0Source$agent"); if (source2 == null) { return "未初始任何c3p0数据源"; } return source2.toString(); } }
打包
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>artisan-apm</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>apm-agent</artifactId> <dependencies> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.18.1-GA</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> <scope>provided</scope> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.2</version> <configuration> <archive> <manifestEntries> <Project-name>${project.name}</Project-name> <Project-version>${project.version}</Project-version> <Boot-Class-Path>javassist-3.18.1-GA.jar</Boot-Class-Path> <Premain-Class>com.artisan.agent.collect.c3p0.C3P0Agent</Premain-Class> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes> </manifestEntries> </archive> <skip>true</skip> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <artifactSet> <includes> <include>org.javassist:javassist</include> </includes> </artifactSet> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.artisan.agent.collect.c3p0.C3P0Agent</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
打包的时候,连同javassist也打包进去
配置验证
在启动的JVM参数中配置如下信息
-javaagent:D:\IdeaProjects\artisan-apm\apm-agent\target\apm-agent-1.0-SNAPSHOT.jar
然后,访问 http:// localhost:7777