企业级应用系统体系架构十状态管理.PPT

上传人:国*** 文档编号:495746 上传时间:2018-10-15 格式:PPT 页数:107 大小:717.50KB
下载 相关 举报
企业级应用系统体系架构十状态管理.PPT_第1页
第1页 / 共107页
企业级应用系统体系架构十状态管理.PPT_第2页
第2页 / 共107页
企业级应用系统体系架构十状态管理.PPT_第3页
第3页 / 共107页
企业级应用系统体系架构十状态管理.PPT_第4页
第4页 / 共107页
企业级应用系统体系架构十状态管理.PPT_第5页
第5页 / 共107页
点击查看更多>>
资源描述

1、企业级应用系统体系架构(十) 状态管理,Chen HaopengMonday, October 15, 2018,References:TedNeward: Effective Enterprise Java,1,2,状态管理,对于寻求真理的人而言,有些准则是必须遵守的,真理并非教条或无知,而是通过推理、调查、检验、与探究得来的。无论其意图有多好,信仰都必须构建在事实而非幻想之上,幻想之上的信仰是最糟糕的虚假希望。 Thomas Edison,3,状态管理,在企业级系统中,大部分工作都涉及数据处理。事实上,可以论证,企业级系统只做了一件事,那就是数据处理。在两层架构的客户/服务器系统时代,这还

2、不太明显,那时企业级程序员需要关心两种状态:瞬时状态(transient state)与持久状态(durable state)。瞬时状态并不算企业级数据所覆盖的正式部分,持久状态则是无论发生什么都需要被跟踪的部分。,4,状态管理,瞬时状态是那些企业并不关心,也不会为之流泪的数据,因为在系统崩溃的时候,真正重要的东西决不会丢失。电子商务中的购物车是瞬时状态典型的例子。在厚客户端或富客户端的应用中,瞬时状态很容易处理:相当于客户端进程中存储在局部变量中的数据,它们没有被保存在持久的存储介质中。当客户端进程结束的时候,瞬时状态也随之消亡,无需为其生命周期的处理而多费心思。,5,状态管理,不过在瘦客户

3、端中,例如基于HTML浏览器的应用,瞬时状态则呈现出另一种尺度。因为HTTP本就是无状态的协议,自身并不具备保存每一个客户端状态的能力。所以要由程序员在底层协议之上自己实现瞬时状态机制。另一方面则是持久状态,在谈到它时人们自然就会想到 “持久数据(persistent data)”,即需要长久保存的数据。正规的说法是,如果定义了某个持久状态,那么它就绝对会被保存下来,即使遇到JVM终止甚至崩溃的情况也是如此。,6,状态管理,持久状态通常具有隐含的法律性或经济意义。在讨论状态管理时,二者的区别至关重要。因为对瞬时状态有效的机制对持久状态不一定有效,反之亦然。,7,状态处理,第1项:节省地使用 H

4、ttpSession第2项:使用对象优先的持久化来保存你的领域模型第3项:使用关系优先的持久化来显示关系模型的威力第4项:使用过程优先的持久化来创建一个封装层第5项:识别对象-层次结构的阻抗失配(impedance mismatch),8,节省地使用 HttpSession,在基于HTML/HTTP的应用中,为维护代表客户端的瞬时状态,servlet容器提供了一种称为会话空间的设施,被表示为HttpSession接口。遗憾的是,这种机制并非完全免费的。首先,在服务器端为每个客户端存储数据将会减少该服务器上的可用资源,这意味着服务器的最大负载能力会成比例地下降。这个算式很简单:在会话空间中保存越

5、多的数据,机器能够处理的会话就越少。由此推导出,为了令给定的机器能够支持尽可能多的客户端,必须将会话的存储量保持在最小。实际上,对于真正具备可扩展性的系统而言,无论何时都应该避免使用会话。如果在服务器端可以不产生任何为每个客户端进行处理的开销,那么机器的负载能力(在理论上)可以到达无限,能够支持任意多连接到它的客户端。,9,节省地使用 HttpSession,避免使用会话的建议不单单是考虑到系统的可扩展性。对于在Web集群内运行的servlet容器而言,这也是必须的。会话是驻留内存的结构。因为内存是局限于特定的机器,除非Web集群有某种机制,能够令给定客户端的每一次请求都被传送给同一个服务器,

