《订单管理系统(SSH版)》

更新时间:2024-04-25 02:39:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

浪潮优派 订单管理系统(SSH版)技术总结报告

《订单管理系统(SSH版)》

技术总结报告

年级 班级 姓名 教师 成绩

山东浪潮优派教育科技有限公司

1

浪潮优派 订单管理系统(SSH版)技术总结报告

1、软件开发流程。

按照标准的的项目开发流程,整个项目的过程需要经历以下几个步骤,分别是可行性分析和项目开发计划、需求分析、概要设计、详细设计、编码、测试、维护。

首先,可行性分析和项目开发计划主要是在经济可行性、社会可行性和技术可行性三个主要方面进行研究,比如技术可行性这个方面,如果我们没有学过jsp和j2ee技术的话那么这个项目是不能做出来的。这个阶段出的文档主要有《可行性研究报告》《项目开发计划》。需求分析阶段主要分析功能的需求、性能的需求、环境的需求以及用户界面的需求。出的主要文档是《需求规格说明书》。概要设计主要是在宏观的角度对整个项目的结构和组织进行分析,设计软件系统结构和进行数据结构及数据库设计,主要文档是《概要设计说明书》、《数据库设计说明书》。详细设计阶段主要是对每个模块进行主要算法的设计以及其它部分的具体设计,必须要写的文档是《详细设计说明书》。然后是编码,这个过程才是我们主要去要完成的。前几个过程都在项目的原始文件夹中已经都被设计好了,我们需要工作的就是编写具体的代码。编码之后最重要的工作就是进行测试,测试是保证软件质量的重要环节。最后,是进行软件维护,软件维护在整个的软件生命周期中占有的比重是很大的,所以不能忽视它的作用。分为预防性维护、改正性维护、完善性维护和适应性维护。其中,预防性维护比例最小。如果我们对我们的项目在开发后一个较短的时间内需要进行维护的话,看主要是进行改正性维护和完善性维护。

当然,这个项目中主要需要我们完成的是编码阶段的工作。

注:以上文字来源于《JSP课程设计-设计报告》的“软件开发流程”部分。

2

浪潮优派 订单管理系统(SSH版)技术总结报告

2、项目整体功能概要。

了解项目的功能,首先要了解整个公司的大概的业务领域和工作模式。从《需求规格说明书》中可以很明确的了解到,该公司的主要经营业务领域是电力电缆、连接配件等电力设备,工作模式是“业务员+代理商”。其实这里还隐藏了一个工厂,没有工厂哪来的业务员。那么,这三者之间是有关系的,工厂负责生产,代理商负责销售,业务员负责连通工厂和代理商,作为生产和销售之间的重要桥梁。

当然,业务员进行工作,需要进行记录,比如和代理商签订合同、开具发票、回收货款,这一些都需要进行认真地记录。那么在以往的方式中,刀耕火种的纸笔记录和口头传话模式首先工作量比较大,再者也比较容易出错,甚至是造成一错再错的情况。不利于公司的稳定运行和长期发展。那么,就需要一种现代化的方式来进行干预。

故该项目就应运而生,需求中对该项目的要求是发现回款是否延迟、统计业务员的业绩和代理商的出货情况,从而以为公司领导提供数据参考。

3、团队组成说明。

序号 01 02 03 04 05 06 组员 分工 是否是组长

3

浪潮优派 订单管理系统(SSH版)技术总结报告

4、个人承担的开发任务说明。

通过“订单管理系统概要设计说明书”文件夹中的“画面迁移及概要说明”得知整个项目由29个画面或者说29个功能模块组成。我在该项目中完成了前11个模块。下面简单叙述所完成的这11个功能模块,以及截图和功能说明。

4.1已完成的模块及完成时间。。

序号 01 02 03 04 05 06 07 08 09 10 11

4

画面名称 登录画面 菜单画面 密码修改画面 用户管理 用户增加/编辑画面 客户管理 客户增加/编辑画面 货币管理 货币增加/编辑画面 代理商管理 代理商增加/编辑画面 页面名称 login mainMenu resetPassword userMaster userEdit customerMaster customerEdit currencyMaster currencyEdit agencyMaster agencyEdit 完成时间 2014.11.11 2014.11.11 2014.11.12 2014.11.12 2014.11.12 2014.11.13 2014.11.13 2014.11.13 2014.11.13 2014.11.14 2014.11.14 浪潮优派 订单管理系统(SSH版)技术总结报告

4.2每个子模块的截图和功能说明。 (1)登陆画面

①功能说明:这是用户访问该系统的唯一入口。任何未登录的用户想要访问该系统中的任一功能界面,都会被定义的拦截器拦截,并转向登陆界面提示登录后再访问。输入正确的用户名和密码才能够正常登陆,转向“菜单界面”。如果用户名或密码输入错误,会提示错误。 ②功能说明:经过后台处理发现用户名或密码输入错误,那么把该信息返回给前台,提示用户重新登录。

5

浪潮优派 订单管理系统(SSH版)技术总结报告

③功能说明:此外,如果用户名或密码任何一个没有输入完全那么会弹出对话框提示。当然这是有前台直接控制的,避免了把空值发给服务器才进行校验的小题大做。

6

浪潮优派 订单管理系统(SSH版)技术总结报告

(2)菜单画面

功能说明:正确登录以后会转向“菜单界面”,菜单界面展现了该系统的全部功能。但是并不是所有的用户可以使用全部的功能,从概要说明书的画面迁移图中可以知道,该系统系统成员身份有三种,分别是管理人员、财务人员和业务人员。每个人员有不同的功能模块。其中,管理人员享有的功能模块是全部模块,即下图中的9个按钮。因为在该项目中完成的11个画面的大多数是在后四个大模块中,即用户管理、客户管理、代理商管理和货币管理,在管理人员的权限下都可以进行查看和测试。故不再展现财务人员和业务人员的眼前画面。

7

浪潮优派 订单管理系统(SSH版)技术总结报告

(3)密码修改画面

①功能说明:系统的右上角单击自己的用户名,会出现下拉菜单,下拉菜单中有修改密码模块可以单击进去进行密码的修改。

②功能说明:输入密码提交后就会有校验。如下图所述,是旧密码没有输入正确,然后把该消息告诉前台,那么用户需要重新检查后再次提交修改申请。

③功能说明:如果两次新密码输入不一致,也会提醒。当都输入正确后,提交后会返回登陆界面,这时候可以输入新密码重新登录系统。

④功能说明:此外,与修改密码在一起的还有一个退出系统的功能。当选择退出系统后,系统也会跳转到登陆界面,这时候用户可以继续登录或者关闭页面。

8

浪潮优派 订单管理系统(SSH版)技术总结报告

(4)用户管理

