设计模式之禅学习——动态代理
一、代理模式就是为其他对象提供一种代理,来控制对这个对象的访问,代理模式的好处有很多,最常见的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 ");
}
}
动态代理是个好东西,有很多框架都用到动态代理,我们在用代理模式的时候,也可以把这些框架直接拿来使用,很方便。还记得第一次见到动态代理的时候,那时候感觉”哇。。这都什么东东,,,这么神奇呢,,我肯定看不懂,,算了不看了,,,“ 好多次都这样,,到现在也还是不理解动态代理到底是什么,怎么实现的动态代理,但是还是硬着头皮看了,相信会慢慢理解的。。。。。