代理设计模式是开发当中用的最多的设计模式之一,比如Spring的AOP就是用动态代理实现的。这篇博客由浅到深,先讲解静态代理。然后通过静态代理的不足引出动态代理,在介绍动态代理的同时介绍java反射的用法。
我觉得关系图是讲解设计模式最好的方式,所以先上图,如下图:
如上图:在代理类的内部有一个被代理类的引用,代理类和被代理类同时实现相同的接口。然后代理类使用被代理类对象的方法与其他对象进行交互,让我们看起来就是代理对象在和其他对象交互。接着通过一个简单的例子来说明静态代理。例子取自<大话设计模式>
先描述一下这个例子的场景,有个男生A想追求一个女生娇娇,但是男生比较怂,不敢自己去送花,于是他让他的朋友(男生B)去帮忙送花。从代码的角度怎么实现呢?
首先创建一个女生的接口,具体的细节不用关心
public interface Girl {
//女生该有的功能
}
接着创建我们的女主角-娇娇
public class JiaoJiao implements Girl{
//娇娇的细节
}
接着是想追娇娇的男生A
/**
* 被代理者
* @author shuyweng
*
*/
public class BoyA implements ChasingGirl{
@Override
public void giveFlowers(Girl girl) {
System.out.println("---------给娇娇送花");
}
}
接着是帮男生A追娇娇的男生B
/**
* 代理
* @author shuyweng
*
*/
public class BoyB implements ChasingGirl{
//内部含有BoyA的引用
private ChasingGirl boyA = new BoyA();
@Override
public void giveFlowers(Girl girl) {
//通过调用boyA的送花方法来实现自己的送花方式
boyA.giveFlowers(girl);
}
}
客户端代码
public class Client {
public static void main(String[] args) {
//帮男生A送花的男生B
ChasingGirl boyB = new BoyB();
Girl jiaojiao = new JiaoJiao();
boyB.giveFlowers(jiaojiao);
}
}
我们可以看到在客户端的代码中并没有出现boyA,这也是代理的一个优点之一:屏蔽了被代理者的细节。这个例子仅仅是说明了静态代理的用法,并没有说明其优势。静态代理最大的优势是可以在被代理者执行方法的前后加一些额外的操作。
举个例子:我们想在送花之前先送水(场景乱举的。。。),那么该怎么办?为了遵循开闭原则,我们不能修改giveFlowers()这个方法,这时候静态代理就派上用场了。修改代码如下:
/**
* 代理
* @author shuyweng
*
*/
public class BoyB implements ChasingGirl{
private ChasingGirl boyA = new BoyA();
@Override
public void giveFlowers(Girl girl) {
//额外执行的送花操作
giveWater();
boyA.giveFlowers(girl);
}
/**
* 想在送花前执行的送水操作
*/
private void giveWater() {
System.out.println("-------------送水");
}
}
现在静态代理的应用场景就一目了然了,举个例子:我们一开始写了个登录程序。在一段时间后,我们想在登录中添加一个记录登录耗时的功能,就可以通过动态代理实现。(可以考虑这是如何实现的?)再回到我们追女生的例子中来,如果在整个校园场景中,要为每一个男生送花前都加一个送水的动作,我们是否需要为每一个男生都写一个动态代理。举个现实开发中的场景,我们想在所有的方法前后,加一个日志功能我们是否要对每个类都写个代理呢?由此我们引出今天的主角动态代理!现在我们想在所有人送花方法前后都加一个日志操作。修改代码如下:
先创建一个日志处理器,定义了方法前后的要进行的操作
/**
* 日志处理器(动态代理的参数)
* @author shuyweng
*
*/
public class LoggerInterceptor implements InvocationHandler {
private Object object;//目标对象引用,也可以设计成某一类对象的公用接口,用Object增强了通用性
public LoggerInterceptor(Object object){
this.object = object;
}
//通过反射的方式来执行传入对象的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//在执行方法前打印一些额外信息
System.out.println("before method:对象类:"+object.getClass()+"---对象方法:"+method.getName()+"----参数:"+args[0]);
//通过反射的方式调用目标对象的方法
Object res = method.invoke(object, args);
//执行方法后再打印一些信息
System.out.println("after method:" + res.toString());
return res;
}
}
再使用Java库提供的代理类,代码如下:
//通过动态代理的方式给送花动作前后进行日志操作
public static void main(String[] args) {
ChasingGirl boyB = new BoyB();
ChasingGirl proxy = (ChasingGirl) Proxy.newProxyInstance(
boyB.getClass().getClassLoader(),
boyB.getClass().getInterfaces(),new LoggerInterceptor(boyB));
proxy.giveFlowers(new JiaoJiao());
}
结果如下图:
这里出现空指针异常了,原因是因为日志处理器中如下这段代码:
Object res = method.invoke(object, args);
System.out.println("after method:" + res.toString());
因为送花方法并没有返回值,所以出现空指针了。修改代码如下:
Object res = method.invoke(object, args);
if (res != null) {
System.out.println("after method:" + res.toString());
}
再执行一次代码,结果如下:
这样就完全没有错误了,动态代理主要利用了反射可以通过类名,方法名执行方法的特性实现了动态在方法前后添加动作的功能。接下来会写一篇博客好好讲一下java反射!
代理的例子说明了什么,哈哈,追妹子应该自己来。不要用代理!!