java字节码操作

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

	}
}


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值