第一章: 项目开发团队以及项目的生命周期
地本章内容说明了建立一个成功的项目从开始到最终的发布的基本要求.一开始需要定义技术架构师(以下简称为架构师)是什么,要做些什么,以及总结出架构师怎样与其他项目组成员协调工作.接下来介绍了在项目的开发进度方面几个可选方法,这仍然是一个不断讨论的话题,由于没有最权威的开发进度的标准导致了许多公司不得不去适应那些杂乱的开发计划.
项目开发团队: 角色和职责
所有的J2EE开发团队都需要开发成员有着广泛的知识从而去充当不同的角色.能让J2EE项目能够得到成功有以下一些角色:
架构师
项目经理
业务分析员
美工设计人员
表现层开发人员
业务逻辑开发人员
数据建模人员
数据库管理员
数据迁移专家
基础设施专家
测试专家
虽然我本书主要是集中在架构师上面,但是我们还是列出了在J2EE开发团队中的其他角色以及他们的职责,并且说明了架构师应该怎么样对项目中的其他角色负责.
一些公司用一些别的名称来表示上述的角度,例如,基础设施专家可能叫系统管理员,测试专家可能叫测试员,还有一些公司可能会区分测试经理和测试员.我们不管你是工作在哪种角色下面,把一个项目开发组分成几个部分能很大的提高J2EE项目开发成功的可能性.
还有,一个人也可能去完成上面多种角色,也有可能多个人去共同完成一个角色所要求的内容(如果你的项目足够大的话),一些公司将架构师和项目经理捆绑在一起做为一个角色,也有高级管理人员即是数据管理员同时也是系统管理员,还有一些开发人员即工作在表现层也工作在业务逻辑层.在这里我并不去建议一个开发团队如何去组织这些,而仅仅是说明哪些是需要的,如何去组织是他们的事了.
架构师
架构师决定在项目中使用的技术.在许多公司,一些技术的选择都是企业级的.例如,一些公司的硬件,操作系统以及软件(J2EE容器提供商)的选择都是企业级的.一般来说,JAVA语言也是一种企业级的选择.
但是,大多数应用程序使用的技术并不在企业级的范畴内。对于所用技术的选择我在企业级和个人应用程序做了一下比较,例如,对于服务器端的编程可能会用企业级的,而XML解释器之类的可能就使用那些独立架构。在许多公司,企业级技术的选择往往导致一部分人与J2EE开发小组分离。
一般来说架构师的主要工作是负责选择使用在项目中的第三方的工具包,例如,架构师可能会根据需要为模板引擎而选择使用Apache的Velocity开源项目。
架构师为项目提出方法学和框架的建议。典型地,架构师是向项目经理提出这些,例如,一般来说这些建议是使用用例来表示分析的结果并且做出原形,还有就是根据对象模型写出设计文档。一些公司将这些方法学定义在企业级的层面上。
架构师要提供全部的设计和应用程序的结构。每个开发人员都会有自已的想法,习惯以及偏爱,这可能会导致开发组内有很大的分歧,架构师要进行协调。
我把架构师的角色比做是管弦乐队的指挥,所有的音乐演奏者对所演出的音乐有不同的理解,指挥就需要发出指示让各演奏者协调工作。
架构师要确保项目已经准备充分。对项目的分析应尽可能的详细和一致,从而能在这个基础之上建立应用程序。典型地,架构师需要与项目经理和业务分析人员一起来定义项目。
架构师确保项目有足够的文档。书写设计文档是一个很关键的步骤,它能很好的建立开发人员之间交流的桥梁。建立文档的过程可以使架构师完全地想清楚整个项目。接下来的事情就是分配开发人员,就不会对架构师的时间分配有任何的影响。对于开发人员来说,文档可以让他们在架构师不在项目组的时候继续工作,而不会去花费开发组其它成员的时间。文档也帮助项目本身不受个人思想的左右。
我见过许多项目是没有开发文档的,这样导致的结果是每增加一个开发人员将成为一个很烦琐的事,因为架构师不得不用口述的方式来向新来的开发者描述设计。口头的传达这些设计如此烦琐使用我们看不到新增加开发人员所带来的任何好处。
架构师建立编码规范。因为每个开发人员都有自已的偏爱,编码标准是一个枢纽,很容易的将各开发人员结合起来。架构师负责为以下主题建立的项目流程和规范如下,在后续将有更多的方面包括进来:
异常处理
日志记录
测试
多线程
架构师决定项目经理所要执得的任务。这个角色对J2EE项目是很重要的,因为J2EE项目比起其它别的项目包涵了更多的技术。除此之外,架构师还帮助项目经理进行项目的计划和评估。
架构师为开发人员解决疑难问题。典型地,架构师比开发人员更有经验。当开发人员遇到技术问题时,通常是架构师帮他们解决。对于很多项目,架构师更多的是一个指导者。
架构师要让开发者强制遵守编码规范。做为编码规范的作者,架构师能很容易的发现没有遵守规范的地方,因此可以合理的进行纠正。项目经理负责这一强制性任务,不过他们通常没有这样的经验去发现这些不规范的地方。
代码复查是很好的强制遵守规范的机制,但如果是其他组的成员来检查另一个组的代码并且是按这个组的编码规范来进行时,将会是很困难的。
代码复查对开发小组的所有成员也是一个很好的学习手段,架构师发现设计漏洞,所有的开发人员将能从这里学习到一些东西。一个团队如果有很有经验的成员,将会推动代码复查。要使用代码复查发挥作用,应该在一个友好的氛围中进行。
架构师协助项目经理估计管理项目的开销以及管理项目带来的好处。虽然通常来说这是项目经理的事,但是大多数对于J2EE技术缺乏经验的项目经理来说可能不能意识到所有需要做的。
架构师协助管理部门做人事决定。人才的选拔通常是看成行政手段,架构师能对人员技术能力做出一个较好的评估,不当的人才选拔会对项目进度造成很大的影响。
项目经理
项目经理的工作是负责计划和调节项目中的所有任务,项目经理也必须与管理部门以及最终用户就当前项目进行交流,还有就是项目经理要获取项目组所需要的资源。
架构师负责向项目经理提供技术建议和指引。架构师协助项目经理确定项目中的任务以及完成的顺序,也帮助项目经理决定项目需要的资源包括项目组成员的选取。
业务分析员
业务分析员负责与最终用户(以下称为客户)交流定义需求。详细的需求对于设计和建立整个应用是很有必要的,因为客户和开发人员的用的是不同的术语,业务分析员的作用就是在他们中间做一个翻译,一般来说业务分析人员都同时具有业务经验和技术的经验。
在控制项目的进度方面,业务人员的作用是很小的。开发人员在编码和测试阶段都会有一些新的问题,业务人员会在业务逻辑的角度回答这些问题。
架构师负责确保由业务人员整理的需求是足够的。业务人员分析出的需求100%正确是不现实的,毕竟业务人员会加入自已的主观想法,分析要彻底才能保证设计的进行。
美工设计人员
许多应用特别是那些公众的应用都需要专业的图形设计人员。许多架构师通过使用自已的开发工具开发出的WEB页面非常难看,而且很难用。图形的设计比科学更具有艺术性,通常,图形开发人员与业务分析人员以及其它业务的图形开发人员一起工作,同时图形开发人员也需要与表现层的开发人员工作建立原型。
架构师负责确保页面设计的技术可行性。我见过一些页面的设计使用了文字的效果,能在文字处理软件中显示,但在HTML里就达不到这种效果,比如,让文字旋转90度,架构师因此需要在早期就发现这些问题,并纠正。
表现层开发人员
表现层开发人员负责所有的HTML,JAVASCRIPT,APPLET/SWING,JSPs以及SERVLET的编码,一般来说,所有与客户界面开交道的地方都是表现层开发人员的工作范围。一般来说与美工设计人员合作,表现层开发人员使用原型开发出工作版本。并且与架构师一起决定开发的结构以及前台导航的设计。
架构师负责确保设计模式可维护可扩展。导航通常比较复杂,会很容易的使代码很难维护。架构师要解决这些问题以及由此引起的新问题。
业务逻辑开发人员
业务逻辑开发人员负责项目应用不可见部分的编码,包括企业Bean,Web Service,RMI Service,CORBA Service,业务对象以及数据访问对象,人们称这些不可见部分为服务器端组件。业务逻辑开发人员通常是JAVA开发专家,与架构师工作的很近并且根据需要帮助架构师调整性能。
架构师向业罗逻辑开发人员提供指导。一般来说问题是出现在服务器端,这通常是最复杂的一部分,架构师往往扮演指导者的角色。
数据建模人员
数据建模人员使用业务分析的结果来定义,分类存放在数据库中的数据,典型的数据建模是将应用中的数据在ER(实体关系)图里表示出来。数据库管理则使用ER图建立物理的数据库设计,由此看来,数据建模人员和数据库管理员一般是合在一起的。
架构师负责确保有足够的数据模型。和业务分析一样,100%完全的建立模型是不现实的,如果数据模型大部分建成并且作为第三方的标准形式,那以后模型的更改是一件很容易的事。
数据库管理员
数据库管理员负责明确描述数据库的设计,并建立和维护数据库应用环境。通常,数据库管理员协助性能的改善以及帮助业务逻辑开发人员通过对数据的访问来诊断应用程序。有些时候数据库管理员同时担任即是业务逻辑开发人员和数据迁移专家两种角色。
架构师与数据库管理员共同解决由数据储存带来的问题。但数据库管理员主要是与数据建模人员和业务逻辑人员进行交流的。
数据迁移专家
有一些应用比如数据仓库,是严重依赖于移植来的资源的。数据迁移专家书写和管理所有的脚本和程序以使数据能够移植到正在进行的项目中去,当一个应用有很少的移植需求的时候,这个角色可能不需或者将这个角色融合到数据库管理员的角色中去。
架构师为数据迁移专家定义迁移需求。并且与数据迁移专家一起工作去解决出现的问题。
基础设施专家
基础设施专家负责提供开发,测试,产品环境以及部署的方式。适当的基础结构能节约开发和部署的时间,这包括管理容器,书写部署描述符,并且通过测试环境协助其他开发人员诊断问题。
架构师为基础设施专家定义需求。架构师与基础设施专家协同工作确定需要环境的数量及其特性,以及这些环境所需要的支持级别,大多数项目需要至少开发,测试以及产品环境。一些公司将架构师与基础设施专家合二为一了。
测试专家
测试专家确保产品是符合文档的要求以及确保没有错误,一般来说,测试专家至少有业务方面的基本知识。
架构师与测试人员一起决定需要的基础设备以及提供支持。项目经理和业务分析通常一起完成测试计划和测试的方法,因此,架构师在测试中提供技术支持。
第二章 定义项目
概述
开发任何应用程序的第一步都是进行分析从而定义出项目的目标以及应用范围,J2EE应用也不例外.在开发进程中对实际应用进行分析是最基本的认识,但我发现很多项目是混乱的,没有首先定义出需要完成的目标.
架构师并不直接的定义项目,这是由项目经理,业务分析员和最终用户来确定.架构师负责确保项目定义是否一致以及是否定义的足够详细以便能进行设计和实现.J2EE开发组的其他成员并不清楚哪些东西是必须用于设计和实现应用的,架构师通常让帮助大家讨论得到较好的项目定义.
架构师必须要有分析的技能.没有分析的技能,架构师就不能意识到项目定义中的一些弱点.缺少分析的项目虽然可以在建立的过程中发现大多数的错误,但这样做付出的代价是昂贵的.
我听到过许多开发人员都在抱怨我所说的,技术人员希望听到更多的是编码技术,而不是项目的定义和分析策略.我完全理解.没有什么比书写出有用的代码更重要的啦,不过要写出良好代码需求有很好的分析和完善的项目定义.根据我的经验,想要得到良好代码而没有进行很好的分析的可能性几乎没有.
用例是很重要的分析工具.UML(统一建模语言)被用于描述,分析,设计面向对象语言所建立的系统,如JAVA语言.主要是建立UML来描述应用系统将要完成的事.这一章定义了用例这个术语,指导你通过建立项目的用例,列出了通常建立用例时会出现的错误以及如何去避免它们,并且讨论了一个项目的用例的例子.
这一章并不是UML规范的用例的完整说明,我只是它的其中的一个子集,这子集是实际工作中要用到的.要看完整的描述,去看看Booch, Rumbaugh, and Jacobson (1999).
一些开发人员区分用例和需求,但是在我看来这没有什么不同.需求是特别的,用商业术语来书写,一个应用必须要提供这些.因此,需求是用例的总结书写形式.
如果你使用极限编程(XP),那你编故事比用例更好,尽管如此,你将仍然发现这章是很有用的. 尽管编故事相对于UML用例能够表现出更强的细粒度,我认为故事和用例的构造在概念上是统一的.主要的不同在于故事的细粒度能让两个程序员在三周内去实现它,而一个程序员要去实现一个用例则通常需要更多的时间.
另外,我喜欢用户界面原型化.原型是一个优秀的设计手段,它能让客户和开发人员了解到项目的目标是什么.我通常让客户对原型感兴趣而没有任何困难,因为它代表了客户需要的一些东西.原型也可以帮助优化用例.
一旦你定义了项目的用例(或故事),你能清楚用商业术语来定义项目的细节,这样开发人员和业务人员都能看懂.可以让客户以及任何的管理层都能尽早的提供反馈信息,在项目中从用例得到形式化的信息能使项目经理更好定义项目范围.
第三章: 定义范围和估算
在大多数的组织里,项目经理与客户和管理层协调工作来建立项目的范围以及估计时间和所需要用到的资源.在这些任务中项目经理经常性地依赖于架构师的帮助.这样的情形在J2EE应用里也不例外.这章的内容是架构师的责任,包括帮助项目经理定义和估计交付.不涉及到此部分的读者可以跳过这一章.
定义范围
客观的根据用例定义项目的范围,并且获得客户的同意.在项目因为时间或者开发组成员士气低落的时候就需要调整项目范围.当客户在开发已经进行后又提出新的需求时,用用例将它们记录下来,然后计划在下一个发布版本中加入它们.通常,为每一个用例进行粗略的估计会得到一些信息,对客户来说这些信息在决定项目范围时很有用.
在用例上得到项目发起人的同意.当用例是用商业术语书写时,它们能被看成是与客户和管理层的"合同"被提交.与客户合作去选择哪些用例需要在当前项目中实现,另外的将被推迟实现.即然项目范围在口头上同意了,也需要书写或以E-MAIL的形式通知与此项目相关的所有人.确保保留一个拷贝.
一旦项目范围确定下来,要坚持不懈的执行.项目经理在项目范围被确定能做的最重要的事就是执行它.架构师有责任提醒项目经理改变项目范围.去击打一个移动的目标是很难的(译注:要改动一个执行中的计划也是很难的).虽然我一般更愿意去为将来的版本计划所有改进,但架构师通常没有这样的计划.我评估需求是按用例的形式来进行,并且提供一个原始的估计.通常,项目经理使用这些信息来指引客户去计划今后版本的需求.
第四章 设计外部应用接口
概述
将J2EE应用与外部应用进行通讯是很正常的. 例如,一个购买应该程序可能会通知一个所有购买交易的帐务应用程序,或者一个库存管理系统可能会通知一个所有货物的入库和出库的记帐应用程序. 架构师负责设计程序接口以及应用本身. 本章将详细的介绍如何定义外部的应用程序接口,让你能设计和实现这样的任务.
如果J2EE应用的初始化是在外部应用程序中进行时,你应该确定这些应用放在用例中进行分析,就像在第二章中讨论的那样. 所有在J2EE应用与外部应用之间信息传送的细节都应当成为这些用例的核心. 例如, 购买系统通知帐务系统中,所有的订单的存放应该是用例的核心.
对于外部应用程序的用例, 应该确定使用这些接口要触发的事件,以及每一个事件需要传递进去的信息.例如,考虑以下的用例:
库存系统将通知帐务系统所有的入库记录.
通知在入库记录时发生.
通知将包括每一笔入库的商家ID及时间戳, as well as the UPC, 以及出货的数量
来自帐务系统的确认.
定义和文档化外部接口以便双方的开发人员能有一个对象模型基础(在第六章中说明).架构师在两个系统需要建立一个基础以便进行建模和实现.还有,项目经理需要制定相应的合同来分配两边开发人员的各自的责任.
以下几方面应该与外部开发人员讨论并在团队之间达成共识:
交流方式
数据结构
触发事件(数据交换)
错误处理以及职责
架构师应该推动接口设计的讨论. 尽可能让讨论接近上述的话题.内部的应用设计与本次讨论毫不相干.与外部应用程序使用到的平台相关,因为它将影响到使用它提供的一些通信方法的J2EE应用程序的性能.For example, if the external application is not written in Java, the J2EE application cannot use any form of communication that uses RMI, such as enterprise beans.
jsp开发中的 监听器和过滤器,这两个东西要说起来,很大,很繁。今天我想用一个现实的例子,来说明这两个东东的作用。
案例描述
假设你平时生活在北京,有天你去一个很边远很贫穷的山村体验生活,到了那个山村后,你想要什么东西,村长都会同意,然后安排村民送给你,但是有一点例外的是,一旦你问他们要钱,村长坚决不给,还会带领村民把你打回北京去。
你为了从村民那里带走尽量多的东西,所以你在出发前准备了一个大袋子,希望把村民给的东西都放到袋子里;然后你在回到城市后,这个袋子就没用了,你就把这个袋子给扔了。
代码模拟
恩,我想用如下简单代码来模拟以上案例,大家可以想一下监听器扮演者什么角色?过滤器扮演什么角色?
1. 新建一个java web project;
2. 默认的index.jsp页面上加上两个链接,模拟要村民要红薯和要钱,如下:
Html代码
1. <html>
2. <body>
3. <h1>欢迎掉入无名村,哈哈</h1><br>
4. <a href="hongshu.jsp">给红薯</a><br/>
5. <a href="money.jsp">给钱</a><br/>
6. </body>
7. </html>
<html>
<body>
<h1>欢迎掉入无名村,哈哈</h1><br>
<a href="hongshu.jsp">给红薯</a><br/>
<a href="money.jsp">给钱</a><br/>
</body>
</html>
3. 新建一个大布袋类:BigBag.java,如下:
Java代码
1. package chb.demo.web;
2.
3. import java.util.HashMap;
4.
5. public class BigBag ...{
6. public static HashMap<String, Integer> bag;//村民的东西就放在这个袋子里
7. }
package chb.demo.web;
import java.util.HashMap;
public class BigBag ...{
public static HashMap<String, Integer> bag;//村民的东西就放在这个袋子里
}
4. 新建一个监听器:MyListener.java,如下:
Java代码
1. package chb.demo.web;
2.
3. import java.util.HashMap;
4.
5. import javax.servlet.ServletContextEvent;
6. import javax.servlet.ServletContextListener;
7.
8. public class MyListener implements ServletContextListener...{
9.
10. public void contextDestroyed(ServletContextEvent arg0) ...{
11. BigBag.bag = null;
12. System.out.println("回城了,让掉烂袋子吧");
13. }
14.
15. public void contextInitialized(ServletContextEvent arg0) ...{
16. BigBag.bag = new HashMap<String, Integer>();
17. System.out.println("要进山了,准备好布袋子");
18. }
19.
20. }
package chb.demo.web;
import java.util.HashMap;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyListener implements ServletContextListener...{
public void contextDestroyed(ServletContextEvent arg0) ...{
BigBag.bag = null;
System.out.println("回城了,让掉烂袋子吧");
}
public void contextInitialized(ServletContextEvent arg0) ...{
BigBag.bag = new HashMap<String, Integer>();
System.out.println("要进山了,准备好布袋子");
}
}
5. 再新建一个过滤器:MyFilter.java,如下:
Java代码
1.
2. package chb.demo.web;
3.
4. import java.io.IOException;
5.
6. import javax.servlet.Filter;
7. import javax.servlet.FilterChain;
8. import javax.servlet.FilterConfig;
9. import javax.servlet.ServletException;
10. import javax.servlet.ServletRequest;
11. import javax.servlet.ServletResponse;
12. import javax.servlet.http.HttpServletRequest;
13. import javax.servlet.http.HttpServletResponse;
14.
15. public class MyFilter implements Filter ...{
16.
17. public void destroy() ...{
18.
19. }
20.
21. public void doFilter(ServletRequest request, ServletResponse response,
22. FilterChain chain) throws IOException, ServletException ...{
23. HttpServletRequest req = (HttpServletRequest)request;
24. HttpServletResponse rsp = (HttpServletResponse)response;
25. if(req.getRequestURI().toString().endsWith("money.jsp"))...{
26. //什么时候有过钱啊?打回北京去
27. rsp.sendRedirect("gohome.jsp");
28. System.out.println("什么时候有过钱啊,滚回家去!");
29. return;
30. }else if(req.getRequestURI().toString().endsWith("hongshu.jsp"))...{
31. if(BigBag.bag.containsKey("hongshu"))...{
32. 1;
33. BigBag.bag.put("hongshu", num);
34. System.out.println("得到第"+num+"个红薯");
35. }else...{
36. 1);
37. System.out.println("得到第1个红薯");
38. }
39. //村长同意了,下面就是到村民家中拿了。。
40. chain.doFilter(request, response);
41. }else...{
42. //只要不要钱就行,放行
43. chain.doFilter(request, response);
44. }
45. }
46.
47. public void init(FilterConfig filterConfig) throws ServletException ...{
48.
49. }
50.
51. }
package chb.demo.web;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyFilter implements Filter ...{
public void destroy() ...{
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException ...{
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse rsp = (HttpServletResponse)response;
if(req.getRequestURI().toString().endsWith("money.jsp"))...{
//什么时候有过钱啊?打回北京去
rsp.sendRedirect("gohome.jsp");
System.out.println("什么时候有过钱啊,滚回家去!");
return;
}else if(req.getRequestURI().toString().endsWith("hongshu.jsp"))...{
if(BigBag.bag.containsKey("hongshu"))...{
int num = BigBag.bag.get("hongshu")+1;
BigBag.bag.put("hongshu", num);
System.out.println("得到第"+num+"个红薯");
}else...{
BigBag.bag.put("hongshu", 1);
System.out.println("得到第1个红薯");
}
//村长同意了,下面就是到村民家中拿了。。
chain.doFilter(request, response);
}else...{
//只要不要钱就行,放行
chain.doFilter(request, response);
}
}
public void init(FilterConfig filterConfig) throws ServletException ...{
}
}
6. 在web.xml中配置监听器和过滤器,如下所示:
Xml代码
1.
2. <?xml version="1.0" encoding="UTF-8"?>
3. <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5. xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
6. http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
7.
8. <filter>
9. <filter-name>myFilter</filter-name>
10. <filter-class>chb.demo.web.MyFilter</filter-class>
11. </filter>
12.
13. <filter-mapping>
14. <filter-name>myFilter</filter-name>
15. <url-pattern>/*</url-pattern>
16. </filter-mapping>
17.
18. <listener>
19. <listener-class>chb.demo.web.MyListener</listener-class>
20. </listener>
21. <welcome-file-list>
22. <welcome-file>index.jsp</welcome-file>
23. </welcome-file-list>
24. </web-app>
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<filter>
<filter-name>myFilter</filter-name>
<filter-class>chb.demo.web.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>chb.demo.web.MyListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
好了,大功告成,部署运行,观察结果怎样?
分析结果
我们会观察到如下结果:
当服务启动过程中,控制台输出“要进山了,准备好布袋子”
当点击一下“给红薯”的链接时,控制台会输出“得到第x个红薯”
当点击一下“给钱”的链接时,控制台输出“什么时候有过钱啊,滚回家去!”
怎么样?有点明白了?
其实,监听器就是在服务启动之前做好保证服务正常运行的准备工作,服务结束的时候再善后一下;对应案例中的情况,就是在上路之前把袋子准备好,回来了把袋子扔掉;类似保姆的工作,照顾孩子起床,再照顾孩子睡下。
过滤器就是针对服务器的请求,加以过滤限制;对应案例中的情况,就是村长了,掌握着生杀大权,什么事都得过我这关才行