Java2004.12
J2EE Web框架汇总
E M A G . C S D N . N E T
创刊
号Struts 最佳实践J2EE模板技术对话:Web技术的思考
本期推荐
eMagJavaI’m loving it
James Gosling, Inventor of Java Technology
James Gosling, Inventor of Java Technology
篇首寄语
带着满身的风尘,笔者从上海回到了北京。这时,我们的第一期Java电子杂志也即将面世了。
在刚刚落幕的BEA eWorld 2004大会上,笔者有幸见到了BEA的多位技术专家和高层管理人士。作为CSDN的代表,我向他们提出了同一个问题:对中国软件开发者的印象如何?中国程序员还有什么差距?有趣的是,他们无一例外地告诉笔者:中国程序员的技术能力和聪明才智令人折服;但中国程序员最大的缺陷在于经验和交流。
是 的 , 经 验 和 交 流 , 这是 我 们 最 欠 缺 的 。 据 统 计 ,北美的职业程序员平均年龄为37.5岁,而中国大约为26岁。相比之下,我们少了10年的实践经验,所以我们的技术、我们的软件行业才显得那么稚嫩,那么不成熟。而年轻往往又同时伴随着气盛,所以我们才看到了那么多的争吵、那么多的浮躁。
为了弥补10年的差距,我们只能寄望于更多、更主动的交流。只有大家都能把自己的实践经验和知识积累与所有同行分享,我们才有可能更快地
提升水平,才有可能逐渐摆脱位于产业链末端的尴尬境地。CSDN电子杂志正是希望为所有中国程序员提供一个交流的平台,我们的Java电子杂志也正是思想激烈碰撞的产物。也许它还有很多不足之处,但我们毕竟上路了。
作为一个职业编辑,看着Java电子杂志的几位业余编辑制作这份杂志是件饶有兴味的事情。我本可以给他们更多的帮助,但我更愿意给他们少许建议,然后看着他们自己摸索前进。其实不仅这份杂志,我们大家的技术之路、职业之路又何尝不是在不断的挫折和迂回中摸索前进?所以,希望每个看到这份电子杂志的人都尽可能地参与进来,不管是提出意见和建议、写作投稿、或是加入编辑团队,甚至哪怕只是给我们一些精神上的鼓励,请你参与进来,让这份杂志也能在我们所有人的交流与协作中不断进步。
希望我们的Java电子杂志会越来越好。希望我们每个人的前途都会越来越好。
透明
2004年12月18日凌晨·北京
主编的话
主办官方站点
主编责任编辑
站点支持
CSDN.NEThttp://www.csdn.net
透明awaysrainanderstotodojerrykey大阿福剑神一笑枯木
emag.csdn.com
CSDN eMag项目Java电子杂志
目 录
版权声明本刊所有文章版权属作者所有,未经允许,不得以任何方式转载、复制或翻印。CSDN和eMag标示为csdn.net所拥有。
9.Web技术史话
15.Servlet规范简介
20.Struts最佳实践
27.Java Web 框架汇总
39.Java 模板技术
44.Rich Client 技术简介
48.对话:Web技术的思考
49.J P e t S t o r e 项 目 分 析 : 一 个 典 型 的 J 2 E E 应 用Web层的实现
关于我们Introduce
熊节 [ 透明 ]CSDN网站Java频道主编,J2EE架构师,Java程序员
联系方式 [email protected]个人Blog http://gigix.blogdriver.com擅长的技术领域 Java编程和系统架构个人作品 翻译《最后期限》、《重构》、《软件工艺》、《与熊共舞》等书
林仪明[anders]翼华科技(厦门)有限公司
联系方式 [email protected]擅长的技术领域 Java Web应用开发/.Net Web应用开发
张凯峰[大阿福]IT技术爱好者,现就职于国内一大型软件公司
联系方式 QQ:1836566 擅长的技术领域 J2SE个人作品 《谁动了我的电脑》主编
陈玉泉[枯木 cyqxyz]普通程序员,喜编程,常乐此不疲;好读书,却不求甚解,故但凡神交于各方名士,仅有小得,而无大成;胸存点墨,而心无大志,惟愿宁静淡泊而已。
联系方式 [email protected] [email protected]个人Blog http://cyqxyz.china-pub.com擅长的技术领域 J2SE、Servlet、JSP。
从事企业应用,有3年的开发经历联系方式 [email protected]
擅长的技术领域 j2ee相关。
石红军[awaysrain,绝对零度]csdn Web开发大版主,scjp,熟悉web开发技术,对java有浓厚的兴趣。曾有三年的统计行业软件经验,最近在一家软件公司做电子商务方面的工作。
联系方式:[email protected],[email protected]个人Blog:http://blog.csdn.net/awaysrain擅长的技术领域:web开发
戴悦大连灵溢信息咨询有限公司技术部负责人。
联系方式:[email protected]擅长的技术领域:精通JAVA网络程序开发,了解J2EE,了解Struts,精通Mysql、SQL Server数据库
Introduce
关于我们
全新组合 首次亮相希望可以得到您的支持和鼓励。也欢迎您加入到我们的队伍
中来,共同开创我们自己的技术天地
张礼[totodo]
潘坤[剑神一笑]联系方式 [email protected]
好书推荐
The Java Developers Guide to
Eclipse 原版进口
The Java Developers Guide to
Eclipse
链接:http://www.dearbook.
com.cn/book/viewbook.
aspx?pno=TS0026898
作者:Shavor, Sherry/
D’Anjou, Jim/ Fairbrother,
Scott/ Kehn, Dan/ Kellerman,
John/ McCarthy, Pat
出版社:Pearson
ISBN书号:0321159640
Core J2EE Patterns, Second Edition 原版进口 Core J2EE Patterns, Second Edition链接:http://www.dearbook.com.cn/book/viewbook.aspx?pno=TS0026897
作者:Alur, Deepak/ Malks, Dan/ Crupi, John 出版社:Pearson原出版社: Pearson EducationISBN书号:0131422464
Java编程艺术 原书名:The Art of Java链接:http://www.dearbook.com.cn/book/viewbook.aspx?pno=TS0027258&indexPageForward=1作者:[美]Herbert Schildt, James Holmes译者:邓劲生出版社:清华大学出版社版别版次:2004年9第1版第1次印刷ISBN书号:7-302-09054-8
Web技术史话Web技术史话
1. 荒芜年代 1990-1992
1990年,HTML标记的出现。
这标志着Web开发时代的到来,B/S模型开始在之后的岁月中,不断的发展壮大,攻城略地蚕食着传统C/S的领域。
如同所有的新生事物一样,在web的史前岁月,web的开发技术在html标记诞生后,无论是在服务端还是客户端都缓慢的发展着。在相当长的一个时间内,它并未象今天这样辉煌。甚至于只是静态的文本标识。
关键字:HTML。
技术特性:静态文本显示, 表现力和交互能力不足。
2. 帝国时代和封建诸侯 1993-1996
1 9 9 3 年 , N C S A 提 出 了
荒芜年代 1990-1992 帝国时代和封建诸侯 1993-1996 工业文明 1996-1999 多元文化 2000-2004 新技术革命——XML及其相关 大事记
CGI1.0草案。
web开发终于迎来了它的第二次重大飞跃。伴随着CGI,带 来 w e b 的 动 态 处 理 能 力 。CGI就是这个时代的国王。这个国度下的人们编写着一个个CGI程序,把标准输入重定向为http请求,处理请求数据,把标准输入重定向http响应。C语言一度统治了这个世界。
关键字:CGI。
技术特性:实现了客户端和服务端的动态交互,在程序代码写html标记,面向过程的开发方式,应用程序,多进程运行。
C 语 言 的 帝 国 在 短 暂 的辉煌后,迅速灭亡。1994年Rasmus Lerdorf举起PHP大旗打响革命第一战,1995年第一个用Perl写得程序诞生,很快,Perl在CGI编程领域的风头就盖过了它的前辈C语言。随后,Python等著名的脚本语言也
陆续加入了CGI编程语言的行列。1996年微软带来了ASP。
关键字:脚本语言。
技术特性:脚本代码和页面显示混合,面向过程,解释程序,线程运行,调试困难。
3. 工业文明 1996-1999
1 9 9 7 年 , S u n 公 司 推 出Servlet规范。Java阵营终于迎来了自己的web英雄。1998年,JSP技术诞生。Servlet和JSP的组合(还可以加上JavaBean技术)让Java开发者同时拥有了类 似 C G I 程 序 的 集 中 处 理 功能和类似PHP的HTML嵌入功能,此外,Java的运行时编译技术也大大提高了Servlet和JSP的执行效率
关键字:servlet,jsp。
技术特性:代码和页面显示混合,线程运行,运行时编译。
Anders*小明
热情Passion
JAVA M A G A Z I N E _ F R O M _ C S D N . N E T
Web技术史话
1998年, Sun发布了EJB 1.0标准。1999年,Sun正式发布了J2EE的第一个版本。紧接着,遵循J2EE标准,为企业级应用提供支撑平台的各类应用服务软件争先恐后地涌现了出来。(同时2001年微软发布ASP.NET技术)
关键字:企业开发。
技术特性:Web开发从简单的信息展示变成企业电子商务应用,融入了系统开发中。开发视角从单一的页面转向全站管
理;开发的关注点也从单一页面实
现转向系统功能实现,页面只是作
为用户交互的表现层存在系统中,
依然重要但已经不是唯一的关注
点。
客户端技术:
1 9 9 6年 和 1 9 9 7年 是 客 户 端
技术的重要年份,在这两年中
产生影响深远的技术:W3C提出
了CSS的建议标准;Netscape推出 自 己 的 h t m l 扩 展 , 支 持
JavaApplets和JavaScript,同
时引入了QuickTime插件;IE支
持 DHTML和ActiveX控件的功能;
Macromedia推出Flash。
关键字:动态交互。
技术特性:在比与服务器动态
交互晚了多年后,客户端自身与用
户交互能力得到大大加强。不仅如
此,applet和activex(特别是微
软xmlhttp)以及后来的mozilla的
xmlhttprequest更带来了客户端从
被动与服务端交互到具有主动交互
能力。
5. 多元文化 2000-2004
2001年,Struts1.0发布。
Web开发模型从Model 1 发展为
Model 2(MVC)。
无论是开始的CGI,还是后来
的ASP,PHP甚至是Servlet都是页
面控制器模式,每个页面都知道
本页面所需数据控制对象。基于
Servlet发展了一批前端控制器模型
系统,所有请求都转发到一个统一
的控制程序上,经过处理后把数据
转发到某个特定页面上,Java下的
Struts,Spring MVC和Turbine等都
是代表。
MVC的发展也演变出了——推
技术与拉技术。
以Struts,Spring MVC和Turbine为代表的是推技术。而
Tapestry和微软的ASP.NET则带来
了拉技术。
伴随着推技术和拉技术的发
展,在视图技术发展出不同方向:
推技术的需求下,模板技术
开始红火地发展起来,Velocity和JDynamic就是的代表。无论是
ASP下的模板技术,PHP模板技术
还是新的开发模型的引入包括前端
控制器和新页面控制器,都使用同
一处理技术——占位符动态替换技
术。模板引擎在解释模板时通过占
位别名在内存查找对应的数据,并
替换到视图中。当然,不同模板技
术,其模板语言是和其对模板文件
的处理都是不一样的。
而 在 拉 技 术 的 带 领 下 , 引
入Web开发组件技术。Java下的
Tapestry框架,JetSpeed所代表的
Portal和Sun公司大力推广的JSF,
以及微软的ASP.Net都大力发展了
Web Component的开发模型。
客户端技术:
2000年, Mozilla发布XUL0.6;
2003年微软提出XAML。
关键字:rich client
技术特性:以DHTML、CSS和
ActiveX、Applet所代表的Thin Client技术交互能力毕竟有限,人们
需要更丰富的交互能力,这催生了
Rich Client技术——利用XML描述
客户端界面,而不仅仅是HTML或DHTML;同时引入新的交互方式
Web Services。Mozilla下XUL技术
以及微软的XAML都是代表。
6. 新 技 术 革 命 ——XML及其相关
1998年,W3C正式发布了XML 1.0标准。XML语言对信息的格式
和表达方法做了最大程度的规范,
应用软件可以按照统一的方式处理
所有XML信息。
1 9 9 9 年 , W 3 C 制 定 出 了
XSLT标准,用以将XML信息转
换为HTML等不同的信息展现形
式;同年其研究小组提出了RDF(Resource Description Framework)标准草案。RDF在XML语法的基础
上,规定了元数据的存储结构和相
关的技术标准。
2000年, W3C发布 SOAP(Simple Object Access Protocol)协
议的1.1版。2001年,W3C发布了
WSDL(Web Services Description Language)协议的1.1版。SOAP协议和WSDL协议共同构成了Web Service的基础。
2 0 0 1年 , W 3 C又 开 始 着 手
制定OWL(OWL Web Ontology Language)标准。OWL语言是一
种基于XML的语言,它比RDF更加深入、细致地描述信息内容。
2003年,W3C成立了语义化Web Service研究小组(Semantic Web Services Interest Group),研究在
Web Service中加入语义技术的相
关问题。 2004年 2月,W3C宣布
专业Professional
JAVA M A G A Z I N E _ F R O M _ C S D N . N E T
RDF和OWL标准正式成为W3C的建
议方案。
7. 大事记
1990 HTML标记出现
1993年CGI 1.0的标准草案
由NCSA提出
1994年Rasmus Lerdorf发
明了专用于Web服务端编程的
PHP语言
1995年Java诞生
1995年NCSA开始制定CGI
1.1标准
1995年第一个用Perl写成的
CGI程序问世
1996年Netscape 2.0版中支
持JavaApplets和JavaScript
1996年W3C提出了CSS的建
议标准,同年,IE 3.0引入了
对CSS的支持
1996年W3C在SGML语言的基
础上,提出了XML语言草案
1997年Microsoft发布了IE
4.0,支持 DHTML
1 9 9 6年插件开发方式开
始 风 靡 了 浏 览 器 的 世 界 。
Netscape 2.0引入了对
QuickTime插件的支持, 同年
IE 3.0正式支持在HTML页面中
插入ActiveX控件的功能
1996年Macromedia收购了
FutureWave,并将Jonathan
Gay的发明改名为Flash
1996年Microsoft借鉴PHP的
思想,在其Web服务器IIS 3.0中
引入了ASP技术
1997年CGI1.2也被纳入了议
事日程
1997年Servlet技术问世
1998年JSP技术诞生
1998年W3C正式发布了XML
1.0标准
2 0 0 0年W 3 C发布S O A P(
Simple Object Access
Protocol)协议的1.1版
2001年ASP.NET技术诞生
Web技术史话
技术Technology
JAVAM A G A Z I N E _ F R O M _ C S D N . N E T
Servlet规范简介awaysrain
web框架是如何注入到Servlet中的
Web框架一般是通过一个Servlet提供统一的请求入口,将指定的资源映射到这个servlet,在这个servlet中进行框架的初始化配置,访问Web页面中的数据,进行逻辑处理后,将结果数据与的表现层相融合并展现给用户。WEB框架想要在符合Servlet规范的容器中运行,同样也要符合Servlet规范。
将一个WEB框架注入到一个servlet中,主要涉及到Servlet规范中以下部分: 部署描述符 映射请求到Servlet Servlet生存周期 请求分发
部署描述符
部署描述符就是位于WEB应用程序的/WEB-INF目录下的web.xml的XML文件,是WEB应用程序不可分割的部分,管理着WEB应用程序的配置。部署描述符在应用程序开发人员,应用程序组装人员,应用程序部署人员之间传递WEB应用程序的元素和配置信息。
在WEB应用程序的部署描描述符中以下类型的配置和部署信息是所有的servlet容器必须支持的:
ServletContext初始化参数 Session配置 Servlet声明 Servlet映射 应用程序生存周期监听器 Filter的定义和映射 MIME类型的映射 欢迎文件列表 错误文件列表出现在部署描述符中的安全信息可以不被支持,
除非这个Servlet容器是J2EE规范实现的一部分。所 有 正 确 的 W E B应 用 程 序 部 署 描 述 符
(Servlet2.3规范)必须包含下面的DOCTYPE声明:<!DOCTYPE web-app PUBLIC “-//Sun Microsystems, Inc.//DTD WebApplication 2.3//EN” “http://java.sun.com/dtd/web-app_2_3.dtd”>
下面说明在部署描述符中是如何进行Servlet声明和映射的,这个DTD的全部内容可以在下面这个地址获得:
http://java.sun.com/dtd/web-app_2_3.dtd在这个DTD中有关Servlet声明和映射和映射的部
分如下:
Servlet规范简介
<!--The servlet element contains the declarative data of aservlet. If a jsp-file is specified and the load-on-startup elementis present, then the JSP should be precompiled and loaded.Used in: web-app--><!ELEMENT servlet (icon?, servlet-name, display-name?, description?,(servlet-class|jsp-file), init-param*, load-on-startup?, runas?,security-role-ref*)><!--The servlet-class element contains the fully qualified class nameof the servlet.Used in: servlet--><!ELEMENT servlet-class (#PCDATA)><!--The servlet-mapping element defines a mapping between a servletand a url patternUsed in: web-app--><!ELEMENT servlet-mapping (servlet-name, url-pattern)><!--The servlet-name element contains the canonical name of theservlet. Each servlet name is unique within the web application.Used in: filter-mapping, servlet, servlet-mapping--><!ELEMENT servlet-name (#PCDATA)>
根据以上DTD,一个典型的Servlet的声明的格式如下:<servlet><servlet-name>catalog</servlet-name><servlet-class>com.mycorp.CatalogServlet</servlet-class><init-param><param-name>catalog</param-name><param-value>Spring</param-value></init-param></servlet>
一个典型的Servlet映射如下:<servlet-mapping><servlet-name>catalog</servlet-name><url-pattern>/catalog/*</url-pattern></servlet-mapping>
通过上面的方法,我们就声明了一个名称为
catalog的Servlet,它的实现类为com.mycorp.CatalogServlet,并且带有一个catalog参数,参数值为Spring,所有向/catalog/*的请求都被映射到名称为catalog的Servlet。
映射请求到Servlet接收到一个请求后,WEB容器要确定转到哪一个
WEB应用程序。被选择的应用程序的最长的上下文路径必须和请求的URL开始部分匹配。URL匹配的部分是映射到Servlet的上下文路径。
WEB容器下一步必须按照下面的程序定位处理请求的Servlet。
用来映射到Servlet的路径是请求对象的URL减去上下文的路径。下面的URL路径映射规则按顺序执行,容器选择第一个成功的匹配并且不在进行下一个匹配: 容器试着对请求的路径和Servlet的路径
进行精确匹配,如果匹配成功则选择这个Servlet。
容器会循环的去试着匹配最长的路径前缀:把’/’当作路径分隔符,按照路径树逐级递减的完成,选择最长匹配的Servlet。
如果这个URL路径的最后有扩展名(比如.jsp),Servlet容器会试着匹配处理这个扩展名的Servlet。
如果前面的没有与前面三条规则相匹配的Servlet,容器会试着为资源请求提供适当的资源,如果有“默认”的Servlet定义给这个应用程序,那么这个Servlet会被使用。
容器必须使用一个大小写敏感的匹配方式。在部署描述符中,用下面的语法定义映射: 一个以’/’开始并且以’/*’结束的字符
串用来映射路径。 一个以’*.’为前缀的字符串用来映射扩展
名。 一个只包含’/’的字符串指示着这个应用
程序“默认”的Servlet,在这种情况下,servlet的路径是请求的URI减去上下文路径,并且这个路径是null。
所有其他的字符只用来精确匹配。如果容器内置JSP容器,那么*.jsp被映射到
这个容器,并允许JSP页面在需要的时候被执行。这种映射叫做隐含映射。如果WEB应用程序中定义了*.jsp的映射,那么这个映射有比隐含映射高的优先级。
WEB容器允许显式的声明隐含映射以获得优先级,例如,*.shtml的隐含映射可以在服务器上被映射为包含功能。
映射实例:path pattern servlet/foo/bar/* servlet1/baz/* servlet2/catalog servlet3*.bop servlet4
Servlet规范简介
下面是实际请求映射的结果incoming path servlet handling request/foo/bar/index.html
servlet1
/foo/bar/index.bop
servlet1
/baz servlet2/baz/index.html servlet2/catalog servlet3/catalog/index.html
“default” servlet
/catalog/racecar.bop
servlet4
/index.bop servlet4
请注意/catalog/index.html 和/catalog/racecar.bop这两种情况,因为是精确匹配,所以并没有映射到处理/catalog的servlet。
Servlet生存周期
在介绍Servlet的生存周期之前需要先介绍一下javax.servlet.Servlet接口。所有的Servlet必须实现或者间接实现这个接口,我们通常可以通过继承javax.servlet.GenericServlet或者javax.servlet.http.HttpServlet.类来实现这个接口。
这个接口中定义了下面5种方法:public void init(ServletConfig config);public ServletConfig getServletConfig();public void service(ServletRequest req,
ServletResponse res);public String getServletInfo();public void destroy() ;
init()方法
init方法在容器器装入Servlet 时执行,Servlet容器在实例化后只调用一次init方法, init方法必须在servlet接收到任何请求之前完成。这个方法通常用来进行一些资源的管理和初始化,如从配置文件读取配置数据,读取初始化参数,初始化缓冲迟等一次性的操作。
getServletConfig()方法
GetServletConfig方法返回一个 ServletConfig 对象,该对象用来返回这个Servlet的初始化信息和启动参数。返回的是传递到init方法ServletConfig。
Service()方法
Service方法是应用程序逻辑的进入点,是servlet方法的核心,WEB容器调用这个方法来响应进入的请求,只有servlet成功被init()方法初始化后,Service方法才会被调用。
getServletInfo()方法
这个方法返回一个字符串对象,提供有关servlet 的信息,如作者、版本等。
destroy()方法
destroy方法在容器移除Servlet 时执行,同样只执行一次。这个方法会在所有的线程的service()方法执行完成或者超时后执行,调用这个方法后,容器不会再调用这个servlet的方法,也就是说容器不再把请求发送给这个Servlet。 这个方法给servlet释放占用的资源的机会,通常用来执行一些清理任务。
这个接口定义了初始化一个servlet,服务请求和从容器中移除servlet的方法。他们按照下面的顺序执行:1. servlet被实例化后,用init方法进行初始化2. 客户端的任何请求都调用service方法3. servlet被移除服务,调用destroy方法销毁
servlet的生存周期如下图:
请求分发
请求分发可以让一个Servlet把请求分配到另外一个资源,RequestDispatcher接口提供了实现他的机制。可以通过下面两种方式从ServletContext中获得一个实现了RequestDispatcher接口的对象:
• getRequestDispatcher• getNamedDispatchergetRequestDispatcher方法接受一个指向目标资源的
URL路径RequestDispatcher rd = getServletContext().
getRequestDispatcher(“/catalog”);
getNamedDispatcher方法接受一个Servlet名称参数,这个名称是在部署描述符中<servlet-name>元素指定的那个名称。
RequestDispatcher rd = getServletContext().getNamedDispatcher (“catalog”);
RequestDispatcher接口有两个方法,允许你在调用的servlet完成初步处理后把请求响应分配到另外一个资源,
forward()方法:public void forward(ServletRequest request,
ServletReponse reponse) throws SwerletException,IOException
forward方法上让你把请求转发到另外的Servlet或者jsp或者html等资源,由这个资源接下来负责响应。如:
RequestDispatcher rd = getServletContext().getRequestDispatcher(“/catalog”);
rd. forward(request,response);
include()方法:public void include (ServletRequest request,
ServletReponse reponse) throws SwerletException,IOException
include方法让你的Servlet响应中包含另外一个资源
Servlet规范简介
生成内容RequestDispatcher rd = getServletContext().
getRequestDispatcher(“/catalog”);rd. include(request,response);
结合WebWork的具体分析
WebWork是由OpenSymphony组织开发实现MVC模式的J2EE Web框架。在介绍完servlet规范的相关内容后,我们看看WebWork是如何注入到一个Servlet中的,假设我们有一个上下文环境为“/WebWorkdDemo”的WEB应用。
部署描述符
在部署描述符中,我们需要进行如下配置:<servlet>
<servlet-name>webwork</servlet-name><servlet-class>com.opensymphony.w e b w o r k . d i s p a t c h e r .ServletDispatcher</servlet-class>
</servlet>……<servlet-mapping>
<servlet-name>webwork</servlet-name><url-pattern>*.action</url-pattern>
</servlet-mapping>
我们声明了一个名为webwork的Servlet和*.action到这个Servlet的映射,这个Servlet就是webwork中的controller,担任MVC框架中非常重要的控制器角色。
映射请求到Servlet在XWork的配置文件xwork.xml中有如下片段:
<action name=”demo” class=” webworkapp.DemoAction”> <result name=”success” type=”dispatcher”> <param name=”location”>/demo.jsp</param> </result></action>
这 样 我 们 由 h t t p : / / l o c a l h o s t : 8 0 8 0 /WebWorkDemo/demo.action这个URL向服务器发出请求时,WEB容器首先确定转到哪一个WEB应用程序,容器将请求URL和上下文环境进行匹配后知道将转到/WebWorkdDemo这个WEB应用。
接下来容器会在 /WebWorkdDemo这个应用的部署描述符中进行查找处理这个请求的servlet,根据后缀*.action找到名称为webwork这个Servlet,这样根据部署描述符,这个请求被映射到webwork中
的controller组件com.opensymphony.webwork.dispatcher.ServletDispatcher来处理。这个担任控制器组件的Servlet在他的service()方法中在根据请求的路径解析出对应的action来进行处理。
通过上面的的处理,实现了将web请求转到了webwork中的控制器ServletDispatcher。不止是webwork,实现MVC的web框架都需要进行类似的处理来
将web请求转入到自己的controller.以便进行进一步的处理。
Servlet生存周期
ServletDispatcher这个Servlet的存周期可以如下:
1) 在 服 务 器 启 动 的 时 候 , 容 器 首 先 实 例化ServletDispatcher
2) 实例化完成后,将调用init()方法,在init方法中执行了以下操作:
初始化Velocity引擎 检查是否支持配置文件重新载入功能。
如果支持,每个request请求都将重新装载xwork.xml配置文件,在开发时非常方便。
设置一些文件上传的信息,比如:上传临时目录,上传的最大字节等。
3) 每次请求都调用 s e r v i c e()方法 ,在service方法中执行了以下方法
通过request请求取得action的命名空间 根据servlet请求的Path,解析出要调用该请
求的Action的名字(actionName) 创建Action上下文(extraContext),
遍历HttpServletRequest、HttpSession、ServletContext 中的数据,并将其复制到Webwork的Map实现中,至此之后,所有数据操作均在此Map结构中进行,从而将内部结构与Servlet API相分离。
以 上 述 信 息 作 为 参 数 , 调用A c t i o n P r o x y F a c t o r y 创 建 对 应 的ActionProxy实例。ActionProxyFactory 将根据Xwork 配置文件(xwork.xml)中的设定,创建 ActionProxy实例,ActionProxy中包含了Action的配置信息(包括Action名称,对应实现类等等)。
执行proxy的execute()方法4) 容器移除Servlet 时执行destroy(),在
ServletDispatcher这个Servlet中并没有重写destroy方法,在移除Servlet时,将什么也不做。
请求分发
WebWork提供了多种活灵活视图展现方式,例如还是我们上面在xwork.xml中的配置:
Servlet规范简介
<action name=”demo” class=” webworkapp.DemoAction”> <result name=”success” type=”dispatcher”> <param name=”location”>/demo.jsp</param> </result></action>
根 据 以 上 配 置 当 D e m o A c t i o n 的 返 回 值为"success"时的处理类型为"dispatcher",当result的type为"dispatcher"时,通过javax.servlet.RequestDispatcher的forward()或include()方法将处理结果和表现层融合后展现给用户
我们可以看看WebWork提供的dispatcher类型Result Type的实现类 com.opensymphony .webwork.dispatcher.ServletDispatcherResult中的代码片断:
HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation); if (dispatcher == null) { response.sendError(404, “result ‘” + finalLocation + “’ not found”); return; } if (!response.isCommitted() && (request.getAttribute(“javax.servlet.include.servlet_path”) == null)) { request.setAttribute(“webwork.view_uri”, finalLocation); request.setAttribute(“webwork.request_uri”, request.getRequestURI()); dispatcher.forward(request, response); } else { dispatcher.include(request, response); }
S e r v l e t D i s p a t c h e r R e s u l t 类 的 从ServletActionContex中得到HttpServletRequest和HttpServletResponse,然后调用request.getRequestDispatcher(finalLocation)方法得到一个RequestDispatcher实例,如果返回的是null,则输出404页面未找到的错误,否则将调用dispatcher.forward(request, response)或者dispatcher.include(request,w response)进行请求分发,将处理结果和表现层融合后展现给用户。
结束语
通过以上的介绍,我们对web框架是如何注入到servlet中有了简单的了解,如果想更深入的研究,可以阅读Servlet规范以及一些成熟框架的源码。
Servlet规范简介
CSDN社区电子杂志项目是全社区的项目,Java电子杂志只有依靠大家的支持,才能够发展下去。因此,现在面向全社区征集Java电子杂志第二期的相关稿件。
本启事主要描述了Java杂志第二期的主题以及稿件内容要求。这是我们的近期目标。我们同时还给出了一些备选主题,供大家选择。这是我们的长期目标。围绕着这些主题的文章,会陆续出现在第二期以后的各期Java杂志中。所以,您完全可以选择自己感兴趣的方向来投稿。一旦您的稿件被我们采用,您将会得到我们对文章作者的奖励。如果您的稿件没有被我们采用,我们也会及时地通知您。
Java杂志第二期的主题是:“深入剖析J2SE 1.5新特性”。
我们的计划是:精选一些J2SE 1.5新特性,然后组织一系列文章分别加以深入的剖析和阐述。
我们选择的特性如下:1、元数据。2、Generic Types。3、自动装箱和拆箱。4、循环方法的加强。5、静态引入。6、格式化输入/出。7、可变参数列表。8、并发工具,线程处理API。9、监控和管理相关的内容,包括JMX MBeans,
JVMTI等等。10、客户端的改进,比如Swing相关的改进。11、XML支持的改进。12、支持补充字符。13、增强的JDBC RowSets支持。当然,肯定还存在一些您认为很有趣,但却没有在
这里列出来的特性。没有关系,您完全可以把您的文章发给我们,我们深表欢迎,并会有专人负责审核您的文章。
我们的备选主题有:1、容器2、分布式组件3、数据持久化4、Remoting和EAI欢迎大家为我们踊跃投稿!您可以先参考一下我们的《CSDN电子杂志作者指
南》相关奖励措施敬请留意即将颁布的《CSDN社区电
子杂志项目奖励制度》。 请将稿件发送至:[email protected]
eMag:Java杂志
第二期征稿启事
Struts 最佳实践totodo
背景:从本期的电子杂志内容介绍中,大家已经看
到了web发展的整个历史, Struts 自从 2001 6月
1.0 Release之后经历了漫长的春秋,有着庞大的用
户群体, 并且IBM, Tomcat Web控制台都采用了
Struts Framework..
Web框架有好多种选择,并不一定要使用
Struts, 但如果你正刚开始使用的话,希望下面的一
些实践能给你带来帮助, 仅此而已。 所以,请原
谅,本文没有什么实例代码, 因为今天早上,我的好
友Leemassn 跟我说,这东西已经烂掉了:-( ,所以相
信阅读此文的读者对Struts基本用法已经有了一
定了解, 业余时间和能力有限,以下写的内容
也均属纸上谈兵。也希望能抛砖引玉。
正确是导向,才是使用Web Framework的真正
目的。倒并不是因为Struts 耳濡目染的多了,我们
才一定要使用Struts ,并不是MVC就一定要使用
Struts ,当你能灵活游刃于粒度良好的数据持久
层,业务层,展示层,清晰的划分好你的业务对
象,规划好WEB数据的展现,能为你的Web应
用留下可扩展的接口时候,这时候你已经比需要
特定的Framework了, 而Struts 只是为这些都做
了考虑,让你的MVC变得容易实现。
无论项目大或小,复杂或者简单,使用Struts的
一些建议:
1. 请选用支持广泛的JDO 或者Hibernate,或
者iBates,来做你的数据持久层。
2. 请加入Struts-el标签,(丰富你标签显示
时所需要逻辑)。
3. 请尽可能通过JNDI连接数据库。
4. 请做单元测试( 对于Struts 可采用
StrutsTestCase)。
5. 请了解,阅读 sun 的命名规范。
1.Struts 之对象使用篇Struts 版本换了好多了,,如果你只是使用最
基本的Form,Action相信0.5 就够了,目前最为
流行的是1.1因此我们尽量使用1.1给我们带来的
便捷。
推荐:
每写个应用都用继承 BaseAction ,和BaseForm
(BaseAction 最好是继承LookupDispatchAction,
BaseForm 最好是继承ValidatorActionForm,这两
个都是从1.1开始有的)
ValidatorActionForm也是继承ActionForm ,
因为1.1有了PlugIn,当你使用ValidatorPlugIn使
得你的FormBean具备了验证能力啦.
Validator也能算的上是Struts中比较精粹的一
个东东,它能做到对你FormBean里的每个数据
类型做判断,实现了资源绑定,并且你可以自定
义一些规则放在框架里使用,相信它会你的应用
程序增光不少。 自带了一些常用的基本数据类
型校验,你可直接使用。
由于要介绍的太多,具体使用方法可参见
Apache 的Validator 项目,在Struts中,只需要在
Struts-config.xml里增加
<plug-in classname="org.apache.struts.validator.ValidatorPlugIn"> <set-property
property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/></plug-in>
(1.1 也同也有了 DynaActionForm,它的
作用就是不需要你写专门的ActionForm类,而
在Struts-Config文件中,但实际开发中,我们并
不是简单的一个Form,我们可能要给Form做验
证,做初始化,还要增添一些方法,所以并不建
议大量使用DynaActionForm)
LookupDispatchAction 继承DispatchAction,
自己多加两个方法,就是getKeyMethod(),localM
ap() ,它能帮你从本地submit的资源文件读取,
比如submit 的名字是add或者delete,而你只管执
行submit就可以, 就能找到你的add方法delete方
法。
代码如下:protected Map getKeyMethodMap() { Map map = new HashMap(); map.put("button.add", "add"); map.put("button.delete", "delete"); return map; }
public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // do add return mapping.findForward("success"); }
public ActionForward d e l e t e ( A c t i o n M a p p i n g mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException { // do delete return mapping.findForward("success"); }
JSP中:
<html:form action="/test"> <html:submit property="action"> <bean:message key="button.add"/> </html:submit> <html:submit property="action"> <bean:message key="button.delete"/> </html:submit>
</html:form>
2.Struts 之标签库篇.
Struts最佳实践
相信Struts是很讲究复用的
标签库主要还是用做View的,所以,在设计
ActionForm的时候大可大胆设计,别担心他们不
能在jsp页面上很好的显示, 该是Date类型就
是,也可以使用List,Map ,这样的符合类型,也可
以是其他对象的引用,因为他们都能很方便的在
页面上显示。
例:如果Test1Form包含了 各种Model对象,Class Test1Form extends ValidatorActionForm{ Student student //有Students 有id,name等属性 Teach teach //Teach对象有id, classid等属性 List classes //一组Classes 对象 Classes又含有 classid等属性 Test2Form test2form //另外一个ActionForm 有 attr2 等field .... }
那么,在JSP页面中你可以这样子:
<html:form action="/test1"> <bean:define name=”test1Form” property=”test2Form” id=”test2” type=”com.yourcompony.Test2Form” /> <html:text name=”test2” property=”test2.attr2” /> <html:text name=”id” property=”student.id”><html:text name=”name” property=”teach.name”> <html:text name=”classid” property=”classes[0].classid”> </html:form >
先承认这样子的代码很难看,但只是想说
明,对于标签库,他能展示任何request中的对
象,以及其引用对象。
还有一个原则,如果上层标签中给一个对象
定义id,那么那个id将被下层标签中作为name 来
引用,并且得到它的所有属性。 看本例中 Bean:
define 里的test2 (同样常见的在Iterator标签里也
经常指定ID来被下面的标签引用。)
Struts对于对象的展示,普通的标签库算是尽
了责任了。但也别得意,他在显示逻辑处理上功
能还是很弱。
推荐:
尽量使用EL标签(也可以引入JSTL):主要
用于复杂的逻辑判断。
举个简单例子:<logic:present name="yourForm" property="a">
….< logic:present >
虽然标签库也提供了逻辑判断,上面判断了
a属性是否存在,
但你又想知道a 是否等于1,怎么样头痛了
吧,换成el 那就是:<c:if test='${yourForm.a =="1" }'> ....</c:if>
(不过也不要怀念你以前使用纯JSP的美好日
子,JSP由于表达能力过强,(等于是Java代码的
脚本显现),就担心你用它也过多的业务逻辑代
码,使得你的程序难以阅读和维护。)
[ 补充一些:对于前台的显示,,Struts标签不
得不说是似乎已经成了一根鸡肋, 美丽,容易操
作的的界面,往往要以来于其他技术,Struts的标签
使用的最大的目的也是为了方便于数据显示, 美
丽复杂操作还是要依赖你的Html与JavaScript]。
3.Struts之异常处理篇.
异常处理的基本原则: catch之后记录日志,
并且做处理,不能处理,则抛出。
基本上来讲,Struts 给异常处理提供了较为
完善的框架.
按道理讲,当一个系统正式交付的时候,,我
们还让用户看到那些 一大串英文字母是一种极
端粗暴的行为, 所以,我们从两头抓起。
推荐:
1 . 使 用 v a l i d a t i o n F o r m 做 可 验 证 的确
formBean , 当然你也可以使用你积累下来比较丰
富的javascript,而validationForm的好处就是,他
能帮你能在服务器端验证,如果应用系统对于数
据的校验教为严格,正应该使用FormBean。
2..Action处理中,在try catch 使用saveMessage
或者saveErrors()
(Struts1.2开始抛弃了ActionErrors,全部是
ActionMessages了)
Struts最佳实践
这里就不得不说到一些构架上的一些东西了.
Struts Action中处理的异常,已经不该是你的原始
异常了.在你的Dao层要抛出DataException,而在
你的业务层,需要捕获到,作为你的自定义异常抛
出,这样你在struts里捕获到的异常才是友好的,且
是有意义的.
我 们 都 知 道 往 往 D a o 那 一 层 都 是 抛
的 一 个 统 一 的 异 常 比 如 D a t a E x p c e t i o n ,
HibernteException之类的。
然后到你的业务处Service调用DAO的方法时
候,就能就捕获DataExpcetion, 因为在Serivce那
一层(你的业务层),你总归是知道自己要做什
么,因此应该Service的业务方法里,再抛出一个
自定义的业务异常,带上你
比如在业务处理,添加一个学生过程,如果
假设是StudentServiceImpl.java public void doAddStudent(Student student) throws DupKeyExption,StudentException{ try{ if((Student)getObject(student,student.getId()).getId()==student.getId()){ throw new DupKeyExcption(“该学生已经存在"); } studentDao.add(); }catch(DataException e){ logger.debug(“错误信息:",e); throws new StudentException(“增加学生失败”); }
}
那么在Action中调用该业务处理方法就应该
是:try {
service.doAddStudent(); }catch (DupikeyException e1){ //重复添加了 errors.add("message",new ActionError("student.error.dupkey")); }catch (Exception e) { //添加失败 logger.error(e.getMessage()); errors.add("message",new ActionError("student.error.add")); e.printStackTrace(); forward = “glablefaild”;
} if(! error.isEmpty()){ saveErrors(request,errors);
….
至于显示嘛,直接使用<html:errors/>就
OK了.但是你要觉得没有空白的地方显示,也
可以使用类的。<html:messages id="message"> <script>window.alert('<bean:write name="message"/>')</script></html:messages>
或者以红色醒目字显示: <html:messages id="msg" message="true"> <font color="red"> <bean:write name="msg"/> </font></html:messages>
对于,全局的异常,那你最好forward一个
gloable-forward 的 errorPage.jsp之类的JSP
(注:Struts1.2开始,抛弃了ActionErrors了,
统一使用使用ActionMessages了,对于
题外话: WEB应用系统大部分异常只是
起到了一个传递错误信息的作用,因此把他们
叫做Messge也是更为贴切,ActionErrors是继承
ActionMessage的,用法也是和ActionMessages一
样, 似乎显得多余,Struts体积已经够庞大了,
Errors这样一个孩子也就这样在1.2里夭折了。)
如果你将异常定义的级别教好,使用
<global-exceptions> <exception handler="com.yourcorp.CustomizedExceptionHandler" key="global.error.message" path="/error.jsp" scope="request" type="java.lang.Exception"/></global-exceptions>
4 Struts 之 扩展篇Struts 的Plugin 给Struts应用程序算是留了一到
后门,扩展Struts 可以由此窃入
Plugin 能使得你的web应用程序在startup或
shutdown 的时候,能完成一部分操作. 它是基于配
置的,你可以很方便的外挂一些应用程序。配置
文件将在启动时载入,并且完成实例化。
比如HibernatePlugin,代码引之hibernate.
org/105.htm
public class HibernatePlugIn implements PlugIn
{
public static final String SESSION_FACTORY_
KEY
= SessionFactory.class.getName();
private static Log _log = LogFactory.
getLog(HibernatePlugIn.class);
private boolean _storedInServletContext = true;
private String _configFilePath = "/hibernate.cfg.
xml";
private ActionServlet _servlet = null;
private ModuleConfig _config = null;
private SessionFactory _factory = null;
/**
* Destroys the <code>SessionFactory</code>
instance.
*/
public void destroy() {
_servlet = null;
_config = null;
try {
_log.debug("Destroying SessionFactory...");
_factory.close();
_log.debug("SessionFactory destroyed...");
} catch (Exception e) {
_log.error("Unable to destroy SessionFactor
y...(exception ignored)",
e);
}
}
/**
* Initializes the <code>SessionFactory</code>.
* @param servlet the <code>ActionServlet</
code> instance under which the
* plugin will run.
* @param config the <code>ModuleConfig</
code> for the module under which
* the plugin will run.
*/
public void init(ActionServlet servlet,
ModuleConfig config)
throws ServletException {
_servlet = servlet;
_config = config;
initHibernate();
}
/**
* Initializes Hibernate with the config file found
at* <code>configFilePath</code>. */ private void initHibernate() throws ServletException { Configuration configuration = null; URL configFileURL = null; ServletContext context = null; try { configFileURL = HibernatePlugIn.class.getResource(_configFilePath); context = _servlet.getServletContext(); if (_log.isDebugEnabled()) { _log.debug("Initializing Hibernate from " + _configFilePath + "..."); } configuration = (new Configuration()).configure(configFileURL); _factory = configuration.buildSessionFactory(); if (_storedInServletContext) {
Struts最佳实践
_log.debug("Storing SessionFactory in ServletContext..."); context.setAttribute(SESSION_FACTORY_KEY, _factory); } } catch (Throwable t) { _log.error("Exception while initializing Hibernate."); _log.error("Rethrowing exception...", t); throw (new ServletException(t)); } } /** * Setter for property configFilePath. * @param configFilePath New value of property configFilePath. */ public void setConfigFilePath(String configFilePath) { if ((configFilePath == null) || (configFilePath.trim().length() == 0)) { throw new IllegalArgumentException( "configFilePath cannot be blank or null."); } if (_log.isDebugEnabled()) { _log.debug("Setting 'configFilePath' to '" + configFilePath + "'..."); } _configFilePath = configFilePath; } /** * Setter for property storedInServletContext. * @param storedInServletContext New value of property storedInServletContext. */ public void setStoredInServletContext(String storedInServletContext) { if ((storedInServletContext == null) || (storedInServletContext.trim().length() == 0)) { storedInServletContext = "false"; } if (_log.isDebugEnabled()) { _log.debug("Setting 'storedInServletContext' to '"
+ storedInServletContext + "'..."); } _storedInServletContext = new Boolean(storedInServletContext).booleanValue(); } }
基 于 配 置 实 例 化 的 工 具 有 好 多 , 引 入
SpringFramework,它能为你做不少工作,可以
配置化的实现 数据连接获取, DAO,Serivice的
获取,自动事务,一切也都能梆在Struts中完成
现。
具体参考SpringFramework。
5. 测试篇
Struts通过传统的手工输入进行测试, 效
率教低, 请尽量使用单元测试
基本使用如下:
public class StudentActionTest extends MockStrutsTestCase {
public StudentActionTest(String arg0) {super(arg0);}
public static void main(String[] args) {junit.textui.TestRunner.run(UserActionTest.
class);}
public void setUp() throws Exception {super.setUp();
setContextDirectory(new File(“E:\\webapp\\webContext”));}protected void tearDown() throws Exception { super.tearDown();}
//测试方法public void testAdd() {
setRequestPathInfo("/student");addRequestParameter("method","add");actionPerform(); //执行verifyForward("success"); //验证
}}
Java Web框架汇总王海龙
0.简介本文介绍 Java Web Framework的基本工作
原理,和一些常用的开源Web MVC Framework (Struts, Web Work, Tapestry, Echo, JSF, Maverick, Spring MVC, Turbine, Cocoon, Barracuda)。
Web开发的最重要的基本功是HTTP;J a v a W e b 开 发 的 最 重 要 的 基 本 功 是 S e r v l e t Specification。HTTP和Servlet Specification对于Web Server和Web Framework的开发实现来说,是至关重要的协议规范。
应用和剖析开源Web Framework,既有助于深入掌握HTTP& Servlet Specification, 也有助于了解一些现代的B/S Web框架设计思想,如MVC,事件处理机制,页面组件,IoC,AOP等。在这个现代化的大潮中,即使Servlet规范本身也不能免俗,不断引入Filter、Listener等现代框架设计模式。同是Sun公司出品的JSF更是如此。
关于MVC模型、项目简介、配置文件、入门示例等基础知识,网上已经有大量的重复资料信息,本文不再赘述。
文中会提到一些相关的开源项目,和一些编程思想,如有需要,可以用相关的关键字在网上搜索,获取基本的背景知识。
本文力图言简意赅,突出重点。着重描述其他资料没有提到、或很少提到的较重要内容,如运行原理、主流用法,相关知识,关键特性等。
1. Java Web工作原理
[编者按:本部分内容在本期杂志《Servlet规范简
介》有更详细介绍]
Tomcat的Server.xml文件中定义了网络请求路
径到主机本地文件路径的映射。比如,<context path="/yourapp" docBase="yourapp_dir/webapp"/>
我们来看一下,一个HTTP Request-Response Cycle的处理过程。
HTTP Request URL一般分为三段:host, context, path。
如http://yourhost/yourapp/en/index.html这个URL,分为host=yourhost, context=yourapp, path=en/index.html三段。其中,Context部分由
request.getContext()获得,path部分由request.getServletPath()获得(返回结果是“/en/index.html”)。
yourhost主机上运行的Tomcat Web Server接收到这个URL,根据Context定义,把yourapp这个网络路径映射为yourapp_dir/webapp,并在此
目录下定位en/index.html这个文件,返回到客户
端。
如果我们这个URL更换为http://yourhost/
yourapp/en/index.jsp,这个时候Tomcat会试
图把yourapp_dir/webapp/en/index.jsp文件编译成
Servlet,并调用运行这个Servlet。
我们再把这个URL更换为http://yourhost/
yourapp/en/index.do。
注意,戏剧化的事情就发生在这个时候,
Servlet规范中最重要的类RequestDispatcher登场
了。RequestDispatcher根据WEB-INF/web.xml配置文件的定义,调用对应的Servlet来处理en/index.do这个路径。
假设web.xml里面有这样的定义
<servlet> <servlet-name>DispatchServlet</servlet-
name> <servlet-class>yourapp.DispatchServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DispatchServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
那么,RequestDispatcher会调用yourapp.DispatchServlet类处理这个路径。
如 果 w e b . x m l 没 有 定 义 对 应 e n /i n d e x . d o 这 个 路 径 的 S e r v l e t , 那 么
To m c a t返 回 “ 您 请 求 的 资 源 不 存 在 ”
RequestDispatcher用于Web Server中,也可以
用于应用程序中进行处理转向,资源定位。比
如,我们在处理en/index.do的代码中调用,
request.getRequestDispatcher(“cn/index.jsp”).forward(request, response), 就可以转交另外的资
源cn/index.jsp来处理。
几乎所有的 Web Framework 都需要定
义自己的 Dispatch作用的 Servlet,并调用
RequestDispatcher进行转向处理。
阅读Web Framework源代码,有两条主要线
索,(1)根据web.xml找到对应的Servlet类;(2)搜索包含“RequestDispatcher”词的代码文件。
我们看到,request, response 这 两 个参
数,被RequestDispatcher在各种Servlet之间传
来传去(JSP也是Servlet)。
所以, request的s e t A t t r i b u t e ( )和getAttribute()方法
是Servlet之间传送
数据的主要方式。
在MVC结构中,
一般的处理流程如
下:
处 理 H T T P Request的基本单位一
般称为Action,是一个
比Servlet轻
量得多的接口定义,通常只有一两个方法,如
execute(perform), validate等。
我们知道,URL->Servlet映射,定义在Web.xml配置文件里,但MVC框架通常会有另外一个
定义URL-> Action映射的配置文件。
入口Dispatcher Servlet根据URL -> Action的映
射关系,把请求转发给Action。
Action获得输入参数,调用商业逻辑,并把
结果数据和View标识给(Model & View)返回
给Dispatcher Servlet。
Dispatcher Servlet根据这个View 标识,定位
相应的View Template Path,把处理转交给View(
JSP +TagLib, Velocity, Free Marker, XSL等)。
View一般通过request.getAttribute()获得结
果数据,并显示到客户端。至于是谁把结果数
据设置到request.attribute里面,有两种可能:
Action或Dispatcher Servlet。
2. Struts
http://struts.apache.org/
Struts是目前用户群最大、开发厂商支持最多
的开源Web Framework。
Struts劳苦功高,为普及MVC框架作出了
不可磨灭的贡献。显赫的声望,趋于老化的
厚重结构,令Struts成为很多现代Web
Framework参照、挑战的目
标。
S t r u t s应 用
主要包括 3件事
情: 配置struts-config.xml文件,实
现Action类,实现
View;还有一些高
级扩展用法。下面
分别讲述。
1. 配置struts-config.xml文件:
Struts支持多级配
Java Web 框架汇总
置文件,具体用法和限制,详见Struts文档。
这里只讨论struts-config.xml主流配置的内
容。:-)
(1) URL Path到Action的映射。
如<action path="/LogonSubmit" type="app.
LogonAction" ... />
Struts的入口Servlet是ActionServlet。
ActionServlet需要此信息把URL Path调用对
应的Action类处理。
在Struts运行期间,一
个URL Path,只存在一个
对应的Struts Action实
例。所有的该URL Path的
请求,都经过这同一个
Struts Action实例处理。
所以Struts Action必须线
程安全。
想想看,其实这个要求
并不过分,Action只是一
个处理程序,不应该保存
跨HTTP请求的状态数据,
按理来说,也应该做成线
程安全的。
(2) Template Name到View Template Path的
映射。
<forward name="success" path="/pages/
Welcome.jsp"/>
Action类返回一个Template Name,
ActionServlet根据这个Template Name获得对
应的View Template Path,然后调用
request.getRequestDispatcher(“View
Template Path”),把处理转向路径对应
的Servlet。在这个例子中,是转向/pages/
Welcome.jsp编译后的Servlet。
我们来看一个一个Velocity的例子。
<include name="success" path="/pages/
Welcome.vm"/>
web.xml的定义如下
<servlet>
<servlet-name>velocity</servlet-name>
<servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>velocity</servlet-name>
<url-pattern>*.vm</url-pattern>
</servlet-mapping>
这 时 , r e q u e s t .
getRequestDispatcher(“/
p a g e s / W e l c o m e .
vm”)会调用VelocityViewServlet,由
VelocityViewServlet负责装并驱动运行/
pages/Welcome.vm这个模板文件。
这 里 面 有 一 个 问 题 , 如 果 调 用 的 是
Dispat c h R e q u e s t e r . i n c l u d e ( )方法,
那么如何才能把pag e s / W e l c o m e . v m传给
VelocityViewServlet呢?
如前所说,RequestDispatcher传递的参数
只有两个,request和response。那么只能通过
request attribute。正是为了解决这个问题,
Servlet2.3规范之后,加入了javax.servlet.
include.servlet_path这个属性。
参见Vel o c i t y V i e w S e r v l e t的代码(
velocity-tool开源项目)
// If we get here from
RequestDispatcher.include(),
getServletPath()
// will return the original (wrong)
应 用 和 剖 析 开 源 W e b
F r a m e w o r k , 既 有 助 于
深入掌握HTTP& Servlet
Specification, 也有助于了解
一些现代的B/S Web框架设
计思想,如MVC,事件处
理机制,页面组件,IoC,
AOP等。
Java Web 框架汇总
URI requested. The following special
// attribute holds the correct path.
See section 8.3 of the Servlet
// 2.3 specification.
String path = (String)request.
getAttribute("javax.servlet.include.
servlet_path");
从这里我们可以看出,为什么通晓Servlet
Specification对于通晓Web Framework至关重
要。
(3) Form Bean的定义
如<form-bean name="logonForm" type="app.LogonForm"/>
Struts Form Bean需要继承ActionForm类。
Form Bean类,主要有三个作用:
[1]根据bean的定义,利用reflection机制,
自动把request参数转化为需要的数据类型,填
入到bean的属性当中。ActionForm类名中虽然有
Form这个词,但不仅能够获取Form提交后的HTTP
Post参数,也可以获取URL后缀的HTTP Get参
数。
[2]输入验证。用户可以配置validation.
xml,定义各属性的验证规则。
[3]当作View Object来用。用户需要熟练
掌握Struts HTML TagLib的用法,才能把Form
Bean的属性正确显示出来。
(4)其他定义。详见Struts文档。不再赘述。
2.实现Action。
Action类从Form Bean或直接从request中
获得输入参数,调用商业逻辑,把结果数据
(也许会包装成View Object),用request.
setAttribute()放到request中,最后返回一个
用ForwardMapping类包装的Template Name。
3.实现View。
Struts View的标准实现方法是JSP + Struts
TagLib,其中最重要的就是Struts HTML
TagLib。
html:form tag则是整个HTML Tag的核心,其
它的如html:input, html:select等tag,都包含
在html:form tag里面。
html:form tag用来映射Form Bean(也可以
通过适当定义,映射其他的bean,但使用上会有
很多麻烦)。html:form tag包含的其他Struts
html tag用来映射Form Bean的属性。
Struts Bean TagLib的用法比较臃肿,一般
情况下可以用JSTL代替。当然,如果需要用到
bean:message tag实现国际化,那又另当别论。
Struts Tile TagLib用于页面布局。开源
Portal项目Liferay使用了Struts Tile TagLib做
为布局控制。
4.高级扩展用法
用户可以重载Struts的一些控制类,引入自
己的一些定制类。详见Struts文档。
本文不是Struts专题,只讲述最重要的主流
用法,其它边边角角的,不再赘述。
3. WebWork
http://www.opensymphony.com/webwork/
WebWork由于灵活的可插拔特性,受到很多资
深程序员的欢迎。似乎很有可能大肆流行起来。
W e b W o r k 项目建立在 X W o r k项目上。
入口 S e r v l e t 是 W e b W o r k 项目中定义的
ServletDispatcher,而Action在XWork项目中定
义。
XWork Action接口的execute()方法没有
参数,不像Struts Action那样接受request,
response参数,所以XWork Action能够脱离
Web环境被直接调用,便于单元测试。
这里引入了一个问题。没有了request参
数,那么XWork Action如何获得request
parameters作为输入数据?又通过什么桥梁(
Struts用request.setAttribute)把结果数据传
Java Web 框架汇总
给您的知识库加点油
JavaeMag我 们 期 待 着 您 的 加 入 !
请和我们联系 投稿信箱[email protected] 杂志主页http://emag.csdn.net/Default.aspx?tabid=57
送到View层?
在Web Work 中,只能通过 Action 本身的
getter,setter属性来传送输入参数和输出结
果。
比如,我们有这样一个实现了 X W o r k
Action接口的类,
YourAction implements Action{
int productId = null;
String productName = null;
public void setProductId(int
productId){this.productId = productId;}
public String getProductName(){return
productName;}
public String execute(){
productName =
findNameById(productId);
return “success”;
}
}
这个类里面的productId将接受request输入
参数,productName是输出到页面显示的结果。
比如,这样的请求,http://yourhost/
yourapp/MyAction.action?productId=1
Web Work会把1填到YourAction的productId里
面,然后执行execute()方法,JSP里的语句
<ww:property value=“productName”>会把
YourAction的productName显示在页面上。
如果一个Web Framework采用了这种屏蔽
Action的request, response参数的设计方式,
一般也同时会采用这种Action和输入输出数据
结合成一体的解决方式。类似的情形也存在于
Tapestry和Maverick中,后面会讲到。
当WebWork ServletDispatcher接收到HTTP
Request的时候,首先把所有相关的信息(包
括request, response, session, servlet config, servelt context, 所有request参数)等存放到
AcationContext中,然后根据Interceptor配
置信息,生成一个YourAction的动态代理类对
象。实际上运行的正是这个代理对象,如同
Servlet Filter的工作机制一般,所有注入的
Interceptor方法会先于Actio方法运行。
我们来看一下Action和Interceptor的地位:
Action没有参数,无法获得ActionContext;而
Interceptor接受的ActionInvoication参数拥有
包括ActionContext在内的所有重要信息。
这种权力分配的不平等,注定了Action的作
用非常有限,只限于调用商业逻辑,然后返回一
个成功与否标志。所有与外部Web世界打交道、
协调内部工作流程的重担,都责无旁贷地落在
Interceptor的肩上。
我们可以设想一个极端的例子。我们声
明一批不做任何事情的空Action,我们只是
需要它们的空壳类名;我们制作一批对应的
Interceptor,所有的转发控制、商业逻辑都在
Interceptor上实现,然后把Interceptor都注入
到对应的空Action。这在理论上是完全可行的。
在W e b海洋的包围中,A c t i o n可少,
Interceptor不可少。Action是一个孤岛,如果没
有外来盟友Interceptor的协助,只能在自己的
小范围内独立作战(比如Unit Test),而对整
体大局的作战目标无法产生影响。
下 面 我 们 来 看 一 下 A c t i o n 是 如 何 在
Interceptor的全程监管下工作的。
在WebWork中,我们需要如下配置XWork.
xml。
<xwork>
<!-- Include webwork defaults (from
WebWork-2.1 JAR). -->
<include file="webwork-default.xml"
/>
<!-- Configuration for the default
package. -->
<package name="default"
extends="webwork-default">
<!-- Default interceptor stack. -->
<default-interceptor-ref name="
defaultStack" />
<!-- Action: YourAction. -->
<action name="youraction"
class="yourapp.YourAction">
Java Web 框架汇总
<result name="success"
type="dispatcher">
YourAction.jsp
</result>
</action>
</package>
</xwork>
webwork-default.xml里面的相关定义如下:
<interceptors>
<interceptor name="validation"
class="com.opensymphony.xwork.validator.
ValidationInterceptor"/>
<interceptor name="static-params"
class="com.opensymphony.xwork.interceptor.
StaticParametersInterceptor"/>
<interceptor name="params" class="com.
opensymphony.xwork.interceptor.
ParametersInterceptor"/>
<interceptor name="conversionError"
class="com.opensymphony.webwork.
interceptor.WebWorkConversionErrorInterce
ptor"/>
<interceptor-stack name="defaultStack">
<interceptor-ref name="static-
params"/>
<interceptor-ref name="params"/>
<interceptor-ref
name="conversionError"/>
</interceptor-stack>
</interceptors>
从 上 述 的 配 置 信 息 中 可 以 看 出 ,
YourAction执行execute()方法的前后,会被
defaultStack所定义的三个Intercepter截获。
这些Interceptor的任务之一就是把输入参数设
置到Action的对应属性当中。
如果我们需要加入对YourAction的属性的
验证功能,只要把上述定义中的validation
Interceptor加入到defaultStack中就可以了。
当然,实际工作还没有这么简单,一般来说,还
要为每个进行属性验证的Action的都配置一份
validation.xml。
XWork Interceptor能够在Package和Action
级别上,进行截获处理。
Servlet Filter能够在URL Patten级别上,
进行截获处理。虽然实际上,Servlet Filter截
获的是Servlet,但某些情况下,可以达到和截
获一批Action的同样效果。
比如,在Web Work中,我们可以为所有admin
package的Action,加入一个Interceptor,当检
查到当前Session的用户没有admin权限时,统一
返回一个警告页面:您没有足够的权限执行这个
操作。
我们看到也可以为所有URL Pattern 为
“admin/*.action” 的URL定义一个Servlet
Filter,当检查到当前Session的用户没有
admin权限时,统一返回一个警告页面:您没有
足够的权限执行这个操作。
WebWork的Interceptor配置是相当灵活的,
相当于对Action实现了AOP。Interceptor相当于
Aspect,基类AroundInterceptor的before(),
after()方法相当于Advice。
另外,XWork也提供了从XML配置文件装
配Com p o n e n t的机制,相当于实现了对于
Component的IoC。
提到AOP和IoC,顺便多讲两句。Spring
AOP能够截获所有Interface,不限于某个特定接
口;Spring框架支持所有类型的IoC,不限于某
种特定类型。
要知道,AOP, IoC可是现在最时髦的东西,
一定不要错过啊。:D
相关概念导读(如果需要,请用如下关键字
搜索网络):
AOP -- Aspect Oriented Programming -- 面
向方面编程。
IoC – Inversion of Control --控制反转
Dynamic Proxy -- 动态代理,JDK1.4引入的
特性。还可以进一步参考CGLib, ASM等开源项
目。
W e b W o r k 直 接 支 持 所 有 主 流 V i e w - -
XSL,Velocity, FreeMarker,JSP。WebWork还提
供了自己的TagLib。“直接支持”的意思是说,
不用像Struts那样,使用Velocity的时候,还需
要引入辅助桥梁Velocity-tool。
Java Web 框架汇总
WebWork中用到一种功能和XPath类似的对象
寻径语言ONGL,是一个开源项目。ONGL同样用在
下面要介绍的Tapestry项目中。
Opensymphony下还有一个SiteMesh项目,
通过Servlet Filter机制控制布局。可以和
WebWork组合使用。
4. Tapestry
http://jakarta.apache.org/tapestry/
Tapestry近来突然火了起来,令我感到吃惊。
也许是JSF带来的Page Component风潮令人们开
始关注和追逐Tapestry。
Ta p e s t r y的 重 要 思 想 之 一 就 是 P a g e Component。
前面讲到,XWork能够自动把 request参数
映射到Action的属性当中。Tapestry走得更远,
甚至能够根据 request参数,映射到Action(Tapestry里面称为Page)的方法,并把request参数映射为Page方法需要的参数,进行正确的调
用。就这样,Tapestry不仅把输入输出数据,而
且把事件方法也绑定到了Page上面。
在Tapestry框架中,Action的概念已经非常模
糊,而换成了Page的概念。而Tapestry Page是拥
有属性和事件的页面组件,其中的事件处理部相
当于Action的职责,而属性部分起着Model的作
用。
除了使用Page和其它的Tapestry页面组件,用
户也可以自定义页面组件。
这种页面组件/属性事件的编程模型,受到一
些程序员的欢迎。当然,这种编程模型并不是没
有代价的,每个Tapestry模板文件都需要一个对
应的.page文件。这些.page文件定义了页面组件
的属性、事件、Validator等信息。
我们来看一下B/S结构中,组件的属性、事
件和HTTP Request绑定的基本原理。一个能够
发出请求的页面组件(比如Link和Button),在
输出自己的HTML的时候,需要输出一些特殊的
信息来标志本组件的属性/事件,这样下次HTTP Request来的时候,会把这些信息带回来,以便
Web Framework加以辨认识别,发给正确的Page
Component处理。
这些特殊信息通常包含在URL参数或Hidden Input里面,必要的时候,还需要生成一些Java Script。Tapestry,Echo,JSF都是这种原理。
Tapestry的例子如下:
<a href="#" jwcid="@DirectLink" parameters="ognl:currentItem.itemId" listener="ognl:listeners.showItem">
[编者按:OGNL是一种利用java对象setter和
getter方法来访问其属性的表达式语言,Tepestry项
目及很多项目使用了该技术。更详细链接http://www.
ognl.org/]
JSF用TagLib实现页面组件,也提供了类似
的CommandLink和CommandButton Tag。其中对
应Tapestry listener的Tag属性是action。后面会讲
解。
Tapestry的模板标签是HTML标签的扩展,具
有良好的“所见即所得”特性,能够直接在浏览
器中正确显示,这也是Tapestry的一个亮点。
5. Echohttp://sourceforge.net/projects/echo
Echo提供了一套类似Swing的页面组件,直
接生成HTML。
从程序员的角度看来,用Echo编写Web程序,和用Swing编写Applet一样,属于纯面向组
件事件编程,编程模型也以Event/Listener结构为
主体。
Echo没有Dispatcher Servlet,也没有定义
URL->Action映射的配置文件。
Echo的Action就是实现了ActionListener接口(参数为ActionEvent)的 Servlet(继承
EchoServer类)。
所以,Echo直接由Web Server根据web.xml配置的URL -> Servlet的映射,进行转发控制。
Java Web 框架汇总
Echo也没有明显的View层,Echo在页面组件
方面走得更远,所有的HTML和JavaScript都由框
架生成。你不必(也没有办法)写HTML,只需
要(也只能)在Java代码中按照类似Swing编程
方式,生成或操作用户界面。用户也可以定制自
己的Echo组件。
Echo的UI Component的实现,采用了两
个重要的模式。一个是Peer(Component -> ComponentPeer)模式,一个是UI Component -> Renderer模式。
虽然 Echo的 API更类似于 Swing,但实
现上却采用更接近于AWT的 Peer模式。每
个 Component类(代表抽象的组件,比如
Button),都有一个对应的ComponentPeer类(
代表实际的组件,比如windows桌面的Button,Linux桌面的Button,HTML Button等)。
先 别 急 , 这 个 事 情 还 没 有 完 。 虽 然
ComponentPeer落实到了具体的界面控件,但是
它还是舍不得显示自己,进一步把显示工作交给
一个Renderer来执行。
比如,在Echo里面,Button类对应一个
ButtonUI(继承了ComponentPeer)类,而这个
ButtonUI类会把最终显示交给ButtonRender来处
理。
据说多了这么一步,能够让显示控制更加灵
活丰富。比如,同一个Renderer可以处理不同的
UI Component,同一个UI Component也可以交给
不同的Renderer处理。
JSF的页面组件也采用了UI Component -> Renderer模式,后面会讲到。
6. JSF
http://java.sun.com/j2ee/
javaserverfaces/index.jsp
http://wwws.sun.com/software/
communitysource/jsf/download.html download source
JSF的中心思想也是页面组件 /属性事件。
一般来说,JSF的页面组件是一个三件套{ UI Component, Tag, Renderer}。
UI Component 有可能对应 Model,Event,Listener。Tag包含 componentType和rendererType两个属性,用来选择对应的的UI Component和Renderer。
JSF的应用核心无疑是JSF TagLib。JSF TagLib包含了对应所有重要HTML元素的Tag,而且Input Tag可以直接包含Validator Tag或者
Validator属性,来定义验证手段。
我们通过JSF携带的cardemo例子,来看JSF的处理流程。
(1) carDetail.jsp有如下内容:
<h:commandButton action="#{carstore.buyCurrentCar}" value="#{bundle.buy}" />
可以看到,这个button的submit action和carstore.buyCurrentCar方法绑定在一起。我们在
Tapestry里面曾经看到过类似的情景。
(2) carstore在faces-config.cml中定义:
<managed-bean> <managed-bean-name> carstore </managed-bean-name>
<managed-bean-class> carstore.CarStore </managed-bean-class> <managed-bean-scope> session </managed-bean-scope> </managed-bean>
(3) carstore.CarStore类中的buyCurrentCar方法
如下:
public String buyCurrentCar() {
getCurrentModel().getCurrentPrice();
return "confirmChoices";
}
(4) confirmChoices转向在faces-config.cml中定
义:
<navigation-rule> <from-view-id>/carDetail.jsp</from-view-id> <navigation-case> <description> Any action that returns "confirmChoices" on carDetail.jsp should cause
navigation to confirmChoices.jsp </description> <from-outcome>confirmChoices</from-outcome> <to-view-id>/confirmChoices.jsp</to-view-id> </navigation-case> </navigation-rule>
(5)于是转到页面confirmChoices.jsp。
除了Interceptor之外,JSF几乎包含了现代
Web Framework应该具备的所有特性:页面组
件,属性事件,IoC (ManagedBean),Component -> Renderer,类似于Swing Component的Model-Event-Listener。
也许设计者认为,众多庞杂的模式能够保证
JSF成为一个成功的框架。Portal开源项目eXo就是建立在JSF框架上。
可 以 看 出 这 样 一 个 趋 势 , 现 代 We b Framework认为B/S结构的无状态特性和HTML界面是对编程来说是需要极力掩盖的一个缺陷,所
以尽量模拟C/S结构的组件和事件机制,以吸引
更多的程序员。
7. Maverick
http://mav.sourceforge.net/
Maverick是一个轻量而完备的MVC Model 2框架。Maverick的Action不叫Action,直截了当
的称作Controller。
Controller只接受一个ControllerContext参数。
request,response, servlet config, servelt context等输入信息都包装在ControllerContext里面,而且
Model也通过ControllerContext的model属性返
回。整个编程结构清晰而明快,令人赞赏。
但这个世界上难有十全十美的事情,由于
ControllerContext只有一个model属性可以传递数
据,程序员必须把所有需要的数据都打包在一个
对象里面设置到model属性里。这种麻烦自然而
然会导致这样的可能用法,直接把Controller本身设置为model,这又回到了Controller(Action)和Model一体的老路。
前面讲到,WebWork也把所有的输入信息
都包装在ActionContext里面,但Action并没
有权力获取。而在Maverick中,Controller对于 Control lerContext拥有全权的控制,两
者 地 位 不 可 同 日 而 语 。 当 然 , 由 于 参 数
ControllerContext包含request,reponse之类信
息,这也意味着,Maverick Controller不能像
WebWork Action那样脱离Web环境独立运行。
当 然 , 这 也 并 不 意 味 着 任 何 结 构 性
缺 陷 。 程 序 的 结 构 由 你 自 己 控 制 , 你
完 全 可 以 把 需 要 单 元 测 试 的 那 部 分 从
We b环 境 脱 离 开 来 , 放 到 B u s i n e s s层 。 如同WebWork,Maverick直接支持所
有的主流View。Maverick的配置文件采Struts, Cocoon两家之长,URL -> Action -> View映射
的主体结构类似于Struts,而View定义部分对
Transform的支持则类似于Cocoon。如:
<command name="friends"> <controller class="org.infohazard.friendbook.ctl.Friends"/> <view name="success" path="friends.jsp"> <transform path="trimInside.jsp"/> </view> </command>
8. Spring MVC
http://www.springframework.com/
Spring MVC是我见过的结构最清晰的MVC Model 2实现。
Action不叫Action,准确地称做Controller;Controller接收request, response参数,干脆利落地
返回ModelAndView(其中的Model不是Object类型,而是Map类型)。
其它的Web Framework中, Action返回值一
般都只是一个View Name;Model则需要通过其
它的途径(如request.attribute,Context参数,或
Action本身的属性数据)传递上去。
Spring以一招IoC名满天下,其AOP也方兴未
艾。“Spring出品,必属精品”的观念已经深入
人心。我这里多说也无益,强烈建议读者去阅读
Spring Doc & Sample & Code本身。
9. Turbine
http://jakarta.apache.org/turbine/
Turbine是一个提供了完善权限控制的坚实框
架(Fulcrum子项目是其基石)。Turbine的个人
用户不多,但不少公司用户选择Turbine作为框
架,开发一些严肃的应用(我并没有说,用其它
框架开发的应用就不严肃^_^)。Portal开源项目
JetSpeed建立在Turbine上。
Turbine用RunData来传递输入输出数据。如
同Maverick的ControllerContext,RunData是整
个Turbine框架的数据交换中心。除了request, response等基本信息,RunData直接包括了User/ACL等权限控制相关的属性和方法,另外还包
括Action Name和Target Template Name等定位属
性。
Module是Turbine里面除了RunData之外的
又一个核心类,是Turbine框架的基本构件,
Action是Module,Screen也是Module。Turbine提供了LoginUser和LogoutUser两个Action作为整个
系统的出入口。而其余流量的权限控制则由类似
于Servlet Filter机制的Pipeline控制。
Turbine Pipeline的编程模型和Servlet Filter一模一样:Turbine Pipeline的Valve就相当于Servlet Filter,而ValveContext则相当于Filter Chain。还有更相近的例子,Tomcat源代码里面也有
Valve和ValueContext两个类,不仅编程模型一
样,而且名字也一样。
权限控制贯穿于Turbine框架的始终。要用好
Turbine,首先要通晓子项目Fulcrum 的Security部分的权限实现模型。
Fulcrum Security的权限实体包括四个-- User, Group, Role, Permission。
实体之间包含{Role,Permission}和{ Group, User, Role}两组关系。
{Role,Permission}是多对多的关系,一个
Java Web 框架汇总
Role可以具有各种Permission;{ Group, User, Role}之间是多对多的关系,一个Group可包含多
个User,并可以给User分配不同的Role。
权限模型的实现同样采用Peer模式,Entity -> EntityPeer, Entity -> ManagerPeer。
Entity和EntityManger代表抽象的模型概念,
而EntityPeer和ManagerPeer代表具体的实现。
用户可以根据模型,提供不同的实现,
比如,用内存结构中实现,用数据表结构实
现,与Windows NT权限验证机制结合,与
OSWorkflow的权限控制模型结合,等等。其
中,用数据表结构实现,又可以选择用Torque实现,或者用Hibernate实现。(Torque是Turbine的O/R Mapping子项目)
例如,Falcrum.property配置文件包含如下
Security相关选项:
# -------------------------------------------------------------------
# S E C U R I T Y S E R V I C E
# -------------------------------------------------------------------
services.SecurityService.user.class=org.apache.fulcrum.security.impl.db.entity.TurbineUser
services.SecurityService.user.manager=org.apache.fulcrum.security.impl.db.DBUserManager
services.SecurityService.secure.passwords.algorithm=SHA
# -------------------------------------------------------------------
# D A T A B A S E S E R V I C E
# -------------------------------------------------------------------
services.DatabaseService.database.newapp.driver=org.gjt.mm.mysql.Driver
services.DatabaseService.database.newapp.
url=jdbc:mysql://127.0.0.1/newapp
services.DatabaseService.database.newapp.username=turbine
services.DatabaseService.database.newapp.password=turbine
这说明,权限控制实现由数据库提供,需要
根据权限模型创建如下数据表:
TURBINE_USER,TURBINE_ROLE,TURBINE_GROUP,
TURBINE_PERMISSION,TURBINE_ROLE_PERMISSION,
TURBINE_USER_GROUP_ROLE。
10. Cocoonhttp://cocoon.apache.org
Cocoon项目是一个叫好不叫做的框架。采用
XML + XSLT Pipeline机制,Java程序只需要输出
XML数据,Cocoon框架调用XSL文件把XML数据转换成HTML、WML等文件。
Cocoon强大灵活的XSL Pipeline配置功能,
XSLT的内容 /显示分离的承诺,一直吸引了
不少程序员fans。怎奈天不从人愿,由于复杂
度、速度瓶颈、XSL学习难度等问题的限制,
Cocoon一直主要限于网站发布出版领域,向
CMS和Portal方向不断发展。另外,Cocoon开发
了XSP脚本和Cocoon Form技术。
Cocoon的sitemap.xmap配置文件比较复杂,
与其它的Web Framework差别很大。
主体Pipelines配置部分采用Pattern Match的方
式,很像XSL语法,也可以类比于Web.xml里面
Servlet Mapping的定义。比如,一个典型的URL->Action的映射定义看起来是这个样子:
Java Web 框架汇总
<map:pipelines>
<map:pipeline>
<map:match pattern="*-dept.html">
<map:act set="process">
<map:parameter name="descriptor"
value="context://docs/department-form.xml"/>
<map:parameter name="form-descriptor"
value="context://docs/department-form.xml"/>
<map:generate type="serverpages" src="docs/confirm-dept.xsp"/>
<map:transform src="stylesheets/apache.xsl"/>
<map:serialize/>
</map:act>
<map:generate type="serverpages" src="docs/{1}-dept.xsp"/> <map:transform src="stylesheets/apache.xsl"/> <map:serialize/> </map:match> </map:pipeline> </map:pipelines>
11. Barracuda
http://barracudamvc.org/Barracuda/index.html
Barracuda是一个HTML DOM Component + Event/Listener结构的框架。
根据模板文件或配置文件生成静态Java类,
并在代码中使用这些生成类,是Barracuda的一
大特色。
Bar racuda需要用 XMLC项目把所有的
HTML或WML模板文件,静态编译成DOM结构的Java类,作为页面组件。XMLC会根据HTML元素的id定义,生成相应DOM结点的简便操作方法。
Barracuda的事件类也需要用Barracuda Event Builder工具把event.xml编译成Java类,引入到工程中。Barracuda直接用Java类的继承关系映射事件之间的父子层次关系。比如,ChildEvent是ParentEvent的子类。
Barracuda的事件分为两类:Request Events(Control Events)和Response Events(View Events)。
Barracuda事件处理过程很像Windows系统消息队列的处理机制。
(1) Barracuda根据HTTP Request生成Request Event,放入到事件队列中。
(2) EventDispatcher检查事件队列是否为空,如果为空,结束。如果非空,按照先进先出的方式,从事件队列中取出一个事件,根据这个事件的类型,选择并调用最合适的EventListener,参数Event Context包含事件队列。
“ 根 据 事 件 类 型 , 选 择 最 合 适 的EventListener对象”的过程是这样的:比如,
EventDispatcher从时间队列里取出来一个事件,类型是ChildEvent;Barracuda首先寻找注册了监听ChildEvent的EventListener,如果找不到,再上溯到ChildEvent的父类ParentEvent,看哪些EventListener对ParentEvent感兴趣。
详细过程参见Barracuda 的DefaultEventDispa-tcher类。
(3) EventListener根据Event Context包含的
request信息,调用商业逻辑,获得结果数据,
然后根据不同情况,把新的事件加入到Event Context的事件队列中。
(4)控制交还给EventDispatcher,回到第
(2)步。
Java Web 框架汇总
Java 模板技术Anders*小明
一、起源与现状:关于Template和JSP的起源还要追述到Web开
发的远古年代,那个时候的人们用CGI来开发web应用,在一个CGI程序中写HTML标签。
在 这 之 后 世 界 开 始 朝 不 同 的 方 向 发 展 :sun公司提供了类似于CGI的servlet解决方案,但是无论是CGI还是servlet都面对同一个问题:在程序里写html标签,无论如何都不是一个明智的解决方案。于是sun公司于1999年推出了JSP技术。而在另一个世界里,以PHP和ASP为代表的scriptlet页面脚本技术开始广泛应用。
不过即便如此,问题并没有结束,新的问题出现了:业务和HTML标签的混合,这个问题不仅导致页面结构的混乱,同时也使代码本身难以维护。
于是来自起源于70年代后期的MVC模式被引入开发。MVC的三个角色:Model——包含除UI的数据和行为的所有数据和行为。View是表示UI中模型的显示。任何信息的变化都由MVC中的第三个成员来处理——控制器。
在之后的应用中,出现了技术的第一次飞跃:前端的显示逻辑和后端的业务逻辑分离,COM组件或EJB或CORBA用于处理业务逻辑,ASP、JSP以及PHP被用于前端的显示。这个就是Web开发的Model 1阶段(页面控制器模式)。
不过这个开发模式有很多问题:1、页面中必须写入Scriptlet调用组件以获得所必需的数据。2、处理显示逻辑上Scriptlet代码和HTML代码混合交错。3、调试困难。JSP被编译成servlet,页面上的调试信息不足以定位错误。
这一切都是因为在Model 1中并没有分离视图和控制器。完全分离视图和控制器就成了必须。这就是Model 2。它把Model 1中未解决的问题——分离对组件(业务逻辑)的调用工作,把这部分工作移植到了控制器。现在似乎完美了,不过等等,原来的控制器从页面中分离后,页面所需的数据怎么获得,谁来处理页面显示逻辑?两个办法:1. 继续利用asp,php或者jsp等机制,不过由于它们是运行在web环境下的,他们所要显示的数据(后端逻辑产生的结果)就需要通过控制器放入request流中;2. 使用新手法——模板技术,使用独立的模板技术由于脱离的了web环境,会给开发测试带来相当的便利。至于页面所需数据传入一个POJO就行而不是request对象。
模板技术最先开始于PHP的世界,出现了PHPLIB Template和FastTemplate这两位英雄。不久模板技术就被引入到java web开发世界里。目前比较流行的模板技术有:XSTL,Velocity,JDynamiTe,Tapestry等。另外因为JSP技术毕竟是目前标准,相当的系统还是利用JSP来完成页面显示逻辑部分,在Sun公司的JSTL外,各个第三方组织也纷纷推出了自己的Taglib,一个代表是struts tablib。二、 模板技术分析:
模板技术从本质上来讲,它是一个占位符动态替换技术。一个完整的模板技术需要四个元素:0. 模板语言,1. 包含模板语言的模板文件,2. 拥有动态数据的数据对象,3. 模板引擎。以下就具体讨论这四个元素。(在讨论过程中,我只列举了几个不同特点技术,其它技术或有雷同就不重复了)模板语言:
模板语言包括:变量标识和表达式语句。根据表达式的控制力不同,可以分为强控制力模板语言和弱控制力模板语言。而根据模板语言与HTML的兼容性不同,又可以分为兼容性模板语言和非兼容性模板语言。
模板语言要处理三个要点:标量标记。把变量标识插入html的方法很
多。其中一种是使用类似html的标签;另一种是使用特殊标识,如Velocity或者JDynamiTe;第三种是扩展html标签,如tapestry。采用何种方式有着很多考虑,一个比较常见的考虑是“所见即所得”的要求。
条件控制。这是一个很棘手的问题。一个简单的例子是某物流陪送系统中,物品数低于一定值的要高亮显示。不过对于一个具体复杂显示逻辑的情况,条件控制似乎不可避免。当你把类似于<IF condition=”$count <= 40”><then><span class=”highlight”>count </span></then></IF>引入,就象我们当初在ASP和PHP中所做得一样,我们将不得不再一次面对scriptlet嵌入网页所遇到的问题。我相信你和我一样并不认为这是一个好得的编写方式。实际上并非所有的模板技术都使用条件控制,很多已有的应用如PHP上中的以及我曾见过一个基于ASP.NET的应用,当然还有Java的JDynamiTe。这样网页上没有任何逻辑,不过这样做的代价是把高亮显示的选择控制移交给编程代码。你必需做个选择。也许你也象我一样既不想在网页中使用条件控制,也不想在代码中写html标记,但是这个显示逻辑是无可逃避的(如果你不想被你的老板抄鱿鱼的话),一个可行的方法是用CSS,在编程代码中决定采用哪个css样式。特别是CSS2技术,其selector机制,可以根据html类型甚至是element的attributes来apply不同的样式。
迭代(循环)。在网页上显示一个数据表单是一个很基本的要求,使用集合标签将不可避免,不过幸运的是,它通常很简单,而且够用。特别值得一提的是PHP的模板技术和JDynamiTe技术利用html的注释标签很简单的实现了它,又保持了“所见既所得”的特性。下面是一些技术的比较:
Velocity
变量定义:用$标志表达式语句:以#开始强控制语言:变量赋值:#set $this = "Velocity" 外部引用:#include ( $1 ) 条件控制:#if …. #end非兼容语言
JDynamiTe变量定义:用{}包装表达式语句:写在注释格式(<!-- )中弱控制语言兼容语言
XSLT变量定义:xml标签表达式:xsl标签强控制语言:外部引用:import,include 条件控制:if, choose…when…otherwise非兼容语言
Tapestry采用component的形式开发。变量定义(组件定义):在html标签中加上jwcid表达式语句:ognl规范兼容语言
模板文件:模板文件指包含了模板语言的文本文件。模板文件由于其模板语言的兼容性导致不同
结果。与HTML兼容性的模板文件只是一个资源文件,其具有良好的复用性和维护性。例如JDynamiTe的模板文件不但可以在不同的项目中复用,甚至可以和PHP程序的模板文件互用。而如velocity的非兼容模板文件,由于其事实上是一个脚本程序,复用性和可维护性大大降低。拥有动态数据的数据对象:
模板文件包含的是静态内容,那么其所需的动态数据就需要另外提供。根据提供数据方式的不同可以分为3种:Map:利用key/value来定位。这个是最常见的技术。如velocity的VelocityContext就是包含了map对象。Example.vm:Hello from $name in the $project project.
Example.java:VelocityContext context = new VelocityContext();context.put("name", "Velocity");context.put("project", "Jakarta");
DOM:直接操作DOM数据对象,如XSLT利用XPath技术。POJO:直接利用反射取得DTO对象,利用JavaBean机制取得数据。如Tapestry。模板引擎:
模板引擎的工作分为三步:1. 取得模板文件并确认其中的模板语言符合
规范。比如velocity,确定#if有对应得#end等。
Xml+xslt的模型中,xml文件标签是否完整等。
Java 模板技术
在完成这些工作后,模板引擎通常会把模板文件解析成一颗节点树(包含模板文件的静态内容节点和模板引擎所定义的特殊节点)。
2. 取得数据对象。 该数据对象一般通过程序传递引用实
现。现有的大量框架在程序底层完成,处理方式也各自不同,有两种技术分别为推技术和拉技术。推技术:controller调用set方法把动态数据注入,模板引擎通过get方法获得,典型代表:Struts;拉技术:模板引擎根据配置信息,找到与view对应的model,调用model的get方法取得数据,典型代表:Tapestry。
3. 合并模板文件(静态内容)和数据对象(动态内容),并生成最终页面。
合并的机制一般如下,模板引擎遍历这颗节点树的每一个节点,并render该节点,遇到静态内容节点按正常输入,遇到特殊节点就从数据对象中去得对应值,并执行其表达式语句(如果有的话)。
以下详细说明:VelocityTemplate template = Velocity.getTemplate("test.wm");Context context = new VelocityContext();context.put("foo", "bar");context.put("customer", new Customer());template.merge(context, writer);
当调用Velocity.getTemplate 方法时,将调用ResourceManger的对应方法。
ResourceManger先查看该模板文件是否在cache中,如果没有就去获取,生成resource对象并调用process()方法,确定该模板是否有效,如果有效,则在内存中生成一个Node树。
当 调 用 t e m p l a t e . m e r g e ( ) 时 , 遍 历 这 颗Node树,并调用每个Node的render方法。对于模板中的变量和对象Node,还将调用execute()方法,从context中取得value。 注:ResourceManger在runtime\resource包下,Node在runtime\parser\node包下Tapestry
Tapestry比较麻烦,先介绍一下http请求的处理过程。
当 h t t p r e q u e s t 请 求 到 达 时 。 该 请 求 被ApplicationServlet捕获,随后ApplicationServlet通过getEngine取到对应的Engine,通过该engine的getService拿到对应的service,调用其service方法执行http请求。
每 个 s e r v i c e 通 过 R e q u e s t C y c l e 对 象 的ge tP age方法取得Page对象,并将其设置为该Cycle对象的active Page。之后service调用renderResponse方法执行输出。
renderResponse调用page的getResponseWriter(output)取得writer对象,并把它传给cycle.
renderPage(writer)方法,该方法调用page的renderPage方法。
Page执行renderPage时,首先判断是否有listener的请求,如果有则处理listener请求;然后调用BaseComponentTemplateLoader的process方法把模板文件载入并形成一个component节点树,依次执行节点的renderComponent方法。
每个component对象将通过ongl的机制取得对象属性。并把该值写入输入流。
例如:insert componentprotected void renderComponent(IMarkupWrit
er writer, IRequestCycle cycle) { if (cycle.isRewinding()) return; Object value = getValue(); if (value == null) return; String insert = null; Format format = getFormat(); if (format == null) { insert = value.toString(); } else{ try{ insert = format.format(value); } catch (Exception ex) { throw new ApplicationRuntimeException(
Tapestry.format("Insert.unable-to-format",value),this, getFormatBinding().getLocation(), ex);
} } String styleClass = getStyleClass(); if (styleClass != null) { writer.begin("span"); writer.attribute("class", styleClass); renderInformalParameters(writer, cycle); } if (getRaw()) writer.printRaw(insert); else writer.print(insert); if (styleClass != null) writer.end(); // <span> }getValue为取得insert的value属性。
三、JSP技术分析 1. JSP技术: JSP,一个伪装后的servlet。web server会对任何一个jsp都生成一个对应jsp类,打开这个类,就会发现,jsp提供的是一个代码生成机制,把jsp文件中所有的scriptlet原封不动的copy的到生成的jsp类中,同时调用println把所有的html标签输出。Test.jsp:
Java 模板技术
<html><head><title>jsp test</title></head><body><table width="226" border="0" cellspacing="0" cellpadding="0"> <tr><td><font face="Arial" size="2" color="#000066"> <b class="headlinebold">The jsp test file</b> </tr></td> </font> </table><body></html>Test_jsp.java:package org.apache.jsp;import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.jsp.*;import org.apache.jasper.runtime.*;
public class Test _jsp extends HttpJspBase { private static java.util.Vector _jspx_includes; public java.util.List getIncludes() { return _jspx_includes; } public void _jspService(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException { JspFactory _jspxFactory = null; javax.servlet.jsp.PageContext pageContext = null; HttpSession session = null; ServletContext application = null; ServletConfig config = null; JspWriter out = null; Object page = this; JspWriter _jspx_out = null;
try { _jspxFactory = JspFactory.getDefaultFactory(); response.setContentType("text/html;charset=ISO-8859-1"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out;
out.write("<html>\r\n"); out.write("<head><title>jsp test</title></head> \r\n");
out.write("<body>\r\n"); out.write("<table width=\"226\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\r\n "); out.write("<tr><td><font face=\"Arial \" size=\"2\" color=\"#000066\"> \r\n\t "); out.write("<b class=\"headlinebold\">The jsp test file"); out.write("</b>\r\n\t "); out.write("</tr></td></font>\r\n\t "); out.write("</table>\r\n"); out.write("<body>\r\n"); out.write("</html>"); } catch (Throwable t) { out = _jspx_out; if (out != null && out.getBufferSize() != 0) out.clearBuffer(); if (pageContext != null) pageContext.handlePageException(t); } finally { if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext); } }}
2. Taglib技术: Taglib作为jsp之上的辅助技术,其工作本质依托与jsp技术,也是自定义标签翻译成java代码,不过这次和jsp略有不同,它还要经过几个过程。
先来看一下,实现一个tag的2个要点: 1. 提供属性的set方法,此后这个属性就可以在jsp页面设置。以jstl标签为例 c:out value=""/,这个value就是jsp数据到tag之间的入口。所以tag里面必须有一个setValue方法,具体的属性可以不叫value。例如setValue(String data){this.data = data;}。这个“value”的名称是在tld里定义的。取什么名字都可以,只需tag里提供相应的set方法即可。2. 处理 doStartTag 或 doEndTag 。这两个方法是 TagSupport提供的。 还是以c:out value=""/为例,当jsp解析这个标签的时候,在“<”处触发 doStartTag 事件,在“>”时触发 doEndTag 事件。通常在 doStartTag 里进行逻辑操作,在 doEndTag 里控制输出。
在处理tag的时候: 0. 从tagPool中取得对应tag。
� 为该tag设置页面上下文。� 为该tag设置其父tag,如果没有就为
null。� 调用setter方法传入标签属性值tag,如果
该标签没有属性,此步跳过。� 调用doStartTag方法,取的返回值。
Java 模板技术
� 如果该标签有body,根据doStartTag返回值确定是否pop该标签内容。如果要pop其body,则:setBodyContent(),在之后,doInitBody()。如果该标签没有body,此步跳过。
� 调用doEndTag()以确定是否跳过页面剩下部分。
� 最后把tag类返还给tagPool。
tag类为:package my.customtags;import javax.servlet.jsp.JspWriter;import javax.servlet.jsp.PageContext;import javax.servlet.jsp.tagext.TagSupport;
public class Hidden extends TagSupport{ String name; public Hidden(){ name = ""; } public void setName(String name){ this.name = name; } public void release(){ value = null; } public int doStartTag(){ return EVAL_BODY_INCLUDE;}
public int doEndTag() throws JspTagException{ try{ pageContext.getOut().write(", you are
welcome"); } catch(IOException ex){ throw new Jsp
TagException("Error!"); } return EVAL_PAGE;}
}
Jsp页面:<my:hidden name="testname"/>
生成的jsp代码:my.customtags.Hidden _jspx_th_my_hidden_11 = (my.customtags.Hidden) _jspx_tagPool_my_hidden_name.get(my.customtags.Hidden.class);_jspx_th_my_hidden_11.setPageContext(pageContext);_jspx_th_my_hidden_11.setParent(null);_jspx_th_my_hidden_11.setName("testname");int _jspx_eval_my_hidden_11 = _jspx_th_my_hidden_11.doStartTag();if (_jspx_th_my_hidden_11.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) return true;_jspx_tagPool_my_hidden_name.reuse(_jspx_th_my_hidden_11);return false;
Taglib技术提供两个机制,Body和non-
Body导致了taglib的出现了两个分支:Display Tag和Control Tag, 前者在java code中嵌入了html标签,相当与一个web component,而后者则是另一种模板脚本。
四、两种技术方案的比较: 1. 技术学习难易度
模板技术。使用模板技术,第一点就是必须学习模板语言,尤其是强控制的模板语言。于是模板语言本身的友好性变的尤为重要。以下依据友好性,表现力以及复用性三点为主基点比较了一下几种模板技术。
Velocity:Turbine项目(http://jakar ta .apache.org/Turbine)采用了velocity技术。1. 友好性不够。理由: 强控制类型,出现页面显示控制代码和html混合。与Html的不兼容,无法所见即所得。遇到大的HTML页面,从一个 “#if”找到对应的 “#end”也是很痛苦的一件事情。2. 表现力强。理由:强控制语言。3. 复用性弱。理由:模板脚本和页面代码混合。
XSLTCocoon项目(http://cocoon.apache.org/)采用XML + XSLT的方法。CSDN社区也是采用此方案。1. 内容和显示风格分离,这点XSLT做的最好。2. 速度慢。理由:XSLT的使用XPath,由于是要解析DOM树,当XML文件大时,速度很慢。3. 友好性不够。理由:由于没有HTML文件,根本看不到页面结构、显示风格和内容。XSL语法比较难以掌握,由于没有“所见即所得”编辑工具,学习成本高。4. 表现力强。理由:强控制语言。5. 复用性弱。理由:xsl标签和html标签混合。
JDynamiTe1. 表现力中等。理由:弱控制语言。2. 友好性强。理由:所见即所得的效果。在模板件中的ignore block在编辑条件下可展示页面效果,而在运行中不会被输出。3. 复用性强。理由:利用html标签。
Java 模板技术
Tapestry1. 友好性中等。理由:整个Tapes t ry页面文件都是HTML元素。但是由于component会重写html标签,其显示的样子是否正确,将不预测。2. 表现力强。理由:强控制语言。3. 复用性强。理由:扩展了HTML元素的定义。
在JSP中大量的使用TagLib,能够使得JSP的页面结构良好,更符合XML格式,而且能够重用一些页面元素。但TagLib的编译之后的代码庞大而杂乱。TabLib很不灵活,能完成的事情很有限。TabLib代码本身的可重用性受到TagSupport定义的限制,不是很好。 另外是,我不得不承认的一件事是,TagLib的编写本身不是一件愉快的事情,事实我个人很反对这种开发方式。
2. 技术使用难易度 模板技术:模板技术本身脱离了Web环境,可以在不启动Web server得情况下进行开发和测试,一旦出错详细的信息易于错误的定位。由于模板引擎的控制,页面中将只处理显示逻辑(尽管其可能很复杂) JSP技术:工作在Web环境下,开发测试一定要运行web server。此外,一些TagLib能够产生新的标签,页面的最终布局也必须在web环境下才可以确定。测试时出错信息不明确,特别是TagLib得存在,极不容易定位。由于其本质是程序,很容易在其中写入业务逻辑,甚至于数据库连接代码,造成解耦的不彻底。
3. 总结模板技术更加专注于页面的显示逻辑,有效
帮助开发人员分离视图和控制器。在学习,开发和测试都更加容易。
JSP技术本身是一个早期的技术,本身并没有提出足够的方式来分离视图和控制器。相反,我认为其本身是鼓励开发人员不做解耦,因为在JSP代码中插入业务逻辑是如此的容易。
Java 模板技术
正如前文所介绍的,传统的基于C/S的Windows应用程序总是让客户面临着一些感觉很
是不爽的问题,如:部署问题、升级困难、维
护困难、安全问题,但是完全的WEB开发由于
HTTP协议的无状态特性——浏览器和服务器总
是在不停地执行Request和Response来营造一种
有状态、持续会话的假象,致使人们又开始怀恋
具有更强运算能力、本地存储能力和更稳定的通
讯能力的客户端程序了。
不得不说的是,宽带网络的出现在某种意
义促成了Rich Client的诞生。通过快捷的发布,
特定的通讯协议标准,Rich Client正以不可阻挡
的气势向人们重现着C/S模式下客户端程序的优
势。
Rich Client的发布
C/S架构下,客户端程序发布与维护一直比
较困难和繁琐。在版本更新以后,需要对客户的
客户端程序进行逐个下载安装及配置更新,这
是一个体力活,而这也一直是使用户大量选择
WEB程序的因素之一。
在Rich Client时代,由于宽带网络的便
利,在客户端尽尽需要从服务器端下载已经更新
好的程序运行,而不必理会繁琐的下载、安装和
配置的过程。
这里不得不提Java的是WebStart技术。
WebStart是让用户只需在网页上点击一个超
级链接就能运行一个Java桌面应用的技术。对于
一个拥有WebStart能力的Java应用来说,用户使
Rich Client技术简介awaysrain
用它就和使用WEB应用一样的简单,但它所具有
的界面能力和本地处理能力却是WEB应用无法望
其项背的。
具体的应用的技术知识可以从http://java.
sun.com中寻找相关文档,这里不一一赘述。
Rich Client的通信机制
除了快捷方便的发布外,Rich Client还需要
与服务器端建立一种快速、可靠、强大、易用
的通信交互机制。但我们开发WEB应用时,表
现层和业务服务层常常只是同一个进程中的不
同对象,它们之间的交互不过是Java的方法调用
而已,当表现层逻辑被分发到世界各地的计算
机上,客户端和服务器之间的交互就成了一个大
问题——从前的C/S被淘汰,很大程度上归咎于
socket通信的复杂性。
现在,形形色色的RPC(远程过程调用,
Remote Procedure Call)技术以独特的优势扮演
起了信使的角色。以下列举几种Rich Client可以
采用的通信机制。
CORBA和RMI
C O R B A(通用对象请求代理体系结构,
Common Object Request Broker Architecture)
曾经红极一时,它能够兼容各种操作系统平台的
语言,强大的的可扩展性所带来的负面影响就
是实现的复杂和繁琐。如果服务器端和客户端
都采用Java开发,那么CORBA所需要的语言无关
的IDL就完全变成了画蛇添足。当然,对于需要
集成大量企业内遗留的系统的EAI(企业应用集
成)项目中,它一直是首选的技术。
RMI可以是做CORBA的Java版本,但相比较而
言这是一个轻量级的版本了,对于服务器和客户
端两边都用Java来实现的前提下,这是一个非常
好的选择。
CORBA和RMI有一个共同的缺陷:通常不会在
系统80端口提供服务,所以这在具备网罗防火墙
的情况下显得非常被动。
XML-RPC
为了解决在系统的80端口提供RPC的服务,
而又不影响正在执行的WEB服务,人们想出了用
HTTP协议传输RPC包的办法。对于几乎是专门用
于传输文本的HTTP协议,要在其上传输RPC封
包,最方便的方法莫过于把RPC封包编码成文本
形式——例如XML文件。XML-RPC(http://www.
xml-rpc.com)是由美国UserLand公司指定的一
个RPC协议。它将RPC信息封包编码为XML,然后
通过HTTP传输封包;它是一个简单的RPC协议,
只支持一些基本数据类型,不支持对象模型,这
势必掣肘在客户端和服务器端之间传输复杂的对
象。
SOAP
SOAP即Simple Object Access
Protocol(简单对象访问协议)是在分散或分
布式的环境中交换信息的简单的协议,是一
个基于XML的协议。它允许所有的操作在80端
口上进行,从而也绕过了防火墙等问题。
Rich Client 技术简介
JavaeMag我 们 期 待 着 您 的 加 入 !
请和我们联系 投稿信箱[email protected] 杂志主页http://emag.csdn.net/Default.aspx?tabid=57
给您的智慧库加点货
S O A P规范中有三个基本组成部分:
S O A P 封 装 , 编 码 规 则 , 以及
在 请 求 和 响 应 之 间 的 交 互 方 式 。 目前已有的基于J A V A提供S O A P功能
的产品有:Apache SOAP, IBM SOAP4J等 要了解更多关于SOAP的信息,可以访问 http://
www.w3.org/TR/SOAP
Hessian
Hessian(http://www.caucho.com)是由
Resin应用服务器的开发商Caucho公司制定的一
个RPC协议,虽然它也是通过HTTP协议传输RPC封
包,但是它的RPC封包却是以二进制形式编码
的,而且能够表现对象模型和异常体系,这就使
得Hessian比XML-RPC具有更高的效率。
具体通信机制资料请读者参考网上内容和透
明于2004年5期《程序员》杂志中《王朝复辟还
是浴火重生》一文。
Rich Client开源开发平台Laszlo
Laszlo是一个开源的Rich client开发环境。
使用Laszlo平台时,开发者只需编写名为LZX的
描述语言(其中整合了XML和JavaScript),
运行在J2EE应用服务器上的Laszlo平台会将其
编译成FLASH文件并传输给客户端展示。单从
运行原理来说,Laszlo与XUL(XML用户接口语
言, XML User interface Language)、
XAML(“Longhorn”)标记语言很类似。但它的
最大优势在于:它把描述语言编译成FLASH,而
FLASH是任何浏览器都支持的展示形式,从而一
举解决了浏览器之间的移植问题。而且,在未来
的计划中,Laszlo还可以将LZX编译成Java或.
NET本地代码,从而大大提高运行效率。
具体请参考http://www.openlaszlo.org。
IBM AlphaWorks网站近日发布了用于开发
Laszlo应用程序的集成开发环境(实际上是一
个Eclipse插件),使J2EE开发者能够在他们熟
悉的Eclipse环境中快速开发基于Laszlo的rich
client应用程序。可以在下列地址下载该插件:
http://alphaworks.ibm.com/tech/
ide4laszlo
此外,AlphaWorks网站还提供了一个用
Laszlo开发的示例应用,展示了在Eclispe环境
下开发Laszlo应用的过程。demo的地址如下:
http://dl.alphaworks.ibm.com/technologies/
rcb/demo.html
FLEX
Flex是Macromedia公司开发的,用于
Rich client开发的环境,其原理是将MXML(the
Macromedia Flex Markup Language)文件,
编译成SWF文件,然后显示在浏览器中,并利
用Web Service技术和服务器通信。从而利用
Flash的强大功能,带来更丰富的用户体验。 F l e x 官 方 说 法 如 下 ( 摘 自 网络
上相关文章): 原 代 号 为 “ R o y a l e ”的MacromediaFlex软件将把服务器软件、开发指
南和其他工具组合在一起,使传统的网络应
用开发人员能够用Macromedia公司的Flash格
式创作软件单元。如从前报道的那样,该
产品的重点是让那些使用Sun微系统公司的
Java2企业版(J2EE)的开发人员能够创作出更
有吸引力、更容易导航的J2EE应用软件接口。 Flex将使J2EE开发人员使用标准的文
本式开发工具来制作Flash应用程序,而不
必使用Macromedia公司以前出售的复杂的设
计工具。Macromedia公司从今年年初开始,
努力扩大Flash格式对于主流开发商的吸引
力,其目标是扩大Flash的用途,使其成为
提供互联网应用和建立交互式网站的基础。 Macromedia公司计划在2004年上半年
推出Flex服务器软件,该软件的价格目前还
没有确定。它的初级版本将运行于J2EE中,
并计划随后推出支持微软的.Net格式的版
本。最初的支持者包括IBM公司,它将随自
己的WebSphere软件一起推广Flex的应用。
需要了解更多Flex技术的朋友可以访问
Flex的主页:http://www.macromedia.com/
software/flex/
Thinlet
Thinlet是一个采用Applet解析XUL并提供
相应界面的解析器,在事件发生时,调用用户自
己的事件处理程序(java 程序),需要客户端浏
览器支持Applet。更多信息可以参考 http://
www.thinlet.com/
Rich Client 技术简介
对话:Web技术的思考透明/编著
Steven:嘿,Weber,你最近忙什么呢?
Weber:哦,我刚做了一个项目,用WebWork做的,感觉挺好。
Steven:WebWork吗?我知道它,它有什么好的?
Weber:好处可多了,比Struts强太多了。你用Struts那么久,难道就不觉得有什么不舒服的吗?
Steven:恩……确实有一些。比如说,Struts的ActionForm其实不太好用,有点不伦不类的,平白的在action和view之间引入了麻烦。Struts最近的设计也逐渐在淡化ActionForm的作用了。
Weber:是呀。而且Struts的不爽的地方还有接口比较难看。action必须要实现继承,到现在也没有改为接口继承。而且execute方法的接口也全是HttpServlet...,不能脱离servlet container,要测试还得提供mock的request,真是麻烦。
Steven:Struts由于要重用action的实例,因此不得不把所有状态从action里剔除,从而需要每次都传入request/response,这是一个典型的无状态设计,为pool和负载作了准备,理论上讲性能的延展性要更好一些。Struts由于每次都要处理request/response,所以必须提供一些工具方法,于是Action不再是接口,而改成一个class,这个设计在ood里也是常用的手法。如果没有这些接口,又怎么在servlet和action之间传递数据呢?
Weber:这就是WebWork的设计精彩之处了。action都是普通的JavaBean,它们只实现自己的业务功能,其他基础设施级的功能——例如怎么与servlet交换数据——都是用拦截器来实现的。正是因为有这个拦截器机制,所以WebWork才这么好用呢。
Steven:不过我看WebWork提供的功能还是比较少,比如它自己就没有数据校验的能力,必须要用别的工具来帮助校验。
Weber:没错,但这种功能都可以用拦截器机制来做,你可以把这些拦截器抽象出来复用。所以WebWork本身不需要包含那么齐全的功能,它只提供了一个灵活的核心,很多功能都可以做成插件插进去。而Struts就比较麻烦
本文的内容来自各种渠道,有朋友非正式的讨论与邮件往来,也有网络上的各种资料,还有开发者们口耳相传的实践经验。为了方便读者,我不揣冒昧将它们整理成对话的形式,并借了两个虚构人物(WebWork的爱好者Weber和Struts的老用户Steven)之口来比较这两种流行的web框架,希望对读者的选择有所帮助。
了,新加一个功能就会伤筋动骨,所以Struts老是有很多新特性要发布呢。
Steven:是的。最近Struts又放出消息,未来的版本将增加对JSR-168 portlet的支持。
Weber:这个问题在WebWork里根本就不成问题。只要做一个portlet作为引擎,再修改几个配置,所有的WebWork action都可以原封不动地移植到portlet环境,因为它们原本就是最普通的JavaBean,根本就不知道外面的环境究竟是servlet环境还是portlet环境。由于action不依赖具体的运行环境,所以单元测试也很方便,直接把action new出来,把参数设置进去就可以测试了。
Steven:说起测试嘛,抛开先富起来的地区不说,起码中国还有1/3的软件企业处在对TDD懵懂的阶段吧?还有1/3的企业在追捧CMM和一些瀑布模型的开发方法吧?那么对于这些企业,Struts和WebWork在易测上的差异他们是感受不到的。当前的状态下,易测性并不是软件企业技术选型的一个重点目标,那么Struts就有了其生存的土壤。当然这就扯得有点远了。
Weber:你说得很有道理。Struts好在够多的人支持、使用,让人觉得够稳定、保险、有保障。要是做个项目,很多老板一定说,我要的不是新技术,要的是稳定。所以我现在也还常常在用Struts开发项目。
Steven:看来我也应该多了解一下WebWork。如果以后采用TDD的开发方法,可测性的确是很重要的因素,那时也许我就会选择用WebWork了。
Weber:还有一种折中的办法,就是改造Struts,给它加上拦截器机制,然后再用拦截器来实现Dependency Injection,这样可以把Struts变得跟WebWork一样易用,而且又不会损失它原来的功能,实现起来也不算复杂。
Steven:确实不错。这么一来,我的工具箱里又多了一种可选的方案了。
附 录 : 关 于 S t r u t s 与 W e b W o r k 之 间 的技 术 比 较 , 请 看 下 列 两 个 地 址 : h t t p : / /udoo . 51 . ne t /m t / a r ch i ve s / 000044 . h tm l http://wiki.opensymphony.com/display/WW/Comparison+to+Struts
JPetStore项目分析:一个典型的J2EE应用Web层的实现
枯木
关于JpetStore(http://www.jfox.cn/jpetstore/ h t t p : / / w w w . i b a t i s . c o m /jpetstore/jpetstore.html)
JPetStore是Sun公司开发的 , 在 大 众 化 的 宠 物 商 店程 序 基 础 上 完 全 重 写 的 ,最早的基于J2EE平台的宠物商 店 网 络 应 用 服 务 程 序 。与其它的PetS to r e最本质的不同是JPetStore使用了类似Microsoft网络宠物商店的设计,但是在骨子确是完全不一样的网络服务构架,可以说这种构架完全体现了Java程序在网络应用方面的优势,故在此以它为例,从Java的底层实现方面来分析 J2EE在Web层的应用。
系统架构概览JPetStore以一个小型宠物
商店电子商务平台为原型向大家展示了一个带有简单数据库连接的J2EE Web层的应用,它集成了Web层应用领域所必须具备的要素,如:用户ID认证、商品信息查询、商品信息列表等等。总之,JPetStore是一个集客户购物,下订单,管理为一体的简单且典型的电子商务的例子。
当然,现实中的系统不会如此简单,大多数的系统要和多个数据源和其他的EIS(企业信息系统)相连通的,有着比较复杂的数据库系统
的设计与连接。比如说,订单的提交可能是一个公司,信用卡服务可能另外的公司,运送又是一个公司,这样的话,数据系统的设计就很复杂(一般来说这就需要专门的DBA进行数据库的设计)。然而作为窥探J2EE的一个窗口,了解J2EE的运作,JPetStore足以胜任。
JPetStore划分成了多个模块,采取了松耦合的设计架构,允许存在多个数据源及EIS进行信息交互。它的结构相对简单,总共包括4个部分:
• 购物站点• 管理。如:销售统计,手工接受/拒绝订
单• 订单处理
通过JMS接受/处理订单消息用java mail来通知客户通过JMS发订单给供应商并修改订单数据库的相应信息
• 供应商通过JMS接受订单派送货物给用户提 供 一 个 基 于web的库存管理维护库存数据库
系统架构解析宠物商店的网站
服务采用自上而下的构架,其最上层是由WAF(Web Application Framework)控制应用屏幕的跳转,进而产生视图,然后再调用商业组件来实现流程的处理(整
体结构如图1所示)。
图1 JPet Store 总体框架结构
WAF提供众多Web应用所需的服务,其中包括请求过滤和分发、产生模板视图、可重用的Taglib,以及屏幕流程控制等等。应用组件封装
了处理的逻辑,它们代表了商业的数据,并且可以对这些商业数据进行必要的操作;实体EJB代表了商业应用的实体,包括客户、地址、账目等;会话EJB提供了一些方法,如用户登陆、输出用户信息、管理购物车等;其它会话EJB提供了一些通用的方法,如产生唯一标示符等。传统的JavaBean组件演变成了值对象,在EJB组件和应用间进行传递数据,成功地处理了各个对象的封装,有效地降低了程序设计的难度。而XML文档类则用来处理订单信息。
这个例子的WAF是对J2EE蓝图Web层规范的一个有效实现。一个通用的Web层处理一般可以划分为四个步骤(如图2所示):
◆ 解释一个请求 ◆ 执行一个商业逻辑处理 ◆ 选择下一个视图 ◆ 产生这个视图
图2 WAF的Web层处理
下面将分析宠物商店各个模块的设计 ,宠物商店主要的一些独立组成模块(如图3所示): ◆ 控制模块 用来分发请求到各个业务处理逻辑、控制屏幕跳转、处理对应的组件及用户 ◆ 登录和注册 控制模块由WAF实现和扩展 ◆ 购物车模块 购物车跟踪用户购物的过程 ◆ 登录模块 确定用户登陆的页面
◆ 消息模块 从宠物商店到定单处理中心用来异步传输订单
◆ 类别模块 根据用户查询需求提供一个类别视图
◆ 客户模块 表示客户信息,如地址、信用卡、联系方式等
JPetStore项目分析
图3 JPet Store 模块设计
在图3中,控制模块将控制所有的交互信息和执行信息,每个用户会话都有一个购物车与之相对应。
宠物商店的组件主要有以下几个部分: ◆ EJB 代表了商业数据和商业逻辑的处理 ◆ JSP页面 定义了整个应用视图的框架模板(template.jsp),由模板组成的各个JSP页面文件,以及各种被引用的图形文件 ◆ XML文件 用来定义屏幕、屏幕跳转控制、绑定URL到某个HTML Action、定制signOn以及J2EE的部署 ◆ Servlet过滤器 用来校验用户安全登陆和输出的编码 ◆ 异步信息发送组件 传输使用XML封装的订单到订单处理中心
◆ 安装程序 用来产生例子数据库。JpetStore系统的实现
前面介绍的是JpetStore所涉及的J2EE Web构架方面的常识以及JpetStore系统构架的一些信息,接下来,让我们进一步来了解一下JpetStore系统的实现。
首先,来认识一下JpetStore实现代码整体文件框架(图4,见下页):
下面让我们对其中的一些实现的技术进行更深入的探讨。JPetStore 程序包括基于Spring规范的数据源,DAO层和交互式页面。你
可以在XML文件内看到一序列的Bean,其标识如:<form-beans> </form-beans>或<bean> </bean>, 例如struts-config.xml中: <form-beans> <form-bean name="accountBean"
type="com.ibatis.jpetstore.presentation.AccountBean"/>
<form-bean name="catalogBean" type="com.ibatis.jpetstore.presentation.CatalogBean"/>
<form-bean name="cartBean" type="com.ibatis.jpetstore.presentation.CartBean"/>
<form-bean name="orderBean" type="com.ibatis.jpetstore.presentation.OrderBean"/>
</form-beans>下面是jPetStore的Spring层的三个结构文件
及所实现的功能, 如图5所示:Presentation Layer
界面交互Business Layer
Domain Model 实现Data Layer
Dao 实现web.xml
struts-config.xml
JPetStore项目分析
Dao.xml
JpetStroeDomain 的结构(如图6):
图6 JpetStrore业务类
其内部都是对一些涉及到的实体的属性进行定义,并实现数据封装。如:Account.java
public class Account implements Serializable {
/* Private Fields */ private String username; private String password; …… /* JavaBeans Properties */ public String getUsername() { return username; } public void setUsername(String username) { this.username = username; }}JpetStroe Persistence是对Domain业务的操作
的实现。包括接口(iface)和实现(sqlmapdao)。如AccountDao.java和AccountSqlMapDao.java
public interface AccountDao {
public Account getAccount(String username); ……}
public class AccountSqlMapDao extends BaseSqlMapDao implements AccountDao {
…… public Account getAccount(String username) {
return (Account) queryForObject("getAccountByUsername", username);
} ……}
JpetStroe Presentation定义了Formbean,例如AccountBean.java就为Account.java实现了Formbean。在进行构架分析之前,先来了解一下MVC构架(如图6):MVC(Model/View/Controller,MVC)构架允许一个开发者将一个可视化接口连接到一个面向对象的设计中,而同时还可以避免我们上面讨论的几个问题。MVC通过创建下面三个层将面向对象的设计与可视化接口分开:
模型(Model):模型包含完成任务所需要的所有的行为和数据。模型一般由许多类组成并且使用面向对象的技术来创建满足五个设计目标的程序。
界面(View):一个界面就是一个程序的可视化元素,如对话框、选单、工具条等。界面显示从模型中提供的数据,它并不控制数据或提供除显示外的其它行为。一个单一的程序或模型一般有两种界面行为。界面从模型内的对象中显示数据。这些对象的改变可以通过也可以不通过用户的交互操作来完成。如:在一个Web浏览器中负责接收页面的对象收集和装配栈中的信息,必须有某种方式来让这些对象通知界面数据已经被改变了。在模型变化时有两种方法来对界面进行更新。
控制器(Controller):控制器将模型映射到界面中。控制器处理用户的输入,每个界面有一个控制器。它是一个集接收用户输入、创建或修改适当的模型对象并将修改在界面中体现出来的一种状态控制的集合体。控制器在需要时还负责创建其它的界面和控制器。控制器一直决定哪些界面和模型组件应该在某个给定的时刻是活动的,它一直负责接收和处理用户的输入,来自用
JPetStore项目分析
户输入的任何变化都被从控制器送到模型。
通 过 以 上 的 介 绍 , 现 在 让 我 们 来 分 析JpetStore是如何实现MVC,并进行流程处理的。首先是整个系统的配置,它是由一些xml文件来实现的。如:
<form-beans> <form-bean name="accountBean"
type="com.ibatis.jpetstore.presentation.AccountBean"/>
<form-bean name="catalogBean" type="com.ibatis.jpetstore.presentation.CatalogBean"/>
<form-bean name="cartBean" type="com.ibatis.jpetstore.presentation.CartBean"/>
<form-bean name="orderBean" type="com.ibatis.jpetstore.presentation.OrderBean"/>
</form-beans>其次来看一看Controller层(在struts文件夹
中),如:basebean.java public abstract class BaseBean extends
ActionForm { …… public void reset(ActionMapping mapping,
ServletRequest request) { stMap.put("errors",errorList); validate(); errorList = (List) requestMap.
get("errors"); ActionErrors actionErrors = null; if (errorList != null && !errorList.
isEmpty()) { actionErrors = new ActionErrors(); actionErrors.add(ActionErrors.GLOBAL_
ERROR,new ActionError("global.error")); } return actionErrors; } …… } 再来接触一下Formbean(在Presentation文件
夹中),如AccountBean.java public class AccountBean extends BaseBean {
/* Constants */ private static final AccountService
accountService = AccountService.getInstance(); ……
/* Private Fields */ private Account account;
JPetStore项目分析
……
/* Static Initializer */
static { List langList = new ArrayList(); …… }
/* Constructors */
public AccountBean() { account = new Account(); }
/* JavaBeans Properties */
public String getUsername() { return account.getUsername(); }
…… public void validate() {//错误信息反馈 ……
}}JpetStore便是通过以上的基本技术来实现
MVC的基础构架的。 小结
从Java应用于Internet开发的发展历程来看,可将其开发方法划分为三个阶段:第一阶段是将业务逻辑和表现逻辑完全集成在一起,采用HTML、JSP和Servlets技术开发;第二阶段是将业务逻辑和表现逻辑分开,采用HTML、JSP、Servlets、JavaBeans Compoments和Custom Tags技术开发;第三个阶段是MVC设计模式(J2EE的开发方法)。透过JpetStore这个比较简单项目,我们就已经能够充分体验到了J2EE开发方法所带来的便利性,难怪,在网络技术高速发展的今天,新技术层出不穷,可J2EE开发技术始终占据着主流的霸主地位。
业界动态
BEA eWorld现场报道:第一天
12月15日,上海阳光明媚,室外气温17度。BEA eWorld大会将于明天开幕。本次大会BEA高层悉数来华,可见对此次大会的重视程度。本次大会的主题有三个。第一,Diablo;第二,SOA;第三,开发者关系。
Diablo不是暗黑破坏神的游戏,是将于明天发布的WebLogic Server 9.0的代号。12月7日在旧金山,BEA曾经把Oracle OpenWorld大会的一群演讲者拉到一家宾馆,向他们展示了Diablo的beta版本,也引起了这些技术专家们很大的兴趣。据来自各方面的零星消息称,新版本的WebLogic应用服务器支持J2EE 1.4规范,并且据称对SOA有特别优化的支持,实际上是用基于JMS的消息存储和转发机制实现了WS-ReliableMessaging规范——这是一个关于“提供可靠异步消息”的web service规范。据BEA产品执行副总裁黄卫文(Wai Wong)声称,在这个消息机制基础上搭建的企业服务总线(Enterprise Service Bus,ESB)在效率方面比以前的版本至少有三到四倍的提升。正是因为有这样一个可靠并且高效的消息总线,BEA一直推崇的SOA才能成其为现实,这也是为什么说Diablo这款应用服务器尤其适合SOA的原因。
另外,BEA尤其强调这款服务器的热部署(hot-deploy)能力。在本次eWorld大会的宣传资料中,笔者看到一份题为“流体计算”(Liquid Computing)的材料,极言BEA对于企业应对快速变化的帮助,Diablo强大的热部署能力应当是其中重要的技术环节。众所周知,过去WebLogic处理EJB采用了“预编译”的做法,即首先用ant脚本自动根据业务代码生成home对象等EJB必要的代码,然后部署到应用服务器,这种实现策略的一大缺陷就是无法实现有效的热部署。此次BEA重点宣传Diablo的热部署能力,再加上此前BEA招聘了AspectWerkz的两位核心开发者,令人不禁联想Diablo的核心是否采用了AOP技术,而彻底放弃了预编译的做法。这个猜想在未来的两天内即将得到确认。
除了Diablo的发布之外,SOA的应用也是本次大会的重点话题。在整个2004年,SOA的概念已经
被各大厂商炒得沸沸扬扬,但开发者们普遍仍处于观望阶段,希望看到在实际项目中应用SOA的架构示例。在未来两天的大会中,BEA将向开发者们展示电信、金融、政府应用等行业的SOA应用实例。
另外,BEA开发者日活动将是此次大会的重头戏之一。BEA一向在开发者群体中有不错的口碑,这次来华的BEA高层除了创始人兼CEO庄思浩(Alfred Chuang)、全球产品执行副总裁黄卫文等人之外,还有CTO办公室首席技术专家Michael Smith和负责开发者关系的Scott Regan等技术人士。CSDN网站将对这两位技术专家进行独家专访,就开发者感兴趣的新一代应用服务器技术架构、BEA中国开发者活动策略、BEA中国人才计划等话题展开讨论。请CSDN网友继续关注记者的后续报道。
开发你的Rich Client:IBM对Laszlo开源富客户端平台提供支持
IBM AlphaWorks网站近日发布了用于开发Laszlo应用程序的集成开发环境(实际上是一个Eclipse插件),使J2EE开发者能够在他们熟悉的Eclipse环境中快速开发基于Laszlo的rich client应用程序。可以在下列地址下载该插件:
http://alphaworks.ibm.com/tech/ide4laszlo
此外,AlphaWorks网站还提供了一个用Laszlo开发的示例应用,展示了在Ecl i spe环境下开发Laszlo应用的过程。demo的地址如下:
http://dl.alphaworks.ibm.com/technologies/rcb/demo.html
Laszlo是一个开源的rich client开发环境。使用Laszlo平台时,开发者只需编写名为LZX的描述语言(其中整合了XML和JavaScript),运行在J2EE应用服务器上的Laszlo平台会将其编译成FLASH文件并传输给客户端展示。单从运行原理来说,Laszlo与XUL、XAML很类似。但它的最大优势在于:它把描述语言编译成FLASH,而FLASH是任何浏览器都支持的展示形式,从
而一举解决了浏览器之间的移植问题。而且,在未来的计划中,Laszlo还可以将LZX编译成Java或.NET本地代码,从而大大提高运行效率。近日Laszlo的CTO David Temkin接受了一个采访,对Laszlo的战略和发展做了更多的介绍:
http://news.com.com/David+vs.+Goliath+vs.+Goliath/2008-7344_3-5457982.html
JSP 2.1和JSF 1.2规范发布预览版本
J2EE的两种重要的表现层技术JSP和JSF发布了新技术规范的预览版本,其中最重要的一点是两者将表达式语言(Expression Language,EL)部分合二为一。在不久的将来,这两种技术有可能更进一步地彼此融合,成为一种统一的表现层技术。然而在J2EE社群的普遍观点中,如果单单作为一种视图技术,JSP并不是最佳的选择,Velocity和XSLT等基于模板的视图技术通常比JSP更方便;而基于组件的JSF也面临广泛的信任危机。两者的组合是否能得到业界的认可,还需要时间的检验。
以下是官方公告
我们很高兴向大家宣告,JavaServer Pages、JSR-245下开发的Faces.JavaServer Pages(JSP)2.1和JSR-252下开发的JavaServer Faces(Faces)1.2的新版规范的Early Draft Review发布。
JSP 2.1把Expression Language(EL)输出到它自己各自分离的文档中,在技术上,这些文档是JSP规范的子文档。这些统一的EL规范定义了一个更高层的java 包,javax.el。这个包与使用它的技术之间完全独立,并且允许此技术将自身插入EL处理过程。更改的JSP规范遵从使用标准化EL的规范。
对于前面提到的JSR-252,这个规范并没什么新特性。Faces 1.2支持新的标准化EL,还包含一些bug修复的相关规范。
Faces和JSP在JSRs下的结盟带来了一些新功能,也为将来的发展打下了坚实的基础。例如,在同时使用Faces和JSP的web应用中,网页仅使用JSP(不包含任何faces内容)来访问Managed Beans成为可能。在JSP规范的附录E中和Faces规范的前言中都可以看到更改内容的细节。
JSP 2.1 EDR (JSR-245) http://jcp.org/aboutJava/communityprocess/edr/jsr245/ Faces 1.2 EDR (JSR-252)
http://jcp.org/aboutJava/communityprocess/edr/jsr252/
Google大举挖进Java人才 BEA首席架构师投奔
不久前,Google刚刚挖到了Joshua Bloch,他是Effective Java的作者,他设计的Collections框架也是迄今最受好评的类库框架之一。同时他还是JSR175(A Metadata Facility for the JavaTM Programming Language)的首席专家。
紧接着,Google又把BEA的首席架构师Adam Bosworth拢入自己旗下。Bosworth在软件行业作为技术主管受到广泛的尊敬。在为新创企业Crossgain(2001年被BEA收购)工作之前,Bosworth曾在微软任职数年,并成功地从事于一些项目的开发,如微软的Access数据库。
他的跳槽来得太突然了,两个月以前,他还在供应商的“年度eWorld秀”中担任重要角色,并他的主题演讲中介绍Alchemy项目----一个建立下一代移动浏览器的计划。
Google的招兵买马计划一直在有条不紊的进行着,曾在SUN微系统工作的David Stoutamire,现 在 在 G o o g l e 工 作 。 就 在 上 星 期 , N e a l Gafter,SUN公司的javac主管,也离开SUN转向Google。
不仅是Java方面,Greg Stein,曾是CollabNet项目经理,管理Subversion 项目并且发布了他们的SourceCast产品,现在在Google的博客软件组工作;Rob Pike,曾是贝尔实验室最初Unix团队成员之一,参与过Plan 9 和Inferno操作系统的开发,如今也投奔Google。
G o o g l e 一 直 渴 求 人 才 , 对 于 开 发 者 来 说 ,Google也是一个充满吸引力的地方。他只雇佣最棒的、最聪明的、近乎于天才的那些家伙,在笼络人才这方面,也只有微软可与之媲美。最近Java人才不断涌入Google究竟是巧合,或是Google准备尝试基于Java做一些事情,我们拭目以待。
eMagJavaI’m loving it