论坛首页 Java企业应用论坛

基于javassist实现对接口的动态代理引擎

浏览 3185 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-07-07  
一提到jdk中的java.lang.reflect.Proxy,用过spring,hibernate等框架的人应该都有所了解,对!就是动态代理。AOP - 面向切面编程 - 就是基于动态代理实现的。

平日里项目中用spring aop框架进行日志拦截和声明式事务处理确实很方便好用,从另一种角度将代码解耦,极大的提高了代码的灵活性和可扩展性,在获益的同时我们不得不惊叹aop框架的神奇,但是静下心来想一想:它的核心 - 动态代理 - 其实是依靠运行时动态在内存中实现要代理的接口,并在所有接口的方法实现中反射java.lang.reflect.InvocationHandler的invoke方法;所以要用动态代理就必须先实现自己的InvocationHandler;返回给用户的是代理对象本身,而非接口的原有实现。

想明白了代理的本质,实现它就很容易了,我们可以借助开源的动态生成字节码/类的项目,如:

Byte Code Engineering Library (BCEL) - 在实际的JVM 指令层次上进行操作,提供在运行时在内存中动态生成类的支持

Javassist - 提供类似BCEL的功能,不过它更强调源代码级别的工作,对于程序员来说更加容易上手

好了,废话不多说了,现在看看我用Javassist实现的动态代理,提供接近java.lang.reflect.Proxy的功能

package com.cuishen.myAop;

import java.lang.reflect.Method;

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

/**
 * 基于javassist实现的动态代理类,即在运行时在内存中动态生成要代理的接口的实现,并在接口的方法实现中反射
 * com.cuishen.myAop.InterceptorHandler(拦截器接口)的invoke方法,所以在使用本代理之前请先实现
 * 拦截器接口。本代理提供接近java.lang.reflect.Proxy的功能
 * @author cuishen
 * @version 1.0
 * @see com.cuishen.myAop.InterceptorHandler
 * @see java.lang.reflect.Proxy
 */
public class MyProxyImpl {
	/** 动态代理类的类名后缀 */
	private final static String PROXY_CLASS_NAME_SUFFIX = "$MyProxy_";
	/** 拦截器接口 */
	private final static String INTERCEPTOR_HANDLER_INTERFACE = "com.cuishen.myAop.InterceptorHandler";
	/** 动态代理类的类名索引,防止类名重复 */
	private static int proxyClassIndex = 1;
	
	/**
	 * 暴露给用户的动态代理接口,返回某个接口的动态代理对象,注意本代理实现需和com.cuishen.myAop.InterceptorHandler拦截器配合
	 * 使用,即用户要使用本动态代理,需先实现com.cuishen.myAop.InterceptorHandler拦截器接口
	 * <br>
	 * 使用方法如下:
	 * <br>
	 * <code>
	 * StudentInfoService studentInfo = (StudentInfoService)MyProxyImpl.newProxyInstance(String, String, String);
	 * <br>studentInfo.方法调用;
	 * </code>
	 * @param interfaceClassName String 要动态代理的接口类名, e.g test.StudentInfoService
	 * @param classToProxy String 要动态代理的接口的实现类的类名, e.g test.StudentInfoServiceImpl
	 * @param interceptorHandlerImplClassName String 用户提供的拦截器接口的实现类的类名
	 * @return Object 返回某个接口的动态代理对象
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws NotFoundException
	 * @throws CannotCompileException
	 * @throws ClassNotFoundException
	 * @see com.cuishen.myAop.InterceptorHandler
	 */
	public static Object newProxyInstance(String interfaceClassName, String classToProxy, String interceptorHandlerImplClassName) throws InstantiationException, IllegalAccessException, NotFoundException, CannotCompileException, ClassNotFoundException {
		Class interfaceClass = Class.forName(interfaceClassName);
		Class interceptorHandlerImplClass = Class.forName(interceptorHandlerImplClassName);
		return dynamicImplementsInterface(classToProxy, interfaceClass, interceptorHandlerImplClass);
	}
	
