动态编译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()); } }
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); } } }