①功能说明:下图展现了用户管理界面。在该界面内,可以查看到所有用户的个人信息,默认每页显示10条记录,下面的页码可以进行自由的切换和进行显示条数的选择。需要说明的是,测试中为了插入数据方便以能够插入大量的数据来测试分页效果,故用户名那一列没有进行唯一性约束。真实环境中还是应该进行唯一性约束的,毕竟登陆的时候需要使用唯一的用户名和与之匹配的正确的密码才能登陆系统。 ②功能说明:下图展现了页码切换区的灵活展现与切换功能。所谓灵活展现和切换。首先是要正确,正确显示该显示的页码标签数,比如经过与后台的交流发现一共有45条记录,发现在用户指定每页显示10条记录的情况下,应该显示5个页码标签,而且第5页应该显示最后的5条记录。像这种的情况,首先是要灵活的展现该显示的争取的页码标签。再者,做到灵活,第一,比如单击任何一

页页码号,都可以查询到那一个页面上对应的数据。第二,比如页数为20页,那么不会显示20个页码标签,而显示的是用户所选择的当前页的前后10页的页码标签。如果这时候用户想回到第一页,首选需要单击下图的5。更过分的如果有50页总不能显示50个页码的标签,所以还是需要一定的灵活的措施和判断逻辑来决定当前页面上应该显示的页码标签的起止区间。

9

浪潮优派 订单管理系统(SSH版)技术总结报告

③功能说明:如果单击了50,表示用户要求一页中显示50条记录。可以看到,左侧的序号已经递增到了50,且原先的10多页已经缩减为了4页。这是用户可以自由选择一页内显示多少条记录的选项。

④功能说明:下图是当前页面内现实的记录的记录状态的说明。该区域表示当前页面中显示的是第几条到第几条记录,斜杠右边表示按照您的查询条件数据库中一共有多少条符合要求的记录。

⑤功能说明:下图使用了条件查询和模糊查询功能。用于想要查找名字中含有li的用户,并且联系电话含有130字样,并且是管理人员或者业务人员,状态可以有效或者无效都可以。那么查找出来了一部分符合要求的记录,上图仅仅截图了前四条。

⑥功能说明:再比如查询所有的状态为无效的用户。

10

浪潮优派 订单管理系统(SSH版)技术总结报告

(5)用户增加/编辑画面

①功能说明:下图展现了“新增用户”的界面,空空的输入框等待用户输入信息。其中用户编号是后台自动生成的,用户不需要也不能进行输入。

②功能说明:输入好用户的信息,单击“保存”后,用户的信息会被保存到数据库,保存成功后,弹出对话框告诉用户保存成功。

11

浪潮优派 订单管理系统(SSH版)技术总结报告

③功能说明:“用户编辑”模块使用的是与“新增用户”模块同一样的对话框模板。但是当用户双击一行或者单击一行后面的“编辑”链接的时候弹出的对话框里面的值是会与那一行的数据同步的,这是与空空如也的新增用户模块的区别。

④功能说明:同样,当用户单击“保存”后,更新请求会提交到服务器,更新成功后,前台会有成功的提示。 ⑤功能说明:无论是编辑还是保存,如果用户名没有填写或者写了又删了,那么会有提示,不会提交到服务器。

12

浪潮优派 订单管理系统(SSH版)技术总结报告

(6)客户管理

①功能说明:前面对用户管理界面的各种细致的功能点已经介绍的很详细了,其实客户管理、货币管理和代理商管理与前面介绍的用户管理模块的80%都是相同的。所以,这里进行更简略的说明。下图展现了客户管理的最基本的查询功能,当单击主菜单的客户管理按钮进来的时候,已经默认查找出所有的客户的个人信息供用户进行查看。 ②功能说明:当然,如果用户在默认查询出的全部信息中通过翻页也找不到目标行,那么可以使用支持模糊查询的条件查询功能。比如下图,进行了名字中含有

“sa”并且客户类型是“国网”或者“南网”的所有客户的查询。结果显示两条。最下方的页码和信息状态也是正常的。关于页码,用户管理中展示的很详细,这里不再絮叨。

13

浪潮优派 订单管理系统(SSH版)技术总结报告

(7)客户增加/编辑画面

①功能说明:与用户增加类似,单击新增客户按钮也会弹出一个对话框,对话框中的文本框是空白的等待输入。其中“客户名称”是必须输入的,否则会有提示必须输入的提示框。最后,输入完成,单击保存,当服务器完成保存后前台会有提示。

②功能说明:下图是“客户编辑”模块。首先会把用户要修改的那一个客户的所对Sally修改为了Sally3。当单击保存,服务器会进行保存操作,保存成功后前台会有成功的提示。

在行的值同步到这个编辑对话框中。然后等待用户进一步的修改动作。如下图,

14

浪潮优派 订单管理系统(SSH版)技术总结报告

(8)货币管理

①功能说明:下图是“货币管理”的界面。当用户单击主菜单的货币管理的时候第一眼看到的即如下所展示的画面,已经默认查询出了数据库中的所有货币的信息。当然,测试的时候没有对货币名称列进行唯一性约束,否则也不会有这么多“卢布”充斥眼球。仅为了测试方便使用,真实环境中这种情况不允许。

②功能说明:使用了条件查询功能,查询数据库中所有含有“元”字样的货币信息,并且要求货币的“状态”是“有效”的。

15

浪潮优派 订单管理系统(SSH版)技术总结报告

(9)货币增加/编辑页面

①功能说明:新增货币界面尤为简单,只需要用户填写“货币名称”,默认已经勾选状态为“有效”。然后单击保存即可。与用户和客户的添加类似,“货币名称”必须填写,否则会弹出对话框阻止用户提交。

②功能说明:“货币编辑”界面,也是会把用户想要编辑的那行的值同步到对话框中然后等待用户编辑。单击“保存”后提交到服务器。

16

浪潮优派 订单管理系统(SSH版)技术总结报告

(10)代理商管理

①功能说明:上图展现了代理商界面的查询功能,也是默认查询出系统中所有的代理商的信息。如果找出一个与前面三个实体管理不同的一点,那就是该代理商查询界面中有一个字段是关联到另一个实体的,即“业务员”字段。这个关系是说的,每一个代理商会有一个业务员来负责,唯一对应到一个业务员,该业务员指的是“用户类型”为S的用户。 ②功能说明:下图使用了条件查询,查询出了所有业务员为“Strong”这个人的所有的代理商信息。如上图所示,查询出来了三个,状态有“无效”的也有“有效”的。

③功能说明:如果再只勾选一个无效条件约束进行查询,结果集中就只有两条记录了。那一条状态为有效的已经不符合条件,所以没有查询出带到前台来。

17

浪潮优派 订单管理系统(SSH版)技术总结报告

(11)代理商增加/编辑画面

①功能说明:上图展现了单击“新增代理商”按钮后弹出来的对话框,文本框中等待用户输入。其中代理商编号为后台自动生成,用户不需要也不能进行输入。只需要填写好“代理商名称”和勾选“状态”以及“业务员”的选择。这三项中,代理商名称是必须填写的,不填写不能提交。“状态”默认“有效”。关于业“务员”的选择,相对复杂一些,下页来介绍。 ②功能说明:关于“业务员”的选择,需要单击父对话框的放大镜弹出小对话框如上图所述,可以进行“业务员”的选择。

