動態編譯Java源文件:
主要作用就是可以動態的編譯生成一些class文件,源碼你可以自己拼寫。
demo:
package com.partner4java.javacompiler;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
/**
* 動態編譯
*
*/
public class CompilerTest {
public static void main(String[] args) throws Exception {
// 類的文本
String source = "public class Main { public static void main(String[] args) {System.out.println(\"Hello World! \");} }";
// 爲 JavaFileObject 中的大多數方法提供簡單實現。應子類化此類並用作 JavaFileObject 實現的基礎。
// 子類可以重寫此類任意方法的實現和規範,只要不違背 JavaFileObject 的常規協定。
SimpleJavaFileObject sourceObject = new CompilerTest.StringSourceJavaObject(
"Main", source);
// 爲查找工具提供者提供方法,例如,編譯器的提供者。
// 獲取此平臺提供的 Java 編程語言編譯器。
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 爲此工具獲取一個標準文件管理器實現的新實例。
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
null, null, null);
Iterable<? extends JavaFileObject> fileObjects = Arrays
.asList(sourceObject);
// 使用給定組件和參數創建編譯任務的 future。
CompilationTask task = compiler.getTask(null, fileManager, null, null,
null, fileObjects);
boolean result = task.call();
if (result) {
System.out.println(" 編譯成功。");
}
}
/**
* 主要指定了編譯的存儲位置和格式
*
*/
static class StringSourceJavaObject extends SimpleJavaFileObject {
private String content = null;
public StringSourceJavaObject(String name, String content)
throws URISyntaxException {
super(URI.create("string:///" + name.replace('.', '/')
+ Kind.SOURCE.extension), Kind.SOURCE);
this.content = content;
}
public CharSequence getCharContent(boolean ignoreEncodingErrors)
throws IOException {
return content;
}
}
}
是在字節碼生成之後對其修改,增強其功能。
CGLIB hellowrld:
http://blog.csdn.net/partner4java/article/details/7627527
基本都類似於這種做法。
java.lang.instrument:
提供允許 Java 編程語言代理檢測運行在 JVM 上的程序的服務。
jdk提供的合法的AOP
兩個比較牛B的技術帖子:
http://www.ibm.com/developerworks/cn/java/j-lo-instrumentation/
http://www.ibm.com/developerworks/cn/java/j-cwt06075/
http://blog.sina.com.cn/s/blog_605f5b4f0100qfvc.html
package com.partner4java.instrument;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class MyLog implements ClassFileTransformer {
// 方法。通過這個方法,代理可以得到虛擬機載入的類的字節碼(通過 classfileBuffer
// 參數)。代理的各種功能一般是通過操作這一串字節碼得以實現的。同時還需要提供一個公共的靜態方法:
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
// transform 函數的最後,返回 null 值,表示不需要進行類字節碼的轉化
System.out.println("Hello,\t" + className);
return null;
}
// 一般會在這個方法中創建一個代理對象,通過參數 inst 的 addTransformer()
// 方法,將創建的代理對象再傳遞給虛擬機。這個方法是一個入口方法,有點類似於一般類的 main 方法。
public static void premain(String agentArgs, Instrumentation inst) {
// options 參數是通過命令行傳遞進來的,類似於調用 main 函數時傳遞的參數。被傳遞進來的命令行參數是一個完整的字符串,不同於
// main 方法,該字符串的解析完全由代理自己負責。
// 定製完代理的行爲之後,創建一個 MyLog 代理的實例,將該實例傳遞給虛擬機。
if (agentArgs != null) {
System.out.printf(" I've been called with options: \"%s\"\n",
agentArgs);
} else
System.out.println(" I've been called with no options.");
inst.addTransformer(new MyLog());
}
}
maven打包如com.partner4java.javautil.codemanage-0.0.1-SNAPSHOT.jar,然後
C:\Users\Administrator>java -javaagent:E:\partner4java\code\workspace\java字節碼
操作\target\com.partner4java.javautil.codemanage-0.0.1-SNAPSHOT.jar="hello world
" com.partner4java.javacompiler.CompilerTest
打印運行時間demo:
com.partner4java.instrument.Timing
package com.partner4java.instrument;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.Type;
public class Timing implements ClassFileTransformer {
private String methodName;
private Timing(String methodName) {
this.methodName = methodName;
System.out.println(methodName);
}
public byte[] transform(ClassLoader loader, String className, Class cBR,
java.security.ProtectionDomain pD, byte[] classfileBuffer)
throws IllegalClassFormatException {
try {
ClassParser cp = new ClassParser(new java.io.ByteArrayInputStream(
classfileBuffer), className + ".java");
JavaClass jclas = cp.parse();
ClassGen cgen = new ClassGen(jclas);
Method[] methods = jclas.getMethods();
int index;
for (index = 0; index < methods.length; index++) {
if (methods[index].getName().equals(methodName)) {
break;
}
}
if (index < methods.length) {
addTimer(cgen, methods[index]);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cgen.getJavaClass().dump(bos);
return bos.toByteArray();
}
System.err.println("Method " + methodName + " not found in "
+ className);
System.exit(0);
} catch (IOException e) {
System.err.println(e);
System.exit(0);
}
return null; // No transformation required
}
private static void addTimer(ClassGen cgen, Method method) {
// set up the construction tools
InstructionFactory ifact = new InstructionFactory(cgen);
InstructionList ilist = new InstructionList();
ConstantPoolGen pgen = cgen.getConstantPool();
String cname = cgen.getClassName();
MethodGen wrapgen = new MethodGen(method, cname, pgen);
wrapgen.setInstructionList(ilist);
// rename a copy of the original method
MethodGen methgen = new MethodGen(method, cname, pgen);
cgen.removeMethod(method);
String iname = methgen.getName() + "_timing";
methgen.setName(iname);
cgen.addMethod(methgen.getMethod());
Type result = methgen.getReturnType();
// compute the size of the calling parameters
Type[] parameters = methgen.getArgumentTypes();
int stackIndex = methgen.isStatic() ? 0 : 1;
for (int i = 0; i < parameters.length; i++) {
stackIndex += parameters[i].getSize();
}
// save time prior to invocation
ilist.append(ifact.createInvoke("java.lang.System",
"currentTimeMillis", Type.LONG, Type.NO_ARGS,
Constants.INVOKESTATIC));
ilist.append(InstructionFactory.createStore(Type.LONG, stackIndex));
// call the wrapped method
int offset = 0;
short invoke = Constants.INVOKESTATIC;
if (!methgen.isStatic()) {
ilist.append(InstructionFactory.createLoad(Type.OBJECT, 0));
offset = 1;
invoke = Constants.INVOKEVIRTUAL;
}
for (int i = 0; i < parameters.length; i++) {
Type type = parameters[i];
ilist.append(InstructionFactory.createLoad(type, offset));
offset += type.getSize();
}
ilist.append(ifact.createInvoke(cname, iname, result, parameters,
invoke));
// store result for return later
if (result != Type.VOID) {
ilist.append(InstructionFactory.createStore(result, stackIndex + 2));
}
// print time required for method call
ilist.append(ifact.createFieldAccess("java.lang.System", "out",
new ObjectType("java.io.PrintStream"), Constants.GETSTATIC));
ilist.append(InstructionConstants.DUP);
ilist.append(InstructionConstants.DUP);
String text = "Call to method " + methgen.getName() + " took ";
ilist.append(new PUSH(pgen, text));
ilist.append(ifact.createInvoke("java.io.PrintStream", "print",
Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));
ilist.append(ifact.createInvoke("java.lang.System",
"currentTimeMillis", Type.LONG, Type.NO_ARGS,
Constants.INVOKESTATIC));
ilist.append(InstructionFactory.createLoad(Type.LONG, stackIndex));
ilist.append(InstructionConstants.LSUB);
ilist.append(ifact.createInvoke("java.io.PrintStream", "print",
Type.VOID, new Type[] { Type.LONG }, Constants.INVOKEVIRTUAL));
ilist.append(new PUSH(pgen, " ms."));
ilist.append(ifact.createInvoke("java.io.PrintStream", "println",
Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));
// return result from wrapped method call
if (result != Type.VOID) {
ilist.append(InstructionFactory.createLoad(result, stackIndex + 2));
}
ilist.append(InstructionFactory.createReturn(result));
// finalize the constructed method
wrapgen.stripAttributes(true);
wrapgen.setMaxStack();
wrapgen.setMaxLocals();
cgen.addMethod(wrapgen.getMethod());
ilist.dispose();
}
public static void premain(String options, Instrumentation ins) {
if (options != null) {
ins.addTransformer(new Timing(options));
} else {
System.out
.println("Usage: java -javaagent:Timing.jar=\"class:method\"");
System.exit(0);
}
}
}