java代理-javassist

代理 (agent) 是在你的main方法前的一个拦截器 (interceptor),也就是在main方法执行之前,执行agent的代码。agent的代码与你的main方法在同一个JVM中运行,并被同一个system classloader装载,被同一的安全策略 (security policy) 和上下文 (context) 所管理。

在java5和java6中只需要实现premain这个方法:

package monitor;

import java.lang.instrument.Instrumentation;

public class MyAgent {

	public static void premain(String agentArgs, Instrumentation inst) {
		inst.addTransformer(new MonitorTransformer());
	}
}

premain方法的参数里有一个Instrumentation,使用instrumentation开发者可以构建独立于应用程序的java agent(代理)程序,用来监测运行在JVM上的程序,甚至可以动态的修改和替换类的定义。给力的说,这种方式相当于在JVM级别做了AOP支持,这样我们可以在不修改应用程序的基础上就做到了AOP.你不必去修改应用程序的配置,也不必重新打包部署验证。

JDK5中只能通过命令行参数在启动JVM时指定javaagent参数来设置代理类,而JDK6中已经不仅限于在启动JVM时通过配置参数来设置代理类,JDK6中通过 Java Tool API 中的 attach 方式,我们也可以很方便地在运行过程中动态地设置加载代理类,以达到 instrumentation 的目的。
Instrumentation 的最大作用,就是类定义动态改变和操作

最简单的一个例子,计算某个方法执行需要的时间,不修改源代码的方式,使用Instrumentation 代理来实现这个功能。

建立一个 Transformer 类:MonitorTransformer 

这个类实现了接口public interface ClassFileTransformer。实现这个接口的目的就是在class被装载到JVM之前将class字节码转换掉,从而达到动态注入代码的目的。那么首先要了解MonitorTransformer 这个类的目的,就是对想要修改的类做一次转换,这个用到了javassist对字节码进行修改,可以暂时不用关心jaavssist的原理,用ASM同样可以修改字节码,只不过比较麻烦些。

package monitor;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

public class MonitorTransformer implements ClassFileTransformer {

	final static String prefix = "\nlong startTime = System.currentTimeMillis();\n";
	final static String postfix = "\nlong endTime = System.currentTimeMillis();\n";
	final static List<String> methodList = new ArrayList<String>();

	public MonitorTransformer() {
		methodList.add("main.TimeTest.sayHello");
		methodList.add("main.TimeTest.sayHello2");
	}