这个画面的布局和结构的一部分是自己编写的。按照项目模板中的代码,如果单击“选择业务员”后弹出的页面中的页码切换或者目标查询,如果提交一个表单请求进行查询,那么肯定会引起页面的刷新,再次来到该页面的时候还要通过javascript代码来还原提交前的页面状态,如还原用户打开对话框的层次,还原用户曾经选择了哪一个业务员等等。或者页码切换和查询请求不使用表单提交和链接跳转,而是用ajax请求,然后把返回的数据通过javascript再组装,数据量较大重新组装比较麻烦。

这里,改变了原项目模板中的前台代码,使用iframe嵌套一个子页面,该子页面即下图所示的选择业务员这一个页面。下图中的业务员选择模块的组件都是放在一个单独的jsp文件中,然后被iframe标签来引用到父页面中。这样做的好处是,依旧可以使用原来的页码切换和查询逻辑,依旧可以使用表单提交和链接跳转来实现数据的更新和页码的切换,但是不需要javascript来还原现场也不需要ajax后的javascript来重新组织数据,因为iframe的刷新只会影响iframe本身,而不会影响父页面的刷新。

上面简单说了一下修改后的实现思路。然后说一下该页面如何操作来实现选

18

浪潮优派 订单管理系统(SSH版)技术总结报告

择业务员的功能,最上面一行蓝色字提示的很清楚,双击一行即代表选择一个业务员,然后业务员的信息会显示在最下面的那个单行的表格中,代表用户的选择。当然,用户这时候如果再双击另一行,那么这一个单行表格也会跟着更新。而且,提交请求进行页码切换或者目标查询的时候,用户曾经选择的这个业务员信息也不会丢,不仅本页面不会丢,父页面也会依旧记得。因为第一,父页面还是那个父亲,不会刷新的,第二,每双击一行,不仅更新了单行表格中的值呈现给用户,也通知了父页面的javascript中的变量来记忆。单击“完成选择”即可选择一个业务员。父对话框中就会显示用户选择的这一个业务员的名字。

19

浪潮优派 订单管理系统(SSH版)技术总结报告

③功能选择:如下图,新增的代理商是tomcat名称,然后业务员最终在百般纠结之后选择了“superwoman”,即测试用户增加模块的时候增加的那一个用户。这个业务员的文本框是不能编辑的,不能手工输入,所以“superwoman”这几个字符的显示是建立在单击放大镜按钮之后的系统进行业务员查询后的罗列和用户坐在电脑前双击一行进行选择的基础上生成的。单击保存后,保存该代理商到系统,保存成功后,前台会有提示。

20

浪潮优派 订单管理系统(SSH版)技术总结报告

4.3该系统如何工作。

以上结合系统中已做模块的截图对系统的功能层面进行了简要又罗嗦但是必要的说明,看到了系统的外观和它能完成怎样的工作。下面我想对系统内部是如何工作的进行叙述。也是因为在本设计报告模板的第五部分,对于“技术总结”每个部分的标题都很有较强针对性,不太方便单独拿出一块去做这样的叙述,故在这里拿出一块来进行系统内部工作的叙述。

这里直接结合一个具体的例子来叙述一下这个请求响应在客户端和服务器走过了怎样的路线。那就以上边刚写完的“代理商查询”为例,趁热打铁,叙述一下这个流程。下面开始步入正题。

首先,客户端在主菜单界面单击大大的“代理商管理”按钮即可进入这一个功能,回忆前面的功能叙述,用户应该看到的应该是系统默认查询出的第一页的全部代理商的信息。别着急,这个结果的出现还是需要经过一些周折的。下面从用户单击动作开始。下图展现了前台的按钮界面,以及界面背后的代码的真实面目。 从代码可以看出,用户单击按钮后,页面会跳转,跳转的目的地在location猜测一下参数的含义,getAll代表是查询,first代表是第一次查询,第一次的意思是指是主菜单上单击“代理商管理”按钮而引起的默认查询,而不是单击分页的查询或条件查询,所以应该清空action中原有的查询参数采用默认查询。上面对参数的含义进行了猜测。实际实现中,就是如此。

现在还是在前台,当单击事件发生,代码执行,发送请求到了后台。那么这些参数是怎么传递的。答案肯定是参数按照名字进行匹配注入到action中对应的同名属性字段上来的。当然,这些属性字段它们都必须有合法的get和set方法。现在登场的这两个简单的参数对应的action中的属性字段的如下图。

21

后面有描述。即跳转到agencyAction.action这一个action,并且传递了参数。

浪潮优派 订单管理系统(SSH版)技术总结报告

action收到请求,通过purpose参数来分流请求,看看是增删改查中的哪一类请求。当然关于分流请求还有其它多种方式,比如使用DMI动态方法调用提交到不同的方法,这里请求种类并不是很多,就采用这种参数分流的方式吧。那么当action知道当前的请求类型是getAll查询请求的时候,就会走查询逻辑,这时候又发现purposeFlag是first,于是知道了这是从主菜单过来的默认查询请求,所以查询参数置空,这个判断和置空的过程,如下图。

之所以查询参数要置空,是因为要抹除那些非默认查询的参数的干扰,可能在拿个环节会有残余。比如上一次查询可能用户刚经过一次由页码切换或者条件查询引起的查询,action中或者url中还保存着陈旧的参数。而当用户从主菜单单击进入代理商查询的时候,这时候执行的查询应该是默认的查询,如果使用这些旧参数进行查询,那查出来的结果会使用户感到茫然,“咦,怎么默认查出来是这个样子,我没做这样的筛选条件啊”。当然,这样未尝不可,在技术层面上

讲无可厚非,但是更好的还是刚进入这一个查询模块的时候采用默认查询方式。

上图可见,参数都已经初始化为默认值。一串变量初始化为空串,当前页面为1,页长为10,且purposeFlag重置为null,那么下一次请求只要不是主菜单过来的便不会走这个重置参数为默认值的逻辑了,因为purposeFlag不会再为first了。

废话少说,其实也没少说多少,来步入关键的查询逻辑。查询逻辑这里不是

22

浪潮优派 订单管理系统(SSH版)技术总结报告

简单地一个getAll()方法的调用就拍屁股走人,因为这样的getAll()获得全部的逻辑太简单了,这一层调下一层,最后的hql才仅仅是个“from Agency”。虽然这里是默认查询全部,但是在前面的功能讲解中也有看到查询界面中,还有分页查询、排序查询、条件查询等一系列复杂查询,这些查询是会有更多的参数进行控制和约束的。那么为了代码的高复用性和低耦合性,所以使用了相同的查询逻辑。这也是为什么上一段说的查询参数重置为默认值的原因,置空即处理,处理了当然就要用,怎么用,就是把这些参数往公共查询逻辑里面塞,为了代码的复用,只能牺牲上述的参数重置默认值的逻辑来契合公共逻辑的胃口了。那么下图就展现了这些参数的使用和业务逻辑层查询逻辑的代码的调用。 为了不引起介绍逻辑上的混乱,还是以默认值的方式进行默认查询为例,后面再叙述分页查询、排序查询和条件查询的实现,那时候这些参数可能就是千奇百怪,这个后面再说,反正都是调用的相同的逻辑。

