简单对象访问协议-CNXML标准教程  简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程
简单对象访问协议-CNXML标准教程
简单对象访问协议-CNXML标准教程  简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程
|
文谷 www.wengu.com
来源:网络 作者:何杭军 时间:2006-2-28 浏览人数:
|
处理 SSI 文件时出错
简单对象访问协议-CNXML标准教程  简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程
SOAP是在非集中、分布环境中交换信息的轻量级协议。它是基于XML的协议,包括三个部分: 封套(envelope)定义了消息内容和处理的框架、一套编码规则用来表达应用定义数据类型的实例以及表达远程过程调用和响应的协定。" ——SOAP 1.1规范 第一节 SOAP简介 SOAP(Simple Object Access Protocal,简单对象访问协议) 技术有助于实现大量异构程序和平台之间的互操作性,从而使存在的应用能够被广泛的用户所访问。SOAP是把成熟的基于HTTP的WEB技术与XML的灵活性和可扩展性组合在了一起。 SOAP的一个主要目标是使存在的应用能被更广泛的用户所使用。为了实现这个目的,没有任何SOAP API或SOAP 对象请求代理(SOAP ORB),SOAP是假设你将使用尽可能多的存在的技术。几个主要的CORBA厂商已经承诺在他们的ORB产品中支持SOAP协议。微软也承诺在将来的COM版本中支持SOAP。DevelopMentor已经开发了参考实现,它使得在任何平台上的任何Java或Perl程序员都可以使用SOAP。而且IBM和Sun也陆续支持了SOAP协议,和MS合作共同开发SOAP规范和应用。目前SOAP已经成为了W3C和IETF的参考标准之一。 SOAP的指导理念是“它是第一个没有发明任何新技术的技术”。它采用了已经广泛使用的两个协议:HTTP和XML。HTTP用于实现SOAP的RPC风格的传输,而XML是它的编码模式。采用几行代码和一个XML解析器,HTTP服务器(如MS的IIS或Apache)立刻成为了SOAP的ORBs。 因为目前超过一半的Web服务器采用IIS或Apache, SOAP将会从这两个产品的广泛而可靠的使用中获取利益。这并不意味着所有的SOAP请求必须通过Web服务器来路由,传统的Web 服务器只是分派SOAP请求的一种方式。因此Web服务如IIS或Apache对建立SOAP性能的应用是充分的,但决不是必要的。 SOAP把XML的使用代码化为请求和响应参数编码模式,并用HTTP作传输。这似乎有点抽象。具体地讲,一个SOAP方法可以简单地看作遵循SOAP编码规则的HTTP请求和响应。一个SOAP终端则可以看作一个基于HTTP的URL,它用来识别方法调用的目标。象CORBA/IIOP一样,SOAP不需要具体的对象被绑定到一个给定的终端,而是由具体实现程序来决定怎样把对象终端标识符映射到服务器端的对象。 SOAP请求是一个HTTP POST请求。SOAP请求的content-type必须用text/xml。而且它必须包含一个请求-URI。服务器怎样解释这个请求-URI是与实现相关的,但是许多实现中可能用它来映射到一个类或者一个对象。一个SOAP请求也必须用SOAPMethodName HTTP头来指明将被调用的方法。简单地讲,SOAPMethodName头是被URI指定范围的应用相关的方法名,它是用#符作为分隔符将方法名与URI分割开: SOAPMethodName: urn:strings-com:IString#reverse 这个头表明方法名是reverse,范围URI是urn:strings-com:Istring。 在SOAP中,规定方法名范围的名域URI在功能上等同于在DCOM 或 IIOP中规定方法名范围的接口ID。 简单的说,一个SOAP请求的HTTP体是一个XML文档,它包含方法中[in]和[in,out]参数的值。这些值被编码成为一个显著的调用元素的子元素,这个调用元素具有SOAPMethodName HTTP头的方法名和名域URI。调用元素必须出现在标准的SOAP 和元素内(后面会更多讨论这两个元素)。下面是一个最简单的SOAP方法请求: POST /string_server/Object17 HTTP/1.1 Host: 209.110.197.2 Content-Type: text/xml Content-Length: 152 SOAPMethodName: urn:strings-com:IString#reverse Hello, World SOAPMethodName头必须与下的第一个子元素相匹配,否则调用将被拒绝。这允许防火墙管理员在不解析XML的情况下有效地过滤对一个具体方法的调用。 SOAP响应的格式类似于请求格式。响应体包含方法的[out]和 [in,out]参数,这个方法被编码为一个显著的响应元素的子元素。这个元素的名字与请求的调用元素的名字相同,但以Response后缀来连接。下面是对前面的SOAP请求的SOAP响应: 200 OK Content-Type: text/xml Content-Length: 162 dlroW ,olleH 这里响应元素被命名为reverseResponse,它是方法名紧跟Response后缀。要注意的是这里是没有SOAPMethodName HTTP头的。这个头只在请求消息中需要,在响应消息中并不需要。 第二节 SOAP体的核心 SOAP的XML特性是为把数据类型的实例序列化成XML的编码模式。为了达到这个目的,SOAP不要求使用传统的RPC风格的代理。而是一个SOAP方法调用包含至少两个数据类型:请求和响应。考虑这下面个COM IDL代码: [ uuid(DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA) ] interface IBank : IUnknown { HRESULT withdraw([in] long account, [out] float *newBalance, [in, out] float *amount [out, retval] VARIANT_BOOL *overdrawn); } 在任何RPC协议下,account和amount参数的值将出现在请求消息中,newBalance、overdrawn参数的值,还有amount参数的更新值将出现在响应消息中。 SOAP把方法请求和方法响应提升到了一流状态。在SOAP中,请求和响应实际上类型的实例。为了理解一个方法比如IBank::withdraw怎样映射一个SOAP请求和响应类型,考虑下列的数据类型: struct withdraw { long account; float amount; }; 这时所有的请求参数被打包成为单一的结构类型。同样下面的数据表示打包所有响应参数到单一的数据类型。 struct withdrawResponse { float newBalance; float amount; VARIANT_BOOL overdrawn; }; 再给出下面的简单的Visual Basic程序,它使用了以前定义的Ibank接口: Dim bank as IBank Dim amount as Single Dim newBal as Single Dim overdrawn as Boolean amount = 100 Set bank = GetObject("soap:http://bofsoap.com/am") overdrawn = bank.withdraw(3512, amount, newBal) 这里,在发送请求消息之前,参数被序列化成为一个请求对象。同样被响应消息接收到的响应对象被反序列化为参数。一个类似的转变同样发生在调用的服务器端。 当通过SOAP调用方法时,请求对象和响应对象被序列化成一种已知的格式。每个SOAP体是一个XML文档,它具有一个显著的称为的根元素。标记名由SOAP URI (urn:schemas-xmlsoap-org:soap.v1)来划定范围,所有SOAP专用的元素和属性都是由这个URI来划定范围的。SOAP Envelope包含一个可选的元素,紧跟一个必须的元素。元素也有一个显著的根元素,它或者是一个请求对象或者是一个响应对象。下面是一个IBank::withdraw请求的编码: 3512 100 下列响应消息被编码为: 0 5 true 注意[in, out]参数出现在两个消息中。在检查了请求和响应对象的格式后,你可能已经注意到序列化格式通常是: field1value field2value ...... 在请求的情况下,类型是隐式的C风格的结构,它由对应方法中的[in]和[in, out]参数组成。对响应来说,类型也是隐式的C风格的结构,它由对应方法中的[out]和[in, out]参数组成。这种每个域对应一个子元素的风格有时被称为元素正规格式(ENF)。一般情况下,SOAP只用XML特性来传达描述包含在元素内容中信息的注释。 象DCOM和IIOP一样,SOAP支持协议头扩展。SOAP用可选的元素来传载被协议扩展所使用的信息。如果客户端的SOAP软件包含要发送头信息,原始的请求将可能如图9所示。在这种情况下命名causality的头将与请求一起序列化。收到请求后,服务器端软件能查看头的名域URI,并处理它识别出的头扩展。这个头扩展被http://comstuff.com URI识别,并期待一个如下的对象: struct causality { UUID id; }; 在这种情况下的请求,如果头元素的URI不能被识别,头元素可以被安全地忽略。 但你不能安全的忽略所有的SOAP体中的 |
| # posted by myali88 @ 2005-04-10 12:32 评论(0) |
 |
