没有合适的资源?快使用搜索试试~ 我知道了~
使用序列化的系统的测试和分析
理论计算机科学电子笔记116(2005)171-184www.elsevier.com/locate/entcs使用序列化的系统的测试和分析乔瓦尼·德纳罗和莱昂纳多·马里亚尼Universit`adegliStudidiMilanoBicoccaDipartimentodi Informatica,Sistemistica e ComunicazioneVia Bicocca degliArcimboldi,8 I-20126 -米兰,意大利{denaro,mariani}@ disco.unimib.it摘要对象序列化有助于将结构化对象转换为字节流,因此对于所有强烈依赖组件之间数据交换的基于组件的应用程序来说都很不幸的是,实现和控制串行化机制可能会使软件暴露于细微的错误。本文为专门针对使用序列化的软件评估的测试和分析技术铺平了道路特别是,我们引入了一个税收的抽象和术语的语义特征和分类的主要数据交换的情况下,序列化可以参与。由此产生的概念框架提供了一种方法来预测如何错误的实现序列化会看起来像在不同的情况下,从而使测试和分析技术的重点,以解决序列化相关的故障。关键词:对象序列化,测试,软件分析,数据交换1介绍基于代理的软件应用在若干应用领域中越来越流行在一般情况下,这些应用程序由一组独立开发和独立部署的软件组件组成,这些组件通过在分布式或本地环境中协调和交换数据来实现共同的目标。高级通信1这项工作部分由意大利政府在QUACK项目(QUACK:新一代集成嵌入式系统质量平台1571-0661 © 2004 Elsevier B. V.根据CC BY-NC-ND许可证开放访问。doi:10.1016/j.entcs.2004.02.075172G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171机制倾向于促进组件之间的交互。特别地,交换的数据表现为对象,即,抽象数据类型的实例及其运行时状态。例如,在分布式组件技术中,组件方法可以通过跨进程和网络边界传递的对象参数远程调用[1]。类似地,在移动代码技术中,组件(也称为执行单元)可以与本地数据对象集合一起通过网络迁移[2]。作为另一个例子,数据持久性的常见实现是通过将对象休眠到文件系统和从文件系统恢复对象来实现的。然而,尽管对象可能具有复杂的结构,但在一般情况下,数据交换是通过低级管道进行的,这些管道仅支持字节流抽象,例如文件和TCP套接字。对象序列化可以弥补这一差距,便于将对象重命名为字节流。当一个对象被序列化时,被递归序列化的值和类型都被保存在目标字节流中,并带有足够的信息,以确保稍后可以重新创建等效的类型化对象;引用的对象(如果有的话)也被递归序列化。序列化是从序列化表示重新创建对象(或对象图)的对称过程。编程语言,如Java和C#,广泛用于实现基于组件的软件,提供了内置的原语用于序列化(串行化)对象和在某种程度上控制序列化过程的机制[12,13]。作为后者的一个例子,Java允许显式指定在序列化期间必须忽略的对象引用,从而防止对象图的某些部分在根对象被序列化时被序列化。在C#中有一个类似的工具。在许多实际情况下,控制序列化非常有用。有趣的是,Ghezzi,Martena和Picco描述了一种基于控制序列化优化远程方法调用性能的方法:他们的方法通过修剪作为参数传递的对象的未使用的子图来减少网络开销[3]。不幸的是,更改序列化的默认行为会使软件暴露于细微的错误。例如,考虑两个组件交换一个对象的情况,这涉及到序列化。如果接收组件假设对象结构与发送组件中定义的一样,但在序列化过程中结构被修改,则可能会出现细微的错误和失败。本文的第一部分报告了一些可以解释为使用序列化的软件故障的示例案例然而,据我们所知,在当前的研究和工业实践中,缺乏在存在序列化的情况下解决软件这激发了我们的研究G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171173专门针对使用序列化的软件评估的测试和分析技术。作为实现这一目标的第一个里程碑,本文的主要贡献是定义了一个推理序列化的概念框架我们引入了一个分类的抽象和术语的语义特征和分类的主要数据交换的情况下,序列化可以参与。我们在已识别的语义情况和对序列化的语言支持之间建立了联系我们的概念框架提供了一种方法来预测序列化的错误实现在不同情况下会是什么样子。这使得测试和分析技术能够集中于解决与串行化相关的故障。作为对我们研究的初步评估,我们概述了定义的概念框架如何促进传统数据流测试[6,5]的思想的适应,以验证序列化。我们说明的框架和例子是指Java,但我们相信,这些想法可以很容易地移植到其他编程语言。本文的部分工作涉及Fuggetta、Picco和Vigna在[2]中提出的概念。参考移动代码系统,这些作者区分移动性机制的三个元素的程序:代码,执行状态和数据空间。特别是,数据移动性涉及跨计算环境的结构化数据的传输,并且通常通过序列化来实际实现[10]。我们的数据交换案例分类可以被认为是[2]中提出的数据移动案例的扩展。本文的结构如下。第2节举例说明了一些可以解释为使用序列化的软件错误.第3节提出了一个概念框架推理的测试和分析技术的串行化。第4节举例说明了我们的框架如何支持基于数据流的序列化相关故障测试的定义。第五部分总结了本文的研究成果,并对未来的研究方向进行了展望。2Java中的串行化错误Java提供了使用和控制串行化[4]。对于可序列化的对象,它们需要实现接口Java.io.Serializable。 这个接口只是作为可序列化对象的标记,而它不包含 任何进一 步的定 义。可 以使用 ObjectOutputStream 类的 方法 voidwriteObject(Object)将可序列化对象重命名为流,并且可以使用方法从重命名的表示形式重新创建序列化对象174G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171Fig. 1. (a)表示对象O 1的结构,而(b)是O 1的序列化版本类ObjectInputStream的void readObject(Object)。默 认 情况下,当对象被序列化时,其整个对象图被序列化,即,所有直接和间接引用的对象也被递归地序列化。序列化的默认行为可以通过将对象引用标记为瞬态来改变,从而中断对象图的a分支中的递归,或者为特定的对象类重新定义方法readObject和writeObject。序列化可能是软件系统中细微错误的根源。关于Java,本节提供了在特定情况下可以解释为序列化2.1序列化的传递闭包复杂对象的状态通常由其他对象组成例如,Person对象的状态可能包括一个Address对象,其中包含街道、城市、邮政编码等信息。默认情况下,在Java中,Person对象的序列化意味着对应Address对象的序列化一般来说,对象o的序列化是通过递归序列化o中引用的所有对象来执行的。然而,在许多情况下,序列化任何引用的对象并不是最好的选择。例如,如果我们通过网络发送一个包含对本地服务提供者的引用的对象,在大多数情况下,我们不希望将服务提供者状态与对象状态一起序列化。在序列化过程中不必考虑的对象属性可以用关键字transient指定。参考示例,如果我们指定引用本地服务提供程序作为临时的,则在序列化,而是将空值添加到流中。图1说明了通用对象图的这种行为:图1(a)中对象o1的序列化导致图1(b)中的图,其中已经去除了瞬时边缘E2和E4初始化后的临时属性可能会导致失败。例如,让我们考虑以下代码:G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171175//序列化对象public class Person implementsjava.io.Serializable{ private transient Personparent;...public int getParent(){return parent;}...}//错误的沙漠化//...打开流“targetStream”Person p=(Person)targetStream.readObject();//将对象Personp2=p.getParent();System.out.println(p2.getName()); //这很可能是一个错误!!//关闭流“targetStream”.Person类的parent属性被指定为transient,因此当person对象被序列化时,它会被跳过。上面代码的最后一条指令注定会失败:由于非线性化,p2将具有空值,并且方法getName的调用将导致NullPointerException。2.2构造函数的静态属性和复制静态属性解决了在类的所有对象之间共享的属性的定义。在Java中,静态属性的值默认情况下不会被序列化。因此,当对象从流恢复时,它的静态属性从新的运行时上下文继承。然而,如果不考虑所有可能的绑定,这可能是潜在的故障源。例如,让我们考虑一个包含静态属性样式的类WebPage,它引用了一个存储给定上下文中所有页面的样式属性的类PageStylepublic classWebPage实现java.io.Serializable{ publicstatic PageStyle style;...}当一个WebPage对象被序列化时,包含的静态引用被忽略。 当对象被格式化时,样式被设置为新环境中的PageStyle对象。但是,如果属性在新环境中未定义,则已定义的WebPage对象的属性样式将继承默认引用值,即,空引用。 这可能产生不期望的效应(例如,如果在使用前没有检查style的值,则会出现NullPointerException)。在某些情况下,处理静态属性可能需要修改序列化的默认行为。这通常176G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171构造函数方法中的部分代码。事实上,构造和实例化在某种程度上是类似的操作:尽管实例化不会创建新对象,但在两种情况下都将对象添加到系统中。例如,让我们考虑以下代码:public classSon实现 java.io.Serializable{ publicstatic intnumberOfSons;public int findDuplicate(){findDuplicate ++;}...}静态属性numberOfSons意味着计算在任何时刻存在于系统中的类Son的实例的数量。 当创建此类的对象时,构造函数会递增实例计数器,其值在该类的所有对象之间共享。当从流中恢复类Son的对象时,实例化也应该更新实例计数器。 处理这个需求的一种方法是在类Son中定义一个自定义的readObject方法,模仿构造函数的行为,如下所示:private void readObject(ObjectInputStreamois)抛出IOException,ClassNotFoundException {public int findDuplicate(){numberOfSons++;}}从构造函数复制代码在实践中经常是这样用于在存在静态属性的情况下自定义所述数据化过程。然而,对于复杂的程序,很难正确选择必须复制哪个构造函数和哪个部分。这就产生了新的潜在故障源。2.3类继承属于继承自其他类的类的对象的序列化,需要休眠一个跨越继承树分支中所有类的对象状态。只要所有涉及的类都实现了java.io.Serializable接口,这种行为就不会导致问题,但是如果其中一些类是不可序列化的,则会出现意想不到的结果。让我们考虑以下代码:public class Person { privateString name; privateString name;G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171177--Person(){name=""; surname="";}Person(String n,String s){name=n; surname=s;}}public class Person extends Person implementsjava.io.Serializable{ private String universityId;...}不可序列化的类Person有两个字符串属性,name和姓, 一个没有参数的构造函数,它同时包含两个属性空字符串,还有一个构造函数为属性提供特定的值可序列化类Student继承自Person,并添加了另一个属性universityId。如果我们序列化和非序列化学生实例“Leonardo“,“Mariani“,“UMB 1556445“,我们得到的结果是一个universityId为“UMB 1556445“的学生,但是空的名字和姓氏。这是由于类Person的不可序列化。一般来说,当一个类如果继承树中的对象是不可序列化的,则序列化的默认行为将忽略其属性,并在序列化时通过默认构造函数重新构造这些属性。在本例中,name和surname在使用Person类的无参数构造函数的非线性化。可序列化类的继承树中的不可序列化类需要特别考虑,并且可能会导致细微的错误。2.4不兼容的类版本在基于组件的应用程序中,不同的组件可能指的是同一类的不同版本,例如,因为平台在不同的时刻进行了更新。组件接收到的序列化对象可能与类的保持版本不匹配。在这种情况下,虚拟化将失败下面的代码显示了Point类的两个可能版本分别使用笛卡尔坐标和极坐标://笛卡尔坐标版本public class Point实现java.io.Serializable{ privatedouble x;私有双y;...}//极坐标版本public class Point实现java.io.Serializable{ privatedoubler;private double phi;...}一般来说,在一个对象中转换类版本C1的对象的问题类版本C2的对象包括三个数据元素的转换:对象,178G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171对象状态变量、元数据和类的serialVersionUID。后者是在编译时分配给类的静态属性。它将类版本表示为一个长整数。在序列化过程中,可以通过两种方式完成转换:• 重新定义序列化的行为,使其根据类的C2版本的格式在流中写入数据• 重新定义格式化的行为,以便它根据版本C1的格式从流中读取数据,并将其转换为版本C2的格式。由于篇幅限制,本文中没有给出示例代码。然而,类版本之间的转换需要复杂的管理.需要保持不同状态、元数据和serialVer-sionUID之间的一致性,这很容易导致代码中的错误2.5保持身份在给定对象的引用的传递闭包中存在循环,原则上会导致同一个对象在同一个流中被序列化多次。为避免这种情况,默认序列化将维护已序列化对象的内存,并在同一对象再次出现时仅将标记插入流中。令牌是这样的,它们唯一地标识对象。这种机制可以防止无限递归,但也会阻碍序列化已经序列化的版本的新副本,这有时可能是期望的行为。例如,考虑通过TCP套接字进行通信的服务器和客户端组件,如以下代码所示public class Node {public static int findDuplicate(int [] nums){...//正在初始化通信套接字,套接字=....ObjectOutputStreamS=new ObjectOutputStream();String s = new String();String s.writeObject();c.Inc();s.writeObject(c); s.close();...}}公共类服务器{public static int findDuplicate(int [] nums){...//打开通信Socket =.G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171179ObjectInputStreamS=new System. out. println();System. out. println(c); System.out.println(c);c=(Counter)s.readObject(); System.out.println(c); s.close();...}}在建立通信之后,客户端向服务器发送两个Counter类的对象。 (虽然没有显式显示,但类Counter应该提供简单计数器的功能,该计数器在构造时被初始化为零,并且可以用方法Inc.客户端生成一个计数器,将其序列化,并通过TCP套接字将其发送到服务器。 操作重复两次,计数器在中间递增。服务器接收对象并在屏幕上打印它们的值。在所示的示例中,通信导致失败:服务器打印数字0两次,而不是像预期的那样打印0和1。 这种错误行为的原因是Counter对象在客户端第二次将其写入流时未序列化。相反,引用计数器的先前副本的令牌被插入到流中。这个问题的一个可能的解决方案,可以利用reset()方法在同一对象的两个序列化之间重置流的状态。 这样,Counter对象就被存储了两次。然而,让我们注意到,第二个对象被序列化为一个新对象,而不是替换前一个对象。在其他情况下,这可能是错误的根源:例如,因为如果在随后的通信中,服务器无法处理客户端中单个副本中存在的对象的两个实例。3一种序列化推理到目前为止,我们展示了序列化的使用如何在基于组件的系统中引起细微的错误。这就需要专门针对此类故障的测试和分析技术然而,为此目的定义测试和分析技术并不简单。在本节中,我们讨论了一个概念框架的建议,该框架对主要的数据管理策略进行了分类这种分类的目的是便于定义评估序列化的技术图2(a)说明了拟议的数据管理战略分类。这个分类中的每一个条目都代表了一种特定的方式,180G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171图二、数据交换的语义分类当它参与两个组件之间的数据交换时,处理对象的图形。每个条目的语义可以非正式地给出如下:绑定移除发生在对象图被排除在数据交换之外时通常,绑定移除用于分离目标环境中不需要当对象图被排除在数据交换之外时,重新绑定发生,其语义是对应的引用将在目标环境中重新链接到相同类型的对象。 重新绑定 可以通过两种不同的方式,这取决于重新链接对象的标识。 当原始子图通过跨越目标和原始环境的链接重新链接。相反,当实际的重新链接对象与原始对象图不同时,发生重新绑定到不同对象。例如,当远程可访问对象作为参数传递给远程方法时,在Java-RMI([8])中使用重新绑定到原始对象(即,远程组件的方法),导致对象保持本地,并且只有远程引用被传递到远程组件:对象图不被传送,而作为调用的结果,它从远程组件内被重新链接。重新绑定到不同的对象发生在例如软件代理的情况下,该软件代理在分离了原始主机中的相应资源By copy指定了一种策略,当对象图的副本G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171181在目标环境中创建,即,根对象被复制,引用的对象(如果有的话)也被递归地复制(这被简单复制调用)。在Java-RMI中,这是将不可远程访问的对象作为远程调用的参数该策略的一个变体不执行镜像复制,但也跨原始和目标环境转换对象图格式。我们将此策略称为复制到演员表。例如,可能需要通过强制转换复制来保证对象类的不同版本通过移动指定了一种策略,它与通过复制相同,但也涉及从原始环境中消除对象图。类似的考虑适用于关于复制的转换策略移动 演员表在同一个数据交换案例中,上述一组策略可以共存。例如,对象的图可以通过复制来传输,但是它的一些子图可以通过移动来传输,或者通过绑定移除或重新绑定来处理。此外,我们正在研究在我们的概念框架中分类的策略与Java中串行化的实现支持之间的联系。 图2(b)初步说明了这种想法在某些情况下:关键字transient可以与绑定删除相关;transient和读对象的主体可以与重新绑定到新对象相关;读对象的主体,写对象的主体,过程Object-StreamField和关键字SerialVersionUID可以与复制相关。图中所代表的协会并不意味着详尽无遗。然而,我们设想的可能性是,完善我们对这些关联的理解,可能有助于实现支持透视测试和分析技术的工具。例如,基于这些关联,工具可以自动识别代码中的特定数据管理策略 其他工具可以利用关联来检测相关代码,例如解决特定数据管理策略的分析。使用此分类框架来支持序列化测试技术的定义将在下一节中举例说明4建立序列化让我们考虑序列化测试技术的定义,基于扩展众所周知的数据流测试标准([6,5])以处理序列化故障。数据流测试基于练习def-use对(即, 程序182G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171给定数据的定义和使用之间的路径),旨在发现数据被赋值的方式与这些值影响计算的方式之间的令人敬畏的相互作用这个想法似乎满足了对序列化测试要求的微小修改。事实上,概括第2节中的例子,(1)与序列化相关的问题出现在使用对象时(在序列化之后),(2)错误是由于错误理解了对象是如何相对于它们的初始定义(在序列化之前)被修改的(通过序列化过程)。例如,在目标环境中重新链接的对象可能不符合原始环境的假设,从而成为误用的来源。在序列化之前导致对象定义的测试用例然而,由于普遍存在的对象定义和用途的大量组合(也考虑到对象子部分的定义和用途),这一标准的不加区别的适用在实践中会受到阻碍。测试案例的数量在多元主义的存在下趋于进一步爆炸[9]。我们的分类框架提供了一种区分过度或冗余的测试用例的方法,从而增加了该技术实际可行性的机会。让我们考虑对应于绑定移除的第一个对象子图。在这种情况下,它总是一个错误,访问这样的子图后,具体化。通过静态检查目标环境中错误使用的存在,可以很容易地发现相应的错误,因此不需要对所有绑定删除进行测试。然后,让我们考虑与重新绑定到原始对象相对应的对象子图。在这种情况下,一个单独的测试用例可以评估修剪的引用是否被正确地重新链接,而对于其他交互,可以依赖于原始组件的单元测试。因此,在这种情况下,测试所有相关的def-use对将是多余的。接下来,让我们对象子图对应于重新绑定到一个不同的对象。在这些情况下,使用相关的def-use对确实是有意义的:相应的测试用例有机会发现一个错误,如果某个对象在初始化过程中没有正确地重新链接,或者重新链接的对象与原始环境的假设相冲突。最后,让我们考虑使用复制和移动策略交换的对象和对象子图。只有当它们也具有by cast策略时,它们才会生成有趣的测试用例。就序列化而言,交换的对象是原始对象的精确副本,因此不会违背原始环境的假设。对于通过移动对象,当序列化发生时,G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171183在原始环境中进行特定静态检查的要求。总之,应通过考虑对象(子)图的定义-使用对的所有组合来定义序列化的测试用例,这些组合对应于重新绑定到不同对象、通过强制转换复制和通过强制转换移动策略,其中定义发生在原始环境中,在目标环境中使用,并且序列化用于在两个环境之间交换对象。此外,需要单例测试用例来重新绑定到原始对象,并分别在目标和原始环境中对绑定移除和移动策略进行静态检查。测试技术的合理和完整定义不是本文的主题。然而,这个例子支持了这样一种可能性,即本文中定义的概念分类框架可以提供一个基本原理,以便于定义测试和分析技术来解决序列化问题。5最后意见和今后的工作序列化是一种将对象的图形记录到字节流中以及从字节流中检索对象的图形的强大方法。串行化机制经常用在基于组件的应用领域中,分布式和移动系统。然而,序列化机制的使用可能会导致软件中的细微错误。串行化故障很难用传统的测试技术来揭示解决序列化的测试技术的定义并不直接,因为必须考虑当前操作的语义和序列化的语义。为了支持序列化的测试,我们开发了一个推理框架的基础上,我们的知识可能发生的序列化的数据交换策略。我们并不声称这个框架是完整的或详尽的,但是我们通过展示它允许对使用序列化的系统进行调优和扩展数据流测试来研究它的有用性。我们目前正在努力完善和完善我们的概念框架,用于序列化推理。特别是,我们目前正在研究框架中分类的数据交换案例与Java中为序列化提供的实现支持之间的联系。我们亦正进一步研究可根据我们的概念框架界定的测试及分析技术。我们正在完善我们在本文中初步描述的专业数据流测试方法。我们最终将研究基于断言的运行时验证[7,11]。后者可以利用我们的概念框架作为定义专门断言的基础,以解决序列化问题。184G. 德纳罗湖Mariani/Electronic Notes in Theoretical Computer Science 116(2005)171引用[1] Emmerich,W.,软件工程和中间件,在:第22届国际软件工程会议(ICSE-00)(2000年),第100页。117-132[2] Fuggetta,A.,G. Picco和G. Vigna,Understanding code mobility,IEEE Transactions onSoftware Engineering24(1998),pp. 342-361[3] Ghezzi 角 , V.Martena 和 G. Picco , Enhancing remote method invocation through type-basedstatic analysis , in : Proceedings of the International Conference on FundamentalApproaches toSoftware Engineering(FASE 2004),Barcelona,Spain,2004。[4] S. D、[5] Harrold,M. J.和G. Rothermel,在类上执行数据流测试,在:第二届ACM SIGSOFT软件工程基础研讨会论文集(FSE154-163。[6] Laski,J.和B.王志华,一种面向数据流的程序测试策略,《软件工程学报》,1983年,第9期,第10页。347-354[7] 迈耶湾,“Ei Quel:语言”,面向对象系列,Prentice Hall,纽约,纽约,1992.[8] 微系统公司,S.,JavaTM远程方法调用规范,技术报告,Sun Microsystems(2002)。[9] Orso,A.,“面向对象软件的集成测试”,博士学位。论文,米兰理工大学,意大利(1999年)。[10] 皮科湾P.,µCode:A Lightweight and Flexible Mobile Code Toolkit,in:K.Rothermel和F. Hohl,editors,Proceedings of the 2nd International Workshop on Mobile Agents,Lecture Notes in Computer Science1477(1998),pp.160-171。[11] Rosenblum,D.美国,A practical approach to programming with assertions,IEEETransactions on Software Engineering21(1995),pp. 十九比三十一[12] Sun Microsystems,Inc. Java对象序列化规范,修订版1.4.4,技术报告(2001)。[13] Wiltamuth,S.和A. Hejlsberg,C#语言规范,技术报告,Microsoft Corporation(2003)。
下载后可阅读完整内容,剩余1页未读,立即下载
cpongm
- 粉丝: 4
- 资源: 2万+
上传资源 快速赚钱
- 我的内容管理 收起
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
会员权益专享
最新资源
- zigbee-cluster-library-specification
- JSBSim Reference Manual
- c++校园超市商品信息管理系统课程设计说明书(含源代码) (2).pdf
- 建筑供配电系统相关课件.pptx
- 企业管理规章制度及管理模式.doc
- vb打开摄像头.doc
- 云计算-可信计算中认证协议改进方案.pdf
- [详细完整版]单片机编程4.ppt
- c语言常用算法.pdf
- c++经典程序代码大全.pdf
- 单片机数字时钟资料.doc
- 11项目管理前沿1.0.pptx
- 基于ssm的“魅力”繁峙宣传网站的设计与实现论文.doc
- 智慧交通综合解决方案.pptx
- 建筑防潮设计-PowerPointPresentati.pptx
- SPC统计过程控制程序.pptx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功