看到上图中的三大部分,被绿色注释分隔开。第一部分,按照默认条件去查询了代理商的结果集,虽然是所有的,但是也必须遵守默认条件,默认条件即默认参数,虽然是默认参数但是分页参数却是有值的,分页参数就决定了查询的结果是从第1条开始,长度为10,所以这一句话返回的应该是前10个代理商的结果集给List类型的agencies。既然说到了这个功能,就把它的实现展开说完,下面将叙述代码走到这里是如何一层层调用下去的。它的实现需要调用Service业务逻辑处理层的方法,该方法的方法体如下图。

23

浪潮优派 订单管理系统(SSH版)技术总结报告

业务逻辑层的逻辑主要分为两部分。第一部分,对参数的初步处理,第二部分,调用dao层访问数据库。

首先第一部分,对参数进行初步处理,这里调用了一个方法,由于方法体较长,就不截图了,但是逻辑很简单,一说就明白。这个函数的主要功能就是根据传过来的参数来组装一个hql的雏形,比如采用默认参数的,组装回来的hql就是“from Agency a and 1=1 and 1=1 and 1=1”,意思不就是没有筛选条件的情况下查询所有的记录嘛。因为传给了这个函数有三个参数即分别是name、username和isValid,且每一个都在action中被置为空串,那么经过判断,默认查询全部,为了代码的可复用性,所以这里都被翻译为了“1=1”永真,即传过来的这些条件不起到筛选作用。

不过这样说可能有些故意和矫情,感觉“1=1”加不加都行。那么再举一个有参数的例子,别混乱,这里我们还是说的无参数的情况,只不过借用一个有参数的例子来说明这个hql构造函数的逻辑的可靠性和复用性。如果传入name为“Bob”,传入username为“Tom”,传入isValid为“T”,那么返回的hql的样子是怎样的呢?这里的三个参数肯定是起到筛选作用了,肯定不能再是坑爹的“1=1”了。而且这三个参数的筛选条件还应该同时满足,即“and”来

连接。那么答案就是“from Agancy a and a.name like ’%Bob%’ and a.user.username like ‘%Tom%’ and a.isValid=’T’”,显而易见,原本的“1=1”被三个筛选条件替换了。与事先的分析一样,筛选参数起作用了,并且用“and”来连接。这也是为什么会有“1=1”,既然这个恒等式可有可无为

24

浪潮优派 订单管理系统(SSH版)技术总结报告

什么还要出现三次,这是因为“and”的原因。无论传过来的参数是不是默认,该函数接受了三个参数name、username和isValid,那么无论怎样,首先两个“and”摆上,如果参数不是默认那么让你起到筛选作用填到“and”的空里,如果是默认值筛选参数不起作用,那么就是向“and”的空里填入“1=1”。么?不填行不行?那两个“and”在一起肯定出错了。当然,也可以用别的逻辑判断和处理来避免这种情况,比如一看是默认值“and”也不加这是完全可以的,这完全取决于if是怎样来摆放以及每句筛选子句是自带“and”还是经过其它判断的。不多说了,总结一句。逻辑上实现很简单,只需要根据传递的每个参数是不是默认值,来组装为不同的hql的where字句中的筛选子句,通过if-else和字符串处理函数很容易来实现。

继续接着前一段说。那么第一句话调用函数返回了一个hql子句的雏形,之所以是雏形,是还不能直接用,还需要经过再处理和再加工。因为刚才调用的函数在之前也说好了是可以复用的,那么也就是说用于返回结果集也行、进行统计数目也行。所以,正式因为这样,该函数中才只去影响了where子句,只组装了查询和统计两个部分所能共用hql子句,试想能公用的不就是where嘛。查询结果集还需要order by和设置分页,而统计总数不需要这两块,然后统计总数还需要select count(x.id),查询结果集通过hql的“from Agancy...”就已经可以方便的得到List这样的结果集了,select加上多此一举除非单独选字段或其它类型的查询,这里也没这个必要。所以,这些求同存异出来的“异”就在调用该函数后的自己的模块中进行了再处理和再加工。

所谓的再处理和再加工,在目前叙述的这个例子中,如上图所述,所做的工作是去检查是否有排序的要求,如果有那么组装排序order by语句缀在hql雏形后面,如果没有,就不加order by子句采用数据库默认的顺序。到这里hql就组装完了,再调用dao层的逻辑来访问数据库,把组装好的hql和分页参数传给dao即可。从这一对叙述中,似乎感到了Service层重要的责任和较复杂

25

浪潮优派 订单管理系统(SSH版)技术总结报告

的逻辑,起到一个承上启下和中流砥柱的作用。如果没有它,action会显得臃肿或者dao会显得太操心。

各司其职,Service把处理好的hql和分页参数给dao之后,dao就只负责进行数据库的查询了。通过上一句的叙述,我们可以想到有hql和分页参数,那么不就是用hql实例化一个Query实例,然后为它setFirstResult()和setMaxResults()嘛,似乎无论是代理商还是用户还是客户或者货币的查询来了,dao层的代码都是这样的嘛。这正是由于Service的存在和辛勤工作导致了dao层逻辑瞬间清晰了很多。所以这里调用的dao的查询函数虽然是写的agencyDao的,但是其实是agencyDao和所有实体类Dao的父类BaseDao中的函数,核心代码如下。 通过泛型,让其可以支持更多不同的实体类的查询。反正都是给hql和分页参数的查询,仅仅只是实体类不一样罢了,一个泛型就可以解决的问题嘛。从上图可以看出,真实的代码与上一段分析的一致,实例化Query然后设置分页参数,调用list()方法进行查询,返回一个List。到这里,dao层的任务就已经结束了。本来嘛,dao层就是做数据访问用的,数据访问就是那么单纯,说到底就是连接好数据库然后通过hql进行交流,然而使用了HibernateTemplate或者是使用原生的Hibernate接口,似乎连接的过程也不用程序员去操心了。然后dao层把这个查询出来的结果集返回给Service,再返回给action。这样关于默认的查询的元素部分的查询就结束了,action的agencies属性字段中就保存了从第1条记录开始10条记录的这些Agency代理商实体,这就是默认查询查询出来的结果集的结果。说完了,松了一口气,但是这仅仅只是结果集的查询。回想action中的代码截图,似乎还有两块逻辑没有说。action中的下一个逻辑如下图。

那么下面,还要进行的第二部分的工作,那就是总数的查询。既然都查出前10个元素来了,给用户看不就完了,为么还要查总数。这是因为前台分页和显示当前已查到的记录状态的需要。如果不知道总记录数,只知道页长10,当前

26

浪潮优派 订单管理系统(SSH版)技术总结报告

页是1,又怎么能知道一共应该显示多少个页码标签给用户,用户又怎能单击写有页号的页码标签来实现分页查询。所以,查询总数还是必须的。Action中进行这一个操作的代码就是上面那幅图片中所述的代码了。

依旧调用了Service层的逻辑,但是查询总数传入的参数似乎比查询结果集的参数简单多了。来,再看一下查询结果集时使用的参数。

