代理模式
假如一个A类具备做甲事情的能力,我们希望它做甲事情之前或之后做多一些操作(比如记录日志),同时又不想去修改A类的结构(因为A类可能有很多其它引用,如果一改A类,所有地方都变动了)。这时候怎么办?
针对这个情况,我们可以在A类外面包多一个类(叫它B类吧),B类同样实现甲方法,B类的甲方法里调用了A类的甲方法,除此之外,B类的甲方法中还额外加多一些操作。随后,调用B类的甲方法,就等同于调用A类的甲方法,同时也执行了我们想要扩展的操作。
一个比较形象的比喻,如果A类是明星,B类就是经纪人,我们想叫明星来演出,跟经纪人说就能实现明星演出这件事,但经纪人在安排明星演出这件事中,可以做其它一些额外的事情,比如出场费商讨、安保工作或行程安排等。经纪人就是明星的代理人,这里的B类就是A类的代理类。
这就是代理模式的概念。来个代码Demo。
定义一个明星接口StarObject
public interface StarInterface { public void show(String name); }
Star实现StarInterface接口,实现一些业务逻辑操作
public class Star implements StarInterface { @Override public void show(String name) { System.out.println("我是明星" + name + ",我在演出。"); } }
实现经纪人类,即代理类
public class Broker implements StarInterface { private StarInterface star; public Broker(StarInterface star) { this.star = star; } @Override public void show(String name) { System.out.println("我是经纪人,现在去叫" + name + "来演出。"); star.show(name); System.out.println("我是经纪人," + name + "演出结束,谢谢大家。"); } }
运行代码
StarInterface star = new Broker(new Star()); star.show("Leslie Zhang");
得到结果:
我是经纪人,现在去叫Leslie Zhang来演出。 我是明星Leslie Zhang,我在演出。 我是经纪人,Leslie Zhang演出结束,谢谢大家。
这就是代理模式了。但注意,上面所述的是指静态代理模式。有静态就会有动态,那动态代理是什么?
动态代理
上面的方式对于一个类扩展出一个代理类的情况来说,是可以解决问题了。但如果之后对“明星”类要实现更多的“经纪人”类时,我们都需要再写多个代理类,这会变得麻烦和复杂。JDK为此给出了动态代理的方案。
先看Demo。
实现一个动态代理类
public class BrokerDynamic implements InvocationHandler { private Object star; public BrokerDynamic(Object star) { this.star = star; // 传入实现类。 } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("这里是经纪人在明星演出前的前置处理。"); Object result = method.invoke(star, args); // 得到并返回方法值 System.out.println("这里是经纪人在明星演出后的后置处理。"); return result; } // 值得注意的是,这个动态代理类中,对象成员、方法调用返回值类型、方法参数都是Object类型 // 这表示这个代理类能适用多种类型。 // 在使用时通过强制转换类型就可以得到指定的对象了。}
运行代码
StarInterface star = (StarInterface) Proxy.newProxyInstance(StarInterface.class.getClassLoader() ,new Class[] {StarInterface.class} , new BrokerDynamic(new Star())); star.show("Leslie Zhang");// 在调用star的show()方法时,代理类会转为去调用BrokerDynamic类invoke方法,// invoke方法的参数proxy就是发起调用的代理类star实例,// method就是指show()方法,// args就是调用show()方法时传的参数"Leslie Zhang"
得到运行结果
这里是经纪人在明星演出前的前置处理。 我是明星Leslie Zhang,我在演出。 这里是经纪人在明星演出后的后置处理。
Proxy.newProxyInstanc是JDK提供的得到一个动态代理类的静态方法。
第一个参数为类加载器,我们这里使用StarInterface接口,就直接得到它对应的类加载器;
第二个参数是指实类所实现的接口列表,我们这里只实现StarInterface接口,故只传一个;
第三个参数为一个实现了调用处理器InvocationHandler接口的实例。
Proxy.newProxyInstanc返回对应类型的代理类,因为返回值为Object类型,所以需要强转一下。
得到代理类后,调用代理类对应的方法,这一个代理类就会自动地调用InvocationHandler实例的invoke方法。这样,我们就可以在invoke里面实现一些扩展操作了。
这就是动态代理的基本概念。
也许你还有这样的问题
上面的例子倒是很动态,其它的类一传进来都可以生成它对应的代理类,但前置处理和后置处理的操作写死了,并不灵活。
InvocatioHandler中的invoke方法第一个参数proxy就是代理类实例了,在BrokerDynamic类中invoke方法里,直接调用proxy的方法不就可以了,为什么在构造方法中还要另外存一个star实例。
第一个问题确实存在一点局限,或许我们可以用两个抽象方法在BrokerDynamic类的invoke方法中分别作为前置和后置处理,之后再创建子类实类把这两个“坑”补上,便可实现前置后置处理的不同需求。
第二个问题中,形参proxy是指代理类(经纪人),即调用show方法时的代理类实例本身,不是原来的实类(明星),如果我们在invoke方法中,把Object result = method.invoke(star, args); 中的star换为proxy中进行调用,就会再执行一次invoke方法,invoke方法里又是proxy在调用show方法,会形成递归循环。所以,在BrokerDynamic里,我们还是需要另存一个原始实类(明星),在invoke方法中,调用原始实类对应的方法。
作者:Geeny
链接:https://www.jianshu.com/p/020ea154e6b3
共同学习,写下你的评论
评论加载中...
作者其他优质文章