6、否则对应先前的某个请求,其后续处理可能会找不到之前存储的会话对象。,10,节省地使用 HttpSession,有一种可能的机制可以对此提供支持:在服务器集群中,指派一个单独的节点作为会话状态服务器。对每一个请求,无论哪个节点正在处理它,该节点都向会话状态服务器查询此客户端的会话状态,然后将会话状态通过网络传给处理此请求的节点。然而,这种机制有两种副作用:(1)每个请求都增加了一次与会话状态服务器之间的往返访问,这增加了客户端请求的等待时间。但更重要的是,(2)所有的会话状态都被存储在集中的服务器上,这使得集群中产生了一个单一故障点(single point of failure)。,11,节省

7、地使用 HttpSession,另一种可能的机制是采用P2P(peer-to-peer)方式。当一个请求进入某节点时,此节点发出一个集群广播信号,询问其它节点是否拥有此客户端最近的会话状态。拥有此客户端最近状态的节点对此做出回答,并将该会话状态传递给当前正处理请求的节点。,12,节省地使用 HttpSession,与会话相关的另一个需要注意的问题是会话的意外使用。JSP的规范清楚地陈述道,对于给定的JSP网页,“打开”会话的指示(带有session属性的page指示性标记),缺省地被设置为true,这意味着下面的JSP网页将为其建立一个会话,即使该网页从未使用会话:Hello, world,

8、Im a stateless JSP page.It is now .,13,节省地使用 HttpSession,更糟的是,在Web应用的任何角落,只要有一个这样的JSP页面,在该客户端使用此Web应用的整个过程中,该会话(即使没有在会话中保存对象,也会带来相关的系统开销。)将一直存在。所以,除非你需要使用会话,否则你应该确保你的JSP页面都关闭会话。我希望对于给定的Web应用,有某种方法能够令session = false成为缺省设置,然而到目前为止它并非如此。希望你不要误解,我并不是鼓吹完全不使用会话,实际上,如果建议HTTP不提供任何合理的机制以提供每个用户的状态的话,将是很滑稽的。如果

9、小心地使用,那么HttpSession就可以提供必要而强大的机制去提供在Web应用中的每个用户的状态,而这通常是关键而且必需的东西。(否则怎么才能确定用户是否成功地通过了身份认证,或者在应用中跟踪用户的进度呢?)危险在于,不是必需使用会话的时候,过度地使用或滥用此机制,那将给servlet容器带来额外的开销。所以,如非必要,尽量不要使用会话,如果非用不可,为了尽可能少地消耗运行servlet容器的机器上的资源,请保持会话精简而有意义。,14,状态处理,第1项:节省地使用 HttpSession第2项:使用对象优先的持久化来保存你的领域模型第3项:使用关系优先的持久化来显示关系模型的威力第4项:

10、使用过程优先的持久化来创建一个封装层第5项:识别对象-层次结构的阻抗失配(impedance mismatch),15,使用对象优先的持久化来保存你的领域模型,使用对象优先的持久化方式时,我们力求在持久化的过程中保持对象的视角。这意味着无需我们的任何提示,对象就知道如何默默地持久化自己,或者它们能够提供某种以对象为中心的(object-centric) API进行持久化和读取操作。,16,使用对象优先的持久化来保存你的领域模型,那么在理想世界中,编写像下面这样的代码将自动在数据库中创建一个包含了代表25岁的Stu Halloway的项:Person p = new Person(“Stu”,

11、“Halloway”, 25);System.out.println(p);/ Prints “Stu Halloway, age 25” 而下面的代码将自动更新在第一段代码中所创建的行,将Stu的年龄从25改成30:Person p = Person.find(“Stu”, “Halloway”);System.out.println(p);/ Prints “Stu Halloway, age 25”p.setAge(30);System.out.println(p);/ Prints “Stu Halloway, age 30” 注意到了吗,对象优先持久化方法的一个重要的优点:没有丑陋的

