java动态代理(jdk和cglib)

分类:软件编程
阅读:1372
作者:majingjing
发布:2016-11-02 11:29

在TMBlog的系统程序中,使用了大量的代理. 并非是代理在程序中非得使用,而是在这个系统中将所涉及到的知识都应用在了TMBlog中. 其中典型的实现就是hessian的接口服务发布. 学习过hessian的客官都知道,发布hessian服务必须要使用接口和实现类.(具体请参见这篇文章) 尤其是在和spring做集成的时候,spring的暴露服务校验更是相当的全面.原因也是因为hessian在安全方面做得确实不是很强,安全机制都要求自己去实现.

下面介绍下两种代理的示例 1. jdk代理 MyInvocationHandler.java

//这里我们使用了泛型,假设我们有很多的类都需要进行性能监控,就可以通过在创建本类对象时在泛型标识处改成对应需要监控的类即可。
//注意需要实现JDK反射包中的InvocationHandler接口
public class MyInvocationHandler<E> implements InvocationHandler {
	//需要被代理的对象
	private E target;
	
	public MyInvocationHandler(E target){
		this.target = target;
	}
	/**
	 * @param:
    *  proxy : 在其上调用方法的代理实例
    *  method : 对应于在目标对象调用的方法。
    *  args : 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null
    *           基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。 
	*  @return  从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,
*  			则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。
    *  		如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException
    *  		否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。 
    *  @throws  Throwable - 从代理实例上的方法调用抛出的异常。
	*  		 该异常的类型必须可以分配到在接口方法的 throws 子句中声明的任一异常类型
	*  		 或未经检查的异常类型 java.lang.RuntimeException 或 java.lang.Error。
	* 			 如果此方法抛出经过检查的异常,该异常不可分配到在接口方法的 throws 子句中声明的任一异常类型.
	*   		 代理实例的方法调用将抛出包含此方法曾抛出的异常的 UndeclaredThrowableException。
	*/
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Long beginTime = System.currentTimeMillis();//记录开始时间
		//调用目标对象的方法,同时获取该方法的返回值,作为我们本代理方法(invoke)的返回值
		Object returnValue = method.invoke(target, args);//target为我们方法所在的目标类,args为方法参数
		System.out.println("方法" + method.getName() + "调用结束,耗时"+ (System.currentTimeMillis() - beginTime));
		return returnValue;
	}

}

MyProxy.java

public interface MyProxy {

	void method1() throws InterruptedException;
	void method2() throws InterruptedException;
	void method3() throws InterruptedException;
}

OldClass.java

public class OldClass implements MyProxy {
	@Override
	public void method1() throws InterruptedException{
		System.out.println("正在处理业务逻辑1");
		Thread.sleep(100);//模拟处理业务逻辑4过程
		System.out.println("业务逻辑1处理完成");
	}
	@Override
	public void method2() throws InterruptedException{
		System.out.println("正在处理业务逻辑2");
		Thread.sleep(200);//模拟处理业务逻辑2过程
		System.out.println("业务逻辑2处理完成");
	}
	@Override
	public void method3() throws InterruptedException{
		System.out.println("正在处理业务逻辑3");
		Thread.sleep(300);//模拟处理业务逻辑3过程
		System.out.println("业务逻辑3处理完成");
	}
	//下面还有很多很多。。
}

测试代码 MainTest.java

public class MainTest {
	public static void main(String args[]) throws InterruptedException{
		OldClass oldClass = new OldClass();
		InvocationHandler handler = new MyInvocationHandler<OldClass>(oldClass);
		MyProxy myProxy = (MyProxy)Proxy.newProxyInstance(MyProxy.class.getClassLoader(),new Class[]{MyProxy.class},handler);
		myProxy.method1();
		myProxy.method2();
		myProxy.method3();
	}
}

控制台输出

正在处理业务逻辑1 业务逻辑1处理完成 方法method1调用结束,耗时100 正在处理业务逻辑2 业务逻辑2处理完成 方法method2调用结束,耗时200 正在处理业务逻辑3 业务逻辑3处理完成 方法method3调用结束,耗时300


