一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
定义
为另一个对象提供一个替身或占位符以控制对这个对象的访问。让代表对象控制某对象的访问,被代理的对象可以是远程的对象、创建开销大的对象或需要安全控制的对象。Provide a surrogate or placeholder for another object to control access to it.
也叫做委托模式,是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理模式可以提供非常好的访问控制。
代理在结构上类似装饰者,但是目的不同。装饰者为对象加上行为,而代理则是控制访问。
上图是代理模式的通用类图。
- Subject 抽象主题角色
- 抽象主题类可以是抽象类也可以是接口,是一个普通的业务类型定义
- RealSubject 具体主题角色
- 也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑 的具体执行者
- Proxy 代理主题角色
- 也叫做委托类、代理类。负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作
优点
- 职责清晰
- 真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事物,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰
- 高扩展性
- 具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,代理类完成可以在不做任何修改的情况下继续使用
- 智能化
- 动态代理
静态代理
普通代理
客户端只能访问代理角色,而不能访问真实角色。玩游戏时升级太枯燥了,找代理刷到最高级。
普通代理游戏者
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 + " 又升了一级!");
}
}
普通代理代理者
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(IGamePlayer gamePlayer) {
this.gamePlayer = gamePlayer;
}
/**
* 代理登录
* @param user
* @param password
*/
@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();
}
}
使用场景类
public class Client {
public static void main(String[] args) {
// 定义一个痴迷的玩家
IGamePlayer gamePlayer = new GamePlayer("张三");
// 开始打游戏,记下时间戳
System.out.println("开始时间:" + getTime());
// 登录
gamePlayer.login("zhangsan", "password");
// 打怪
gamePlayer.killBoss();
// 升级
gamePlayer.upgrade();
System.out.println("结束时间:" + getTime());
}
public static String getTime() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
}
}
使用了代理和直接使用真实角色的游戏结果完全一致。在该模式下,调用者只知道代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响 ,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。当然,在实际的项目中,一般都是通过约定来禁止 new 一个真实的角色,这也是一个非常好的方案。
动态代理
动态代理之所以被称为动态,是因为运行时才将它的类创建出来。代码开始执行时,还没有 proxy 类,它是根据需要从你传入的接口集创建的。
动态代理 handler
public class GamePlayInvocationHandler implements InvocationHandler {
/**
* 被代理者
*/
Class cls = null;
/**
* 被代理的实例
*/
Object obj = null;
/**
* 代理谁
* @param obj
*/
public GamePlayInvocationHandler(Object obj) {
this.obj = obj;
}
/**
* 调用被代理的方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj, args);
// 如果是登录方法,则发送信息
if ("login".equalsIgnoreCase(method.getName())) {
System.out.println("有人登录账号");
}
return result;
}
}
使用场景
public class Client {
public static void main(String[] args) {
// System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); jdk 8
// jdk 8 之后
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
// 定义一个痴迷的玩家
IGamePlayer player = new GamePlayer("张三");
// 定义一个 handler
InvocationHandler handler = new GamePlayInvocationHandler(player);
// 开始打游戏,记下时间戳
System.out.println("开始时间:" + getTime());
// 获得类的 class loader
ClassLoader classLoader = player.getClass().getClassLoader();
// 动态产生一个代理者
IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(classLoader, new Class[]{IGamePlayer.class}, handler);
// 登录
proxy.login("zhouliu", "password");
// 打怪
proxy.killBoss();
// 升级
proxy.upgrade();
// 结束
System.out.println("结束时间:" + getTime());
}
public static String getTime() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
}
}
Proxy.newProxyInstance() 新生成了一个类。在代码中加上 System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")(jdk8)或者 System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true")(jdk8后的版本)。会保存临时生成的 class 文件。 Proxy class 文件
public final class $Proxy0 extends Proxy implements IGamePlayer {
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m3;
private static Method m5;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void upgrade() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void killBoss() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void login(String var1, String var2) throws {
try {
super.h.invoke(this, m5, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("cn.bugstack.design.first.IGamePlayer").getMethod("upgrade");
m3 = Class.forName("cn.bugstack.design.first.IGamePlayer").getMethod("killBoss");
m5 = Class.forName("cn.bugstack.design.first.IGamePlayer").getMethod("login", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到新生成的类继承了 Proxy,实现了 IGamePlayer 接口,从而可以完成对游戏的代理!!!
- super.h.invoke(this, m4, (Object[])null);
- h 就是我们传入的 GamePlayIHandler