如果想了解SOAP到底是什么,就要自己动手建立自己的SOAP标准对象。本文可以帮助你起步。 简介 SOAP - 简单对象处理协议(Simple Object Access Protocol) - 是当前XML开发的热点。它是微软新一代Visual Studio的主要角色,是".NET"策略的基础。如果想用VB 6编制一个有保障的SOAP服务,可以查阅微软的SOAP工具包(VB)。但如果想了解SOAP到底是什么,就要自己动手建立自己的SOAP标准对象。本文可以帮助你起步。 本文中,我们创建一个简单的SOAP服务端和一个客户端。服务端用ASP编写,名为soap.asp。这个文件应存放在个人Web服务器的根目录下,如:\Inetpub\wwwroot。这个服务器将接受和处理客户端提出的SOAP请求。客户端是一个简单的VB可执行文件,由Sub Main()启动。 步骤 众所周知,SOAP是一个“呼叫-响应”机制,按客户/服务方式运行。客户端(应用程序)向服务端(位于互联网上的某个Web服务器)发出函数调用请求并传递参数;服务端则返回响应。呼叫与响应的内容和数据都是按XML文件格式进行传送的。因此,要建立一个简单的SOAP应用系统,就要建立一个客户端和一个服务端,即一个呼叫-响应体系。 下面是一个简单例子: 我们建立一个服务端来计算销售交易的税款。按照传统的VB术语,即建立一个函数,定义如下: Public Function GetSalesTax(ByVal pSalesTotal As Double) as Double GetSalesTax = pSalesTotal * 0.04 End Function 一个粗糙的函数,但可作为示例(本例只能用于税率为4%的地方)。 这个函数定义了一个函数名(GetSalesTax),一个参数(pSalesTotal – 销售金额)和一个返回值(函数返回值)。按照面向对象原则,可以认为pSalesTotal是一个"IN"参数,GetSalesTax返回值是一个"OUT"参数。因此我们的SOAP服务端就要侦听客户发出的调用GetSalesTax的请求和传递的"IN"参数(销售金额),然后返回带有"OUT"参数的回应,向客户返回所需税款。 客户端 下面是用VB建立一个呼叫服务的客户端程序: dblSalesTax = GetSalesTax(100) 得到返回值$4。 如果GetSalesTax函数是一个外部对象,比如在MTS服务器上,就要调用服务器上的DLL模块: Dim objTax As New CTaxCalc dblSalesTax = objTax.GetSalesTax(100) 在SOAP系统中,远程调用的方式略有不同,呼叫是通过XML文件传送到服务器的。XML文件里有调用的函数名和相应的参数: 100 为确保服务器能够识别和解释客户请求,呼叫指令被包装到一个称之为SOAP信封的大文件里。这个信封使用的是SOAP封装标准的通用命名空间: 100 最后,加入函数调用的命名空间,起到函数声明的作用: 100 现在,已经准备好客户请求文件,可以送往服务端了。发送请求很简单,可以跟浏览器表单一样,用HTTP post方式。浏览器可以向服务端发送复杂的表单,.NET可以向服务器发送VB代码,但我使用XMLHTTP(IE 5以上版本才能用)。 假设strEnvelope含有XML文件格式的请求,发送格式如下: Dim objHTTP As New MSXML.XMLHTTPRequest Dim strEnvelope As String '设定发往本地服务器 objHTTP.open "post", "http://localhost/soap/soap.asp" '设定标准SOAP/ XML文件头格式 objHTTP.setRequestHeader "Content-Type", "text/xml" '设置呼叫函数请求 objHTTP.setRequestHeader "SOAPMethodName", _ "urn:myserver/soap:TaxCalc#GetSalesTax" '呼叫SOAP objHTTP.send strEnvelope '取得返回值 strReturn = objHTTP.responseBody 至此,客户端完成了向服务端发送请求的过程。现在回到服务端,看看服务端如何侦听客户请求并作出响应。 服务端 服务端要能够接收客户发出的HTTP请求,在本地服务器(http://localhost/soap.asp)接收到客户请求时作出回应。因此服务端要能够解析客户端发出的XML格式(SOAP封装)的请求,取出调用的函数名和参数。 服务端文件是soap.asp,它接收客户请求的做法是: Set objReq = Server.CreateObject("Microsoft.XMLDOM") objReq.Load Request 然后用XSL样式从封装的XML文件中取出参数: strQuery = "SOAP:Envelope/SOAP:Body/m:GetSalesTax/SalesTotal" varSalesTotal = objReq.SelectSingleNode(strQuery).Text 根据参数计算税款: varSalesTax = varSalesTotal * 0.04 在将结果返回给客户之前,要按SOAP标准做格式化封装。过程与客户端类似,只是把"IN"参数改换为"OUT"参数,并将函数标记名标为回应: 4 可以用字符串方式构造这个回应文件,也可以创建一个DOM对象,增加一个节点。 文件返回给客户后,客户经过解码就能得到结果: Dim objReturn As New MSXML.DomDocument objReturn.LoadXML strReturn strQuery = _ "SOAP:Envelope/SOAP:Body/m:GetSalesTaxResponse/SalesTax" dblTax = objReturn.SelectSingleNode(strQuery).Text 这样就完成了一个简单的SOAP服务应用。虽然Visual Studio 7掩盖了内在的SOAP协议,但我希望本文有助于理解SOAP的操作过程。 下面是客户端VB代码: VB Client Code Sub Main() Dim objHTTP As New MSXML.XMLHTTPRequest Dim strEnvelope As String Dim strReturn As String Dim objReturn As New MSXML.DOMDocument Dim dblTax As Double Dim strQuery As String '创建SOAP封装 strEnvelope = _ "" & _ "" & _ "" & _ "" & _ "100" & _ "" & _ "" & _ "" '设定发往本地服务器 objHTTP.open "post", "http://localhost/soap.asp", False '设定标准SOAP/ XML格式 objHTTP.setRequestHeader "Content-Type", "text/xml" '设置调用函数头 objHTTP.setRequestHeader "SOAPMethodName", _ "urn:myserver/soap:TaxCalculator#GetSalesTax" 'SOAP呼叫 objHTTP.send strEnvelope '取出返回信封 strReturn = objHTTP.responseText '加载到DOM objReturn.loadXML strReturn '查询返回值 strQuery = _ "SOAP:Envelope/SOAP:Body/m:GetSalesTaxResponse/SalesTax" dblTax = objReturn.selectSingleNode(strQuery).Text Debug.Print dblTax End Sub 下面是服务端ASP代码(文件名soap.asp,存放在本地服务器根目录下): <% Set objReq = Server.CreateObject("Microsoft.XMLDOM") '加载请求到XML DOM objReq.Load Request '按照输入参数查询 strQuery = "SOAP:Envelope/SOAP |
| # posted by myali88 @ 2005-04-10 12:30 评论(0) |
 |