对比很明显,查询总数只需要代理商名字、业务员名字和状态,这些似乎都只是where子句中的,我们意想,应该就是符合where子句中的这些筛选条件的代理商会被一个个统计计数。然而在查询结果集的时候,还传入了分页和排序相关的,这影响到了用户将会看到哪一个记录区间的记录和以怎样的顺序去看。而统计总数不需要排序不需要分页,排序了也不影响结果,分页了那还哪是总数。讲明白这一点,就知道为什么传这些参数。那么下面调用Service层逻辑,与查询结果集的时候类似,只不过参数少了,如下图。

同样,调用刚才查询结果集的时候调用过的组装参数为hql雏形的方法。上面也有提到该方法之所以只组装where子句,是为了复用,如果该方法中又涉及了排序子句的组装,那么对于统计总数的hql是无法进行复用的。上图中,理所当然的,把属于where子句参数的三个参数传到方法中,返回一个hql雏形。至于雏形啥样,针对是否是默认参数有不同的样子,叙述结果集查询的时候已经进行了很详尽的解析了。

好了,hql雏形有了。对比查询结果集时候的再加工,那时是对order by的加工,那么在统计总数这里不需要order by,排序了也没用,但是需要的聚集函数来统计总数,所以这一模块的再加工就是在hql雏形前面加上”select count(a.id)”,即以id的数目来统计总数,否则不用count聚集函数怎能返回总数。然后调用dao层查询逻辑进行查询,同样,这一个方法也是AgencyDao

27

浪潮优派 订单管理系统(SSH版)技术总结报告

从BaseDao继承而来的,BaseDao中规定了进行数据库访问中一些共用的模块和方法。调用到的这个函数,可以想到的是实例化一个Query,然后调用uniqueResult()返回一个long类型的总数就可以了。当然也可以直接使用hibernateTemplate的find来返回一个List,不过再转比较麻烦。于是实际代码中使用了前者,如下图。

然后得到的long类型的总数count返回给Service返回给action。于是action就知道了当前应该传送给客户哪一些数据,也知道了一共有多少条数据从而前台可以参考它来做分页。action中最后做的,就是根据记录总条数和当前页长,来计算出了页码号的总数,前台直接拿到这个数据进行页码的显示就好了。哎,action还真是操心。如图。

计算分页总数,即记录总条数除以每页中的记录数得出double类型值再向上取整。比如recordCount为11代表一共有11条记录,pageSize为5代表每页显示5条记录,除法运算之后得2.2,如果分页为2页的话,那么第1-10条肯定没问题,但是第11条肯定显示不出来,所以应该ceil()向上取整得3才是最后的答案。

到了这里,服务器端的处理就结束了,知道了结果集,知道了分页参数。然后转向agencyMaster字符串指向的逻辑视图即agencyMaster.jsp。jsp页面中的展现牵扯到了一些前台的部分,毕竟本次项目的主要精力放在后台,给的原始项目模板中前台也编写的差不多了。这样,下面对前台使用struts2标签进行数据展现的部分和分页部分的前台实现进行一些必要的说明。

首先说,对于结果集的展现。服务器勤苦的按照默认参数查询出了第一条记录开始,前十条记录的结果集,或者说干脆说第一页的结果集,前台这时候需要展现服务器的辛勤工作的成果了。如何展现,强有力的方式就是通过struts2的标签,然后使用ognl访问action的值栈。action中的agencies结果集和计算出来的分页数据都在值栈中。如下图,使用s:iterator标签进行遍历。

然后把每一次遍历得到的当前的那个Agency实例放到var指向的agency

28

浪潮优派 订单管理系统(SSH版)技术总结报告

变量中,再一个个抽取它的属性值进行展现。展现输出即使用了s:property标签,然后用s:if标签进行了一些判断以进行参数的必要转换,毕竟有的参数是面向数据库的为了好存储,但是前台不一定好看,即用户不一定很快的看懂,比如数据库寸的isValid为“T”,意思是“有效”,那么前台应该显示哪个?当然现实“有效”了,显示“T”还以为是“Transformer”变形金刚来。所以,有些转换还是很有必要的。如下图,同时展现了这两种标签的作用。 当然那一块s:if的也可以使用s:property并在其中使用三元运算符一个问号一个叹号来决定是显示“有效”还是“无效”,但是该字段中还有一个icon图标,那样做不太方便,所以使用了s:if标签来进行判断以显示不同的状态。

以上说明了结果集的遍历和展现,action中还有分页属性。对于分页的相关属性,前台的处理方式如下。

这一个引入的jsp文件的说明就到此为止。从而知道了from区间左端点和to区间右端点,那么下面只需要把from-to这个左闭右闭区间内的这pageSize个数字给遍历出来,然后以有好的形式展现给用户。这一个展现的过程就是上面使用Counter类的s:bean的标签和标签体内的过程。s:iterator标签不具有jstl

29

浪潮优派 订单管理系统(SSH版)技术总结报告

中的c:foreach标签那样指定好var、begin、end、step就能实现for循环的功能。所以这样的遍历需要借助struts的Counter类,这一点也是刚学到的。关于学习过程和遇到的问题会在后面的异常及问题描述中很详细的提到,关于Counter类的学习和探索过程是学习的同时进行记录的,不是在报告书写的时候才补充的。

使用了Counter,知道了起止点,那么通过s:iterator来每次拿去一个实体数来进行前台展现的组织就可以了。当当前数与当前页号吻合的时候,给与一个特别的标记。简单的遍历的过程如下图所示。 到这里,关于用户单击主菜单上的代理商管理,然后服务器进行默认参数查询,然后把查询结果放到action值栈,直到最后jsp中通过struts标签的遍历就絮叨完了。

下面再补充一点,就是用户如何操作,从而产生了分页查询、排序查询和条

件查询。再一次絮叨之前,声明两点。第一,后台代码是完全一模一样的复用,故后台不做介绍;第二,前台注册事件和事件处理涉及太多前台,不做介绍。那么要介绍的就是javascript得到参数后如何向服务器发送请求的这个过程。

发送请求的方式有很多,熟悉的表单可以一个form写上然后指定好action,或者单击一个链接就看到了一条新闻的这样的习以为常却又视而不见的链接跳转,或者富互联网应该用兴起所应运而生的ajax或者等等。在项目的原始模板中,使用的form不多,使用ctrl+F查询agencyMaster.jsp中的form一个也没查到。关于分页查询、排序查询和条件查询使用ajax的话太费劲,回来的数据需要用javascript做调整,做微调还可以,但是一旦变换条件回来的数据可能是天翻地覆的区别,这种调整太费劲,虽然使用ajax可以很好地保存页面上用户曾经的输入值,因为页面都不会刷新。那么这里,如果使用链接跳转的方式,第一,不用写form,第二,可以在每次重新加载页面的时候使用struts的s:iterator和s:property方便的展现数据从而也不用ajax后使用javascript来小题大做的费劲调整。所以,项目中这一块的请求还是统一使用的链接跳转。

30

浪潮优派 订单管理系统(SSH版)技术总结报告

如何获得各各参数的过程不看,只看把怎样的值以怎样的形式传给了谁。如图。 请求还是发给了agencyAction.action,目的purpose参数还是getAll查询。然后是一对关于分页查询、排序查询和条件查询的7个参数。之所以一下子传过去所有的参数,而不是说用户只进行了排序那么传排序参数,只进行了分页那么传分页参数。之所以这么做,是因为第一,再一次的查询需要遵循用户上一次的喜好,比如用户查询了业务员是Bob或者含有Bob的记录,然后这时候又想按照代理商名字排序,然后又想勾选出所有状态为有效的信息,这样的操作不是一次查询请求完成的,而是用户根据这一次的返回结果来进行喜好的补充。所以,这一些历史的查询条件应该被记住,被记住的过程很简单,这里絮叨一下。第一次喜好传过去,存在了action的属性中,直到查询出数据回来还在这个界面,action就不会消亡,值栈中还是有那些喜好的,然后通过s:property标签遍历出这些喜好,赋值给javascript的变量,比如上图中的name、username以及isValid等。然后再次发送请求的时候,无论这些变量的值改变否,都能有效的把旧的但是不过时的喜好和用户新添加的喜好传给action进行下一次的查询请求。好了,该说第二点了。什么?你找不到“第一”那两个字了,嗯,就在上面,说了太多,仔细找找。那么第二点就是因为代码的复用性,之前的段落中通过好长一段的叙述,可以看到后台代码的简洁性和高度复用性,即使是默认查询也要把参数赋值为默认还要费劲的穿进去还要组装成1=1的hql的where子句,原因就在这里,为了高复用性和低耦合性。高复用性和理解,意思是谁都可以用,说的是共性,低耦合性是因为查询逻辑不与某一类特定的查询有关,比如不与单独的分页查询有关不与默认的从主菜单迁移过去的默认查询有关,屏蔽了个性,求同存异。继续说上面的javascript代码,也就最后一句话没说了。调用windows.location.href=location让页面重新跳转到location指定的界面,这

31

浪潮优派 订单管理系统(SSH版)技术总结报告

一句执行以后即向服务器发送了一个请求。优点已经从上面的叙述中说到了,弊端是所有的参数都在url后面,如果是密码什么的很不合适,再一个就是乱码问题,url中的中文乱码不能通过request的setCharacterEncoding()来作用。这个在后面的异常叙述中也会进行很详细的描述。到这里,请求发送给了服务器,然后服务器重复与前面叙述了的一样的逻辑进行查询,返回值,前台再以同样的方式进行遍历,就是这么简单,就是这么无聊。

关于具体例子的叙述就到这里,结合了代理商管理进行了详细的叙述。当然,每个环节的实现都有更优秀的太多方法和方式。这里叙述的大概只是一天内能够想到的最笨的一种方法。

32

浪潮优派 订单管理系统(SSH版)技术总结报告

5、技术总结。

5.1、说明对系统逻辑结构设计的理解。

关于逻辑结构的设计无非就是上述三层和已经叙述太多遍的MVC。 但是还是要认真地再简单的说一遍这个逻辑结构这块的东西。结合第四部分

最后叙述的例子和上图,表示层就是agencyMaster.jsp和AgencyAction这两个成员,业务层就是AgencyService,持久层就是AgencyDao和BaseDao。前台jsp发送请求给action,action作为控制器,调用Service业务逻辑处理层进行业务处理和逻辑处理,然后Service层再调用Dao数据访问层进行数据库访问取得数据。返回给Service,返回给action,然后action根据返回的结果转向不同的逻辑视图,从而转向不同的物理视图。物理视图比如一个jsp页面中可以通过ognl来访问action值栈的数据,来取出值来进行展示。

5.2、个人所开发的模块涉及到的数据库表及对表中数据所进行的处理说明。

33

浪潮优派 订单管理系统(SSH版)技术总结报告

上图展现了该项目中完成的11个子模块中所涉及到的四个表。其中m_agency的一个外键fk引用到m_user的主键pk,表示一个代理商由一名特定的业务员来负责。虽然看上去只有这么一个关联,用户表和货币表似乎很独立。其实这四个表的联系最终是在订单表中可以观察到的,因为项目中没有做订单模块,没有建立表,不方便展现。引用项目模板中的文档中的ER图可以更直观的表述。

左侧是用户表、客户表、代理商表和货币表,中间一个订单表,四个外键引

34

浪潮优派 订单管理系统(SSH版)技术总结报告

出分别指向了这四个表,使他们产生了联系。表示一个订单,对应一个客户对应一个业务员对应一个代理商对应一种货币。换句更通俗的话就是,代理商把货物卖给哪一个客户,使用哪一种货币进行结算,这个过程中有一个唯一的业务员负责。其它右边的三个表,分别是订单明细表、回款表和发票表,已经完成的项目模块中没有涉及到。

5.3、个人所开发的模块中主要有哪些处理过程会发生异常。

35

浪潮优派 订单管理系统(SSH版)技术总结报告

(1)关于java编译环境版本的不统一问题: 解决: ①properties-Java Build Path ②properties-Java Compiler

③properties-MyEclipse-Project Facets

(2)javascript中字符串类型的var,求它的字符串长度的方法是: 即使用length属性,而绝对不能使用length()方法。 这一点不同于java。否则会报错:

36

浪潮优派 订单管理系统(SSH版)技术总结报告

(3)使用struts2的标签判断字符串的时候,如下使用导致页面中什么都没有输出,即本应该输出管理、财政、业务中的一个词,却什么也没有:

原因:这样的字符串比较的写法不被页面所正确解析。 解决:

交换其中双引号和单引号的位置,即双引号永远用来表示字符串常量,特殊需要的时候test可以使用单引号来引出。 于是结果表现为: ■此外,s:property等标签中可以使用字符串处理函数,如图方式使用即可: 以及

37

浪潮优派 订单管理系统(SSH版)技术总结报告

(4) 异常: ①表象:②根源: 原因: 插入的数据的长度超过字段定义的最大长度值。

比如,原本对m_currency表的name列定义是varchar(15),但是前台传过来的值是: 8个汉字,至少varchar(16)才能存下,所以,报处理截断异常。 解决:

◆方案一:前台使用js来控制输入长度,把异常的苗头扼杀在前台的摇篮; 优点:更加友好,减轻服务器负担;

缺点:与数据库定义耦合太大,表定义修改会引起前台js的修改。 ◆方案二:后台捕捉该异常,捕捉到后提示前台长度非法重新输入。

优点:与数据库定义耦合减小,表定义随便改,只要长度非法,抛出异常捕获提示就是了;

缺点:用户认为可以自由输入,却返回异常信息,友好度下降,增加服务器负担。 ◆折中方案:前台向后台先ajax询问长度,再使用js控制。

38

浪潮优派 订单管理系统(SSH版)技术总结报告

5.4、SVN工具的安装和使用。 (1)必备的安装软件 svn服务器:

svn- 1.4.6-setup.exe

SVNService.exe //配置svn服务自动运行 svn客户端:

TortoiseSVN-1.4.8.12137-win32-svn-1.4.6.msi

LanguagePack-1.4.8.12137-win32-zh_CN.exe //SVN客户端中文包 (2)安装服务器

运行svn-1.4.6-setup.exe,指定软件的安装路径,不断点击next就成功了。 (3)安装客户端 ①运行

TortoiseSVN-1.4.8.12137-win32-svn-1.4.6.msi,直接运行

