javaassist入门(一)-no such field

构建javaassist入门实例:

场景;

监控每个方法的执行时间

方式:

通过javaassist实现

代码:

package com.sirding;

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

public class MyAgent {
	private static Instrumentation inst = null;
	
	public static void premain(String agentArgs, Instrumentation _inst){
		System.out.println("PerfMonAgent.premian() was called.");
		inst = _inst;
		ClassFileTransformer trans = new MyTransform();
		System.out.println("Adding a PerfMonXformer instance to the JVM.");
		inst.addTransformer(trans);
	}
}


package com.sirding;

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

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.NotFoundException;

public class MyTransform implements ClassFileTransformer{

	@Override
	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
		byte[] transformed = null;
		System.out.println("Transforming " + className);
		ClassPool pool = ClassPool.getDefault();
		CtClass cl = null;
		try {
			cl = pool.makeClass(new ByteArrayInputStream(classfileBuffer));
			if(!cl.isInterface()) {
				CtBehavior[] methods = cl.getDeclaredBehaviors();
				for(CtBehavior method : methods){
					doMethod(method);
				}
				transformed = cl.toBytecode();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(cl != null){
				cl.detach();
			}
		}
		return transformed;
	}
	
	
	private void doMethod(CtBehavior method) throws NotFoundException, CannotCompileException {
		if("myTest".equalsIgnoreCase(method.getName())){
			//添加局部变量,如果不同过addLocalVariable设置,在调用属性时将出现compile error: no such field: startTime
			method.addLocalVariable("startTime", CtClass.longType);
			method.insertBefore("System.out.println(startTime);");
			method.insertBefore("startTime = System.currentTimeMillis();");
//			method.insertBefore("long startTime = System.currentTimeMillis();System.out.println(startTime);");
			method.insertBefore("System.out.println("insert before ......");");
			method.insertAfter("System.out.println("leave " + method.getName() + " and time is :" + (System.currentTimeMillis() - startTime));");
		}
	}
}

pom.xml用于生成jar

<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>com.sirding</groupId>
	<artifactId>java-inst</artifactId>
	<version>1</version>
	<packaging>jar</packaging>

	<name>java-inst</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>4.12</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.20.0-GA</version>
		</dependency>

	</dependencies>


	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>2.4</version>
				<configuration>
					<archive>
						<manifestEntries>

							<!-- 重点配置 -->
							<Premain-Class>
								com.sirding.MyAgent
							</Premain-Class>

							<!-- 依赖javaassit包路径 -->
							<Boot-Class-Path>
								自己的系统路径/Maven/repo_3.3.3/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar
							</Boot-Class-Path>
						</manifestEntries>
					</archive>
				</configuration>
			</plugin>
			 
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<configuration>
					<skip>true</skip>
				</configuration>
			</plugin>
			
			<plugin>
		        <groupId>org.apache.maven.plugins</groupId>
		        <artifactId>maven-compiler-plugin</artifactId>
		        <version>3.5.1</version>
		        <configuration>
		            <source>1.8</source>
		            <target>1.8</target>
		        </configuration>
    		</plugin>
		</plugins>
	</build>
</project>

通过maven将上述的项目打java-inst.jar

编写测试类

public class Test{
		public static void main(String[] args){
				System.out.println("hello world");
				new Test().myTest();
			}
			
			public void myTest(){
				System.out.println("===============================================");
				}
	}

将Test.java java-inst.jar放在同一个文件中,执行如下命令

javac Test.java 生成Test.class

java -javaassist:java-inst.jar Test