使用单元测试来逐步改进代码 Malcolm Davis 顾问 2000 年 11 月 内容: 从类开始 自动化单元测试 集成到构建中 了解测试的工作原理 并非完全无痛 实现 24x7 参考资料 关于作者 软件开发习惯中一个细微更改都可能会对软件质量产生巨大改进。将单元测试合并到开发过程中,然后从长远角度来看它可以节省多少时间和精力。本文通过使用代码样本说明了单元测试的种种好处,特别是使用 Ant 和 JUnit 带来的各种方便。 测试是大型开发过程中的基本原则之一。在任何职业中,验证都是一个重要部分。医生要通过验血来确诊。波音公司在研制 777 的过程中对飞机的每个组件都进行了精心测试。为什么软件开发就应该例外呢? 以前,由于在应用程序中将 GUI 和商业逻辑紧密联系在一起,这就限制了创建自动测试的能力。当我们学会通过抽象层将商业逻辑从界面中分离出来时,各个单独代码模块的自动测试就替代了通过 GUI 进行的手工测试。 现在,集成开发环境 (IDE) 能在您输入代码的同时显示错误,对于在类中快速查找方法具有智能探测功能,可以利用语法结构生成彩色代码,而且具有许多其它功能。因此,在编译更改过的代码之前,您已经全盘考虑了将构建的类,但您是否考虑过这样的修改会破坏某些功能呢? 每个开发者都碰到过更改“臭虫”。代码修改过程可能会引入“臭虫”,而如果通过用户界面手工测试代码的话,在编译完成之前是不会发现它的。然后,您就要花费几天的时间追踪由更改所引起的错误。最近在我做的一个项目中,当我把后端数据库由 Informix 更改到 Oracle 时就遇到了这种情况。大部分更改都十分顺利,但由于数据库层或使用数据库层的系统缺少单元测试,从而导致将大量时间花费在尝试解决更改“臭虫”上。我花了两天的时间查到别人代码中的一个数据库语法更改。(当然,那个人仍是我的朋友。) 尽管测试有许多好处,但一般的程序员对测试都不太感兴趣,开始时我也没有。您听到过多少次“它编译了,所以它一定能用”这种言论?但“我思,故我在”这种原则并不适用于高质量软件。要鼓励程序员测试他们的代码,过程必须简单无痛。 本文从某人学习用 Java 语言编程时所写的一个简单的类开始。然后,我会告诉您我是如何为这个类编写单元测试,以及在编写完它以后又是如何将单元测试添加到构建过程中的。最后,我们将看到将“臭虫”引入代码时发生的情况。 从一个典型类开始 第一个典型的 Java 程序一般都包含一个打印 "Hello World" 的 main()。在清单 1 中,我创建了一个 HelloWorld 对象的实例并调用 sayHello() 方法,该方法会打印这句习惯说法。 清单 1. 我的第一个 Java 应用程序 "Hello world" /* * HelloWorld.java * My first java program */ class HelloWorld { /** * Print "Hello World" */ void sayHello() { System.out.println("Hello World"); } /** * Test */ public static void main( String[] args ) { HelloWorld world = new HelloWorld(); world.sayHello(); } } main() 方法是我的测试。哦噢!我将代码、文档、测试和样本代码包含在了一个模块中。保佑 Java!但随着程序越变越大,这种开发方法很快就开始显现出了缺陷: 混乱 类接口越大,main() 就越大。类可能仅仅因为正常的测试而变得非常庞大。 代码膨胀 由于加入了测试,所以产品代码比所需要的要大。但我不想交付测试,而只想交付产品。 测试不可靠 既然 main() 是代码的一部分,main() 就对其他开发者通过类接口无法访问的私有成员和方法享有访问权。出于这个原因,这种测试方法很容易出错。 很难自动测试 要进行自动测试,我仍然必须创建另一程序来将参数传递给 main()。 类开发 对我来说,类开发是从编写 main() 方法开始的。我在编写 main() 的时候就定义类和类的用法,然后实现接口。它的一些明显的缺陷也开始显现出来。一个缺陷是我传递给 main() 来执行测试的参数个数。其次,main() 本身在进行调用子方法、设置代码等操作时变得很混乱。有时 main() 会比类实现的其余部分还要大。 更简单的过程 我原来的做法有一些很明显的缺陷。因此,让我们看看有什么别的方法可以使问题简化。我仍然通过接口设计代码并给出应用示例,正如原来的 main() 一样。不同的是我将代码放到了另一个单独的类中,而这个类恰好是我的“单元测试”。这种技术有以下几点好处: 设计类的一种机制 因为是通过接口进行开发,所以不太可能利用类的内部功能。但因为我是目标类的开发者,我有到其内部工作的“窗口”,所以测试并不是个真正的黑箱。仅凭这一点就足够推断出需要开发者本人在编写目标类的同时负责测试的开发,而不是由其他任何人代劳。 类用法的示例 通过将示例从实现中分离出来,开发者可以更快地提高速度,而且再不用在源代码上纠缠不清。这种分离还有助于防止开发者利用类的内部功能,因为这些功能将来可能已经不存在了。 没有类混乱的 main() 我不再受到 main() 的限制了。以前我得将多个参数传递给 main() 来测试不同的配置。现在我可以创建许多单独的测试类,每一个都维护各自的设置代码。 接下来我们将这个单独的单元测试对象放入构建过程中。这样,我们就可以提供自动确认过程的方法。 确保所做的任何更改都不会对其他人产生不利影响。 我们在进行源码控制之前就可以测试代码,而无需等待汇编测试或在夜晚进行的构建测试。这有助于尽早捕捉到“臭虫”,从而降低产生高质量代码的成本。 通过提供增量测试过程,我们提供了更好的实现过程。如同 IDE 帮助我们在输入时捕捉到语法或编译“臭虫”一样,增量单元测试也帮助我们在构建时捕捉到代码更改“臭虫”。 使用 JUnit 自动化单元测试 要使测试自动化,您需要一个测试框架。您可以自己开发或购买,也可以使用某些开放源代码工具,例如 JUnit。我选择 JUnit 出于以下几个原因: 不需要编写自己的框架。 它是开放源代码,因此不需要购买框架。 开放源代码社区中的其他开发者会使用它,因此可以找到许多示例。 它可以让我将测试代码与产品代码分开。 它易于集成到我的构建过程中。 测试布局 图 1 显示了使用样本 TestSuite 的 JUnit TestSuite 布局。每个测试都由若干单独的测试案例构成。每个测试案例都是一个单独的类,它扩展了 TestClass 类并包含了我的测试代码,即那些曾在 main() 中出现的代码。在该例中,我向 TestSuite 添加了两个测试:一个是 SkeletonTest,我将它用作所有新类和 HelloWorld 类的起点。 图 1. TestSuite 布局 测试类 HelloWorldTest.java 按照约定,测试类的名称中包含我所测试的类的名称,但将 Test 附加到结尾。在本例中,我们的测试类是 HelloWorldTest.java。我复制了 SkeletonTest 中的代码,并添加了 testSayHello() 来测试 sayHello()。请注意 HelloWorldTest 扩展了 TestCase。JUnit 框架提供了 assert 和 assertEquals 方法,我们可以使用这些方法来进行验证。HelloWorldTest.java 显示在清单 2 中。 清单 2. HelloWorldTest.java package test.com.company; import com.company.HelloWorld; import junit.framework.TestCase; import junit.framework.AssertionFailedError; /** * JUnit 3.2 testcases for HelloWorld */ public class HelloWorldTest extends TestCase { public HelloWorldTest(String name) { super(name); } public static void main(String args[]) { junit.textui.TestRunner.run(HelloWorldTest.class); } public void testSayHello() { HelloWorld world = new HelloWorld(); assert( world!=null ); assertEquals("Hello World", world.sayHello() ); } } testSayHello() 看上去和 HelloWorld.java 中原来的 main 方法类似,但有一个主要的不同之处。它不是执行 System.out.println 并显示结果,而是添加了一个 assertEquals() 方法。如果两个值不同,assertEquals 将打印出两个输入的值。您可能已经注意到这个方法不起作用!HelloWorld 中的 sayHello() 方法不返回字符串。如果我先写过测试,就会捕捉到这一点。我将 "Hello World" 字符串与输出流联结起来。这样,按照清单 3 中显示的那样重写了 HelloWorld,去掉 main(),并更改了 sayHello() 的返回类型。 清单 3. Hello world 测试案例。 package com.company; public class HelloWorld { public String sayHello() { return "Hello World"; } } 如果我保留 |
| # posted by myali88 @ 2005-04-10 10:54 评论(0) |
 |
