模拟场景:在现实中经常会有游戏代理玩家的存在,什么是游戏代理玩家呢?也就说说一些游戏玩家觉得自己没有时间去为自己的游戏角色练级,那么就可以把自己的账号密码交给代理玩家,让代理玩家帮你完成这些操作。在这里会很清晰的发现,虽然在玩的是代理玩家,但是实际中真实的操作对象,就游戏的角色还是你自己的账号。只是把你自己原本的一些工作交给了代理玩家。
那么转换为类文件的话,就是至少要有一个ProxyGamePlayer和一个RealGamePlayer,两个对象的操作时一模一样的,只是在ProxyGamePlayer中代理了RealGamePlayer的动作。既然两者有着一模一样的操作,那么就可以抽象出一个抽象类或者一个接口,让两个对象都继承抽象类或者接口。这里使用接口IGamePlayer。
IGamePlayer
1 package com.zqz.dp.proxy; 2 /** 3 * @author Qin GamePlayer的通用接口,用来定义游戏玩家的操作,具体实现交给子类 4 */ 5 public interface IGamePlayer { 6 public void login(String name, String password); // 登录操作 7 public void kissBoss(); // 打怪 8 public void upgrade(); // 升级 9 }
RealGamePlayer
1 package com.zqz.dp.proxy; 2 /** 3 * @author Qin GamePlayer的真实实现类,定义一系列的动作 4 */ 5 public class RealGamePlayer implements IGamePlayer { 6 private String username; // 定义一个用户的名字,登录的时候便于确认 7 public RealGamePlayer(String username) { // 构造方法赋值 8 this.username = username; 9 } 10 @Override 11 public void login(String name, String password) { 12 System.out.println(name + "登录成功,欢迎" + this.username + "进入游戏。。。"); 13 } 14 @Override 15 public void kissBoss() { 16 System.out.println(this.username + "开始打怪。。。"); 17 } 18 @Override 19 public void upgrade() { 20 System.out.println(this.username + "升级了。。。"); 21 } 22 }
ProxyGamePlayer
1 package com.zqz.dp.proxy; 2 /** 3 * @author Qin GamePlayer的代理玩家,在代理类中不进行实际的操作,主要是调用真实实现类的对象去完成操作 4 */ 5 public class ProxyGamePlayer implements IGamePlayer { 6 private IGamePlayer gamePlayer; // 定义一个游戏玩家,主要是用来传递真实的gamePlayer的实例 7 public ProxyGamePlayer(IGamePlayer gamePlayer) { // 构造方法赋值 8 this.gamePlayer = gamePlayer; 9 } 10 @Override 11 public void login(String name, String password) { 12 this.gamePlayer.login(name, password); // 调用传递进来的真实实现类的具体方法 13 } 14 @Override 15 public void kissBoss() { 16 this.gamePlayer.kissBoss();// 调用传递进来的真实实现类的具体方法 17 } 18 @Override 19 public void upgrade() { 20 this.gamePlayer.upgrade();// 调用传递进来的真实实现类的具体方法 21 } 22 }
Client
1 package com.zqz.dp.proxy; 2 /** 3 * @author Qin 场景类,进行关系的设置与实现 4 */ 5 public class Client { 6 public static void main(String[] args) { 7 IGamePlayer realGamePlayer = new RealGamePlayer("张三"); // 实例化一个真实的gamePlayer 8 IGamePlayer proxyGamePlayer = new ProxyGamePlayer(realGamePlayer); // 实例化一个代理对象,并指定被代理的对象 9 proxyGamePlayer.login("user", "password"); //代理类调用具体方法--登录 10 proxyGamePlayer.kissBoss(); //代理类调用具体方法--打怪 11 proxyGamePlayer.upgrade(); //代理类调用具体方法--升级 12 } 13 }
以上就完成了代理(proxy)的具体的实现,很多人会开始矛盾了,这是在干什么啊?明明可以直接实例化一个ReadGamePlayer,然后调用具体的方法不就可以了,这不是典型的脱裤子放屁吗?
如果单单的只考虑上面的操作的话,那确实有几分多此一举的感觉,但是如果考虑到加一个特性的话,这种代理模式确是很吃香的。
比如我现在需要记录下我今天开始登陆玩游戏到角色升级花了多久的话。如果不用代理模式的话,不管你是添加一个新类还是在Client端中添加static方法,是不是都要在main方法调用具体的操作前后添加一些逻辑。这确实可以实现,但需求是时刻在改变的,如果我现在要求我不记录时间了,在玩游戏之前我要记录一些日志。我要先记录时间再记录日志,或者先记录日志再记录时间。。。
这样的要求是很正常的,难道我们要时刻去改变main中的逻辑顺序吗?这显然是不合适的。记住一点,在开发中,main中的代码越少越好,每次添加功能或者维护的时候,main方法最后不需要动或者只需要进行简单的修改。当然要视情况而定。
而且在现实中,是要根据实际情况来判断是否需要使用代理的,不可能说每天代练玩家说要帮你代练你就同意吧,比如今天没钱,心情不好,所以就没钱雇代练玩家进行代练了,所以也就是说,在使用代理时要对是否进行对象的访问进行控制。
如果使用代理模式的话,只需要在ProxyGamePlayer中添加两个方法before和after,再用一个boolean值proxyFlag来模拟对对象的访问进行控制。进行具体的操作,然后在调用具体的逻辑操作前后执行before和after即可。
修改的ProxyGamePlayer
1 package com.zqz.dp.proxy; 2 import java.util.Random; 3 /** 4 * @author Qin 5 * GamePlayer的代理玩家,在代理类中不进行实际的操作,主要是调用真实实现类的对象去完成操作 6 */ 7 public class ProxyGamePlayer implements IGamePlayer { 8 private IGamePlayer gamePlayer; // 定义一个游戏玩家,主要是用来传递真实的gamePlayer的实例 9 public ProxyGamePlayer(IGamePlayer gamePlayer) { // 构造方法赋值 10 this.gamePlayer = gamePlayer; 11 } 12 private Random random = new Random(); // 产生随机对象 13 private boolean flag = random.nextBoolean(); // 产生一个随机值,true or false 14 private boolean proxyFlag=false; //是否需要进行代练,由上面的变量flag决定 15 @Override 16 public void login(String name, String password) { 17 if (flag) { // 用一个变量控制对对象的访问 18 proxyFlag=true; //如果需要代练,则修改状态并进行登录操作 19 this.befor(); // 登录前记录时间。 20 this.gamePlayer.login(name, password); // 调用传递进来的真实实现类的具体方法 21 } else { 22 System.out.println("今天心情不好,不需要代练了。。。"); 23 } 24 } 25 @Override 26 public void kissBoss() { 27 if (proxyFlag) {// 判断今天是否需要代练 28 this.gamePlayer.kissBoss();// 调用传递进来的真实实现类的具体方法 29 } 30 } 31 @Override 32 public void upgrade() { 33 if (proxyFlag) {// 判断今天是否需要代练 34 this.gamePlayer.upgrade();// 调用传递进来的真实实现类的具体方法 35 this.after(); // 升级结束之后记录时间 36 } 37 } 38 /** 39 * 添加一些事务或者时间记录的操作 这里有before和after 40 */ 41 public void befor() { 42 System.out.println("login time..."); 43 } 44 public void after() { 45 System.out.println("upgrade time..."); 46 } 47 }
这个时候只添加逻辑,Client并不需要进行任何的修改。
而如果这个时候添加比较多的逻辑的时候,也不用担心会修改大量的代码,因为代理类可以继续代理代理类,也就是说如果我要先记录日志再记录时间的话,只需要再实现一个proxy类,不过该代理类代理的是记录时间的代理类。所以最好执行的结果会先执行日志,在记录时间,最后再执行具体的操作。当然这个时候就要修改Client端了。不管也就是几行代码的问题,只需要再实例化一个代理类,然后设置好关系即可。
以上的操作其实就是AOP(aspect-oriented-programming)面向切面编程。确切的说AOP是采用的是动态代理的机制。所谓的切面就是指贯彻到系统中各个模块中的一个功能。面向切面就是指把系统的一个方面的功能封装成对象,嵌入到原来的系统模块中。在ProxyGamePlayer中before和after两个方法就是以切面的方式嵌入到系统中。AOP主要是来做一些日志、权限、事务等等的处理。可以说AOP是代理模式的一种体现。
代理模式的定义如下:为其他对象提供一种代理以控制对该对象的访问。代理模式也称为委托模式。一般的代理模式存在如下的三个角色:
1、 抽象主角色类Subject:也可以是接口,定义系列的方法,让子类实现。
2、 真实主题角色RealSubject:被委托类,也就说具体执行的是该类的操作。
3、 代理主题角色ProxySubject:委托类,负责对真实觉得的调用,应用AOP的话可以在此添加操作,如日志、事务等等。
代理模式的优点:
1、 职责清晰:真实的具体类只实现具体的操作,不关心代理类中添加的操作。
2、 拓展性高:真实的实现类可以进行拓展,不关联到其他的类,只要求实现抽象主题类。
代理模式的使用场景:
如Spring AOP,不过Spring AOP应用的是动态代理。需要在类执行前后加入操作的可以考虑代理模式。
代理模式的拓展:
代理模式有很多的拓展,不过大部分拓展都是在原来静态代理的情况下进行了细微的修改而已,这里只会简单的提一下。最重要的拓展还是动态代理,在框架中,我们使用的Spring框架的AOP就是采用的动态代理的机制。
1、 普通代理
普通代理就是说我们要知道代理类的存在,针对上面的例子也就是说要知道ProxyGamePlayer的存在,在调用的时候我们不需要指定真实类的任何操作,包括进行实例化的操作。简单的说就是客户端要求只能访问代理类,而不能访问真实的具体类。这要的操作改动其实很小,只是改了下RealGamePlayer的构造方法,指明一个IGamePlayer对象,表示是被那个类代理。然后在代理类进行实例化的时候直接指明为谁进行代理的操作。
修改的RealGamePlayer
1 package com.zqz.dp.simpleproxy; 2 /** 3 * @author Qin GamePlayer的真实实现类,定义一系列的动作 4 */ 5 public class RealGamePlayer implements IGamePlayer { 6 private String username; // 定义一个用户的名字,登录的时候便于确认 7 public RealGamePlayer(IGamePlayer gamePlayer,String username) { // 构造方法赋值,指明委托的对象 8 if(gamePlayer==null){ 9 try { 10 throw new Exception("没有进行委托,无法操作..."); 11 } catch (Exception e) { 12 e.printStackTrace(); 13 } 14 }else{ //说明了委托的对象,进行赋值 15 this.username = username; 16 } 17 } 18 @Override 19 public void login(String name, String password) { 20 System.out.println(name + "登录成功,欢迎〖" + this.username + "〗进入游戏。。。"); 21 } 22 @Override 23 public void kissBoss() { 24 System.out.println(this.username + "开始打怪。。。"); 25 } 26 @Override 27 public void upgrade() { 28 System.out.println(this.username + "升级了。。。"); 29 } 30 }
修改的ProxyGamePlayer
1 package com.zqz.dp.simpleproxy; 2 /** 3 * @author Qin GamePlayer的代理玩家,在代理类中不进行实际的操作,主要是调用真实实现类的对象去完成操作 4 */ 5 public class ProxyGamePlayer implements IGamePlayer { 6 private IGamePlayer gamePlayer; // 定义一个游戏玩家,主要是用来传递真实的gamePlayer的实例 7 public ProxyGamePlayer(String name) { // 构造方法赋值 8 this.gamePlayer = new RealGamePlayer(this,name); 9 } 10 @Override 11 public void login(String name, String password) { 12 this.befor(); //登录前记录时间。 13 this.gamePlayer.login(name, password); // 调用传递进来的真实实现类的具体方法 14 } 15 @Override 16 public void kissBoss() { 17 this.gamePlayer.kissBoss();// 调用传递进来的真实实现类的具体方法 18 } 19 @Override 20 public void upgrade() { 21 this.gamePlayer.upgrade();// 调用传递进来的真实实现类的具体方法 22 this.after(); //升级结束之后记录时间 23 } 24 /** 25 * 添加一些事务或者时间记录的操作 26 * 这里有before和after 27 */ 28 public void befor(){ 29 System.out.println("login time..."); 30 } 31 public void after(){ 32 System.out.println("upgrade time..."); 33 } 34 }
至于Client就不再需要实例化RealGamePlayer了,因为我们已经在ProxyGamePlayer中指明好了被代理的对象了,所以只需要实例化代理类然后进行操作即可。
2、 强制代理
在我们之前的操作中,都是由代理类找到真实实现类然后进行操作。而在强制代理中,是逆过来的,指明真实实现类一定要被代理,所以由真实实现类找到代理类然后进行系类的操作。
以之前的静态代理为模板进行修改,因为我们调用的是父类实例指向子类对象,所以得到代理的操作要放在父类中,由子类去实现,所以在IGamePlayer中添加一个getProxy()的方法,RealGamePlayer进行实现的操作,ProxyGamePlayer可以进行空实现,或者返回自己的实例。下面只列出部分代码
修改的RealGamePlayer
1 package com.zqz.dp.enforceproxy; 2 /** 3 * @author Qin GamePlayer的真实实现类,定义一系列的动作 4 */ 5 public class RealGamePlayer implements IGamePlayer { 6 private String username; // 定义一个用户的名字,登录的时候便于确认 7 private IGamePlayer gamePlayer=null; //定义一个代理类,表示委托的对象,父类实例指向子类 8 private boolean proxy=false; //表示是否设置了代理的操作 9 public IGamePlayer getProxy(){ 10 this.gamePlayer=new ProxyGamePlayer(this); //指明代理对象 11 this.setProxy(true); //表示进行了代理的操作 12 return this.gamePlayer; 13 } 14 protected void setProxy(boolean proxy) { 15 this.proxy = proxy; 16 } 17 protected boolean isProxy() { //getter方法取得 18 return proxy; 19 } 20 public RealGamePlayer(String username) { // 构造方法赋值 21 this.username = username; 22 } 23 @Override 24 public void login(String name, String password) { 25 if(this.isProxy()){ 26 System.out.println(name + "登录成功,欢迎〖" + this.username + "〗进入游戏。。。"); 27 }else{ 28 System.out.println("没有指明代理对象..."); 29 } 30 } 31 @Override 32 public void kissBoss() { 33 if(this.isProxy()){ 34 System.out.println(this.username + "开始打怪。。。"); 35 }else{ 36 System.out.println("没有指明代理对象..."); 37 } 38 } 39 @Override 40 public void upgrade() { 41 if(this.isProxy()){ 42 System.out.println(this.username + "升级了。。。"); 43 }else{ 44 System.out.println("没有指明代理对象..."); 45 } 46 } 47 }
修改的Client
1 package com.zqz.dp.enforceproxy; 2 /** 3 * @author Qin 场景类,进行关系的设置与实现 4 */ 5 public class Client { 6 public static void main(String[] args) { 7 IGamePlayer realGamePlayer = new RealGamePlayer("张三"); // 实例化一个真实的gamePlayer 8 IGamePlayer proxyGamePlayer = realGamePlayer.getProxy(); // 实例化一个代理对象,并指定被代理的对象 9 proxyGamePlayer.login("user", "password"); //代理类调用具体方法--登录 10 proxyGamePlayer.kissBoss(); //代理类调用具体方法--打怪 11 proxyGamePlayer.upgrade(); //代理类调用具体方法--升级 12 } 13 }
3、 虚拟代理
其实虚拟代理可说可不说,因为没什么可说的,所谓的虚拟代理就是委托类在代理真实类进行操作的时候先判断下真实实现类是否已经被实例化了,没有的话再进行实例化。也就是在需要的时候才进行初始化。
修改的ProxyGamePlayer
1 package com.zqz.dp.proxy; 2 /** 3 * @author Qin GamePlayer的代理玩家,在代理类中不进行实际的操作,主要是调用真实实现类的对象去完成操作 4 */ 5 public class ProxyGamePlayer implements IGamePlayer { 6 private IGamePlayer gamePlayer; // 定义一个游戏玩家,主要是用来传递真实的gamePlayer的实例 7 @Override 8 public void login(String name, String password) { 9 this.befor(); //登录前记录时间。 10 if(this.gamePlayer==null){ //判断真实实现类是否被初始化 11 this.gamePlayer=new RealGamePlayer("张三"); 12 } 13 this.gamePlayer.login(name, password); // 调用传递进来的真实实现类的具体方法 14 } 15 @Override 16 public void kissBoss() { 17 if(this.gamePlayer==null){ //判断真实实现类是否被初始化 18 this.gamePlayer=new RealGamePlayer("张三"); 19 } 20 this.gamePlayer.kissBoss();// 调用传递进来的真实实现类的具体方法 21 } 22 @Override 23 public void upgrade() { 24 if(this.gamePlayer==null){ //判断真实实现类是否被初始化 25 this.gamePlayer=new RealGamePlayer("张三"); 26 } 27 this.gamePlayer.upgrade();// 调用传递进来的真实实现类的具体方法 28 this.after(); //升级结束之后记录时间 29 } 30 /** 31 * 添加一些事务或者时间记录的操作 32 * 这里有before和after 33 */ 34 public void befor(){ 35 System.out.println("login time..."); 36 } 37 public void after(){ 38 System.out.println("upgrade time..."); 39 } 40 }
这样在Client端就不再需要进行真实实现类了,只需要实例化一个代理类即可。
4、 动态代理(重点)
前面介绍的都是静态代理,只是为了抛砖引玉,下面要讲的是代理的重头戏,也就说动态代理。学习过Spring AOP的读者应该能清楚什么是所谓的动态代理。说白了,AOP只是动态代理的一种体现。
在Java JDK中,对动态代理提供了支持,主要就是InvocationHandler接口和Proxy类。详情请自己查看jdk文档。
InvocationHandler是jdk提供的动态代理接口,对被代理类的方法进行代理。
Object invoke(Object proxy, Method method, Object[] args ) throws Throwable |
Proxy主要是调用newInstance方法进行操作
通过传递对象的类加载器、类实现的接口、和InvocationHandler接口实例进行代理的操作。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException |
在invocationHandler接口中,invoke是一定要实现的方法。动态代理是根据被代理对象的接口生成全部所有的方法,也就是说给定一个接口,动态代理会表示已经将接口中的全部方法实现了。在Proxy类中,我们传递了类加载器,类的接口和invocationHandler实例,最终所有被代理对象被代理的方法都是交给invocationHandler接管的。
MyInvocationHandler
1 package com.zqz.dp.dynamicproxy; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Method; 4 /** 5 * @author Qin invocationHandler对象,通过实现InvocationHandler接口,可以在调用方法前后添加自己的逻辑 6 */ 7 public class MyInvocationHandler implements InvocationHandler { 8 private Object target; // 被代理对象 9 public MyInvocationHandler(Object target) { // 构造方法赋值 10 this.target = target; 11 } 12 @Override 13 public Object invoke(Object proxy, Method method, Object[] args) 14 throws Throwable { 15 Object result = method.invoke(target, args); // 进行方法的调用,得到返回的结果 16 return result; 17 } 18 }
这样就不需要代理对象了。在Client端调用jdk提供的Proxy进行操作即可。
修改的Client
1 package com.zqz.dp.dynamicproxy; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Proxy; 4 /** 5 * @author Qin 场景类,进行关系的设置与实现 6 */ 7 public class Client { 8 public static void main(String[] args) { 9 IGamePlayer realGamePlayer = new RealGamePlayer("张三"); // 实例化一个真实的gamePlayer 10 InvocationHandler myInvocationHandler = new MyInvocationHandler( 11 realGamePlayer); // 实例化invocationHandler对象,传递被代理对象 12 IGamePlayer gamePlayer = (IGamePlayer) Proxy.newProxyInstance( 13 realGamePlayer.getClass().getClassLoader(), realGamePlayer 14 .getClass().getInterfaces(), myInvocationHandler); // 通过动态代理取得对象 15 gamePlayer.login("user", "password"); // 代理类调用具体方法--登录 16 gamePlayer.kissBoss(); // 代理类调用具体方法--打怪 17 gamePlayer.upgrade(); // 代理类调用具体方法--升级 18 } 19 }
以上就完成了动态代理的操作。或许这个时候大家还不能看出什么端倪来。现在我们添加一个需求,要求用户在登录之后发出一条反馈消息。如果用静态代理的话就是实现IGamePlayer接口并在调用方法前面添加逻辑,而不需要添加逻辑的直接调用方法即可。之前已做过实例。现在用动态代理进行修改
修改的MyInvocationHandler
1 package com.zqz.dp.dynamicproxy; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Method; 4 /** 5 * @author Qin invocationHandler对象,通过实现InvocationHandler接口,可以在调用方法前后添加自己的逻辑 6 */ 7 public class MyInvocationHandler implements InvocationHandler { 8 private Object target; // 被代理对象 9 public MyInvocationHandler(Object target) { // 构造方法赋值 10 this.target = target; 11 } 12 @Override 13 public Object invoke(Object proxy, Method method, Object[] args) 14 throws Throwable { 15 Object result = method.invoke(target, args); // 进行方法的调用,得到返回的结果 16 if("login".equalsIgnoreCase(method.getName())){ 17 System.out.println("============我是代理玩家,登录了游戏============"); 18 } 19 return result; 20 } 21 }
现在我只是添加了上面的粗体的那几行代码,就完成了添加逻辑的功能。现在想想,用动态代理可以只对自己需要的方法进行添加逻辑,而其他方法不进行操作。而是用静态代理的话,因为实现了接口,所以即使不用添加逻辑,还是要进行实现。这就是动态代理的好处。
现在确实是实现了功能,但是上面的操作是在调用方法前后进行逻辑的添加,而如果我需要在程序启动的时候就添加逻辑呢?这个时候可以抽象出一个类DynamicProxy
DynamicProxy
1 package com.zqz.dp.dynamicproxy; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Proxy; 4 public class DynamicProxy { 5 @SuppressWarnings("unchecked") 6 public static<T> T newProxyInstance(ClassLoader classLoader,Class<?>[] interfaces,InvocationHandler h){ 7 if(true){ //判断操作,按实际情况选择 8 System.out.println("添加逻辑的操作"); 9 } 10 return (T) Proxy.newProxyInstance(classLoader, interfaces, h); 11 } 12 }
这样在Client端就要调用DynamicProxy中的具体方法了,其实会很清楚的发现,这个类并没有添加新的操作,最终调用的还是Proxy类,而且使用了泛型,这样在Client就不用进行强制转换了。
现在动态代理模式基本就讲完了。不知道大家是否会觉得有点别扭,因为在Client的main方法中,操作貌似有点太多了。包括得到类加载器ClassLoader、类的接口Interfaces、InvocationHandler实例。而且这些操作都是只需要一个Object类实例。这样使得main方法看起来比较乱,也不容易维护。所以这样我们可以抽象出一个类,这个类负责加载这些东西。因为说白了,抽象出来的类还是要调用DynamicProxy的newProxyInstance方法,所以可以从这个类继承。
RealDynamicProxy
1 package com.zqz.dp.dynamicproxy; 2 /** 3 * @author Qin 4 * 抽象出来的真实代理类,主要是获取类加载器、类的接口、invocationHandler实例 5 * 为了维护方便,只是重载父类的方法 6 */ 7 public class RealDynamicProxy extends DynamicProxy { 8 public static<T> T newProxyInstance(Object target){ //传递被代理对象 9 ClassLoader classLoader=target.getClass().getClassLoader(); //取得类加载器 10 Class<?>[] interfaces=target.getClass().getInterfaces(); //取得类的接口 11 MyInvocationHandler myInvocationHandler=new MyInvocationHandler(target); //实例化InvocationHandler 12 return newProxyInstance(classLoader,interfaces,myInvocationHandler); //调用父类的方法 13 } 14 }
这样Client就不再需要进行大量操作,只需要调用RealDynamicProxy具体方法即可。以后main方法的修改也会降低很多。
Client
1 package com.zqz.dp.dynamicproxy; 2 /** 3 * @author Qin 场景类,进行关系的设置与实现 4 */ 5 public class Client { 6 public static void main(String[] args) { 7 IGamePlayer realGamePlayer = new RealGamePlayer("张三"); // 实例化一个真实的gamePlayer 8 IGamePlayer gamePlayer = RealDynamicProxy 9 .newProxyInstance(realGamePlayer); // 通过动态代理取得对象 10 gamePlayer.login("user", "password"); // 代理类调用具体方法--登录 11 gamePlayer.kissBoss(); // 代理类调用具体方法--打怪 12 gamePlayer.upgrade(); // 代理类调用具体方法--升级 13 } 14 }
其实要注意,即使不抽象出RealDynamicProxy类也是可以完成操作的,只是这样main方法中的操作就会麻烦点。