Java之 代理模式(Proxy)(四)

2014-11-24 07:48:24 · 作者: · 浏览: 1
数据库的URL", "用户名", "密码");

}

}

(4)写个客户端来测试看看,是否能正确实现代理的功能呢,示例代码如下:

public class Client {

public static void main(String[] args) throws Exception{

UserManager userManager = new UserManager();

Collection col =

userManager.getUserByDepId("0101");

//如果只是显示用户名称,那么不需要重新查询数据库

for(UserModelApi umApi : col){

System.out.println("用户编号:="+umApi.getUserId()

+",用户姓名:="+umApi.getName());

}

//如果访问非用户编号和用户姓名外的属性,那就会重新查询数据库

for(UserModelApi umApi : col){

System.out.println("用户编号:="+umApi.getUserId()

+",用户姓名:="+umApi.getName()

+",所属部门:="+umApi.getDepId());

}

}

}

运行结果如下:

用户编号:=user0001,用户姓名:=张三1

用户编号:=user0002,用户姓名:=张三2

用户编号:=user0003,用户姓名:=张三3

重新查询数据库获取完整的用户数据,userId==user0001

用户编号:=user0001,用户姓名:=张三1,所属部门:=010101

重新查询数据库获取完整的用户数据,userId==user0002

用户编号:=user0002,用户姓名:=张三2,所属部门:=010101

重新查询数据库获取完整的用户数据,userId==user0003

用户编号:=user0003,用户姓名:=张三3,所属部门:=010102

仔细查看上面的结果数据会发现,如果只是访问用户编号和用户姓名的数据,是不需要重新查询数据库的,只有当访问到这两个数据以外的数据时,才需要重新查询数据库以获得完整的数据。这样一来,如果客户不访问除这两个数据以外的数据,那么就不需要重新查询数据库,也就不需要装载那么多数据,从而节省内存。

(5)1+N次查询

看完上面的示例,可能有些朋友会发现,这种实现方式有一个潜在的问题,就是如果客户对每条用户数据都要求查看详细的数据的话,那么总的查询数据库的次数会是1+N次之多。

第一次查询,获取到N条数据的用户编号和姓名,然后展示给客户看。如果这个时候,客户对每条数据都点击查看详细信息的话,那么每一条数据都需要重新查询数据库,那么最后总的查询数据库的次数就是1+N次了。

从上面的分析可以看出,这种做法最合适的场景就是:客户大多数情况下只需要查看用户编号和姓名,而少量的数据需要查看详细数据。这样既节省了内存,又减少了操作数据库的次数。

看到这里,可能会有朋友想起,Hibernate这类ORM的框架,在Lazy Load的情况下,也存在1+N次查询的情况,原因就在于,Hibernate的Lazy Load就是使用代理来实现的,具体的实现细节这里就不去讨论了,但是原理是一样的。

11.3 模式讲解

11.3.1 认识代理模式

(1)代理模式的功能

代理模式是通过创建一个代理对象,用这个代理对象去代表真实的对象,客户端得到这个代理对象过后,对客户端没有什么影响,就跟得到了真实对象一样来使用。

当客户端操作这个代理对象的时候,实际上功能最终还是会由真实的对象来完成,只不过是通过代理操作的,也就是客户端操作代理,代理操作真正的对象。

正是因为有代理对象夹在客户端和被代理的真实对象中间,相当于一个中转,那么在中转的时候就有很多花招可以玩,比如:判断一下权限,如果没有足够的权限那就不给你中转了,等等。

(2)代理的分类

事实上代理又被分成多种,大致有如下一些:

  • 虚代理:根据需要来创建开销很大的对象,该对象只有在需要的时候才会被真正创建
  • 远程代理:用来在不同的地址空间上代表同一个对象,这个不同的地址空间可以是在本机,也可以在其它机器上,在Java里面最典型的就是RMI技术
  • copy-on-write代理:在客户端操作的时候,只有对象确实改变了,才会真的拷贝(或克隆)一个目标对象,算是虚代理的一个分支
  • 保护代理:控制对原始对象的访问,如果有需要,可以给不同的用户提供不同的访问权限,以控制他们对原始对象的访问
  • Cache代理:为那些昂贵的操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果
  • 防火墙代理:保护对象不被恶意用户访问和操作
  • 同步代理:使多个用户能够同时访问目标对象而没有冲突
  • 智能指引:在访问对象时执行一些附加操作,比如:对指向实际对象的引用计数、第一次引用一个持久对象时,将它装入内存等

    在这些代理类型中,最常见的是:虚代理、保护代理、远程代理和智能指引这几种。本书主要讨论和示例了虚代理和保护代理,这是实际开发中使用频率最高的。

    对于远程代理,没有去讨论,因为在Java中,远程代理的典型体现是RMI技术,要把远程代理讲述清楚,就需要把RMI讲述清楚,这不在本书讨论范围之内。

    对于智能指引,基本的实现方式和保护代理的实现类似,只是实现的具体功能有所不同,因此也没有具体去讨论和示例。

    (3)虚代理的示例

    前面的例子就是一个典型的虚代理的实现。

    起初每个代理对象只有用户编号和姓名的数据,直到需要的时候,才会把整个用户的数据装载到内存中来。

    也就是说,要根据需要来装载整个UserModel的数据,虽然用户数据对象是前面已经创建好了的,但是只有用户编号和姓名的数据,可以看成是一个“虚”的对象,直到通过代理把所有的数据都设置好,才算是一个完整的用户数据对象。

    (4)copy-on-write

    拷贝一个大的对象是很消耗资源的,如果这个被拷贝的对象从上次操作以来,根本就没有被修改过,那么再拷贝这个对象是没有必要的,白白消耗资源而已。那么就可以使用代理来延迟拷贝的过程,可以等到对象被修改的时候才真的对它进行拷贝。

    copy-on-write可以大大降低拷贝大对象的开销,因此它算是一种优化方式,可以根据需要来拷贝或者克隆对象。

    (5)具体目标和代理的关系

    从代理模式的结构图来看,好像是有一个具体目标类就有一个代理类,其实不是这样的。如果代理类能完全通过接口来操作它所代理的目标对象,那么代理对象就不需要知道具体的目标对象,这样就无须为每一个具体目标类都创建一个代理类了。

    但是,如果代理类必须要实例化它代理的目标对象,那么代理类就必须知道具体被代理的对象,这种情况下,一个具体目标类通常会有一个代理类。这种情况多出现在虚代理的实现里面。

    (6)代理模式调用顺序示意图

    代理模式调用顺序如图11.4所示:

    \

    图11.4 代理模式调用顺序示意图

    11.3.2 保护代理

    保护代理是一种控制对原始对象访问的代理,多用于对象应该有不同的访问权限的时候。保护代理会检查调用者是否具有请求所必需的访问权限,如果没有相应的权限,那么就不会调用目标对象,从而实现对目标对象的保护。

    还是通过一个示例来说明。

    1:示例需求

    现在有一个订单系统,要求是:一旦订单被创建,只有订单的创建人才可以修改订单中的数据,其他人不能修改。

    相当于现在如果有了一个订单