	@Override
	public byte[] transform(ClassLoader loader, String className,
			Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
			byte[] classfileBuffer) throws IllegalClassFormatException {
		if (className.startsWith("main")) {//判断加载的class的包路径是不是需要监控的类
			className = className.replace("/", ".");
			CtClass ctclass = null;
			try {
				ctclass = ClassPool.getDefault().get(className);//使用全称,用于取得字节码类<使用javassist>
				for (String method : methodList) {
					if (method.startsWith(className)) {
						String methodName = method.substring(
								method.lastIndexOf(‘.‘) + 1, method.length());

						String outputStr = "\nSystem.out.println(\"this method "
								+ methodName
								+ " cost:\" +(endTime - startTime) +\"ms.\");";

						CtMethod ctmethod = ctclass
								.getDeclaredMethod(methodName);//得到这方法实例
						String newMethodName = methodName + "$impl";//新定义一个方法叫做比如sayHello$impl
						ctmethod.setName(newMethodName);//原来的方法改个名字   

						CtMethod newMethod = CtNewMethod.copy(ctmethod,
								methodName, ctclass, null);//创建新的方法,复制原来的方法 ,名字为原来的名字
						//构建新的方法体
						StringBuilder bodyStr = new StringBuilder();
						bodyStr.append("{");
						bodyStr.append(prefix);
						bodyStr.append(newMethodName + "($$);\n");//调用原有代码,类似于method();($$)表示所有的参数
						bodyStr.append(postfix);
						bodyStr.append(outputStr);
						bodyStr.append("}");

						newMethod.setBody(bodyStr.toString());//替换新方法
						ctclass.addMethod(newMethod);//增加新方法
					}
				}
				return ctclass.toBytecode();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}
}

代码结构:

Manifest-Version: 1.0
Premain-Class: monitor.MyAgent
Can-Redefine-Classes: true
Boot-Class-Path: javassist.jar

注意有一行空格

下面把代理打成一个jar包

导出的时候注意:将MANIFEST.MF打包进去

导出的jar放入:D:\javaagentTest\agentMethod.jar  供后面测试使用,将javassist.jar也放入相同路径

测试代码:

package main;

public class TimeTest {

	public static void main(String[] args) {
		sayHello();
		sayHello2("hello world222222222");
	}

	public static void sayHello() {
		try {
			Thread.sleep(2000);
			System.out.println("hello world!!");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void sayHello2(String hello) {
		try {
			Thread.sleep(1000);
			System.out.println(hello);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

测试代码在运行的时候加上VM arguments:
-javaagent:D:\javaagentTest\agentMethod.jar

hello world!!
this method sayHello cost:2000ms.
hello world222222222
this method sayHello2 cost:1000ms.

参考:http://blog.csdn.net/qyongkang/article/details/7765255

时间: 2024-10-26 00:15:24

java代理-javassist的相关文章

java 代理模式详解

java 动态代理(JDK和cglib) 设计模式这东东每次看到就明白可过段时间又不能很流利的说出来,今天就用详细的比喻和实例来加深自己的理解(小弟水平不高有不对的地方希望大家能指出来). (1)代理这个词生活中有很多比如在街边卖手机卡.充公交地铁卡的小商店他们都起了代理的作用,java中的代理跟这些小店商的作用是一样的.再比如我想在淘宝上开个服装店但又没有货源怎么办,这时候我就要跟淘宝上某一卖家联系做他的代理.我跟我的商家都要卖衣服(就好比我们都继承了卖衣服的接口sellClothesInte

java代理Proxy

首先是静态代理: 1 public class Test1 { 2 3 public static void main(String[] args) { 4 IA a = new APoxy(new A()); 5 a.doJob(); 6 } 7 8 } 9 //功能接口 10 interface IA{ 11 public void doJob(); 12 } 13 //委托类 14 class A implements IA{ 15 16 @Override 17 public void

Java代理(Aop实现的原理)

经过大牛同事的一句指点立刻明白的代理实现方式,Spring Aop应该也是这么去做的.直接上代码 实现在Car的run方法之前调用star方法,在run方法之后调用stop方法. Car类 package com.lubby.test; public class Car { public void run() { System.out.println("I am running...."); } } Car的run方法之前和之后调用的方法 package com.lubby.test;

Java代理模式

Java代理模式分为静态代理和动态代理模式 静态代理模式比较简单,直接上图和代码: 代理模式类图如下: 在代理模式中的角色: ● 抽象对象角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象. ● 目标对象角色:定义了代理对象所代表的目标对象. ● 代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象:代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象.代理对象通常在客户端调用传递给目标对象之前或之后,执行某个

java 代理的三种实现方式

Java 代理模式有如下几种实现方式: 1.静态代理. 2.JDK动态代理. 3.CGLIB动态代理. 示例,有一个打招呼的接口.分别有两个实现,说hello,和握手.代码如下. 接口: public interface Greeting { public void doGreet(); } 实现类: public class SayHello implements Greeting { @Override public void doGreet() { System.out.println("

java代理模式学习

Java动态代理模式 1. 代理:一个角色代表别一个角色来完成某些特定的功能. 比如:生产商,中间商,客户这三者这间的关系 客户买产品并不直接与生产商打交道,也不用知道产品是如何产生的,客户只与中间商打交道,而中间商就可以对产品进行一些包装,提供一些售后的服务. 代理模式有三个角色: 1. 抽象主题角色 2. 代理主题角色 3. 实际被代理角色 其它类通过访问代理主题角色来访问实际被代理角色. 2. 下面我们来个一个静态代理的实现. 我以一个坦克为例. 抽象主题角色:Moveable Java代

java代理机制简单实现

java代理分静态代理和动态代理,动态代理有jdk代理和cglib代理两种,在运行时生成新的子类class文件.本文主要练习下动态代理,代码用于备忘.对于代理的原理和机制,网上有很多写的很好的,就不班门弄斧了. jdk代理 Java代码下载    import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Prox

java 代理 进程ID

java 代理设置 System.setProperty("http.proxyHost", "127.0.0.1"); System.setProperty("http.proxyPort", "3306"); java 获取进程ID String name = ManagementFactoryHelper.getRuntimeMXBean().getName(); System.out.println(name); //

Java 代理机制学习总结

-------<a href="http://www.itheima.com/"">android培训</a>.<a href="http://www.itheima.com/" ">java培训</a>期待与您交流!---------- Java 代理机制学习总结 在编写程序时,除了要解决核心业务逻辑时还要编写一些与核心业务逻辑相关的系统服务代码.如日志,安全等代码.在没有使用java代理机制时,