一、简介
代理模式可以为实际对象创建一个代理,在对象的方法调用时,代理模式可以在方法调用前添加前置处理(比如权限验证),在结果返回前添加结果处理(比如加密),可以添加异常处理等等。。。总之用途十分广泛,java也为此提供了支持。
二、代码示例
要求:
1、设计一个人类,初始化姓名,拥有外出和回家两个方法;
2、通过代理模式为外出方法添加前置处理:出门前检查手机钱包;
3、通过代理模式为回家方法添加后置处理:回家后结算当日开销;
通知接口(定义前置、后置方法)、人类接口(定义行为):
//人类接口
public interface IPeople {
//外出
void goOut();
//回家
void goHome();
}
//前置、后置方法接口定义
public interface IAdvice {
void before();
void after();
}
IAdvice 接口的命名是应用时常用的命名方式,可自行修改,后文中以“通知”代表该接口。另外常用的通知方法还有处理异常和处理结果等,可自行添加。
人类及通知的简单实现:
//人类实现类
public class PeopleImpl implements IPeople {
private String name;
//初始化姓名
public PeopleImpl(String name) {
this.name = name;
}
@Override
//外出方法
public void goOut() {
System.out.println(name+" 要外出了...");
}
@Override
//回家方法
public void goHome() {
System.out.println(name+" 回家了...");
}
}
//通知简单实现
public class CheckAdvice implements IAdvice {
@Override
//出门之前
public void before() {
System.out.println("before---检查手机钱包是否带齐...");
}
@Override
//回家之后
public void after() {
System.out.println("after---结算今日开销");
}
}
这里通知和行为的不同实现就是代理模式中代理行为的体现,在后续需要进行新的前后置处理、结果处理等只需要实现通知接口即可。现在有了具体实现类以及对行为的前后置处理,最后就需要将通知植入到行为中(即Handler),java内置支持动态代理,创建handler需要实现 InvocationHandler 接口。
Handler实现类
public class ScheduleHandler implements InvocationHandler {
private IAdvice advice;
private Object target;
//handler需要持有目标对象和一个通知才能实现植入
public ScheduleHandler(Object target,IAdvice advice) {
this.advice = advice;
this.target = targefat;
}
@Override
//方法执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//存储方法执行的返回值,方法无返回值则返回空
Object result = null;
//执行目标对象的外出方法前执行前置处理
if(method.getName().equals("goOut")){
advice.before();
result = method.invoke(target, args);
}
//执行目标对象的回家方法后执行后置处理
else if(method.getName().equals("goHome")){
result = method.invoke(target, args);
advice.after();
}
return result;
}
}
这里实现了通知植入及方法执行,但不同的方法名会对应一个 if…else… 从设计的角度看这显然是不合理的,假如下次增加了新的行为如吃午餐,则这里又需要添加 if…else… 语句,所以可以写一个方法执行器统一管理方法行为,虽然看起来是将 if…else… 搬到了另一个类中,但是当你有多个 Handler 需要维护时,这将节省不少工作。比如多加一个Handler,他将在开销结算后对结果进行处理:藏一部分私房钱(原Handler需要保留,毕竟不是每一个人都藏私房钱),那我们只要在方法执行器中添加新的方法处理即可,而不用修改两个Handler。
场景模拟:
public class ProxyTest {
public static void main(String[] args) {
//创建Tom
IPeople tom = new PeopleImpl("Tom");
//准备创建代理需要的参数
Class<?> clazz = tom.getClass();
Class<?>[] interfaces = clazz.getInterfaces();
ScheduleHandler hander = new ScheduleHandler(tom, new CheckAdvice());
//创建代理对象(注意:第一个参数是类加载器)
IPeople tomProxy = (IPeople)Proxy.newProxyInstance(clazz.getClassLoader(),interfaces,hander);
//结果打印
System.out.println("-------------------------");
tomProxy.goOut();
System.out.println("-------------------------");
tomProxy.goHome();
}
}
通常代理创建的过程也可以封装成一个函数,可以自己完成。
结果打印:
-------------------------
before---检查手机钱包是否带齐...
Tom 要外出了...
-------------------------
Tom 回家了...
after---结算今日开销
小结:
代理模式的用途十分广泛,如Spring中的AOP等。另外代理模式与装饰模式十分相像,他们都能改变对象的行为,但装饰者模式更强调增加对象的职责与行为,即主要实现装饰者自由切换添加,而被装饰者不变;代理模式则强调添加每个行为的控制,增加固定的前置、后置处理等,即处理的对象不断变化,但处理方式不变。