• 1.68 MB
  • 2022-04-22 11:26:43 发布

基于社区的商品代理服务平台的系统设计.doc

  • 70页
  • 当前文档由用户上传发布,收益归属用户
  1. 1、本文档共5页,可阅读全部内容。
  2. 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,可选择认领,认领后既往收益都归您。
  3. 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细先通过免费阅读内容等途径辨别内容交易风险。如存在严重挂羊头卖狗肉之情形,可联系本站下载客服投诉处理。
  4. 文档侵权举报电话:19940600175。
'基于社区的商品代理服务平台的系统设计摘要本次毕业设计是设计并实现一个基于社区的商品代理服务平台,也可以把它看成一个购物网站,它有三种用户:社区用户(顾客)、物业(代理商或商店)、厂商(生产厂商或代理商)。厂商可以注册商品,查看任务列表;社区用户可以购物,查看定单信息,通过电子邮件向物业咨询和交流;物业可以查看用户订单,通过电子邮件和用户交流,向厂商订货。在开发过程中,使用了JavaScript、Jsp、Struts、Hibernate等技术,数据库采用MySql数据库,网页开发工具使用Dreamweaver,WEB服务器采用ApacheTomcat5.0,开发平台使用eclipse。论文中首先对整个商品代理服务平台进行了需求分析,然后着重讨论了厂商管理、商品注册、邮件管理三个子系统的数据库设计、详细设计和功能实现。关键词:JSP;Struts;Hibernate;MySQL;社区购物网站;66 AbstractThegraduationprojectistodesignandimplementacommunity-basedmerchandiseagencyservicesplatform.Itcanalsobetreatedasashoppingsite,whoseusersincludecommunityofusers(customers),realestatecompany(agentsorstores),manufacturers(productionmanufacturersordealers).Themanufacturercanregisteramerchandiseandlookintotasklist.Thecommunityuserscandoshopping,canlookintoanorderforminformation,alsocanpassanE-mailofconsultationtotherealestatecompany.Therealestatecompanycanbrowseorderformofuser,candeliveranE-mailofcommuniontotheuser,alsocanindentmerchandisetothemanufacturer.Inthedevelopmentprocess,JavaScript,Jsp,Struts,Hibernate,andothertechnologyareused.MySqlisusedasdatabaseplatform,Dreamweaveraswebdevelopmenttool,ApacheTomcat5.0asWEBserver.Thecodeisdevelopedundereclipse.Inthethesis,softwaredemandforthecommunity-basedmerchandiseagencyservicesplatformwasanalysedatfirst.Thenthepaperpayedmoreattentiontodatabasedesign,detaildesignandfunctionimplementationofthreesubsystemincludingmanagementofmanufacturers,merchandiseregistrationandmanagementofemail.Keywords:JSP;Struts;Hibernate;MySQL;Communityshoppingwebsite;66 目录1绪论12毕业设计选用的技术和工具22.1为什么使用Java语言22.1.1Java语言的特点22.1.2选用Java的原因22.2开发环境及开发工具的介绍22.2.1Eclipse概述22.2.2Tomcat介绍22.2.3MySQL介绍22.3Servlet技术32.3.1Servlet概述32.3.2Servlet的优势32.4JSP技术42.4.1JSP的概述42.4.2JSP的特点和优点42.5Struts技术52.5.1MVC设计模式52.5.2Struts简介62.6Hibernate技术82.6.1Hibernate的产生背景82.6.2Hibernate简介82.6.3Hibernate原理93需求分析和概要设计103.1系统用例说明103.2系统设计中的层次划分113.2.1视图层113.2.2业务层143.2.3持久化层153.3系统设计中的模块设计173.3.1商品注册设计173.3.2厂商管理设计183.3.3邮件管理设计183.4静态模型194数据库设计214.1E-R图214.2数据库表说明234.3数据库表SQL语句245详细设计和实现275.1系统模块划分275.2邮件管理模块275.2.1发送邮件285.2.2查看邮件列表295.2.3删除邮件2966 5.2.4查看邮件内容305.2.5回复邮件315.3厂商及商品管理模块345.3.1注册新商品345.3.2查看注册商品列表365.3.3修改商品信息365.3.4删除商品386测试426.1整合测试426.2功能测试426.3商品注册测试42结论44致谢45参考文献46附录一:系统部分代码47附录二:英文翻译5466 1绪论中国互联网网络信息中心的数据显示,到2007年年底,中国网民人数已经达到2.1亿。中国2008年的网民人数继续增长,网民人数现在已经超越美国,成为全球网络用户最多的国家。随着网民的增加,网购用户也迅速增加。上网购物已成为主流消费人群的消费习惯之一。以2003年作为网络购物成长期的起点,中国大陆累计购物用户规模首次突破1000万,达到1520万人,经过前期的高速增长后,购物用户增长逐步放缓,累计到2006年12月底,中国网络购物市场总体用户数达到4310万人,较上年增长32.6%,预计到2010年用户规模将突破1亿。市场交易规模2007年我国网购总交易量达594亿元,和2006年312亿的总成交额相比,增长了90.4%。随着网上购物的发展,商品流通基础设施和配套行业的重点将会发生偏转。各类送货系统、快递运输公司、支付公司、安全、广告、商务软件、信息服务等新型物流和中介机构发展呈加速态势,并且有很大的发展空间。不难看出,由于电子商务的出现和发展,商品流通领域将面临全方位的深刻变革。网上购物以其特有的优势,必将成为将来趋势。首先,它降低了商家成本,商家不用租昂贵的店面,不用招大量的营业员。其次,他大大扩大了商家的销售范围,有互联网的地方,都是销售范围。对顾客来说,不用出家门就可购物,更为其带来了更大的方便。网上购物以其低廉的价格,方便快捷的方式,短短几年已经风魔全球。因为它突破了时间和地域的限制,顾客可以24小时上网购物,你不尽可以购买本地的商品,还可以购买异地,甚至国外的商品。但如果你想开一家网上商店,必须具备相应的技术知识,以便管理商品。这对一般人来说,并不简单。网上购物的发展,同样带来一些信誉问题,如:产品质量,款到货不到的现象。而我们的基于社区的商品代理服务平台可以减少这个问题的发生,首先,在这个系统中,商店的管理者和商品的销售者是不同的,并且用户和物业是固定联系的,一个顾客对应一个物业。这一定程度上保证了商店的信誉,维护了顾客的利益。因此我们选择了这个课题——基于社区的商品代理服务平台的系统设计。     66 2毕业设计选用的技术和工具2.1为什么使用Java语言2.1.1Java语言的特点1、纯面向对象2、跨平台(“writeonce,runanywhere!”一次编译到处运行)。Java编译后生成的是.class文件,它是运行在Java虚拟机上的。3、简单(Java语法去掉了指针,运算符重载,多重继承等;有垃圾回收器:程序员只负责对象的创建,垃圾回收器负责对象销毁和资源释放,一般只会在内存空间不够的情况下进行资源回收)。2.1.2选用Java的原因Java除了上述特点,另外它还提供了大量的开发工具包,加快了项目的开发进度,与Java相关的技术也都已很成熟,而且有大量的开源框架供开发者免费使用。另外,Java与互联网技术有很好的融合,J2EE(Java2EnterpriseEdition)就是Java的一个重要发展方向,非常适合开发企业应用网站。这次毕业设计就是基于互联网的,是面向互联网用户的购物网站。所以选用了Java作为这次毕业设计的基础语言。我们选用的其它技术也都是与Java相关,或者是用Java编写的,包含在J2EE这个大范围内的各种技术。2.2开发环境及开发工具的介绍2.2.1Eclipse概述Ecpipse是java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。它是开源免费的,可以在网上下载。如果要用到Web开发,还要安装MyEclipse插件。Ecpipse本身也是用Java开发的,与Java有很好的融合性,本次项目使用的就是Java语言,再加上此平台是免费的,所以使用它作为开发平台。2.2.2Tomcat介绍Tomcat是一个免费的开源的Serlvet/Web容器,它是Apache基金会的Jakarta项目中的一个核心项目,由Apache、Sun和其它一些公司及个人共同开发而成。由于有了Sun的参与和支持,最新的Servlet和Jsp规范总能在Tomcat中得到体现。因此,它现在使用比较广泛的一种Web服务器.同样它也是免费,与Java有很好的融合性,这也是我们使用它的原因。2.2.3MySQL介绍MySQL是最受欢迎的开源SQL数据库管理系统,它由MySQLAB开发、发布和支持。MySQLAB是一家基于MySQL开发人员的商业公司,它是一家使用了一种成功的商业模式来结合开源价值和方法论的第二代开源公司。MySQL是MySQLAB的注册商标。MySQL也是开源的,这意味着任何人都可以使用和修改该软件,任何人都可以从66 Internet上下载和使用MySQL而不需要支付任何费用。如果你愿意,你可以研究其源代码,并根据你的需要修改它。另外,它占用内存较小,如果是Oracle一般的电脑很难畅通的运行。MySQL非常适合单机开发。Eclipse+MySQL+Tomcat是当前J2EE开发的流行组合。2.3Servlet技术2.3.1Servlet概述Servlet技术是Sun公司提供的一种实现动态网页的解决方案,它是基于Java编程语言的WEB服务器端编程技术,主要用于在WEB服务器端动态生成对客户端的响应。Servlet技术也是JSP(JavaServerPages)技术(另外一种动态网页开发技术)的基础。一个Servlet程序就是一个实现了特殊接口的Java类,用于被支持Servlet的WEB服务器调用和运行,即只能运行于具有Servlet引擎的WEB服务器端。一个Servlet程序负责处理它所对应的一个或一组URL地址的访问请求,接收访问请求信息和产生响应内容。2.3.2Servlet的优势Servlet是Java技术对CGI(CommonGatewayInterface)编程的回答。Servlet程序在服务器端运行,动态地生成Web页面。与传统的CGI和许多其他类似CGI的技术相比,JavaServlet具有更高的效率,更容易使用,功能更强大,具有更好的可移植性,更节省投资:高效:在Servlet中,每个请求由一个轻量级的Java线程处理(而不是重量级的操作系统进程)。在传统CGI中,如果有N个并发的对同一CGI程序的请求,则该CGI程序的代码在内存中重复装载了N次;而对于Servlet,处理请求的是N个线程,只需要一份Servlet类代码。在性能优化方面,Servlet也比CGI有着更多的选择,比如缓冲以前的计算结果,保持数据库连接的活动,等等。方便:Servlet提供了大量的实用工具例程,例如自动地解析和解码HTML表单数据、读取和设置HTTP头、处理Cookie、跟踪会话状态等。功能强大:在Servlet中,许多使用传统CGI程序很难完成的任务都可以轻松地完成。例如,Servlet能够直接和Web服务器交互,而普通的CGI程序不能。Servlet还能够在各个程序之间共享数据,使得数据库连接池之类的功能很容易实现。可移植性好:Servlet用Java编写,ServletAPI具有完善的标准。因此,为I-PlanetEnterpriseServer写的Servlet无需任何实质上的改动即可移植到Apache、MicrosoftIIS或者WebStar。几乎所有的主流服务器都直接或通过插件支持Servlet。节省投资:66 不仅有许多廉价甚至免费的Web服务器可供个人或小规模网站使用,而且对于现有的服务器,如果它不支持Servlet的话,要加上这部分功能也往往是免费的(或只需要极少的投资)。2.4JSP技术2.4.1JSP的概述JavaServerPages(JSP)是一种实现普通静态HTML和动态HTML混合编码的技术,许多由CGI程序生成的页面大部分仍旧是静态HTML,动态内容只在页面中有限的几个部分出现。但是包括Servlet在内的大多数CGI技术及其变种,总是通过程序生成整个页面。JSP使得我们可以分别创建这两个部分。 从本质上说,JSP也是一种Servlet技术.Web服务器在遇到访问JSP网页的请求时,首先执行其中的程序段,然后将执行结果连同JSP文件中的HTML代码一起返回给客户。插入的Java程序段可以操作数据库、重新定向网页等,以实现建立动态网页所需要的功能。JSP与JavaServlet一样,是在服务器端执行的,通常返回该客户端的就是一个HTML文本,因此客户端只要有浏览器就能浏览。2.4.2JSP的特点和优点为了快速方便地进行动态网站的开发,JSP在以下几个方面做了改进,使其成为快速建立跨平台的动态网站的首选方案。1.将内容的生成和显示进行分离用JSP技术,Web页面开发人员可以使用HTML或者XML标识来设计和格式化最终页面,并使用JSP标识或者小脚本来生成页面上的动态内容(内容是根据请求变化的,例如请求账户信息或者特定的一瓶酒的价格等)。生成内容的逻辑被封装在标识和JavaBeans组件中,并且捆绑在脚本中,所有的脚本在服务器端运行。由于核心逻辑被封装在标识和JavaBeans中,所以Web管理人员和页面设计者,能够编辑和使用JSP页面,而不影响内容的生成。在服务器端,JSP引擎解释JSP标识和脚本,生成所请求的内容(例如,通过访问JavaBeans组件,使用JDBC技术访问数据库或者包含文件),并且将结果以HTML(或者XML)页面的形式发送回浏览器。这既有助于作者保护自己的代码,又能保证任何基于HTML的Web浏览器的完全可用性。2.可重用组件绝大多数JSP页面依赖于可重用的、跨平台的组件(JavaBeans或者EnterpriseJavaBeans组件)来执行应用程序所要求的复杂的处理。开发人员能够共享和交换执行普通操作的组件,或者使得这些组件为更多的使用者和客户团体所使用。基于组件的方法加速了总体开发过程,并且使得各种组织在他们现有的技能和优化结果的开发努力中得到平衡。66 3.采用标识Web页面开发人员不会都是熟悉脚本语言的编程人员。JSP技术封装了许多功能,这些功能是在易用的、与JSP相关的XML标识中进行动态内容生成所需要的。标准的JSP标识能够访问和实例化JavaBeans组件,设置或者检索组件属性,下载Applet,以及执行用其他方法更难于编码和耗时的功能。4.适应平台几乎所有平台都支持Java,JSP+JavaBeans几乎可以在所有平台下通行无阻。从一个平台移植到另外一个平台,JSP和JavaBeans甚至不用重新编译,因为Java字节码都是标准的与平台无关的。2.5Struts技术2.5.1MVC设计模式所谓“MVC模式”是三个单词的首字母缩写,它们是Model(模型)、View(视图)和Controller(控制)。分布式企业应用软件结构复杂、涉及多种技术,对设计开发人员提出了很高的要求。在此情况下,运用设计模式――可复用的设计方案进行软件的设计开发十分必要。MVC模式已被证明是一种成功的软件设计模式,Struts就是基于MVC模式的一种框架。这个模式认为,程序不论简单或复杂,从结构上看,都可以分成三层。1)最上面的一层,是直接面向最终用户的“视图层”。它是提供给用户的操作界面,是程序的外壳。2)最底下的一层,是核心的“数据层”,也就是程序需要操作的数据或信息。3)中间的一层,就是“控制层”,它负责根据用户从“视图层”输入的指令,选取“数据层”中的数据,然后对其进行相应的操作,产生最终结果。这三层是紧密联系在一起的,又是互相独立的,每一层内部的变化不影响其他层,并且设计了接口,供其上面一层调用。这样一来,程序编写就可以实现模块化,修改外观或者变更数据都不用修改其他层,大大方便了维护和升级。这也是实现了程序对高内聚,低偶合的要求。 MVC其结构如图1所示:图1MVC模式框架66   模型表示业务逻辑和业务规则等,在MVC的三个部件中拥有最多的处理任务。它可以用JavaBean和EJB(EnterpriseJavaBean)等组件技术来处理数据库的访问。模型能为多个视图提供数据。由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。  视图是在屏幕上显示的。模型进行操作之后,其结果就是通过视图来显示的。在视图中其实没有真正的处理发生,只是作为一种输出数据并允许用户操作的方式。  控制器用于管理用户与视图发生的交互。一旦用户想对模型进行处理时,它不能直接去执行模型,而是通过控制器来间接地实现。控制器能从视图中取值,然后将相应的值传给模型进行处理。控制器接受用户的输入并调用模型和视图去完成用户的需求。  由上述可知,MVC模式的处理过程是:首先由控制器接收用户的请求,并决定应该调用哪个模型来进行处理,然后模型用业务逻辑来处理用户的请求并返回数据,最后控制器用相应的视图返回数据,并通过表达层呈现给用户。2.5.2Struts简介 Struts是Apache组织的一个开放源码项目。Struts是一个比较好的MVC框架,提供了对开发MVC系统的底层支持,它采用的主要技术是Servlet,JSP和Customtaglibrary。其基本构成如图2所示。图2Struts框架图  由图2可以看出,在Struts框架中Controller功能由ActionServlet和ActionMapping对象构成,核心是一个Servlet类型的对象ActionServlet,它用来接受客户端的请求。ActionServlet包括一组基于配置的ActionMapping对象,每个ActionMapping对象实现了一个请求到一个具体的Model部分中Action处理器对象之间的映射。  Model部分由Action和ActionForm对象构成。所有的Action处理器对象都是开发者从Struts的Action类派生的子类。Action处理器对象封装了具体的处理逻辑,调用业务逻辑模块,并且把响应提交到合适的View组件以产生响应。Struts提供的ActionForm组件对象可以通过定义属性描述客户端表单数据。开发者可以从它派生子类对象,利用它和Struts提供的自定义标66 记库结合可以实现对客户端的表单数据的良好封装和支持,Action处理器对象可以直接对它进行读写,而不再需要和request、response对象进行数据交互。通过ActionForm组件对象实现了对View和Model之间交互的支持。  View部分是通过JSP技术实现的。Struts提供了自定义的标记库,通过这些自定义标记可以非常好地和系统的Model部分交互,通过使用这些自定义标记创建的JSP表单,可以实现和Model部分中的ActionForm的映射,完成对用户数据的封装。Struts的工作流程和MVC设计模式差不多,首先,视图层接受用户请求,可以把请求封装在Form表单里,通过查找Struts-cfg.xml配置文件(既控制器),转发到相应的Action里(既模型层),在Action里调用相应的业务方法,经过处理以后,返回响应结果。控制器获得响应结果,根据配置文件,返回到响应界面(视图层)给用户看。中间还可以加表单验证。这就是Struts工作的基本流程。2.6Hibernate技术2.6.1Hibernate的产生背景大多数应用程序都需要处理数据。Java应用程序运行时,往往把数据封装为相互连接的对象网络,但是当程序结束时,这些对象就会消失在一团逻辑中,所以需要有一些保存它们的方法。有时候,甚至在编写应用程序之前,数据就已经存在了,所以需要有读入它们和将其表示为对象的方法。手动编写代码来执行这些任务不仅单调乏味、易于出错,而且会占用整个应用程序的很大一部分开发工作量。  优秀的面向对象开发人员厌倦了这种重复性的劳动,他们开始采用通常的“积极”偷懒做法,即,创建工具,使整个过程自动化。对于关系数据库来说,这种努力的最大成果就是对象/关系映射(ORM)工具。  这类工具有很多,从昂贵的商业产品到内置于J2EE中的EJB标准。然而,在很多情况下,这些工具具有自身的复杂性,使得开发人员必须学习使用它们的详细规则,并修改组成应用程序的类以满足映射系统的需要。由于这些工具为应付更加严格和复杂的企业需求而不断发展,于是在比较简单和常见的场景中,使用它们所面临的复杂性反而盖过了所能获得的好处。这引起了一场革命,促进了轻量级解决方案的出现,而Hibernate就是这样的一个例子。2.6.2Hibernate简介Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了轻量级的对象封装,使Java程序员可以随心所欲的使用对象编程思维来操纵数据库。它的工作原理是通过文件把值对象和数据库表之间建立起一个映射关系,这样,我们只需要通过操作这些值对象和Hibernate提供的一些基本类,就可以达到使用数据库的目的。例如,使用Hibernate的查询,可以直接返回包含某个值对象的列表(List),而不必向传统的JDBC访问方式一样把结果集的数据逐个装载到一个值对象中,为我们的编码工作节省了大量的劳动。Hibernate提供的HQL是一种类SQL语言,它和66 EJBQL一样都是提供对象化的数据库查询方式,但HQL在功能和使用方式上都非常接近于标准的SQL。Hibernate不仅提供了从Java类到数据表之间的映射,也提供了数据查询和恢复机制。相对于使用JDBC和SQL来手工操作数据库,Hibernate可以大大减少操作数据库的工作量。另外Hibernate可以利用代理模式来简化载入类的过程,这将大大减少利用HibernateQL从数据库提取数据的代码的编写量,从而节约开发时间和开发成本Hibernate可以和多种Web服务器或者应用服务器良好集成,如今已经支持几乎所有的流行的数据库服务器。2.6.3Hibernate原理Hibernate技术本质上是一个提供数据库服务的中间件。它的架构如图3所示:图3Hibernate架构图图3显示了Hibernate的工作原理,它是利用数据库以及其他一些配置文件如Hibernate.properties,XMLMapping等来为应用程序提供数据持久化服务的。Hibernate具有很大的灵活性,但同时它的体系结构比较复杂,提供了好几种不同的运行方式。在轻型体系中,应用程序提供JDBC(JavaDatabaseConnectivity)连接,并且自行管理事务,这种方式使用了Hibernate的一个最小子集;在全面解决体系中,对于应用程序来说,所有底层的JDBCAPI都被抽象了,Hibernate会帮你实现所有的细节。66 3需求分析和概要设计3.1系统用例说明从下图可以看出,网站有未注册用户、物业代表、用户代表、厂商代表,还有管理员。对于未注册用户,他可以了解网站的公开信息,如:商品信息、商品价格信息、厂商信息等,但如果他想购物,就要登录或注册了。图4系统用例图当一个用户要注册一个购物者时,他要选泽一个物业,作为买商品的代理人。这是不同与一般购物商店的地方,这样设计的最根本的目的是保证购物者的利益。通过这种长期的代理人关系,可以保证购物双方的利益。一旦建立了这种关系,物业为保持一个长期的客户,必然努力保证自己的信誉,购物者也可以凭借自己的信誉获得购物的优惠。这也是本网站的建设的核心思想。厂商在网站上直接向消费者提供商品信息,以保证统一商品价格,维护厂商的利益,拉近了和顾客的距离。当顾客注册为一个普通用户后,他便可以购物了,同时可以向自己的物业发送邮件,咨询商品信息,对于顾客买不熟悉的商品有很大的帮助。当然了他还可以查看自己的定单,了解自己的购物情况。当顾客注册为一个物业后,他便可以进入邮件管理系统,查看用户留言,查看顾客定单,并生成任务向厂商订货。对于厂商来说,他可以注册商品,修改商品信息,并且可以查看物业定货。对于网站管理员来说,他可以管理各种用户信息,评定各用户的信用等级,对于一些不合格的厂商和物业有删除功能。网站的工作流程如图5所示:66 物业用户未登陆用户社区用户邮件列表商品列表定单列表注册、登陆注册、登陆注册、登陆查看查看查看生成采购任务生成确认供货任务生成确认注册商品厂商用户图5系统流程图3.2系统设计中的层次划分本系统从层次上可以划分为三个层次:视图层,业务层和持久化层。3.2.1视图层所谓视图层,就是和用户交互的那部分,它直接和用户打交到,用户有他的使用习惯和风格,我们不能因为为了满足用户习惯,而去改变业务逻辑。这就要求我们在程序设计时,尽量把视图层和业务逻辑层分割开来,使得页面的改变,不影响业务逻辑。因此,在视图层中,我们用了Struts框架。为什么要用他那?因为他很好的利用了MVC的设计模式(详见上面的技术介绍),很好的满足了我们的需求。我们利用JSP做为给用户的显示业面,当用户发送请求后,Struts会去读取配置文件,配置文件就是Struts的控制器,根据配置文件转发到相应的Action,通过Action调用相应的业务逻辑。当其中的一个层次改变时,其它的层次不用改变,大大提高了代码的可重用性和可维护性。这也是我们使用Struts框架的原因。下图为struts-config.xml控制器的部分代码://商品信息的Form表单//邮件信息的form表单//全局转发//以下是action影射表://Struts通过path找到相应的action,action里调用业务方法,parameter的值是//action里的方法名。//此action获得收件箱邮件的//此action获得发件箱邮件的//当执行完action后,会通过此路径转发到相应的页面或action66 //此action用于厂商注册商品并且Struts还给我们提供了Form表单和验证功能,当用户向服务器发送信息时。Form表单会自动把信息封装,并且可以根据需求验证。这样,如果用户输入的信息错误,控制器就能直接转回给用户,不用进入以后的模型层和业务逻辑,这就大大提高了效率。减少了程序异常的可能性。在模型层,他是直接和业务逻辑交互的,Struts通过Action调用业务逻辑方法,当业务逻辑改变时,他不会影响视图层和控制器层。大大降低了,程序间的偶合性。3.2.2业务层业务层是程序的核心部分,一个好的业务分析抽象,即可减少代码量,也可提高效率。业务层是对业务分析得出的结果,他是实现系统功能的部分,同时又是和视图层、持久化层交互的部分。他根据业务需要,查询数据库,或者向数据库存入数据。同时又要对这些数据,进行操作,以向视图层发送,满足用户需要。在这个系统中,我们使用了工厂设计模式,既把业务封装成一个接口供视图层使用,也是为了减少层次之间的联系,提高代码的可重用性和可维护性。下图是工厂模式的部分代码://从此工厂可以获得收件箱和发件箱packageedu.ahut.cs.cmss.service;publicclassBoxFactory{//创建收件箱privatestaticfinalReceiverBoxRBOX=newReceiverBox();//创建发件箱privatestaticfinalSendBoxSBOX=newSendBox();//通过此方法返回一个收件箱publicstaticReceiverBoxgetRBOX(){returnRBOX;}//通过此方法返回一个发件箱publicstaticSendBoxgetSBOX(){returnSBOX;}66 }3.2.3持久化层持久化层是和数据库直接打交到的,他对数据的持久化和页面的动态显示有重要意义。一提到数据库,我们就会想到SQL语句。对,SQL语句的编写过程,是容易出错,但又要大量重复劳动的工作。因此,我们在持久化层,使用Hibernate框架,它很好的封装了SQL语句,把它转化为了,具有面向对象特点的HQL语句。使我们不在操作那些无意义的字段,我们可以根据面向对象的特点,来操作数据库。使用Hibernate,你不需要实现任何不可思议的接口以便能够持续存在。惟一需要做的就是创建一份XML“映射文档”,告诉Hibernate您希望能够保存在数据库中的类,以及它们如何关联到该数据库中的表和列,然后就可以要求它以对象的形式获取数据,或者把对象保存为数据。运行时,Hibernate读取映射文档,然后动态构建Java类,以便管理数据库与Java之间的转换。在Hibernate中有一个简单而直观的API,用于对数据库所表示的对象执行查询。要修改这些对象,(一般情况下)只需在程序中与它们进行交互,然后告诉Hibernate保存修改即可。类似地,创建新对象也很简单;只需以常规方式创建它们,然后告诉Hibernate有关它们的信息,这样就能在数据库中保存它们。下面是商品表的影射文件代码://Goods为类名,tb_goods是与Goods相对应的表名//主键//class后对应的是主键产生方式,此句为自动生成//name后为类的属性,column后为表中对应的字段,unique是判断表中的字段是否唯一66 另外,Hibernate还给我们提供了连接数据库的配置文件Hibernate.cfg.xml,下面是我在系统中使用的配置文件:truetrue//数据库对应的方言org.hibernate.dialect.MySQLDialect//连接数据库对应的驱动com.mysql.jdbc.Driver//连接数据库的url,后面是数据库名jdbc:mysql://localhost:3306/cmss//登录名root//登录密码1234//声明影射文件3.3系统设计中的模块设计3.3.1商品注册设计厂商要注册商品的目的是在网站上销售商品,因此,对商品要有详细的描述,如:商品名,价格,对商品功能的描述。要有图片,以便用户更直观的流览商品。可能一段时间商品要打折,或者其他信息,对商品要有备注等描述属性。66 另外,商品有很多类,因此要对商品进行归类。为了使将来能够增加,修改商品类型。因此,我们新建了一个类,商品类型类。每个商品类型,都有一个商品类型字段。为了方便直接在商品类里获得商品类型名,因此,在商品类里,它的商品类型字段是字符串。用户在注册商品时,显示商品类型下拉列表,供用户选择。商品和商品类型的类图如图7所示:图7商品和商品类型的类图3.3.2厂商管理设计厂商在网站上注册商品是为了销售,为了保证厂商的信誉,厂商要公开自己的信息,以便用户或网站管理者查证。这样对于信誉好的厂商来说,这样可以扩大知名度,促进本公司其它商品的销售。对于用户来说,通过对公司信息的了解,也能更放心的选择自己的商品。对于管理者来说,他要尽力确保在网站上注册商品的公司都是有信誉的,以确保购物者的利益,同时也是扩大网站访问量的一种方式。对于厂商用户,他除了要有用户名密码外(这些是保密的),还要有自己公司的真实姓名,以便用户查找,要有准确的地址,以确保其真实性。当物业要购买商品时,要能够找到你的联系方式。当商品有问题时,要确保别人能够投诉。因此,厂商的类图如图8所示:66 图8厂商类图3.3.3邮件管理设计由于本网站不同于一般的购物网站,一般的购物网站,就象商店一样,你想买东西了,可以去这家商店,也可以去那家。而我们的这个购物网站,用户只能向你的物业买东西,他是一种代理人的关系,物业就是你的购物代理人。由于你们是一种长期的代理关系,物业为了保持这个长期的客源,一定会保证信誉,间接上也保护了购物者的利益。代理人不光提供商品,同时也向他的用户提供免费的咨询服务,也可以说是对顾客保持这种长期关系的一种回报。这对顾客买一些自己不了解的商品,有很大的吸引力,由于他们是一种长期的代理人关系,这就大大降低了,物业欺骗顾客的现象。而我们的邮件服务,就是为了满足他们这种交流需求的。我们的邮件是与用户和物业帮定的,但是不对厂商开放。因为他与顾客直接交流的可能性极小。当顾客成为注册用户后,他就可以进入他的邮件系统。我们的邮件是内部邮件,他是物业和用户交流用的,而一个用户只能有一个物业。因此,为了方便用户,用户发的邮件,不用输入收件人,邮件会自动发给他的物业,同时将信存入发件箱。而对于物业来说,他要选定用户,然后可以向用户发邮件。为了降低项目投入成本,我们用数据库代替邮件服务起器,把邮件信息存入数据库。对于一封邮件他对于收件人和发件人都是可见的。我们可以把他存入一条记录。存入一条记录又有一个新问题,当发件人删除邮件时,而发件人没有删除,怎么办?我们的解决方案是,增加两个字段,以判断,此封邮件对于发件人和收件人分别处于什么状态。他是发件类型和收件类型,发件类型有已发送和已删除状态。收件类型有已读、未读和已删除状态。66 它的类图如图9所示:图9邮件类图3.4静态模型66 图10静态模型图4数据库设计4.1E-R图由以上对所做模块的分析,设计了以下实体的E-R图1、邮件实体E-R图idemailtimesendTypetitleinformationsendManreceiveManreceiveType2、商品类型实体E-R图66 goodsTypetitletitle3、商品实体E-R图idgoodsgoodsTypeDescriptionitemPriceVersionnamefactoryNameNoteimage4、厂商实体E-R图userIduserIdfactoryrelationsaddressuserTypefactoryNameloginNameloginPwdpeoplelevelreactionType5、实体联系图66 factory注册goodsgoodsType属于11nn4.2数据库表说明1、邮件表表1邮件表NameCodeDataTypeLengthPrimaryForeignKey邮件编号idintX发件人sendManintX收件人receiveManintX标题titlevarchar(100)100内容informationvarchar(1024)1024发件类型sendTypeint收件类型receiveTypeint发件时间timetimestamp2、商品表表2商品表NameCodeDataTypeLengthPrimaryForeignKey商品编号idintX商品名Namevarchar(255)255X生产厂商名factoryNamevarchar(255)255X单价itemPricevarchar(100)100型号Versionvarchar(255)25566 描述Descriptiontext备注Notetext商品类型goodsTypevchar(255)255图片imagevarchar(255)2553、商品类型表表3商品类型表NameCodeDataTypeLengthPrimaryForeignKey商品类型编号idintX商品类型gooTypevarchar(255)2554、厂商表表4厂商表NameCodeDataTypeLengthPrimaryForeignKey厂商编号userIdintX用户名loginNamevarchar(255)255密码loginPwdvarchar(255)255用户类型userTypeint厂商名factoryNamevarchar(255)255地址addressvarchar(255)255联系人peoplevarchar(255)255联系方式relationsvchar(255)255投诉方式reactionTypevarchar(255)255等级levelint4.3数据库表SQL语句//厂商信息表TB_FACTORY(创建厂商表,userId为主键,唯一键为loginName,factoryName。)CREATETABLE`cmss`.`tb_factory`(`userId`int(11)NOTNULLauto_increment,66 `loginName`varchar(255)defaultNULL,`loginPwd`varchar(255)defaultNULL,`userType`int(11)defaultNULL,`factoryName`varchar(255)defaultNULL,`address`varchar(255)defaultNULL,`people`varchar(255)defaultNULL,`relations`varchar(255)defaultNULL,`reactionType`varchar(255)defaultNULL,`level`int(11)defaultNULL,PRIMARYKEY(`userId`),UNIQUEKEY`loginName`(`loginName`),UNIQUEKEY`factoryName`(`factoryName`))ENGINE=InnoDBDEFAULTCHARSET=gb2312;//商品表TB_GOODS(创建商品表,id为主键,唯一键为name,外键为goodsType,它引用TB_GOODSTYPE表的gooType键)CREATETABLE`cmss`.`tb_goods`(`id`bigint(20)NOTNULLauto_increment,`name`varchar(255)defaultNULL,`factoryName`varchar(255)defaultNULL,`itemPrice`doubledefaultNULL,`version`varchar(255)defaultNULL,`description`text,`note`text,`goodsType`varchar(255)defaultNULL,`image`varchar(255)defaultNULL,PRIMARYKEY(`id`),UNIQUEKEY`name`(`name`))ENGINE=InnoDBDEFAULTCHARSET=gb2312;//商品类型表TB_GOODSTYPE(创建商品类型表,id为主键,唯一键为gooType)CREATETABLE`cmss`.`tb_goodstype`(`id`int(11)NOTNULLauto_increment,`gooType`varchar(255)defaultNULL,66 PRIMARYKEY(`id`),UNIQUEKEY`gooType`(`gooType`))ENGINE=InnoDBDEFAULTCHARSET=gb2312;//邮件列表(创建邮件表,id为主键,外键为sendMan和receiveMan,sendMan引用TB_USER表的id键,receiveMan引用TB_ZONE表的id键)createtablet_email(idint(20)auto_increment,sendManint(20),receiveManint(20),titlevarchar(100),informationvarchar(1024),sendTypeint(2)default1,receiveTypeint(2)default1,timetimestampdefaultCURRENT_TIMESTAMP,constraintt_email_pk_idprimarykey(id))ENGINE=InnoDBDEFAULTCHARSET=gb2312;5详细设计和实现5.1系统模块划分由以上业务需求的分析可知,本系统可以划分为六个模块,他们是:1.  用户管理模块  2.  订单管理模块    3.  邮件管理模块    4.  厂商及商品管理模块  66 5.  任务管理模块6. 管理员管理模块   由于是三个人合作这个项目,分工是免不了的。 根据小组安排,本人负责完成邮件管理模块、厂商及商品管理模块。5.2邮件管理模块图11邮件模块用例图当用户或物业登陆以后,他们就可以进入邮件管理模块。厂商是无权进入的。邮件是内部邮件,而且,用户只能向他的物业发送,以便咨询商品信息。而物业只能向他的用户发送。当然了,可以选择向不同的用户发送。5.2.1发送邮件当用户或物业点击发送邮件后,便可编写邮件。如果是用户,他不用输入收件人。因为他的收件人只有一个,那就是物业。如果是物业,他要选择给哪个用户发送。发送邮件的基本流程如下:66 获取用户标识用户身份判断不合法,抛出异常收件人判断获取收件人名称发送邮件获取邮件内容获取邮件标题不合法,抛出异常图12发送邮件流程图l基本流程1、获取用户标识2、获取收件人名称3、获取邮件标题4、获取邮件内容5、发送邮件l扩展流程1*用户身份非法,并提示用户类型错误2*如果用户不存在,提示错误。5.2.2查看邮件列表邮件列表有两个,一个是收件箱,一个是发件箱。在收件箱,可以看到那些邮件,哪些未读。在这里,用户和物业的功能差不多。66 当用户点击收件箱或发件箱时,会触发程序查询数据库。获得属于本用户的邮件,然后就可在页面上显示出来。查看邮件列表的基本流程如下:获取用户标识用户身份判断不合法,抛出异常获取收件人为用户的所有邮件获取用户发出的所有邮件图13查看邮件列表流程图l基本流程1、获取用户标识2、获取收件人为用户的所有邮件3、获取用户发出的所有邮件l扩展流程1*用户身份不合法,提示用户无此权限5.2.3删除邮件选择已读邮件,删除即可。在这里,是把邮件存在数据库里的,只有收件人和发件人都删除后,才真正把邮件从数据库删除。当收件人删除,而发件人没删除时,邮件对收件人不可见,而对发件人是可见的。当收件人和发件人都删除同一封邮件后,此封邮件才在数据库中删除。66 获取用户标识用户身份判断不合法,抛出异常邮件标识判断获取邮件标识删除指定邮件不合法,抛出异常邮件状态判断修改邮件状态图14删除邮件流程图删除邮件基本流程:l基本流程1、获取用户标识2、获取邮件标识3、删除邮件l扩展流程1*用户身份不合法,提示用户无此权限2*邮件标识判断,如果邮件不存在,抛出异常2*邮件状态判断,将邮件设为删除状态5.2.4查看邮件内容选择想要查看的邮件标题,即可查看邮件内容了。当邮件是未读状态时,查看邮件以后,邮件状态改为已读状态。查看邮件内容的基本流程图如下:66 获取用户标识用户身份判断不合法,抛出异常邮件标识判断获取邮件标识显示指定邮件不合法,抛出异常邮件状态判断修改邮件状态图15查看邮件内容流程图l基本流程1、获取用户标识2、获取邮件标识3、显示邮件信息l扩展流程1*用户身份不合法,提示用户无此权限2*邮件标识判断,如果邮件不存在,抛出异常2*邮件状态判断,将邮件设为删除状态5.2.5回复邮件对收到的邮件,进行回复。和写一封邮件类似。回复邮件的基本流程图如下:66 获取用户标识用户身份判断不合法,抛出异常邮件标识判断获取邮件标识获取收件人名称不存在,抛出异常收件人判断获取邮件标题获取邮件内容回复邮件不合法,抛出异常图16回复邮件流程图l基本流程1、获取用户标识2、获取邮件标识3、获取收件人名称4、获取邮件标题5、获取邮件内容6、回复邮件66 l扩展流程1*用户身份不合法,提示用户无此权限2*邮件标识判断,如果邮件不存在,抛出异常3*收件人判断,如果不要存在,抛异常邮件管理的部分Action代码://继承MappingDispatchAction,可以有多个路径访问这个BoxActionpublicclassBoxActionextendsMappingDispatchAction{//此方法的功能是,获得用户的收件列表,把它放到request里供下一页面调用//如果成功,转发到sucRece对应的页面publicActionForwardreceiverBox(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{Useruser=(User)request.getSession(false).getAttribute("user");ReceiverBoxrBox=BoxFactory.getRBOX();ZoneServerzoneS=newZoneServerImpl();Zonezone=zoneS.findZoneByName(user.getZoneName());Listlist=rBox.getEmail(user.getUserId(),zone.getUserId());request.setAttribute("receiverBox",list);returnmapping.findForward("sucRece");}//此方法的功能是,获得用户的发件列表,把它放到request里供下一页面调用//如果成功,转发到sucSen对应的页面publicActionForwardsendBox(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{Useruser=(User)request.getSession(false).getAttribute("user");SendBoxsBox=BoxFactory.getSBOX();ZoneServerzoneS=newZoneServerImpl();Zonezone=zoneS.findZoneByName(user.getZoneName());Listlist=sBox.getEmail(user.getUserId(),zone.getUserId());request.setAttribute("sendBox",list);returnmapping.findForward("sucSen");}//此方法的功能是,从Form表单里获得用户写的邮件信息,并把它存入数据库//如果成功,转发到success对应的页面publicActionForwardwriteEmail(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throws66 Exception{Useruser=(User)request.getSession(false).getAttribute("user");EmailFormemailForm=(EmailForm)form;SendBoxsBox=BoxFactory.getSBOX();Emailemail=newEmail();email.setTitle(emailForm.getTitle());email.setInformation(emailForm.getInformation());email.setSendMan(user.getUserId());email.setTime(newDate());ZoneServerzoneS=newZoneServerImpl();Zonezone=zoneS.findZoneByName(user.getZoneName());email.setReceiveMan(zone.getUserId());sBox.writeEmail(email);returnmapping.findForward("success");}}5.3厂商及商品管理模块当厂商登录以后,便可对自己注册的商品进行管理。5.3.1注册新商品商品注册信息要尽量填写完整,一定要上传一张商品图片。这样可以让顾客直观的看到商品。66 图17商品注册用例图获取用户标识用户身份判断不合法,抛出异常信息检查获取商品的相关信息保存商品信息缺参数,抛出异常图18注册商品流程图l基本流程1、获取用户标识2、获取商品的相关信息3、保存商品信息l扩展流程1*用户身份判断,如果用户身份非法,提示用户无此权限2*商品信息完整性检查,如果信息不完整,抛出参数不完整异常5.3.2查看注册商品列表显示自己已经注册的商品列表66 获取用户标识用户身份判断不合法,抛出异常获取相应用户的商品列表图19查看商品流程图l基本流程1、获取用户标识2、获取商品列表l扩展流程1*用户身份判断,如果用户身份非法,提示用户无此权限5.3.3修改商品信息选择想要修改的商品名,便可进入修改业面。注意,这里没有修改图片的功能66 获取用户标识用户身份判断不合法,抛出异常商品标识检查获取商品标识显示商品信息不合法,抛出异常信息检查获取商品的相关信息缺参数,抛出异常保存商品信息图20修改商品信息流程图1、获取用户标识2、获取商品标识3、显示商品信息4、获取修改后的商品信息5、保存商品信息l扩展流程1*用户身份判断,如果用户身份非法,提示用户无此权限2*商品标识判断,如果无此商品抛异常66 3*商品信息完整性检查,如果信息不完整,抛出参数不完整异常5.3.4删除商品对在销售的商品,只要在其后点击删除即可。获取用户标识用户身份判断不合法,抛出异常商品标识判断获取商品标识删除指定商品不合法,抛出异常图21删除商品流程图l基本流程1、获取用户标识2、获取商品标识3、删除商品l扩展流程1*用户身份判断,如果用户身份非法,提示用户无此权限2*商品标识判断,如果邮件不存在,抛出异常商品管理的Action代码://继承MappingDispatchAction,可以有多个路径访问这个GoodsManagerActionpublicclassGoodsManagerActionextendsMappingDispatchAction{66 //此方法的功能是,获得某厂商的注册商品列表//如果成功,转发到success对应的页面publicActionForwardallGoodsByFactoryName(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{HttpSessionsession=request.getSession(false);Factoryfa=(Factory)session.getAttribute("user");GoodsServergSer=newGoodsServer();Listlist=gSer.getGoodsByFactoryName(fa.getFactoryName());request.setAttribute("list",list);returnmapping.findForward("success");}//此方法的功能是,从form获得注册商品的信息,并把它保存的数据库里//如果成功,转发到successGoodsList对应的页面publicActionForwardregisterGoods(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{GoodsFormgoodsForm=(GoodsForm)form;HttpSessionsession=request.getSession(false);Factoryfa=(Factory)session.getAttribute("user");Goodsgoods=newGoods();goods.setName(goodsForm.getName());goods.setFactoryName(fa.getFactoryName());goods.setItemPrice(goodsForm.getItemPrice());goods.setVersion(goodsForm.getVersion());goods.setDescription(goodsForm.getDescription());goods.setNote(goodsForm.getNote());goods.setGoodsType(goodsForm.getGoodsType());Stringpathold=goodsForm.getImage();Stringfilename=pathold.substring(pathold.lastIndexOf("\")+1);Stringpath="../images/goods/"+filename;goods.setImage(path);GoodsServergSer=newGoodsServer();gSer.registerGoods(goods);returnmapping.findForward("successGoodsList");}66 //此方法的功能是,通过商品的id号删除此注册商品//如果成功,转发到successGoodsList对应的页面publicActionForwarddeleteGoods(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{GoodsServergSer=newGoodsServer();Goodsgoods=newGoods();goods.setId(Long.parseLong(request.getParameter("id")));gSer.deleteGoods(goods);returnmapping.findForward("successGoodsList");}//此方法的功能是,从form获得修改后商品的信息,并更新数据库//如果成功,转发到successGoodsList对应的页面publicActionForwardupdateGoods(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{GoodsFormgoodsForm=(GoodsForm)form;HttpSessionsession=request.getSession(false);Factoryfa=(Factory)session.getAttribute("user");Goodsgoods=newGoods();goods.setId(Long.parseLong(request.getParameter("id")));goods.setName(goodsForm.getName());goods.setFactoryName(fa.getFactoryName());goods.setItemPrice(goodsForm.getItemPrice());goods.setVersion(goodsForm.getVersion());goods.setDescription(goodsForm.getDescription());goods.setNote(goodsForm.getNote());goods.setGoodsType(goodsForm.getGoodsType());goods.setImage(goodsForm.getImage());GoodsServergSer=newGoodsServer();gSer.updateGoods(goods);returnmapping.findForward("successGoodsList");}//此方法的功能是,通过商品id获得商品的注册信息//如果成功,转发到success对应的页面publicActionForwardreadGoods(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throws66 Exception{GoodsServergSer=newGoodsServer();Goodsgoods=gSer.getGoodsById(Long.parseLong(request.getParameter("id")));request.setAttribute("goods",goods);returnmapping.findForward("success");}}6测试6.1整合测试由于我们是几个人合作一个项目,因此,把各个模块组合起来运行是很重要的一件事情。由于我们在毕业设计之前,已经统一了包名和工具类,因此组合起来比较容易。只是代码的融合性不够,要经过一定的测试。6.2功能测试1、用户注册登录测试2、邮件管理测试3、订单管理测试4、商品注册测试66 5、任务管理测试6、管理员管理测试6.3商品注册测试1、新商品未注册前,网站首页图22网站首页2、厂商用户登录后,开始注册新商品图23商品管理页3、商品注册后,便能看到注册的商品图24注册商品后的管理页66 4、此时看到网站的首页如下:图25注册商品后的首页5、测试结果正确,符合要求结论毕业设计是大学四年来,最重要的一次作业。它是对你大学四年的一次综合考核。它不光检验知识,同时也检验了你的能力。如果毕业设计认真做了,并完成的很好,它是对你大学四年学习的一次升华。我的这次毕业设计是基于社区的商品代理服务平台,可以认为它是一个购物商店,不过它更像一个购物商城。如果要是想完整的完成,那要花费很大的人力,物力。不是短时间能做完的。我们有三个人一起合作这个项目,首先是一起分析项目,分析好以后根据每个人的能力,给每个人分配任务。在分配任务时,我们要统一包名,使用相同的工具类,对什么类存放在不同的包下,使用什么类名都有规定,这样即方便别人调用。然后,各做各的模块,做完以后再合起来,进行整合。几个人共同做项目最重要的事情就是沟通,只有沟通好了,后来整合起来才容易,否则可能每个模块单个运行是正确的,合起来就不能用了。甚至,有人的代码可能要重写。66 我们尽管一开始就进行了规定,当到整合时,仍有一些问题。比如:一个人的代码不能满足另一个人的需求或者另一个人想要的功能,这个人却没有写。这是我们开始做项目的时候没有想到的,因此,项目里还有一些不完善的地方,比如:邮件管理的功能过于简单,项目的健壮性还不够强,这是今后要努力的方向。致谢毕业设计是大学里的最后一门课,也可以说是最难的一门课。它对我们的能力提出了更高的要求,在毕业设计中不免要遇到这样那样的困难。在这里首先要感谢我的指导老师陆勤老师,是他督促我制定详细的毕业设计计划,监督我按时完成计划的每一步,当遇到技术上的困难给他发邮件时,他都第一时间回复我。在此我要对他表示深深的感谢。同样要感谢我们的战老师,在毕业设计中,他也给了我极大的帮助。还要感谢我的合作伙伴,汪沛然同学和徐鑫海同学,有他们的极力配合我才完成了这次毕业设计。66 参考文献[1]戎伟,张双.精通Struts-java流行服务器、框架、工具及整合应用.人民邮电出版社.2007[2]刘斌.精通JavaWeb整合开发.电子工业出版社.2007[3]孙卫琴,李洪成.Tomcat与JavaWeb开发技术详解.电子工业出版社.2004[4]孙卫琴.精通Hibernate:Java对象持久化技术详解.电子工业出版社.2005[5]夏昕,曹晓钢,唐勇.深入浅出Hibernate.电子工业出版社.2005[6]MartyHall,LarryBrown著.赵学良译.Servlet与JSP核心编程.清华大学出版社.2004[7]刘晓华,张健,周慧贞.JSP应用开发详解.电子工业出版社.2007[8]孙卫琴.精通Struts:基于MVC的JavaWeb设计与开发.电子工业出版社.2007[9]ChristianBauer.Hibernate实战(第2版·英文版)(原名:Hibernateinaction).人民邮电出版社.2007[10]TedHusted(美).StrutsInAction.北京:机械工业出版社.2007[11]BruceTate.Crossingborders:TypingstrategiesbeyondtheJavamodel.http://www.ibm.com.200666 附录一:系统部分代码/**此类是用于对注册商品的管理,有注册商品,删除商品,修改商品信息,*通过商品名获得商品,通过商品Id获得商品,通过商品生产厂商获得商品.*@authorliulei**/publicclassGoodsServer{//注册商品publicvoidregisterGoods(Goodsgoods){GoodsDaogoodsDao=null;Transactiontra=null;Sessionsession=null;try{//获得数据库连接session=HibernateUtil.getCurrentSession();//开启事物tra=session.beginTransaction();goodsDao=newGoodsDaoImpl();//保存商品信息goodsDao.registerGoods(goods);//事物提交tra.commit();66 }catch(HibernateExceptione){//如果有异常,事物回滚tra.rollback();e.printStackTrace();}finally{//关闭数据库连接HibernateUtil.closeCurrentSession();}}//删除数据库中,这条商品记录publicvoiddeleteGoods(Goodsgoods){GoodsDaogoodsDao=null;Transactiontra=null;Sessionsession=null;try{session=HibernateUtil.getCurrentSession();tra=session.beginTransaction();goodsDao=newGoodsDaoImpl();goodsDao.deleteGoods(goods);tra.commit();}catch(HibernateExceptione){tra.rollback();e.printStackTrace();}finally{HibernateUtil.closeCurrentSession();}}//修改商品信息publicvoidupdateGoods(Goodsgoods){GoodsDaogoodsDao=null;Transactiontra=null;Sessionsession=null;try{session=HibernateUtil.getCurrentSession();tra=session.beginTransaction();goodsDao=newGoodsDaoImpl();goodsDao.updateGoods(goods);66 tra.commit();}catch(HibernateExceptione){tra.rollback();e.printStackTrace();}finally{HibernateUtil.closeCurrentSession();}}//通过商品名,获得商品publicGoodsgetGoodsByName(Stringname){GoodsDaogoodsDao=null;Transactiontra=null;Sessionsession=null;Goodsgoods=null;try{session=HibernateUtil.getCurrentSession();tra=session.beginTransaction();goodsDao=newGoodsDaoImpl();goods=goodsDao.getGoodsByGoodsName(name);tra.commit();}catch(HibernateExceptione){tra.rollback();e.printStackTrace();}finally{HibernateUtil.closeCurrentSession();}returngoods;}//通过商品ID获得商品publicGoodsgetGoodsById(Longid){GoodsDaogoodsDao=null;Transactiontra=null;Sessionsession=null;Goodsgoods=null;try{session=HibernateUtil.getCurrentSession();tra=session.beginTransaction();goodsDao=newGoodsDaoImpl();goods=goodsDao.getGoodsById(id);66 tra.commit();}catch(HibernateExceptione){tra.rollback();e.printStackTrace();}finally{HibernateUtil.closeCurrentSession();}returngoods;}//通过商品生产厂商获得商品publicListgetGoodsByFactoryName(StringfactoryName){GoodsDaogoodsDao=null;Transactiontra=null;Sessionsession=null;Listlist=null;try{session=HibernateUtil.getCurrentSession();tra=session.beginTransaction();goodsDao=newGoodsDaoImpl();list=goodsDao.getGoodsByFactoryName(factoryName);tra.commit();}catch(HibernateExceptione){tra.rollback();e.printStackTrace();}finally{HibernateUtil.closeCurrentSession();}returnlist;}//获得所有注册商品publicListgetAllGoods(){GoodsDaogoodsDao=null;Transactiontra=null;Sessionsession=null;Listlist=null;try{session=HibernateUtil.getCurrentSession();tra=session.beginTransaction();66 goodsDao=newGoodsDaoImpl();list=goodsDao.getAllGoods();tra.commit();}catch(HibernateExceptione){tra.rollback();e.printStackTrace();}finally{HibernateUtil.closeCurrentSession();}returnlist;}}/**此类继承MappingDispatchAction,可以有多个路径访问这个BoxAction*它有以下功能:通过邮件ID删除收件箱邮件,删除收件箱所有邮件,通过ID读取邮件*内容*@authorliulei**/publicclassOpeReceiverBoxActionextendsMappingDispatchAction{/***此方法,通过邮件ID删除邮件,如果成功,转到success对应的路径*@parammapping包含此Action的配置文件信息*@paramform请求中的Form表单*@paramrequest包含了请求信息*@paramresponse包含响应信息*@return*@throwsException声明可以抛出的异常*/publicActionForwarddeleteEmail(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{ReceiverBoxrBox=BoxFactory.getRBOX();rBox.deleteEmail(Long.parseLong(request.getParameter("id")));returnmapping.findForward("success");}//此方法,通过用户ID删除此用户收件箱所有邮件66 publicActionForwarddeleteEmailAll(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{Useruser=(User)request.getSession(false).getAttribute("user");ReceiverBoxrBox=BoxFactory.getRBOX();ZoneServerzoneS=newZoneServerImpl();Zonezone=zoneS.findZoneByName(user.getZoneName());rBox.deleteEmailAll(user.getUserId(),zone.getUserId());returnmapping.findForward("success");}//此方法,通过邮件ID读取邮件内容publicActionForwardreadEmailById(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{ReceiverBoxrBox=BoxFactory.getRBOX();Emailemail=rBox.readEmailById(Long.parseLong(request.getParameter("id")));request.setAttribute("email",email);returnmapping.findForward("success");}}/***此类继承MappingDispatchAction,可以有多个路径访问这个GoodsShowManagerAction*它有以下功能:获得所有商品供页面显示,通过商品ID删除注册的商品,通过ID读取商品信息*@authorliulei**/publicclassGoodsShowManagerActionextendsMappingDispatchAction{/***获得所有商品供页面显示*@parammapping包含此Action的配置文件信息*@paramform请求中的Form表单*@paramrequest包含了请求信息*@paramresponse包含响应信息*@return66 *@throwsException声明可以抛出的异常*/publicActionForwardallGoods(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{GoodsServergSer=newGoodsServer();Listlist=gSer.getAllGoods();request.setAttribute("allGoodsList",list);returnmapping.findForward("success");}//通过商品ID删除注册的商品publicActionForwarddeleteGoods(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{GoodsServergSer=newGoodsServer();Goodsgoods=newGoods();goods.setId(Long.parseLong(request.getParameter("id")));gSer.deleteGoods(goods);returnmapping.findForward("successDelete");}//通过ID读取商品信息publicActionForwardreadgoods(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{GoodsServergSer=newGoodsServer();Goodsgoods=gSer.getGoodsById(Long.parseLong(request.getParameter("id")));request.setAttribute("goods",goods);returnmapping.findForward("success");}}66 附录二:英文翻译Crossingborders:TypingstrategiesbeyondtheJavamodelTheJavacommunityissplitwhenitcomestothelanguage"sapproachtotyping.Somelovethecompile-timeerrorchecking,bettersecurity,andimprovedtools--allfeaturesenabledbystatictyping.Otherswouldpreferamoredynamicallytypedexperience.ThistimeinCrossingborders,you"lllookatthedramaticallydifferenttypingstrategiesusedbytwohighlyproductivenon-JavalanguagesandatwaysyoucanachievesometypingflexibilityinyourJavaprogramming.Oneofthemorecontentiousissuesinanyprogramming-languagediscussionisthetypingmodel,andwithgoodreason.Typingdeterminesthekindsoftoolsyoucanuseandimpactsapplicationdesign.Manydeveloperslinktypingtoproductivity(asIdo)ormaintainability.ThetypicalJavadeveloperisoftenespeciallyprotectiveoftheJavalanguage"stypingmodel,citingbetterdevelopmenttools,theabilitytocatchcertainkindsofbugs--suchastypeincompatibilitiesandmisspellings--atcompiletime,andperformancebenefits.Ifyouwanttounderstandanewprogramminglanguage,orevenafamilyoflanguages,youshouldoftenstartwiththetypingstrategy.Inthisarticle,you"llseealternativestotheJavatypingmodel.I"llstartwithageneraldescriptionofthedecisionsanylanguagedesignermustconsiderwithinatypingmodel,focusingonthecontroversialdecisionofstaticversusdynamictyping.I"llshowexamplesonbothendsofthespectrum--inObjectiveCamlforstatictypingandinRubyfordynamictyping.I"llalsotalkabouttheJavalanguage"stypinglimitationsandhowit"spossibletoprogramproductivelybothwithinandagainstthem.TypingstrategiesYoucanlookattypingalongatleastthreeaxes:·Staticversusdynamictypingdependsonwhenatypingmodelisenforced.Staticallytypedlanguagesenforcetypingatcompiletime.Dynamicallytypedlanguagesenforcetypingatruntime,usuallybasedonanobject"scharacteristics.·Strongversusweaktypingdependsonhowtypingmodelsareenforced.Strongtypingenforcesthetypingrigorously,throwingrun-timeorcompilationerrorsifyoubreaktypingrules.Weaktypingallows66 moreleeway.Attheextremeendpoints,aweaklytypedlanguagesuchasAssemblerletsyouassignanydatatypetoanyother(whethertheassignmentmakessenseornot).Staticallytypedlanguagescanhavestrongorweaktyping;dynamicallytypedsystemsareusually,thoughnotexclusively,stronglytyped.·Explicit(alsocalledmanifest)typingversusinferredtypingdependsonhowthelanguagedeterminesagivenobject"stype.Amanifestlytypedlanguageforcesyoutodeclareeachvariableandeachfunctionargument.Atype-inferredlanguagedetermineswhichtypeanobjectmightbebasedonsyntacticorstructuralcluesinthelanguage.Staticallytypedlanguagesareusually,butnotexclusively,typeexplicit;dynamicallytypedlanguagesarealmostalwaystype-inferred.Herearetwoexamplesthatgiveyouagoodviewofwhathappensalongtwoofthethreeaxes.SupposeyoucompilethisJavacode:classTest{publicstaticvoidtest(inti){Strings=i;}}Yougetthiserrormessage:Test.java:3:incompatibletypesfound:intrequired:java.lang.StringStrings=i;^1errorExecutingthisRubycode:1+"hello"Givesyouthiserrormessage:TypeError:Stringcan"tbecoercedintoFixnum66 from(irb):3:in"+"from(irb):3Bothlanguagesleantowardstrongtypingbecauseboththrowerrormessageswhenyoutrytouseanobjectoutsideofitsintendedtypestructure.TheJavatypingstrategygivesyoutheerroratcompiletimebecauseitperformsstatictypechecking.RubygivesyouanerroratruntimebecauseRubysupportsdynamictyping.Saidanotherway,Javabindstheobjecttoatypeatcompiletime.Rubybindstheobjecttothetypeatruntime,eachtimeyouchangetheobject.BecauseIdeclaredthevariableintheJavacodebutdidn"tinRuby,youseethedifferencebetweentheJavalanguage"sexplicittypingandRuby"sinferredtypinginaction.Ofthethreeaxes,staticversusdynamictypingtendstohavethemostimpactonalanguage"scharacter,soI"llfocusnowonthesetwostrategies"respectivestrengths.StrengthsofstatictypingInstaticallytypedlanguages,theprogrammer(throughadeclarationorconvention)orthecompiler(throughinferencebasedonstructuralandsyntacticclues)assignsatypetoavariableorobject,andthattypedoesn"tchange.StatictypingusuallycomeswithacostbecausestaticallytypedlanguagessuchastheJavalanguageareusuallytypeexplicit.Thatmeansyoumustdeclareallofyourvariablesandcompilethecode.Thatcostcomeswithapayoff:earlyerrordetection.Atthemostbasiclevel,statictypingexiststogivethecompilermuchmoreinformation.Oneofthebenefitsoftheadditionalinformationistheabilitytocatchcertainkindsoferrorsthatadynamicallytypedlanguagewon"tdetectuntilruntime.Ifyouwaituntilruntimetocatchthesekindsofbugs,somewillmakeitintoproduction.Thisisperhapsthestrongestargumentagainstdynamicallytypedlanguages.Thecounterclaimisthatmodernsoftware-developmentteamsoftenrunautomatedtests,anddynamic-languageproponentsclaimthateventhesimplestautomatedtestscatchavastmajorityoftypeerrors.Butthebestargumentdynamiclanguageproponentscanpossiblymakeagainstcompile-timeerrordetectionisthatthebenefitofearlydetectionisn"tworththecostbecauseyou"vegottotestanyway,whetherornotyouusedynamictyping.66 Oneinterestingtrade-offwouldbetouseinferredtypingwithstaticallytypedlanguages,reducingthecostoftyping.TheopensourceObjectiveCaml(OCaml)languageisastaticallytypedLispderivativethatenablesexcellentperformancewithoutsacrificingproductivity.OCamlusesinferredtyping,soyoucanhavethisstaticallytypedexample:#letx=4+7OCamlreturns:valx:int=11OCaml,basedonsyntacticcluesintheexpression,infersthetypeofx.4isanint,and7isanint,soxmustbeaninttoo.Type-inferredlanguagescanhaveallofthetypesafetyoftheJavalanguage,andevenmore.Thedifferenceistheamountofinformationthatyouneedtoprovideandtheamountofinformationthatyouhaveavailablewhenyou"rereadingtheprogram.Manypeoplewholovestatictypingpreferinferredtyping.They"dratherthecompilerdotheworkthanbeforcedtorepeatthemselvesintheircode.Onebigadvantageoftype-inferredsystemsisthatyoudon"tneedtodeclarethetypesofargumentstoafunctionbecausethecompilerinfersthemfromtheparametersyoupassin.Thisletsyouusethesamemethodformultiplepurposes.Thecompilerisn"ttheonlytoolthatcantakeadvantageoftheadditionalinformationthatstatictypingprovides.IDEs,throughstatictyping,canprovidemuchbettersupportforrefactoring.Afewyearsago,arevolutionaryideachangedthewaythatdevelopmentenvironmentswork.InIDEAandEclipse,yourcodelookslikeatextview,butthedevelopmentenvironmentisactuallyeditingtheAbstractSyntaxTree(AST).Sowhenyouwanttorenameamethodorclass,it"seasyfortheenvironmenttofindeveryuseofitbypinpointingthelocationintheAST.Today,it"shardtoimagineprogrammingintheJavalanguagewithoutexcellentrefactoring,madeeasierbystatictyping.InmyexplorationofRuby,ImissIDEAmorethananyothertoolorfeature.StatictypinghassomeotheradvantagesthatIwon"tdiscussindetailhere.Statictypingpossiblyenablesbettersecurityanddefinitelyimprovescodereadabilityinplaces.Statictypingcanalsoprovidemoreinformationthatacompilercanusetomakeearlieroptimizations,66 improvingperformance.Butthebiggestwinsforstatictypingformostdevelopersareearliererrordetectionandbettertooling.StrengthsofdynamictypingRubymavenDaveThomashaslabeleddynamictypingwiththetermducktyping(seeResources),whichhastwomeanings.Thefirstisthatthelanguagedoesn"treallyimplementtyping--itduckstheissue.Thesecondmeaningisthatifsomethingwalkslikeaduckandquackslikeaduck,it"sprobablyaduck.Inthecontextofprogramminglanguages,ducktypingmeansthatifanobjectrespondstothemethodsofatype,thenforallpracticalpurposes,youcantreatitlikethattype.Thisbehaviorleadstosomeinterestingoptimizations.Inadditiontoarguingthattestsmakethecostofearlyerrordetectionunnecessary,mostdeveloperswhopreferdynamictypingcitetheexpressivenessandproductivityofadynamicallytypedlanguage.Quitesimply,youcanusuallyexpressmoreideasinfewerkeywords.AsanewRubyconvert,Istronglybelievethatdynamiclanguagesaremoreproductive,thoughIdon"thaveanymorehardevidencethanatypicalstatic-languageproponent.ButI"vedefinitelyexperiencedanimprovementinmyproductivitysinceIstartedtowritemoreRubycode.Sure,Icanstillseethebenefitsofstatictyping,especiallyinmytoolsets,butI"malsostartingtorecognizethedisadvantages.ThebigthingthatchangedformewhenIstartedcodinginRubyismyabilitytoproduceandconsumemetaprogrammingconstructs.Ifyou"vebeenfollowingtheCrossingbordersseriessinceitsinception,youknowthatmetaprogramming,orwritingprogramsthatwriteprograms,isoneofthedrivingforcesbehindRubyonRailsinparticular,anddomain-specificlanguagesmoregenerally.InRuby,I"musuallycodinglargerbuildingblocks,orI"mbuildingwiththelargerblocks.IfindthatIcanextendmyprogramwithmorekindsofreusableblocksthanIcaninJavacoding.AswithJavaprogramming,youcanextendyourprogramswithnewclasses.YoucanalsoaddmethodsanddatatoexistingRubyclassesbecauseclassesareopen.Youcanusemix-ins(morebelow,underRun-timebinding)toaddcorecapabilitiestoexistingclasses.Youcanalsochangethedefinitionofanobjectatanytimetosuityourpurposes.Idon"tneedthesecapabilitiesveryoftenanddidn"tusethemasanoviceRubyprogrammer,butwhenIdidstarttousethem,theresultswerequiteamazing.Forexample,toaddaninterceptor,yousimplyrenameamethodandcreateanewimplementationoftheoriginalone.Tointerceptnew,you"dwrite66 thiscode:classClassalias_method:old_new,:newdefnew(*args)puts"Interceptednew"#dointerceptionworkhereold_new(*args)endendYoudon"tneedanAspectJlibrary,bytecodeenhancement,orstacksoflibraries.Yousimplycodeyourinterceptordirectlywhereyouneedit.Atanotherlevel,dynamictypingsaveseffortintermsofrawlinesofcode.Becausedynamiclanguagesarealmostalwaystypeinferred,youdon"tneedtoworkashardtoexpressbasicideas.Ratherthandeclaringavariable,you"refreejusttostartusingit;ratherthanexpressallofthepossiblepermutationsofthetypesofarguments,youcanjustenteralistofnames.Yourcodecanbemorepolymorphic--everythingthatrespondstoamethodcanbetreatedasonetype--soyoucanoftenexpressideasmuchmoreconciselythanyoucaninotherlanguages.Youalsohavemuchlesscouplinginyourcode.Whenyouwanttochangethetypeofsomething,thechangeisusuallylocalized,soyouaren"tforcedtomakechangesinmultipleplaces.Thefinalproductivityboosteristhelackofacompilestep.Manydynamicallytypedlanguagesareinterpreted,soyoucanseechangesimmediatelyaftermakingthem.ExploringthebehaviorsoflibraryandapplicationcodeismucheasierinRuby,evenwithoutaconventionaldebugger,becauseyoucanopenupaninterpreter,ofteninthecontextofadebuggingsession(asIshowedintheprecedingarticleofthisseries),andsimplyexplore.Andyet...Compilationdoesmorethansupportstatictyping,though.Static-languageproponentsoftenclaimbetterperformancetoo.Manystaticlanguages,suchasJavacode,C,andC++,arecalledsystemslanguagesbecausethey"rethemostcommonlanguagesusedtobuildoperatingsystems,devicedrivers,andotherhigh-performancesystemscode.Thisoftenleadsfansofdynamicallytypedlanguagestoaccusestaticallytypedlanguagesofalwaysbeingtoolow-leveltobeproductive66 languagesforapplications--butthat"sanarrowworldview.TheOCamllanguageisaquitehigh-levellanguage,capableofobject-orientedprogramming,functionalprogramming(likeLisporErlang),ortraditionalstructuredprogramming.Thetypingmodelisstatic,andmanysaythattheperformanceisevenbetterthanC++"s(seeResources).WithOCaml,youpayminimaloverheadforyourstatictypingbecausethelanguageistypeinferred.Forthetrouble,yougetrock-solidperformance,compile-timetypechecking,andaveryhigh-levellanguage.Eventhestaunchestproponentsofducktypinghavetorespectthosetrade-offs.TypinglimitationsintheJavalanguageJavadeveloperstakefulladvantageofstatictyping.Theyhavesomeofthebestdevelopmenttoolsintheworldwithcapabilities,suchascodecompletionandrefactoring,thatleanheavilyonstatictyping.ThemanyJavaprogrammerswhoareonlynowstartingtotakefulladvantageoftest-firstdevelopmentgetaddedstabilitybecausethecompilercancatchbugsrelatedtotype.Newlanguagefeaturessuchasgenericsstrengthenthetypingmodelandprovidestillmoreinformationtothecompiler.ButJavadevelopersareoftenblindtosomeofthebenefitsofdynamictyping.Run-timebindingTheflexibilitythatdynamictypingpermitsismoreimportantthanyoumightthink.Insomeways,JavadevelopersseektodefeatstatictypingbyusingmoreXML(whichcandelaybindinguntilruntime)andstrings(whichcanrepresentmanydifferentkindsoftypes).Configuration,whichcanoftenbenefitfromrun-timebinding(andthusdynamictyping)inRubynormallytakestheformofRubycode,andinJavaprogrammingnormallytakestheformofXML.TaketheSpringframework(seeResources):ToconfigureagenericSpringbean,youuseXML.YoumustprovideavalidJavaclassnameandsetthepropertiesforeachvariable.Forexample,persistenceenginessuchasHibernateneedasessionfactory(seeResources).Configuringadata-accessobjectintheJavasyntaxwouldbequitepleasant:DaomyDataAccessObject=Dao.new(sessionFactory);Theproblemisthatthislineofcodegetsboundatcompiletime,andthat"stoostatic.Youoftenneedtoreplacetheinstanceofthesessionfactoryorthedata-accessobjectwithsomethingelse,suchasamock66 data-accessobjectfortesting.Soinsteadofhard-codingtheexampleasabove,you"duseaframeworkliketheSpringframeworktoconfiguretheitemwithXML,whichmightlooksomethinglikethis(fromtheSpringFrameworkexamplecalledpetclinic):TheSpringframeworkisoneofthemoreimportantandinfluentialframeworksintheJavacommunitytodaybecauseitletsyoudelaybindingandloosencouplingbetweenmajorelementsofyoursystem.Further,youcandecouplewithoutblowingyouroneshotatinheritance.InJavaprogramming,especiallywhenyou"rewritinganincreasingnumberofPOJOs(plainoldJavaobjects),youmustbeverycarefulwhenyouuseinheritancebecausewiththeJavalanguage,yougetonlyoneshot.InadynamiclanguagesuchasRuby,thesolutionwouldbestrikinglydifferent.First,Iwouldbeinclinedtouseamix-intoimplementpersistence.Allassociationswouldhappenonceinthemix-in.Thinkofamix-inasaninterfacewithanimplementationbehindit.Inotherwords,withamix-in,youcanaddmultiplecapabilitiestothesameobjectwithoutmultipleinheritance.Infact,ActiveRecorddoesexactlythisthroughinheritingfromacommonbaseclass,whichhasmultiplecapabilitiesmixedin:classPojoSpring框架是目前Java社区中最重要、最有影响力的框架之一,因为它使您可以延迟绑定,并使系统主要元素之间的耦合性更为松散。而且,您不需要关心继承就可以去耦。在Java编程中,尤其是在编写越来越多的POJO(plainoldJavaobject)的时候,使用继承时必须特别小心,因为在Java语言中只有一次这样的机会。在动态语言,例如Ruby中,解决方案就截然不同。首先,我倾向于使用一个mix-in来实现持久性。所有关联只在mix-in中发现一次。可以把一个mix-in想像成一个接口,其背后有一个实现。换句话说,通过mix-in,可以添加多个功能到同一个对象中,而不必使用多重继承。实际上,ActiveRecord通过继承一个公共基类来解决这个问题,这个公共基类混合了多种功能:classPojo