C:yrtz	estaa>java -javaagent:java-inst-1.jar Test
PerfMonAgent.premian() was called.
Adding a PerfMonXformer instance to the JVM.
Transforming java/lang/invoke/MethodHandleImpl
Transforming java/lang/invoke/MethodHandleImpl$1
Transforming java/lang/invoke/MethodHandleImpl$2
Transforming java/util/function/Function
Transforming java/lang/invoke/MethodHandleImpl$3
Transforming java/lang/invoke/MethodHandleImpl$4
Transforming java/lang/ClassValue
Transforming java/lang/ClassValue$Entry
Transforming java/lang/ClassValue$Identity
Transforming java/lang/ClassValue$Version
Transforming java/lang/invoke/MemberName$Factory
Transforming java/lang/invoke/MethodHandleStatics
Transforming java/lang/invoke/MethodHandleStatics$1
Transforming sun/misc/PostVMInitHook
Transforming sun/usagetracker/UsageTrackerClient
Transforming java/util/concurrent/atomic/AtomicBoolean
Transforming sun/usagetracker/UsageTrackerClient$1
Transforming sun/usagetracker/UsageTrackerClient$4
Transforming sun/usagetracker/UsageTrackerClient$2
Transforming java/lang/ProcessEnvironment
Transforming java/lang/ProcessEnvironment$NameComparator
Transforming java/lang/ProcessEnvironment$EntryComparator
Transforming java/util/Collections$UnmodifiableMap
Transforming java/lang/ProcessEnvironment$CheckedEntrySet
Transforming java/util/HashMap$EntrySet
Transforming java/lang/ProcessEnvironment$CheckedEntrySet$1
Transforming java/util/HashMap$EntryIterator
Transforming java/util/HashMap$HashIterator
Transforming java/lang/ProcessEnvironment$CheckedEntry
Transforming sun/usagetracker/UsageTrackerClient$3
Transforming java/io/FileOutputStream$1
Transforming sun/launcher/LauncherHelper
Transforming sun/misc/URLClassPath$FileLoader$1
Transforming Test
Transforming sun/launcher/LauncherHelper$FXHelper
Transforming java/lang/Class$MethodArray
hello world
insert before ......
1492659861901
===============================================
leave myTest and time is :24
Transforming java/lang/Shutdown
Transforming java/lang/Shutdown$Lock

可以看到“leave myTest and time is :24”通过静态代理,动态的更新执行的method的二进制编码,有效的统计方法执行的时间

PS:

如果在动态添加局部变量时出现如下异常

javassist.CannotCompileException: [source error] no such field: startTime
        at javassist.CtBehavior.insertAfter(CtBehavior.java:877)
        at javassist.CtBehavior.insertAfter(CtBehavior.java:792)
        at com.sirding.Perfmonxformer.doMethod(Perfmonxformer.java:49)
        at com.sirding.Perfmonxformer.transform(Perfmonxformer.java:27)
        at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
        at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
Caused by: compile error: no such field: startTime
        at javassist.compiler.TypeChecker.fieldAccess(TypeChecker.java:845)
        at javassist.compiler.TypeChecker.atFieldRead(TypeChecker.java:803)
        at javassist.compiler.TypeChecker.atMember(TypeChecker.java:988)
        at javassist.compiler.JvstTypeChecker.atMember(JvstTypeChecker.java:66)
        at javassist.compiler.ast.Member.accept(Member.java:39)
        at javassist.compiler.TypeChecker.atBinExpr(TypeChecker.java:329)
        at javassist.compiler.ast.BinExpr.accept(BinExpr.java:41)
        at javassist.compiler.TypeChecker.atPlusExpr(TypeChecker.java:371)
        at javassist.compiler.TypeChecker.atBinExpr(TypeChecker.java:312)
        at javassist.compiler.ast.BinExpr.accept(BinExpr.java:41)
        at javassist.compiler.JvstTypeChecker.atMethodArgs(JvstTypeChecker.java:221)
        at javassist.compiler.TypeChecker.atMethodCallCore(TypeChecker.java:735)
        at javassist.compiler.TypeChecker.atCallExpr(TypeChecker.java:695)
        at javassist.compiler.JvstTypeChecker.atCallExpr(JvstTypeChecker.java:157)
        at javassist.compiler.ast.CallExpr.accept(CallExpr.java:46)
        at javassist.compiler.CodeGen.doTypeCheck(CodeGen.java:242)
        at javassist.compiler.CodeGen.atStmnt(CodeGen.java:330)
        at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
        at javassist.compiler.Javac.compileStmnt(Javac.java:569)
        at javassist.CtBehavior.insertAfterAdvice(CtBehavior.java:892)
        at javassist.CtBehavior.insertAfter(CtBehavior.java:851)
        ... 18 more

确保 你在执行method.addLocalVariable(“startTime”, CtClass.longType);操作,对要使用的局部变量进行了定义。

猜你喜欢

转载自blog.csdn.net/m0_54849806/article/details/126725756