Javassist 实现代码增强

一、什么是Javassist   

Javassist是一个开源的分析、编辑和创建Java字节码的类库。    

二、如何通过Javassit实现动态代理

利用Javassist实现字节码增强时,可以无须关注字节码刻板的结构,其优点就在于编程简单。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构或者动态生成类。其中最重要的是ClassPool、CtClass、CtMethod、CtField这四个类:

  • CtClass(compile-time class):编译时类信息,它是一个class文件在代码中的抽象表现形式,可以通过一个类的全限定名来获取一个CtClass对象,用来表示这个类文件。
  • ClassPool:从开发视角来看,ClassPool是一张保存CtClass信息的HashTable,key为类名,value为类名对应的CtClass对象。当我们需要对某个类进行修改时,就是通过pool.getCtClass(“className”)方法从pool中获取到相应的CtClass。
  • CtMethod、CtField:这两个比较好理解,对应的是类中的方法和属性。

下面通过代码演示如何使用Javassist.

    现在有一个需求希望可以向对指定方法进行增强,支持,在原方法前、后增强或替换原方法。

首先定义一个枚举,表示增强位置

/**
 * Desc:TODO
 * 
 * @author zhangwei<wei.zw@corp.netease.com>
 * @since 2016年1月14日 下午9:23:17
 * @version v 0.1
 */
public enum InjectType {
	BEFORE, AFTER, REPLACE;
}

增强实现方法

/**
 * Desc:TODO
 * 
 * @author zhangwei<wei.zw@corp.netease.com>
 * @since 2016年1月14日 下午9:19:34
 * @version v 0.1
 */
public final class ClassEnhancedGenerator {
	
	private ClassEnhancedGenerator() {
	}

	/**
	 * 类方法增强<BR>
	 *
	 * 对指定类的方法进行代码增强(将指定的原方法名改为$enhanced,同时复制原方法名进行代码注入)
	 * 
	 * @param className
	 *            待增强的类名
	 * @param methodName
	 *            待增强的方法名
	 * @param provider
	 *            {@link ClassInjectProvider}实现类
	 * @throws Exception
	 */
	public static void enhancedMethod(Class<?> cls, Method[] methods, InjectType injectType, ClassInjectProvider provider)
			throws Exception {
		CtClass ctClass = ClassPool.getDefault().get(cls.getName());
		for (int i = 0; i < methods.length; i++) {
			injectCodeForMethod(ctClass, methods[i].getName(), injectType, provider);
		}
		String resource = cls.getName().replace(".", "/") + ".class";
		URI uri = ClassLoader.getSystemClassLoader().getResource(resource).toURI();
		String classFilePath = uri.getRawPath().substring(0, uri.getRawPath().length() - resource.length());
		ctClass.writeFile(classFilePath);
	}

	/***
	 * 注入增强代码
	 * 
	 * @param ctClass
	 * @param methodName
	 * @param injectType
	 * @param provider
	 * @throws Exception
	 * @author zhangwei<wei.zw@corp.netease.com>
	 */
	private static void injectCodeForMethod(CtClass ctClass, String methodName, InjectType injectType,
			ClassInjectProvider provider) throws Exception {
		CtMethod oldMethod = ctClass.getDeclaredMethod(methodName);
		// 修改原有的方法名称为"方法名$enhanced",如果已存在该方法则返回
		String originalMethod = methodName + "$enhanced";
		CtMethod[] methods = ctClass.getMethods();
		for (int i = 0; i < methods.length; i++) {
			CtMethod method = methods[i];
			if (method.getName().equals(originalMethod)) {
				return;
			}
		}
		oldMethod.setName(originalMethod);
		// 增加代码,复制原来的方法名作为增强的新方法,同时调用原有方法即"方法名$enhanced"
		CtMethod enhancedMethod = CtNewMethod.copy(oldMethod, methodName, ctClass, null);
		// 对复制的方法注入代码
		StringBuffer methodBody = new StringBuffer();
		methodBody.append("{");
		switch (injectType) {
		case BEFORE:
			methodBody.append(provider.injectCode(enhancedMethod));
			methodBody.append("return " + originalMethod + "($$); ");
			break;
		case AFTER:
			methodBody.append("try{");
			methodBody.append("return " + originalMethod + "($$); ");
			methodBody.append("}finally{");
			methodBody.append(provider.injectCode(enhancedMethod));
			methodBody.append("}");
			break;
		default:
			String injectCode = provider.injectCode(enhancedMethod);
			methodBody.append(injectCode);
		}
		methodBody.append("}");
		enhancedMethod.setBody(methodBody.toString());
		ctClass.addMethod(enhancedMethod);
	}
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈脩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值