`
youyu4
  • 浏览: 440409 次
社区版块
存档分类
最新评论

代理模式 -- 保护代理与Java反射 -- 转

 
阅读更多

本笔记内容:

1. Java动态代理,反射机制

2. 保护代理

3. 应用保护代理实现的约会系统

----------------------------------------------------------------------------

 

 

走进保护模式

 

我们知道,相亲节目已经在天朝的电视上已经火了多年了,说明了有广大同胞需要这个配对服务,更别说咱们程序猿这个群体了。

现在要实现一个约会服务系统。你希望在这个服务系统中,增加一个评分功能,类似于豆瓣上的评分系统。

浏览一个人资料后,可以给对方进行评分。

你希望这套系统能帮助顾客找到一个理想的对象,这也会让事情更有趣。

 

 

很快,你应该就能想到服务系统需要一个PersonBean的接口,来设置或取得一个人的信息: 

 

[java] view plain copy
 
  1. //这个接口可以设置和取得人的名字,性别,兴趣,和评分  
  2. public interface PersonBean {  
  3.    
  4.     String getName();        // 获取名字  
  5.     String getGender();      // 过去性别  
  6.     String getInterests();   // 获取兴趣  
  7.     int getHotOrNotRating(); // 获取评分值  
  8.    
  9.     void setName(String name);           // 设置姓名  
  10.     void setGender(String gender);       // 设置性别  
  11.     void setInterests(String interests); // 设置兴趣  
  12.     void setHotOrNotRating(int rating);  // 设置评分  
  13.    
  14. }  

 

 

然后实现这个接口:

 

[java] view plain copy
 
  1. public class PersonBeanImpl implements PersonBean {  
  2.     String name;  
  3.     String gender;  
  4.     String interests;  
  5.     int rating;  
  6.     int ratingCount = 0;  
  7.     
  8.     public String getName() {  
  9.         return name;      
  10.     }   
  11.     
  12.     public String getGender() {  
  13.         return gender;  
  14.     }  
  15.     
  16.     public String getInterests() {  
  17.         return interests;  
  18.     }  
  19.      
  20.     public int getHotOrNotRating() {  
  21.         if (ratingCount == 0return 0;  
  22.         return (rating/ratingCount);  
  23.     }  
  24.     
  25.    
  26.     public void setName(String name) {  
  27.         this.name = name;  
  28.     }  
  29.    
  30.     public void setGender(String gender) {  
  31.         this.gender = gender;  
  32.     }   
  33.     
  34.     public void setInterests(String interests) {  
  35.         this.interests = interests;  
  36.     }   
  37.     
  38.     public void setHotOrNotRating(int rating) {  
  39.         this.rating += rating;    
  40.         ratingCount++;  
  41.     }  
  42. }  



想想如果系统直接使用这个类会有什么问题?

 

如果真的直接用这个类,而不考虑其他题,那么估计很快就会大量用户抱怨:我的兴趣竟然被别人篡改了!!有人竟然给自己评高分!!太不公平了!!

没错,在这个系统中,应该不允许用户篡改别人的数据,如果直接按照上面的类定义,任何客户都可以调用任何方法了。

 

在这个例子中,使用保护代理是最合适不过了。

 

什么是保护代理?是提供某些级别的保护,根据客户的权限和决定客户可否访问哪些特定的方法,所以保护代理可能只提供给客户部分接口。 

 

比如在我们的约会系统中,我们之希望顾客可以设置自己的信息,同时又防止他人更改这些信息。而HotOrNot评分则相反,你不可以更改自己的评分,但是他人可以设置你的评分。

 

 

Java动态代理

 

Java在java.lang.reflect(反射)包中有自己的代理支持,利用这个包你可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类。因为实际的代理类是在运行时创建的,所以我们称这个Java技术为:动态代理

 

 

Invocation的工作原理是响应代理的任何调用,可以看成是代理收到方法调用后,请求做实际工作的对象。

我们要用Java的动态代理创建这个约会系统的保护代理。

下面来看怎样用Java动态代理实现保护模式。

 

实现保护代理的约会系统

顾客不可以改变自己的HotOrNot评分,也不可以改变其他顾客的个人信息。为了达到这个目标,必须要创建两个代理:一个用来访问你自己的PersonBean对象,另一个用来访问另一个顾客的PersonBean对象。这样,代理就可以控制在每一种情况下允许哪一种请求了。

 

创建这个代理,就必须使用Java API的动态代理。Java会为我们创建两个代理,而我们只需要提供handler来处理代理转来的方法。

 

步骤一:创建两个InvocationHandler

InvocationHandler实现了代理的行为,Java负责创建真实代理类和对象。我们只需要提供在方法调用发生时知道做什么的handler。我们需要写两个InvocationHandler(调用处理器),其中一个给拥有者使用,另一个给非拥有者使用。究竟什么是InvocationHandler呢?可以这样想:当代理方法被调用时,代理就会把这个调用转发给InvocationHandler,但是这并不是通过调用InvocationHandler的对应方法做到的。

 

给拥有者使用的InvocationHandler:

 

[java] view plain copy
 
  1. // 拥有者的InvocationHandler  
  2.   
  3. // InvocationHandler在java.lan.reflect包中  
  4. import java.lang.reflect.*;  
  5.    
  6.   
  7. // 所有调用处理器都实现InvocationHandler接口  
  8. public class OwnerInvocationHandler implements InvocationHandler {   
  9.     PersonBean person;  
  10.    
  11.     // 将person传入构造器,并保持引用  
  12.     public OwnerInvocationHandler(PersonBean person) {  
  13.         this.person = person;  
  14.     }  
  15.    
  16.     // 每次代理的方法被调用,就会导致代理调用此invoke方法  
  17.     public Object invoke(Object proxy, Method method, Object[] args)   
  18.             throws IllegalAccessException {  
  19.     
  20.         try {  
  21.             if (method.getName().startsWith("get")) {  
  22.                 // 如果方法是一个getter,就允许调用  
  23.                 return method.invoke(person, args);  
  24.   
  25.             } else if (method.getName().equals("setHotOrNotRating")) {  
  26.                 // 否则,如果方法是HotOrNotRating(), 因为不能给自己打分,所以就抛出异常,表示不允许  
  27.                 throw new IllegalAccessException();  
  28.   
  29.             } else if (method.getName().startsWith("set")) {  
  30.                 // 如果是setter,可以设置自己的信息,所以就给调用  
  31.                 return method.invoke(person, args);  
  32.   
  33.             }   
  34.         } catch (InvocationTargetException e) {  
  35.             e.printStackTrace();  
  36.         }   
  37.         return null//其他方法都是未定义的,返回null  
  38.     }  
  39. }  

 

给非拥有者使用的InvocationHandler:

 

 

[java] view plain copy
 
  1. import java.lang.reflect.*;  
  2.    
  3. public class NonOwnerInvocationHandler implements InvocationHandler {   
  4.     PersonBean person;  
  5.    
  6.     public NonOwnerInvocationHandler(PersonBean person) {  
  7.         this.person = person;  
  8.     }  
  9.    
  10.     public Object invoke(Object proxy, Method method, Object[] args)   
  11.             throws IllegalAccessException {  
  12.     
  13.         try {  
  14.             if (method.getName().startsWith("get")) {  
  15.                 // 可以查看其他人的信息  
  16.                 return method.invoke(person, args);  
  17.   
  18.             } else if (method.getName().equals("setHotOrNotRating")) {  
  19.                 // 可以给其他人评分  
  20.                 return method.invoke(person, args);  
  21.   
  22.             } else if (method.getName().startsWith("set")) {  
  23.                 // 不可以设置别人的信息,所以返回异常  
  24.                 throw new IllegalAccessException();  
  25.   
  26.             }   
  27.         } catch (InvocationTargetException e) {  
  28.             e.printStackTrace();  
  29.         }   
  30.         return null;  
  31.     }  
  32. }  



 

步骤二:创建Proxy类并实例化Proxy对象

现在,只剩下创建动态Proxy类,并实例化Proxy对象了。让我们开始编写一个以PersonBean为参数,并知道如何为PersonBean对象创建拥有者代理的方法,也就是说,我们要创建一个代理,将它的方法调用转发给 OwnerInvocationHandler。

 

下面的方法是调用拥有者的方法

 

[java] view plain copy
 
  1.    // 此方法需要一个person对象作为参数,然后返回该对象的代理  
  2.    // 因为代理和主题有相同的接口,所以我们返回接口PersonBean  
  3. PersonBean getOwnerProxy(PersonBean person) {  
  4.           
  5.        // 此代码创建了代理(代码有点丑)  
  6.        // 我们利用Proxy类的静态newProxyInstance方法创建代理对象(Java反射机制)  
  7.        return (PersonBean) Proxy.newProxyInstance(   
  8.             person.getClass().getClassLoader(),  // 将personBean的类载入器当作参数  
  9.             person.getClass().getInterfaces(),   // 代理需要实现的接口  
  10.                new OwnerInvocationHandler(person)); // 调用非拥有者的处理器  
  11. }  

 

下面方法获取非拥有者的方法

 

[java] view plain copy
 
  1. PersonBean getNonOwnerProxy(PersonBean person) {  
  2.       
  3.        return (PersonBean) Proxy.newProxyInstance(  
  4.             person.getClass().getClassLoader(),     
  5.             person.getClass().getInterfaces(),     
  6.                new NonOwnerInvocationHandler(person));  
  7. }  




 

 

步骤三:测试配对服务系统。利用适当的代理包装任何PersonBean对象。

 

[java] view plain copy
 
  1. import java.lang.reflect.*;  
  2. import java.util.*;  
  3.   
  4. public class MatchMakingTestDrive {  
  5.     // 实例变量, 当作是保存顾客的“数据库”  
  6.     Hashtable datingDB = new Hashtable();  
  7.       
  8.     public static void main(String[] args) {  
  9.         MatchMakingTestDrive test = new MatchMakingTestDrive();  
  10.         test.drive();  
  11.     }  
  12.    
  13.     public MatchMakingTestDrive() {  
  14.         // 在构造器中初始化数据库  
  15.         initializeDatabase();  
  16.     }  
  17.   
  18.     public void drive() {  
  19.         PersonBean joe = getPersonFromDatabase("Joe Javabean");  //从数据库中取出一个人  
  20.         PersonBean ownerProxy = getOwnerProxy(joe); // 创建这个人的拥有者代理  
  21.         System.out.println("Name is " + ownerProxy.getName()); //  输出这个人的名字  
  22.         ownerProxy.setInterests("bowling, Go");  // 使用拥有者代理来设置自己的兴趣  
  23.         System.out.println("Interests set from owner proxy");  
  24.         try {  
  25.             // 尝试用拥有者代理来给自己评分  
  26.             ownerProxy.setHotOrNotRating(10);  
  27.         } catch (Exception e) {  
  28.             // 如果给自己评分会出错  
  29.             System.out.println("Can't set rating from owner proxy");  
  30.         }  
  31.         System.out.println("Rating is " + ownerProxy.getHotOrNotRating());  
  32.   
  33.         // 创建一个非拥有者的代理  
  34.         PersonBean nonOwnerProxy = getNonOwnerProxy(joe);  
  35.         System.out.println("Name is " + nonOwnerProxy.getName());  
  36.         try {  
  37.             // 尝试用非拥有者代理来设置兴趣  
  38.             nonOwnerProxy.setInterests("bowling, Go");  
  39.         } catch (Exception e) {  
  40.             // 不可以给别人设置兴趣  
  41.             System.out.println("Can't set interests from non owner proxy");  
  42.         }  
  43.         // 可以给别人评分  
  44.         nonOwnerProxy.setHotOrNotRating(3);  
  45.         System.out.println("Rating set from non owner proxy");  
  46.         System.out.println("Rating is " + nonOwnerProxy.getHotOrNotRating());  
  47.     }  
  48.   
  49.     // 此方法需要一个person对象作为参数,然后返回该对象的代理  
  50.     // 因为代理和主题有相同的接口,所以我们返回接口PersonBean  
  51.     PersonBean getOwnerProxy(PersonBean person) {  
  52.         // 此代码创建了代理(代码有点丑)  
  53.         // 我们利用Proxy类的静态newProxyInstance方法创建代理对象(Java反射机制)  
  54.         return (PersonBean) Proxy.newProxyInstance(   
  55.                 person.getClass().getClassLoader(),  // 将personBean的类载入器当作参数  
  56.                 person.getClass().getInterfaces(),   // 代理需要实现的接口  
  57.                 new OwnerInvocationHandler(person)); // 调用非拥有者的处理器  
  58.     }  
  59.   
  60.     PersonBean getNonOwnerProxy(PersonBean person) {  
  61.           
  62.         return (PersonBean) Proxy.newProxyInstance(  
  63.                 person.getClass().getClassLoader(),     
  64.                 person.getClass().getInterfaces(),     
  65.                 new NonOwnerInvocationHandler(person));  
  66.     }  
  67.   
  68.     PersonBean getPersonFromDatabase(String name) {  
  69.         return (PersonBean)datingDB.get(name);  
  70.     }  
  71.   
  72.     // 初始化“数据库”  
  73.     void initializeDatabase() {  
  74.         PersonBean joe = new PersonBeanImpl();  
  75.         joe.setName("Joe Javabean");  
  76.         joe.setInterests("cars, computers, music");  
  77.         joe.setHotOrNotRating(7);  
  78.         datingDB.put(joe.getName(), joe);  
  79.   
  80.         PersonBean kelly = new PersonBeanImpl();  
  81.         kelly.setName("Kelly Klosure");  
  82.         kelly.setInterests("ebay, movies, music");  
  83.         kelly.setHotOrNotRating(6);  
  84.         datingDB.put(kelly.getName(), kelly);  
  85.     }  
  86. }  

 

输出:

 

 

 

小结:

动态代理之所以称为动态代理,是因为运行时才将它的类创建出来。代码开始时,还没有proxy类, 它是根据需要从你传入的接口集创建的。

InvocationHandler不是一个proxy,它只是一个帮助proxy的类,proxy会把调用转发给它处理.Proxy本身是利用静态的Proxy.newProxyInstance()方法在运行时动态创建的。

 

 

代理模式一共记了三篇的笔记:远程代理,虚拟代理,和保护代理。代理模式的还远远不止这些:

 

防火墙代理(Firewall Proxy)

控制网络资源的访问,保护主题免于“坏客户”的侵害。常用在公司的防火墙系统

 

智能引用代理(Smart Reference Proxy):

当主题被引用时,进行额外的动作,例如计算一个对象被引用的次数

 

缓存代理(Caching Proxy):

为开销大的运算结果提供暂时存储:它也可以允许多个客户共享结果,以减少计算或网络延迟。常用在Web服务器代理,以及内容管理与出版系统。

 

同步代理(Synchronizationi Proxy): 

在多线程的情况下为主题提供安全的访问。常用在JavaSpaces,为分散式环境内的潜在对象集合提供同步访问控制。

 

复杂隐藏代理(Complexity Hiding Proxy):

用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也称为外观代理(Facade Proxy),这不难了解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口.

 

写入时复制代理(Copy-On-Write Proxy):

用来控制对象的复制,方法是延迟对象的复制,知道客户真的需要为止。这是虚拟代理的变体。

 

 

原文:http://blog.csdn.net/shuangde800/article/details/10381495

分享到:
评论

相关推荐

    JAVA设计模式--程序设计--反射--注解--泛型

    Java设计模式、程序设计、反射、注解和泛型是Java开发中的核心概念,它们各自在不同的场景下发挥着重要作用,构建出高效、可维护的软件系统。 首先,Java设计模式是面向对象编程中的一种最佳实践,是解决常见问题的...

    Java设计模式-代理模式

    代理模式在Java中有两种常见的实现方式:静态代理和动态代理。静态代理是通过程序员显式创建代理类来实现的,而动态代理则利用Java的反射API在运行时动态创建代理对象。 1. **静态代理**:在静态代理中,我们需要为...

    Java代理模式Java动态代理

    ### Java代理模式与Java动态代理详解 #### 一、代理模式概述 代理模式是一种软件设计模式,它在客户端和目标对象之间提供了一种间接层。这种模式的主要目的是控制客户端对目标对象的访问,并且可以在不修改原有...

    java常用设计模式-代理模式

    Java 设计模式 - 代理模式 代理模式(Proxy Pattern)是一种常用的设计模式,提供了间接访问目标对象的一种方式,即通过代理对象访问目标对象。这样做的好处是,可以在不改变原有目标对象的基础上,对目标对象增加...

    JAVA的反射机制与动态代理

    Java的反射机制与动态代理是Java编程中两个非常重要的高级特性,它们在许多实际场景中发挥着关键作用,如框架开发、插件系统、元数据处理等。下面将详细讲解这两个概念及其应用。 首先,Java的反射机制允许我们在...

    代理模式-静态动态代理-jdk动态代理-cglib动态代理

    在Java中,代理模式有多种实现方式,包括静态代理、JDK动态代理和CGLIB动态代理。 **静态代理** 静态代理是最早也是最基础的代理实现方式。在静态代理中,我们需要创建一个代理类,这个代理类与原始类(被代理类)...

    java中的三种代理模式

    JDK动态代理基于Java反射API,可以在运行时动态地生成代理类和实例。它需要目标对象实现至少一个接口,然后通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来创建代理对象。...

    JAVA设计模式(代理模式)

    了解并熟练掌握代理模式,对于提高Java开发中的设计质量与可维护性具有重要意义。在实践中,应根据具体需求选择合适的代理实现方式,以达到最佳的设计效果。通过学习和实践,开发者可以更好地理解和运用这一模式,...

    JAVA反射与代理

    在"JAVA反射与代理"这个主题中,我们将深入探讨这两个核心概念。 首先,让我们了解反射的基本用法。当一个类的名称在运行时才被知道,反射可以帮助我们动态地创建对象。例如,我们可以使用`Class.forName()`方法...

    Java反射与设计模式

    本文详尽地论述了Java反射,工厂模式,动态代理模式

    java模式设计--代理模式

    代理模式的核心在于,它提供了一个与真实主题(即原始对象)相同的接口,使得客户端可以透明地使用代理对象,而无需知道其背后是代理还是真实的对象。 在Java中,代理模式主要分为静态代理和动态代理两种类型。静态...

    利用Java的反射与代理实现IOC模式

    在这个主题中,我们将深入探讨Java反射和动态代理如何帮助我们实现IOC。 首先,让我们理解Java反射。反射允许程序在运行时检查类、接口、字段和方法的信息,并且能够动态地创建对象和调用方法。在IOC中,反射用于在...

    JAVA 反射机制与动态代理ppt

    总的来说,Java反射机制和动态代理是Java平台强大而灵活的特性,它们使得Java程序能够在运行时具有更高的灵活性和可扩展性。然而,使用反射也可能带来性能开销和安全风险,因此在实际应用中需要权衡利弊,合理使用。

    反射实现 AOP 动态代理模式(Spring AOP 的实现 原理) - Java 例子 -

    本文将深入探讨Spring AOP的实现原理,以及如何使用反射来实现动态代理模式。 首先,我们需要了解AOP的基本概念。AOP的核心思想是切面,它包含两个主要部分:切点(Pointcut)和通知(Advice)。切点定义了在程序...

    java反射机制与动态代理

    总之,Java反射机制与动态代理为开发者提供了强大的灵活性,可以在运行时探索和修改程序行为,实现更复杂的设计模式和编程策略。然而,由于它们涉及底层操作,过度使用可能会影响程序性能和安全性,因此在实际应用中...

    Java 代理 代理模式 静态代理与动态代理 常见的动态代理实现 .md

    - **JDK Proxy**:基于Java反射API实现,仅支持接口的代理。使用JDK Proxy时,需要指定被代理接口的类型,并且代理类必须实现该接口。 - **CGLIB**:基于字节码技术实现,支持对类的代理。CGLIB通过动态生成子类的...

    Java反射机制和动态代理

    主要讲述Java反射机制与设计模式之一:代理模式的原理与应用;同时详细讲述了Java对代理模式的支持以及Java中动态代理的原理,应用与实践。

Global site tag (gtag.js) - Google Analytics