	/**
	 * 动态实现要代理的接口
	 * @param classToProxy String 要动态代理的接口的实现类的类名, e.g test.StudentInfoServiceImpl
	 * @param interfaceClass Class 要动态代理的接口类, e.g test.StudentInfoService
	 * @param interceptorHandlerImplClass Class 用户提供的拦截器接口的实现类
	 * @return Object 返回某个接口的动态代理对象
	 * @throws NotFoundException
	 * @throws CannotCompileException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	private static Object dynamicImplementsInterface(String classToProxy, Class interfaceClass, Class interceptorHandlerImplClass) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException {
		ClassPool cp = ClassPool.getDefault();
		String interfaceName = interfaceClass.getName();
		//动态指定代理类的类名
		String proxyClassName = interfaceName + PROXY_CLASS_NAME_SUFFIX + proxyClassIndex++;
		//要实现的接口的包名+接口名
		String interfaceNamePath = interfaceName;
		
		CtClass ctInterface = cp.getCtClass(interfaceNamePath);
		CtClass cc = cp.makeClass(proxyClassName);
		cc.addInterface(ctInterface);
		Method [] methods = interfaceClass.getMethods();
		for(int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			dynamicImplementsMethodsFromInterface(classToProxy, cc, method, interceptorHandlerImplClass, i);
		}
		return (Object)cc.toClass().newInstance();
	}
	
	/**
	 * 动态实现接口里的方法
	 * @param classToProxy String 要动态代理的接口的实现类的类名, e.g test.StudentInfoServiceImpl
	 * @param implementer CtClass 动态代理类的包装
	 * @param methodToImpl Method 动态代理类里面要实现的接口方法的包装
	 * @param interceptorClass Class 用户提供的拦截器实现类
	 * @param methodIndex int 要实现的方法的索引
	 * @throws CannotCompileException
	 */
	private static void dynamicImplementsMethodsFromInterface(String classToProxy, CtClass implementer, Method methodToImpl, Class interceptorClass, int methodIndex) throws CannotCompileException {
		String methodCode = generateMethodCode(classToProxy, methodToImpl, interceptorClass, methodIndex);
		CtMethod cm = CtNewMethod.make(methodCode, implementer);
		implementer.addMethod(cm);
	}
	