面向对象设计最根本的魅力在于,它能够将真实世界领域中的实体及各自的行为建模为抽象的对象。以面向对象方式设计的系统产生了很多有效的业务对象,比如 Person、Account、Order 以及 Event。面向对象设计的缺点在于,这样的业务对象会因为混合的属性和与对象最初意图不一致的操作而变得混乱。 通过使设计者运用动态和静态横切,用一种非强制性的整洁和模块化的方法来添加对象行为,面向方面编程有效地解决了这一问题。 什么是横切? 横切 是面向方面编程的专有名词。它指的是在一个给定的编程模型中穿越既定的职责部分(比如日志记录和性能优化)的操作。在横切的世界里,横切有两种类型:动态横切和静态横切。在本文中,尽管我将简要地同时讨论二者,但我主要关注静态横切。 动态横切 动态横切 是通过 切入点 和 连接点 在一个 方面 中创建行为的过程,连接点可以在执行时横向地应用于现有对象。动态横切通常用于帮助向对象层次中的各种方法添加日志记录或身份认证。下面让我们花点时间了解一下动态横切中的一些实际概念: 方面(aspect)类似于 Java 编程语言中的类。方面定义切入点和通知(advice),并由诸如 AspectJ 这样的方面编译器来编译,以便将横切(包括动态的和静态的)织入(interweave)现有的对象中。 一个 连接点(join point) 是程序执行中一个精确执行点,比如类中的一个方法。例如,对象 Foo 中的方法 bar() 就可以是一个连接点。连接点 是个抽象的概念;不用主动定义一个连接点。 一个 切入点(pointcut) 本质上一个用于捕捉连接点的结构。例如,可以定义一个切入点来捕捉对对象 Foo 中的方法 bar() 的所有调用。和连接点相反,切入点需要在方面中定义。 通知(advice) 是切入点的可执行代码。一个经常定义的通知是添加日志记录功能,其中切入点捕捉对对象 Foo 中的 bar() 的每个调用,然后该通知动态地插入一些日志记录功能,比如捕捉 bar() 的参数。 这些概念是动态横切的核心,虽然正如我们即将看到的,它们并不全都是静态横切所必需的。请参阅 参考资料 来了解关于动态横切的更多内容。 静态横切 静态横切 和动态横切的区别在于它不修改一个给定对象的执行行为。相反,它允许通过引入附加的方法字段和属性来修改对象的 结构。此外,静态横切可以把扩展和实现附加到对象的基本结构中。 虽然现在还无法谈及静态横切的普遍使用——它看起来是 AOP 的一个相对未被探索(尽管非常具有吸引力)的特性——然而这一技术蕴含的潜力是巨大的。使用静态横切,架构师和设计者能用一种真正面向对象的方法有效地建立复杂系统的模型。静态横切允许您不用创建很深的层次结构,以一种本质上更优雅、更逼真于现实结构的方式,插入跨越整个系统的公共行为。 |
| # posted by myali88 @ 2005-04-09 14:26 评论(1) |
 |
