`
cuishen
  • 浏览: 297922 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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

    博客分类:
  • j2se
阅读更多
一提到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
分享到:
评论

相关推荐

    struts的必须包

    在Struts2中,Javassist用于类的动态代理,以便实现AOP(面向切面编程)和拦截器功能。 5. **ognl-3.0.jar**:Object-Graph Navigation Language(OGNL)是一种强大的表达式语言,用于获取和设置Java对象的属性。在...

    struts2基础上实现简单的图书管理系统(增删改查)的jar包

    在Struts2中,它用于动态生成代理类,实现如AOP(面向切面编程)等高级功能。 6. **sqljdbc4.jar**:这是Microsoft提供的JDBC驱动,用于与SQL Server数据库进行连接,执行SQL语句,实现数据的增删改查操作。 7. **...

    Hibernate包含的jar包

    20. **hibernate-search.jar**:提供全文搜索功能,基于Lucene或其他搜索引擎。 21. **jboss-transaction-api_1.2_spec.jar**:JTA 1.2规范的实现。 22. **javassist-3.20.0-GA.jar**:另一个版本的javassist库,...

    struts2基本(最小)jar包

    5. **.javassist.jar**:Javaassist库用于动态地修改类结构,Struts2使用它来处理Action类的动态代理,实现拦截器的功能。 6. **.commons-logging.jar**:这是一个通用的日志接口,提供日志抽象层,使得Struts2可以...

    ssh2整合好的jar

    在Hibernate中,Javassist用于动态生成代理类以实现懒加载和其他特性。 8. **commons-collections-3.1.jar**:Apache Commons Collections是Apache软件基金会提供的一个工具包,提供了大量的集合类和算法,扩展了...

    activiti.zip

    10. **cglib-2.2.jar**:Code Generation Library,用于生成子类以实现对目标类的动态代理或方法拦截,是 Spring AOP(面向切面编程)框架的重要组成部分,也可能在 Activiti 中用于动态代理或扩展。 综上所述,...

    struts2 所需jar包

    在Struts2中,它用于动态代理,实现如AOP(面向切面编程)的拦截器功能。 5. `commons-lang3-3.1.jar`:Apache Commons Lang是一个包含许多实用工具类的库,提供了字符串处理、日期/时间操作、数学计算等大量功能,...

    J2EE SSH所需的架包

    使用Hibernate,开发者无需编写大量的SQL语句,就能实现对数据库的CRUD操作。为了使用Hibernate,你需要以下架包: - `hibernate-core.jar`:Hibernate的核心库,提供了ORM的所有基本功能。 - `hibernate-...

    hibernate+Struts2包

    在Hibernate中,Javassist用于动态生成代理类,支持延迟加载(Lazy Loading)等特性。 6. Commons Collections:`commons-collections-3.1.jar`是Apache Commons项目的一部分,提供了一系列实用的集合类和算法,...

    s2sh-jar

    6. **javassist-3.9.0.GA.jar**:这是一个Java字节码操作和分析库,常被Hibernate和其他框架用来自动生成或修改类的字节码,以实现动态代理或运行时增强。 7. **commons-collections-3.1.jar**:Apache Commons ...

    Struts2+Spring2+Hibernate3+Annotation所需JAR包

    - **javassist-3.9.0.GA.jar**:动态字节码操作库,被Hibernate用来生成代理类。 - **cglib-2.2.jar**:用于创建动态代理,也是Hibernate依赖的库之一。 - **hibernate-commons-annotations.jar**:Hibernate使用的...

    【Struts 2开发模式基础和简单示例程序】

    `javassist-3.20.0-GA.jar`是Java编程辅助工具,用于运行时动态修改类和类加载器,Struts 2使用它来实现动态代理。 `commons-lang3-3.6.jar`是Apache Commons Lang库,提供了一些高级语言功能,如字符串操作、日期...

    Spring+SpringMVC+Hibernate的jar包集合

    `javassist-3.12.0.GA.jar`是Hibernate使用的字节码处理库,用于动态生成代理类,实现懒加载和缓存机制。 此外,该集合中还包括了其他一些辅助库: - `log4j`是一个日志记录框架,`...

    struts2必须jar包

    5. **javassist.jar**:这是一个代码生成库,Struts2使用它来实现运行时动态代理,以实现拦截器的AOP(面向切面编程)功能。 6. **struts2-plugins.jar**:这通常包含了一些预定义的插件,例如struts2-convention-...

    hibernate-release-4.1.0.Final全部jar包

    Hibernate通过`hibernate-jpa-2.0-api.jar`与JPA 2.0规范对接,同时`javassist.jar`负责运行时字节码操作,以实现动态代理和类增强。`jta.jar`则是Java Transaction API,用于管理事务,确保数据的一致性。 3. **...

    Struts2+Spring3+mybatis3整合Jar包全了

    - `javassist-3.9.0.GA.jar`:MyBatis使用javassist动态生成代理类,以实现SQL执行的动态绑定。 - `spring.jar`:可能包含Spring的部分或全部组件,如AOP、DI等。 整合这三个框架,通常步骤包括: 1. 配置Struts...

    struts2全部jar

    `javassist-3.11.0.GA.jar`是一个动态类库,用于在运行时修改类和接口。Struts2使用它来实现动态代理和AOP(面向切面编程),这使得在不修改原有代码的情况下,可以方便地添加新的功能或拦截器。 `ognl-3.0.6.jar`...

    struts2 所需最少JAR

    5. **javassist-x.x.x.jar**:Javaassist是一个库,允许在运行时动态修改类和接口,Struts2用它来实现AOP和动态代理。 6. **struts2-convention-plugin-x.x.x.jar**:这是一个插件,提供了基于约定优于配置的特性,...

    Struts2.5.10.1的全部jar包

    在Struts2中,它可能用于动态代理和拦截器的实现。 6. **struts2-embeddedjsp-plugin-2.5.10.1.jar**:这是一个插件,使得Struts2能够内嵌JSP视图,使开发者可以直接在Action类中使用JSP标签,简化视图层的开发。 ...

    struts2基本lib架包

    8. **javassist.jar**:这是一个代码生成库,Struts2使用它来实现运行时动态代理,创建拦截器。 9. **slf4j-api.jar** 和 **log4j.jar**:日志框架接口和实现,Struts2使用它们进行日志记录,方便调试和问题定位。 ...

Global site tag (gtag.js) - Google Analytics