	/**
	 * 动态组装方法体,当然代理里面的方法实现并不是简单的方法拷贝,而是反射调用了拦截器里的invoke方法,并将接收到的参数进行传递
	 * @param classToProxy String 要动态代理的接口的实现类的类名, e.g test.StudentInfoServiceImpl
	 * @param methodToImpl Method 动态代理类里面要实现的接口方法的包装
	 * @param interceptorClass Class 用户提供的拦截器实现类
	 * @param methodIndex int 要实现的方法的索引
	 * @return String 动态组装的方法的字符串
	 */
	private static String generateMethodCode(String classToProxy, Method methodToImpl, Class interceptorClass, int methodIndex) {
		String methodName = methodToImpl.getName();
		String methodReturnType = methodToImpl.getReturnType().getName();
		Class []parameters = methodToImpl.getParameterTypes();
		Class []exceptionTypes = methodToImpl.getExceptionTypes();
		StringBuffer exceptionBuffer = new StringBuffer();
		//组装方法的Exception声明
		if(exceptionTypes.length > 0) exceptionBuffer.append(" throws ");
		for(int i = 0; i < exceptionTypes.length; i++) {
			if(i != exceptionTypes.length - 1) exceptionBuffer.append(exceptionTypes[i].getName()).append(",");
			else exceptionBuffer.append(exceptionTypes[i].getName());
		}
		StringBuffer parameterBuffer = new StringBuffer();
		//组装方法的参数列表
		for(int i = 0; i < parameters.length; i++) {
			Class parameter = parameters[i];
			String parameterType = parameter.getName();
			//动态指定方法参数的变量名
			String refName = "a" + i;
			if(i != parameters.length - 1) parameterBuffer.append(parameterType).append(" " + refName).append(",");
			else parameterBuffer.append(parameterType).append(" " + refName);
		}
		StringBuffer methodDeclare = new StringBuffer();
		//方法声明,由于是实现接口的方法,所以是public
		methodDeclare.append("public ").append(methodReturnType).append(" ").append(methodName).append("(").append(parameterBuffer).append(")").append(exceptionBuffer).append(" {\n");
		String interceptorImplName = interceptorClass.getName();
		//方法体
		methodDeclare.append(INTERCEPTOR_HANDLER_INTERFACE).append(" interceptor = new ").append(interceptorImplName).append("();\n");
		//反射调用用户的拦截器接口
		methodDeclare.append("Object returnObj = interceptor.invoke(Class.forName(\"" + classToProxy + "\").newInstance(), Class.forName(\"" + classToProxy + "\").getMethods()[" + methodIndex + "], ");
		//传递方法里的参数
		if(parameters.length > 0) methodDeclare.append("new Object[]{"); 
		for(int i = 0; i < parameters.length; i++) {
			//($w) converts from a primitive type to the corresponding wrapper type: e.g.
			//Integer i = ($w)5;
			if(i != parameters.length - 1) methodDeclare.append("($w)a" + i + ",");
			else methodDeclare.append("($w)a" + i);
		}
		if(parameters.length > 0) methodDeclare.append("});\n");
		else methodDeclare.append("null);\n");
		//对调用拦截器的返回值进行包装
		if(methodToImpl.getReturnType().isPrimitive()) {
			if(methodToImpl.getReturnType().equals(Boolean.TYPE)) methodDeclare.append("return ((Boolean)returnObj).booleanValue();\n");
			else if(methodToImpl.getReturnType().equals(Integer.TYPE)) methodDeclare.append("return ((Integer)returnObj).intValue();\n");
			else if(methodToImpl.getReturnType().equals(Long.TYPE)) methodDeclare.append("return ((Long)returnObj).longValue();\n");
			else if(methodToImpl.getReturnType().equals(Float.TYPE)) methodDeclare.append("return ((Float)returnObj).floatValue();\n");
			else if(methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return ((Double)returnObj).doubleValue();\n");
			else if(methodToImpl.getReturnType().equals(Character.TYPE)) methodDeclare.append("return ((Character)returnObj).charValue();\n");
			else if(methodToImpl.getReturnType().equals(Byte.TYPE)) methodDeclare.append("return ((Byte)returnObj).byteValue();\n");
			else if(methodToImpl.getReturnType().equals(Short.TYPE)) methodDeclare.append("return ((Short)returnObj).shortValue();\n");
		} else {
			methodDeclare.append("return (" + methodReturnType + ")returnObj;\n");
		}
		methodDeclare.append("}");
		System.out.println(methodDeclare.toString());
		return methodDeclare.toString();
	}
	
}


我也提供了类似于java.lang.reflect.InvocationHandler的接口,我暂且称其为拦截器接口,要用我的代理,就得先实现它

package com.cuishen.myAop;

import java.lang.reflect.Method;

/**
 * 拦截器接口,用户使用com.cuishen.myAop.MyProxyImpl动态代理前,请先实现本接口,
 * 在执行动态代理对象的方法时会自动反射到invoke方法,被代理的对象、方法和参数将做为
 * 参数传递给invoke方法
 * @author cuishen
 * @version 1.0
 * @see com.cuishen.myAop.MyProxyImpl
 */
public interface InterceptorHandler {
	
	/**
	 * 调用动态代理对象的方法将反射本方法,可在本方法实现中添加类似AOP的事前事后操作,只有在本方法体中加入如下代码
	 * <br>
	 * <code>
	 * Object returnObj = method.invoke(obj, args);
	 * <br>
	 * ...
	 * <br>
	 * return returnObj;
	 * </code>
	 * <br>
	 * 被代理的方法才会被执行,返回值将返回给代理最后返回给程序
	 * @param obj Object 被代理的对象
	 * @param method Method 被代理对象的方法
	 * @param args Object[] 被代理对象的方法的参数
	 * @return Object 被代理对象的方法执行后的返回值
	 * @throws Throwable
	 */
	public Object invoke(Object obj, Method method, Object[] args) throws Throwable;
}
  • aop.rar (570.7 KB)
  • 下载次数: 313
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics