基于J2EE规范的TeaFramework框架(毕业设计用于学习J2EE写的一个框架)

基于J2EE的框架技术的实现与应用

【摘要】 J2EE的应用越来越广泛,J2EE应用从设计上可以分为三层:表示层、业务层和数据持久层。在这三层上与之对应的J2EE技术为WEB技术和EJB技术,WEB技术实现表现层,而EJB规范负责业务层和数据层。由于J2EE技术的不断发展,在J2EE设计的各个层次现在都有对应的轻量级框架出现,轻量级框架的发展极大了推动了J2EE技术的发展,轻量级框架技术已经成为了下一代J2EE构架的基础。 本次论文中,对J2EE技术和框架技术进行了研究,特别是J2EE轻量级框架技术的研究,在研究了J2EE技术的新发展和分析了一些轻量级框架(Struts2、Spring、iBatis)思想及源码的基础上提出了一个基于J2EE的轻量级框架TeaFramework,并给出了其各层相应的框架组件的设计,包括表示层MVC组件;业务层包括IoC组件;数据持久层主要是O/R映射组件。重点分析论述了MVC组件和业务层组件,并给出了MVC组件和业务层组件详细的设计和实现。在论文的最后通过应用一个资产管理系统,实际检验了本论文研究的轻量级的通用的快速开发框架的可行性及稳定性,该资产管理系统的功能主要包括用户管理、资产管理以及资产统计等。

目 录1 绪 论 11.1课题的研究背景 11.2国内外现状及趋势 21.3课题的主要研究工作 22 相关技术分析 42.1 J2EE技术 42.1.1 J2EE的概念 42.1.2 J2EE的体系结构 42.2框架技术 52.2.1 框架出现的原因 62.2.2 框架的定义 62.2.3 框架的特点 72.3 轻量级框架的发展 73 轻量级框架体系结构设计 93.1 框架的设计分析 93.2 框架的总体设计 94 轻量级表现层框架设计与实现 114.1 Web应用系统传统开发方式分析 114.2表现层MVC框架的设计 124.3表现层MVC框架的实现 134.3.1 框架的初始化 134.3.2 入口过滤器的实现 154.3.3 数据流体系的实现 174.3.4 控制流体系的实现 184.3.5 一次完整请求处理的实现 204.4 MVC框架所涉及的设计模式 224.4.1 ThreadLocal模式 224.4.2拦截器模式 234.4.3策略模式 234.5 MVC框架配置及使用说明 245 轻量级业务层框架设计与实现 265.1 IoC模式的分析 265.1.1 IoC模式的产生 265.1.2 IoC模式与工厂模式 265.1.3 IoC组件产生的意义 275.2业务层 IoC框架的设计 285.2.1 IoC容器的总体设计 285.2.2 IoC框架的关键技术 295.2.3 IoC框架在TeaFramework中的作用 305.3 业务层IoC框架的实现 305.3.1 BeanDefinition的载入与解析 315.3.2 BeanDefinition在IoC容器中的注册 325.3.3 Bean的实例化及依赖注入的实现 335.3.4 TeaIoC整体核心类图 355.3.5从BeanFactory得到Bean的流程 365.4 IoC框架与MVC框架的整合实现 375.5 IoC框架配置及使用说明 386 轻量级数据持久层框架设计与实现 406.1 数据持久化技术分析 406.1.1 JDBC技术 406.1.2数据持久层分析 406.1.3全自动ORM与半自动ORM技术的比较 416.2 数据持久层ORM框架的设计 426.3 数据持久层ORM框架的实现 436.3.1 TeaORM的实现原理 436.3.2 SQL语句的解析 436.3.3 Statement的实现 446.3.4 Parameter输入参数的实现 456.3.5 ResultMap输出结果的实现 466.4 IoC框架与ORM框架的整合实现 476.5 ORM框架配置及使用说明 487 轻量级TeaFramework框架的应用 507.1 系统分析 507.1.1 资产管理系统简介 507.1.2 资产管理系统功能介绍 507.1.3 资产管理系统数据分析 517.2 系统设计 517.2.1 系统总体结构设计 517.2.2 数据库设计 527.3 系统实现 547.3.1 TeaFramework在资产管理系统中的应用 547.3.1.1 TeaMVC与Web环境的整合配置 547.3.1.2 TeaMVC的应用配置 557.3.1.3 TeaMVC拦截器实现登录及权限验证 567.3.1.4 TeaORM的应用配置 587.3.1.5 TeaIoC的应用配置 597.3.1.6 TeaIoC和TeaMVC的整合配置 607.3.1.7 TeaIoC和TeaORM的整合配置 617.3.2.系统功能模块的设计与实现 617.3.2.1 资产管理系统工程包层次结构 617.3.2.2 资产添加模块的设计与实现 627.3.2.3 资产查询模块的设计与实现 647.4 利用TeaFramework实现资产管理系统的效果 67 1.1 课题的研究背景J2EE本身就是一个标准的集合,是一个标准的WEB应用。之所以使用框架,从根本上来说还是为了理清程序逻辑和程序结构,减轻程序员的开发强度,让程序员更加注重业务的开发。J2EE框架标准将一个系统划分为WEB和EJB两个主要部分,从设计上可以抽象为表现层、业务层和持久层,这三个层次从一个高度将J2EE分离开来,实现了解耦的目的。因此,在实际编程中大多数应用从功能上根据这三个层次来划分,但要这样做,没有技术上约束限制是比较困难的,因此一般借助J2EE具体技术来实现,可以使用EJB规范来实现服务层和持久层,Web技术来实现表现层。J2EE应用虽然从总体上划分了三个层次,但在针对每一层上的具体应用,要设计出可维护性、高拓展性的软件设计目标还是有一定的难度。首先,Web层的技术主要是JSP/SERVLET,在这层仍然可以表现业务逻辑和数据访问,那么其后果是WEB层与后台逻辑过于耦合,造成多层结构开发无法分工合作;其次,EJB被认为是一种重量级的高度侵入性的框架规范,重量级在于它的基于分布式的应用,高侵入性是指它制定了众多的接口和编码规范,要求实现者必须遵从;重量级的后果是部署复杂、运行慢、测试困难和代价高等,侵入性的后果就是一旦系统基于侵入性框架设计开发,那么之后任何脱离这个框架的企图都将付出极大的代价。轻量级框架的发展已经成为推动J2EE技术发展的重要推动力,目前,轻量级框架的发展非常的快,同一类型的框架不断有新框架推出,原因在于各个框架都不是很完美。J2EE框架研究与实现也是基于这样几个出发点:(1)现在一些优秀的开源框架的设计理念很多是值的学习及研究的,一些好的的设计可以应用到自己定制的轻量级框架中。(2)不是所有的框架都适合每个项目的应用,很多时候我们只需要应用其中的部分功能,而有时开发一个项目时需要多个框架的集成。因此,在开发项目时需要定制适合自己项目的轻量级框架。(3)在框架的应用前还是要了解框架本身的技术实现,在实现项目开发的轻量级框架过程中,既有技术基础的积累,又能便于框架的定制和推广。(4)现在的很多框架只是集中在一个层次解决J2EE的应用问题,定制的轻量级框架可能是一个多层次的解决方案。(5)希望实现的轻量级框架能成为比较实用和通用的软件开发半成品框架或平台,能迅速地实现软件的构建从而提高软件的生产率。    1.2 国内外现状及趋势针对重量级框架中的问题,近年来,轻量级框架的发展非常的繁荣,比较成功的有在Web层出现了许多通用的Web开发框架如Turbine,Struts,Webx等,为Web应用开发提供了便利。其中Struts是一个典型的MVC模式的Web开发框架,也是一个应用非常广泛的Web框架,应用Struts2框架可以开发出基于MVC设计模式的Web应用构架。在业务层非常成功的主要有Spring和Hibernate框架,这两个框架联合起来的作用相当于在J2EE体系结构中的EJB组件发挥的作用。Spring是一个以反向控制(IoC Inversion of Control)为基础的轻量级框架。其设计目标是提供一种无侵入式的高扩展性构架。即无需代码中涉及Spring专有的类,即可将其纳入Spring容器进行管理。而Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得JAVA程序员可以随心所欲的使用对象编程思维来操纵数据库。不断完善的Hibernate可以在应用EJB的J2EE架构中取代CMP完成数据持久化的重任。人们日益对开源框架的重视,使得很多项目的成本大大降低,并且投放使用以及维护速度都增加了。现在的开源框架都有很高的质量,都提供了很好的文档&一些书籍让开发者做参考。即便如此,两大因素是的J2EE领域充满了不确定性:开源领域和J2EE“标准”的冲突和AOP的日益重要。开源和标准之间的冲突表现在两个地方。一个是表现层,JSF的身后有Sun公司和其他的一些大公司,而在这个领域有Struts等开源产品与之竞争。在中间层,EJB 3.0采用J2SE5.0的annotations实现了依赖注入(dependency injection)的功能,但这个功能只是Spring的一个子集。在这两个领域,开源产品都有了革新:JSP借鉴了ASP .NET,而Tapestry则采用了WebObjects的思想。于此同时,AOP的重要性在J2EE社区猛增,在使用上。AOP也越来越受到开发者的青睐。像Spring框架的实现提升了AOP的知名度,而纯粹的AOP技术比如AspectJ,在将来的几年也会流行起来。下一代的J2EE规范将拥抱更简单的POJO编程模型,就像Spring和Hibernate做的一样。J2EE开发者也注定要将焦点转到以自己的编程经验开发上来。1.3 课题的主要研究工作本次设计的目标是实现一个基于现在主流框架思想的快速开发J2EE项目的的软件框架或者是软件平台TeaFramework,这个框架抽取了大多数的J2EE项目中设计(非业务逻辑)中的同类问题、性能问题和安全问题等解决方案,主要工作主要集中在:(1)技术分析。分析J2EE技术和框架技术的发展,特别是J2EE轻量级框架技术的特点和发展,阐述了J2EE技术设计的多层结构,及各对应层的功能和特点。(2)TeaFramework体系结构设计。先从整体上提出TeaFramework的架构,将其层次划分为三层结构,即表示层、业务层和数据持久层结构,设计对应每层的主要框架组件,包括MVC组件,IoC组件,O/R映射组件等。(3)框架实践。在论文的最后通过一个资产管理系统来实践TeaFramework框架,并充分说明利用该框架开发实际应用软件的作用及意义。(4)设计总结。综合论述从总体上对所做的工作进行了总结并指出了论文存在的不足以及后继工作的展望。(5)特别说明。本文的多层次的框架的设计思想和设计理念都来源于现在主流的开源框架,前期对许多优秀开源框架做了深入的研究。TeaMVC的实现理念来源于Struts2框架,TeaIoC的实现理念来源于Spring框架,TeaORM的实现的理念来源于iBatis框架,只是实现了部分核心功能,目的是整合它们的核心功能,作为开发工具,能快速及有针对性地开发软件,并且能在以后定制项目需要的功能。论文的新颖之处在于:(1)本论文的框架整合了一些流行的开源框架的设计思想,各个层次间不需要额外的插件,能作为一个简易的开发平台,快速地开发软件。(2)本论文一大亮点是对每个层次组件之间的整合实现,就是通过互相整合,最后把单一的组件框架的实现整合成一个完整的能够工作的整合框架。(3)框架技术的发展大多是目前比较新兴的技术,论文本身研究的框架内容如:MVC,IoC和O/R映射都是比较新颖的J2EE技术,是下一代J2EE技术的基础。(4)TeaFramework框架的设计从整体上设计了J2EE分层结构的各层(WEB层、业务层以及数据持久层)的解决方案。(5)本论文不仅是对新兴的框架技术作研究,重点是都实现了这些J2EE技术如:MVC和IoC技术,为实现软件工厂目标积累了技术基础。(6)论文的最后通过一个资产管理系统来实践了本论文研究的轻量级的框架,可以充分说明TeaFramework框架对应用系统开发的作用及意义。2 相关技术分析2.1 J2EE技术J2EE(Java2 platform Enterprise Edition)是SUN 公司提出的在分布式环境中的一种体系结构,它提供了一种基于组件的设计、开发、集成、部署企业应用系统的方法。J2EE平台提供了多层分布式的应用系统模型,重用组件的能力、统一的安全模型和灵活的事务控制。基于组件的J2EE企业应用系统具有平台独立性,所以不受任何软件产品和任何软件厂家API的约束。2.1.1 J2EE的概念J2EE是一种利用Java 2平台来简化企业解决方案的开发、部署和管理相关的复杂问题的体系结构。J2EE技术的基础就是核心Java平台或Java 2平台的标准版,J2EE不仅巩固了标准版中的许多优点,例如“编写一次,随处运行”的特性、方便存取数据库的JDBC API、CORBA技术以及能够在Internet应用中保护数据的安全模式等等,同时还提供了对 EJB(Enterprise JavaBeans)、Java Servlets API、JSP(Java Server Pages)以及XML技术的全面支持,其最终目的就是成为一个能够使企业开发者大幅缩短投放市场时间的体系结构。J2EE体系结构提供中间层集成框架用来满足无需太多费用而又需要高可用性、高可靠性以及可扩展性的应用的需求。通过提供统一的开发平台,J2EE降低了开发多层应用的费用和复杂性,同时提供对现有应用程序集成强有力支持,完全支Enterprise JavaBeans,有良好的向导支持打包和部署应用,添加目录支持,增强了安全机制提高了性能。2.1.2 J2EE的体系结构J2EE使用多层的分布式应用模型,应用逻辑按功能划分为组件,各个应用组件根据它们所在的层分布在不同的机器上。事实上,Sun设计J2EE的初衷正是为了解决两层模式(Client/Server)的弊端,在传统模式中,客户端担当了过多角色而显得臃肿,在这种模式中,第一次部署的时候比较容易,但难于升级或改进,可伸展性也不理想,而且经常基于某种专有的协议――通常是某种数据库协议。它使得重用业务逻辑和界面逻辑非常困难。现在J2EE 的多层企业级应用模型将两层化模型中的不同层面切分成许多层。一个多层化应用能够为不同的每种服务提供一个独立的层,以下是 J2EE 典型的四层结构:客户层、WEB层、业务层及企业信息系统层(EIS)。J2EE体系结构图如图2.1所示。