2. cglib代理

ProxyFactory.java

public class ProxyFactory {
	
	private Enhancer enhancer = new Enhancer();// 动态的类生成器

	public Object createSubObject(Class<?> clazz) {
		enhancer.setSuperclass(clazz);// 设置需要创建的类,这个类的父类是clazz类
		// 当通过enhancer创建的类中的方法被调用时,该方法会被CallBack指定的对象拦截。
		enhancer.setCallbacks(new Callback[] { new MyTimeInterceptor(), new MyRecordInterceptor() });
		enhancer.setCallbackFilter(new MyCallBackFilter());// 设置我们自定义的过滤器
		return enhancer.create();// 通过字节码技术动态创建子类实例
	}
}

MyTimeInterceptor.java

public class MyTimeInterceptor implements MethodInterceptor {

	@Override
	public Object intercept(Object target, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {//拦截所有父类方法的调用
		Long beginTime = System.currentTimeMillis();//记录开始时间
		//调用目标对象的方法,同时获取该方法的返回值,作为我们本代理方法(invoke)的返回值
		Object returnValue = proxy.invokeSuper(target, args);//target为我们方法所在的目标类,args为方法参数
		System.out.println("方法" + method.getName() + "调用结束,耗时"+ (System.currentTimeMillis() - beginTime));
		return returnValue;
	}
	
}

MyRecordInterceptor.java

public class MyRecordInterceptor implements MethodInterceptor {

	@Override
	public Object intercept(Object target, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {// 拦截所有父类方法的调用
		System.out.println(args[0] + "在"
				+ new SimpleDateFormat("yyyy-MM-dd HH-mm").format(new Date())
				+ "调用了方法" + method.getName());
		Object returnValue = proxy.invokeSuper(target, args);// target为我们方法所在的目标类,args为方法参数
		return returnValue;
	}

}

MyCallBackFilter.java

public class MyCallBackFilter implements CallbackFilter{//需要实现特定接口

	@Override
	public int accept(Method method) {
		if("method3" .equals(method.getName())){//如果被拦截的方法名满足特定条件
			//这里的序号对应于enhancer.setCallbacks(new Callback[]{new MyTimeInterceptor(),new MyRecordInterceptor()})中的Callback数组的拦截器索引
			return 1;
		}else{
			return 0;
		}
	}
	
}

OldClass.java

public class OldClass {
	public void method1() throws InterruptedException{
		System.out.println("正在处理业务逻辑1");
		Thread.sleep(100);//模拟处理业务逻辑4过程
		System.out.println("业务逻辑1处理完成");
	}
	public void method2() throws InterruptedException{
		System.out.println("正在处理业务逻辑2");
		Thread.sleep(200);//模拟处理业务逻辑2过程
		System.out.println("业务逻辑2处理完成");
	}
	public void method3(String userName) throws InterruptedException{
		System.out.println("正在处理业务逻辑3");
		Thread.sleep(300);//模拟处理业务逻辑3过程
		System.out.println("业务逻辑3处理完成");
	}
	//下面还有很多很多。。
}

MainTest.java

public class MainTest {
	public static void main(String args[]) throws InterruptedException{
		ProxyFactory cgLibProxy = new ProxyFactory();//创建我们的代理类
		//通过enhancer创建我们的子类
		//因为oldClass是我们子类的父类,所以这里向上转型成功
		OldClass oldClass = (OldClass) cgLibProxy.createSubObject(OldClass.class);
		oldClass.method1();//调用方法
		oldClass.method2();//调用方法
		oldClass.method3("cglib");//调用方法
	}
}

控制台输出:

正在处理业务逻辑1 业务逻辑1处理完成 方法method1调用结束,耗时117 正在处理业务逻辑2 业务逻辑2处理完成 方法method2调用结束,耗时200 cglib在2016-11-02 14-05调用了方法method3 正在处理业务逻辑3 业务逻辑3处理完成

上面介绍了两种方式的简单实践,在程序中自己可以发挥其锋利的作用.

附示例代码: java动态代理(jdk和cglib).tar.gz