小四最近在项目中有用到设计的地方,恰巧学习设计模式也是我一直的一个计划,话不多说,直接开始。
坐标:上海浦东新区某"高档"小区门口,人物:长腿mm,帅哥小四, 丑B隔壁老王
起因:小四一眼看中一个长腿mm,但是小四并不认识她,害怕贸然上去要联系方式精到人家,所以小四很纠结,这是老王来了兴致,他似乎从我纯真的眼神中看到了我对于爱情的向往,所以拉着我说:“小四,这个美女咋样,我介绍给你?”,我一脸蒙B的看着他猥琐的脸说:“你认识她?”,好了故事开始。
想法:我自己不好亲自去要联系方式,想通过一种更加委婉的方式来达到目的。应用所学,考虑老王这个中间人,由老王代替小四去讨好mm,想到设计模式的代理模式。
代码分析:提取共同点,我和老王的共同点是送礼物,只是我买来,他送去,那么引入一个公共的送礼接口
一:静态代理
package test0901;
public interface IGiveGift {
public void giveDolls();
public void giveFollwers();
public void giveChocollate();
}
主人公小四:
package test0901;
//追求者,不认识美丽的姑娘
public class XiaoSi implements IGiveGift{
//拿到美丽的姑娘的信息
BeautifulGirl mm;
public XiaoSi(BeautifulGirl gril) {
this.mm=gril;
}
@Override
public void giveDolls() {
System.out.println(mm.getName()+"送你一个娃娃");
}
@Override
public void giveFollwers() {
System.out.println(mm.getName()+"送你一个鲜花");
}
@Override
public void giveChocollate() {
System.out.println(mm.getName()+"送你一个巧克力");
}
}
代理人老王:
package test0901;
public class LaoWang implements IGiveGift{
XiaoSi pursuit;
public LaoWang(BeautifulGirl mm) {
//这里虽然我没有出面,但是在构造代理的同时,其实也创造了我的对象,其实还是我拿到了mm的联系方式,机智如我。代理模式的一个特性
pursuit=new XiaoSi(mm);
}
//这里的方法,如果想要沿用原来的方法,那么就调用原来的,不想用的可以自己写属于自己的方法,这也是代理模式的另一个特性
@Override
public void giveDolls() {
pursuit.giveDolls();
}
@Override
public void giveFollwers() {
pursuit.giveFollwers();
}
@Override
public void giveChocollate() {
// pursuit.giveChocollate();
System.out.println("小四给的巧克力很好吃,老王我自己吃了");
}
}
执行送礼计划:
package test0901;
//代理模式,不需要被代理对象亲自出马,由代理对象来出面,例子是:小四我看重一个美女,但是我们不认识,所以委托
//我的朋友:隔壁王某来替我去送礼物以博取美女好感,王某认识美女所以可以通过构造拿到美女的信息。
public class test {
public static void main(String[] args) {
BeautifulGirl mm=new BeautifulGirl();
mm.setName("美女");
//这里,由认识美女的代理来拿到美女的联系方式,我不出面
LaoWang px=new LaoWang(mm);
px.giveFollwers();
px.giveChocollate();
}
}
结果:
这里可以看到,老王很不靠谱的把巧克力自己吃了,只把花花送给了mm,好吧怎么说也是一个进步,哈哈,你学到了么?
总结:
1.代理类一般要持有一个被代理类的对象的引用
2.对于我们不关心的方法,还是沿用被代理对象的方法,我们关注的可以重写被代理类的相应方法。
等等,还没完,通过上面的例子我们发现所有的代码都要自己写,很麻烦,而且代理类是固定的不可变的,我们需要的不是这样一个不靠谱的老王,我们想要mm身边随便的一个更亲近的人,去做代理人,那么在java中具体要如何实施呢?
二:动态代理是JDK自带的功能,它需要你去实现一个InvocationHandler接口,并且调用Proxy的静态方法去产生代理类。
小四:
package test0902;
//追求者,不认识美丽的姑娘
public class XiaoSi implements IGiveGift{
@Override
public void giveDolls() {
System.out.println("送你一个娃娃");
}
@Override
public void giveFollwers() {
System.out.println("送你一个鲜花");
}
@Override
public void giveChocollate() {
System.out.println("送你一个巧克力");
}
}
实现InvocationHandler接口的类:
package test0902;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//动态的老王?错了,现在他已经不是那个老王了,新的老王由java动态产生,并可以强转成任何类型。
//那么这个类的作用是:作为InvocationHandler接口的实现,主要用来书写业务逻辑
//而动态新生的代理满足两个条件,1,实现了被代理类的接口2.继承了Proxy类
public class LaoWang implements InvocationHandler{
BeautifulGirl mm;
public LaoWang(BeautifulGirl mm) {
this.mm=mm;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("giveDolls")) {
System.out.println(mm.getName()+":有人送你一个娃娃");
}
if (method.getName().equals("giveFollwers")) {
System.out.println(mm.getName()+":有人送你一朵鲜花");
}
if (method.getName().equals("giveChocollate")) {
System.out.println(mm.getName()+":又被老王截胡了?尼玛的!!");
}
return null;
}
}
mm类:
package test0902;
public class BeautifulGirl {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
送礼物:
package test0902;
import java.lang.reflect.Proxy;
//代理模式,这次是动态代理,
public class test {
public static void main(String[] args) throws Throwable {
BeautifulGirl mm=new BeautifulGirl();
mm.setName("小丽");
//这里,由认识美女的代理来拿到美女的联系方式,我不出面
LaoWang px=new LaoWang(mm);
//被代理的小四,静静的等待结果
XiaoSi xs=new XiaoSi();
// 获得代理类($Proxy0 extends Proxy implements Manager)的实例.这个代理类可以转化为任意的代理类
IGiveGift iGiveGiftProxy =(IGiveGift) Proxy.newProxyInstance(xs.getClass().getClassLoader(), xs.getClass()
.getInterfaces(), px);
// Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事.
// (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0类 ,它实现了IGiveGift的接口,并继承了Proxy类.
// (2)实例化$Proxy0并在构造方法中把XiaoSi传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值
// class Proxy{
// InvocationHandler h=null;
// protected Proxy(InvocationHandler h) {
// this.h = h;
// }
// ...
// }
iGiveGiftProxy.giveFollwers();
iGiveGiftProxy.giveChocollate();
iGiveGiftProxy.giveDolls();
}
}
结果:
注意事项:动态代理有一个强制性要求,就是被代理的类必须实现了某一个接口,或者本身就是接口,就像我们的Connection。
道理其实很简单,这是因为动态代理生成的代理类是继承Proxy类的,并且会实现被你传入newProxyInstance方法的所有接口,所以我们可以将生成的代理强转为任意一个代理的接口或者Proxy去使用,但是Proxy里面几乎全是静态方法,没有实例方法,所以转换成Proxy意义不大,几乎没什么用。假设我们的类没有实现任何接口,那么就意味着你只能将生成的代理类转换成Proxy,那么就算生成了,其实也没什么用,而且就算你传入了接口,可以强转,你也用不了这个没有实现你传入接口的这个类的方法。
好了,小四在这里给大家分析了静态代理和动态代理两种代理模式,平时工作中自己写代理模式的时候并不多,但是大家最好熟悉代理模式的原理,spring的aop就是应用代理模式的最好的例子。两种代理从虚拟机加载类的角度来讲,本质上都是一样的,都是在原有类的行为基础上,加入一些多出的行为,甚至完全替换原有的行为, 静态代理采用的方式就是我们手动的将这些行为换进去,然后让编译器帮我们编译,同时也就将字节码在原有类的基础上加入一些其他的东西或者替换原有的东西,产生一个新的与原有类接口相同却行为不同的类型。被代理类不多的时候最好还是用静态代理。