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