12、SQL语句,不用为是该INSERT还是该UPDATE这样的问题而烦心。我们所能看到的只是对象,这正是我们喜欢的方式。,17,使用对象优先的持久化来保存你的领域模型,然而,在读取对象时,对象优先的方法往往很快就不起作用了。一般说来,对象优先的方法可能会采取以下两种方式:要么以纯面向对象的形式,通过创建包含查询规则的对象,进行对象查询;要么使用某种特定的“查询语言”进行对象查询。在纯粹的对象优先的环境中,除了对象,我们不希望看到任何东西,所以我们创建了查询对象(Query Object),它包含了我们所关心的约束查询的规则。遗憾的是,如果要创建一个复杂的查询,若它的查询规则不是对象的主键(有时称为

13、对象标识符object identifier,简称OID),从OODBMS的角度来看,这样做通常是复杂而且笨拙的:QueryObject q = new QueryObject(Person.class);q.add(Criteria.and( Criteria.greaterThan(“dependents”, 2), Criteria.lessThan(“income”, 80000);q.add(Criteria.and( Criteria.greaterThan(“dependents”, 0), Criteria.lessThan(“income”, 60000);,18,使用对象优

14、先的持久化来保存你的领域模型,我们此处所做的等价于下面几行语句:SELECT * FROM person pWHERE ( (p.dependents 2 AND p.income 0 AND p.income 60000) ) 哪一种更易于阅读?如果我们开始在查询中执行深层嵌套的布尔逻辑,例如查询“收入少于$80,000而且有超过2个子女的人,或者收入少于$60,000而且无子女的人”,此时事情将以指数级地恶化。事实上并不难发现,比起通用目的的查询语言,例如SQL,纯对象优先的查询方法对于能查询什么,有着过于严格的限制。,19,使用对象优先的持久化来保存你的领域模型,这启发我们找寻第二条路,

15、即创建某种“查询语言”,使它能够更简明地表达查询,而无需使用过于复杂的代码。Java中所有的对象优先技术最终都回到了这一点:EJB 引入了EJBQL,一种为实体bean编写查询方法(finder)的查询语言;JDO引入了JDOQL,它为JDO增强的持久类做相同的工作;而OODBMS回过头来采用OQL(Object Query Language)对象查询语言。这些语言相互之间有着微妙的区别,但有着一个明确的共同点:它们都很像SQL,而SQL正是我们起初试图摆脱的。,20,使用对象优先的持久化来保存你的领域模型,使用对象优先的方法还有另一个副作用,即不可视的往返访问。例如,当像下面这样使用实体be

16、an时,引发了多少次数据库访问呢?PersonHome ph = (PersonHome)ctx.lookup(java:comp/env/PersonHome);Collection personCollection = ph.findByLastName(Halloway);for (Iterator i = personCollection.iterator(); i.hasNext(); ) Person p = (Person)i.hasNext(); System.out.println(Found + p.getFirstName() + + p.getLastName(); 虽

17、然看起来似乎只访问了一次数据库(读取每个姓Halloway的Person对象,并将其组装到PersonBean池中的实体bean上),但实际上,这正是EJB中的N+1次查询问题,查找方法调用只查找符合查询条件的行的主键,然后用只知道主键的实体bean的存根组装Collection中,并在必要的时候才将数据惰性加载到(lazy-load)实体bean中。,21,使用对象优先的持久化来保存你的领域模型,开发一个实体bean的实现,对于查询的结果,它不是简单地取回实体的OID/主键,而是取得保存在实体中的整个数据集,这也是可行的。实质上这就是采用积极加载(eager-load),而不是更为常用的惰性

18、加载。遗憾的是,这引发了一个相反的问题,现在我们抱怨的是取回的数据太多,而不是太少。这里问题的关键是,在对象优先的持久化场景中,数据读取的原子单位是对象本身,从面向对象的观点来看,返回那些比对象小的东西根本没有意义,就像在SQL查询中,如果返回的是比行还小的单位,结果同样没有意义。,22,使用对象优先的持久化来保存你的领域模型,我可以写出某些类似于下面的语句:SELECT p.FirstName, p.LastNameFROM Person pWHERE p.LastName = Halloway; 可是,这到底返回了什么?通常而言,一次对象查询的返回值是一个已定义类型的对象(如上例中的Per

19、son实例)。可此处我们得到的是什么?对于只返回“部分”对象,并没有普遍都可接受的方式,所以典型的结果是采用像ResultSet或者Java Map一类的东西(或者是Map实例的一个List)。即使我们理清了这些问题,对象优先的查询还是有其它的问题:对象到对象的引用。在此种情况下,困难并不经常发生,因为我们还没有很好的建模技巧去管理在关系型数据库中的一对多、多对多、或多对一的关系(顺带一提,这并非微不足道)。但是问题仍然存在,当一个对象被读取的时候,问题就来了,是否应该将所有与它相关联的对象都读取出来呢。还有,我们应该如何解决这个问题:两次独立的查询通过该间接引用取回了两个相同的对象?,23,

20、使用对象优先的持久化来保存你的领域模型,例如,考虑此场景,我们的系统中有四个Person对象:Stu Halloway 娶了Joanna Halloway,他们有两个孩子Hattie Halloway和Harper Halloway。从任何良好的对象观点来看,这意味着好的Person模型应该有一个配偶属性spouse,它是Person类型的(或者更确切一点,是指向Person的引用),同样还有一个孩子属性children,它是某种集合类型,包含指向Person的引用。现在,如果我们执行前面的查询,以取得第一个对象(让我们假设是Stu),那么通过网络取回Stu对象时是否也应该通过网络去取回Joa

21、nna、Hattie和Harper呢?问题又来了,我们此处是采用积极加载数据,还是惰性加载呢,记住,这些对象是被Stu对象实例的域所引用的。当我们从查询结果中取得下一个对象Joanna时,她也引用着Stu,此时在客户端的进程空间中,我们是有一个Stu对象还是两个?如果我们做两次独立的查询,第一次只读取Stu对象,第二次读取Joanna,会发生什么状况呢?对象同一性(identity)的概念很重要,因为在Java中,对象的同一性是通过this指针(对象的位置)来确立的,而在数据库中它是通过主键来表现的,令二者相匹配困难重重,特别是当我们将事务处理置于二者之间时。不过这也并非是不可解决的问题,同一

22、性映射(Identity Map)就是一个典型的解决方案,但是作为一个对象程序员,你对此必须警惕,以防你采用的对象优先持久化机制没有考虑到此问题。,24,使用对象优先的持久化来保存你的领域模型,此处最终的结论是,如果你想采用某种对象优先的持久化方法,可不能仅仅因为“更容易使用”就选择它。在很多情况下,只使用对象,其性能与吸引力就已经足够弥补你别处的损失了,并且还具备很多优点。,25,状态处理,第1项:节省地使用 HttpSession第2项:使用对象优先的持久化来保存你的领域模型第3项:使用关系优先的持久化来显示关系模型的威力第4项:使用过程优先的持久化来创建一个封装层第5项:识别对象-层次结

23、构的阻抗失配(impedance mismatch),26,使用关系优先的持久化来显示关系模型的威力,对象和关系相处得并不好。在这两种技术之间取得良好的映射很困难,这种困难甚至有自己的名字:对象与关系的阻抗失配(impedance mismatch)。面向对象语言与关系型数据访问技术(如JDBC)协同工作时的问题,很大一部分只是因为这两种技术的基础在看待世界时所采用的方式非常不同。面向对象语言希望使用对象,它具有属性(域)和行为(方法)。而关系技术将世界看成元组,即被群组为某种逻辑“事物”的数据项集合。,27,使用关系优先的持久化来显示关系模型的威力,尽管针对对象-关系映射层所固有的问题有大量

24、的论文,且此问题也超出了本书的讨论范围,但是简要地看看其中的一个问题,将有助于我们理解为什么对象-关系映射问题在J2EE系统中如此普遍。请思考下面这个简单的领域对象模型:public class Person private String firstName; private String lastName; private int age; / . . .public class Employee extends Person private long employeeID; private float monthlySalary; 这可能是世界上最简单的领域模型了,但我们应该如何将它持久化

25、到一个关系型数据库中呢?,28,使用关系优先的持久化来显示关系模型的威力,一种方法是创建两个表:PERSON和EMPLOYEE。使用外键(foreign-key)关系将二者的行彼此关联起来。每次我们想得到一个Employee时就需要对两个表做一次连接(join)操作,而每次查询和修改数据时,数据库还需做更多的工作。我们也可以将Person和Employee数据存入单一的EMPLOYEE表中,但如果我们又创建了Student(继承自Person),并想找到所有姓Smith的Person对象时,我们不得不搜索STUDENT和EMPLOYEE两个表,而二者在关系层面上并不相关。如果这种继承层次继续变

26、得更深,则问题几乎呈指数级地混杂起来。更何况,企业应用的开发人员通常没有对数据库模式(schema)的控制权,因为遗留系统或其它J2EE系统已经在使用它了,或者是由其他开发团队负责数据库模式。所以,即使我们想建立一个表的结构,使它优雅地映射到我们的对象模型,我们也不能随心所欲地改变数据库模式的定义。,29,使用关系优先的持久化来显示关系模型的威力,从另一个完全不同的角度来看,也许有其它更为实际的原因致使我们放弃对象优先的方式。由于这些(以及更多的)原因,我们通常更易于采用关系的观点来看待并操纵数据,而不是将访问关系的操作隐藏在其它某种封装技术之后,例如面向对象、面向过程、或面向层次结构。如果要

27、理解我对于采用关系优先方式的看法,我们需要后退一步,重新看看关系型方式到底是什么。Chris Date与E. F. Codd一同被看作是关系模型之父,按Chris Date的说法,“关系系统建立在形式化的基础或理论之上,被称为数据的关系模型”。对数学家而言,关系模型是建立在集合论和谓词逻辑的基础上。然而对我们而言,用最简单的话来说,数据的关系模型就是表而已。访问数据得到的只是表,而操作那些数据的运算符(SQL)也是由表再产生表。,30,使用关系优先的持久化来显示关系模型的威力,关系型数据库的核心就是表,表就是关系模型中“关系”,就是它使得关系模型如此强大。由此关系数据访问做到了闭包性(clos

28、ure):一次访问的结果可以作为另一次访问的输入。这使得我们能够写出嵌套的表达式:表达式中的操作数由一般的表达式来代表,而不是直接使用表名。SQL之所以那么强大,很大部分原因就是因为支持嵌套,虽然我们并不想过多地使用嵌套为什么闭包性很重要?对于关系型数据库而言,SQL是一种强大的数据访问语言,而SQL查询得到的结果就是表,这真是值得庆幸,因为这样我们只需要一个API,就能取得任何一次查询的返回结果,无论结果数据是很多,还是很少。我们也没有“比对象小”的问题,因为取得的结果总是表,即使是只有一列的表。我们必须面对的问题是,关系模型经常不能与程序员使用的对象模型相匹配,不过关于这一点我们可说的有很

29、多。还是让我们先来看看怎样令关系访问本身更容易吧。,31,使用关系优先的持久化来显示关系模型的威力,在你一想到你余下的职业生涯都要跟讨厌的底层的JDBC访问打交道,因而在恐惧中萎缩之前,请先做个深呼吸,采用关系优先的方法并不意味着放弃任何比JDBC层次高的方法。实际上,远远不是这样。Java允许我们可以采用多种更为简单的机制进行关系的存取访问,而不仅仅是原始的JDBC(在许多情况下JDBC仍然是一种可选的方案,尽管它具有相对比较低层的特性)。首先,JDBC可不仅仅只是Connection、Statement、和ResultSet对象。RowSet和Sun公司独特的CachedRowSet,通过

30、将查询行为与获得的结果进行封装,令JDBC更便于使用。,32,使用关系优先的持久化来显示关系模型的威力,因此,假设你没有JDBC DataSource,也可以很容易地像下面这样进行查询:RowSet rs = new WebRowSet();/ Or use another RowSet implementation/ Provide RowSet with enough information to obtain a/ Connectionrs.setUrl(jdbc:dburl:/dbserver/PEOPLE);rs.setUsername(user);rs.setPassword(pa

31、ssword);rs.setCommand(SELECT first_name, last_name FROM person + WHERE last_name=?);rs.setString(1, Halloway);rs.execute();/ rs now holds the results of the query,33,使用关系优先的持久化来显示关系模型的威力,大多数对RowSet的调用都可以被隐藏在对象工厂接口之后,所以客户端代码还可以再少一些,如下所示:RowSet rs = MyRowSetFactory.getRowSet();rs.setCommand(. . .);rs.

32、setString(1, “Halloway”);rs.execute(); 这与直接使用SQL进行访问几乎一样简单。此处使用的工厂也并不难以想象:public class MyRowSetFactory public static getRowSet() RowSet rs = new WebRowSet(); rs.setDataSourceName( java:comp/env/jdbc/PEOPLE_DS); return rs; ,34,使用关系优先的持久化来显示关系模型的威力,一种方法是保持数据库面向表的观点,然后通过表数据网关(Table Data Gateway)在你与数据访问

33、技术之间构架更多的渠道。其实质上就是将每个表变成一个类,然后用这些类依次作为该表中任意行的访问点。public class PersonGateway private PersonGateway() /* Singletoncant create */ public static Person findAll() ArrayList al = new ArrayList(); RowSet rs = MyRowSetFactory.getRowSet(); rs.setCommand(SELECT first_name,last_name,age + FROM person); rs.exec

34、ute(); while (rs.next() al.add(new Person(rs.getString(1), rs.getString(2), rs.getInt(3); return (Person)al.toArray(new Person0); public static void update(Person p) / And so on, and so on, and so on ,35,使用关系优先的持久化来显示关系模型的威力,表数据网关可以扩展出新的变体,叫做查询数据网关(Query Data Gateway),它不是对单个表进行包装,而是对一个查询进行包装。public

35、class ChildrenGateway private ChildrenGateway() public static Person findKidsForPerson(Person p) ArrayList al = new ArrayList(); RowSet rs = MyRowSetFactory.getRowSet(); rs.setCommand(SELECT first_name,last_name,age + FROM person p, parent_link pp + WHERE p.id = pp.child_id + AND p.last_name=?); rs.

36、setInt(1, p.getPersonID(); rs.execute(); while (rs.next() al.add(new Person(rs.getString(1), rs.getString(2), rs.getInt(3); return (Person)al.toArray(new Person0); ,36,状态处理,第1项:节省地使用 HttpSession第2项:使用对象优先的持久化来保存你的领域模型第3项:使用关系优先的持久化来显示关系模型的威力第4项:使用过程优先的持久化来创建一个封装层第5项:识别对象-层次结构的阻抗失配(impedance mismatch

37、),37,使用过程优先的持久化来创建一个封装层,使用存储过程作为持久化模型最大的优点是,我们能够将存储过程的实现交给负责数据库的同事,将整个数据模型都交给他们设计、实现,以及在必要时进行调整。只要存储过程的定义不变(否则我们就会得到运行期的SQLExecption异常),以及它的实现确实如我们所希望的(存储或取回数据),我们就可以完全忽略底层的数据模型。如果你在关系的设计与执行方面与我一样“优秀”的话(即根本不擅长),这确实是件好事。,38,状态处理,第1项:节省地使用 HttpSession第2项:使用对象优先的持久化来保存你的领域模型第3项:使用关系优先的持久化来显示关系模型的威力第4项:

38、使用过程优先的持久化来创建一个封装层第5项:识别对象-层次结构的阻抗失配(impedance mismatch),39,识别对象-层次结构的阻抗失配,XML无所不在,包括在你的持久化方案中。问题在于XML在表示数据时使用的是固有的层次结构的方式,看看XML Infoset 规范,它要求数据是格式良好的(well-formed),即XML文档中的元素必须形成一棵良好的元素树(每个元素都可以有子元素嵌套其中,每个元素都有一个唯一的父结点,唯一的例外是“根”结点,它囊括了整个文档)。这意味着XML善于表现层级结构的数据(所以作为这一项的题目),假设你的对象形成一个整洁的层次结构,则XML是表达该对象

39、最自然的方式(因此自然地假设了XML和对象是手牵着手)。,40,识别对象-层次结构的阻抗失配,但是当对象没办法形成整洁、自然的树时会发生什么呢?层次模型带来的问题是,在其中查找数据很困难。用户必须亲自在树的元素中搜寻,这迫使用户必须知道“如何查询”以获取数据,而不是仅仅关注“查询什么”感兴趣的数据即可。随着XML的出现(以及不断增长的对“XML数据库”的兴趣,尽管这个词具有二义性),似乎层次数据模型也再次流行起来。虽然对层次数据模型的全面讨论超出了本书的范围,但在此仍有必要弄清两件事:我们何时会在J2EE中使用层次数据模型,而它对Java程序员又有什么影响。,41,识别对象-层次结构的阻抗失配

40、,将对象映射到XML(今天最常见的层次结构存储模型)并不是件简单的事情,这使得我们想知道是否存在对象-层次结构的阻抗失配(impedance mismatch),换言之,形式自由的对象模型与XML Infoset严格的层次结构模型是否难以匹配。将对象映射到层次模型的问题,很大程度上与发生在将对象映射到关系模型时发生的问题一样:保证对象的同一性。为了说清楚我的意思,让我们重新看看前面曾经用过的Person对象:public class Person public String firstName; public String lastName; public int age; public Pe

41、rson(String fn, String ln, int a) firstName = fn; lastName = ln; age = a; ,42,识别对象-层次结构的阻抗失配,同样地简单而直接,也不难想象该对象的XML表示会是什么样子: Ron Reynolds 30 到目前为止一切都好。但是,现在让我们来添加一些东西,它们对面向对象模型而言绝对合理,却将层次结构完全打破了,这就是循环引用:public class Person public String firstName; public String lastName; public int age; public Person

42、 spouse; public Person(String fn, String ln, int a) firstName = fn; lastName = ln; age = a; ,43,识别对象-层次结构的阻抗失配,你应该如何表现下面的对象集?Person ron = new Person(Ron, Reynolds, 31);Person lisa = new Person(Lisa, Reynolds, 25);ron.spouse = lisa;lisa.spouse = ron; 下面的将ron序列化输出到XML的方法并非不合理,它简单地遍历属性成员,递归处理每个的对象并依次遍历

43、其属性成员,以此类推。然而这样做很快就会出问题,如下所示: Ron Reynolds 31 Lisa Reynolds 25 Ron Reynolds 31 ,44,识别对象-层次结构的阻抗失配,正如你看到的,因为两个对象循环引用对方,此处产生了无限递归。我们可以使用与Java 对象序列化所采用的相同的方法来处理这个问题,即对哪些项被序列化了而哪些项没有被序列化都保持跟踪,但是这样的话我们就陷入了更大的问题:即使在给定的XML层次结构中我们保持对对象同一性的跟踪,那么我们怎么在多个层次结构之间作到这一点呢?String param1 = ron.toXML(); / Serialize to

44、XMLString param2 = lisa.toXML(); / Serialize to XMLsendXMLMessage( + param1 + param2 + );/* Produces:param1 =,45,识别对象-层次结构的阻抗失配,Ron Reynolds 31 Lisa Reynolds 25 ,46,识别对象-层次结构的阻抗失配,param2 = Lisa Reynolds 25 Ron Reynolds 25 ,47,状态管理,对于寻求真理的人而言,有些准则是必须遵守的,真理并非教条或无知,而是通过推理、调查、检验、与探究得来的。无论其意图有多好,信仰都必须构建在

45、事实而非幻想之上,幻想之上的信仰是最糟糕的虚假希望。 Thomas Edison,48,状态处理,第1项:使用进程内(in-process)或本地存储以避开网络第2项:不要假设拥有数据或数据库第3项:惰性加载不频繁使用的数据第4项:积极载入频繁使用的数据第5项:批处理SQL的工作以避免往返访问第6项:了解你的JDBC供应商第7项:调整你的SQL语句,49,使用进程内或本地存储以避开网络,大多数时候,当J2EE开发人员开始设计或划分项目的架构层次时,数据存储问题已经形成结论了:将采用关系型数据库,运行在数据中心或操作中心某处的机器上。为什么?并没有什么神秘或神奇的答案,仅仅只是为了可访问性。我们

46、希望该系统的任何潜在客户都能够访问到数据。过去,在n层架构流行之前,客户端直接连接到服务器上,为了让所有客户端都能够访问到全部数据,数据需要直接存储在服务器端。那时网络还不普及,使网络更简单的无线访问技术还没出现。将机器连接到网络上是日常主要的杂务,结果是,P2P通讯的基础概念总是在讨论最底层的网络堆栈问题(例如IP协议)。,50,使用进程内或本地存储以避开网络,我们渐渐认识到,将所有数据存放在一台中心服务器上有许多优点,即主要的负荷都置于该服务器端,而不是客户端。因为服务器是单独的机器,(所以我们相信)升级或替换它变得很划算,特别是比起替换所有连接到它的客户端来,更是如此。所以不久之后我们就建立起了数据库中心化的思想,我们开始将数据库放在能找到的最重的铁盒子中,用容量最大的RAM和巨大的千兆字节(后来是万亿字节)的驱动器装满其中。这些关于历史的题外话的重点是,中心化的远程数据库服务器的存在只是为了提供单一的数据聚合点,而不是因为数据库“必须”运行在服务器上,那可是花了5、6、或7位数的开销的。我们将数据存放到服务器上,因为(a)它是个很方便的地方;(b)易于将所有为客户端进行的处理放在一个集中的地方,且无需向客户端推出更新(零部署);(c)这是一种令数据与处理靠近的方法。,

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 重点行业资料库 > 1

Copyright © 2018-2021 Wenke99.com All rights reserved

工信部备案号浙ICP备20026746号-2  

公安局备案号:浙公网安备33038302330469号

本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。