1.代理模式
(1)作用
为替他对象提供一种代理,从而控制对这个对象的访问
(2)优点
在某些情况下,一个客户不想活着不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中间作用
(3)涉及角色
1)抽象角色:声明真实对象和代理对象的共同接口
2)代理角色:内部含有真实对象,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时候都能替代真实对象。同时,代理对象可以在执行真实对象操作时,附加上其他的操作,相当于对真实对象进行封装 3)真实角色:代理角色代表的真实对象,使我们最终要引用的对象
、
(4)举例
比如西门庆找潘金莲,潘金莲不好意思答复啊,咋办,找那个王婆做代理,表现在程序上是是这样的:先定义一个接口,代表某一类女人,王婆、潘金莲各自都实现
public interface KindWoman
{
//这种女人能做什么事情呢?
public void makeEyesWithMan();//抛媚眼
public void happyWithMan();//和男人那个....
}
然后定义潘金莲,真实能做这些事.......
public class PanJinLian implements KindWoman
{
@Override
public void happyWithMan()
{
System.out.println("潘金莲和男人在做那个...");
}
@Override
public void makeEyesWithMan()
{
System.out.println("潘金莲抛媚眼...");
}
}
再定义一个丑陋的王婆
/ *王婆这个人老聪明了,她太老了,是个男人都看不上她,
*但是她有智慧经验呀,他作为一类女人的代理!
*/
public class WangPo implements KindWoman
{
private KindWoman kindWoman;
public WangPo()
{
//默认的话是潘金莲的代理
this.kindWoman = new PanJinLian();
}
//她可以是KindWomam的任何一个女人的代理,只要你是这一类型
public WangPo(KindWoman kindWoman)
{
this.kindWoman = kindWoman;
}
@Override
public void happyWithMan()
{
//自己老了,干不了了,但可以叫年轻的代替。
this.kindWoman.happyWithMan();
}
@Override
public void makeEyesWithMan()
{
//王婆年纪大了,谁看她抛媚眼啊
this.kindWoman.makeEyesWithMan();
}
}
两个女主角都上场了,该男主角了,定义一个西门庆,也就是实际上的客户:
/ *水浒传是这样写的:西门庆被潘金莲用竹竿敲了一下,西门庆看痴迷了,被王婆看到了,就开始撮合两人好事,王婆作为潘金莲的代理人收了不少好处费,那我们假设一下:
*如果没有王婆在中间牵线,这两个不要脸的能成事吗?难说得很!
*/
public class XiMenQiang
{
public static void main(String[] args)
{
WangPo wangPo;
//把王婆叫出来
wangPo = new WangPo();
//然后西门庆说,我要和潘金莲Happy,然后王婆就安排了西门庆丢筷子哪出戏:
wangPo.makeEyesWithMan();
//看到没有表面是王婆在做,其实爽的是潘金莲
wangPo.happyWithMan();
}
}
这就是一个活生生的例子,通过代理人实现了某种目的。
那么再考虑一下,水浒传里还有没有这种类型的女人?有,卢俊义的老婆贾氏,那么我们也让王婆做代理(相当于拉皮条的,下面不可能只有一个女的吧)
public class JiaShi implements KindWoman
{
@Override
public void happyWithMan()
{
System.out.println("贾氏和男人在做那个...");
}
@Override
public void makeEyesWithMan()
{
System.out.println("贾氏抛媚眼...");
}
}
西门庆既勾引潘金莲又勾引贾氏
public class XiMenQiang
{
public static void main(String[] args)
{
WangPo wangPo;
//把王婆叫出来
wangPo = new WangPo();
//然后西门庆说,我要和潘金莲Happy,然后王婆就安排了西门庆丢筷子哪出戏:
wangPo.makeEyesWithMan();
//看到没有表面是王婆在做,其实爽的是潘金莲
wangPo.happyWithMan();
//西门庆勾引贾氏
JiaShi jiaShi = new JiaShi();
wangPo = new WangPo(jiaShi);
wangPo.makeEyesWithMan();
wangPo.happyWithMan();
}
}
2.JDK动态代理
(1)使用原因
上面的方式对于每个接口都需要写一个代理类,当需要使用代理类的地方很多时,就需要很多代理类,会造成类过多,使用动态代理就可以解决这个问题。比如说上面的王婆,只能为潘金莲、贾氏等这种水性杨花的女人牵线搭桥,但是对于陈世美这种想勾搭别人的男的就束手无策了,按上面的思路只能再写一个KindMen接口,让牛郎代理类和陈世美类都实现这个接口,这样就多出来一个代理类。
但是由于王婆和牛郎代理类作用完全一样,就是牵线搭桥嘛,所以这部分代码是重复的。java中提供了一个动态代理,即只用写一个代理类,指明操作,那么它就可以为所有类提供代理(在这里就是皮条客,既可以为女的拉生意,也可以为男的拉生意)
(2)例子
首先还是定义一个那种女人的接口,然后让潘金莲类实现这个接口
interface KindWomen
{
public void happyWithMan();
public void makeEyesWithMan();
}
class PanJinLian implements KindWomen
{
@Override
public void happyWithMan()
{
System.out.println("pan happy with man");
}
@Override
public void makeEyesWithMan()
{
System.out.println("pan make eyes with man");
}
}
然后定义那种男的,让陈世美类实现这个接口
interface KindMen
{
public void happyWithWomen();
public void makeEyesWithWomen();
}
class ChenShiMei implements KindMen
{
@Override
public void happyWithWomen()
{
System.out.println("chen happy with women");
}
@Override
public void makeEyesWithWomen()
{
System.out.println("chen make eyes with women");
}
}
接下来就是皮条客类了,必须实现InvocationHandler接口,在其中的invoke方法,就是我们调用真实对象的方法,并对其前后添加附加操作的地方了
class PiTiaoKe implements InvocationHandler
{
private Object obj; //注意,此处必须把类型设置为Object,这样才能为所有类做代理,而不仅仅是KindWomen
public PiTiaoKe(Object obj)
{
super();
this.obj = obj;
}
public void setObj(Object obj)
{
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
doBefore();
Object result = method.invoke(obj, args); //调用真实对象的方法
doAfter();
return result;
}
private void doAfter()
{
System.out.println("完事后,收拾床铺.....");
}
private void doBefore()
{
System.out.println("开始前,收费");
}
}
测试
@Test
public void test()
{
PanJinLian pan = new PanJinLian();
PiTiaoKe pitiao = new PiTiaoKe(pan); //设置为潘金莲代理
KindWomen women = (KindWomen) Proxy.newProxyInstance(pan.getClass().getClassLoader(),
pan.getClass().getInterfaces(), pitiao);
women.happyWithMan();
ChenShiMei chen = new ChenShiMei();
pitiao.setObj(chen); //设置为陈世美代理
KindMen men = (KindMen) Proxy.newProxyInstance(chen.getClass().getClassLoader(),
chen.getClass().getInterfaces(), pitiao);
men.happyWithWomen();
}
结果为:
开始前,收费
pan happy with man
完事后,收拾床铺.....
开始前,收费
chen happy with women
完事后,收拾床铺.....
3.Cglib实现动态代理
(1)产生原因
因为Jdk的动态代理只能为实现了接口的类生成代理对象,如果想代理没有实现接口的类,则可以使用Cglib
(2)例子
对于代理类,必须要实现MethodInterceptor接口
class PiTiaoKeCglib implements MethodInterceptor
{
private Object target; //被代理对象
public Object getInstance(Object target) //自定义方法,用来根据被代理类生成代理对象
{
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, //相当于jdk动态代理中的invoke,在里面做一些操作
MethodProxy proxy) throws Throwable
{
System.out.println("开始前");
proxy.invokeSuper(obj, args);
System.out.println("结束后");
return null;
}
}
测试:
@Test
public void test()
{
PiTiaoKeCglib cglib = new PiTiaoKeCglib(); //生成皮条客对象
PanJinLian pan = (PanJinLian) cglib.getInstance(new PanJinLian()); //生成代理对象
pan.happyWithMan();
}
结果为:
开始前
pan happy with man
结束后
4.总结
(1)静态代理模式需要为每一个被代理对象生成一个代理类,类一多,就显得相当复杂
(2)Jdk的动态代理可以只使用一个代理类为多个被代理类进行代理,但是这些被代理类必须实现了一个或者多个接口
(3)cglib方式可以为没有实现接口的类提供代理(Spring的AOP也是使用这种方式实现的)
(4)就使用方式而言,jdk方式和cglib方式只不过就是代理类换个实现的接口,生成代理对象的方式改变一下而已