静态代理和动态代理

代理模式主要应用的场景是:当某些类由于一些原因不方便直接访问或者修改,需要通过一个代理类作为桥梁,来实现间接访问并扩展功能。

静态代理

假设我们有一个游戏模块,包含各种不同类型的游戏,我们需要在游戏开始和结束的时候加入提示,让我们看看利用静态代理怎么实现这个需求:

上面的UML图定义了一个游戏接口(Game),格斗游戏类(FightingGame)和射击游戏类(ShootingGame)分别实现了该接口,代码如下: ```java public interface Game { public void playGame(); } ``` ```java public class FightingGame implements Game { public void playGame() { System.out.println("FightingGame"); } } ``` ```java public class ShootingGame implements Game { public void playGame() { System.out.println("ShootingGame"); } } ``` 假如我们要在游戏开始前打印"Game Start",在游戏结束时打印"Game Over",为此我们定义一个代理类(GameProxy): ```java public class GameProxy implements Game {
Copy
private Game game; public GameProxy(Game game) { this.game = game; } public void playGame() { if(game!=null) { System.out.println("Game Start!"); game.playGame(); System.out.println("Game Over!"); } }

}

Copy
新建一个测试类: ```java public class MyTestClass { @Test public void demo01() { ShootingGame shootingGame = new ShootingGame(); Game game = new GameProxy(shootingGame); game.playGame(); } @Test public void demo02() { FightingGame fightingGame = new FightingGame(); Game game = new GameProxy(fightingGame); game.playGame(); } }

运行两个测试方法,将分别打印出:

Copy
Game Start! FightingGame Game Over! Game Start! ShootingGame Game Over!

动态代理

我们用另一个游戏场景来演示动态代理的使用(∩_∩)。
在LOL里有一个英雄是狂野女猎手(俗称豹女),她有人形和猎豹两种形态,每种形态下都有对应的技能,UML如图:

上面的UML图定义了人形态(Human)和猎豹形态(Leopard)两种接口,女猎手类(Huntress)同时实现了这两种接口,代码如下:

Copy
public interface Human { /** 变身为猎豹 */ public void transformIntoLeopard(); /** 向目标投掷标枪,返回伤害值 */ public int throwJavelin(String enemy); /** 在指定位置放置陷阱 */ public void setTrap(int x, int y); }
Copy
public interface Leopard { /** 变身为人形 */ public void transformIntoHuman(); /** 爪击,返回伤害值 */ public int clawAttack(); }
Copy
public class Huntress implements Human, Leopard { public void transformIntoLeopard() { System.out.println("变身为猎豹"); } public int throwJavelin(String enemy) { System.out.println("对" + enemy + "造成100点伤害"); return 100; } public void setTrap(int x, int y) { System.out.println("在(" + x + ", " + y + ")处放置了一个陷阱"); } public void transformIntoHuman() { System.out.println("变身为人形"); } public int clawAttack() { System.out.println("对前方敌人共造成200点伤害"); return 200; } }

不同于静态代理需要建立实体代理类,我们直接在测试模块用代码创建动态代理:

Copy
public class MyTestClass { @Test public void demo01() { Huntress huntress = new Huntress(); Object proxy = Proxy.newProxyInstance( huntress.getClass().getClassLoader(), Huntress.class.getInterfaces(), new HuntressInvocationHandler(huntress)); Human humanProxy = (Human)proxy; humanProxy.transformIntoLeopard(); humanProxy.setTrap(25,50); humanProxy.throwJavelin("武器大师"); Leopard leopardProxy = (Leopard)proxy; leopardProxy.transformIntoHuman(); leopardProxy.clawAttack(); } }

Proxy.newProxyInstance方法的声明如下:

Copy
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);

loader -- 指定用哪个类加载器去加载代理类
interfaces -- 代理类需要实现的接口列表,在本例中length是2
h -- 可简单理解为"方法调用处理器实例",InvocationHandler是JDK中专门用于实现动态代理的接口,它有一个invoke方法去处理代理实例上的方法调用并返回结果

下面的代码定义了一个HuntressInvocationHandler,它继承自InvocationHandler:

Copy
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; public class HuntressInvocationHandler implements InvocationHandler { public Huntress huntress; public HuntressInvocationHandler(Huntress huntress) { this.huntress = huntress; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("------------------------------------------"); System.out.println("当前方法:" + method.getName()); String argsString = Arrays.toString(args); System.out.println("当前参数:" + argsString); System.out.println("*****开始施放技能!*****"); Object result = method.invoke(huntress, args); System.out.println("*****结束施放技能!*****"); if(result!=null) { System.out.println("*****造成"+result+"点伤害*****"); } return result; } }

invoke方法的声明如下:

Copy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

proxy -- 代理类本身的一个实例。这个参数大部分情况下不需要关注。
method -- 当前调用的方法
args -- 当前调用方法的参数列表

运行测试方法,将打印下列信息:

Copy
-------------------------------------------------------- 当前方法:transformIntoLeopard 当前参数:null *****开始施放技能!***** 变身为猎豹 *****结束施放技能!***** -------------------------------------------------------- 当前方法:setTrap 当前参数:[25, 50] *****开始施放技能!***** 在(25, 50)处放置了一个陷阱 *****结束施放技能!***** -------------------------------------------------------- 当前方法:throwJavelin 当前参数:[武器大师] *****开始施放技能!***** 对武器大师造成100点伤害 *****结束施放技能!***** *****造成100点伤害***** -------------------------------------------------------- 当前方法:transformIntoHuman 当前参数:null *****开始施放技能!***** 变身为人形 *****结束施放技能!***** -------------------------------------------------------- 当前方法:clawAttack 当前参数:null *****开始施放技能!***** 对前方敌人共造成200点伤害 *****结束施放技能!***** *****造成200点伤害*****

总结

静态代理要求代理类和委托类实现同一个接口,代理类在编译期生成,效率高。相应缺点是会多出一些代理类。
动态代理不要求代理类和委托类实现同一个接口(没有实现接口的类无法使用动态代理),它是在程序运行时根据需要动态创建目标类的代理对象,但由于它是通过反射来代理方法,在性能上会有所消耗。

posted @   CoderWayne  阅读(315)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程
点击右上角即可分享
微信分享提示