最近开发过程中遇到一些问题,为了可以好好的解决,研究了几天的动态代理。记下来,怕以后忘记了。
动态代理适用的情景:
某个类的某个方法已经做好了,你在不想或者不能改动源代码的情况下,要做一些额外的操作,这时候就可以使用代理设计模式。
例如,周杰伦要举行新唱片的的签名发布会,他总不能自己去布置会场吧。所以他就请了他的经理人Kim去帮忙布置场地罗,这个Kim就是属于代理的角色了。他要在Joy签名会之前去布置场地,签名会之后清理场地。
下面我们来看看JDK代理怎么做的。
1.首先Joy同学需要先声明自己有一个签名会,也就是interface。然后再去开这个签名会。
public interface Joy {
public void holdSignature();
}
public class JoyImpl implements Joy{
public void holdSignature() {
System.out.println("joy say:大家好,我是Joy,今天我在这里举行签名会哦...");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//这是专门帮人家布置场地的JDK代理
public class JDKProxy implements InvocationHandler{
//要代理的对象,也即是帮哪个明星做场地啦,今天可能是Joy,明天可能就是SHE了
private Object star;
public JDKProxy(Object star) {
this.star = star;
}
public Object getProxySatr(){
return Proxy.newProxyInstance(
star.getClass().getClassLoader(),
star.getClass().getInterfaces(),
this);
}
public Object invoke(Object obj, Method method, Object[] params)
throws Throwable {
System.out.println("jdk say:签名会开始之前,我要帮忙布置场地...");
//明星开始他的签名会了
Object result = method.invoke(star, params);
System.out.println("jdk say:签名会结束了,我要清理场地。");
return result;
}
}
3.双方都准备就绪了,我们现在开始签合同工作了。
public static void main(String[] args) {
//jdkProxy代理接到order是帮joy布置场地呢。
JDKProxy jdkProxy = new JDKProxy(new JoyImpl());
//请出代理的明星,让他签名去咯
Joy joy = (Joy) jdkProxy.getProxySatr();
joy.holdSignature();
}
可以看到最后的结果是:
jdk say:签名会开始之前,我要帮忙布置场地...
joy say:大家好,我是Joy,今天我在这里举行签名会哦...
jdk say:签名会结束了,我要清理场地。
大家可以发现,Joy开演唱会之前还有做一次声明,多么让人不爽快的interface啊。所以SHE开签名会的时候就换了一个叫cglib的代理。
下面来看cglib代理是怎么做的。
1.首先,SHE的演唱会还是要自己去开的。
public class SHE {
public void musicShow() {
System.out.println("she say:大家好,我们是SHE,下面为大家带来《星光》....");
}
}
2.然后我们的Cglib其实是一家公司来的员工了,他可以为好多的明星布置会场的。成立这个比较好使代理公司,总的多投入点成本吧。
引入asm-3-3-1.jar和cglib-2.2.jar
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor{
public Enhancer enhancer = new Enhancer();
public<T> Object getCglibProxy(Class<T> star){
//指定这次要伺候的明星
enhancer.setSuperclass(star);
//要做什么呢?就是下面的intercept里面干的事
enhancer.setCallback(this);
//合约签订,把你的暂时仆人送过去服务你了。
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("cglib say:我在布置演唱会的场地.....");
//boss可以开演唱会了
Object result = proxy.invokeSuper(obj, args);
System.out.println("cglib say:演唱会结束了,我在清理场地.....");
return result;
}
}
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
SHE she = (SHE) cglibProxy.getCglibProxy(SHE.class);
she.musicShow();
}
4.最后你会发现,演唱会灰常的成功啦。
cglib say:我在布置演唱会的场地.....
she say:大家好,我们是SHE,下面为大家带来《星光》....
cglib say:演唱会结束了,我在清理场地.....
对比JDK代理和Cglib代理可以发现,JDK多了一个接口。
为什么JDK代理只能针对接口呢?
JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类,有兴趣的自己去查源代码哈。
而Cglib则采用ASM工具,直接修改字节码。所以不用接口规范。