TortoiseSVN安装文件,无须特殊设置,安装完毕后会提示你重启。客户端机器只需要安装TortoiseSVN。服务器上安装客户端是为了方便配置;

②运行LanguagePack-1.4.8.12137-win32-zh_CN.exe,安装中文语言包,不但是TortoiseSVN软件界面变成中文,帮助文档也是中文的。强烈推荐安装,查看帮助很方便;

③安装完成后在鼠标右键菜单中出现了TortoiseSVN项,进入“设置”菜单,选择语言“中文”。

(4)Eclipse中安装svn插件

①打开eclipse -> Help ->Install New Software选项, 点击Add按钮 ②根据需要,添加自己需要的版本svn控制器的版本,填写name和url,点击ok。

Name: Subclipse 1.6.x (Eclipse 3.2+) URL: http://subclipse.tigris.org/update_1.6.x ③点击Select All,全选后点击next

④选中接受license复选框,点击Finish,开始安装

⑤安装完成后重启eclipse,打开Window->Preference,在左侧类别区,打开“team”,如有“svn”的选项表示已安装成功。

39

浪潮优派 订单管理系统(SSH版)技术总结报告

5.5、用Hibernate访问Oracle数据库主要用到哪些类?

使用Hibernate的API访问数据库使用到了Configuration、SessionFactory、Session、Transaction以及Query接口。这五个是比较重要的,以下例子中展现了前四个接口的用法,并有注释做详细说明,说明了这些接口的作用。由于项目中使用的是Spring的HibernateTemplate来访问的数据库,所以以下代码来自于以前某次的作业。 项目中使用了spring对hibernate的封装的形式来访问数据库,除了使用到的HibernateTemplate,此外还有HibernateDaoSupport类。这两个类都是对hibernate访问DB的过程做了轻量级的封装。比如hibernate存储一个stu对象,必须SessionFactory、Session再save。而使用spring的HibernateTemplate只需要一个save就可以了。访问更加简单了,否则spring也不会闲着没事去封装个hibernate的,程序员也就没必要学spring对hibernate的整合了。applicationContext.xml是spring的配置文件,包含了对spring在当前项目中的所有叙述。但是默认这个文件中是没有对hibernate的叙述的,不知道hibernate自然不知道数据库的存在。所以,该文件中需要写那一堆类似hibernate配置文件中的xml代码,整合了伟大的hibernate,从而可以使用spring对hibernate封装后的很好用的方法了。当然,也可以在ssh整合的项目中单独hibernate.cfg.xml,只不过访问数据库依旧通过hibernate的标准格式而已。spring中整合hibernate只是为了可以使用spring的方式来

40

浪潮优派 订单管理系统(SSH版)技术总结报告

访问数据库,底层还是hibernate。

还有最后一个Query接口没有说明,这个接口在本项目中使用的还是很多的。既然有了HibernateTemplate为什么还需要Hibernate的原生接口Query。因为才疏学浅的自己不知道用hibernate的find()方法去查询后怎么分页。而Query的接口有setFirstResult()和setMaxResults()两个方法很方便很优雅的实现分页。项目中分页查询太多了,下面Dao曾德这一段代码作为例子。

上例子即项目中的源代码,使用泛型以适应各种不同的实体类,改代码放在BaseDao基类中,任何继承该类的Dao层子类都享有该方法,从而可以为不同的实体类提供方便的分页查询。通过一个hql来实例化一个Query,然后通过上述两个方法来设置分页。此外,还有setParameter()方法来设置参数以及其它的一些用法。

对于Hibernate访问数据库的核心接口结合例子就叙述到这里。

41

浪潮优派 订单管理系统(SSH版)技术总结报告

5.6、开发过程中遇到的技术难点及解决办法。

(1)struts2的 ①引出:

言归正传,在struts2标签中,为了达到遍历1-10的目的,可以使用以下代码: counter: 即需要使用辅助类org.apache.struts2.util.Counter来实现该功能。

②应用案例:

可以应用的场合比如页码的遍历输出,知道总页数,要从1开始遍历生成总页数个页码号,单纯的

42

浪潮优派 订单管理系统(SSH版)技术总结报告

③注意点和深入:

I发现问题:需要十分注意的一点是current的取值,看以下的例子:

初始值first赋值为1,last赋值为totalPageCount,totalPageCount值在后台值栈中是8。本应该在界面输出1-8。但是在界面上的输出确是:

显然每个值都比原先的值大了1。

所以,应该注意的是在iterator中访问counter的成员变量current的时候,访问当前值的方式应该current-1而不是current。

II原因解析:

从原码的debug调试分析中得出。

首先,调用setFirst()和setLast()设置区间值,注意区间值是左闭右闭的。 每显式数。

这样访问一个数,都会调用一系列的函数。经

过调试,发现是hasNext()->next()->getNext()->getCurrent()这四个函

43

浪潮优派 订单管理系统(SSH版)技术总结报告

故原因就是“调用顺序”造成了“取值的错觉”:

弄清楚是这么一个调用顺序,首先主控调用hasNext()判断有没有下一个,然后主控再调用next(),next()内部再自己显式调用getNext()取值。

这一个过程的产生是因为

,即该Counter类实现了

iterator接口,从而规定必须实现两个方法即hasNext()和next(),iterator迭代器每次访问都必须经过getNext()和next()的调用过程。换言之,只要用iterator标签,每一次循环中这两个方法都会被先后调用,因为这是规范。 然而后来通过

显式访问current的时候,调用到了getCurrent()

引发的,其次要知道是与

方法。首先要知道这个调用过程是由hasNext()和next()过程相互独立的。

那么就很容易明白,其实这时候已经是在iterator标签自动调用了hasNext()和next()之后了,本次

只负责调用getCurrent()。然而要注意

了,当调用getCurrent()之前,在已经被实现调用的next()方法中,调用了getNext()。该getNext()方法从源码截图可知current已经加上了一个interval增量即

,换言之next()结束以后current已经物是人为的加了1了,

所以当再独立调用getCurrent()的时候获得的current已经是加上interval之后的那个current,故从2开始遍历到9了,每次多了1。

44

浪潮优派 订单管理系统(SSH版)技术总结报告

III继续深入:

■那既然如此,不调用current了,因为多此一举调用getCurrent(),何不直接手动访问next(),这样总该可以直接获取当前current了吧(因为next()调用的getNext()的return是加interval之前的current)。那么如下述例子代码: 例1:

但是得出来的结果输出的确是2、4、6、8,脑洞大开。 ■然而,如下代码直接例2:

输出却没有问题。

■最后,再看两个例子。 例3:

结果:例4:

结果:

45

浪潮优派 订单管理系统(SSH版)技术总结报告

IV该问题的产生原因:

例1:显示调用next()输出2、4、6、8,默认这里首先知道

输出1-8正常。

输出的是么,输出的是栈顶元素,何来栈顶元素。其实如

果只写一个空标签,那么它也会工作,默认遍历栈顶元素,由于这时候该标签的外层是counter,所以counter压入栈顶,空标签即遍历的counter。

