代理模式——动态代理
设计模式之禅学习——动态代理
一、代理模式就是为其他对象提供一种代理,来控制对这个对象的访问,代理模式的好处有很多,最常见的AOP,原理就是使用了代理模式的动态代理。下面学习书中游戏者的例子。就是有一个玩游戏的人,自己不想升级,来找代理者,帮他升级的故事。
1、游戏者接口:
package com.wang.proxyPattern.example; /** * 游戏者接口 * @author HeJW * */ public interface IGamePlayer { public void login(String user, String password); public void killBoss(); public void upgrade(); }
2、真实的游戏者:
package com.wang.proxyPattern.example; /** * 游戏者 * @author HeJW * */ public class GamePlayer implements IGamePlayer { private String name=""; public GamePlayer(String name) { this.name = name; } @Override public void login(String user, String password) { System.out.println( "用户名为"+user + "的用户,"+ this.name + "登陆成功"); } @Override public void killBoss() { System.out.println(this.name + "在打怪"); } @Override public void upgrade() { System.out.println(this.name + "又升一级"); } }
3、游戏代理者:
package com.wang.proxyPattern.example; /** * 游戏代练者 * @author HeJW * */ public class GamePlayerProxy implements IGamePlayer { private IGamePlayer gamePlayer = null; public GamePlayerProxy( IGamePlayer gamePlayer ){ this.gamePlayer = gamePlayer; } @Override public void login(String user, String password) { this.gamePlayer.login(user, password); } @Override public void killBoss() { this.gamePlayer.killBoss(); } @Override public void upgrade() { this.gamePlayer.upgrade(); } }
4、常见类:
package com.wang.proxyPattern.example; import java.util.Date; public class App2 { public static void main(String[] args) { IGamePlayer player = new GamePlayer("张三"); IGamePlayer proxy = new GamePlayerProxy(player); System.out.println("开始时间是:" + new Date()); proxy.login("zhangSan", "password"); proxy.killBoss(); proxy.upgrade(); System.out.println("结束时间是:" + new Date()); } }
当然,这是最简单的一种代理模式,代理模式还包括普通代理和强制代理。
二、普通代理,普通代理就是限制客户端创建对象,必须构建一个代理对象,使用这个代理对象完成业务。
1、普通代理的游戏者
package com.wang.proxyPattern.develop.common; import com.wang.proxyPattern.example.IGamePlayer; /** * 普通代理的游戏者 * @author HeJW * */ public class GamePlayer implements IGamePlayer { private String name=""; public GamePlayer( IGamePlayer _gamePlayer, String name ) throws Exception{ if( _gamePlayer == null ){ throw new Exception("不能创建真是角色"); } else { this.name = name; } } @Override public void login(String user, String password) { System.out.println( "用户名为"+user + "的用户,"+ this.name + "登陆成功"); } @Override public void killBoss() { System.out.println(this.name + "在打怪"); } @Override public void upgrade() { System.out.println(this.name + "又升一级"); } }
2、普通代理的代理者
package com.wang.proxyPattern.develop.common; import com.wang.proxyPattern.example.IGamePlayer; /** * 普通代理的代理者 * @author HeJW * */ public class GamePlayerProxy implements IGamePlayer { private IGamePlayer gamePlayer = null; public GamePlayerProxy( String name ){ try { gamePlayer = new GamePlayer(this, name); } catch (Exception e) { e.printStackTrace(); } } @Override public void login(String user, String password) { this.gamePlayer.login(user, password); } @Override public void killBoss() { this.gamePlayer.killBoss(); } @Override public void upgrade() { this.gamePlayer.upgrade(); } }
3、客户端:
package com.wang.proxyPattern.develop.common; import java.util.Date; import com.wang.proxyPattern.example.IGamePlayer; public class App { public static void main(String[] args) { IGamePlayer proxy = new GamePlayerProxy("张三"); System.out.println("开始时间是:" + new Date()); proxy.login("zhangSan", "password"); proxy.killBoss(); proxy.upgrade(); System.out.println("结束时间是:" + new Date()); // //通过约定,禁止这么用 // try { // GamePlayer player = new GamePlayer(proxy, "张三"); // System.out.println("开始时间是:" + new Date()); // player.login("zhangSan", "password"); // player.killBoss(); // player.upgrade(); // System.out.println("结束时间是:" + new Date()); // } catch (Exception e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } }
当然了,在代码的实现中,客户端不一定非要new一个代理者对象,但是,可以通过约定,进行限制。
三、强制代理,强制代理就是必须通过真实角色找到这个真实角色的代理类,再用这个代理类完成业务。
1、强制代理接口类:
package com.wang.proxyPattern.develop.compel; /** * 强制代理的接口类 * @author HeJW * */ public interface IGamePlayer { public void login(String user, String password); public void killBoss(); public void upgrade(); //每个人都可以找到自己的代理类 public IGamePlayer getProxy(); }
2、强制代理的真实角色:
package com.wang.proxyPattern.develop.compel; /** * 强制代理的真是角色 * @author HeJW * */ public class GamePlayer implements IGamePlayer { private String name = null; //代理类 private IGamePlayer proxy = null; public GamePlayer(String name) { this.name = name; } @Override public IGamePlayer getProxy() { this.proxy = new GamePlayerProxy(this); return proxy; } @Override public void login(String user, String password) { if (this.isProxy()) { System.out.println( "用户名为"+user + "的用户,"+ this.name + "登陆成功"); } else { System.out.println("请使用指定的代理类"); } } @Override public void killBoss() { if (this.isProxy()) { System.out.println(this.name + "在打怪"); } else { System.out.println("请使用指定的代理类"); } } @Override public void upgrade() { if (this.isProxy()) { System.out.println(this.name + "又升一级"); } else { System.out.println("请使用指定的代理类"); } } private boolean isProxy(){ if( this.proxy == null ){ return false; } else { return true; } } }
3、强制代理的代理类:
package com.wang.proxyPattern.develop.compel; /** * 强制代理的代理类 * @author HeJW * */ public class GamePlayerProxy implements IGamePlayer { private IGamePlayer gamePlayer = null; public GamePlayerProxy(IGamePlayer gamePlayer) { this.gamePlayer = gamePlayer; } @Override public void login(String user, String password) { this.gamePlayer.login(user, password); } @Override public void killBoss() { this.gamePlayer.killBoss(); } @Override public void upgrade() { this.gamePlayer.upgrade(); } @Override public IGamePlayer getProxy() { return null; } }
4、在客户端必须像如下形式完成客户端:
package com.wang.proxyPattern.develop.compel; import java.util.Date; public class App3 { public static void main(String[] args) { IGamePlayer player = new GamePlayer("张三"); IGamePlayer proxy = player.getProxy(); System.out.println("开始时间是:" + new Date()); proxy.login("zhangSan", "password"); proxy.killBoss(); proxy.upgrade(); System.out.println("结束时间是:" + new Date()); } }
如果用一下两种形式的客户端,就会打印出”请使用指定代理访问“
package com.wang.proxyPattern.develop.compel; import java.util.Date; public class App1 { public static void main(String[] args) { IGamePlayer player = new GamePlayer("张三"); System.out.println("开始时间是:" + new Date()); player.login("zhangSan", "password"); player.killBoss(); player.upgrade(); System.out.println("结束时间是:" + new Date()); } }
第二种:
package com.wang.proxyPattern.develop.compel; import java.util.Date; public class App2 { public static void main(String[] args) { IGamePlayer player = new GamePlayer("张三"); IGamePlayer proxy = new GamePlayerProxy(player); System.out.println("开始时间是:" + new Date()); proxy.login("zhangSan", "password"); proxy.killBoss(); proxy.upgrade(); System.out.println("结束时间是:" + new Date()); } }
四、动态代理,动态代理之所以叫动态,是因为它在实现阶段不用关心代理谁,而在运行阶段才指定代理那个对象,要完成动态代理就要实现一个JDK提供的接口:InvocationHandler(动态代理接口)
(一下用到的代码和上面的没有关系)
1、首先先定义一个抽象主题接口:
package com.wang.proxyPattern.handler; /** * 抽象主题 * @author HeJW * */ public interface Subject { //业务操作 public void doSomething(String str); }
2、真实主题:
package com.wang.proxyPattern.handler; /** * 真实主题类 * @author HeJW * */ public class RealSubject implements Subject { @Override public void doSomething(String str) { System.out.println("do something ------" + str); } }
3、动态代理的Handler类:
package com.wang.proxyPattern.handler; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 动态代理的Handler * @author HeJW * */ public class MyInvocationHandler implements InvocationHandler { //被代理的对象 private Object target = null; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(this.target, args); } }
4、动态代理类:
package com.wang.proxyPattern.handler; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * 动态代理类 * @author HeJW * * @param <T> */ public class DynamicProxy<T> { public static <T> T newProxyInstance( ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){ if(true){ //执行一个前置通知 (new BeforAdvice()).exec(); } return (T) Proxy.newProxyInstance(loader, interfaces, h); } }
5、上面用到的通知接口:
package com.wang.proxyPattern.handler; /** * 通知接口 * @author HeJW * */ public interface IAdvice { public void exec(); }
实现:
package com.wang.proxyPattern.handler; /** * 通知的实现类 * @author HeJW * */ public class BeforAdvice implements IAdvice { @Override public void exec() { System.out.println("前置通知"); } }
6、动态代理的使用:
package com.wang.proxyPattern.handler; import java.lang.reflect.InvocationHandler; public class App1 { public static void main(String[] args) { //定义一个主题 Subject subject = new RealSubject(); //定义一个Handler InvocationHandler handler = new MyInvocationHandler(subject); //定义主题的代理 Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler); proxy.doSomething(" hello handler "); } }
7、进一步实现具体业务的动态代理:
package com.wang.proxyPattern.handler; import java.lang.reflect.InvocationHandler; /** * 具体业务的动态代理 * @author HeJW * */ @SuppressWarnings("rawtypes") 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); } }
8、客户端使用:
package com.wang.proxyPattern.handler; public class App2 { public static void main(String[] args) { Subject subject = new RealSubject(); Subject proxy = SubjectDynamicProxy.newProxyInstance(subject); proxy.doSomething(" hello "); } }
动态代理是个好东西,有很多框架都用到动态代理,我们在用代理模式的时候,也可以把这些框架直接拿来使用,很方便。还记得第一次见到动态代理的时候,那时候感觉”哇。。这都什么东东,,,这么神奇呢,,我肯定看不懂,,算了不看了,,,“ 好多次都这样,,到现在也还是不理解动态代理到底是什么,怎么实现的动态代理,但是还是硬着头皮看了,相信会慢慢理解的。。。。。