AOP作为Spring这个轻量级的容器中很重要的一部分,得到越来越多的关注,Spring的Transaction就是用AOP来管理的,今天就通过简单的例子来看看Spring中的AOP的基本使用方法。 首先确定将要Proxy的目标,在Spring中默认采用JDK中的dynamic proxy,它只能够实现接口的代理,如果想对类进行代理的话,需要采用CGLIB的proxy。显然,选择“编程到接口”是更明智的做法,下面是将要代理的接口: public interface FooInterface { public void printFoo(); public void dummyFoo(); } 以及其一个简单的实现: public class FooImpl implements FooInterface { public void printFoo() { System.out.println("In FooImpl.printFoo"); } public void dummyFoo() { System.out.println("In FooImpl.dummyFoo"); } } 接下来创建一个Advice,在Spring中支持Around,Before,After returning和Throws四种Advice,这里就以简单的Before Advice举例: public class PrintBeforeAdvice implements MethodBeforeAdvice { public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("In PrintBeforeAdvice"); } } 有了自己的business interface和advice,剩下的就是如何去装配它们了,首先利用ProxyFactory以编程方式实现,如下: public class AopTestMain { public static void main(String[] args) { FooImpl fooImpl = new FooImpl(); PrintBeforeAdvice myAdvice = new PrintBeforeAdvice(); ProxyFactory factory = new ProxyFactory(fooImpl); factory.addBeforeAdvice(myAdvice); FooInterface myInterface = (FooInterface)factory.getProxy(); myInterface.printFoo(); myInterface.dummyFoo(); } } 现在执行程序,神奇的结果就出现了: In PrintBeforeAdvice In FooImpl.printFoo In PrintBeforeAdvice In FooImpl.dummyFoo 虽然这样能体会到Spring中AOP的用法,但这决不是值得推荐的方法,既然使用了Spring,在ApplicationContext中装配所需要 的bean才是最佳策略,实现上面的功能只需要写个简单的applicationContext就可以了,如下: "http://www.springframework.org/dtd/spring-beans.dtd"> The aop application context FooInterface myAdvice
当然,main中的代码也要进行相应的修改: public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); FooInterface foo = (FooInterface)context.getBean("foo"); foo.printFoo(); foo.dummyFoo(); } 现在运行一下,结果将和上面的运行结果完全一样,这样是不是更优雅?当需要更改实现时,只需要修改配置文件就可以了,程序中的代码不需任何改动。 但是,这时候会发现被proxy的object中的所有方法调用时都将运行advice中的before,这显然不能满足绝大多数情况下的需要,此时,只 需借用Advisor就可以了,当然要在Advisor中利用pattern设置好哪些方法需要advice,更改applicationContext 如下: "http://www.springframework.org/dtd/spring-beans.dtd"> The springeva application context class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> .*print.* FooInterface myAdvisor
主程序不需进行任何修改,运行结果已经变样了: In PrintBeforeAdvice In FooImpl.printFoo In FooImpl.dummyFoo 至此,应该已经理解了Spring中AOP的使用方法,当然Spring中AOP最重要的应用是Transaction Manager,举个这方面的applicationContext例子看看: class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> /WEB-INF/jdbc.properties class="org.springframework.jdbc.datasource.DriverManagerDataSource"> ${jdbc.driverClassName} ${jdbc.url} ${jdbc.username} ${jdbc.password} class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> smartmenu.hbm.xml ${hibernate.dialect} class="org.springframework.orm.hibernate.HibernateTransactionManager"> |
简单对象访问协议-CNXML标准教程  简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程
|
处理 SSI 文件时出错
|
版权与免责声明:① 本网转载其他媒体稿件是为传播更多的信息,此类稿件不代表本网观点,版权归原作者所有,本网不承担此类稿件侵权行为的连带责任。
② 本站原创文章,转载时请注明出自文谷及作者姓名
③在本网 BBS上发表言论者,文责自负。
④如您因版权等问题需要与本网联络,请在30日内联系 。
简单对象访问协议-CNXML标准教程  简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程
简单对象访问协议-CNXML标准教程  简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程2006-2-28 21:31:56简单对象访问协议-CNXML标准教程
|