java instrument学习

Java在1.5引入了java.lang.instrument,它允许实现一个java agent,通过agent监测运行在JVM上的程序,监测的机制是对方法字节码的修改。有点类似AOP,但比AOP更加松耦合,因为AOP的代码还是在工程里面的,而agent却与被监控的程序完全隔离。

下面我们用instrument来实现一个简单的java agent。

在启动JVM时,通过指示代理类及其代理选项,会启动一个代理程序(agent),该代理类必须实现公共的静态premain方法,该方法原理上类似于main应用程序入口点:

public static void premain(String agentArgs, Instrumentation inst);
(1)Agent类

package org.kylin.agent;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;

public class Agent {

	private static Instrumentation inst;
	
	public static void premain(String agentArgs, Instrumentation _inst) {
		System.out.println("Agent is called...");
		inst = _inst;
		ClassFileTransformer transformer = new AopClassTransformer();
		inst.addTransformer(transformer);
	}

}
Agent类必须实现premain方法,该方法有两个参数,第一个为自定义传入的代理类参数,第二个为VM自动传入的Instrumentation实例。我们需要给inst添加一个类的转换器,该转换器必须实现ClassFileTransformer接口。

(2)实现ClassFileTransformer类

package org.kylin.agent;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

public class AopClassTransformer implements ClassFileTransformer {

	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
			ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

		System.out.println("Transforming Class: " + className);
		if (!className.equals("org/kylin/agentTest")) {
			return classfileBuffer;
		}

		byte[] data = null;
		ClassReader cr = new ClassReader(classfileBuffer);
		ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
		ClassAdapter ca = new AddTimeClassAdapter(cw);
		cr.accept(ca, ClassReader.SKIP_DEBUG);
		data = cw.toByteArray();

		return data;
	}

}
这里我们实现一个AopClassTransformer类,实现了ClassFileTransformer接口,该类中必须实现transform方法。我们在该方法中打印输出被transform的类。为了方便测试,我们只对稍后做测试的类,即代码中的agentTest类做修改,其他类则直接返回原来的字节码。

在修改类的过程中,用了ASM框架,这里使用了一个ASM中经典案例,

public class C {
    public void m() throws Exception{
        Thread.sleep(100); 
    }
}
即将上述代码,修改如下。

public class C {
    public static long timer;
    public void m() throws Exception{
        timer -= System.currentTimeMillis();
        Thread.sleep(100); 
        timer += System.currentTimeMillis();
        System.out.println(timer);
    }
}
这样,我们就能得到一个方法执行的时间了。关于ASM这部分以及上述代码中用到的AddTimeClassAdapter类,在下一篇文章, ASM学习中详细介绍。

(3)打包agent

在打包agent的时候,需在jar的META-INF/MANIFEST.MF中加入Premain-Class,即我们这里的agent类。

下面是maven的pom.xml。

<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">
	<modelVersion>4.0.0</modelVersion>

	<groupId>org.kylin</groupId>
	<artifactId>agent</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>agent</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>asm</groupId>
			<artifactId>asm</artifactId>
			<version>3.3.1</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>2.6</version>
				<configuration>
					<archive>
						<manifestEntries>
							<Premain-Class>org.kylin.agent.Agent</Premain-Class>
						</manifestEntries>
					</archive>
				</configuration>
			</plugin>

			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		</plugins>

	</build>

</project>
build之后我们会得到一个包agent-0.0.1-SNAPSHOT.jar。

(4)测试agent

另写一个工程测试agent。

package org.kylin;

public class agentTest {

	public static void main(String[] args) throws Exception {
		System.out.println("Agent Test...");
		Thread.sleep(100);
	}

}
运行时,在Run Configurations -> Arguments -> VM arguments里面添加-javaagent:xx/xx/agent-0.0.1-SNAPSHOT.jar,即agent包的全路径。

运行结果如下:

Agent is called...
Transforming Class: java/lang/invoke/MethodHandleImpl
Transforming Class: java/lang/invoke/MethodHandleImpl$1
Transforming Class: java/lang/invoke/MethodHandleImpl$2
Transforming Class: java/util/function/Function
Transforming Class: java/lang/invoke/MethodHandleImpl$3
Transforming Class: java/lang/invoke/MethodHandleImpl$4
Transforming Class: java/lang/ClassValue
Transforming Class: java/lang/ClassValue$Entry
Transforming Class: java/lang/ClassValue$Identity
Transforming Class: java/lang/ClassValue$Version
Transforming Class: java/lang/invoke/MemberName$Factory
Transforming Class: java/lang/invoke/MethodHandleStatics
Transforming Class: java/lang/invoke/MethodHandleStatics$1
Transforming Class: sun/misc/PostVMInitHook
Transforming Class: sun/launcher/LauncherHelper
Transforming Class: sun/misc/URLClassPath$FileLoader$1
Transforming Class: org/kylin/agentTest
Transforming Class: sun/launcher/LauncherHelper$FXHelper
Transforming Class: java/lang/Class$MethodArray
Transforming Class: java/lang/Void
Agent Test...
100
Transforming Class: java/lang/Shutdown
Transforming Class: java/lang/Shutdown$Lock
由输出结果可以看到,agent确实被执行了,且先于测试类agentTest的main方法,ASM修改类的字节码计算方法执行时间也实现了。

由此也可以看出,java instrument实现的监控代码和应用代码完全隔离了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值