图2.1  J2EE体系结构图从图2.1中可以看出,一个基于J2EE的企业应用从结构上来看,可以分成四部分:分别是客户层、WEB层、业务层和企业信息层。下面是对它们的描述:(1)客户层(Client Tier)J2EE 应用可以是基于Web的,也可以是不基于Web的。在一个基于Web 的J2EE 应用中,用户的浏览器在客户层中运行,并从一个Web服务器上下载Web 层中的静态HTML页面或由JSP或SERVLETS生成的动态HTML页面。(2)Web层(Web Tier)J2EE的Web组件可以由Jsp页面、基于Web 的Applets 以及显示Html页面的Servlets组成。调用Servlets或者Jsp页面的Html页面在应用程序组装时与Web 组件打包在一起,就像客户层一样,Web层可能包括一个JavaBeans类来管理用户输入并将输入发送到在业务层中运行的EJB类来处理。(3)业务层(Busniss Tier)业务层经常被称作EJB层。作为解决或满足某个特定业务领域(比如银行、零售等)的需要的逻辑的业务代码由运行在业务层的EJB来执行。一个EJB从客户程序处接收数据,对数据进行处理,再将数据发送到企业信息系统层存储。一个EJB 还从存储中检索数据,并将数据送回客户程序。运行在业务层的EJB依赖容器来为诸如事务、生命期、状态管理、多线程及资源存储池提供通常都非常复杂的系统级代码。(4)企业信息系统层(Enterprise Information System Tier)企业信息系统层运行企业信息系统软件,这层包括企业基础设施系统,例如企业资源计划(ERP),大型机事务处理、数据库系统及其他信息系统。J2EE 应用组件因为某种原因(例如访问数据库)可能需要访问企业信息系统。J2EE平台的未来版本将支持Connector架构,该架构是将J2EE平台连接到企业信息系统上的一个标准API。2.2 框架技术伴随着软件开发技术的发展在多层的软件开发项目中可重用易扩展而且是经过良好测试的软件组件越来越为人们所青睐这意味着人们可以将充裕的时间用来分析构建业务逻辑的应用,而非繁杂的代码工程。于是人们将相同类型问题的解决途径进行抽象抽取成一个应用框架,也就是说框架是一些经过实践证明的、能用来开发高效应用系统的技术。J2EE项目就可以通过框架的设计和运用来达到控制软件质量提高软件的生产效率。2.2.1 框架出现的原因框架,在大型应用中还是相当有好处的。在多人参与,长时间的开发,业务逻辑复杂的情况下,使用框架可以很好的进行业务分层,代码分层,可以实现同步开发,提高开发效率,缩短开发周期。现有的应用程序大致上有两类性质不同的组件组成:一类与程序要处理的具体事务密切相关,把它们叫做业务组件:另一类是应用服务。不同的应用程序的业务组件肯定是不同的,但是有一些通用的服务只与程序相关而与业务无关。 可以把这些不同应用程序里面的通用部分抽取出来,做成一个程序框架,具体的业务逻辑就可以在这个框架上实现。许多大型软件企业由于长时间的项目积累,把一些优秀的,被实践证明是高效的模块提取出来,构建自己的框架。这样可以大大节约开发成本,提高开发效率。2.2.2 框架的定义框架实现了更高级别的软件复用形式---设计复用,那么,具体来说框架是什么和由什么成分组成,还缺乏普遍认可的定义。关于框架有多种不同但相似的定义:(1)框架是能被程序员在特定的计算机问题中使用、扩展或者定制的一组普遍的,预先定制好的软件构建模块的集合。使用框架,开发者在开发应用项目时无需每次从头开始。(2)框架是从对象集合构建的,框架的设计和代码都可以被重用。(3)框架是构成一类特定软件可复用设计的一组相互协作的类;是一组具体表达了抽象设计的类用于解决一族相关的问题;是一个能被开发者插入自己的代码,并能提供大部分的通用功能的应用架构。归纳上述定义,框架是整个或部分系统的可重用的设计,表现为一组抽象的构件及构件实例间交互的方法:或认为,框架是可被应用开发者定制的应用骨架,前者是从应用方面而后者从目的方面给出的定义。具体来说,一个框架由一组协作类组成,阐明了整个设计、类间依赖及成员类的责任分步。这些类通常是抽象类,实现细节放在具体子类中,构成一个抽象设计,不同的子类构成对设计的不同实现。特定领域应用系统共有的设计因素由框架预先定义,应用开发人员只须关注与特定应用系统的特有部分,框架刻画了其应用领域所共有的设计决策,所以说框架着重于设计复用。 2.2.3 框架的特点框架是一个可复用的软件成分,它可以看作应用系统实现的一个半成品,它为一类相似应用系统提供了共有结构的设计与实现,因此框架可以针对特定的应用系统渐进行扩展。具体地说,框架有以下主要特点:(1)抽象性。框架抽象了特定领域中一组相似地应用系统(或子系统)设计与实现地共有部分,并给出了这些部分地设计和实现。(2)可扩展。性由于框架抽象了一类相似应用的共同部分开发者可以扩展这些共同部分(如抽象类)实现特定的应用。(3)迭代性。框架开发过程本身具有迭代性。在使用框架的过程中,框架支持领域的共性被不断发掘,因此框架需要反复的开发来完善其功能。(4)结构性。框架定义了特定领域中应用系统公共结构的设计和实现。开发者在这个共同的结构上扩展其功能实现具体的应用,因此一个好的框架具有明确清晰的层次结构,以便于复用和扩展。(5)可复用性。可复用性是框架的最根本的特点,也是框架具有上述特点的最终目的,框架是一种大粒度的可复用资源,是从设计到实现层次的复用,但它也必然地包括小粒度的复用---类的复用(如对抽象类的继承)。2.3 轻量级框架的发展目前轻量级框架发展非常的繁荣,同一产品类别没有一到两周都有新的框架推出,比较成功的应用框架有这几个方面:在Web层面主要是采的MVC体系结构的Struts2框架,在业务层非常成功的主要有Spring和Hibernate框架,这两个框架联合起来的作用相当于在J2EE体系结构中的EJB组件发挥的作用。下面简单对Struts2、Spring和Hibernate做一介绍:Struts2是采用Java Servlet/JSP技术,开发Web应用程序的开放源码的框架,采用Struts2能开发出基于MVC(Model-View-Controller)设计模式的应用构架,Struts2有一组相互协作的类、Servlet以及JspTaglib组成。Struts2有如下的主要功能:(1)包含一个controller servlet,能将用户的请求发送到相应的Action对象。(2)JSP自由tag库,并且在controller servlet中提供关联支持,帮助开发员创建交互式表单应用。(3)提供了一系列实用对象:XML处理通过Java Reflection APIs自动处理JavaBeans属性、国际化的提示和消息。Spring是一个以反向控制(IoC,Inversion of Control)原则为基础的轻量级框架。IoC也可以叫依赖注入(DI,Dependency Injection),即组件之间的依赖关系由容器在运行期决定,由容器动态将某种依赖关系注入到组件,这是Spring核心,其设计目标是提供一种无侵入式的高扩展性构架。即无需代码中涉及Spring专有的类,即可将其纳入Spring容器进行管理。作为对比,EJB则是一种高度侵入性的框架规范,它制定了众多的接口和编码规范,要求实现者必须遵从。侵入性的后果就是,一旦系统基于侵入性框架设计开发,那么之后任何脱离这个框架的企图都将付出极大的代价。为了避免这种情况,实现无侵入性的目标,Spring大量引入了Java的Reflection机制,通过动态调用的方式避免硬编码方式的约束,并在此基础上建立了其核心组件BeanFactory,以此作为其依赖注入机制的实现基础。Spring已成为企业应用的一站式(即在一个服务点可以完成所有服务)选择;同时Spring也是组件化的,允许使用它的部分组件而不需牵涉其他部分。可以使用 bean容器,在前台展现层使用Struts,还可以只使用Hibernate集成部分或是JDBC抽象层。Spring是无侵入性的,根据实际使用的范围,应用对框架的依赖几乎没有或是绝对最小化的。Spring把程序中所涉及到包含业务逻辑和Dao的Objects,例如事务管理控制(transaction management handler)、对象工厂(Object Factories)、服务组件(service objects),都通过XML来配置联系起来。iBatis是O/R Mapping框架的新贵,先已成为Apache开源组织的子项目。iBatis框架以POJO与SQL语句之间的映射来完成持久层的工作。iBatis框架不像Hibernate自动生成SQL语句来运行,具体的SQL需要程序员编写,然后通过映射配置文件,将SQL所需要的参数,以及返回的结果字段映射到指定的POJO。iBatis以SQL开发的工作量和数据库移植上的让步,为系统设计提供了更大的自由空间。轻量级框架体系结构设计3.1 框架的设计分析在J2EE的应用开发中,软件的可维护性和可扩展性是软件设计者着重考虑的目标,它同时也是衡量J2EE应用系统设计的重要标准,要实现这个目标,就要降低软件之间的耦合性,实现软件功能之间的解耦。常用的结构设计是采用分层结构,分层是软件设计中使用最多的方法,也是J2EE具有特色的一面。合理地使用分层技术可以将层次间的依赖性减到最少,还有利于标准化工作。本文设计的轻量级框架(命名为TeaFramework框架)的设计同样也是采用分层的结构,先看一下J2EE典型的三层结构,然后设计我们自已的三层结构,以及对于每一层的框架组件。J2EE典型的三层结构如图3.1所示。

图3.1  J2EE经典的三层结构 上图是典型的J2EE三层应用框架,在表示层主要任务是负责页面的显示和Web页面的流程控制,在中间层主要是实现业务逻辑部分,这部分对应的是由EJB技术来提供支撑的。最后是数据的持久化实现,主要通过DAO或者JDBC等来实现数据的持久化,一般把数据持久化在数据库中。3.2框架的总体设计针对J2EE的三个层次:表现层,业务层,持久层的划分,具体到本文设计的轻量级框架(TeaFramework框架)总体的设计,同样是实现这三层结构,但与上面有所不同,以下是本文TeaFramework框架的各个模块的层次结构。TeaFramework整体结构图如图3.2所示。

       图3.2 TeaFramework整体结构图    在TeaFramework框架的分层结构图中给出了各个层次对应的框架组件,下面分别介绍这些组件的作用:(1)表现层。在TeaFramework框架中的MVC组件,主要是实现页面的表示和MVC控制器的实现。页面表示,只要是JSP页面,这部分主要负责数据和信息的显示,较少地涉及到业务逻辑;控制器只要是控制Web的请求处理流程。本文把表现层框架,命名为TeaMVC框架,主要的配置文件为tea-action.xml。(2)业务层。主要是基于IoC模式的容器部分,提供基础组件和业务组件的依赖注入,从而真正的实现帮助组件之间的解耦工作。本文把业务层框架,命名为TeaIoC框架,主要的配置文件为tea.context.xml。(3)数据持久层。主要是实现对象到关系数据库的映射关系,从而实现对象到关系数据库的持久映射。本文把持久层框架,命名为TeaORM框架,主要的配置文件为tea.orm.xml。4 轻量级表现层框架设计与实现4.1 WEB应用系统传统开发模式分析基于J2EE平台的应用系统的传统开发方式主要是使用JSP开发的两种方式,即Model1(JSP+JavaBean模型)和Model2(JSP+Servlet+JavaBeans模型),这两种模型的主要差别在于它们处理业务的流程不同。图4.1所示的Model1中,JSP页面响应客户请求并将处理结果返回给客户,所有的数据通过JavaBean来处理,JSP实现页面的表现。

图4.1 JSP Model1Model1部分实现了页面表现和业务逻辑相分离。然而这种方式就要在JSP页面使用大量的Java代码,当需要处理的业务逻辑很复杂时,这种情况会变得非常糟糕。大量嵌入式代码使整个页面程序变得异常复杂对于前端界面设计的网页开发人员来说,这将造成极大的开发难度。所以,Model1不能满足大型应用的需求。图4.2所示的Model2结合了JSP和Servlet技术,充分利用了JSP和Servlet两种技术原有的优势。这个模型使用JSP技术来表现页面,使用Servlet完成大量的业务处理,使用JavaBean来存储数据。Servlet用来处理请求,充当控制器的角色,并负责向客户发送请求。它创建JSP需要的JavaBean对象,然后根据用户请求的行为,决定将具体的JSP页面发送给客户。

图4.2 JSP Model2

从开发的观点看,Model2具有更清晰的页面表现和清楚的开发角色划分,而且基本实现了MVC设计模式,分离了模型。控制和视图,因此,Model2可以符合大多数WEB应用对体系结构方面的要求。从WEB应用的共性分析,WEB应用系统不仅具有类似MVC、模型二的体系结构方面的共性,还有一些共性的操作,如请求转发、调用业务逻辑和选择组件视图等。即WEB应用系统需要一个除J2EE平台提供的服务以外的基础服务,而Model1或Model2只是一种概念模型,并没有提供该基础服务;同时,大型WEB应用在项目进度、项目投入等方面的要求也迫使开发者不能完全从零开发,即大型应用需要在既具有优良体系结构又提供一些共性的基础服务的支撑上开发,因此,Model2仍然不能完全满足大型WEB应用系统的需要。4.2 表现层MVC框架的设计为了解决之前讨论的WEB开发模型的缺陷,本文根据Struts2的思想和设计理念提出了通过轻量级的TeaMVC通用表现层框架来解决大多数通用的操作,进而提升开发效率及体系结构的完整性。TeaMVC框架正是在WEB应用层与J2EE平台之间需要插入一个新的框架支撑层的需求下产生的。TeaMVC应用框架在基于J2EE的WEB应用系统中位于J2EE平台上,提供WEB应用系统共同的操作,例如请求转发,调用应用逻辑和选择组件视图。TeaMVC框架为WEB应用系统提供了一些框架和接口,应用开发者只需继承或实现这些类和接口,就可以完成具体应用的功能。TeaMVC框架的实现利用了Model2架构及MVC模式思想,同时也部分借鉴了层次模式的思想。其体系结构如图4.3所示。TeaMVC框架有一下特征:(1)TeaMVC框架提供了应用功能可编程的配置模块。TeaMVC框架利用tea-action.xml配置文件,配置了系统的所有请求处理流程及对应的视图。将系统的请求处理流程配置在文件中,可以有效降低控制、模型、视图三者间的耦合,同时可以通过简单配置修改完成系统功能的增加、删除,从而提高了系统的可扩展性。(2)对于所有客户端请求,TeaMVC框架利用一个核心Filter作为框架的主控制器,如图所示TeaFrameworkFilter。当主控制器接收到一个请求时,它将该请求委托给处理器Dispatcher处理,最终在ActionInvocation中去完成具体的控制器的调用。(3)TeaMVC框架整体是设计成线程安全的,对于每个客户端请求,TeaMVC都会创建唯一一个控制器Action对象及执行调用器ActionInvocation,还有就是Action的上下文环境ActionContext,这从根本上隔离了TeaMVC对于不同请求的处理。(4)TeaMVC框架的核心是引入了Struts2中的拦截器模式,框架所有的功能都可以通过拦截器进行扩展的,开发人员也可以开发自己的拦截器进行业务逻辑的处理,这就给框架带来很好的可扩展性。

