动态代理

       上次总结了一下静态代理,代理的思想很容易理解,但是静态代理的能力毕竟有限。假如我有很多类都需要使用相同的代理,那么我们必须为其编写代理类,而这些代理类做的工作大都一样,在关键时刻会调用真实对象,在调用真实对象的方法前后会有所增强处理。不同之处就是调用的对象不同,那么是否有这样一个类,只需我们给它一个真实对象,给它一些必要的参数,它就能帮我们生成代理类呢。

       当然可以,JDK自带的就有,它们就是java.lang.reflect包中的InvocationHandler接口和Proxy类,这种动态代理依赖于接口。还有一种是cglib的动态代理,它不需要实现特定的接口,基于字节码技术实现。


一.JDK动态代理

还是上次的接口和真实对象:

//专业迎宾团队
public interface IHelloWorld {
	//规定了做什么
	public void sayhello();
}
//第二小队
public class HelloWorldImpl2 implements IHelloWorld {
	//同样是致欢迎   用标准普通话
	@Override
	public void sayhello() {
		System.out.println("您好 !");
	}
}

创建一个自己的调用处理器,实现InvocationHandler接口:

/**
 * @author Lucare
 * JDK动态代理
 * 2014年12月16日
 */

//自定义调用处理器
public class MyInvocationHandler implements InvocationHandler{
	//使用了Object类型   可代理的类更多  
	private Object target;
	
	public MyInvocationHandler(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("doSomething before the method");
		Object result = method.invoke(target, args);
		System.out.println("doSomething after the method");
		return result;
	}
	/**
	 * 获取代理类的实例  实际上是jdk动态帮我们生成了一个代理类  该类继承了Proxy并实现了我们传入的真实类所实现的接口
	 * 这个过程有些复杂,可参见Proxy.newProxyInstance方法源码
	 */
	public Object getProxy(){
		return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
	}

}

客户端测试:

public class Client {
	public static void main(String[] args) {
		IHelloWorld helloword = new HelloWorldImpl2();
		MyInvocationHandler mih = new MyInvocationHandler(helloword);
		IHelloWorld proxy = (IHelloWorld) mih.getProxy();
		proxy.sayhello();
	}
}
输出:

doSomething before the method
您好 !
doSomething after the method

        刚开始我也会好奇:那个接口中的invoke方法被谁调用?当我们获得了代理类的实例proxy,它实现了IHelloWord接口,必然也实现了接口中的sayhello方法,而我们并没有明显调用invoke方法,那么只可能是在sayhello方法中被调用。


        现在假如我们需要再使用代理,我们要更改的只是Client类的代码,或者我们在另一个地方直接重新来个Client,只要和前面的相关,再来一个真实类。也可以再重新定义一个接口,再来若干实现。总之代码可以最大限度得到复用,我们可以共用调用处理器前后的增强。


二.Cglib动态代理

        这个例子需要下载相关jar包。在这里我也只是简单了解了下,但是在框架中得到了很多运用,因为这种方式可以达到非侵入式的效果,你只需要写好真实类,它会帮你在合适的地方加入方法增强,如下所示。

一个真实类:

public class HelloWorld {
	public void sayhello(){
		System.out.println("Hello world!");
	}
}

一个创建代理的类:

//使用第三方cglib的动态代理,基于字节码实现,拦截实际方法
public class CglibProxy implements MethodInterceptor{
	//要代理的原始对象
	private Object obj;
	
	public Object createProxy(Object target){
		this.obj = target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(this.obj.getClass());//设置代理目标
		enhancer.setCallback(this);//设置回调
//		enhancer.setClassLoader(target.getClass().getClassLoader());
		//通过字节码技术动态创建子类实例
		return enhancer.create();
	}
	
	@Override
	public Object intercept(Object proxy, Method method, Object[] params,
			MethodProxy methodProxy) throws Throwable {
		Object result = null;
		
		System.out.println("before this method invoke");
		//调用原始对象的方法
		result = methodProxy.invokeSuper(proxy, params);
		
		System.out.println("after this method invoke");
		
		return result;
	}

}


测试客户端:

package com.fcs.cglib;

public class Client {
	public static void main(String[] args) {
		HelloWorld helloworld = new HelloWorld();
		CglibProxy cglibProxy = new CglibProxy();
		HelloWorld hw = (HelloWorld) cglibProxy.createProxy(helloworld);
		hw.sayhello();
	}
}

输出:

before this method invoke
Hello world!
after this method invoke

是不是感觉清爽了很多,是不是有了春天(spring)的感觉。任何类都可以使用这个代理了,只要它是我们需要的,我们就把真实对象传给这个代理生成类的createProxy方法,然后一切高枕无忧,生成的代理类就是真实类的子类,方法sayhello得到了重写,我们向上转型,然后调用sayhello。


不足之处敬请指正。

   

================================== 赵客缦胡缨,吴钩霜雪明。 银鞍照白马,飒沓如流星。 ==================================
原文地址:https://www.cnblogs.com/lucare/p/9312681.html