Proxy Pattern 代理模式,也叫委托模式 是一个使用率非常高的模式,非常典型的场景就是游戏代练,代练者就是一个代理者或者委托者。其定义如下:
Provide a surrogate or placeholder for another object to control access to it
主要是三个角色:
1,Subject抽象主题角色,可以是抽象类也可以是接口
2,RealSubject具体主题角色,也叫被委托角色、被代理角色,他才是真正的业务逻辑具体执行者
3,Proxy代理主题角色,也叫委托类、代理类,负责对真实角色的应用
通用源代码:
/**
* 抽象主题类
*
* @author XiongNeng
* @version 1.0
* @since 13-5-23
*/
public interface Subject {
// 定义一个方法
void request();
}
一个实现类:
public class RealSubject implements Subject {
@Override
public void request() {
// 核心业务逻辑
}
}
代理类:
public class Proxy implements Subject {
private Subject subject = null;
public Proxy() {
this.subject = new RealSubject();
}
public Proxy(Subject subject) {
this.subject = subject;
}
@Override
public void request() {
before();
this.subject.request();
after();
}
// 预处理
private void before() {
System.out.println("before");
}
// 后处理
private void after() {
System.out.println("after");
}
}
代理模式的优点:
1,职责清晰
真实的角色就是实现业务逻辑,不用关心其他非本职责事务,通过后期代理完成一件事务,附带结果就是编程简介清晰
2,高扩展性
具体主题随时会发生变化,只要它实现了接口,甭管它如何变化,都逃不出接口手掌,那么代理类完全可以不做任何修改下使用
3,智能化
动态代理你会看到的,Spring AOP是典型和经典的动态代理模式
代理是有个性的
一个类可以实现多个接口,完成不同的整合。也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上做增强,这种增强的本质通常是对目标对象的方法进行拦截和过滤。例如游戏代理是要收费的,升级一级要5元钱,这个计算功能就是代理类的个性,它应该在代理的接口中定义。
public interface IProxy {
// 计算费用
public void count();
}
public class GamePlayerProxy implements IGamePlayer, IProxy {
private IGamePlayer gamePlayer = null;
// 通过构造函数传递对谁进行代练
public GamePlayerProxy(IGamePlayer _gamePlayer) {
this.gamePlayer = _gamePlayer;
}
// 代练杀怪
public void killBoss() {
this.gamePlayer.killBoss();
}
// ....省略其他方法
// 代练升级
public void upgrade() {
this.gamePlayer.upgrade();
count();
}
// 计算费用,这个是代理类的提供的功能
public void count() {
System.out.println("升级总费用是:200元");
}
}
代理类可以为真实角色预处理消息、过滤消息、消息转发、事后处理消息等功能。当然一个代理类,可以代理多个真实角色,并且真实角色之间可以有耦合关系。
>>> 动态代理:
最后是我们的压轴大戏动态代理
动态代理在实现阶段不用关系代理谁,而是在运行阶段才指定代理哪一个对象。
InvocationHandler是JDK提供的动态代理接口,对被代理的方法进行代理。
public class GamePlayerIH implements InvocationHandler {
// 被代理者
Class cls = null;
// 被代理的实例
Object obj = null;
// 我要代理谁
public GamePlayerIH(Object _obj) {
this.obj = _obj;
}
// 调用被代理的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj, args);
return result;
}
}
通过InvocationHandler接口,所有的方法都由Handler来进行处理,invoke方法为必须实现的方法。
下面是一个简单的场景使用:
public class Client {
public static void main(String[] args) {
// 定义一个痴迷玩家
IGamePlayer player = new GamePlayer("张三");
// 定义一个Handler
InvocationHandler handler = new GamePlayerIH(player);
// 开始打游戏
System.out.println("开始时间是: 2013-05-23 12:34");
// 获得类的class loader
ClassLoader cl = player.getClass().getClassloader();
// 动态产生一个代理者
IGamePlayer proxy = (IGamePlayer)proxy.newProxyInstance(cl, new Class[] {IGamePlayer.class}, handler);
// 登录
proxy.login("zhangsan", "password");
// 开始杀怪
proxy.killBoss();
// 升级
proxy.upgrade();
// 记录结束时间
System.out.println("结束时间是:2013-05-24 11:10");
}
}
别急,动态代理最强大的地方是注入,如果上面的游戏玩家每次登陆后需要发一个信息给我吗,防止账号被盗,直接修改GamePlayer不是个好方法。
public class GamePlayerIH implements InvocationHandler {
// 被代理者
Class cls = null;
// 被代理的实例
Object obj = null;
// 我要代理谁
public GamePlayerIH(Object _obj) {
this.obj = _obj;
}
// 调用被代理的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj, args);
// 如果是登录方法,则发送信息
if (method.getName().equalsIgnoreCase("login")) {
System.out.println("账号登录了");
}
return result;
}
}
AOP编程没有使用什么新的技术,但是它对我们的设计、编码有非常大的影响,对于日志、事务、权限等都可以在系统设计阶段不同考虑,而在设计后通过AOP的方式切过去。
于是我们知道,一个类的动态代理类是这样一个类,由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有方法的实现。
一个简单的AOP框架,可以在此基础上进行扩展:
抽象主题类Subject:
/**
* 抽象主题类
*
* @author XiongNeng
* @version 1.0
* @since 13-5-23
*/
public interface Subject {
// 定义一个方法
void request();
}
真实主题:
/**
* 真实主题
*
* @author XiongNeng
* @version 1.0
* @since 13-5-23
*/
public class RealSubject implements Subject {
@Override
public void request() {
// 核心业务逻辑
}
}
动态代理的Handler类:
public class MyInvocationHandler implements InvocationHandler {
// 被代理的实例
Object obj = null;
// 我要代理谁
public GamePlayerIH(Object _obj) {
this.obj = _obj;
}
// 调用被代理的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj, args);
return result;
}
}
动态代理类:
public class DynamicProxy<T> {
public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
// 寻找JoinPoint连接点,AOP框架使用元数据定义
if (true) {
// 执行一个前置通知
(new BeforeAdvice()).exec();
}
// 执行目标,并返回结果
return (T) Proxy.newProxyInstance(loader, interfaces, h);
}
}
上面的DynamicProxy只是一个通用的类,不具有业务含义,我们可以实现一个具有业务含义的动态代理类
public class SubjectDynamicProxy extends DynamicProxy {
public static <T> T newProxyInstance(Subject subject) {
// 获得Classloader
Classloader loader = subject.getClass().getClassLoader();
// 获得接口数组
Class<?>[] classes = subject.getClass().getInterfaces();
// 获得handler
InvocationHandler handler = new MyInvocationHandler(subject);
return newProxyInstance(loader, classes, handler);
}
}
这样一来,使用起来就非常简单了。
最佳实践:
代理模式使用非常广泛,大到系统框架、企业平台,小到代码片段、事务处理,稍不留意就用到代理模式。
有了Spring AOP 和 AspectJ这样优秀的工具,就更不用讲了。
AOP框架中,有几个概率一定要弄清楚:
切面 Aspect
切入点 JoinPoint
通知 Advice
织入 Weave
这几个概率请参考我的另一篇文章:
http://yidao620c.iteye.com/blog/1837689
本人博客已搬家,新地址为:http://yidao620c.github.io/