图4.3 TeaMVC体系结构图4.3 TeaMVC框架的实现TeaMVC有两条运行主线,即初始化主线和Http请求处理主线。这两条运行主线无论从运行空间、运行特点以及所承担的职责上来看都完全不同。从运行空间上讲,两者没有任何交集,运行的触发条件也完全不同;从运行特点上讲,初始化主线在系统启动时运行一次,Http请求处理主线则在系统启动完毕后以监听请求的方式运行;从所承担的职责上讲,初始化主线是为整个TeaMVC建立起运行所必备的运行环境,而Http请求处理主线则是TeaMVC的核心功能。4.3.1 框架的初始化TeaMVC的两大主线的入口程序是相同的,都是TeaFrameworkFilter,只不过是不同的方法的驱动了两条不同的运行主线。本框架也正是基于Filter所实现的Servlet规范中的不同的方法的生命周期的不同,规划了TeaMVC不同的运行主线。其中.init方法就是TeaMVC初始化主线的入口方法,其中相关代码如下:public class TeaFrameworkFilter implements Filter {private PrepareOperations prepare;private ExecuteOperations execute;public void init(FilterConfig filtereConfig) throws ServletException {InitOperations init = new InitOperations();String actionConfig = filtereConfig.getInitParameter("actionConfig");     /** 读取并解析action.xml,把信息存入ConfigurationManager中  */     Dispatcher dispatcher = init.initDispatcher(actionConfig);     this.prepare = new PrepareOperations(filtereConfig.getServletContext(),dispatcher);     this.execute=new ExecuteOperations(filtereConfig.getServletContext(),dispatcher);    }       ……..// 这里省略了许多其他代码}TeaMVC的初始化工作主要针对三个元素(Dispatcher、PrepareOperations、ExecuteOperations)展开的,而这三个元素也成为之后Http请求处理主线的执行依据。而入口程序TeaFrameworkFilter的init方法的核心,是通过init.initDispatcher方法调用返回核心分发器Dispatcher的对象。该方法的相关代码如下:public Dispatcher initDispatcher(String actionConfig) {     Dispatcher dispatcher = new Dispatcher();dispatcher.initDispatcher(actionConfig);return dispatcher;}public void initDispatcher(String actionConfig){this.configurationManager = new ConfigurationManager();/** 开始载入与解析TeaMVC的核心配置文件 */this.configurationManager.configInit(actionConfig);}TeaMVC初始化主要是对核心配置文件tea-action.xml的加载和解析,这也是进行请求处理的依据。本框架是通过XML开源解析工具Dom4j来解析XML配置文件的,先配置如下配置文件:<?xml version="1.0" encoding="UTF-8"?>  <tea-web><!--系统开发常量配置,Action是否由IoC容器来控制--><config-const name="ioc" value="true"/>    <!-- 配置可编程控制器模块 --><controller name="admin" namespace="/admin" >    <interceptors>     <interceptor name="my" class="test.interceptor.MyInterceptor"/>     <interceptor-stack name="checkLogin">        <interceptor-ref name="my"/>       <interceptor-ref name="defaultStack"/>     </interceptor-stack>        </interceptors>        <!-- 配置默认拦截器栈 -->        <default-interceptor-ref name="checkLogin"/>        <!-- 配置全局的result  -->    <global-results>    <result name="error" url="/error.jsp"/>    </global-results>           <action name="login" class="userAction" method="login"><result name="success" url="/success.jsp"/>    <!-- 引用自定义的拦截器 -->    <!-- <interceptor-ref name="checkLogin"/> -->    </action></controller></tea-web>由以上配置文件可以清晰地看到TeaMVC的配置文件的主体配置。配置可编程的模块controller,即一定意义上的package;配置拦截器模块interceptor和控制器模块action;配置结果映射模块result,映射到TeaMVC中的类名分别为ControllerMapping,ActionMapping,InterceptorMapping,ResultMapping这几个映射类。这些配置信息类最终会被全部存储到ConfigurationManager中,供框架处理请求时获取对应的配置信息。为了更形象的说明TeaMVC的初始化过程,通过如图4.4的顺序图来说明。

图4.4  TeaMVC初始化顺序图4.3.2 入口过滤器的实现之前已经介绍过TeaMVC的入口程序被设计成一个TeaFrameworkFilter的过滤器,TeaFrameworkFilter的初始化init方法前面已经重点介绍了,这部分所关注的是Filter中真正处理Http请求的doFilter方法,TeaFrameworkFilter的完整源码如下:public class TeaFrameworkFilter implements Filter {private PrepareOperations prepare;   private ExecuteOperations execute;/** 执行初始化工作 */public void init(FilterConfig filtereConfig) throws ServletException {InitOperations init = new InitOperations();       /** 从web.xml中得到MVC核心配置文件 */String actionConfig = filtereConfig.getInitParameter("actionConfig");/** 读取并解析action.xml,把信息存入ConfigurationManager中 */Dispatcher dispatcher = init.initDispatcher(actionConfig);this.prepare=newPrepareOperations(filtereConfig.getServletContext(),dispatcher);this.execute=newExecuteOperations(filtereConfig.getServletContext(),dispatcher);}   /** 所有请求的入口 */public void doFilter(ServletRequest req, ServletResponse res,         FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest)req;HttpServletResponse response = (HttpServletResponse)res;try { /** 创建ActionContext上下文环境  */   this.prepare.createActionContext(request, response);   /** 根据Http请求查找所对应的ActionMapping */ActionMappingactionMapping=this.prepare.findActionMapping(request);    if (actionMapping == null) {chain.doFilter(request, response);} else if (actionMapping.getName() == null) {Forward.forWard(request, response, Forward.ERROR_PAGE_404);} else {/** 处理请求 */this.execute.executeAction(request, response, actionMapping);}} finally {/**清理此请求的环境 */prepare.cleanupRequest(request);}}}doFilter方法是整个TeaMVC的第二条主线入口。在这个方法中,许多在第一条主线中被初始化的对象开始发挥作用,主要包括:(1)Dispatcher ---核心分发器,执行TeaMVC处理逻辑的实际场所。(2)PrepareOperations ---Http预处理类,对所有的Http请求进行预处理。(3)ExecuteOperations ---Http处理执行类,执行Http请求的场所。在进行URL处理时,doFilter方法会根据职责的不同,有条不紊地将URL处理过程分配给PrepareOperations和ExecuteOperations来分别完成。不过在这里PrepareOperations和ExecuteOperations实际上只是被设计成一层薄薄的代理层,实际在其中起决定性作用的还是核心分发器Dispatcher。为了解耦及职责的清晰性,本框架把请求处理的两个阶段分别设计了以上的PrepareOperations和ExecuteOperations两个类,前一个主要负责TeaMVC的初始化工作和每个Http请求的预处理工作,而后一个则负责Http请求的实际逻辑处理。4.3.3 数据流体系的实现TeaMVC的数据流体系的载体被设计成ActionContext类(Action执行的环境),主要有两个职责:数据存储和数据共享。具体的实现如下:public class ActionContext { /**本地线程变量 */staticThreadLocal<ActionContext>actionContext=new ThreadLocal<ActionContext>();public static final String REQUEST = "com.tea.web.ActionContext.requestMap";public static final String SESSION = "com.tea.web.ActionContext.sessionMap";publicstaticfinalString APPLICATION = "com.tea.web.ActionContext.applicationMap";Map<String, Object> context;…… // 这里省略了许多其他代码}ActionContext的一大职责是数据存储,从以上可以看到ActionContext内部有一个Map类型的context,这个Map就是ActionContext 真正的数据存储空间,ActionContext将所有的数据对象以键值对存储于context之中。而与此同时,ActionContext也提供了一些方便访问Web元素的接口方法,在TeaMVC中又设置了一层对Web元素的代理,用Map类型的RequestMap来封装HttpServletRequest,用Map类型的SessionMap来封装HttpSession,用Map类型的ApplicationMap来封装ServletContext。之所以这样处理,是为了减少用户程序与Web元素之间的耦合性,用业务层的角度去处理数据,从而减少与Web元素的耦合。此外,作为数据环境,ActionContext在设计上也考虑到了与Web容器对象进行交互的可能性。因此,为了提供一个完整的与Web容器打交道的方案,TeaMVC在ActionContext的基础上扩展了一个ServletActionContext的子类,并在其中封装了与原生的Web元素打交道的接口方法,所以我们可以在TeaMVC应用中,通过ServletActionContext在任何编程层次被随时调用,通过ServletActionContext所暴露的接口来完成对原生Web元素HttpServletRequest、HttpServletResponse的访问。ActionContext的另一大职责是数据共享,在设计TeaMVC考虑到Web请求的特点,对于不同的Web请求,应该对于每个Http请求都有一个唯一的数据环境,来为控制层程序的请求处理准备上下文数据,如果只设计一个全局的数据接口,则会出现线程安全,即对于所有请求都共享同一个数据环境,这样请求之间就会互相产生影响。为了解决线程安全问题,TeaMVC引入了ThreadLocal线程局部变量来解决线程安全问题,从以上实现可以看出,ActionContext内部封装了一个静态的ThreadLocal对象,而这一静态的ThreadLocal对象所操作和存储的对象,又是ActionContext对象本身,从而保证对于每一个请求都唯一对应一个上下文环境ActionContext对象,确保了请求的线程安全性。4.3.4 控制流体系的实现    TeaMVC的控制流体系被设计成如图4.5所示。从图中可以看到构成控制流的元素主要有五个:ActionProxy,ActionInvocation,Interceptor,Action,Result。TeaMVC把一个请求处理的控制流细化成了若干个以上组件,TeaMVC的控制流体系由于引进了拦截器(Interceptor),即应用了典型的AOP(面向切面编程)的设计思想。

图4.5 TeaMVC控制流体系图下面结合上图详细介绍TeaMVC控制流体系中的各种组件。(1)ActionProxy---执行窗口ActionProxy是整合TeaMVC框架的执行入口,ActionProxy的存在相当于定义了一个事件处理流程的执行范围,主要作用就在于对外屏蔽整个控制流核心元素的执行过程,对内则为Action、Interceptor、Result等事件处理节点对象提供一个无干扰的执行环境。还有值的注意的是对于每个请求都只对应一个ActionProxy对象。(2)ActionInvocation---核心调度器     ActionInvocation是TeaMVC处理请求的核心调度器,也是TeaMVC实现的重点。ActionInvocation是组织起Action、Interceptor、Result等事件处理节点对象执行次序的核心调度器。ActionInvocation被封装在ActionProxy的内部,它的执行调度过程,实际上控制器整个TeaMVC的请求处理体系的执行命脉。ActionInvocation的核心调度功能是通过invoke方法完成的,具体的实现如下:@Overridepublic String invoke() throws Exception {if (this.current < this.interceptorSize) {InterceptorMapping  interceptorMapping =  interceptorList.get(this.current++);   /** 执行Interceptor的回调 */this.resultCode = interceptorMapping.getInterceptor().intercept(this);}else { /** 调用Action的方法,得到resultCode */invokeAction();}/** Result是否已经执行。第一次执行只执行一次,resultCode可能来自Action执行返回结果,也可能来自拦截器返回的结果 */if(!executed){ /** 根据resultCode创建相应的Result对象并执行 */executeResult();executed = true;}return this.resultCode;}以上ActionInvocation的invoke方法结合拦截器和递归调用,是TeaMVC实现AOP的具体体现。这个invoke方法应用了典型的拦截器模式,实现了拦截器和控制器执行顺序的解耦,深刻体现了TeaMVC框架的设计思想。该方法具体的意思如下:如果执行栈中还有拦截器,那么执行拦截器;否则,执行Action,紧接着执行Result。(3)Interceptor---拦截器      Interceptor本身是AOP的概念,表示对程序的某个逻辑执行点进行拦截,从而能够在这个逻辑执行点之前、之后或者环绕着这个逻辑执行点进行逻辑扩展。在本文的TeaMVC框架中,Interceptor的拦截对象是核心控制器Action对象,在Action的周围定义了一个环绕的逻辑扩展层次,其被设计的主要目的就在于能够在核心控制器Action的执行之前、之后进行自定义的逻辑行为扩展。下面通过一个具体的示例拦截器AroundInterceptor来说明TeaMVC对拦截器的设计,实现代码如下:Public class AroundInterceptor implement Interceptor {Public String intercept(ActionInvocation invocation){String result = null;     // 在整个执行栈执行完毕之前的逻辑扩展    before(invocation);// 驱动执行栈的进一步执行invocation.invoke();// 在整个执行栈执行完毕之后的逻辑扩展after(invocation,result);return result;}}在拦截器的实现中,又对ActionInvocation的invoke方法进行了递归调用。这样递归调用的嵌套执行使得以递归调用逻辑为中心的代码段(invocation.invoke的调用)成为逻辑上的分水岭,从而彻底改变了原本正常的执行逻辑。一个有序链表通过递归调用变成了一个堆栈执行过程,将一段有序执行代码变成了两段执行顺序完全相反的代码过程,从而巧妙实现了AOP。(4)Action---核心控制器     Action是TeaMVC中所定义的一个核心控制器接口,本框架的设计的初衷就是为了设计一套轻量级的Web框架,而可以让程序员远离Servlet方面编程,Action作为代替Servlet作为上层控制器,完全能胜任作为控制器的需求。并且,在TeaMVC中一大设计亮点,就是通过内置拦截器(ParametersInterceptor)把请求参数解析出来通过第三方表达式引擎OGNL把请求参数设置为Action的属性,这样,Action的属性就成为了每次请求的数据来源,方便了程序员集中精力去处理具体的业务逻辑,这也体现了TeaMVC抽取通用程序的框架思想。(5)Result—执行结果     Result是TeaMVC中定义用来对核心控制器类Action执行完毕之后的响应处理动作。在TeaMVC中之所以独立定义一层Result,设计的组要目的在于这样使得核心控制器能够更加关注核心业务流程的处理,而将程序的跳转控制逻辑交给Result来完成,具体的实现类有ServletDispatcherResult和ServletRedirectResult。4.3.5 一次完整请求处理的实现下面根据TeaMVC框架对客户端请求的一个完成处理过程来说明TeaMVC处理一个完整请求的实现机制。具体的实现如图4.6及4.7所示。一个请求在TeaMVC框架中的处理大概分为以下几个步骤:(1)客户端初始化一个指向Servlet容器(例如Tomcat)的请求。(2)这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做CharactorEncodingFilter的可选过滤器)。(3)接着核心过滤器TeaFrameworkwork被调用,TeaFrameworkwork去配置文件读取配置信息来决定这个请是否需要调用某个Action。(4)如果决定需要调用某个Action,TeaFrameworkwork会先得到Action对应的映射ActionMapping,再把请求的处理交给Dispatcher。

图4.6 TeaMVC一次完整的请求处理(一)

图4.7 TeaMVC一次完整的请求处理(二)

(5)Dispacther被设计成一个转发类,其中的serviceAction是执行请求的入口,会通过ActionProxyFactory创建一个ActionProxy和ActionInvocation对象。然后把控制权交给ActionProxy代理类。(6)ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。(7)一旦Action执行完毕,ActionInvocation负责根据tea-action.xml中的配置找到对应的返回结果。返回结果通常是一个需要被表示的JSP。(8)最终通过Result组件请求转发到对应的视图进行对此次请求的响应。4.4 MVC框架所涉及的设计模式4.4.1 ThreadLocal模式ThreadLocal模式严格意义上来说并不能说是一种设计模式,因为它只是用来解决多线程程序中数据共享问题的一个方案,正因为如此,TeaMVC框架也引进了这种解决方案,通过ThreadLocal来保证每个请求(线程)拥有线程私有的Action的上下文环境ActionContext对象。ThreadLocal执行示意图如图4.8所示。

图4.8  Thread执行示意图由于ThreadLocal所操作的是维持与整个Thread生命周期的副本(Thread LocalMap),所以无论在J2EE程序的哪个层次(表现层、业务层或持久层),只要在一个线程Thread的生命周期之内,存储于ThreadLocalMap中的对象都是线程安全的(因为ThreadLocalMap本身仅仅隶属于当前线程Thread,是线程Thread内部的一个数据成员)。而这一点,正是我们用来解决多线程环境中的数据共享问题的核心技术。ThreadLocal的这一特性也使其能够广泛应用于J2EE开发中的许多业务场景。4.4.2 拦截器模式Web层请求处理机制可以接收许多不同类型的请求,这些请求分别需要不同类型的处理。有些请求只是简单的转发给合适的处理器组件,而有的请求在进一步处理之前必须进行修改、转换等预处理。拦截器设计模式正是为了解决请求的预处理和响应的后处理的问题。其类图如图4.9所示。

图4.9 拦截器模式结构图FilterChainCreator负责过滤器的处理,在TeaMVC中对应于着ActionProxy。它会用合适的过滤器,按照正确的顺序来创建过滤器链,并且初始化处理过程。FilterChain是独立的过滤器的有序集合,在TeaMVC中对于着ActionInvocation。Filter是单独的过滤器,在TeaMVC中对于着拦截器Interceptor。FilterChain会协调这些过滤器的处理。Target是所请的目标资源,在TeaMVC中是Action对象。4.4.3 策略模式策略模式的意图是定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换,最终算法可以独立于使用它的客户而变化。策略模式的核心是对算法的封装,其最终目的是把使用算法的环境和算法的实现进行解耦。在这种情况下,策略模式中的算法的实现都是“可插拔式”的。其类图如图4.10所示。

图4.10 策略模式结构图

4.5 MVC框架配置及使用说明要应用TeaMVC框架,首先必须要在项目的web.xml中指定TeaMVC的入口程序,如下所示:<!-- 配置Tea MVC框架的入口Filter --><filter><filter-name>actionFilter</filter-name><filter-class>org.teaframework.web.filter.TeaFrameworkFilter</filter-class>     <init-param><param-name>actionConfig</param-name><param-value>tea-action.xml</param-value></init-param></filter><filter-mapping>   <filter-name>actionFilter</filter-name>   <url-pattern>/*</url-pattern></filter-mapping>配置完入口程序,就可以根据需求配置TeaMVC的核心配置文件tea-action.xml了,示例配置如下:<?xml version="1.0" encoding="UTF-8"?>  <tea-web><!-- 配置可编程控制器模块 --><controller name="admin" namespace="/admin" > <interceptors><interceptor name="my" class="test.interceptor.MyInterceptor"/><interceptor-stack name="checkLogin"><interceptor-ref name="my"/><interceptor-ref name="defaultStack"/></interceptor-stack></interceptors><!-- 配置默认拦截器栈 --><default-interceptor-ref name="checkLogin"/><!-- 配置全局的result  --><global-results>   <result name="error" url="/error.jsp"/></global-results><!-- 配置核心控制器Action --><action name="login" class="org.tea.UserAction" method="login"><result name="success" url="/success.jsp"/>   <!-- 可以引用自定义的拦截器 -->   <!-- <interceptor-ref name="checkLogin"/> --></action></controller></tea-web>   配置完核心配置文件就可以编写核心控制器Action了,如下:public class UserAction extends ActionSupport{ // 请求数据来源   private User user = new User();   public User getUser() {   return user;   }   public void setUser(User user) {   this.user = user;   }   public String login() {   if (user.getUserName().equals("Tea")) {      return SUCCESS;   } else {      return ERROR;   }    }}可以通过浏览器输入http://localhost:8080/admin/login.action?user.username=tea进行访问了。

5 轻量级业务层框架设计与实现5.1 IoC模式的分析5.1.1 IoC模式的产生在设计模式中,提倡一种思维编程方式:接口驱动。接口驱动有很多好处,可以提供不同灵活的子类实现,增加代码稳定和健壮性等。面向抽象、面向接口编程是面向对象的核心。

图5.1 面向接口编程示意图上面的组件A依赖组件B在接口编程中,分为两部分,一是接口IB部分,其提供功能的声明,二是接口的一个实现部分B1。下面都假设IB接口,B1是IB接口的一个实现。那么接口的实现形如:IB b = new B1();通过接口应用就如同空的模型套,在必要时,需要向模型套注射石膏,这样才能成为一个模型实体,因此,人为控制接口实现形成“注入”,这也是依赖注入(DI)概念的体现。上述IB接口实现语句表明当前是在调用被调用者A,由于被调用者B1的实现名称写入了调用者A的代码中,这产生了一个接口实现的问题:彼此联系,调用者和被调用者有紧密联系,在UML中是用依赖 Dependency 表示。这种紧密联系的依赖与关注点分离(Separation of Concerns,SoC)思维是相违背的。它们之间必需解耦,IoC模式由此产生,IoC框架就是将依赖先剥离,然后在适当时候由容器负责产生具体的实例再注入到调用者中。5.1.2 IoC模式与工厂模式IoC模式与工厂模式有一定的相似之处,即它们都可以剥离依赖关系。可以实现根据配置文件动态实现实例注入。上例中,A作为调用者,IB是被调用者,在A代码中存在对IB的调用,伪码如下:public class A{ private IB comp;... } 工厂方法:上例A类中增加如下伪代码如下:private final static MyFactory myFactory = MyFactory.getInstance();public A(){ this. comp = myFactory.createInstanceOfIB();} 用工厂方法,也可以通过定义xml配置文件定义IB的子类的实现,通过createInstanceOfB()生成B1的具体实例。IoC:运用IoC框架实现:上例则是在A中增加public void setComp(IB comp){ this.comp = comp; }在配置文件中添加相应的依赖:<bean id=“a” class=“A”><property name=“comp” ref=“comp”/></bean><bean id=“comp” class=”BI”/>客户端调增加用为:public class client{ public static void main( String[] args ) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory ();A a = (A) beanFactory.getBean(“a”);a.someMethod();}}可以看出,IoC框架的特点是A类如同通常POJO类,没有任何在A类代码中嵌入任何工厂模式等代码,但在配置文件中配置了依赖,实现了依赖可配置化。工厂模式与IoC框架的区别:IoC框架是工厂模式的的升华,使用IoC彻底解耦了A和BI之间的联系,使用IoC,在A类代码中将不需要嵌入任何工厂模式等的代码,而工厂模式其实还是与BI有些间接的联系,IoC框架只是将这种联系转移到配置文件进行依赖,由于IoC解放自由了调用者类,而且可以向调用者类实现注入所依赖的接口的各种实现方案。5.1.3 IoC模式产生的重要意义从上面的分析可以看出IoC组件(框架)的意思在于以下三个方面:(1)颠倒了“使用对象之前必须创建” 的基本Java语言定律因为有了IoC组件,对象的调用可以直接从IoC容器中取得,而不是在类中负责调用对象的生命周期。把类的创建和初始化工作统一交给IoC容器来处理,由IoC容器来统一负责类的创建处理,是IoC容器发展的重要驱动力,从这一思想的出发点也正是分离关注点(Separation of Concerns,SOC)的思想的体现。(2)组件间的依赖关系减少极大改善了代码的可重用性通过IoC容器可以在运行期为组件配置所需资源,而无需在编写组件代码时就加以指定,从而在相当程度上降低了组件之间的耦合。将依赖关系从编码中脱离出来,从而大大降低了组件之间的耦合,实现了组件真正意义上的即插即用。(3)真正的面向接口编程使面向接口编程更加优雅,更加自然通过接口编程实现了程序的动态绑定,从而降低了组件的依赖关系,IoC容器为面向接口编程提供了一个更加自然的平台。程序员会自然而然倾向于使用接口来定义不同层次之间的关联关系,这种自发的倾向性,来自于IoC组件所提供的简单舒适的依赖注入实现。这使得接口的定义和使用不再像传统编码过程中那么繁琐(传统编码过程中,引入一个接口往往也意味着同时要引入一个Factory类,也许还有一个额外的配置文件及其读写代码)。5.2 业务层IoC框架的设计5.2.1 IoC容器的总体设计TeaIoC容器主要包括以下几个部分:注册模块、解析模块、自动装配模块、命周期管理模块和缓存管理模块。其整体架构和其在整个系统中的地位如图5.2所示:图5.2 IoC容器的整体结构图(1)解析模块组件信息在容器中注册以后并不能直接使用。由于配置文件的XML设计通常是递归的,所以配置文件通常会比较复杂,这样在容器中所注册的信息也会比较复杂,并且通常配置的组件信息支持继承结构,这样就需要合并组件的配置信息。解析模块就是把这些复杂信息解析为能够直接为管理组件对象的生命周期所直接使用的参数。(2)注册模块容器根据配置文件的XML设计读取并解析配置文件,把读取的信息保存在适当的数据结构中,然后在容器中为每个组件注册信息为以后容器管理这些组件做准备。(3)自动装配模块IoC容器能自动装配组件与组件之间的依赖关系,也就是不需显示指定所依赖的组件,由IoC容器检查配置文件的内容,为主调组件注入依赖关系。自动装配可以具体指定到每个组件,可让某些组件使用自动装配而某些组件不使用,自动装配减少了配置文件的工作量。(4)生命周期管理模块组件对象实例处于容器中,可以通过不同的实例化策略实例化它,在创建后需要执行一些初始化动作,这就是对象的生命周期管理。IoC容器负责管理组件对象的生命周期,如对象的创建、初始化和销毁等。对象的生命周期就是从无到有再到消亡不断变化的过程,容器必须对这个过程进行管理。(5)缓存管理模块TeaIoC容器采用了缓存技术,以提高容器对组件管理的性能。把带有唯一标示的组件实例放置于缓存中,缓存的作用是保存一些组件实例,减少组件重新装载的时间。当程序需要访问某一个组件的实例时,容器先检查这个实例在不在缓存中,如果在缓存中,则缓存命中,返回实例,可以直接使用,如果不在的话,就需要将组件装载到容器中去,然后保存在缓存中。在IoC容器中,缓存用一个ConcurrentHashMap表示,组件以其在容器中的唯一ID作为键值存储到ConcurrentHashMap中。(6)配置文件容器需要提供统一的方式配置运行在其中的对象,配置方式应该简单适用,并且配置代码应该脱离程序代码,这样在改变配置时就不需要重新编译文件。配置文件是通XML文件表示的。5.2.2 IoC框架的关键技术(1)XML解析技术      XML在不同语言里解析方式都是一样的,用Java解析XML文档,只不过实现的语法不同而已,Java中最常用的有两种方法:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object Module)称为DOM。SAX是基于事件流的解析,DOM是基于XML文档树结构的解析。另外还有几种基于以上两种解析技术的开源解析包:Dom4j和Jdom解析开发包。IoC框架采用原生的DOM解析技术来解析XML文件的。(2)Java虚拟机类加载机制 Java中之所以可以在运行时动态地加载指定的类,是由于Java虚拟机的类加载器技术的实现,类被类加载器加载到虚拟机内存中,整个生命周期包括了:加载、验证、准备、解析、初始化、使用和卸载七个阶段。具体我们可以通过Class.forName()和classLoader.loadClass()来实现动态地加载类。(3)Java反射机制Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。Java提供的反射机制的类主要有java.lang.Class和java.lang.reflect包中的Constructor、Method等类。IoC框架通过反射注入的实现思路如下:实现在运行期根据需要动态的实现对象的注入功能,并且实现对注入对象的生命周期的管理等相关任务。其过程是首先容器分析组件间的依赖关系,然后利用Java的反射机制,要动态的生成调用者和被调用者类的实例,必须先得到该类的无参构造器Constructor然后通过Constructor.newInstance将产生相应的实例,取得实例后,将把被调用者的实例注入到调用者的实例中。set注入需要先生成调用者实例,然后取得调用者实例的相应的Setter方法的Method类型的writeMethod对象,然后调用writeMethod.invoke(obj,value)将新生成的被调用者实例注入进去。其中value中包括被调用者实例的参数对象。(4)JavaBean构件 Java.beans包提供了大量方便操作JavaBean的API,其基础结构仍为反射机制。在java.beans包中比较重要的有Introspector类,BeanInfo接口,PropertyDescriptor类和PropertyEditor接口等。(5)设计模式 如何同时提高一个软件系统的可维护性(Maintainability)和可复用性(Reusability)是面向对象的设计要解决的核心问题,通过学习和应用设计模式,可以更加深入地理解面向对象的设计理念,从而帮助设计师改善自己的系统。在IoC框架中用到的主要有开放封闭原则、模板方法模式、代理模式等。5.2.3 IoC框架在TeaFramework中的作用TeaFramework框架的设计中, IoC框架起到了两方面的作用:(1)对整体的TeaFramework框架起到一个基础组件支撑的作用。在TeaFramework框架中有很多的组件需要管理,要实现组件的可管理化和可配置化,实现整个系统的可扩展性和可伸缩性。需要这样的一个IoC基础框架来提供支撑。(2)实现对应用业务组件的上层支撑。就是用户的业务组件可以通过TeaFramework的IoC框架功能实现可管理可配置,实现对用户业务组件的解耦.这样大大的提高了用户的业务组件的可扩展性和灵活性。TeaIoC框架也是一个独立的框架,可以让用户单独使用,对用户的业务组件起到支撑作用。5.3 业务层IoC框架的实现IoC框架的实现可以分成两个步骤,第一个步骤是IoC容器的初始化,包括BeanDefinition的载入与解析、BeanDefinition在IoC容器中的注册,第二个步骤就是Bean的实例化及依赖注入。IoC容器的初始化和依赖注入是两个独立的过程,依赖注入是在应用第一次通过getBean向容器索取Bean的时候,这里有一个实现的细节,在使用IoC容器时有一个预实例化的配置,通过这个预实例化的配置(具体来说,可以通过为Bean定义信息中的lazy-init属性),开发者可以对容器初始化过程作一个微小的控制,从而改变这个被设置了lazy-init属性的Bean 的依赖注入过程。下面就通过IoC容器初始化及依赖注入的实现细节详细论述IoC框架的实现。5.3.1 BeanDefinition的载入与解析TeaIoC通过定义BeanDefinition来管理基于Tea框架的应用中的各种对象以及它们之间的相互依赖关系。BeanDefinition抽象了我们对Bean的定义,是让IoC容器起作用的主要数据结构。对于IoC容器来说,BeanDefinition就是依赖注入模式中管理的对象依赖关系的数据抽象,也是容器实现依赖注入功能的核心数据结构,控制反转功能都是围绕对这个BeanDefinition的处理来完成的。对于IoC容器来说,BeanDefinition的载入与解析过程,相当于把定义的BeanDefinition在IoC容器中转化成一个TeaIoC内部表示的数据结构的过程。TeaIoC容器对Bean的管理和依赖注入功能的实现都是通过对其持有的BeanDefinition进行各种相关操作来完成的。这些BeanDefinition数据在TeaIoC中的通过一个ConcurrentHashMap来保持和维护。BeanDefinition的载入与解析分成两部分,首先通过调用XML的解析器得到Document对象,但这些Document对象并没有按照TeaIoC的Bean规则进行解析。在完成通用的XML解析之后,才是按照TeaIoC的Bean规则进行解析的地方。具体的载入与解析的序列图如图5.3所示。XML定义的BeanDefinition就被整个载入到了IoC容器中,并在容器中建立起了数据映射,在IoC容器中的建立起了对应的数据结构,或者说可以看成是POJO对象在IoC容器的抽象,这些数据结构可以让IoC容器执行索引、查询、操作。现在,已经实现了BeanDefinition的载入与解析,但是这时候的容器还没有完全起作用,要完全起作用,好需要完成数据向IoC容器的注册。

图5.3 BeanDefinition的载入与解析的序列图5.3.2 BeanDefinition在IoC容器中的注册之前已经实现了BeanDefinition的载入与解析,在这些功能完成后,用户定义的BeanDefinition信息已经在IoC容器内建立起了自己的数据结构以及相应的数据表示,但此时这些数据还不能供IoC容器直接使用,需要在IoC容器中对这些BeanDefinition数据进行注册,这个注册的设计,为TeaIoC容器提供了更友好的使用方式,在核心容器类DefaultListableBeanFactory中,是通过一个ConcurrentHashMap来持有载入的BeanDefinition的,这个ConcurrentHashMap的定义如下:/** 通过Map来持有BeanDefinition */Private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();将解析得到的BeanDefinition向IoC容器中的beanDefinitionMap注册的过程是在载入与解析BeanDefinition完成后进行的,注册的调用过程如图5.4所示。

图5.4 BeanDefinition在IoC容器中注册的序列图在具体实现上,核心容器类DefaultListableBeanFactory实现了BeanDefinition Registry接口,这个接口完成BeanDefinition向IoC容器的注册。这个注册就是把BeanDefinition设置到beanDefinitionMap中去。完成了BeanDefinition的注册,就完成了IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,它们都再beanDefinitionMap里被检索和使用。容器的作用就是对这些信息进行处理和维护。这些信息是容器建立依赖注入的基础,有了这些基础数据,就可以实现IoC容器的依赖注入功能了。5.3.3 Bean的实例化及依赖注入的实现到现在为止,IoC容器已经载入了用户定义的Bean信息,就可以实现依赖注入的功能了。首先,依赖注入的过程是用户第一次向IoC容器索要Bean时触发的,当然也有例外,我们可以也可以在BeanDefinition信息中通过控制lazy-init属性来让容器完成对Bean的预实例化,这个预实例化也是一个依赖注入的过程,但它是在初始化的过程中完成的。当用户第一次向IoC容器索要B ean的入口在AbstractBean Factory,具体实现如下:/**  getBean方法触发依赖注入 */public Object getBean(String name) {return doGetBean(name,null);}protected <T> T doGetBean(final String beanName,final Class<T> requiredType){Object sharedInstance = getSingleton(beanName);Object bean = null;    if (sharedInstance != null){bean = sharedInstance;}else{ /**  根据Bean的名字取得BeanDefinition*/final BeanDefinition mbd = getBeanDefinition(beanName);     /** 这里创建Singleton类型的Bean对象 */    if (mbd.isSingleton()) {    sharedInstance = getSingleton(beanName, new ObjectFactory(){    public Object getObject(){return createBean(beanName, mbd);    }});bean = sharedInstance;}     /** 这里创建Prototype类型的Bean对象 */else if (mbd.isPrototype()) {    Object prototypeInstance = null;prototypeInstance = createBean(beanName, mbd);bean = prototypeInstance;}}return (T)bean;}这个就是依赖注入的入口,在这里触发了依赖注入,而依赖注入的发生是在容器中的BeanDefinition数据已经建立好的前提下进行的。重点来说,getBean是依赖注入的重点,之后会调用AbstractAutoCapableBeanFactory的createBean方法,createBean的实现不但生成了需要的Bean对象,还对Bean初始化进行了处理,比如实现了在BeanDefinition中的init-method属性定义等,依赖注入的一个大致过程如图5.5所示。

图5.5 IoC容器创建bean及依赖注入序列图接着我们分析具体的Bean的实例化及依赖注入的实现,具体的实现在Abstract AutowireCapableBeanFactory的doCreate方法中,代码如下所示:public Object doCreateBean(String beanName,BeanDefinition mbd) {BeanWrapper instanceWrapper = null;if (instanceWrapper == null) {  /** 创建bean对象 */   instanceWrapper = createBeanInstance(beanName, mbd);}FinalObjectbean=(instanceWrapper!=null ?instanceWrapper.getWrappedInstance() : null);Class<?>beanType=(instanceWrapper!=null?instanceWrapper.getWrappedClass() : null);Object exposedObject = bean;/** 依赖注入实现方法 */populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) { /** 初始化bean */   exposedObject = initializeBean(beanName, exposedObject, mbd);}return exposedObject;}以上实现可以看出,具体的Bean实例化是通过createBeanInstance方法创建的,而依赖注入的实现是通过populateBean实现的,最后完成Bean初始化操作。具体的依赖注入是在BeanWrapperImpl的setPropertyValues方法,实现如下:public void setPropertyValues(MutablePropertyValues pvs) {List<PropertyValue> propertyValues = pvs.getPropertyValueList();for (PropertyValue pv : propertyValues) {String propertyName = pv.getName();Object value = pv.getValue();try {    PropertyDescriptor pd = getPropertyDescriptor(propertyName); if (pd != null && pd.getWriteMethod() != null ) { /**得到set方法 */     Method writeMethod = pd.getWriteMethod();     writeMethod.setAccessible(true);     try { /** 通过Java反射机制实现依赖注入  */   writeMethod.invoke(this.object, value);    } catch (IllegalArgumentException e) {e.printStackTrace(); }  }} catch (IntrospectionException e) {e.printStackTrace();}  }}在Bean的创建和对象依赖注入的过程中,需要依据BeanDefinition中的信息来传递地完成依赖注入,从上面的实现来看,Bean的依赖注入都是递归调用的,体现在依赖注入时,通过递归调用容器的getBean方法,得到当前Bean所依赖的Bean,同时也是触发对依赖Bean的创建及注入,这样,根据依赖关系,一层一层地完成Bean的创建和注入,直到最后完成当前Bean的创建。5.3.4 TeaIoC整体核心类图     根据IoC容器所需要的功能,按照面向对象的设计原则,可设计IoC容器的整体核心类图如图5.6如下:

图5.6 TeaIoC容器的整体简略核心类图     BeanFactory是容器的核心,它以工厂模式负责bean的创建、管理和销毁,是bean管理的Facade类。容器中的bean通常会互相合作,因而它们之间会产生依赖关系,BeanFactory在配制文件当中来反映这些依赖关系。BeanFactory有多种实现方式,其中最常用的是用XML配置文件来反映bean之间依赖关系的。容器中不同的类都有特定的职责。比如AbstractAutowireCapableBeanFactory负责bean的创建和管理bean的生命周期;DefaultListableBeanFactory实现了BeanDefinitionRegistry接口能够在BeanFactory中注册bean定义信息,并且使IoC核心容器类。BeanDefinitionParserDelegate负责解析XML文件中bean元素的定义;XmlBeanDefinitionReader委托DefaultBeanDefinitionDocumentReader解析整个XML文件。Bean实例的产生有两种方式:一种是采用Prototype方式,每次调用都产生一个新的实例;另一种是Singleton方式建立单例缓存,如果缓存中不存在实例才创建新实例并将它放到缓存中。5.3.5 从BeanFactory得到 Bean的流程从BeanFactory得到Bean分两个阶段。第一个阶段是完成IoC容器的初始化,IoC容器的初始化包括BeanDefinition的载入与解析,BeanDefinition在IoC容器中的注册。第二个阶段是通过BeanDefinition信息,根据适当的实例化策略,生成Bean实例,再执行Bean的生命周期。具体生成Bean实例的流程如图5.7所示:

图5.7 IoC容器创建bean的完整流程图

5.4 IoC框架与MVC框架的整合实现TeaMVC的控制器Action可以由IoC容器来统一管理,要实现这样的整合功能,必须对于MVC框架提供可配置的接口,对于TeaMVC而言即可以选择自己来创建Action对象,也可以选择通过IoC容器来统一管理,TeaMVC的实现方案,是在MVC的配置文件tea-action.xml可以配置开发常量,对应的XML配置文件信息如下:<!--系统开发常量配置,可以定义零个或多个,Action是否由IoC容器来控制--><config-const name="IoC" value="true"/>由上配置信息可以看出,常量IoC为true时,即把Action对象委托给了IoC容器来管理。具体在DefaultActionInvocation中TeaMVC框架在选择创建Action的策略时的实现代码如下:/** 创建Action对象  */public void createAction(){ /** 是否直接从IoC容器取Action对象  */Booleanbool_Const=ConvertUtils.toBoolean(ConfigurationManager.getConst("IoC"));if (bool_Const) { /**直接从IoC容器得到Action对象 */ServletContext servletContext = ServletActionContext.getServletContext();XmlWebApplicationContextwac=(XmlWebApplicationContext)servletContext.getAttribute(XmlWebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);   this.action = wac.getBean(this.proxy.getClassName());}else{ /**直接由TeaMVC来创建Action对象 */    this.action = ClassInstance.newClass(this.proxy.getClassName());}}5.5 IoC框架配置及使用说明为了使用TeaIoC容器,必须配置核心配置文件tea-context.xml,示例配置如下:<?xml version="1.0" encoding="UTF-8"?><beans><bean id="userDao" class="test.dao.impl.UserDaoImpl"/><bean id="userService" class="test.service.impl.UserServiceImpl"><property name="userDao" ref=”userDao”></bean></beans> 对应的示例类如下:public class UserDaoImpl implement UserDao{    public void add(User user){   System.out.println(“添加一个用户”);}} public class UserServiceImpl implement UserService{private UserDao userDao;/** 通过TeaIoC依赖注入UserDao对象*/Public void setUserDao(UserDao userDao){   this.userDao = userDao;}public void save(User user){ this.userDao.add(user);}}通过FileSystemXmlApplicationContext来测试IoC容器,具体使用如下:public class Test {public static void main(String[] args) throws SQLException {    /** 读取TeaIoC的核心配置文件tea-context.xml配置文件,并且执行容器的初始化操作 */FileSystemXmlApplicationContext beanFactory = new FileSystemXmlApplicationContext("tea-context.xml");    /** 从IoC容器中得到已经注入好的userService对象 */UserService userService = beanFactory.getBean("userService");userService.save(new User());       }}

6 轻量级数据持久层框架设计与实现面向对象技术已经成为软件开发的一种趋势,而面向对象的持久性对于大部分的应用来说是使用数据库技术,在数据库技术中,关系型数据库技术又是其中最有影响的技术,当选用关系数据库作为持久机制时,就存在一个对象到关系数据库的映射问题,这是因为面向对象设计机制与关系模型的不同,造成了面向对象设计与关系数据库设计之间的阻抗不匹配。面向对象设计基于如耦合、聚合、封装等理论来表示对象之间的关系,而关系模型则基于数学原理,它主要是通过表的连接、行列的操作来实施数据的存取。习惯了面向对象设计的应用又必须要采用关系型的数据库作为持久层开发的方式,ORM框架的产生就是在它们中间架起的桥梁。6.1 数据持久化技术分析6.1.1 JDBC技术    JDBC可以说是访问持久数据层最原始、最直接的方法。在企业级应用开发中,可使用数据访问对象(Data Access Object,DAO)模式来把数据访问封装起来,然而后在其他层中进行调用。这种方式的优点是运行效率高,缺点是与其他持久化技术相比,JDBC直接访问数据库的方式需要程序员做所有的事,自己关心连接池,自己写大量的get、set方法,把SQL查询出来的值一个一个赋值到Java对象中,或者把Java对象的值一个一个取出来,用SQL插入到数据库,完全手动的进行O/R映射,不利于进行面向对象的编程,并且修改、扩展也很复杂。但是JDBC今天还是Java访问数据库的基石,我们的TeaORM框架也建立在JDBC的基础上,对JDBC进行了轻量级的封装,完全以面向对象的思维操作数据库,提供了更为上层的功能。6.1.2 数据持久层分析数据持久层就是为业务对象提供持久化能力的类的集合。持久层有效的包装了持久机制。持久层有效的包装了持久机制。持久层封装了将对象持久化所需要的行为,即在持久存储中读取、写入和删除对象的操作。持久层概念的核心是对象持久化方法,对象持久化的方法有以下三种:(1)一是将结构化查询语言(SQL)硬编码到类源码中。优点是编码速度快,其缺点是直接耦合了业务类和关系数据库结构。(2)另一种是将SQL语句从业务类中提取出来封装在一个或多个“数据类”中。数据的变动只限制在“中间类”中,从而实现解耦的功能。(3)第三种是使用一定的映射规则将对象映射到数据表,根据配置文件转换成目标SQL语句。这种方法也就是目前持久层普遍采用的对象持久化方法,即O/R映射来实现对象的持久层设计。TeaORM持久层框架采用的是第三种O/R映射(Object Relational Mapping)作为持久层,O/R映射就是对象模型表示的对象和基于SQL的关系模型之间的一个映射。在具体操作数据库时,就不需要再去和复杂的JDBC打交道,只需操作对象就可以了。在几乎所有的程序里面,都存在对象和关系数据库。通过持久层的O/R映射就可以直接把对象的信息(数据信息)保存在关系数据库中。O/R映射关系图如图6.1所示。

图6.1 O/R映射关系图采用O/R映射实现对象持久层的优点是程序员可以直接以面向对象的思维来操作数据库,它能极大的提个软件生产的效率不足是会影响系统的性能。6.1.3 全自动ORM与半自动ORM技术的比较ORM技术从使用者的角度,又可以分为全自动ORM和半自动ORM。对于全自动ORM,在这类实现中,它将对象到关系的映射进行了完整而又封闭的封装。半自动ORM只是对映射进行了部分封装。全自动对象映射的实现共同的特点就是对数据库结构提供了较为完整的封装,提供了从对象到数据表的全套映射机制。开发人员只需定义从对象到数据库表的映射关系,即可通过全自动ORM框架提供的API来完成持久层操作。在运行时刻,全自动ORM框架会根据配置逻辑生成SQL并调用JDBC来加以执行。这种全面的封装给开发者带来了很多好处,但是在许多情况下,完整的封装也表现出了它的不灵活的一面。半自动对象映射不同于全自动对象映射对数据库进行全自动化的封装,而主要定义对象到SQL之间的映射关系,在运行时刻它不会自动生成SQL,具体的SQL需要由程序员来编写,然后通过映射文件,传递SQL所需要的参数,以及返回的结果字段映射到对象。本文的数据持久层TeaORM框架也是采取这种半自动化设计方案。TeaORM框架之所以采用半自动化ORM方案,主要是考虑到与全自动对象关系映射相比,半自动对象关系映射具有如下优点:(1)开发人员可以对数据库操作细节完全的控制,在数据处理量大、对性能要求比较苛刻的情况下。比较有利于对SQL语句进行优化和调试,避免了数据库成为系统的瓶颈。(2)很容易实现动态SQL,每次使用SQL的时候可以随时定制,极大地方便了开发。(3)系统简单使用,只要熟悉SQL语句,开发人员能够马上上手,降低开发人员学习负担。6.2数据持久层ORM框架的设计TeaORM能够大大减少访问数据库的代码,TeaORM可以通过简单的XML配置文件将JavaBean,Map映射成SQL语句,通过SQL语句的执行而获得JavaBean,List等对象,TeaORM实现如图6.2所示。

图6.2 TeaORM的体系结构图TeaORM提供了一个简易的框架,输入参数使用简单的XML配置文件映射成JDBC的查询对象(Statement)— 用来绑定要执行的操作的对象,然后生成结果。TeaORM实现的功能如下:(1)该组件基于XML配置文件,实现底层数据库操作的SQL可配置化,可以控制最终的数据操作方式,通过SQL的优化获得最佳的数据库执行效能,在系统设计上具有比较很大的自由空间。(2) SQL语句的输入参数可以是基本类型的包装类型和JavaBean(如Integer、String、Map、JavaBean),输出参数可以是Integer、Javabean、List。(3)可针对特定的数据库操作设置特定的Java属性/SQL字段列值映射。(4) 能够管理对象的持续性。JavaBean到数据库表的映射,以对象的方式存取数据。O/R Mapping的定义都基于XML,具有良好的扩展性和通用性。(5)支持静态SQL和动态SQL。(6)支持增加、修改、删除、查询的操作。(7)使用TeaORM框架可以实现数据库平台无关性,可以随时切换开发及数据库发布平台,方便移植。6.3数据持久层ORM框架的实现6.3.1 TeaORM的实现原理TeaORM就是简化版的SQL工具,其实现与JDBC的实现内容是相似的,用JDBC来对数据库进行操作,必须要进行如下的操作:(1)加载(注册)适当的JDBC驱动程序。(2)建立数据库连接。(3)建立符合JDBC规范的SQL语句和输入参数。(4)执行SQL语句。(5)处理结果集。(6)关闭数据库连接。TeaORM底层对数据库的操作步骤也是一样的,只是参数信息并不是通过硬编码来实现的,而是通过配置文件来设置的。TeaORM的实现包括了三个部分:一、SQL语句部分;二、输入参数部分;三、输出结果部分。所以,针对上述的JDBC操作,转换为TeaORM就变成了如下操作:(1)配置TeaORM配置文件,载入JDBC驱动程序和数据库连接等信息。(2)配置TeaORM的SQL Map映射文件,包括Parameter、ResultMap、SQL等信息。其中:Parameter是输入参数部分,ResultMap是输出结果部分,sql是SQL语句部分。(3)根据SQL Map配置文件信息,加载配置的JDBC驱动程序。(4)根据SQL Map配置文件,建立数据库连接。(5)根据SQL Map配置文件中的sql信息和Parameter信息,建立符合JDBC规范的SQL语句和输入参数。(6)执行SQL语句。(7)处理结果集。(8)关闭数据库连接。6.3.2 SQL语句的解析在SQL Map配置文件的sql信息如下:<!-- delete语句 --><delete id="deleteUserById">    delete from t_user where username = #username# </delete>从以上配置信息,可以看出我们需要执行删除操作并且传入的参数为username,但是配置文件中的SQL语句还不是标准的SQL语句,我们必须通过解析文本信息,解析出符合JDBC标准的SQL语句,例如:delete from t_user where username = ?,也就我们在实现时需要对#username#进行处理,在TeaORM的具体实现如下:/** 解析sql语句,解析出sql语句及参数属性 */public static String parseSqlText(String sql, List<String> parameterNameList) {StringTokenizer parser = new StringTokenizer(sql, PARAMETER_TOKEN, true);/** 解析后的sql语句 */StringBuffer newSqlBuffer = new StringBuffer();String token = null;String lastToken = null;while (parser.hasMoreTokens()) {token = parser.nextToken();if (PARAMETER_TOKEN.equals(lastToken)) {if (PARAMETER_TOKEN.equals(token)) {        newSqlBuffer.append(PARAMETER_TOKEN);        token = null;} else {parameterNameList.add(token);newSqlBuffer.append("?");token = parser.nextToken();token = null;}} else {if (!PARAMETER_TOKEN.equals(token)) {newSqlBuffer.append(token);}}lastToken = token;}return newSqlBuffer.toString();}6.3.3 Statement的实现TeaORM的SQL Map组件的核心概念是Statement,TeaORM的半自动化的ORM特性也是体现在这里。Statement可以使用任意的SQL语句,并且拥有Parameter(输入)和ResultMap(输出)。有四种类型的Statement,分别对应四种SQL语句,InsertStatement、DeleteStatement、UpdateStatement、SelectStatement四种类型的Statement。Statement组件类的结构如图6.3所示。

图6.3 Statement组件类图  有了以上的实现,我们就可以在SQL Map配置文件中配置如下Statement:   <!-- insert语句 -->   <insert id="insertUser">       insert into t_user(USERNAME,PASSWORD,AGE)       values(#username#,#password#,#age#)   </insert>   <!-- delete语句 -->   <delete id="deleteUserById">       delete from t_user where username = #username#    </delete>   <!-- update语句 -->   <update id="updateUser">       update t_user set password = #password#,GMT_MODIFIED = now() where username = #username#   </update>   <!-- select语句 -->   <select id="selectAllUser" resultMap="userResultMap">       select USERNAME,PASSWORD,AGE,GMT_CREATE,GMT_MODIFIED       from t_user   </select>6.3.4 Parameter输入参数的实现Parameter传入的参数可以是JavaBean、基本类型、Map类型。Parameter的基本思想是定义一系列的有序的参数系列,用于匹配JDBC的PrepareStatement的值符号。Parameter的核心作用就是作为JDBC执行时所需要的参数值,也就是用户指定的条件,具体从JavaBean中获取属性值的实现如下:/** 从JavaBean中取得对应属性的值 */public static List<Object> getJavaBeanValue(BasicStatement statement,Object parameterObject) {List<Object> valuesParam = new ArrayList<Object>();if (statement != null) {if (statement.getPropertyParameters() != null) {try { /** 从Statement中取得属性参数列表 */List<String> propertyParamList = statement.getPropertyParameters();/** 得到参数Object的所有属性的get方法 */Map<String, Method> readMethods = PropertyDescriptorManager.getReadMethods(parameterObject.getClass());if (propertyParamList != null && propertyParamList.size() > 0) {   for (int i = 0; i < propertyParamList.size(); i++) {                   Method readMethod = readMethods.get(propertyParamList.get(i));                   /** 从JavaBean中读取参数属性的值*/                   Object value = readMethod.invoke(parameterObject,new Object[0]);                    /** 把得到的值存入List中 */                   valuesParam.add(value);} return valuesParam;             }        }    }}6.3.5 ResultMap输出结果的实现  ResultMap在执行SelectStatement时,ResultMap负责将结果集的列值映射成JavaBean的属性值,ResultMap的id属性是statement的唯一标识。ResultMap的class属性用于指定Java类的完全限定名。ResultMap可以包括任意多的属性映射,将查询结果集的列值映射成JavaBean的属性。属性的映射按它们在ResultMap中定义的顺序进行。相关JavaBean类必须符合JavaBean规范,每一个属性都必须拥有get/set方法。具体的映射实现如下:/** 得到JavaBean对象,根据resultMap映射关系,设置相应的属性值 */public Object getJavaBean(ReultMap resultMap,BasicStatement statement,ResultSet result) {/** 创建JavaBean对象 */Object resultObject = ClassInstance.newClass(resultMap.getClassName());Map<String, Method> writeMethods;try { /**得到ResultSet中的列名总数 */int Columncount = result.getMetaData().getColumnCount();/** 这里使用HashSet,因为后面使用contains的时候效率高 */Set<String> columnNames = new HashSet<String>();for(int i=0;i<Columncount;i++)  columnNames.add(result.getMetaData().getColumnName(i+1));/**根据JavaBean的Class对象得到所有属性的set方法 */writeMethods = PropertyDescriptorManager.getWriteMethods(resultObject.getClass());/** 注意,这里得到resultMap映射关系  property-->column */Map<String,String> mapColumnsMap = resultMap.getResultMappings();String column;for (String property : mapColumnsMap.keySet()) { /** 根据property取得colum */column = mapColumnsMap.get(property);/** 判断ResultSet中是否有此字段名,如果select语句中没有此字段,就不需要给JavaBean设置属性 */if(columnNames.contains(column)){ /** 根据column名得到查询的值 */Object value = result.getObject(column);/** value必须要为非空,因为如user.setAge(int a)如果参数值为null,就会参数java.lang.IllegalArgumentException异常 *//** value如果为空,设置不设置都一样 */if (value != null) { /** 得到set方法 */Method writeMethod = writeMethods.get(property);                writeMethod.invoke(resultObject, value); /** 向JavaBean中设置属性 */}}}}6.4 IoC框架与ORM框架整合实现IoC容器在O/R映射中的整合应用,只要是用在数据库的连接管理上,对数据库的连接方面,一般有专用的数据库连接池组件(比如Apache的dbcp连接池)来获取数据库连接,应用IoC组件,提供用户简单的接口,用户只需要简单的实现配置就可以,实现灵活的切换连接池,大大的扩展了系统的可扩展性。这里简单看一下配置文件,就可以看出IoC容器在TeaORM框架中应用的灵活度。TeaIoC的核心配置文件tea-context.xml配置如下:<beans><!-- 配置apache下的dbcp数据库连接池,默认连接池大小为8 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/AMS" /><property name="username" value="root" /><property name="password" value="root" /></bean><!-- 配置TeaORM框架的JdbcTemplate --><bean id="jdbcTemplate" class="org.teaframework.orm.support.JdbcTemplate" init-method="init"><property name="dataSource" ref="dataSource" /><!—配置ORM框架的主配置文件--><property name="configLocation"><value>tea-orm.xml</value> </property></bean><!-- 配置DAO,直接把jdbcTempate注入进去 --><bean id="adminDao" class="org.ams.dao.imp.AdminDaoImp"><property name="jdbcTemplate" ref="jdbcTemplate" /></bean>从配置文件可以看出,数据库连接池是可配置的,即随时可以切换数据库连接池及数据库连接信息。dataSource的具体配置和初始化是在IoC容器启动时完成的,通过简单的配置无需用户完成代码就可以实现数据库的连接工作。如果现在要换具体的数据库或具体的连接池实现,这时应用IoC容器,只需要简单的修改dataSource的配置就可以满足。原因在于jdbcTemplate是利用IoC容器注入dataSource实例的,所以jdbcTemplate及应用的DAO根本不需要关注具体的dataSource来自何处、由谁实现。6.5 ORM框架配置及使用说明    为了使用TeaORM框架,必须配置ORM的核心配置文件tea-orm.xml,配置示例如下所示:<tea-OrmConfiig>        <!-- name数据库的用户名 -->        <!-- password数据库的密码 -->        <!-- dbName需要访问的数据库的名字 -->    <connection name="root" password="root" dbName="ibatis" ><dataBaseVersion value="MySQL"/>    <driver value="com.mysql.jdbc.Driver"/> <url value="jdbc:mysql://localhost:3306/tea"/> </connection>        <!--是否在控制器输出Sql语句 --> <config-const name="show_sql" value="true"/> <!-- sqlMap映射文件  --><sqlMap resource="test/model/UserSqlMap.xml"/></tea-OrmConfiig>然后配置实体类User对应的SqlMap配置文件,示例配置如下:<sqlMap namespace="userDao"><resultMap id="userResultMap" class="test.model.User"><result property="username" column="USERNAME" /><result property="password" column="PASSWORD" /><result property="age" column="AGE" /><result property="gmtCreate" column="GMT_CREATE" /><result property="gmtModified" column="GMT_MODIFIED" /></resultMap>   <!-- insert语句 -->   <insert id="insertUser">       insert into t_user(USERNAME,PASSWORD,AGE)       values(#username#,#password#,#age#)   </insert>   <!-- delete语句 -->   <delete id="deleteUserById">       delete from t_user where username = #username#    </delete>   <!-- update语句 -->   <update id="updateUser">       update t_user set password = #password#,GMT_MODIFIED = now() where username = #username#   </update>   <!-- select语句 -->   <select id="selectAllUser" resultMap="userResultMap">       select USERNAME,PASSWORD,AGE,GMT_CREATE,GMT_MODIFIED       from t_user   </select></sqlMap>现在所有的ORM配置文件已经配置完成,我们就可以通过TeaORM来操作数据库了,具体的使用代码如下:public class TestClass {public static void main(String[] args) { // 得到用户接口SqlMapClient对象SqlMapClient sqlMapClient = SqlMapClientBuilder.buildSqlMapClient("tea-orm.xml");User user = new User();user.setUsername("cui");user.setPassword("7999502");user.setAge(23);sqlMapClient.insert("userDao.insertUser",user); //添加user.setPassword("xiu"); //修改 sqlMapClient.update("userDao.updateUser",user);sqlMapClient.delete("userDao.deleteUserById", "cui"); //删除List users = sqlMapClient.queryForList("userDao.selectAllUser"); //查找 System.out.println(users);}7 轻量级TeaFramework框架的应用7.1 系统分析7.1.1 资产管理系统简介资产管理系统为企业提供全面、迅速的资产信息,方便管理者了解和操作企业内部的资产管理。本系统是基于B/S的多层Web应用,在windows xp操作系统下,本系统选择了Myeclipse8.0开发环境、Mysql数据库和JSP、本论文轻量级的TeaFramework框架、AJAX无刷新技术,利用MVC设计模式将业务逻辑和表示逻辑分离,在表示层利用JSP技术实现了页面展现、利用AJAX实现无刷新技术;在业务逻辑层,利用TeaIoC框架技术实现了系统设置、资产管理以及分类统计三个主要模块的开发;后台使用Mysql进行数据库的开发,并利用TeaORM技术完成对数据库的操作,实现了数据查询、修改、增加、删除等功能。资产管理系统应用了轻量级Tea框架的所有功能,可以充分说明利用TeaFramework框架对WEB系统开发的作用和意义。7.1.2 资产管理系统功能介绍 本系统有3个用户,即普通用户(简称用户)、管理员与超级管理员。用户仅仅作为资产拥有者而存在,用户的信息由管理员录入;管理员可以管理用户、部门、类别、资产。超级管理员除了拥有管理员的权限外,还可以管理管理员。资产管理系统用例图如图7.1所示。 系统用例主要包括:管理员可以管理系统(管理用户、管理部门、管理类别、管理管理员)、管理资产(增加资产、删除资产、修改资产、查询资产)、统计分类(统计资产、统计送修)等。

图7.1资产管理系统用例图7.1.3 资产管理系统数据分析本商城购物系统主要包括6个实体:(1)资产(资产id,资产编号,名称,类别,型号,生产厂家,生产日期,购买日期,价格,使用情况,折旧,所属部门,所属用户)(2)类别(类目id,类别名,描述)(3)部门(部门id,部门名,描述)(4)用户(用户id,姓名,所属部门,邮箱,电话,手机)(5)送修(送修id,资产id,送修日期,花费,状态,送修人员,描述)(6)管理员(管理员id,用户名,密码,姓名,登录次数,所属部门,邮箱,电话,手机,权限,最后登录时间)各个实体之间的关系为:某个用户可以拥有多个资产;一个类别中可以包括多个资产;一个资产可以维修多次,一次送修只对应一个资产。7.2 系统设计7.2.1 系统总体结构设计整个系统的架构设计遵循MVC模式,将展示层、控制层、模型层(业务逻辑层)及持久层进行合理分离。表现层采用TeaMVC框架,业务逻辑层采用TeaIoC框架来管理系统中的业务逻辑,数据持久层以半自动化TeaORM框架,提供具体的数据库数据处理操作,三者进行有机整合,构成轻量级J2EE应用框架。整个轻量级框架业务流程为:用户接口层利用JSP+HTML页面实现交互界面,负责传送页面请求和接收响应,表示层TeaMVC收到请求,调用相应的控制器Action,处于业务层的TeaIoC容器负责向Action提供业务服务组件(Service)和相应的数据访问处理组件(DAO),处于持久层的TeaORM负责对象化映射与数据库交互,具体处理DAO组件请求,并返回结果。资产管理系统体系结构图如图7.2所示。

图7.2资产管理系统体系结构图7.2.2数据库设计将系统中的实体根据关系转换原则,并兼顾操作的灵活性,可以将6个实体转化为以下6个关系表,如表7.1~表7.6所示。表7.1  管理员表(admin)

字段名称

数据类型

是否为空

主键

描述

id

Int

Not null

主键

username

Varchar

Not null

用户名

password

Varchar

Not null

密码

字段名称

数据类型

是否为空

主键

描述

name

Int

Not null

姓名

logincount

Int

Not null

登录次数

department

Int

Not null

所属部门

email

Datetime

邮箱

workphone

Varchar

电话

mobilephone

Varchar

手机

permission

Int

Not null

权限

lasttime

Datetime

Not null

最后登录时间

表7.2 资产表(assets)

字段名称

数据类型

是否为空

是否允许为空

描述

id

Int

Not null

主键

aid

Varchar

Not null

编号

assetname

Varchar

Not null

名称

type

Int

Not null

外键

类别

version

Varchar

型号

manufacturer

Varchar

生产厂家

manufacturedate

Datetime

生产日期

buydate

Datetime

Not null

购买日期

price

Double

Not null

价格

usestate

Int

Not null

使用情况

deprecition

Int

Not null

折旧

department

Int

外键

所属部门

user

Int

外键

所属用户

表7.3 类别表(type)

字段名称

数据类型

是否为空

主键

描述

id

Int

Not null

主键

typename

Varchar

Not null

类别名

description

Text

描述

    表7.4 部门表(department)

字段名称

数据类型

是否为空

主键

描述

id

Int

Not null

主键

dpname

Varchar

Not null

部门名

description

Text

描述

表7.5 用户表(user)

字段名称

数据类型

是否为空

主键

描述

id

Int

Not null

主键

name

Varchar

Not null

姓名

department

Int

Not null

外键

所属部门

email

Varchar

邮箱

workphone

Varchar

电话

mobilephone

Varchar

手机

表7.6 送修表(bsend)

字段名称

数据类型

是否为空

主键

描述

id

Int

Not null

主键

aid

Int

Not null

外键

资产ID

bSenddate

Datetime

Not null

送修日期

cost

Double

Not null

花费

state

Int

Not null

状态

bSendperson

Int

Not null

外键

送修人员

description

Text

描述

7.3 系统实现7.3.1 TeaFramework在资产管理系统中的应用7.3.1.1 TeaMVC与Web环境的整合配置     为了在资产管理系统中应用TeaMVC框架,必须在Servlet规范文件web.xml中引入TeaMVC的核心过滤器TeaFrameworkFilter及一些辅助过滤器,如乱码处理过滤器CharacterEncodingFilter,这样就把TeaMVC的入口引入到了资产管理系统中,之后就可以处理客户端的Http请求,统一进入框架处理配置时需要特别注意一点,就是乱码处理过滤器一定要配置在核心过滤器TeaFrameworkFilter之前,因为一旦进入核心过滤器处理,就不会再顺着过滤器链流向下一个过滤器,即其它过滤器就不会起到拦截的作用了。TeaMVC在web.xml的配置如下:  <!-- 利用的TeaMVC框架的过滤器来设置编码方式 -->  <filter><filter-name>encodingFilter</filter-name><filter-class>org.teaframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param>  </filter>  <filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern>  </filter-mapping>

  <!-- 配置TeaMVC框架的入口Filter -->  <filter>   <filter-name>actionFilter</filter-name>   <filter-class>org.teaframework.web.filter.TeaFrameworkFilter</filter-class>   <init-param>   <param-name>actionConfig</param-name>

<!-- 配置TeaMVC的核心配置文件 -->   <param-value>tea-action.xml</param-value>   </init-param>  </filter>  <filter-mapping>   <filter-name>actionFilter</filter-name>   <url-pattern>/*</url-pattern>  </filter-mapping>7.3.1.2 TeaMVC的应用配置      TeaMVC的核心配置文件为tea-action.xml,该配置文件中可以配置开发常量,控制器Action模块,用户自定义拦截器,默认拦截器,全局result模块,资产管理系统的TeaMVC的核心配置文件tea-action.xml如下:<tea-web><!-- 系统开发常量配置,可以定义零个或多个,Action是否由IoC容器来控制--><config-const name="IoC" value="true"/><!-- 配置可编程控制器模块 --><controller name="admin" namespace="/">   <!-- 拦截器配置 -->   <interceptors> <interceptor name="checklogin" class="org.ams.interceptor.CheckLoginInterceptor" /><interceptorname="checkpermission"class="org.ams.interceptor.CheckPermissionInterceptor" /> <interceptor-stack name="withchecklogin"><interceptor-ref name="checklogin" /><interceptor-ref name="defaultStack" /> </interceptor-stack>   </interceptors>   <!-- 默认拦截器 -->   <default-interceptor-ref name="withchecklogin" />   <!-- 全局result配置 -->   <global-results><result name="login" url="/index.jsp" /><result name="permission" url="/nopermission.jsp"/><result name="error" url="/error.jsp"/>   </global-results>

<!-- 配置action --><action name="createtype" class="createTypeAction" method="create" /><action name="createadmin" class="createAdminAction" method="create"><action name="createuser" class="createUserAction" method="create" /><action name="createasset" class="createAssetAction" method="create" /><!-- admin --><action name="createadmin" class="createAdminAction" method="create"><interceptor-ref name="checkpermission" /><interceptor-ref name="defaultStack" /></action><action name="searchadmin" class="searchAdminAction" method="search"><interceptor-ref name="checkpermission" /><interceptor-ref name="defaultStack" /></action><action name="deleteadmin" class="deleteAdminAction" method="delete"><interceptor-ref name="checkpermission" /><interceptor-ref name="defaultStack" /></action>        ……<!-- login及exit及statistics --><action name="login" class="loginAction" method="login" /><action name="exit" class="exit" method="exit" />

<!-- 开发约定必顺在WEB-INF下的errorPage文件夹下 --><errorPage><page name="404" url="errorNotFoundResultAction.jsp" /></errorPage>

</tea-web>7.3.1.3 TeaMVC拦截器实现登录及权限验证     基本的需求是管理员向服务器发起一个请求时,系统通过拦截配置去判断是否已经登录,如果还没登录,则不会执行业务逻辑,而是直接拦截下来转发到登录页面。TeaMVC拦截器实现验证是否已经登录的基本原理图如图7.3所示。

图7.3 TeaMVC拦截器实现是否已登录图具体的拦截器实现如下:/** 使用TeaMVC拦截器  验证是否已经登录  */public class CheckLoginInterceptor implements Interceptor {public String intercept(ActionInvocation invocation) throws Exception {    if (invocation.getAction() instanceof LoginAction) {            return invocation.invoke();         }    Map session = invocation.getInvocationContext().getSession();    Object admin = session.get("admin");    /** 验证是否已经登录 */    if(admin != null){ /** 如果已经登录则继续执行下去  */    return invocation.invoke();    }    else{ /** 如果没有登录则跳转到登录页面index.jsp */    return "login";    }    }}     实现管理员权限验证也是相同的原理。TeaMVC拦截器实现权限验证基本原理如图7.4所示。

图7.4 TeaMVC拦截器实现管理员权限验证图具体的拦截器实现如下:/** 使用TeaMVC拦截器 验证权限 */public class CheckPermissionInterceptor implements Interceptor {public String intercept(ActionInvocation invocation) throws Exception{    Map session = invocation.getInvocationContext().getSession();    /** 先验证是否已经登录 */    if(session.get("admin") == null)     return "login";    Admin admin = (Admin)session.get("admin");    if(admin.getPermission() == 0){ /** 如果是超级管理员,则继续执行下去*/    return invocation.invoke();    }    else{/** 如果不是超级管理员,则权限不够,跳转到无权限页面 */    return "permission";    }    }}具体在TeaMVC的核心配置文件tea-action.xml中的配置如下:<!--拦截器配置  --><interceptors>     <!—配置登录验证拦截器--> <interceptor name="checklogin" class="org.ams.interceptor.CheckLoginInterceptor" />     <!—配置权限验证拦截器--><interceptorname="checkpermission"class="org.ams.interceptor.CheckPermissionInterceptor" /> <interceptor-stack name="withchecklogin">     <interceptor-ref name="checklogin" />     <interceptor-ref name="defaultStack" /> </interceptor-stack></interceptors>

<!--默认拦截器配为登录验证拦截器  --><default-interceptor-ref name="withchecklogin" />7.3.1.4 TeaORM的应用配置TeaORM作为半自动化的ORM框架,为资产管理系统提供了持久层的解决方案,资产管理系统的TeaORM的核心配置文件tea-orm.xml如下:<?xml version="1.0" encoding="UTF-8"?><tea-OrmConfig>    <!-- 是否打印sql语句 -->   <config-const name="show_sql" value="false" />    <!-- SqlMap配置文件,核心JDBC操作及OR映射关系都配置以下配置文件中 -->    <sqlMap resource="org/ams/model/AdminSqlMap.xml" />    <sqlMap resource="org/ams/model/DepartmentSqlMap.xml" />    <sqlMap resource="org/ams/model/TypeSqlMap.xml" />     <sqlMap resource="org/ams/model/UserSqlMap.xml" />     <sqlMap resource="org/ams/model/AssetsSqlMap.xml" />     <sqlMap resource="org/ams/model/BsendSqlMap.xml" />  </tea-OrmConfig>TeaORM具体的核心OR配置文件*SqlMap,起到了资产管理系统中的数据对象DO与数据表之间的映射关系的对应配置,并且系统所需要对这个的DO的增删查改操作可以直接在该DO对应的SqlMap配置文件中进行直接的SQL语句的编写,为系统设计提供了更大的自由空间,为以后SQL语句优化提供了便利,资产管理系统中数据对象Type对应的TypeSqlMap.xml配置文件如下:<?xml version="1.0" encoding="UTF-8" ?><sqlMap namespace="typeDao">   <!--配置对象数据库映射关系--><resultMap id="typeResultMap" class="org.ams.model.Type"><result property="id" column="id" /><result property="typename" column="typename" /><result property="description" column="description" /></resultMap><!-- insert语句 --><insert id="insertType">     insert into type(typename,description)     values(#typename#,#description#)</insert>   <!-- delete语句 --><delete id="deleteType">     delete from type where id = #id#</delete><!-- update语句 --><update id="updateType">     update type set typename = #typename#,description = #description# where id = #id#</update><!-- select语句 --><select id="selectTypeById" resultMap="typeResultMap">     select * from type     where id = #id#</select>   </sqlMap>7.3.1.5 TeaIoC的应用配置TeaIoC作为资产管理系统的IoC容器框架,管理着系统中可配置的bean,资产管理系统的TeaIoC框架的配置文件tea-context.xml如下:<beans><!-- 配置apache下的dbcp数据库连接池,也叫数据源,默认连接池大小为8 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/AMS" /><property name="username" value="root" /><property name="password" value="root" /></bean>    <!-- 配置TeaORM框架的JdbcTemplate --><bean id="jdbcTemplate" class="org.teaframework.orm.support.JdbcTemplate" ><property name="dataSource" ref="dataSource" /><property name="configLocation"><value>tea-orm.xml</value> <!--TeaORM框架的核心配置文件--></property></bean><!-- 配置Dao --><bean id="adminDao" class="org.ams.dao.imp.AdminDaoImp"><property name="jdbcTemplate" ref="jdbcTemplate" /></bean>    ......    <!-- 配置Service --><bean id="adminService" class="org.ams.service.imp.AdminServiceImp"><property name="adminDao" ref="adminDao" /></bean>    ……<!-- 配置Action --><bean id="createAdminAction" class="org.ams.action.admin.CreateAdminAction" scope="prototype"><property name="adminService" ref="adminService" /></bean></beans>7.3.1.6 TeaIoC和TeaMVC的整合配置     TeaMVC中的控制器Action对象可以由TeaMVC框架自己创建,也可以由TeaIoC框架来托管,请求Action时,TeaMVC只需要去向IoC容器取对应的Action对象。在资产管理系统中控制器Action由IoC容器来统一托管,整合配置在tea-action.xml中如下:<tea-web><!—在tea-web.xml中通过开发常量IoC为true指定控制器统一由IoC容器管理 --><config-const name="IoC" value="true"/>   <!--配置action ,这里class即为TeaIoC配置文件中对应的bean的id--><action name="createtype" class="createTypeAction" method="create" />….</tea-web>在TeaIoC的配置文件tea-context.xml中对应的A ction的bean配置如下。<bean id="createTypeAction" class="org.ams.action.type.CreateTypeAction" scope="prototype"><property name="typeService" ref="typeService" /></bean>7.3.1.7 TeaIoC和TeaORM的整合配置TeaORM作为一个独立的ORM框架,可以单独配置数据源,也可以与TeaIoC框架整合,通过bean来管理数据源。在资产管理系统中我们采用后者与TeaIoC整合的方案来来配置系统的数据源。用户的DAO组件可以直接集成TeaORM提供的JdbcDaoSupport父类,再通过TeaIoC向DAO组件注入JdbcTemplate,则直接调用getJdbcTemplate()方法就可以进行数据库操作。tea-context配置文件如下:<!-- 配置apache下的dbcp数据库连接池,默认连接池大小为8 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/AMS" /><property name="username" value="root" /><property name="password" value="root" /></bean>

<!-- 配置TeaORM框架的JdbcTemplate --><bean id="jdbcTemplate" class="org.teaframework.orm.support.JdbcTemplate" ><property name="dataSource" ref="dataSource" /><property name="configLocation"><!--配置TeaORM的配置文件,把TeaIoC和TeaORM整合起来-->     <value>tea-orm.xml</value> </property></bean>用户的DAO组件只需要如下编写:public class AdminDaoImp extends JdbcDaoSupport implements AdminDao{ /** 创建Admin */public void add(Admin admin){getJdbcTemplate().insert("adminDao.insertAdmin", admin);}}7.3.2 系统功能模块的设计与实现7.3.2.1 资产管理系统工程包层次结构资产管理系统工程包层次结构如图7.5所示。

7.5资产管理系统工程包层次结构图7.3.2.2资产添加模块的设计与实现管理员可以添加和查询资产。管理员添加资产具体的序列图如图7.6所示。

  图7.6 添加资产序列图

关键源代码及注释如下所示:/** 创建资产 */public String create() throws IOException, ParseException{ServletResponse response = ServletActionContext.getServletResponse();response.setCharacterEncoding("utf-8");PrintWriter out = response.getWriter();if(typeid == 0 || asset.getAid().equals("") || asset.getAssetname().equals("") || asset.getPrice().equals("")){out.print("empty");return null;}if(isuse.length() > 1 || isdep.length() > 1 || gmrq.equals("")){out.print("empty");return null;}if(Integer.valueOf(isuse) == 2){if(did == 0 || uid == 0){out.print("empty");return null;}}String name, mp = "";if(!ccrq.equals("")){Date date = DateFormat.getDateInstance().parse(ccrq);asset.setManufacturedate(new Timestamp(date.getTime()));}if(!gmrq.equals("")){Date date = DateFormat.getDateInstance().parse(gmrq);asset.setBuydate(new Timestamp(date.getTime()));}asset.setAssetname(name);if(!asset.getManufacturer().equals(""))asset.setManufacturer(mp);asset.setUsestate(Integer.valueOf(isuse));asset.setDeprecition(Integer.valueOf(isdep));assetService.add(asset, typeid, did, uid);out.print("suc");return null;}该功能在tea-action.xml中的配置如下:<action name="createasset" class="createAssetAction" method="create" />该功能在tea-context.xml中的配置如下:<bean id="createAssetAction" class="org.ams.action.asset.CreateAssetAction"scope="prototype">    <property name="assetService" ref="assetService" /></bean>该功能在assetSqlMap.xml中配置如下:<insert id="insertAssets"> insertintoassets(aid,assetname,version,manufacturer,manufacturedate,buydate,price,usestate,deprecition,type,department,user)values(#aid#,#assetname#,#version#,#manufacturer#,#manufacturedate#,#buydate#,#price#,#usestate#,#deprecition#,#typeId#,#departmentId#,#userId#) </insert>该功能实现的效果如图7.7所示。

         图7.7 添加资产效果图7.3.2.3资产查询模块的设计与实现管理员可以查询资产。管理员查询资产具体的序列图如图7.8所示。

  图7.8 查询资产序列图关键源代码及注释如下所示:/** 根据资产编号或资产名称查询资产 */@SuppressWarnings("unchecked")public String search() throws IOException{ServletResponse response = ServletActionContext.getServletResponse();PrintWriter out = response.getWriter();if(selecttype.length() > 1){out.print("empty");return null;}if(Integer.valueOf(selecttype) == 1){ /** 根据资产编号查询 */List<Assets> list = assetService.searchByAid(value, typeid, isdep, isuse, price1, price2, did, uid);for(Assets asset : list){asset.setType(typeService.getTypeById(asset.getTypeId()));        asset.setDepartment(departmentService.getDepartmentById(asset.getDepartmentId()))asset.setUser(userService.getUserById(asset.getUserId()));}JSONArray json = JSONArray.fromObject(list);out.print(json.toString());return null;}if(Integer.valueOf(selecttype) == 2){ /** 根据资产名称查询 */List<Assets> list = assetService.searchByName(value, typeid, isdep, isuse, price1, price2, did, uid);for(Assets asset : list){asset.setType(typeService.getTypeById(asset.getTypeId()));            asset.setDepartment(departmentService.getDepartmentById(asset.getDepartmentId()))asset.setUser(userService.getUserById(asset.getUserId()));}JSONArray json = JSONArray.fromObject(list);out.print(json.toString());return null;}return null;}该功能在tea-action.xml中的配置如下:<action name="searchasset" class="searchAssetAction" method="search" />该功能在tea-context.xml中的配置如下:<bean id="createAssetAction" class="org.ams.action.asset.CreateAssetAction"scope="prototype">    <property name="assetService" ref="assetService" /></bean>该功能在assetSqlMap.xml中配置如下:<select id="selectByAid" resultMap="assetsResultMap">    select * from assets    where aid like concat('%',#aid#,'%')</select>该功能实现的效果如图7.9所示。

图7.9 查询资产效果图7.3 利用TeaFramework实现资产管理系统的效果利用TeaFramework框架实现资产管理系统,使得该业务系统具有了许多优良的特性,具体有:(1)TeaFramework框架使得该管理系统的结构清晰,功能明确利用TeaFramework框架开发的管理系统,使得管理系统的所有功能都清晰的配置在配置文件中,而且Model、View、Controller的结构非常清晰,提高了整个系统的可理解性。(2)TeaFramework框架提高了该管理系统的可扩展性利用TeaFramework框架开发的管理系统,使得管理系统的可扩展性大大提高,如每需要增加一个功能,只需修改配置文件,增加几个相应的动作即可,而不必过多关注各模块交互的细节。(3)TeaFramework框架提高了管理系统开发的效率TeaFramework框架给管理系统提供了清晰的架构,该业务系统的再次开放者只需填充、完善相应的模块的功能,而不需要编写模块之间交互的代码,还有TeaFramework框架的控制器Action完成了WEB应用系统的大部分功能,系统的开发者只需要编写相应的配置文件即可,从而极大的提高了WEB应用系统的开发效率。

总结与展望目前,轻量级框架的发展非常的繁荣,轻量级框架的发展极大的推动了J2EE技术的发展,但没有那一个轻量级框架是十分完美的,而且我们的J2EE项目对各个轻量级框架的要求也不尽相同,即使是针对同一个框架,其提供的功能也不会很符合我们不同项目的要求,我们的目标就是实现一个快速搭建软件的框架或者是平台,这个平台是一个比较实用和通用的软件开发半成品框架,一方面应用框架能迅速的实现软件的构建;另一方面也能根据项目的需要快速的搭配、调整和定制与项目适配的框架,从而实现真正软件快速生成,实现软件工厂的目的。本文主要讨论的就是研究和实现这样一个框架,虽然在实现上还有许多的问题离软件平台的目标还有许多的差距,但还是做了些工作和尝试,下面谨对本论文的工作做如下概括: (1)根据目前主流的开源框架(Struts2、Spring、iBatis)的思想提出了J2EE轻量级框架整体的分层结构的设计,并给出了各层相应的框架组件的设计。(2)详细的给出了各个层次的具体设计和实现,具体实现了表现层TeaMVC,业务层TeaIoC框架,数据持久层TeaORM框架。(3)在实现的表现层和数据层框架中,不仅给出了表现层MVC框架和数据层框架ORM的设计和实现,另外还在该实现中都应用了我们设计的中间层框架的实现。实际上整合了前面设计的中间层组件IoC框架组件。(4)本论文的TeaFramework虽然一些设计思想及设计理念来自于现在主流的开源框架,但是也有自己的特色,就是把所有层次的框架功能都一站式地整合起来,不需要额外的插件,能快速地开发软件半成品。(5)最后通过应用一个资产管理系统,实践了TeaFramework作为一个轻量级框架的通用的快速开发框架的稳定性及可行性。由于时间有限,论文的研究和实现工作仍处于初期阶段,有许多工作有待完成,需要继续补充、完善。下一步要做的工作如下。(1)进一步完善整个框架的结构,从总体上把握和协调好各个框架组件的关系,能实现按需要动态的配置和替换各个框架组件。(2)对已经实现的框架,还需要近一步的完善其功能,改善其性能,具体为:对MVC框架根据页面驱动进行重构;对IoC框架目前实现的功能还有待扩展。(3)后期会增加业务层AOP框架,这个组件已经构思好,很快可以设计及实现。由于作者水平有限对有些问题未能深入探讨错误与不妥之处在所难免,敬请各位评阅专家和老师们批评指正。
TeaFramework下载地址:http://download.csdn.net/detail/c929833623lvcha/5255385
TeaFrameworkTest下载地址:http://download.csdn.net/detail/c929833623lvcha/5255417
 
 

                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于J2EE架构下网络教学平台的设计与实现 摘 要:分析了在构件网络教学平台过程中用到的几个关键J2EE技术。以中国·联合国计划发展暑 403项目中网络教学平台的开发为例,结合甘肃省经济现状,探讨了在J 2EE环境下如何建立一个网 络教学平台的基本框架。 关键词:J2EE;网络教学;教学平台 中图分类号:G434 1 引言 近年来中国西部省份教师的补充及教师质量的 提高取得了长足发展,但合格教师仍然不足,尤其在 农村地区。中国·联合国发展计划署应用远程教育 和ICT技术旨在提高中国西部贫穷地区教师质量。 作者参与了此项目并针对甘肃省教育、经济现状开 发了一个基于J2EE架构的网络教学平台。其基本 组织结构是基于省、县、乡三级资源中心的组织模 型。该教学平台可以采用基于卫星传送或网络传送 等多种远程教学方法。图1是系统运行网络结构 图。 系统开发基于J2EE架构技术,实现平台无关 性、具备开放性与继承性。J2EE一个涉及多个层 面的复杂概念,系统采用Browser/Web Server、Appli2 cation Server和DB Server多层结构,方便系统的维护 和扩充系统完全,提供比Windows更安全的、更能扩 展的应用服务器,允许用户可以选择更多的可靠的, 可扩展的操作系统平台(如:AIX,Solaris,Linux等), 提供更好的扩展接口Web Service(SOAP/XML)到其 它相关系统等。 同时通过标准和可重用的J2EE技术(如:JSP, Servlet,EJB组件等),可以快速和安全的部署实施 网络教学远程培训的基础设施。 2 实现网络教学平台的几种J2EE技术 J 2EE平台由一整套服务(Services)、应用程序接......
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值