潇's profile老肖的地盘PhotosBlogListsMore Tools Help

Blog


    November 06

    死牛肉

        今天无意中在网上瞄到google的一些面试题,其中有一道考察软件工程师的题目,死牛肉的意义。下面参考答案大谈死牛肉如何的无意义,然后怎么变废为宝云云。一声叹息,遂在网上搜了一些死牛肉,与这个题目有关的答案都是这样。悲剧啊,悲剧啊,你说他谷堂堂一个大公司,有必要给你玩啥脑筋急转弯吗?而且那个题目是考察软件工程师,我想他谷对软件工程师肯定要求很高很高,这个题目肯定是考察一个程序员的基本功和钻研能力,于是我将正确答案写在这里,供一些不明真相的群众围观,免得有些人的高谈阔论误人子弟。

        死牛肉,DEAD BEAF,这样一写,对程序员来看就亲切不少,很容易观察到,每个字母都是A-F之间,那么很容易就联想到,他应该是一个16进制的记号,那么这个记号在哪里使用呢?在MAC平台下,堆的开辟会在堆开始的地址和结束的地址中加上DEAD BEAF,中间的区段就是堆实际的内容,加上这些标志位,可以确定堆的开始和结束之处,方便了OS对堆的定位,另一方面,在堆释放的时候,OS会去检查这些标志位是否正常,如果不正常,这会采取一些措施以免恶意的程序破坏操作系统。

        同样,还有一些其它的一语双关的单词,比如坏食物,BAAD F00D,坏线BADCAB1E,这些东东都可以16进制数展现出来,在某些地方发挥着神奇的作用。

        写这篇文章想说明两点,第一,软件工程师的思维是严谨的,这个题目的目的很能测试出这一点,试想像网上大侃特侃的回答,只能显示出那个人的浅薄,和作为一个程序员的不合格。第二,尽信网不如无网,虽然这个时代我们有了我度,还有他谷,但是怎样去看待这些信息资源,和怎样去消化这些资源,是我们每个人都应该思考的,科技让人更聪明,而不是让人更傻瓜。

    October 20

    (ZT)详细解读JVM中的对象生命周期

    也许我了解了,但是我没有了解,对知识的了解不可浅尝辄止啊,转帖一篇文章。

      在JVM运行空间中,对象的整个生命周期大致可以分为7个阶段:创建阶段(Creation)、应用阶段(Using)、不可视阶段(Invisible)、不可到达阶段(Unreachable)、可收集阶段(Collected)、终结阶段(Finalized)与释放阶段(Free)。上面的这7个阶段,构成了 JVM中对象的完整的生命周期。下面分别介绍对象在处于这7个阶段时的不同情形。

        2.2.1  创建阶段

        在对象创建阶段,系统要通过下面的步骤,完成对象的创建过程:

        (1)为对象分配存储空间。

        (2)开始构造对象。

        (3)递归调用其超类的构造方法。

        (4)进行对象实例初始化与变量初始化。

        (5)执行构造方法体。

        上面的5个步骤中的第3步就是指递归地调用该类所扩展的所有父类的构造方法,一个Java类(除Object类外)至少有一个父类(Object),这个规则既是强制的,也是隐式的。你可能已经注意到在创建一个Java类的时候,并没有显式地声明扩展(extends)一个Object父类。实际上,在 Java程序设计中,任何一个Java类都直接或间接的是Object类的子类。例如下面的代码:

    public class A {      … 
    } 
    这个声明等同于下面的声明: 
    public class A extends java.lang.Object {      … 
    } 

        上面讲解了对象处于创建阶段时,系统所做的一些处理工作,其中有些过程与应用的性能密切相关,因此在创建对象时,我们应该遵循一些基本的规则,以提高应用的性能。

        下面是在创建对象时的几个关键应用规则:

        (1)避免在循环体中创建对象,即使该对象占用内存空间不大。

        (2)尽量及时使对象符合垃圾回收标准。

        (3)不要采用过深的继承层次。

        (4)访问本地变量优于访问类中的变量。

        关于规则(1)避免在循环体中创建对象,即使该对象占用内存空间不大,需要提示一下,这种情况在我们的实际应用中经常遇到,而且我们很容易犯类似的错误,例如下面的代码:

    … … 
    for (int i = 0; i < 10000; ++i) {     Object obj = new Object();     System.out.println("obj= "+ obj); 
    } 
    … … 

        上面代码的书写方式相信对你来说不会陌生,也许在以前的应用开发中你也这样做过,尤其是在枚举一个Vector对象中的对象元素的操作中经常会这样书写,但这却违反了上述规则(1),因为这样会浪费较大的内存空间,正确的方法如下所示:

    … … 
    Object obj = null; 
    for (int i = 0; i < 10000; ++i) {     obj = new Object();     System.out.println("obj= "+ obj); 
    } 
    … … 

        采用上面的第二种编写方式,仅在内存中保存一份对该对象的引用,而不像上面的第一种编写方式中代码会在内存中产生大量的对象应用,浪费大量的内存空间,而且增大了系统做垃圾回收的负荷。因此在循环体中声明创建对象的编写方式应该尽量避免。

        另外,不要对一个对象进行多次初始化,这同样会带来较大的内存开销,降低系统性能,如:

    public class A { 
        private Hashtable table = new Hashtable (); 
        public A() { 
            // 将Hashtable对象table初始化了两次 
            table = new Hashtable(); 
        } 
    } 

        正确的方式为:

    public class B { 
         private Hashtable table = new Hashtable (); 
         public B() { 
         } 
    } 

        不要小看这个差别,它却使应用软件的性能相差甚远,如图2-5所示。

    图2-5  初始化对象多次所带来的性能差别

        看来在程序设计中也应该遵从“勿以恶小而为之”的古训,否则我们开发出来的应用也是低效的应用,有时应用软件中的一个极小的失误,就会大幅度地降低整个系统的性能。因此,我们在日常的应用开发中,应该认真对待每一行代码,采用最优化的编写方式,不要忽视细节,不要忽视潜在的问题。

    当对象的创建阶段结束之后,该对象通常就会进入对象的应用阶段。这个阶段是对象得以表现自身能力的阶段。也就是说对象的应用阶段是对象整个生命周期中证明自身“存在价值”的时期。在对象的应用阶段,对象具备下列特征:

        — 系统至少维护着对象的一个强引用(Strong Reference);

        — 所有对该对象的引用全部是强引用(除非我们显式地使用了:软引用(Soft Reference)、弱引用(Weak Reference)或虚引用(Phantom Reference))。

        上面提到了几种不同的引用类型。可能一些读者对这几种引用的概念还不是很清楚,下面分别对之加以介绍。在讲解这几种不同类型的引用之前,我们必须先了解一下Java中对象引用的结构层次。

        Java对象引用的结构层次示意如图2-6所示。

    图2-6  对象引用的结构层次示意

        由图2-6我们不难看出,上面所提到的几种引用的层次关系,其中强引用处于顶端,而虚引用则处于底端。下面分别予以介绍。

        1.强引用

        强引用(Strong Reference)是指JVM内存管理器从根引用集合(Root Set)出发遍寻堆中所有到达对象的路径。当到达某对象的任意路径都不含有引用对象时,对这个对象的引用就被称为强引用。

        2.软引用

        软引用(Soft Reference)的主要特点是具有较强的引用功能。只有当内存不够的时候,才回收这类内存,因此在内存足够的时候,它们通常不被回收。另外,这些引用对象还能保证在Java抛出OutOfMemory 异常之前,被设置为null。它可以用于实现一些常用资源的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory。再者,软可到达对象的所有软引用都要保证在虚拟机抛出OutOfMemoryError之前已经被清除。否则,清除软引用的时间或者清除不同对象的一组此类引用的顺序将不受任何约束。然而,虚拟机实现不鼓励清除最近访问或使用过的软引用。下面是软引用的实现代码:

    … … 
    import java.lang.ref.SoftReference; 
    … 
    A a = new A(); 
    … 
    // 使用 a 
    … 
    // 使用完了a,将它设置为soft 引用类型,并且释放强引用; 
    SoftReference sr = new SoftReference(a); 
    a = null; 
    … 
    // 下次使用时 
    if (sr!=null) { 
         a = sr.get(); 
    } 
    else{ 
         // GC由于内存资源不足,可能系统已回收了a的软引用, 
         // 因此需要重新装载。 
         a = new A(); 
         sr=new SoftReference(a); 
    } 
    … … 

        软引用技术的引进,使Java应用可以更好地管理内存,稳定系统,防止系统内存溢出,避免系统崩溃(crash)。因此在处理一些占用内存较大而且声明周期较长,但使用并不频繁的对象时应尽量应用该技术。正像上面的代码一样,我们可以在对象被回收之后重新创建(这里是指那些没有保留运行过程中状态的对象),提高应用对内存的使用效率,提高系统稳定性。但事物总是带有两面性的,有利亦有弊。在某些时候对软引用的使用会降低应用的运行效率与性能,例如:应用软引用的对象的初始化过程较为耗时,或者对象的状态在程序的运行过程中发生了变化,都会给重新创建对象与初始化对象带来不同程度的麻烦,有些时候我们要权衡利弊择时应用。

        3.弱引用

        弱引用(Weak Reference)对象与Soft引用对象的最大不同就在于:GC在进行回收时,需要通过算法检查是否回收Soft引用对象,而对于Weak引用对象, GC总是进行回收。因此Weak引用对象会更容易、更快被GC回收。虽然,GC在运行时一定回收Weak引用对象,但是复杂关系的Weak对象群常常需要好几次GC的运行才能完成。Weak引用对象常常用于Map数据结构中,引用占用内存空间较大的对象,一旦该对象的强引用为null时,对这个对象引用就不存在了,GC能够快速地回收该对象空间。与软引用类似我们也可以给出相应的应用代码:

    … … 
    import java.lang.ref.WeakReference; 
    … 
    A a = new A(); 
    … 
    // 使用 a 
    … 
    // 使用完了a,将它设置为weak 引用类型,并且释放强引用; 
    WeakReference wr = new WeakReference (a); 
    a = null; 
    … 
    // 下次使用时 
    if (wr!=null) { 
        a = wr.get(); 
    } 
    else{ 
        a = new A(); 
        wr = new WeakReference (a); 
    } 
    … … 

        弱引用技术主要适用于实现无法防止其键(或值)被回收的规范化映射。另外,弱引用分为“短弱引用(Short Week Reference)”和“长弱引用(Long Week Reference)”,其区别是长弱引用在对象的Finalize方法被GC调用后依然追踪对象。基于安全考虑,不推荐使用长弱引用。因此建议使用下面的方式创建对象的弱引用。

    … … 
    WeakReference wr = new WeakReference(obj); 
    或 
    WeakReference wr = new WeakReference(obj, false); 
    … … 

        4.虚引用

        虚引用(Phantom Reference)的用途较少,主要用于辅助finalize函数的使用。Phantom对象指一些执行完了finalize函数,并且为不可达对象,但是还没有被GC回收的对象。这种对象可以辅助finalize进行一些后期的回收工作,我们通过覆盖Reference的clear()方法,增强资源回收机制的灵活性。虚引用主要适用于以某种比 java 终结机制更灵活的方式调度 pre-mortem 清除操作。

        &注意  在实际程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

        2.2.3  不可视阶段

        在一个对象经历了应用阶段之后,那么该对象便处于不可视阶段,说明我们在其他区域的代码中已经不可以再引用它,其强引用已经消失,例如,本地变量超出了其可视范围,如下所示。

    … … 
    public void process () { 
        try { 
             Object obj = new Object(); 
             obj.doSomething(); 
         } catch (Exception e) { 
         e.printStackTrace(); 
         } 
         while (isLoop) { // ... loops forever 
          // 这个区域对于obj对象来说已经是不可视的了 
             // 因此下面的代码在编译时会引发错误 
             obj.doSomething();  
         } 
    } 
    … … 

        如果一个对象已使用完,而且在其可视区域不再使用,此时应该主动将其设置为空(null)。可以在上面的代码行obj.doSomething();下添加代码行obj = null;,这样一行代码强制将obj对象置为空值。这样做的意义是,可以帮助JVM及时地发现这个垃圾对象,并且可以及时地回收该对象所占用的系统资源。

        2.2.4  不可到达阶段

        处于不可到达阶段的对象,在虚拟机所管理的对象引用根集合中再也找不到直接或间接的强引用,这些对象通常是指所有线程栈中的临时变量,所有已装载的类的静态变量或者对本地代码接口(JNI)的引用。这些对象都是要被垃圾回收器回收的预备对象,但此时该对象并不能被垃圾回收器直接回收。其实所有垃圾回收算法所面临的问题是相同的——找出由分配器分配的,但是用户程序不可到达的内存块。

        2.2.5  可收集阶段、终结阶段与释放阶段

        对象生命周期的最后一个阶段是可收集阶段、终结阶段与释放阶段。当对象处于这个阶段的时候,可能处于下面三种情况:

        (1)垃圾回收器发现该对象已经不可到达。

        (2)finalize方法已经被执行。

        (3)对象空间已被重用。

        当对象处于上面的三种情况时,该对象就处于可收集阶段、终结阶段与释放阶段了。虚拟机就可以直接将该对象回收了。

    July 26

    SOAP协议规范

    1. 简介

    SOAP以XML形式提供了一个简单、轻量的用于在分散或分布环境中交换结构化和类型信息的机制。SOAP本身并没有定义任何应用程序语义,如编程模型或特定语义的实现;实际上它通过提供一个有标准组件的包模型和在模块中编码数据的机制,定义了一个简单的表示应用程序语义的机制。这使SOAP能够被用于从消息传递到RPC的各种系统。

    SOAP包括三个部分

    • SOAP封装(见第4节)结构定义了一个整体框架用来表示消息中包含什么内容,谁来处理这些内容以及这些内容是可选的或是必需的。
    • SOAP编码规则(见第5节)定义了用以交换应用程序定义的数据类型的实例的一系列机制。
    • SOAP RPC表示(见第7节)定义了一个用来表示远程过程调用和应答的协定。

    虽然这三个部分都作为SOAP的一部分一起描述,但它们在功能上是相交的。特别的,封装和编码规则是在不同的名域中定义的,这种模块性的定义方法增加了简单性在SOAP封装,SOAP编码规则和SOAPRPC协定之外,这个规范还定义了两个协议的绑定,描述了在有或没有HTTP扩展框架[6]的情况下,SOAP消息如何包含在HTTP消息[5]中被传送。

    1.1 设计目标

    SOAP的主要设计目标是简单性和可扩展性,这意味着传统的消息系统和分布对象系统的某些性质不是SOAP规范的一部分。这些性质包括:

    • 分布式碎片收集
    • 成批传送消息
    • 对象引用(要求分布式碎片收集)
    • 激活机制(要求对象引用)
    1.2 符号约定

    这篇文章中的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT","SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", 和"OPTIONAL"的解释在RFC-2119 [2]中。这篇文章中用到的名域前缀 "SOAP-ENV" 和"SOAP-ENC"分别与"http://schemas.xmlsoap.org/soap/envelope/" 和"http://schemas.xmlsoap.org/soap/encoding/"关联。整篇文档中,名域前缀“xsi”被假定为与 URI"http://www.w3.org/1999/XMLSchema-instance“(在XMLSchema规范[11]定义)相连。类似的,名域前缀”xsd“被假定为与URI"http://www.w3.org/1999/XMLSchema"(在[10]中定义)相连。名域前缀 ”tns“用来表示任意名域。所有其它的名域前缀都只是例子。
    名域URI的基本形式”some-URI“表示某些依赖于应用程序或上下文的URI[4]。这个规范用扩展BNF(在RFC-2616[5] 描述)描述某些结构。

    1.3 SOAP消息举例

    在这个例子中,GetLastTradePrice SOAP 请求被发往StockQuote服务。这个请求携带一个字符串参数和ticker符号,在SOAP应答中返回一个浮点数。XML名域用来区分SOAP标志符和应用程序特定的标志符。这个例子说明了在第6节中定义的HTTP绑定。如果SOAP中管理XML负载的规则完全独立于HTTP是没有意义的,因为事实上该负载是由HTTP携带的。在Appendix A中有更多的例子。

    例1 在HTTP请求中嵌入SOAP消息

    POST /StockQuote HTTP/1.1
    Host:
    www.stockquoteserver.com
    Content-Type: text/xml;
    charset="utf-8"
    Content-Length: nnnn
    SOAPAction:
    "Some-URI"
    <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
    <m:GetLastTradePrice xmlns:m="Some-URI">
    <symbol>DIS</symbol>
    </m:GetLastTradePrice>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

    下面是一条应答消息,包括HTTP消息,SOAP消息是其具体内容 :

    例2 在HTTP应答中嵌入SOAP消息

    HTTP/1.1 200 OK
    Content-Type: text/xml;
    charset="utf-8"
    Content-Length:
    nnnn
    <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    <SOAP-ENV:Body>
    <m:GetLastTradePriceResponse xmlns:m="Some-URI">
    <Price>34.5</Price>
    </m:GetLastTradePriceResponse>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

    2. SOAP消息交换模型

    SOAP 消息从发送方到接收方是单向传送,但正如上面显示的,SOAP消息经常以请求/应答的方式实现。SOAP实现可以通过开发特定网络系统的特性来优化。例如,HTTP绑定(见第6节)使SOAP应答消息以HTTP应答的方式传输,并使用同一个连接返回请求。不管SOAP被绑定到哪个协议,SOAP消息采用所谓的”消息路径“发送,这使在终节点之外的中间节点可以处理消息。一个接收SOAP消息的SOAP应用程序必须按顺序执行以下的动作来处理消息:识别应用程序想要的SOAP消息的所有部分(见4.2.2节)检验应用程序是否支持第一步中识别的消息中所有必需部分并处理它。如果不支持,则丢弃消息(见4.4节)。在不影响处理结果的情况下,处理器可能忽略第一步中识别出的可选部分。如果这个SOAP应用程序不是这个消息的最终目的地,则在转发消息之前删除第一步中识别出来的所有部分。为了正确处理一条消息或者消息的一部分,SOAP处理器需要理解:所用的交换方式(单向,请求/应答,多路发送等等),这种方式下接收者的任务,RPC机制(如果有的话)的使用(如第7节中所述),数据的表现方法或编码,还有其它必需的语义。尽管属性(比如SOAP encodingstyle,见4.1.1节)可以用于描述一个消息的某些方面,但这个规范并不强制所有的接收方也必须有同样的属性并取同样的属性值。举个例子,某一特定的应用可能知道一个元素表示一条遵循第7节约定的RPC请求,但是另外一些应用可能认为指向该元素的所有消息都用单向传输,而不是类似第7节的请求应答模式。
    (译者注:交互双方的SOAP消息并不一定要遵循同样的格式设定,而只需要以一种双方可理解的格式交换信息就可以了)

    3. 与XML的关系

    所有的SOAP消息都使用XML形式编码(更多有关XML的信息请见[7])一个SOAP应用程序产生的消息中,所有由SOAP定义的元素和属性中必须包括正确的名域。SOAP应用程序必须能够处理它接收到的消息中的SOAP名域(见4.4节),并且它可以处理没有SOAP名域的SOAP消息,就象它们有正确的名域一样。SOAP定义了两个名域(更多有关XML名域的信息请见[8])

    • SOAP封装的名域标志符是"http://schemas.xmlsoap.org/soap/envelope/"
    • SOAP的编码规则的名域标志符是"http://schemas.xmlsoap.org/soap/encoding/"

    SOAP 消息中不能包含文档类型声明,也不能包括消息处理指令。[7] SOAP使用"ID"类型"id"属性来指定一个元素的唯一的标志符,同时该属性是局部的和无需校验的。SOAP使用"uri-reference"类型的"href"属性指定对这个值的引用,同时该属性是局部的和无需校验的。这样就遵从了XML规范[7],XMLSchema规范[11]和XML连接语言规范[9]的风格。除了SOAP mustUnderstand 属性(见4.2.3节)和SOAPactor属性(见4.2.2节)之外,一般允许属性和它们的值出现在XML文档实例或Schema中(两者效果相同)。也就是说,在DTD或Schema中声明一个缺省值或固定值和在XML文档实例中设置它的值在语义上相同。

    4. SOAP封装

    SOAP消息是一个XML文档,包括一个必需的SOAP封装,一个可选的SOAP头和一个必需的SOAP体。在这篇规范剩余部分中,提到SOAP消息时就是指这个XML文档。这一节中定义的元素和属性的名域标志符为:

    "http://schemas.xmlsoap.org/soap/envelope/" 。一个SOAP消息包括以下部分:1.在表示这个消息的XML文档中,封装是顶层元素。2.应用SOAP交换信息的各方是分散的且没有预先协定,SOAP 头提供了向SOAP消息中添加关于这条SOAP消息的某些要素(feature)的机制。SOAP定义了少量的属性用来表明这项要素(feature)是否可选以及由谁来处理。(见4.2节)3.SOAP体是包含消息的最终接收者想要的信息的容器(见4.3节)。SOAP为SOAP体定义了一个Fault 元素用来报告错误信息。语法规则如下所示:

    封装

    1. 元素名是 "Envelope"
    2. 在SOAP消息中必须出现。
    3. 可以包含名域声明和附加属性。如果包含附加属性,这些属性必须限定名域。类似的,"Envelope"可以包含附加子元素,这些也必须限定名域且跟在SOAP体元素之后。

    SOAP头 (见4.2节)

    1. 元素名是"Header"
    2. 在SOAP消息中可能出现。如果出现的话,必须是SOAP封装元素的第一个直接子元素。
    3. SOAP头可以包含多个条目,每个都是SOAP头元素的直接子元素。所有SOAP头的直接子元素都必须限定名域。

    SOAP体 (见4.3节)

    1. 元素名是"Body"
    2. 在SOAP消息中必须出现且必须是SOAP封装元素的直接子元素。它必须直接跟在SOAP头元素(如果有)之后。否则它必须是SOAP封装元素的第一个直接子元素。
    3. SOAP体可以包括多个条目,每个条目必须是SOAP体元素的直接子元素。SOAP体元素的直接子元素可以限定名域。SOAP定义了SOAPFault元素来表示错误信息。
    4.1.1 SOAP encodingStyle属性

    EncodingStyle 全局属性用来表示SOAP消息的序列化规则。这个属性可以在任何元素中出现,作用范围与名域声明的作用范围很相似,为这个元素的内容和它的所有没有重载此属性的子元素。SOAP消息没有定义缺省编码。属性值是一个或多个URI的顺序列表,每个URI确定了一种或多种序列化规则,用来不同程度反序列化 SOAP消息,举例如下:

    "http://schemas.xmlsoap.org/soap/encoding/"
    "http://my.host/encoding/restricted http://my.host/encoding/"
    ""

    第 5节中定义的序列化规则由URI"http://schemas.xmlsoap.org/soap/encoding/" 确定。使用这个特定序列化规则的消息应该用encodingStyle属性说明这一点。另外,所有以"http://schemas.xmlsoap.org/soap/encoding/"开头的URI中的序列化规则与第5节中定义的SOAP编码规则相一致。一个零长度的URI("")明确显示所含元素没有任何编码形式。这可以用来取消上一级元素的所有编码声明。

    4.1.2 封装版本模型

    SOAP 没有定义常规的基于主版本号和辅版本号的版本形式。SOAP消息必须有一个封装元素与名域"http://schemas.xmlsoap.org /soap/envelope/"关联。如果SOAP应用程序接收到的SOAP消息中的SOAP封装元素与其他的名域关联,则视为版本错误,应用程序必须丢弃这个消息。如果消息是通过HTTP之类的请求/应答协议收到的,应用程序必须回答一个SOAP VersionMismatch 错误信息(见4.4节)。

    4.2 SOAP头

    SOAP 为相互通信的团体之间提供了一种很灵活的机制:在无须预先协定的情况下,以分散但标准的方式扩展消息。可以在SOAP头中添加条目实现这种扩展,典型的例子有认证,事务管理,支付等等。头元素编码为SOAP封装元素的第一个直接子元素。头元素的所有直接子元素称作条目。条目的编码规则如下:

    一个条目有它的完整的元素名(包括名域URI和局部名)确定。SOAP头的直接子元素必须有名域限制。
    SOAP encodingStyle属性可以用来指示条目所用的编码形式(见4.1.1节)
    SOAP mustUnderstand属性(见4.2.3节)和SOAPactor属性(见4.2.2节)可以用来指示如何处理这个条目以及由谁来处理。(见4.2.1节)

    4.2.1 使用头属性

    这一节中定义的SOAP头属性确定了SOAP消息的接收者应该怎样按第2节中所述的方式处理消息。产生SOAP消息的SOAP应用程序,应该仅仅在SOAP 头元素的直接子元素中使用这些SOAP头属性。SOAP消息的接收者必须忽略所有不在SOAP头元素的直接子元素中SOAP头属性。下面的例子是一个 SOAP头,包括一个元素标志符"Transaction","mustUnderstand"取值为"1"和数值5。这应该以如下方式编码:

    <SOAP-ENV:Header>
    <t:Transaction
    xmlns:t="some-URI" SOAP-ENV:mustUnderstand="1">
    5
    </t:Transaction>
    </SOAP-ENV:Header>

    4.2.2 SOAP actor属性

    一个SOAP消息从始节点到终节点的过程中,可能沿着消息路径经过一系列SOAP中间节点。一个SOAP中间节点是一个可以接收转发SOAP消息的应用程序。中间节点和终节点由URI区分。可能SOAP消息的终节点并不需要所有部分,而在消息路径上的一个和几个中间节点可能需要这些内容。头元素的接收者扮演的角色类似于一个过滤器,防止这些只发给本接受者的消息部分扩散到其它节点。即一个头元素的接收者必须不转发这些头元素到SOAP消息路径上的下一个应用程序。同样的,接收者可能插入一个相似的头元素。SOAP actor全局属性可以用于指示头元素的接收者。SOAP actor属性的值是一个URI。

    URI "http://schemas.xmlsoap.org/soap/actor/next"指出了第一个处理这个消息的SOAP应用程序需要这个头元素。这类似于HTTP头中用Connection域表示hop-by-hop范围模型。省略SOAP actor属性表示接收者是SOAP消息的终节点。如果这个属性要生效,它必须出现在SOAP消息实例中。(见第3节和4.2.1节)

    4.2.3 SOAP mustUnderstand属性

    SOAP mustUnderstand全局属性用来指示接受者在处理消息时这个条目是否必须处理。条目的接收者由SOAP actor属性定义(见4.2.2节)。MustUnderstand属性的值是"1" 或 "0"。缺少SOAP mustUnderstand属性在语义上等同于它的值为"0"。如果一个头元素的SOAP mustUnderstand属性的值是"1",那么条目的接受者必须或者遵守语义(如以元素的全名传送)并按照语义正确的处理,或者放弃处理消息(见4.4节)。SOAP mustUnderstand 属性考虑了消息演变的准确性(robust evolution)。必须假定包含SOAP mustUnderstand属性且值为"1"的元素以某种方式修改了它们的父元素或同层元素的语义。以这种方式连接元素确保了语义上的变化不会被那些不能完全理解它的接收者忽略。如果这个属性要生效,它必须出现在SOAP消息实例中。(见第3节和4.2.1节)

    4.3 SOAP体

    SOAP 体元素提供了一个简单的机制,使消息的最终接收者能交换必要的信息。使用体元素的典型情况包括配置RPC请求和错误报告。体元素编码为SOAP封装元素的直接子元素。如果已经有一个头元素,那么体元素必须紧跟在头元素之后,否则它必须是SOAP封装元素的第一个直接子元素。体元素的所有直接子元素称作体条目,每个体条目在SOAP体元素中编码为一个独立的元素。条目的编码规则如下:

    • 一个条目由它的元素全名(包括名域URI和局部名)确定。SOAP体元素的直接子元素可能是名域限制的。
    • SOAP encodingStyle属性可能用来指示条目(见4.1.1节)的编码方式。
    • SOAP定义了一个Fault条目用来报告错误信息。(见4.4节)
    4.3.1 SOAP头和体的关系

    虽然头和体定义为独立的元素,它们实际上是有关系的。体条目和头条目的关系如下:体条目在语义上等同于actor属性为缺省值且mustUnderstand属性值为"1"的头条目。不使用actor属性则表示缺省的actor。(见4.2.2节)

    4.4 SOAP错误

    SOAP错误元素用于在SOAP消息中携带错误和(或)状态信息。如果有SOAP错误元素,它必须以以体条目的方式出现,并且在一个体元素中最多出现一次。SOAP错误元素定义了以下四个子元素:

    • faultcode
      faultcode元素给软件提供了一个识别此错误的算法机制。SOAP错误元素必须有faultcode子元素,并且它的值必须是一个合法的名(在[8]节定义)。SOAP定义一些SOAP faultcode描述基本的SOAP错误(见4.4.1节)。
    • faultstring
      faultstring元素提供了一个错误解释,而不是为了软件处理。faultstring元素类似于HTTP中定义(见[5],第6.1节)的 'Reason-Phrase'。SOAP错误元素必须有faultstring子元素,并且它应该提供一些错误本质的解释信息。
    • faultactor
      faultactor元素提供了在消息路径上是谁导致了错误发生的信息(见第2节)。它类似于SOAP actor属性(见4.2.2节),只是SOAP actor指的是头条目的目的地,faultactor指的是错误的来源。faultactor属性的值是用来区分错误来源的URI。不是SOAP消息的最终目的地的应用程序必须在SOAP Fault元素中包含faultactor元素。消息的最终目的地可以使用faultactor元素明确的指示是它产生了这个错误(参见下面的detail元素)
    • detail
      detail元素用来携带与Body元素有关的应用程序所要的错误信息。如果Body元素的内容不能被成功的处理,则必须包含detail子元素。它不能用来携带属于头条目的错误信息。头条目的详细出错信息必须由头条目携带。Fault元素中没有detail元素表示这个错误与Body元素的处理无关。在有错误的时候,这可以用来区分Body元素有没有被正确的处理。detail元素的所有直接子元素称作detail条目,并且每个detail条目在 detail元素中编码为独立的元素。detail条目的编码规则如下(参见例10):一个detail条目由它的元素全名(包括名域URI和局部名)确定。SOAP体元素的直接子元素可能是名域限制的。SOAP encodingStyle属性可能用来指示detail条目(见4.1.1节)的编码方式。也可以有其它的Fault子元素,只要它们是名域限制的。
    4.4.1 SOAP 错误代码

    在描述这个规范中定义的错误时,这一节中定义的Faultcode值必须用在faultcode元素中。这些faultcode值得名域标志符为"http://schemas.xmlsoap.org/soap/envelope/"。定义这个规范之外的方法时推荐(不要求)使用这个名域。缺省的SOAP faultcode值以可扩展的方式定义,允许定义新的SOAP faultcode值,并与现有的faultcode值向后兼容。使用的机制类似于HTTP中定义的1xx, 2xx,3xx等基本的状态类(见[5]第10节),不过,它们定义为XML合法名(见 [8] 第3节 ),而不是整数。字符"."(点)作为faultcode的分隔符,点左边的错误代码比右边的错误代码更为普通。如:

    Client.Authentication

    这篇文档中定义的faultcode值是:

    名称
    含义

    VersionMismatch
    处理方发现SOAP封装元素有不合法的名域(见4.1.2节)

    MustUnderstand
    处理方不理解或者不服从一个包含值为"1"的

    mustUnderstand
    属性的 SOAP头元素的直接子元素。(见4.2.3节)

    Client

    Client错误类表示消息的格式错误或者不包含适当的正确信息。例如,消息可能缺少正确的认证和支付信息。一般地,它表示消息不能不作修改就重发。参见4.4节

    SOAP Fault detail子元素的描述。

    Server

    Server错误类表示由于消息的处理过程而不是消息的内容本身使得消息消息不能正确的处理。例如,处理消息时可能要与其它处理器通信,但它没有响应。这个消息可能在迟一点的时间处理成功。 SOAP Fault子元素的详细信息参见4.4节

    5. SOAP编码

    SOAP 编码格式基于一个简单的类型系统,概括了程序语言,数据库和半结构化数据等类型系统的共同特性。一个类型或者是一个简单的(标量的)类型,或者是由几个部分组合而成的复合类型,其中每个部分都有自己的类型。以下将详细描述这些类型。这一节定义了类型化对象的序列化规则。它分两个层次。首先,给定一个与类型系统的符号系统一致的Schema(译者注:这里的schema不是符合XML语法的schema,而仅仅表示广义的用于表示消息结构的定义方式),就构造了XML语法的Schema。然后,给定一个类型系统的Schema和与这个Schema一致的特定的值,就构造了一个XML文档实例。反之,给定一个依照这些规则产生的XML文档实例和初始的Schema,就可以构造初始值的一个副本。这一节中定义的元素和属性的名域标志符为"http://schemas.xmlsoap.org/soap/encoding/"。下面的例子都假定在上一层的元素中声明了名域。
    鼓励使用这一节中描述的数据模型和编码方式,但也可以在SOAP中使用其他的数据模型和编码方式。(见4.1.1节)

    5.1 XML中的编码类型规则

    XML允许非常灵活的数据编码方式。SOAP定义了一个较小的规则集合。这一节在总的层次上定义了这些编码规则,下一节将描述特定类型的编码规则的细节。这一节定义的编码规则可以与第7节中所述的RPC调用和应答映射结合使用。下面的术语用来描述编码规则:

    • 一个"value"是一个字符串,类型(数字,日期,枚举等等)的名或是几个简单值的组合。所有的值都有特定的类型。
    • 一个"simple value"没有名部分, 如特定的字符串,整数,枚举值等等。
    • 一个"compound value"是相关的值的结合,如定单,股票报表,街道地址等等。在"compound value"中,每个相关的值都潜在的以名,序数或这两者来区分。这叫作"a ccessor"。复合值的例子有定单和股票报表等等。数组也是复合值。在复合值中,多个accessor有相同的名是允许的,例如RDF就是这样做的。
    • 一个"array"是一个复合值,成员值按照在数组中的位置相互区分。
    • 一个"struct"也是一个复合值,成员值之间的唯一区别是accessor名,accessor名互不相同。
    • 一个"simple type"是简单值的类,如叫做"string" "integer"的类,还有枚举类等等。
    • 一个"compound type"是复合值的类。复合类型的例子有定单类,它们有相同的accessor名(shipTo, totalCost等),但可能会有不同的值(可能以后被设置为确定的值)。

    在复合类型中,如果类型内的accessor名互不相同,但是可能与其他类型中的accessor名相同,即,accessor名加上类型名形成一个唯一的标志符,这个名叫作"局部范围名"。如果名是直接或间接的基于URI的一部分,那么不管它出现在什么类型中,这个名本身就可以唯一标志这个 accessor,这样的名叫作"全局范围名"。给定了schema中相关的值的序列化信息,就可能确定某些值只与某个accessor的一个实例有关。其它情况下则无法确定。当且仅当一个accessor引用一个值,这个值才能被视为"single-reference",如果有不止一个 accessor引用它,那么就将它视为"multi-reference"。注意,可能一个确定的值在一个schema中是"single- reference",而在另一个schema中是"multi-reference"。在语句构成上,一个元素可能是"independent" 或 "embedded"。一个独立的元素指出现在序列化最顶层的任何元素。所有其它元素都是嵌入元素。虽然用xsi:type属性可以使值的结构和类型变为自描述的,但是序列化规则允许值的类型仅仅参照schema而定。这样的schema可能使用"XML Schema Part 1: Structures" [10]和"XML Schema Part 2: Datatypes" [11]中描述的符号系统,也可能使用其它符号系统。注意,虽然序列化规则可以用于除了数组和结构之外的复合类型,但是许多schema仅仅包含数组和结构类型。序列化规则如下:

    所有的值以元素内容的形式表示。一个multi-reference值必须表示为一个独立元素的内容,而一个single-reference值最好不要这样表示(也可以这样表示)。对于每个具有值的元素,值的类型时必须用下述三种方式之一描述:

    • 所属元素实例有xsi:type属性
    • 所属元素是一个有SOAP-ENC:arrayType 属性(该属性可能是缺省的)的元素的子元素,或者
    • 所属元素的名具有特定的类型,类型可以由schema确定。

    一个简单值表示为字符数据,即没有任何子元素。每个简单值必须具有一个类型,这个类型或者是XML Schemas Specification, part 2 [11]有的类型,或者具有源类型(参见5.2节)。一个复合值编码成一个元素的序列,每个accessor用一个嵌入元素表示,该元素的元素名和 accessor的名一致。如果accessor的名是局部于其所属的类型的,则该元素的元素名不是合格的,否则对应的元素名是合格的。(参见5.4节)
    一个multi-reference的简单值或复合值编码成一个独立的元素,这个元素包含一个局部的无需校验的属性,属性名为"id",类型为"ID" (依照XML Specification [7])。值的每个accessor对应一个空元素,该元素有一个局部的,无需校验的属性,属性名为"href",类型为" uri-reference "(依照XML Schema Specification [11]),"href"属性的值引用了相对应的独立元素的URI标志符。字符串和字符数组表示为multi-reference的简单类型,但是特殊的规则使它们在普通的情况下能被更有效的表示(参见5.2.1节和5.2.3节)。字符串和字符数组值的accessor可能有一个名字为"id",类型为"ID"(依照XML Specification [7])的属性。如果这样,所有这个值的所有其它accessor编码成一个空元素,这个元素有一个局部的,无需校验的属性,属性名为"href",类型为" uri-reference "(依照XML Schema Specification [11]),"href"属性的值引用了包含这个值的元素的URI标志符。编码时允许一个值有多个引用,就像多个不同的值有多个引用一样,但这仅在从上下文可以知道这个XML文档实例的含义没有改变时才可使用。数组是复合值(参见5.4.2节)。SOAP数组定义为具有类型"SOAP- ENC:Array"或从它衍生的类型.

    SOAP数组可以时一维或多维,它们的成员以序数位置相互区分。一个数组值表示为反映这个数组的一系列元素,数组成员按升序出现。对多维数组来说,右边的这一维变化最快。每个成员元素命名为一个独立元素。(见规则2)SOAP数组可以是single-reference 或multi-reference值,因此可以表示为嵌入元素或独立元素的内容。SOAP数组必须包含一个"SOAP-ENC:arrayType"属性,它的值指定了包含元素的类型和数组的维数。"SOAP-ENC:arrayType"属性的值定义如下:

    arrayTypeValue = atype asize
    atype = QName *( rank )
    rank = "[" *( "," ) "]"
    asize = "[" #length "]"
    length = 1*DIGIT

    • "atype"结构是被包含元素的类型名,它表示为QName并且作为类型限制在XML元素声明的
    • "type"属性中出现(这意味着被包含元素的所有值都要与该类型一致,即在SOAP-ENC:a rrayType中引用的类型必须是每个数组成员的类型或超类型)。在arrays of arrays or "jagged arrays"的情况下,类型组件编码为"innermost"类型且在从第一层开始的嵌套数组的每一层中,类型名后都跟随一个rank结构。多维数组编码时从第一维起,每一维之间用逗号隔开。
    • "asize" 结构包含一个以逗号分隔的列表,数值0,1或其它整数表示数组每一维的长度。整数0表示没有指定详细的大小,但是可能在检查数组实际成员的大小后确定。例如,一个5个成员的整型数组的arrayTypeValue值为"int[][5]",它的atype值是int[]",asize值是"[5]"。同样,一个3个成员的两维整型数组的arrayTypeValue值为"int[,][3]",它的atype值是int[,]",asize值是" [3]"。

    一个SOAP数组成员可能包含一个"SOAP-ENC:offset"属性表示这一项在整个数组中的位置偏移值。这被用来指示一个部分储值数组(见5.4.2.1节)的位置偏移值。同样,一个数组成员可能包含一个"SOAP- ENC:position"属性表示这一项在整个数组中的位置,这被用来描述稀疏数组(见5.4.2.2节)的成员。"SOAP-ENC:offset" 和"SOAP-ENC:position"属性值的定义如下:

    arrayPoint = "[" #length "]"
    偏移值和位置从0开始
    NULL值或缺省值可能通过省略accssor元素来表示。NULL值也可能通过一个包含值为'1'的xsi:null属性的accssor元素来表示,其它的依赖于应用程序的属性和值也可能用来表示NULL值。注意,规则2允许独立的元素和数组成员名不同于值类型的元素。

    5.2 简单类型

    SOAP采用了"XML Schema Part 2: Datatypes"规范[11]"Built-in datatypes"节中的所有类型作为简单类型,包括值和取值范围。例如:

    类型
    举例

    int
    58502

    float
    314159265358979E+1

    negativeInteger
    -32768

    string
    Louis "Satchmo" Armstrong

    在XML Schema规范中声明的数据类型可以直接用在元素schema中,也可以使用从这些类型衍生的新类型。一个schema和对应的具有这些类型的元素的数据实例的例子如下所示:

    <element name="age" type="int"/>
    <element name="height" type="float"/>
    <element name="displacement" type="negativeInteger"/>
    <element name="color">
    <simpleType base="xsd:string">
    <enumeration value="Green"/>
    <enumeration value="Blue"/>
    </simpleType>
    </element>
    <age>45</age>
    <height>5.9</height>
    <displacement>-450</displacement>
    <color>Blue</color>

    所有简单值必须编码为元素的内容,它的类型或者在"XML Schema Part 2: Datatypes"规范[11]中定义过,或者是基于一个用XML Schema规范提供的机制能推衍生出的类型。如果一个简单值编码为独立元素或异质数组成员,那么有一个对应于数据类型的元素声明将会很方便。因为"XML Schema Part 2: Datatypes"规范[11]包括了类型定义,但是不包括对应的元素声明,SOAP-ENC schema和名域为每个简单数据类型声明了一个元素,如<SOAP-ENC:int id="int1">45</SOAP-ENC:int>

    5.2.1 字符串

    字符串数据类型的定义在"XML Schema Part 2: Datatypes"规范[11]中。注意,这不同于许多数据库和程序语言中的"string"类型,特别的,字符串数据类型可能禁止某些在那些语言中允许的字符。(这些值必须用xsd:string之外的数据类型表示)一个字符串可能编码为一个single-reference 或 multi-reference值。包含字符串值的元素可能有一个"id"属性。附加的accessor元素可能有对应的"href"属性。
    例如,同一字符串的两个accessor可能以如下形式出现:

    <greeting id="String-0">Hello</greeting>
    <salutation href="#String-0"/>

    但是,如果两个accessor参考同一字符串实例(或字符串的子类型),这不是一个实质问题,它们可以编码为两个single-reference值,如下所示:

    <greeting>Hello</greeting>
    <salutation>Hello</salutation>

    这个例子的schema片断如下所示:

    <element name="greeting" type="SOAP-ENC:string"/>
    <element name="salutation" type="SOAP-ENC:string"/>

    在这个例子中,SOAP-ENC:string类型用作元素的类型,这是声明数据类型是"xsd:string"且允许"id" 和"href"属性的元素的简便方法。精确定义参见SOAP编码schema。Schemas可以使用这些源自SOAP编码schema的声明,但也可以不这样做。

    5.2.2 Enumerations

    "XML Schema Part 2: Datatypes"规范 [11] 定义了"enumeration."机制。SOAP数据模型直接采用了这种机制。但是,由于程序语言和其它语言在定义枚举时通常有些不同,所以我们在这里详细阐述了它的概念并描述了一个列表成员的可能取的值是如何编码的。"Enumeration"作为一个概念表示不同的名字的集合。一个特定的枚举就是对应于特定的基类型的不同的值的列表。例如,颜色集合("Green", "Blue", "Brown")可以定义为基于字符串类型的枚举,("1", "3", "5")可能是一个基于整型数的枚举,等等。"XML Schema Part 2: Datatypes" [11]支持除了布尔型以外所有简单类型的枚举。"XML Schema Part 1: Structures"规范[10]的语言可以用来定义枚举类型。如果schema由另一个没有特定基类型适用的符号系统生成,就使用"string"。在下面schema的例子中,"EyeColor"定义为字符串,可能的值是"Green", "Blue", 或"Brown"的枚举,数据实例按照schema显示如下。

    <element name="EyeColor" type="tns:EyeColor"/>
    <simpleType name="EyeColor" base="xsd:string">
    <enumeration value="Green"/>
    <enumeration value="Blue"/>
    <enumeration value="Brown"/>
    </simpleType>
    <Person>
    <Name>Henry Ford</Name>
    <Age>32</Age>
    <EyeColor>Brown</EyeColor>
    </Person>

    5.2.3 字符数组

    一个字符数组可能编码为single-reference 或multi-reference值。字符数组的编码规则与字符串的编码规则类似。特别的,包含字符数组的元素值可能由一个"id"属性,附加的 accssor元素可能有相应的"href"属性。推荐使用定义在XML Schemas [10][11]中的'base64'编码(使用在2045 [13]中定义的base64编码算法)表示模糊字符数组。不过,由于行长度(line length)的限制,通常在MIME中应用base64编码,SOAP中一般不应用base64编码。但是提供了"SOAP-ENC:base64"子类型使之能用于SOAP。

    <picture xsi:type="SOAP-ENC:base64">
    aG93IG5vDyBicm73biBjb3cNCg==
    </picture>

    5.3 多态accessor

    许多语言允许能够多态访问多种类型值的accessor,每种类型在运行时可用。一个多态accessor实例必须包含一个"xsi:type"属性描述实际值的类型。例如,一个名为"cost"类型值为"xsd:float"的多态accessor编码如下:

    <cost xsi:type="xsd:float">29.95</cost>与之对比,类型值不变的accessor编码如下:

    <cost>29.95</cost>

    5.4 Compound types复合类型

    SOAP定义了与下列常在程序语言中出现的结构性模式对应的类型:

    • 结构:一个"struct"是一个复合值,它的成员值的唯一区别是accessor名称,任意两个accessor名称都不相同。
    • 数组:一个"array"是一个复合值,它的成员值的唯一区别是序数位置。

    SOAP也允许结构和数组之外的其它数据的序列化,例如Directed-Labeled-Graph Data Model之类的数据中,单个节点有许多不同的accssor,有些不止出现一次。SOAP序列化规则不要求底层的数据模型在accssor之间区分次序,但如果有这样的次序的话,这些accssor必须按照这个顺序编码。

    5.4.1 复合值,结构和值引用

    复合值的成员编码为accessor元素。当accessor由名区分时(如结构),accessor名即作为元素名。名局部于类型的accessor有不受限的名,其它的accessor则有受限的名。下面的例子是类型为"Book"的结构:

    <e:Book>
    <author>Henry Ford</author>
    <preface>Prefatory text</preface>
    <intro>This is a book.</intro>
    </e:Book>

    以下是描述上面结构的schema片断:

    <element name="Book">
    <complexType>
    <element name="author" type="xsd:string"/>
    <element name="preface" type="xsd:string"/>
    <element name="intro" type="xsd:string"/>
    </complexType>
    </e:Book>

    以下是一个同时具有简单和复杂成员类型的例子。它显示两层引用。注意"Author"accssor元素的"href"属性是对相应具有"id"属性的值的引用。"Address"与之类似。

    <e:Book>
    <title>My Life and Work</title>
    <author href="#Person-1"/>
    </e:Book>
    <e:Person id="Person-1">
    <name>Henry Ford</name>
    <address href="#Address-2"/>
    </e:Person>
    <e:Address id="Address-2">
    <email>mailto:henryford@hotmail.com</email>
    <web>http://www.henryford.com</web>
    </e:Address>

    当"Person"的值和"Address"的值是multi-reference时,上面的形式是正确的。如果它
    们是single-reference,就必须用嵌入的形式,如下所示:

    <e:Book>
    <title>My Life and Work</title>
    <author>
    <name>Henry Ford</name>
    <address>
    <email>mailto:henryford@hotmail.com</email>
    <web>http://www.henryford.com</web>
    </address>
    </author>
    </e:Book>

    如果添加一个限制,任意两个人都不会有相同的地址,并且地址可以是街道或Email地址,一本书可以有两个作者,编码如下:

    <e:Book>
    <title>My Life and Work</title>
    <firstauthor href="#Person-1"/>
    <secondauthor href="#Person-2"/>
    </e:Book>
    <e:Person id="Person-1">
    <name>Henry Ford</name>
    <address xsi:type="m:Electronic-address">
    <email>mailto:henryford@hotmail.com</email>
    <web>http://www.henryford.com</web>
    </address>
    </e:Person>
    <e:Person id="Person-2">
    <name>Samuel Crowther</name>
    <address xsi:type="n:Street-address">
    <street>Martin Luther King Rd</street>
    <city>Raleigh</city>
    <state>North Carolina</state>
    </address>
    </e:Person>

    序列化可以包含对不在同一个资源的值的引用:

    <e:Book>
    <title>Paradise Lost</title>
    <firstauthor href="http://www.dartmouth.edu/~milton/"/>
    </e:Book>

    以下是描述上面结构的schema片断:

    <element name="Book" type="tns:Book"/>
    <complexType name="Book">
    <!-- Either the following group must occur or else the
    href attribute must appear, but not both. -->
    <sequence minOccurs="0" maxOccurs="1">
    <element name="title" type="xsd:string"/>
    <element name="firstauthor" type="tns:Person"/>
    <element name="secondauthor" type="tns:Person"/>
    </sequence>
    <attribute name="href" type="uriReference"/>
    <attribute name="id" type="ID"/>
    <anyAttribute namespace="##other"/>
    </complexType>
    <element name="Person" base="tns:Person"/>
    <complexType name="Person">
    <!-- Either the following group must occur or else the
    href attribute must appear, but not both. -->
    <sequence minOccurs="0" maxOccurs="1">
    <element name="name" type="xsd:string"/>
    <element name="address" type="tns:Address"/>
    </sequence>
    <attribute name="href" type="uriReference"/>
    <attribute name="id" type="ID"/>
    <anyAttribute namespace="##other"/>
    </complexType>
    <element name="Address" base="tns:Address"/>
    <complexType name="Address">
    <!-- Either the following group must occur or else the
    href attribute must appear, but not both. -->
    <sequence minOccurs="0" maxOccurs="1">
    <element name="street" type="xsd:string"/>
    <element name="city" type="xsd:string"/>
    <element name="state" type="xsd:string"/>
    </sequence>
    <attribute name="href" type="uriReference"/>
    <attribute name="id" type="ID"/>
    <anyAttribute namespace="##other"/>
    </complexType>

    5.4.2 数组

    SOAP 数组定义为具有"SOAP-ENC:Array"类型或一个从"SOAP-ENC:Array"衍生的类型(参见规则8)。数组表示为元素值,对元素的名没有特别的约束(正如元素值并不约束它们所属的元素)。数组可以包含任意类型的元素,包括嵌套数组。可以创建新的类型(受SOAP-ENC:Array
    类型限制)来表示数组,如整数数组或某些用户定义的枚举。数组值表示为组成这个数组的项的元素的规则序列。在数组值中,元素名对于区分accesor并不重要。元素可以有任意的名。实际上,元素常常用它们在schema中暗示或确定的数组类型来命名元素。并且一般情况下对于复合值来说,如果数组中数组项的值是single-reference值,则这个数组项包含它的值,否则,该数组项通过"href"属性引用这个值。下面的例子是一个整型数组的 schema片断:

    <element name="myFavoriteNumbers"
    type="SOAP-ENC:Array"/>
    <myFavoriteNumbers
    SOAP-ENC:arrayType="xsd:int[2]">
    <number>3</number>
    <number>4</number>
    </myFavoriteNumbers>

    在这个例子中,数组"myFavoriteNumbers"包括几个成员,每个成员是一个类型为SOAP-ENC:int的值。注意SOAP- ENC:Array允许不受限制的元素名,它们不传达任何类型信息,所以在使用时,或者它们有xsi:type属性,或者它们所属的元素有SOAP- ENC:arrayType属性。自然,由SOAP-ENC:Array衍生的类型可以声明局部元素,但这种情况下要包括类型信息。上面已经提到,SOAP-ENC schema包含了元素的声明,元素名与"XML Schema Part 2: Datatypes"规范[11]中的简单类型一致。其中包括了对"Array"的声明。于是,我们可以这样写:

    <SOAP-ENC:Array SOAP-ENC:arrayType="xsd:int[2]">
    <SOAP-ENC:int>3</SOAP-ENC:int>
    <SOAP-ENC:int>4</SOAP-ENC:int>
    </SOAP-ENC:Array>

    数组可以包含特定arrayType的任意子类型的实例。即,数组成员可以是arryType属性值指定的类型的任意子类型,这个类型对于 arrayType属性中指定的类型来说是可替换的(根据schema中的替换规则)。例如,一个整型数组可以包含从整型衍生的任意类型(如"int"或任意用户定义的从整型衍生的类型)。同样,一个"address"数组可能包含一个address的受限类型或扩展类型如"internationalAddress"。因为提供的SOAP-ENC:Array类型允许任意类型的成员,所以可以包含任意类型的混合除非使用 arrayType属性加以特别的限制。在实例中,可以使用xsi:type指定成员元素的类型,或通过schema中成员元素的声明来指定。下面是两个例子。

    <SOAP-ENC:Array SOAP-ENC:arrayType="SOAP-ENC:ur-type[4]">
    <thing xsi:type="xsd:int">12345</thing>
    <thing xsi:type="xsd:decimal">6.789</thing>
    <thing xsi:type="xsd:string">
    Of Mans First Disobedience, and the Fruit
    Of that Forbidden Tree, whose mortal tast
    Brought Death into the World, and all our woe,
    </thing>
    <thing xsi:type="xsd:uriReference"> http://www.dartmouth.edu/~milton/reading_room/ </thing>
    </SOAP-ENC:Array>
    <SOAP-ENC:Array SOAP-ENC:arrayType="SOAP-ENC:ur-type[4]">
    <SOAP-ENC:int>12345</SOAP-ENC:int>
    <SOAP-ENC:decimal>6.789</SOAP-ENC:decimal>
    <xsd:string>
    Of Mans First Disobedience, and the Fruit
    Of that Forbidden Tree, whose mortal tast
    Brought Death into the World, and all our woe,
    </xsd:string>
    <SOAP-ENC:uriReference> http://www.dartmouth.edu/~milton/reading_room/ </SOAP-ENC:uriReference >
    </SOAP-ENC:Array>

    数组值可以是结构或其它复合值。例如"xyz:Order"结构数组:

    <SOAP-ENC:Array SOAP-ENC:arrayType="xyz:Order[2]">
    <Order>
    <Product>Apple</Product>
    <Price>1.56</Price>
    </Order>
    <Order>
    <Product>Peach</Product>
    <Price>1.48</Price>
    </Order>
    </SOAP-ENC:Array>

    数组成员值也可以是数组。下例是两个字符串数组组成的数组:

    <SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[][2]">
    <item href="#array-1"/>
    <item href="#array-2"/>
    </SOAP-ENC:Array>
    <SOAP-ENC:Array id="array-1" SOAP-ENC:arrayType="xsd:string[2]">
    <item>r1c1</item>
    <item>r1c2</item>
    <item>r1c3</item>
    </SOAP-ENC:Array>
    <SOAP-ENC:Array id="array-2" SOAP-ENC:arrayType="xsd:string[2]">
    <item>r2c1</item>
    <item>r2c2</item>
    </SOAP-ENC:Array>

    包含数组的元素无需命名为"SOAP-ENC:Array"。它可以有任意的名,只要元素的类型是SOAP-ENC:Array或由之衍生的类型。例如,下面是一个schema片断和与之一致的数组实例。

    <simpleType name="phoneNumber" base="string"/>
    <element name="ArrayOfPhoneNumbers">
    <complexType base="SOAP-ENC:Array">
    <element name="phoneNumber" type="tns:phoneNumber" maxOccurs="unbounded" />
    </complexType>
    <anyAttribute/>
    </element>
    <xyz:ArrayOfPhoneNumbers SOAP-ENC:arrayType="xyz:phoneNumber[2]">
    <phoneNumber>206-555-1212</phoneNumber>
    <phoneNumber>1-888-123-4567</phoneNumber>
    </xyz:ArrayOfPhoneNumbers>

    数组可能是多维的。在这种情况下,在arrayType属性的asize部分将不止有一个值:

    <SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[2,3]">
    <item>r1c1</item>
    <item>r1c2</item>
    <item>r1c3</item>
    <item>r2c1</item>
    <item>r2c2</item>
    <item>r2c3</item>
    </SOAP-ENC:Array>

    虽然上面的例子把数组编码为独立的元素,但元素值也可以是嵌入形式,而且若元素值是single reference时,必须编码为嵌入形式。下例是一个schema片断,电话号码数组嵌入到一个类型为"Person"的结构中,并且通过accessor "phone-numbers"访问它:

    <simpleType name="phoneNumber" base="string"/>
    <element name="ArrayOfPhoneNumbers">
    <complexType base="SOAP-ENC:Array">
    <element name="phoneNumber" type="tns:phoneNumber" maxOccurs="unbounded"/>
    </complexType>
    <anyAttribute/>
    </element>
    <element name="Person">
    <complexType>
    <element name="name" type="string"/>
    <element name="phoneNumbers" type="tns:ArrayOfPhoneNumbers"/>
    </complexType>
    </element>
    <xyz:Person>
    <name>John Hancock</name>
    <phoneNumbers SOAP-ENC:arrayType="xyz:phoneNumber[2]">
    <phoneNumber>206-555-1212</phoneNumber>
    <phoneNumber>1-888-123-4567</phoneNumber>
    </phoneNumbers>
    </xyz:Person>

    下面的例子中,数组值为single-reference,被编码为嵌入元素,包含它的元素名即为入口名:

    <xyz:PurchaseOrder>
    <CustomerName>Henry Ford</CustomerName>
    <ShipTo>
    <Street>5th Ave</Street>
    <City>New York</City>
    <State>NY</State>
    <Zip>10010</Zip>
    </ShipTo>
    <PurchaseLineItems SOAP-ENC:arrayType="Order[2]">
    <Order>
    <Product>Apple</Product>
    <Price>1.56</Price>
    </Order>
    <Order>
    <Product>Peach</Product>
    <Price>1.48</Price>
    </Order>
    </PurchaseLineItems>
    </xyz:PurchaseOrder>

    5.4.2.1 部分储值(partially transmitted)数组

    SOAP提供了对部分储值(partially transmitted)数组的支持,如某些上下文中的可变数组。一个partially transmitted 数组由一个"SOAP-ENC:offset"属性(从第一个transmitted的元素开始的偏移量,基于0)指示。如果省略,偏移量取0。下面的例子中数组的大小为5,但只有从0起,第三和第四个元素被储值。

    <SOAP-ENC:Array ;SOAP-ENC:arrayType="xsd:string[5]" ;SOAP-ENC:offset="[2]">
    <item>The third element</item>
    <item>The fourth element</item>
    </SOAP-ENC:Array>

    5.4.2.2 稀疏数组Sparse Arrays

    SOAP提供了对稀疏数组的支持。每个表示成员值的元素包含一个"SOAP-ENC:position"属性,用来指示它在数组中的位置。下例是两维字符串稀疏数组的例子,数组大小是4,但只用到第2个。

    <SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[,][4]">
    <SOAP-ENC:Array href="#array-1" SOAP-ENC:position="[2]"/>
    </SOAP-ENC:Array>
    <SOAP-ENC:Array id="array-1" SOAP-ENC:arrayType="xsd:string[10,10]">
    <item SOAP-ENC:position="[2,2]">Third row, third col</item>
    <item SOAP-ENC:position="[7,2]">Eighth row, third col</item>
    </SOAP-ENC:Array>

    如果对array-1的引用仅发生在数组内部,上例也可以编码如下:

    <SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[,][4]">
    <SOAP-ENC:Array SOAP-ENC:position="[2]" SOAP-ENC:arrayType="xsd:string[10, 10]>
    <item SOAP-ENC:position="[2,2]">Third row, third col</item>
    <item SOAP-ENC:position="[7,2]">Eighth row, third col</item>
    </SOAP-ENC:Array>
    </SOAP-ENC:Array>

    5.4.3 一般复合类型

    在这里提到的编码规则不仅仅限于accessor名已知的情况,如果accessor名是运行环境下实时获得的,编码规则同样适用,也就是说 accessor编码成一个元素名与accessor名匹配的元素,同时accessor可能包含或者引用该元素的值。如果accessor包含类型不能事先确定的值,它必须包含一个合适的属性xsi:type 。类似地,上述引用的规则已经足够用于复合类型的序列化,这些复合类型可能包含用名区分的accessors(结构)和用名及序数位置区分的 accessors。(可能包含重复的accessor)实际上这并不要求任何schema模式包含这些类型,但更为准确的说法是:一个类型模型(type-model)schema如果有这些类型,就可以构造一个符合XML句法规则的schema和XML文档实例。

    <xyz:PurchaseOrder>
    <CustomerName>Henry Ford</CustomerName>
    <ShipTo>
    <Street>5th Ave</Street>
    <City>New York</City>
    <State>NY</State>
    <Zip>10010</Zip>
    </ShipTo>
    <PurchaseLineItems>
    <Order>
    <Product>Apple</Product>
    <Price>1.56</Price>
    </Order>
    <Order>
    <Product>Peach</Product>
    <Price>1.48</Price>
    </Order>
    </PurchaseLineItems>
    </xyz:PurchaseOrder>

    类似地,将一个结构上类似数组但实际上不是一个 SOAP-ENC:Array类型或者 SOAP-ENC:Array子类型的复合值序列化同样是允许的,例如:

    <PurchaseLineItems>
    <Order>
    <Product>Apple</Product>
    <Price>1.56</Price>
    </Order>
    <Order>
    <Product>Peach</Product>
    <Price>1.48</Price>
    </Order>
    </PurchaseLineItems>

    5.5 缺省值

    省略accessor元素意味着或者有一个缺省值或者值不知道。具体细节依靠这个accessor,方法和上下文。例如,对于多态accessor,省略 accessor一般意味着一个Null值。同样,省略布尔accessor一般意味着False值或者值不知道,省略数字accessor一般意味着值为零或者值不知道。

    5.6 SOAP root属性

    SOAP root 属性可用于标记一个序列化root,从而一个对象可以反序列化(deserialized),而实际上该root并不是真正的对象root。这个属性有两个可选值"1" or "0"。对象真正的roots属性值为“1” ,序列化root但不是真正的root属性值也为“1”,元素如果要显式地指定不能为序列化root,只需将该属性设置为“0” SOAP root属性可以出现在SOAP头和SOAP体元素的任意子元素中。(译者注:SOAP root属性为0的元素不是一个独立的实体,外部的应用不能访问到该元素,但该元素可以被SOAP文档本身的其它元素访问到)SOAP root属性可以出现在SOAP头和SOAP体元素的任意子元素中。这个属性没有缺省值。

    6. 在HTTP中使用SOAP

    这一节讲述了如何在HTTP中使用SOAP。把SOAP绑定到HTTP,无论使用或不用HTTP扩展框架,都有很大的好处:在利用SOAP的形式化和灵活性的同时,使用HTTP种种丰富的特性。在HTTP中携带SOAP消息,并不意味着SOAP改写了HTTP已有的语义,而是将构建在HTTP之上SOAP语义自然地对应到HTTP语义。SOAP自然地遵循HTTP的请求/应答消息模型使得SOAP的请求和应答参数可以包含在HTTP请求和应答中。注意,SOAP的中间节点与HTTP的中间节点并不等同,即,不要期望一个根据HTTP连接头中的域寻址到的HTTP中间节点能够检查或处理HTTP请求中的SOAP消息。
    在HTTP消息中包含SOAP实体时,按照RFC2376[3] HTTP应用程序必须使用媒体类型 "text/xml"。

    6.1 SOAP HTTP请求

    虽然SOAP可能与各种HTTP请求方式相结合,但是绑定仅定义了在HTTP POST请求中包含SOAP消息。(第7节中描述了如何在RPC中使用SOAP,第6.3节描述了如何使用HTTP扩展框架)

    6.1.1 HTTP头中SOAPAction域

    一个HTTP请求头中的SOAPAction域用来指出这是一个SOAP HTTP请求,它的值是所要的URI。在格式、URI的特性和可解析性上没有任何限制。当HTTP客户发出SOAP HTTP请求时必须使用在HTTP头中使用这个域。

    soapaction = "SOAPAction" ":" [ <"> URI-reference <"> ]
    URI-reference = <as defined in RFC 2396 [4]>

    HTTP头中SOAPAction域使服务器(如防火墙)能正确的过滤HTTP中SOAP请求消息。如果这个域的值是空字符串(""),表示SOAP消息的目标就是HTTP请求的URI。这个域没有值表示没有SOAP消息的目标的信息。例子:

    SOAPAction: "http://electrocommerce.org/abc#MyMessage"
    SOAPAction: "myapp.sdl"
    SOAPAction: ""
    SOAPAction:

    6.2 SOAP HTTP应答

    SOAP HTTP遵循HTTP 中表示通信状态信息的HTTP状态码的语义。例如,2xx状态码表示这个包含了SOAP组件的客户请求已经被成功的收到,理解和接受。在处理请求时如果发生错误,SOAP HTTP服务器必须发出应答HTTP 500 "Internal Server Error",并在这个应答中包含一个SOAP Fault元素(见4.4节)表示这个SOAP处理错误。

    6.3 HTTP扩展框架

    一个SOAP消息可以与HTTP扩展框架 [6]一起使用以区分是否有SOAP HTTP请求和它的目标。是使用扩展框架或是普通的HTTP关系到通信各方的策略和能力。通过使用一个必需的扩展声明和"M-"HTTP方法名前缀,客户可以强制使用HTTP扩展框架。服务器可以使用HTTP状态码510 "Not Extended"强制使用HTTP扩展框架。也就是说,使用一个额外的来回,任何一方都可以发现另一方的策略并依照执行。用来表示SOAP使用了扩展框架的扩展标志符是:http://schemas.xmlsoap.org/soap/envelope/

    6.4 SOAP HTTP举例

    例3 使用POST的SOAP HTTP

    POST /StockQuote HTTP/1.1
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    SOAPAction: "http://electrocommerce.org/abc#MyMessage"
    <SOAP-ENV:Envelope...
    HTTP/1.1 200 OK
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    <SOAP-ENV:Envelope...

    例4 使用扩展框架的SOAP HTTP

    M-POST /StockQuote HTTP/1.1
    Man: "http://schemas.xmlsoap.org/soap/envelope/"; ns=NNNN
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    NNNN-SOAPAction: "http://electrocommerce.org/abc#MyMessage"
    <SOAP-ENV:Envelope...
    HTTP/1.1 200 OK
    Ext:
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    <SOAP-ENV:Envelope...

    7. 在RPC中使用SOAP

    设计SOAP的目的之一就是利用XML的扩展性和灵活性来封装和交换RPC调用。这一节定义了远程过程调用和应答的统一表示形式。虽然可以预计到这种表示形式最可能被用于与第5节中定义的编码方式相结合,但也可能有其它的表示形式。SOAP的encodingStyle属性(见4.3.2节)可以用来表明方法调用和应答都使用这一节所指定的表示方式。在RPC中使用SOAP和SOAP协议绑定(见第6节)是紧密相关的。在使用HTTP作为绑定协议时,一个 RPC调用自然地映射到一个HTTP请求,RPC应答同样映射到HTTP应答。但是,在RPC中使用SOAP并不限于绑定HTTP协议。
    要进行方法调用,以下的信息是必需的:

    • 目标对象的URI
    • 方法名
    • 方法signature(可选)
    • 方法的参数
    • 头数据(可选)

    SOAP依靠协议绑定提供传送URI的机制。例如,对HTTP来说,请求的URI指出了调用的来源 。除了必须是一个合法的URI之外,SOAP对一个地址的格式没有任何限制。(更多URI的信息参见 [4])

    7.1 RPC和SOAP体

    RPC方法调用和应答都包含在SOAP Body元素中(见4.3节),它们使用如下的表示形式:

    • 一个方法调用用一个结构表示
    • 一个方法调用被看作一个单个的结构,每个[in]和[in/out]参数有一个accessor。结构的名和类型与方法相同。每个[in]和[in /out]参数都被看作一个accessor,这个accessor的名和类型与参数的名和类型相对应。它们的出现顺序和方法中定义的参数顺序相同。
    • 一个方法应答用一个结构表示。
    • 一个方法应答被看作一个单个的结构,返回值和每个[in]和[in/out]参数有一个accessor。第一个accessor是返回值,之后是参数 accessor,参数accessor的出现顺序和方法中定义的参数顺序相同。每个参数accessor的名称和类型与参数的名称和类型相对应。返回值 accessor的名称并不重要。同样,结构的名称也不重要,不过,通常在方法名称的后面加上字符串"Response"作为结构的名称。

    方法错误使用SOAP Fault元素(见4.4节)表示。如果绑定的协议有额外的规则表示错误,则这些规则也必须要遵从。正如上面所述,方法调用和应答结构可以按照第5节中规则编码,或者用encodingStyle属性(见4.1.1节)指定编码方式。应用程序可以处理缺少参数的请求,但是可能返回一个错误。因为返回结果表示调用成功,错误表示调用失败,所以,在方法应答中同时包含返回结果和错误是错误的。

    7.2 RPC和SOAP头

    在 RPC编码中,可能会有与方法请求有关但不是正规的方法signature的附加信息。如果这样,它必须作为SOAP头元素的子元素。使用这种头元素的一个例子是在消息中传递事务ID。由于事务ID不是方法signature的一部分,通常由底层的组件而不是应用程序代码控制,所以没有一种直接的方法在调用中传递这个必要的信息。通过在头中添加一个给定名字的条目,接收方的事务管理器就可以析取这个事务ID,而且不影响远程过程调用的代码。

    8. 安全性考虑

    这篇文档中没有涉及完整性和保密性,这些问题将在以后的版本中描述。

    9. 参考文献

    [1] S. Bradner, "The Internet Standards Process -- Revision 3", RFC2026, Harvard University, October 1996
    [2] S. Bradner, "Key words for use in RFCs to Indicate Requirement Levels", RFC 2119, Harvard University, March 1997
    [3] E. Whitehead, M. Murata, "XML Media Types", RFC2376, UC Irvine, Fuji Xerox Info. Systems, July 1998
    [4] T. Berners-Lee, R. Fielding, L. Masinter, "Uniform Resource Identifiers (URI): Generic Syntax", RFC 2396, MIT/LCS, U.C.Irvine, Xerox Corporation, A ugust 1998.
    [5] R. Fielding, J. Gettys, J. C. Mogul, H. Frystyk, T. Berners-Lee, "Hypert ext Transfer Protocol -- HTTP/1.1", RFC 2616, U.C. Irvine, DEC W3C/MIT, DEC,W3C/MIT, W3C/MIT, January 1997
    [6] H. Nielsen, P. Leach, S. Lawrence, "An HTTP Extension Framework", RFC 2774, Microsoft, Microsoft, Agranat Systems
    [7] W3C Recommendation "The XML Specification"
    [8] W3C Recommendation "Namespaces in XML"
    [9] W3C Working Draft "XML Linking Language". This is work in progress.
    [10] W3C Working Draft "XML Schema Part 1: Structures". This is work in progress.
    [11] W3C Working Draft "XML Schema Part 2: Datatypes". This is work in progress.
    [12] Transfer Syntax NDR, in "DCE 1.1: Remote Procedure Call"
    [13] N. Freed, N. Borenstein, "Multipurpose Internet Mail Extensions (MIME)Part One: Format of Internet Message Bodies", RFC2045, Innosoft, First Virtu al, November 1996

    10。 附录

    A. SOAP封装举例

    A.1 请求编码举例

    例5 类似于例1,但有一个必要的头

    POST /StockQuote HTTP/1.1
    Host: www.stockquoteserver.com
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    SOAPAction: "Some-URI"
    <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    <SOAP-ENV:Header>
    <t:Transaction
    xmlns:t="some-URI"
    SOAP-ENV:mustUnderstand="1">
    5
    </t:Transaction>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
    <m:GetLastTradePrice xmlns:m="Some-URI">
    <symbol>DEF</symbol>
    </m:GetLastTradePrice>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

    例6 类似于例1,但有多个请求参数

    POST /StockQuote HTTP/1.1
    Host: www.stockquoteserver.com
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    SOAPAction: "Some-URI"
    <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    <SOAP-ENV:Body>
    <m:GetLastTradePriceDetailed
    xmlns:m="Some-URI">
    <Symbol>DEF</Symbol>
    <Company>DEF Corp</Company>
    <Price>34.1</Price>
    </m:GetLastTradePriceDetailed>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

    A.2 应答编码举例

    例7 与例2类似,但有必要的头部

    HTTP/1.1 200 OK
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    <SOAP-ENV:Header>
    <t:Transaction xmlns:t="some-URI" xsi:type="xsd:int" mustUnderstand="1"> 5 </t:Transaction>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
    <m:GetLastTradePriceResponse xmlns:m="Some-URI">
    <Price>34.5</Price>
    </m:GetLastTradePriceResponse>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

    例8 与例2类似,但有一个结构

    HTTP/1.1 200 OK
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    <SOAP-ENV:Body>
    <m:GetLastTradePriceResponse
    xmlns:m="Some-URI">
    <PriceAndVolume>
    <LastTradePrice> 34.5 </LastTradePrice>
    <DayVolume> 10000 </DayVolume>
    </PriceAndVolume>
    </m:GetLastTradePriceResponse>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

    例9 与例2类似,但处理必要的头出错

    HTTP/1.1 500 Internal Server Error
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
    <SOAP-ENV:Fault>
    <faultcode>SOAP-ENV:MustUnderstand</faultcode>
    <faultstring>SOAP Must Understand Error</faultstring>
    </SOAP-ENV:Fault>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

    例10 与例2类似,但处理Body出错

    HTTP/1.1 500 Internal Server Error
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
    <SOAP-ENV:Fault>
    <faultcode>SOAP-ENV:Server</faultcode>
    <faultstring>Server Error</faultstring>
    <detail>
    <e:myfaultdetails xmlns:e="Some-URI">
    <message>
    My application didn't work
    </message>
    <errorcode> 1001 </errorcode>
    </e:myfaultdetails>
    </detail>
    </SOAP-ENV:Fault>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

    July 17

    Linux ACL

    1.甚么是ACL

        ACL就是Access Control List,一个文件/目录的访问控制列表,可以针对任意指定的用户/组分配RWX权限。Linux在2.6版内核开始支持ACL。

    2.ACL条目

        一个ACL包括了一个ACL条目的集合,一个ACL条目为一个用户或者一个组对某个对象的访问权限,包括读、写、执行。一个ACL条目包括了一个条目标签类型,一个可选的条目标签的准入者以及一个权限(permission)的集合。

        条目标签类型如下:

               ACL_USER_OBJ    ACL_USER_OBJ 条目标签表示文件所有者的访问权限。

               ACL_USER           ACL_USER条目标签表示该条目下准入者的访问权限。

               ACL_GROUP_OBJ  ACL_GROUP_OBJ条目标签表示文件所在组的访问权限。

               ACL_GROUP         ACL_GROUP条目标签表示该条目下准入者(特定的组组)的访问权限。

               ACL_MASK           ACL_MASK条目标签表示能给ACL_USER,ACL_GROUP,ACL_GROUP分配的最大权限。

               ACL_OTHER         ACL_OTHER条目标签表示不能满足其它条目中的准入者的访问权限。

    3.合法的ACL

        一个ACL必须符合以下规则才能叫做合法的ACL。

            一个ACL必须包含一条且仅有一条ACL_USER_OBJ,ACL_GROUP_OBJ,ACL_OTHER标签类型。

            一个ACL可以包含0个或者多个ACL_USER以及ACL_GROUP,如果有这样的条目,那么ACL中必须包含一个且仅有一个ACL_MASK标签类型。否则  ACL_MASK可以不出现

            所有ACL_USER准入者中出现的user ID必须唯一,同样,在ACL_GROUP中出现的group ID必须唯一。

    4.ACL与文件权限的联系

            在昨天6点钟之前,肖囧同学只知道文件的权限用chmod管理,这对个人用户来说足够,然而对一些系统管理员来说,只用文件权限来管理访问安全性足够可以让他/她/它无语到极点,为了实现一些比较复杂的权限管理,往往不得不创建很多的组,并加以详细的记录和区分。所以ACL的出现让管理员看到了曙光,然而ACL不能够和已有的权限系统冲突,所以和文件权限就有如下联系:

            1.ACL所规定的权限是文件权限的超集。

            2.文件权限中的用户对应ACL中的ACL_USER_OBJ条目

            3.如果木有ACL_MASK条目,则文件权限中的组权限对应ACL_GROUP_OBJ条目。反之,文件中的组权限对应ACL_MASK条目。

            4.文件权限中的其他权限对应ACL_OTHER_OBJ条目,

            5.文件权限中的任何改动将映射到ACL上,反之亦然。

    5.对象的创建以及缺省ACL

        当使用create() mkdir() mknod() mkfifo() open()等函数创建对象时,这些对象的ACL初始化。如果某一目录关联一个缺省的ACL,那末这些函数的mode参数以及该目录的缺省ACL决定了该对象的ACL。

            1.新对象将继承该目录的缺省ACL。

            2.修改文件权限相关的ACL条目,使得ACL中不存在mode参数中未指定的访问权限。

        如果该目录没有缺省的ACL,那么mode参数以及文件创建的mask(这个东西我有讲过的,文件创建的掩码,不记得请上我公司网站查询)决定该对象的ACL

            1.新建对象的ACL包括ACL_USER_OBJ,ACL_GROUP_OBJ以及ACL_OTHER,这些条目的权限又文件创建掩码决定。

            2.与mode参数中指定的权限进行同步。

    6.通过ACL访问的检查算法

         每当一个进程要访问指定的对象时,ACL会进行如下检查:

            1.If进程的有效uid与对象所有者一致,则。

                if ACL_USER_OBJ中有相应的权限,那么允许访问。

                else 拒绝访问。

            2.else if如果进程的有效uid与某条ACL_USER条目中的准入者uid一致

                if 满足ACL_USER条目以及ACL_MASK中有相应的权限,那么允许访问。

                else 拒绝访问。

            3.else if如果有效的gid与某条ACL_GROUP的准入者gid一致,则

                if ACL中有ACL_MASK条目。则

                      if ACL_MASK或者ACL_GROUP_OBJ或者ACL_GROUP含有指定的访问权限,则允许访问。

                      else 拒绝访问。

                else

                      if ACL_GROUP_OBJ条目中有相应的权限,则允许访问。

                      else 拒绝访问

                else 拒绝访问

            4.else if ACL_OTHER条目中有相应的权限,则运行访问。

            5.else拒绝访问。

    7.查看ACL

        查看一个对象的ACL,可以使用getfacl命令,比如getfacl ./somedir,其结果如下

              1.# file: somedir/
              2:  # owner: lisa
              3:  # group: staff
              4:  user::rwx                                      所有者的权限,所谓的ACL_USER_OBJ条目
              5:  user:joe:rwx               #effective:r-x    其它用户的权限,ACL_USER条目,由于被MASK掉了,所以有效的只有MASK和该条目并集的权限
              6:  group::rwx                 #effective:r-x    ACL_GROUP_OBJ条目
              7:  group:cool:r-x                                    ACL_GROUP条目
              8:  mask:r-x                                            ACL_MAKS条目
              9:  other:r-x                                            ACL_OTHER条目
              10:  default:user::rwx
              11:  default:user:joe:rwx       #effective:r-x
              12:  default:group::r-x
              13:  default:mask:r-x
              14:  default:other:---

    8.设置ACL

         设置一个对象的ACL,要通过setfacl命令。不过我目前没有试验成功,需要ROOT权限,残念啊……请各位用man setfacl开传送门去看看。

    9总结

         ACL大大的方便了管理员对文件对象的管理,可以利用ACL的条目,轻松的添加一些访问例外,而用chmod实现同样的功能,则要添加很多组,然后把用户添加到组中间。无论多么复杂的系统中,文件系统的权限管理都是最基础的内容。而 Linux 对 ACL的支持,无疑是一把管理海量用户系统的利器,对 Linux 在大规模的企业级应用中更方便的发挥更大的作用添了一把火。注意:在单个文件的 ACL 条目的数量上,不同的文件系统有不同的限制。Ext2 和 Ext3 只能支持每个文件 25 个 ACL 条目。ReiserFS 和 JFS 可以支持超过 8,000 个条目。这个方面 Ext* 文件系统还需要加强。

    June 25

    谈论 再见同学,你好先生!

     

    引用

    再见同学,你好先生!

    当去年的这个时候,我还觉得毕业是很遥远的事情。但是逝者如斯夫,抑或说How time flies. 转眼间,毕业的主角就成了我们。

    说起毕业,多多少少有一些伤感啊,不安啊,对前途的憧憬或是迷茫啊。人最难受的时候莫过如此,心情百感交集,今天想着和某某同学吃喝一顿,明天想着去学校里办理一些毕业的事物,后天良心发现,翻个几页书,然后时间久这么混混沌沌的过去了。对于大多数人来说,这也是没办法的事情,几种想法纵横交错的时候,不总是能安下心来做事,不过毕业嘛,就要有毕业的样子,前几年的辛苦,能够得瑟着毕业,不为学分发愁,不为出路担忧,也是一大幸事。

    大四这一年,我就没怎么在学校里呆过,中国最大两座城市各住个半年,为了以后的工作到处飘荡。在这个时候,还难免有那么点伤感的,总觉得有很多话没跟朋友们倾诉,然而时间有限,太多的话积累在一起,就像茶壶里装饺子一般,不知道从何说起,以致现在要把很多东西留在这里,希望我的朋友们能看到,能了解我对朋友们衷心感恩,对朋友美好的祝福。我是一个很普通的人,正因为有了这些朋友们,我的大学才丰富多彩,才趣味十足,我才是现在坐在电脑前写文章的我。

    我总觉得自己是一个沉湎于过去的人,每当我回首大学走过的这段路,我都会带上一些骄傲——我的大学过得很充实,我在大学为未来的生活做了足够的准备。当我被命运安排到武汉大学的那个暑假,我就在思考,我的大学应该怎么度过。可能是上天的眷顾,我在大学期间遇到了很多志同道合的朋友,在他们的帮助下,我们拥有了很多精彩的瞬间。我很满意我的大学生涯,我的生活方式很规律,成绩也不错,动手能力也凑合,有那么几个可以推心置腹的朋友,找到一份不错的工作,最后老天大发慈悲,让高中我就喜欢上的那女孩陪着我……我觉得,这样的大学生活应该很知足了。很感谢我的父亲,在我入大学之前给我转了一贴《给大学生的81条金玉良言》虽然那81条我并没有全做到,不过也八九不离十。

    今天,在拿到毕业证的那一瞬间,一个强烈的声音告诉我,得瑟的日子已经结束了。以后不会再有穿着拖鞋到处溜达,不会再有寡人今天不高兴不去上课,不会再有草坪哪背阴我躺哪,对于我的学生生涯,就像一桌宴席一般,散了,散了。虽然以后天还是那样的天,地也还是那样的地,人不会是那样的人了,一个无忧无虑的学生,马上要面对人世间纷纷扰扰。你应该准备好了吧,你应该是个成熟的成年人了吧,这两句话在这一年一直萦绕在我耳畔,不曾挥去。

    我很庆幸有一年的实习期间来适应未来的生活,这一年里,多多少少还是觉得自己的知识储备,身体状况,心态还是学生时代的那个小屁孩。不过据说高校毕业生大多都是这种状态,那么好吧,大家都能做到,对我来说也是轻松的事情。

    虽然学生生涯的结束,这并不意味学习的结束,这一句对我来说简直就是废话。

    总觉得自己不是那种爱冒险的人,就像我在麻将桌上悲观且谨慎的表现一般,也许我做不成什么大事,也许我只是一个大千世界中的凡夫俗子,让我承担起什么历史社会使命,现在看来蛮不靠谱的。守住家中的一亩三分田,现在看起来倒是一个很现实而且很明晰的未来。好好工作,养家糊口。

    还记得大学刚开学的时候,一个老外说Life is like a book. We finish one chapter after.大学的这一章节已经结束了,接着读下一章吗?当然。

    当回忆与现实不断在混沌的大脑中交替,沉湎于过去固然能够回忆起太多的往事,高兴的、悲伤的、痛苦的、滑稽的,回忆不是过眼云烟,舍不得老师们精彩的讲解,舍不得珞珈山水旖旎的风光,舍不得朋友间亲密无间的扯淡,舍不得随心所欲的生活节奏。再多的不舍,随着那一章最后一页的翻过,都成了过去。迎接我的会是什么?未来是什么样的?平静的面对这一切吧。

    再见了,肖同学,你的大学时代,学生生涯,就这么过去了。你好肖先生,以后好玩的事情多着呢。


    再见同学,你好先生!

    当去年的这个时候,我还觉得毕业是很遥远的事情。但是逝者如斯夫,抑或说How time flies. 转眼间,毕业的主角就成了我们。

    说起毕业,多多少少有一些伤感啊,不安啊,对前途的憧憬或是迷茫啊。人最难受的时候莫过如此,心情百感交集,今天想着和某某同学吃喝一顿,明天想着去学校里办理一些毕业的事物,后天良心发现,翻个几页书,然后时间久这么混混沌沌的过去了。对于大多数人来说,这也是没办法的事情,几种想法纵横交错的时候,不总是能安下心来做事,不过毕业嘛,就要有毕业的样子,前几年的辛苦,能够得瑟着毕业,不为学分发愁,不为出路担忧,也是一大幸事。

    大四这一年,我就没怎么在学校里呆过,中国最大两座城市各住个半年,为了以后的工作到处飘荡。在这个时候,还难免有那么点伤感的,总觉得有很多话没跟朋友们倾诉,然而时间有限,太多的话积累在一起,就像茶壶里装饺子一般,不知道从何说起,以致现在要把很多东西留在这里,希望我的朋友们能看到,能了解我对朋友们衷心感恩,对朋友美好的祝福。我是一个很普通的人,正因为有了这些朋友们,我的大学才丰富多彩,才趣味十足,我才是现在坐在电脑前写文章的我。

    我总觉得自己是一个沉湎于过去的人,每当我回首大学走过的这段路,我都会带上一些骄傲——我的大学过得很充实,我在大学为未来的生活做了足够的准备。当我被命运安排到武汉大学的那个暑假,我就在思考,我的大学应该怎么度过。可能是上天的眷顾,我在大学期间遇到了很多志同道合的朋友,在他们的帮助下,我们拥有了很多精彩的瞬间。我很满意我的大学生涯,我的生活方式很规律,成绩也不错,动手能力也凑合,有那么几个可以推心置腹的朋友,找到一份不错的工作,最后老天大发慈悲,让高中我就喜欢上的那女孩陪着我……我觉得,这样的大学生活应该很知足了。很感谢我的父亲,在我入大学之前给我转了一贴《给大学生的81条金玉良言》虽然那81条我并没有全做到,不过也八九不离十。

    今天,在拿到毕业证的那一瞬间,一个强烈的声音告诉我,得瑟的日子已经结束了。以后不会再有穿着拖鞋到处溜达,不会再有寡人今天不高兴不去上课,不会再有草坪哪背阴我躺哪,对于我的学生生涯,就像一桌宴席一般,散了,散了。虽然以后天还是那样的天,地也还是那样的地,人不会是那样的人了,一个无忧无虑的学生,马上要面对人世间纷纷扰扰。你应该准备好了吧,你应该是个成熟的成年人了吧,这两句话在这一年一直萦绕在我耳畔,不曾挥去。

    我很庆幸有一年的实习期间来适应未来的生活,这一年里,多多少少还是觉得自己的知识储备,身体状况,心态还是学生时代的那个小屁孩。不过据说高校毕业生大多都是这种状态,那么好吧,大家都能做到,对我来说也是轻松的事情。

    虽然学生生涯的结束,这并不意味学习的结束,这一句对我来说简直就是废话。

    总觉得自己不是那种爱冒险的人,就像我在麻将桌上悲观且谨慎的表现一般,也许我做不成什么大事,也许我只是一个大千世界中的凡夫俗子,让我承担起什么历史社会使命,现在看来蛮不靠谱的。守住家中的一亩三分田,现在看起来倒是一个很现实而且很明晰的未来。好好工作,养家糊口。

    还记得大学刚开学的时候,一个老外说Life is like a book. We finish one chapter after.大学的这一章节已经结束了,接着读下一章吗?当然。

    当回忆与现实不断在混沌的大脑中交替,沉湎于过去固然能够回忆起太多的往事,高兴的、悲伤的、痛苦的、滑稽的,回忆不是过眼云烟,舍不得老师们精彩的讲解,舍不得珞珈山水旖旎的风光,舍不得朋友间亲密无间的扯淡,舍不得随心所欲的生活节奏。再多的不舍,随着那一章最后一页的翻过,都成了过去。迎接我的会是什么?未来是什么样的?平静的面对这一切吧。

    再见了,肖同学,你的大学时代,学生生涯,就这么过去了。你好肖先生,以后好玩的事情多着呢。

    June 12

    谈论 C语言声明的优先级规则

     

    引用

    C语言声明的优先级规则

    A    声明从它的名字开始读取,然后按照优先级顺序依次读取。

    B    优先级从高到低依次是:

            B1  声明中被括号括起来的部分

            B2  后缀操作符:括号表示这是一个函数,而方括号表示这是一个数组

            B3  前最操作符:星号*表示“指向……的指针”。

    C    如果const和(或)volatile关键字的后面紧跟类型说明符(比如int ,long等),那么它作用于类型说明符,在其他情况下,const和(或)volatile关键字作用于它左边紧邻的指针星号。

    早看到这个就好了,读C的声明相当纠结的……


    June 11

    C语言声明的优先级规则

    A    声明从它的名字开始读取,然后按照优先级顺序依次读取。

    B    优先级从高到低依次是:

            B1  声明中被括号括起来的部分

            B2  后缀操作符:括号表示这是一个函数,而方括号表示这是一个数组

            B3  前最操作符:星号*表示“指向……的指针”。

    C    如果const和(或)volatile关键字的后面紧跟类型说明符(比如int ,long等),那么它作用于类型说明符,在其他情况下,const和(或)volatile关键字作用于它左边紧邻的指针星号。

    早看到这个就好了,读C的声明相当纠结的……

    March 24

    克隆表达式

    主要用于crontab的任务调度中

    (1)、克隆表达式可以包括7个字段:秒、分、小时、月内日期、月、周内日期、年(可选字段)
    (2)、特殊字符:
    一、反斜线(/)字符表示增量。"5/15"代表从第5秒开始,每15秒一次。
    二、问   号(?)字符和字母L字符只能在月内日期和周内日期字段中可用。问号表示这个字段不包含具体值,所以,如果指定月内日期,可以在周内日期字段中插入"?",表示周内日期值无关紧要,字母L放在月内日期字段中,表示安排在当月最后一天执行。
    三、在月内日期字段中的字母(W)字符把执行安排在最靠近指定值的工作日,把"1W"放在月内日期字段中,表示把执行安排在当月的第一个工作日内。
    四、井号(#)字符为给定月份指定具体的工作日实例。把"MON#2"放在周内日期字段中,表示把任务安排在当月的第二个星期一。
    五、星号(*)字符是通配字符,表示该字段可以接受任何可能的值。
    例子:
    "0 0 12 * * ?" 每天中午12点触发
    "0 15 10 ? * *" 每天上午10:15触发
    "0 15 10 * * ?" 每天上午10:15触发
    "0 15 10 * * ? *" 每天上午10:15触发
    "0 15 10 * * ? 2005" 2005年的每天上午10:15触发
    "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
    "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
    "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
    "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
    "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
    "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
    "0 15 10 15 * ?" 每月15日上午10:15触发
    "0 15 10 L * ?" 每月最后一日的上午10:15触发
    "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
    "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
    "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发 "0 0 12 * * ?" 每天中午12点触发
    "0 15 10 ? * *" 每天上午10:15触发
    "0 15 10 * * ?" 每天上午10:15触发
    "0 15 10 * * ? *" 每天上午10:15触发
    "0 15 10 * * ? 2005" 2005年的每天上午10:15触发
    "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
    "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
    "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
    "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
    "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
    "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
    "0 15 10 15 * ?" 每月15日上午10:15触发
    "0 15 10 L * ?" 每月最后一日的上午10:15触发
    "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
    "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
    "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发

    March 22

    原则,你懂不

    最近看了《疯狂的赛车》,里面那两个杀手,抑或是贼,也许又是毒贩,或者说他们是全才,反正他们的台词相当搞笑,“干一行爱一行”,“多行不义必自毙”,“不专业啊”,其中还有一句话就是“原则,你懂不”。

    其实我不怎么懂原则,我说的原则是软件设计中的原则,每次当我问那些有经验的前辈们,怎么设计才算是一个合理的设计方案,一般都会得到的回答就是,经验,当经历的项目多了,自然就会有一套自己设计的套路。这种回答也实在是无可奈何,毕竟软件设计没有一套验证合理性的数学模型,所以对于一个设计方案,我遇到很多设计评审就是,合理就可以,但没有人可以肯定,这个设计不会造成一些后果。尤其是对于我这种菜鸟来说,设计一个模块都让我战战兢兢,如履薄冰。所以嘛,不知道点原则,那么做出来的事情肯定就不专业了。虽然现在的经验不能保证我能够吃透这些原则,但完全不知道原则,那就像是拿着菜刀去和鬼子搏斗一般,也许一转身,发现还有AK47,迫击炮,火箭筒之类的,虽然现在不会用,但也得学,虽说有时候杀鸡用牛刀,偏偏用牛刀可以把鸡杀了,但反过来杀牛用鸡刀,只怕是牛占据格斗的优势。

    废话到此,先看伟大的wiki上对软件设计7条原则的描述:

    1.开闭原则(Open/closed principle)

    这个是最常提到的,当年我面试的时候,也被问过这个问题,简单的说,就是在面向对象的编程中,软件实体应该可以自由扩展,而不能被修改(software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification),也就是说,在要增加软件的功能,或者是要做其它方面的改进时,不能对原有的代码进行改动,而只能在原来的代码上进行添加。如果说你不得不修改原有的代码来改进一个模块的某些功能,那么只能说这个模块设计得不合理。

    2.里氏代换原则(Liskov Substitution Principle,常缩写为.LSP)

    这条原则是继承复用的基石。(Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.)如果q(x)是对象x的属性,属于T类型,那么如果q(y)是y对象的属性,属于类型S,且S是T的子类型。换言之,一个软件实体如果是用的一个基类的话,那么一定适用于其子类,但是反之不然,基类无法完成子类的某些功能。

    由这条原则引申出了“基于契约设计”(Design By Constarct):类的编写者显式地规定针对该类的契约。客户代码的编写者可以通过该契约获悉可以依赖的行为方式。契约是通过每个方法声明的前置条件 (preconditions)和后置条件(postconditions)来指定的。要使一个方法得以执行,前置条件必须为真。执行完毕后,该方法要保证后置条件为真.就是说,在重新声明派生类中的例程(routine)时,只能使用相等或者更弱的前置条件来替换原始的前置条件,只能使用相等或者更强的后置条件来替换原始的后置条件。

    3.依赖倒置原则(Dependence Inversion Principle)

    这个原则也叫做控制翻转(Inverse of Control),该原则寻求颠覆传统看法,软件的高级模块应该依赖于软件的低级模块,取而代之的,软件的的高级和低级模块都应该依赖于公共的抽象。

    此条原则的有点在于,在软件开发中,开发者不需要在开发高级模块之前完成低级模块的开发,这就使得自顶向下的模式在设计和开发中都成为可能。

    具体而言,此条原则对我来说还很模糊,也许从Springs框架中的IOC,以及依赖注入可以体会到一些灵感。

    4.接口隔离原则(Interface Segregation Principle, ISP)

    一个类对另外一个类的依赖是建立在最小的接口上。

    多个专门的接口比使用一个单一的总接口好,可以根据客户程序的需要,配置不同的接口,而不需要千篇一律的使用大的总接口。

    也就是说,接口的粒度要尽可能的小……至于怎么小,具体情况具体分析了。

    5.合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)

    在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。新的对象通过这些向对象的委派达到复用已有功能的目的。这个设计原则有另一个简短的表述:要尽量使用合成/聚合,尽量不要使用继承。

    其实这个原则我总觉得有些霸王,通过继承也能完成复用,而且有的时候继承更加能反映真实世界的关系……也许有的时候,考虑到软件对象抽象和现实的映射关系,继承的抽象是不是更能够容易理解。等过一段时间,看下自己是不是对这条原则有新的认识。

    6.迪米特法则(Law of Demeter LoD)又叫做最少知识原则(Least Knowledge Principle,LKP)

    其基本观念是,对于给定的对象,他应该尽可能少的了解其它组件的结构和属性。就是说,两个类如果要发生通信,那么最好不要直接直接发生作用,可以通过第三者转发……

    其实这个理由很扯淡,通过第三者进行转发,那么不就是了解了第三者一些信息吗?抑或用一种特定的机制进行转发,搞不懂额。

    7.单一职责原则(Simple responsibility principle SRP)

    也就是说,一个类应该仅有一个引起它变化的原因,如果这个类有多个职责,那么就应该创建新的类去完成这些职责。

    恩~说了这些原则,不过很多原则还必须通过实际项目去加深理解,原则为何在设计中起着作用,以及原则和具体的设计模式直接有什么联系,我还要慢慢体会。如果不知道这些原则,那么就是做软件不专业……多行不义必自毙。(满嘴顺口溜,你考研啊)

    March 14

    字符集的一些点滴

    字符集一直是我的噩梦……从我很小的时候就开始了,那个时候我几乎不懂什么编程,但我知道我国古代有一位伟大的军事家、政治家、文学家变巨。当然,你可能没听说过变巨,但你肯定听说过曹操。是的,BIG5码中的曹操转到国标码那就是变巨。记得当时没少咒骂那些不肯把文字简体中文化的游戏代理商,也没少咒骂那些所谓的内码转换工具。好吧,废话少说,今天上了课,基本上消除了90%对字符串的恐惧,以下随便说一些,主要说说几种双字节和多字节编码:

     

    GB2312-80,传说中的中华人民共和国国家标准汉字信息交换用编码,该编码为双字节编码,高字节的范围位0xA0-0xFE,低字节为0xA0-0xFE,编码空间为……额,94*94=8836,(其中0xB0-0xF7是汉字区)该编码又通过高低位分为94个区与94个位,就成了传说中的区位码,除去了数字啊,符号啊以及空白编码,给汉字留下的,只有6000多个。我天朝文化博大精深,汉字岂是这6000多个编码能够包罗的,所以可以看出,这个编码的确不怎么能满足需要,甚至我国前总理朱镕基的镕字都不在这个字符集中。

     

    GB13000,又叫GBK,K的意思是扩展,再怎么说,我们也不能打不出总理的名字,所以在1995年,信息技术化技术委员会又制定了GBK编码,其编码空间有了大幅度的扩充,单字节编码空间是0x00-0x7F,和ASCII码兼容。双字节编码中,高字节是0x81-0xFE,低字节是0x40-0x7E以及0x80-0xFE,以低字节7F为界,以上的都是与GB2312兼容,以下则是生僻字。GBK一共收入了21886个汉字符号,其分布如下图:

    image

    汉字区:

    GBK/2:OXBOA1-F7FE, 收录 GB2312 汉字 6763 个

    GBK/3:OX8140-AOFE,收录 CJK 汉字 6080 个

    GBK/4:OXAA40-FEAO,收录 CJK 汉字和增补的汉字 8160 个

    图形符号区:

    GBK/1:OXA1A1-A9FE,GB2312 符号+增补其它符号

    GBK/5:OXA840-A9AO,扩除非汉字区

    用户自定义区 :

    即 GBK 区域中的空白区,用户可以自己定义字符

     

    在2000年,我天朝又高瞻远瞩的扩充了编码,考虑到民族团结,将少数民族文字加入,也考虑到曾经的几个属国,所以把日语,韩语符号加入到了编码,于是GB18030-2000应运而生,考虑到囊括内容众多,所以编码最多为四字节……其规则为单字节:0x0~0x7F,双字节:高字节:0x81~0xFE 低字节:0x40~0x7E 0x80~0xFE(兼容GBK),四字节:第1、3字节:0X81~0XFE 第2、4字节:0X30~0X39

    unicode,这个东西是我们听的最多的了,也是国际推荐的编码方式,其产生就不再多说,unicode有多种使用方式,也就是我们尝尝听说的UTF-8 UTF-16 UTF-32,这类的。

    所谓UTF-32,就是用32位去表示一个编码……试想就一串英文也要用每个字母32位去编码,那么存储空间就凭空变成原来的四倍,用张指导的话说,那就是不合理。

    UTF-16嘛,这个编码的方法是,如果编码U小于0x10000,则直接用16位来表示,如果编码U大于0x10000,由于unicode编码最大范围为0x10FFFF,从0x10000到0x10FFFF直接共有0xFFFFF个编码,也就是要20个bit就可以表示这些编码。一个编码U,将其前10bit作为低位和0xD800进行或操作,将后10bit作为低位和0xDC做或操作。这样就组成了4个BYTE的编码。这个东西的好处嘛……大部分编码都以固定长度存储,但无法相容于ASCII编码。

    UTF-8,也是一个变长的编码,用1~4个Byte来表示一个编码,当长度位1Byte时,和ASCII对应,当是两个Byte时,表示拉丁文,希伯来文,3个时,表示CJK中的字符,当4个,表示其它的字符。其编码规则如下表:

    image 可以看出,编码第一字节有几个0,那么该编码就由几个字节构成,后续编码都是10开头,非常容易解析,而且和ASCII兼容。很好很强大。

     

    以上,把国内几个主流的编码简述了一下……至于其它的编码BIG5啊,之类的,需要使用的时候再去查查资料,unicode真是一种好的解决方案。

    March 05

    (zt)session和cookie的最深刻理解

    很多概念,略微了解那么一点,但是稍微细节的东西,就很模糊了,当然……作为一个挨踢人士来说这是不专业的,从网上转了一篇有关session和cookie的认识,贴在这里,没事可以来这里看看。
    --------------------我是分割线---------------------------------------
    以下是正文哟

    先说session

    对SESSION的争论好象一直没有停止过,不过幺麽能理解SESSION的人应该占90以上。
    但还是讲讲,别嫌老~

    有一些人赞成用SESSION,有一些人不赞成。但这个问题到底要怎么说。不妨听听我的看法

    如果有错误请不要朝丢东西,金条和硬币除外。

    有些人应该知道我是做江湖程序的,而江湖程序做看中的就是效率,但这里不谈设计,而

    从一些比较实际的角度看SESSION。

    首先要先说SESSION是干什么的,SESSION是可以存储针对与某一个用户的IE以及通过其当

    前窗口打开的任何窗口具有针对性的用户信息存储机制。为什么要这样说。看下边

    先研究SESSION是如何启动的,当打开IE以后浏览网站后会发出一个指令请求SESSIONID以

    及对各个类型数据的下载许可,如图片,声音以及FLASH。
    数据实际传输内容:IE到服务器
    GET / HTTP/1.1
    Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, */*
    Accept-Language0: zh-cn
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
    Host: www.jh521.com
    Connection: Keep-Alive
    服务器会返回一个没有被使用的SESSIONID让IE使用,当时IE就对返回SESSIONID做存储

    并同时返回相关页面的下载数据,如下:服务器到IE
    HTTP/1.1 200 OK
    Server: Microsoft-IIS/5.0
    Date: Sun, 30 Nov 2003 16:41:51 GMT
    Content-Length: 21174..Content-Type: text/html
    Set-Cookie: ASPSESSIONIDCACBBBRT=IBOMFONAOJFEEBHBPIENJFFC; path=/
    Cache-control: private
    然后就是页面HTML代码

    此时这个IE程序(不是客户机)的SESSIONID就为IBOMFONAOJFEEBHBPIENJFFC

    而当IE在访问任何这个站点的ASP程序的时候,就会把IBOMFONAOJFEEBHBPIENJFFC发送

    给服务器,服务器就会知道IBOMFONAOJFEEBHBPIENJFFC是表示你
    而在服务器上设置SESSION("name")="name"
    完全可以看成是
    SESSION("IBOMFONAOJFEEBHBPIENJFFC")("name")="name"
    或者
    SESSION(SESSIONID)("name")="name"
    这样,SESSION就区分开用户了。
    而当服务器反馈这个ID的时候会看这个ID有没有被使用。如果有在换一个
    反正不会让你重复,如果想模拟某人的SESSION的ID来进行欺骗是可以的。不过要获取到

    对方IE传输信号,并且在保证当时这个SESSIONID没有被取消的情况下才可能实施。

    不过要是我有那时间直接通过POST信号找他NAME和PASS了。我可不费这个劲

    想必一些人明白了了SESSIONID到底是如何工作的

    那么就在看看COOKIE,有人说SESSIONID就是COOKIE,按照技术上来讲他们不属于同类

    但是属于一种工作模式,用户和服务器传输私有数据

    当我设置COOKIE的时候,服务器会反馈给IE一个指令。IE通过这个网络指令生成COOKIE并

    存放,在特定的时候会取得这个这个信息如在访问这个站点并且COOKID有效的时候。

    那么为什么要用COOKIE而不用SESSION呢
    看下区别

    有效时间以及存储方式 传输内容
    COOKIE 可设置并在本地保留 明码信息

    SESSION 在IE不关闭并服务器不超时 只有SESSIONID

    当如果想让用户下次登入网站不需要输入用户名或者密码的时候就只能用COOKIE,

    因为他可以保留相当长的时间(在COOKIE记录被删除或者失效日期之前)

    而SESSION就不可以,他不会保留太长时间,而且IE在关闭后就自动清除了SESSIONID记录

    在下次登入的时候会请求新的SESSIONID

    而服务器想通过用户个人变量校验用户的状态的时候,就不能用COOKIE

    如果用设置用户权限是USER。而IE访问的时候就把USER的明码传输到服务器。

    那么如果我通过一定手段,比如直接修改COOKIE记录,把USER修改成ADMIN呢~~

    就麻烦了。

    但存储用户名和密码或者网站的配色方案这样的信息,用COOKIE是最好的


    好,有点累了,在说说这个东西
    Request.ServerVariables("HTTP_REFERER")

    我想有一些人通过这个Request.ServerVariables("HTTP_REFERER")
    来进行一些关键性限制,特别是对付远程提交以及非法侵入。
    那么我就要提醒下服务器取得的HTTP_REFERER信息完全是IE传输给服务器的,可以模拟
    而且难度不大,用不到半个小时就可以用VB做出一个针对HTTP_REFERER入侵程序。
    December 15

    IT人士群聚喝酒的讲究

    大家喝的是啤酒,这时你入座了……
    你给自己倒了杯可乐,这叫低配置。
    你给自已倒了杯啤酒,这叫标准配置。
    你给自己倒了杯茶水,这茶的颜色还跟啤酒一样,这叫木马。
    你给自己倒了杯可乐,还滴了几滴醋,不仅颜色跟啤酒一样,而且不冒热气还有泡泡,这叫超级木马。
    你的同事给你倒了杯白酒,这叫推荐配置。

    人到齐了,酒席开始了。
    你先一个人喝了一小口,这叫单元测试。
    你跟旁边的人说哥们咱们随意,这叫交叉测试。
    但是他说不行,这杯要干了,这叫压力测试。
    于是你说那就大家一起来吧,这叫内部测试。
    这个时候 boss 向全场举杯了,这叫公开测试。

    菜过三巡,你就不跟他们客气了。
    你向对面的人敬酒,这叫 p2p。
    你向对面的人敬酒,他回敬你,你又再敬他……,这叫 tcp。
    你向一桌人挨个敬酒,这叫令牌环。
    你说只要是兄弟就干了这杯,这叫广播。
    可是你的上司 jj 听了不高兴了,只有兄弟么,罚酒三杯。这叫炸弹。
    可是你的下级 mm 听了不高兴了,我喝一口,你喝一杯,这叫恶意攻击。
    有一个人过来向这桌敬酒,你说不行你先过了我这关,这叫防火墙。
    你的小弟们过来敬你酒,这叫一对多。
    你是 boss,所有人过来敬你酒,这叫服务器。

    酒是一样的,可是喝法是不同的。
    你喝了一杯,boss 喝了一口,这叫 c#。
    你喝了一杯,mm 喝了一口,这叫 vb。
    你喝了一杯,你大哥喝了半杯,这叫 c++。
    你喝了半杯,你小弟喝了一杯,这叫汇编。
    你喝了一杯,你的搭档也喝了一杯,这叫 c。
    你越喝脸越红,这叫频繁分配释放资源。
    你越喝脸越白,这叫资源不释放。
    你已经醉了,却说我还能喝,叫资源额度不足。
    你明明能喝,却说我已经醉了,叫资源保留。
    你喝一段时间就上厕所,这叫 cache。

    酒过三巡,你也该活动活动了。
    你一桌一桌地走,这叫轮巡。
    你突然看到某一桌的漂亮 mm,走了过去,这叫优先级。
    你去了坐下来就不打算走了,这叫死循环。
    你的老大举杯邀你过去,你只好过去,这叫激活事件。
    你向一桌敬酒,他们说不行不行,我们都喝白的,于是你也喝白的,这叫本地化。
    你向 boss 敬酒,可是 boss 被围了起来,你只能站在外圈,这叫排队。
    你终于到了内圈,小心翼翼地向前一步,这叫访问临界区。
    你拍着 boss 的肩膀说哥们咱们喝一杯,这叫越界。
    你不知喝了几圈了,只会说两个字,干了,这叫 udp。
    可是还有人拿着酒瓶跑过来说,刚才都没跟你喝,这叫丢包。

    喝酒喝到最后的结果都一样。
    你突然跑向厕所,这叫捕获异常。
    你在厕所吐了,反而觉得状态不错,这叫清空内存。
    你在台面上吐了,觉得很惭愧,这叫程序异常。
    你在 boss 面前吐了,觉得很害怕,这叫系统崩溃。
    你吐到了 boss 身上,只能索性晕倒了,这叫硬件休克。

    December 07

    【zt】99%的人不了解的真实中国历史

    99%的人不了解的真实中国历史
    1.
    秦始皇灭了六国,但并未统一中国,因为当时还有一个卫国。
    评价:大概是秦始皇在阿房宫那边的工作太忙,把这事给忘了吧。
    2.
    中国历史上最牛的权臣是南北朝时期的宇文护,他权倾朝野的时代历经西魏、北周两朝,这期间他一共杀了3 个皇帝。
    评价:彪悍的人生不需要解释。
    3.
    唐朝最强盛的“开元之治”时期,全国有户820 万,是唐朝的最高值;而隋朝“开皇之治”时期,全国就有户890 万。终唐一世,各项经济指标都没有恢复到隋朝水平。(《隋书》《旧唐书》《新唐书》等)
    评价:原来唐朝这么差。
    4.
    被“主流”专家们诟病为“军事软弱”的宋朝,对外战争的胜率超过了70% ;而被认为是军事最强盛的唐朝却在对外战争中胜少负多。(本人花了8 个月时间,看过《新唐书》《旧唐书》《宋史》《辽史》《金史》《元史》《西夏书事》《续资治通鉴长编》《三朝北盟会编》《建炎以来系年要录》《续资治通鉴》《宋会要辑稿》等书统计出来的,时间较紧,或有错漏,但>70% 是没问题的。)
    评价:原来我们以前学到的历史都是骗人的。
    5.
    宋朝士兵的平均作战负重是32 公斤,训练负重还要高于这个数值;现在美国最精锐的特种部队的训练负重不到15 公斤。 (《建炎以来系年要录》《宋史》《宋会要辑稿》等)
    评价:人类在退化? (身体方面)
    6.
    中国在北宋神宗元丰年间(1078-1085 ),城市化率达到惊人的30% 以上;在所谓“康乾盛世”时代,这一比例也不过9% ;新中国在21 世纪初才重新达到这一数值。 (《元丰九域志》《续资治通鉴长编》《宋史》等)
    评价:原来历史并不总是进步的。
    7.
    同样是元丰年间,中国华北地区的钢铁年产量达到了15 万吨;工业革命后的英国在1788 年钢铁产量才达到7.6 万吨。
    评价:原来宋朝并不跟我们想象的那样差。
    8.
    整个北宋时期,中国一直在扩张领土,直到“靖康之变”的前一年(1126 )才停止。(《续资治通鉴长编》《宋会要辑稿》等)
    评价:看来我们真的误会宋朝了。
    9.
    秦桧所创的“宋体字”是整个南宋时期的官方文书通用字体,尽管南宋大多数时间都将秦桧作为反面典型。
    评价:看来宋朝时期的中国人还是挺理性的。
    10. 整个南宋时期,中国一直没有定都,临安府的级别不过是“行在”(陪都)。
    评价:但最后跟定都没啥区别了。
    11. 南宋之所以一直偏安并非统治者不愿收复故土,而是因为民间反对的声音太大。更让人意想不到的是,民间之所以反对是因为担心收复故土后宋廷会增加江南地区的税收来扶植已被女真人严重破坏的收复区。
    评价:其实,人类就是这样的自私。
    12. 南宋时期,熟练雇工的工资高于知府工资。(宋朝知府一般是从三品或正四品,年薪在400 贯以上,至少相当于今天的15 万元人民币)
    评价:貌似现在只有欧美发达国家才这样。
    13. 中国南宋时期最先进的织布机有1800 多个活动构件,其中有的技术是现代化织布机也无法达到的。
    评价:宋代中国人的创造力真不是盖的。
    14. 南宋中后期,中国(仅指宋)的人口占世界人口的15% 左右,经济总量却占到了全球的75% 以上;今天我国人口占世界人口的21% ,经济总量占全球比例却仅为4% 。
    评价:只能说那时的中国更像中国。
    15. 在1233 年宋蒙联军攻打蔡州的时候,南宋名将孟珙救了蒙古将领张柔一命。4 年后,张柔的儿子张弘范出生,而崖山一战灭亡南宋的就是这位张弘范。 (《宋史》《元史》等)
    评价:这让我想起了农夫与蛇的故事。
    16. 南宋数学家秦九韶至晚在1247 年就已经发现一元三次方程的求根公式,欧洲人在400 多年后才发现,但在中国的课本上这个公式仍是以那个欧洲人的名字来命名的。 (《数学九章》等)
    评价:原来我们祖先什么都有,只是我们不知道罢了。
    17. 蒙古帝国军队在13 世纪的征战中共屠杀了2 亿人,成为了人类历史上最不光彩的一项吉尼斯纪录,其中,仅中国境内就有6000 万人死于蒙军屠杀;与之相比,纳粹屠杀的600 万犹太人简直是小巫见大巫。(《多桑蒙古史》《宋史》《金史》等)
    评价:原来这就是“我们”“伟大”的成吉思汗,那些崇拜它的人赶快醒醒吧!
    18. 明朝在永乐年间与越南发生了一场战争。此战明朝军队号称80 万,而越南军队更是号称700 万,实际上双方军队总数加起来也不过30 万,这毫无疑问是中国历史上牛皮吹得最大的一场战争。[ 注:那时越南还是中国领土] (《明实录》等)
    评价:幸亏朝鲜、越南都脱离中国独立了,要不然我们今天得跟棒子一个德行。
    19. 郑和下西洋的船队中吨位最大的宝船排水量约为7500 吨。
    评价:排水量都接近航母标准了,比较恐怖。
    20. 中国历史上唯一一个只娶一个老婆的皇帝出现在明朝,他就是明孝宗朱祐樘。
    评价:确实是皇帝中的异类。
    21. 中国最早提出计划生育理论的不是马寅初,而是明朝万历年间(1573-1620 )的内阁首辅申行时,而最早实施计划生育的时候却是在更早的宋朝。
    评价:其实,理论都是马后炮。
    22. 明朝的万历皇帝曾连续28 年不上朝,创造了中国皇帝的纪录。但是,这期间中国无论对内还是对外的战争均获得全胜。
    评价:所谓“明亡于万历”,原来如此。
    23. 自1572 年“隆庆开关”到1644 年明朝灭亡,这70 多年的时间里,全世界生产的白银总量的1/3 涌入中国,共计约3.53 亿两(保守估计,目前重新估计约为5亿两)。并且全球2/3 的贸易与中国有关。
    评价:资本主义萌芽?这简直是一只脚迈入资本主义了。
    24. 16 世纪西班牙殖民者在南美掠夺的白银几乎都通过贸易的形式最终流入了中国。
    评价:敢情是中国间接掠夺了美洲人民。
    25. 中国在明朝中后期曾经研究过坦克。 (《武备志》等)
    评价:这个比较雷。
    26. 1644 年中国北方爆发了一次大型鼠疫,造成驻守北京的20 万明军全军覆没,这直接导致了明朝亡于李自成。
    评价:人到底还是没法胜天的。
    27. 被使用时间最长的年号不是康熙,而是明朝最后一个年号——崇祯,一共被朝鲜使用了265 年。 (《朝鲜李朝实录》等)
    评价:那时候棒子还是挺重义气的。我也终于明白,进化论并不适用于棒子。
    28. 清朝乾隆时期修《四库全书》,总计存书(被认定为合格著作正式入库)3457 部, 79070 卷,存目(被认为不合格的著作,留存书名后付之一炬)6766 部,93556 卷。
    评价:真不知道这是修书还是毁书。
    29. 发动第一次鸦片战争是英国议会通过投票产生的结果,支持战争的一方仅获得9 票的优势。
    评价:历史根本就是“上帝在投色子”,所谓历史大势基本纯属胡扯。
    30. 第一次鸦片战争结束后,有人在京城某仓库意外发现一批封存了200 多年的火器。这批火器是明朝末年制造的,其中有的竟然比英军的装备还要先进。
    评价:这么多年都干什么了?不过,这也只能问清朝皇帝了。
    31. 清朝的道光皇帝为人十分节俭,喜欢穿有补丁的衣服,但打个补丁却要花四两银子。
    评价:这冤大头,被人蒙了还不知道。可悲!
    32. 第二次鸦片战争时期,清军曾以12 万对阵2 万英法联军,结果阵亡3 万余,而对方仅阵亡4 人,但非战斗减员却有几十人。
    评价:嗯,这个……
    PS:本人初稿的一部分,欢迎拍砖。版权所有,禁止转载。这是我07年偶尔写着玩的一篇稿子,当时只有几条,最近不小心翻出来,就完善并发表了一下,大家看过后能受到一点点启发就好。当然,这只是历史,还是希望大家以当前为重,努力重现我国昔日之辉煌。
        (本人是宋粉+唐黑,竭诚欢迎各路高段唐粉前来捣乱~~~清粉靠边站)
    补充1.辽道宗耶律洪基曾写诗表达了后悔自己生在蛮夷之国和“来世做中国(宋)人”的愿望。与之类似的是,宋仁宗庆历六年(1046),朝鲜国王王颛也写诗表达了类似的愿望。
    评价:其实,金庸小说的历史背景基本都是反的。当时萧峰阻止辽军南下,其实是救了他们。
    补充2:南宋灭亡的时候,日本“举国茹素”来哀悼大宋的灭亡。(《心史》等)
    评价:进化论同样不适用于鬼子。
    补充3:南宋开禧二年(1206)中央财政收入达到8000万贯钱,当时全国人口约7000万。清朝到1900年代为8000万两,人口却有4亿。而南宋1贯钱购买力是清末一两白银的2倍。更重要的是,宋朝税率低于清朝。
    评价:当我们的“专家”在不遗余力地为“康乾盛世”大唱赞歌的时候,是否想到了有一个朝代正在默默地受着后人们无端的非议,而这个人民生活水平十几倍于“康乾盛世”的朝代却正在人们尽情挥洒的刀笔之下“积贫积弱”着呢。

    November 04

    并不凄凉的OT

    昨天一个不小心,居然OT到两点才回家,本想下班后立即回去,看会书然后好好睡个觉,但是现实是残酷的。临时任务一来,马上开始投入战斗,在被同学K陷害几次后,成功的在9点左右结束了战斗。此时同学K开始抱怨今天晚上搞不定任务了,此时本想一走了之,但是把同学K丢在这里,不仁不义,于是劝他慢慢工作,大不了一起去挤我的小黑屋。于是事情如预料中发展,大约2点,我们终于把事情处理完了。然后两人如同孤魂野鬼般匆匆回家,其实我发现凌晨的上海浦东更加魔幻,雾气笼罩,烟云缥缈,然后这时候如果来几个鬼怪,马上可以就地拍摄一部不错的鬼片。可惜浦东这边乃上海郊外,不要说鬼,一路上连人影子都没见到过。进了小黑屋,稍作调整,便倒头大睡,同学K甚至在第二天说自己睡的像死猪般。不过还好,没听见鼾声。

    实际上OT到这种程度也不算什么,稍微给几个同学抱怨了一下,什么蛮夷的时差啊,什么机器故障啊,很惨很凄凉啊,等等。不过结合到最近的形势,的确颇有感触,自从金融危机发生后,外国企业如同宰猪一般的裁员,一时间搞得人人自危,本科生今年的就业形势相当不好。不过也有在风雨中稳坐钓鱼台的人,反正牛人不愁找不到工作。自己的前途是比较确定了,但这似乎并没有减轻自己的压力,小学想初中,初中想着考高中,高中想着考大学,大学里面想着找工作,真有了工作,又一大堆问题也来了,自己是否为职业做好了准备啊,什么职业生涯的规划啊,什么找老婆啊,怎么养家糊口啊。以前总觉得这些问题还离自己很遥远。现在兵临城下了,却还没有对策。不过这也是当然的,刚开始嘛,有对策才奇怪呢……不过最近一个月来,这些问题如同幽灵般在我周围萦绕。有的时候,也感觉自己很无助,平时可以带着一种自信去面对很多事情,但安静下来想想,还真不知道这种自信来自何方。总以为自己能处理很多事情,结果仔细想想,还不是在家靠父母,出门靠兄弟。自己的学生生涯即将结束,在学校时很想出来见见世面,现在却无比怀念学校里泡自习室的时光,可以不顾周围一切的看书,然后累了摆开睡一会。现在听见电话想就知道自己又有事情做了,学的东西零零散散,浅尝辄止,不成体系。这一切,构筑了少年老肖的烦恼。不知道这算不算是成长中的一些轨迹,也许是自己对自己要求太高了,总对自己的现状不满足吧。好吧,根据自己的历史状况来看,面对的问题都是解决了的,这回也照旧吧。

    也许这些是我职业生涯即将开始的一些思考吧。再过上10年,到时候是满足于过着猪一般的生活,还是意气风发,指点江山。我现在每天对自己说,I have a dream,这句话究竟是受了刺激后所发出的歇斯底里的吼叫,还是蕴藏在自己内心的声音?也许很多人都可以在某种作用力的激发下说出这句话,但是最终很多人都随波逐流。我是一个很平凡的人,但平凡的人也可以改变这个世界,也能有自己的梦想。也许自己现在对这些压力感觉到有点压抑,但是千里之行,还是得始于足下的。我希望10年后,告诉自己,I have a dream这句话是我心里最珍贵的东西。

    October 21

    Factory Method

    终于遇到一个比较熟悉的模式了,先上一段代码,根据书上类图实现的:

    class Product
    {

    };

    class ConcreteProduct : Product
    {
    };

    class Creator
    {
    private:
        Product* product;
    public:
        virtual Product*  FactoryMethod() { return 0;}
        void AnOperation() { product = FactoryMethod();}
    };

    class ConcreteProduct : Creator
    {
        Product* FactoryMethod() { return new ConcreteProduct();}
    };

    适用性:

    当一个类不知道它所必须创建的对象的类的时候。

    当一个类希望由它的子类来指定它所创建的对象的时候。

    当类将创建对象的职责委托给多个帮组子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

    自然,工厂方法还有很多其他的实现方式,比如模板:

    class Creator

    {

    public:

        virtual Product* CreateProduct() = 0;

    };

    template <class TheProduct>

    class StandardCreator: public Creator

    {

    public:

        virtual Product* CreateProduct()

        {

            return new TheProduct;

        }

    };

    Builder

    老肖的脑残实现如下:

    class Product
    {

    };

    class Builder
    {
    public:
        virtual Product* BuildPart();
    };

    class ConcreteBuilder1 : Builder
    {
    public:
        Product* BuildPart() { return new Product(); }
        void GetResult();
    };

    class ConcreteBuilder2 : Builder
    {
    public:
        Product* BuildPart() { return new Product(); }
        void GetResult();
    };

    class Director
    {
    private:
        Builder** builders;
    public:
        void Construct()
        {
            builders = new Builder*[2];
            builders[0] = new ConcreteBuilder1();
            builders[1] = new ConcreteBuilder2();

            for(int i=0;i<2;i++)
            {
                builders[i]->BuildPart();
            }
        }
    };

    很显然,我觉得书上的类图和书上的代码实现有出入,既然是Builder对象的聚合,那么为何只作为Director的构造函数的参数进行传入。如果完全按照类图的实现,就像上面的代码那样脑残,在一个循环创建后,根本无法保留创建后的状态,难道类图中隐藏了Dirctor对于Product的聚合?抑或是根据每个Builder对象进行操作,但每个Builder对象对于Product只是个弱引用而已,不解!

    --------------------过了一天,我是分割线-------------------------------------------------------------

    书上的图,很令我迷茫,但是在今天和同学K讨论后又似乎有那么点开窍。

    首先,关于聚合的概念需要进一步更新,聚合意味着一个对象拥有另一个对象或对另一个对象负责。并且聚合对象和其拥有者具有相同的生命周期。于是乎写出如下代码:

    老肖的非脑残实现如下:

    class Product
    {

    };

    class Builder
    {
    public:
        virtual void BuildPart();
        virtual Product* GetResult() { return 0; }
    };

    class ConcreteBuilder : Builder
    {
    private:
        Product* currentProduct;
    public:
        void BuildPart() { currentProduct = new Product(); }
        Product* GetResult() { return currentProduct; }
    };

    class Director
    {
    private:
        Builder* builders;
    public:
        Director(Builder* outBuilder)
        {
            builders = outBuilder;
        }
        void Construct()
        {
            builders->BuildPart();
        }
    };

    适用性:

    当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式时。

    当构造过程必须允许被构造的对象有不同的表示时。

    October 20

    Abstract Factory

    老肖的简单代码实现如下:

    class AbstractProductA
    {
    public:
        virtual void CreateProduct();
    };

    class ProductA1 : public AbstractProductA
    {
    public:
        void CreateProduct();
    };

    class ProductA2 : public AbstractProductA
    {
    public:
        void CreateProduct();
    };

    class AbstractProductB
    {
    public:
        virtual void CreateProduct();
    };

    class ProductB1 : public AbstractProductB
    {
    public:
        void CreateProduct();
    };

    class ProductB2 : public AbstractProductB
    {
    public:
        void CreateProduct();
    };

    class AbstractFactory
    {
    public:
        virtual AbstractProductA* CreateProductA();
        virtual AbstractProductB* CreateProductB();
    };

    class ConcreteFactoryA : public AbstractFactory
    {
    public:
        AbstractProductA* CreateProductA() { return new ProductA1(); }
        AbstractProductB* CreateProductB() { return new ProductB1(); }
    };

    class ConcreteFactoryB : public AbstractFactory
    {
    public:
        AbstractProductA* CreateProductA() { return new ProductA2(); }
        AbstractProductB* CreateProductB() { return new ProductB2(); }
    };

    适用性:

    一个系统要独立于它的产品的创建、组合和表示时。

    一个系统要由多个产品系列中的一个来配置时。

    当你要强调一系列相关的产品对象的设计一边进行联合使用时。

    点你提供一个产品类库,而只想现实他们的接口而不是实现时。

    October 03

    无锡轻松游

        无锡是个好地方。当年江泽民书记在我绵阳逛了一圈后,随口说了句“绵阳是个好地方”,于是这句话迅速走红,进而衍生出上有天堂,下有苏杭,除了美国,就是绵阳。因为“绵阳是个好地方”。无锡是个好地方,在火车站附近随处可以看见有这样标语的广告牌,难免让人猜测,当年是不是也是某个名人在这里逛了圈,然后下了个这个结论。虽然我不算个名人,不过就逛了这3天来看,无锡的确是个适合居住的地方。

        当然,我这样下结论会败掉很多人品,事实上我这次出行,人品确实不好。早上坐的动车,因为不明原因故障,晚点了1个小时,但是去无锡也只需要45分钟左右,基本晚点100%。但是我觉得无锡是个好地方,自然是有理由的。虽然如同全国很多城市一般,无锡也是那种大楼林立的城市,和我预期的江南水乡有很大的差距。也许在某些城市的角落中,依稀可以看到一条河,河边几户人家,人家边一两条船歪着。这时同学会告诉我,这里属于没拆完的地带,还有那条河也基本是死水了……所以理想和现实总是有不可调和的矛盾。抱怨如此多,依然无法掩盖这座城市其独特的魅力,以下我随便写写在这里的见闻,也许以后去无锡的机会不多,但是每当我翻开这里,我还可以从记忆的碎片中去搜索那轻松的3天。

     

    崇安寺步行街

        虽然我说,无锡和全国很多城市没太多的区别,也就是说,把一个住在上海的四川人绑架了,然后扔到无锡,也许他还以为自己在上海。不过呢,无锡的崇安寺步行街的确浓缩了无锡的民俗风情。古色古香的建筑,可以让人感觉一下江南特有的建筑美学。亭子边有咿咿呀呀的锡剧,就如同外地人不明白四川为何到处是麻将声,我也不明白锡剧唱的是什么,听同学说,无锡方言如同天书,不可意会更不可言传,然后再变点调子唱出来,我能听懂那才是奇怪。步行街上不缺少坑人的奸商,不过那些商品对我来说的确缺少了点吸引力,反正全国到处都买得到。步行街的地下别有洞天,虽然也是众多奸商的聚集地,但是布置非常的精巧,一面墙壁上有飞流而下的瀑布,墙上用我能看懂的字体写着王羲之的兰亭集序。就如同上海的南京路上有众多的老上海建筑,反映了上海一个世纪的变迁。崇安寺步行街上的建筑和人文风情,也向世人展示着无锡的民俗。

    水路九连环

        所谓江南水乡,必然是要水路开道。无锡的水多,一会又是什么湖,一会又是什么河。只能说,我可以通过这些河湖想象一下当年江南水乡的味道……一个美丽的江南姑娘,撑船采菱角,或者采个莲蓬什么的。然而同学的一句“一旦蓝藻爆发……”将这些想象撕得粉碎。于是感叹一下当代的发展,产生了丰富的磷资源,不和谐,不环保。突然同学告诉我,那就是传说中的京杭大运河。只见一条大河,涪江一般宽。只不过涪江可以淌过去,这里还是船只来来往往,非常热闹。由于只是乘车路过,没有时间去凭吊运河。不过也还是有点奇怪的想法。京杭大运河是隋炀帝为了驾临江都游江南所挖造,当年他游江都也是极尽奢华,两岸尽种柳树,一路美女拉纤。虽然隋炀帝最后不得好死,但是他主持挖造的运河却流淌到了现在,而且还有很好的经济效益。不得不说,人家连腐败都这样有水平。

    无锡三国城

        我是个铁杆三国迷。所以三国城是不得不去游览的地方。无锡三国城背负着我朝廷台的影视基地,以及其他无数头衔,最后只证明了一句话,盛名之下,其实难副。首先国庆期间人山人海这就不说了,我觉得这种地方还是比较适合少一点的人,然后去真正的感受一下曹操骊酒临江,横槊赋诗的那种情怀。三国城中,蜀国被华丽的无视掉了,当然,这里到处是水,蜀国又是在四川那种难于上青天的地方,这样无视倒也说得过去。于是三国演艺就成了双城记。一路上有很多景点,比如曹操点将台,曹操书房,曹操寝室,曹操XXX,搞得成了曹操故居那样……以及吴王宫,里面是烧香的地方。凤仪亭,里面不见吕布与貂蝉,反而是一个丢沙包拿奖品的地方,也许是想让人扮演董卓,当年董卓用画戟丢吕布不中,现在给游客一个机会,还有什么孙尚香酒家,自然是园内宰人的地方。由于城中主题是赤壁之战,所以我也体会了一下坐战船的滋味。然而,战船毕竟是给当兵的人坐的,自然不可能像给曹操那样舒服,挤了一船的人后,然后船缓缓从水寨开出,一旁的同学不断的提醒我远方飘荡的蓝藻,战船的舵手自然也不想让士兵们看到太多的蓝藻而恶心,于是10分钟左右边匆匆鸣金收兵。所以我说其实难副,也是有道理的。三国城中还有两场表演,一场是服装展示,只是让我产生了,刘备,西门庆,李隆基,贾宝玉是同一时代人物这样的幻觉。另一场表演则相对不错,三英战吕布,其实并不是战斗场面多么壮观,甚至在冲锋时刻,一个攻城的车辆当众抛锚。参加演出的演员,表演都很专业,能在马上做出各种有难度的动作,仿佛为了证明奥运会他们没有出现在鞍马场上是一种浪费,有演员从马上摔下来,然后连打几个滚,最后扮作尸体,直到被杂兵抬出,栩栩如生。一阵喧嚣过后,我也情愿的被人群冲出了三国城。同学的最大体会是,“等我到时候出名了,要合影,可以,10块钱一人”。

    蠡园

        很难想象,在搜狗拼音中居然有这个现成的词组。我的确孤陋寡闻,在来无锡之前并不知道这么个园。这个蠡,还真是伟大的那个范蠡,陶朱公,商圣,也许还有其它头衔无数,范蠡大人是聪明人,他深知兔死狗烹,鸟尽弓藏,得鱼忘筌,过河拆桥,卸磨杀驴的道理,帮助勾践搞定夫差后,就和西施妹妹隐居于五湖了。这个蠡园,据说就是当年范蠡大人和西施隐居之处……当然,也许是后人强加上去的。这噱头并不重要。园林的风格,我很难说清,因为自己对园林艺术了解不多。不过依仗着周围的湖泊,风景不算太差,而且林中的一座假山,还是让人体会到了什么叫曲径通幽和峰回路转。一路上,我都和同学讨论,如果自己有这样一个园,该有多好,苔痕上阶绿,草色入帘青,来往有鸿儒,往来无白丁。这就不叫简陋了,而叫做档次。出则俯仰湖山,入则读书修身。神仙般的日子,不过如此。不过买下一个园,还是需要点钱的……

    无锡美食

        到了21世纪,我们能够很轻松的吃到其他地方的美食。当然,能够吃到当地的特产,那自然更好,以后看到别人吃那些,还可以稍微摆一下谱:我当年吃的那个才是正宗的……在无锡,我第一次吃到了菱角。菱角本不是什么无锡特产,但是在菱角前加上太湖二字,便摇身一变了。菱角这东西,板栗一般吃法,稍微吃几个,感觉还是不错,吃多了就感觉不舒服了。此外,王兴记的馄饨我也品尝了,都快两点了,还排着一字长蛇阵,足见其名气。馄饨味道不错,就是甜了一点,不过能感受一下,也就知足了。

    以上,就是无锡轻松游的一些点滴。还是那句话,理想虽然和现实有巨大的差距,但是现实中也有美,我要做的,只是去发现他们,感受他们。

    September 15

    你能行的

    恩,这几天,很多人对我说了这句话,尽管这是句客套话,但我还是很惊奇,曾经的我,做事是那么令人不放心。自从进入大学,仿佛自己做事情变得稳重了,也会思考了,几个朋友曾经说过,感觉我在任何时候都有办法,永远不会不知所措。也许是我真的成熟了,遇到大部分问题都可以迎刃而解了。很欣慰,也为以后一段时间所面临的困难,有了更多应对的勇气。

     

    今年中秋,大雨,没有月亮,赏月计划自然搁浅。于是给故人打了一个电话,两年多没听见她的声音,居然一点没变,跟记忆中那个温和甜美的声音一模一样。很久没有聊了,自然是口若悬河,滔滔不绝,互相说说自己的近况,对未来的打算啊,这类的。她惊讶于我家庭主男般的生活,我敬佩她考研冲击3%概率的勇气,谈话持续了接近一个小时,并在友好的氛围中结束……我刚充的话费基本没有了,囧。现在的大学生可真是不容易,无论是找工作,考研还是出国,都是千军万马,好不壮观,一将功成万骨枯。社会让我们分出一个高下,我们只有去接受这些竞争。但现在的努力又能说明什么呢?正如我一个同学聊天时说起,过个10年20年不都是抱着孩子打麻将。哈哈,也许是我太消极了。但是我还是想在年轻时做一些有成就感的事情,还是 那句话,不要因为虚度年华而悔恨,不因碌碌无为而羞耻……

     

    生存的压力总是会有的,而且有的时候会很大,于是几乎每个男人都有不同程度的失眠。这个时候,我会想起某个人,想把他拉过来奚落一顿,如果那人在旁边,就直接施加暴力。当然,这只是玩笑话,我只是觉得,现在已经能很好的控制这种压力了,考虑到毕业以后,工作和生活的问题,总让人感觉到头疼。不过我相信车到山前必有路,我会一一处理这些问题的。哈,朋友们说我对任何问题都有办法处理,从大学以后,的确是真的,自然,对于马上要面临的挑战,我也能行的。