代理模式
看过请留个言,转载请注明出处,尊重作者劳动成果,谢谢!
在日常生活中,会遇到各种各样的中介机构,比如猎头公司,律师事务所,婚姻介绍所,房产公司等。在这些单位工作的人员均可称为代理人。代理人的共同特征是可以代替委托人去和第三方通信。譬如:律师代替委托人打官司,猎头代替委托人物色人才,红娘代替委托人寻找对象,房产代理人代替委托人出租房屋。代理人可以在第三方和委托人之间转发或过滤消息,但是不能取代委托人的任务。譬如你要找女朋友,委托你一要好的朋友去帮你物色,结果被他给物色了。这就不叫代理。
代理模式的作用是:为某个对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
我们来看个例子:
package com.wepull.proxy;
/**
* @author leno
*抽象角色
*/
public interface IHello {
String greeting(String who);
}
package com.wepull.proxy;
/**
* @author leno
*真实角色
*/
public class HelloImpl implements IHello {
public String greeting(String who) {
System.out.println("greeting method is invoked.....");
return "hello,"+who;
}
}
试想一下,如果这时候我们要对问候的业务逻辑方法进行日志记录。我们当然可以这样做:
package com.wepull.proxy;
/**
* @author leno
*真实角色
*/
public class HelloImpl implements IHello {
private Logger logger = Logger.getLogger(this.getClass().getName());
public void log(String message)
{
logger.log(Level.INFO, message);
}
public String greeting(String who) {
log( "starting...");
System.out.println("greeting method is invoked.....");
log("stopping...");
return "hello,"+who;
}
可问题来了,项目经理发话,为了不给现有系统造成干扰,不容许修改现存的类的实现。怎么办?这时候我们就要就想到代理模式了。代理模式分为两种:静态代理和动态代理,我们先来看一下静态代理类:
package com.wepull.proxy;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author leno
*静态代理角色
*/
public class HelloLoggerProxy implements IHello {
private Logger logger = Logger.getLogger(this.getClass().getName());
private IHello helloImpl;
public HelloLoggerProxy(IHello helloImpl) {
super();
this.helloImpl = helloImpl;
}
public String greeting(String who) {
log( "starting...");
String hello = helloImpl.greeting(who);
log("stopping...");
return hello;
}
public void log(String message)
{
logger.log(Level.INFO, message);
}
}
客户端测试代码:
package com.wepull.proxy;
public class TestStaticProxy {
public static void main(String[] args) {
IHello hello = new HelloLoggerProxy(new HelloImpl());
hello.greeting("leno");
}
}
由以上代码可以看出,客户实际需要调用的是HelloImpl类的greeting()方法,现在用HelloLoggerProxy来代理HelloImpl类,同样达到目的,同时还在方法调用的前后都做了日志记录。这就是静态代理。
但是,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;而且,如果项目中要做日志的类有100个,每个类里面有100个方法。重复代码就太可怕了。有人说,不怕,我就一愚公。好,有天项目经理突然要求修改做日志的方式,你再看看这些方法,估计撞墙的心都有了。那怎么办?我们就改用动态代理。
JDK1.3以后提供了动态代理的支持,程序员通过实现java.lang.reflect.InvocationHandler接口提供一个执行处理器,然后通过java.lang.reflect.Proxy得到一个代理对象,通过这个代理对象来执行商业方法,在商业方法被调用的同时,执行处理器会被自动调用。再来见识一下动态代理类:
package com.wepull.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author leno
*动态代理角色
*/
public class DynamicLoggerProxy implements InvocationHandler {
private Logger logger = Logger.getLogger(this.getClass().getName());
private Object delegate;
@SuppressWarnings("unchecked")
public Object bind(Object delegate)
{
this.delegate = delegate;
Class cls = delegate.getClass();
return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), this);
}
public Object invoke(Object o, Method method, Object[] args)
throws Throwable {
log( "starting...");
Object obj = method.invoke(delegate, args);
log("stopping...");
return null;
}
public void log(String message)
{
logger.log(Level.INFO, message);
}
}
客户端测试代码:
package com.wepull.proxy;
public class TestDynamicProxy {
public static void main(String[] args) {
IHello hello = (IHello) new DynamicLoggerProxy().bind(new HelloImpl());
hello.greeting("leno");
}
}
大家看到,上面使用了一个动态代理类就代替了无数个静态代理类。一劳永逸。这里的Dynamic Proxy我们稍微解释一下(具体大家可以查阅JDK API),它是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
好了,代理模式就介绍到这里,请继续关注我们的独孤九剑系列,祝您学习愉快。