即使是空标签,也可以通过debug看到访问hasNext()->next()有8次。也就是说,只要写上了,他就会调用hasNext()->next(),栈顶集合有多少个元素就会调用这个过程多少次。而拿回来的那个数值那个元素压入栈顶,即s:iterator标签体本次循环作用域内的栈顶。好了,栈顶出来了,那么

默认输出的就是本次拿回来的那个集合中的元

素了,即那个栈顶元素了。所以,一句话总结,负责每次拿一个并压入栈顶,然后该标签体内部的标签默认输出当前栈顶元素,从而一唱一和,有条不紊的执行“拿回来->输出”这两个相互独立的过程,并产生了1-8输出的正常结果。

例2:再回到那几个错误例子,即显式调用next()导致只输出2、4、6、8的例子。这里再次声明标签的作用,负责拿回,负责输出,从而问一句负责干嘛,答案是负责“拿回并输出”。所以,当显式调用next()的时候实际已经自动放弃了每次iterator自动调用next()拿回来并压入栈顶的那个值,这个值没有碰到合适的结果输出不出来。这时候又显式调用了next(),从而拿到了一个新的值,正好外面套着一个s:property,于是输出。那么很好理解了,默认next()拿出了1,但是没有找到合适的s:property来输出,在栈顶呆着,忽然又显式next(),于是拿出了2,压入了栈顶,正好,显式next()外面套着一个s:property,于是2被输出。从而1人老珠黄无人问津,因为本次循环已经到了尾声,下次循环即将开始。同理,3被默认next()拿出压入栈顶,得不到输出,忽然显式next()导致4被拿出并输出。从而,造成了2、4、6、8的局面。

46

浪潮优派 订单管理系统(SSH版)技术总结报告

例3:最后,说最后的那两个例子。但是需要结合上一段的逻辑分析,在前的那个例子中,输出默认next()取出的1,然后还是在本次循环中又显示调用了next(),于是取出了2然后被输出。然后下一次循环输出了3和4,再下一次输出5和6,再下一次输出7和8。即上一段中没有被输出的1、3、5、7被出现在前面的输出,避免了人老珠黄永不见天日的悲剧。而出现在后面的一句再拿出另一个,并自己输出。这里有两个点,第一反正有hasNext()控制着next()随便调用去吧反正一不会越界二不会乱序(指拿取的过程不会乱序,输出再议),第二出现在头里的默认调用的next()搭配,自己是一伙,每次循环两次调用,不就是这样嘛。

例4:同理,最后那个乱序的例子也是一个很好的佐证,默认next()拿出了1,但是没有被及时输出,而是被拿出了2压入栈顶后输出于是从栈顶移除,这时候处在后面的便输出了栈顶呆着的默认next()时取出的1。所以,只是输出的顺序有变,拿取的顺序还是依旧那么有条不紊。所以导致了2、1、4、3、6、5、8、7的局面。两两是一次循环得到的结果,只不过是先后输出的问题。

47

浪潮优派 订单管理系统(SSH版)技术总结报告

④总结:

通过以上探索过程和原码分析,解决了三个疑惑, 第一first和last的开闭问题即是否包括端值;

第二,为什么在内部使用遍历的时候本应该输出1-8却输出了2-9每次都大1;

第三,建立在问题二探究结果的基础上,为什么显式调用next()即却只输出了偶数。 这三个问题的结论如下三条所述:

I进行first和last设值的时候,注意两头的端点值都是包括的,即左闭右闭区间; II显示访问current即进行输出的时候,得到的当前current总是比你预想的大1,所以输出当前值的正确方式是(前提,当不改变Counter默认值为1的interval属性时);

III知道以上两点Counter类就可以使用了。这一点要说的是作为学习遍历标签时候的一个补充。即使是空标签也会遍历栈顶集合元素,调用“元素个数”次hasNext()和next()方法,而如果其内部仅仅放一个那么会输出刚刚取回的这个元素。取回和输出是两个完全独立的过程。 (2)

让浏览器跳转到另一个页面或者action的js代码,即类似单击超链接时候的情形: 即使用windows.location.href=”htp://www.http://m.wodefanwen.com//”;语句即可。

48

浪潮优派 订单管理系统(SSH版)技术总结报告

(3)struts收到的值是乱码。 现象:

现象是这样的,从前台输入要进行模糊查询的字串,比如要查询“人民币”,输入了“人民”:

单击查询之后,发现了乱码。

首先后台收到的值就已经是乱码了:

然后在struts转向到jsp后使用ognl取得的参数也必定是乱码了: 原因:

原因的结论很简单,但是也引出了一点其它的问题。慢慢看。 ◆Filter的问题:

首先,在该项目中也有配置字符编码过滤器,并且在过滤器的doFilter()方法中使用UTF-8设置了请求的编码。如下图。

但是还是不行,不行的原因是经过追踪发现请求达到后都不走这个doFilter()过滤器,猜测的原因很简单,肯定就是过滤器制定的过滤拦截规则没有拦截到请求。这个规则制定的位置就是web.xml里面的

上图是原来的,发现请求到达后doFilter()都没有走。于是把改为了/*,这个和action的拦截器是一样的拦截规则。于是拦截到了。网上查,没查到权威的解说的区别。我估计是*.*拦截的是必须带.的这种请求,至于有没有后缀无所谓但是请求的url必须带.否则拦不住。而action的拦截规则就更加大众化了,只要是/*,只要你请求本项目(/代表绝对路径/oms/)的任何的url那么action都会拦截到,这比如很多请求中虽然请求到action却都不带action后缀,或者用do后缀,或者用标准的action后缀,反正都能拦截到。所以,问题就出在这里,把*.*替换为/*后请求的url被正常过滤,走了doFilter()。但是乱码依旧没解决。

49

浪潮优派 订单管理系统(SSH版)技术总结报告

◆编解码的方式要针对请求的方式:

走了编码设置逻辑,但是还是乱码。也就是说明了首先编码逻辑走了,但是该逻辑对于这里类型(乱码有太多类型)不起作用。原因是setCharacterEncoding()只对post请求生效,在filter中很标准的解决了post请求的乱码。但是在该项目中,观察每一个请求的地址栏都会发现大多数还是get请求的:

否则也不会再url后面缀这么多参数。如果用表单,可以设置method为post就不会有这样的问题。当然这是前台的一个设计特点,不多说。在该项目中请求的发送是javascript通过window.location.href重定向到action的:

这样,一条线下来,问题就弄明白了。首先通过配置从而过滤器生效,但是编码逻辑不不起作用。继续分析,找到了是因为编码逻辑只针对pos请求有效而项目中发送的请求是get,url中的乱码,使用上述逻辑不起作用。 解决: ◆方案一:

使用str=new String(str.getBytes(),”UTF-8”)对每一个有中文潜力的参数来手工重新编解码。 ◆方案二: 在过滤器中对url统一编解码。 ◆方案三:

对tomcat的配置文件进行修改,保证url中出现了中文都是utf-8进行编解码传输的,目标文件conf/server.xml,加上红框中的那一句即可:

方案一和二的有点保证了移植项目到别的tomcat也不会乱码,因为这两个方案针对项目本身。方案三的优点是设置简单,针对一个tomcat。

50

本文来源:https://www.bwwdw.com/article/mvgp.html

Top