,,
(1.江南大学信息工程学院,江苏 无锡 214122;2.中国船舶科学研究中心,江苏 无锡 214082)
众所周知,软件开发人员和页面设计人员往往因为一些重合的业务逻辑功能而交织在一起不能独立工作,以致于页面设计人员不能把精力集中在使用标签创建网站上,而软件开发人员也不能将精力完全集中在底层功能的实现上[1];此外JSP和Servlet 作为J2EE 平台中生成动态WEB的技术,虽能产生相同的效果,但JSP在内容中嵌入了逻辑,从而降低了JSP页面的可读性、可维护性和可复用性,而Servlet在逻辑中嵌入了内容,以至于其逻辑处理混乱,可读性差,同时也给表示内容的维护带来了不利。虽然JavaBean的引入很好地解决了JSP和Servlet面临的两难处境,使J2EE平台Web组件的开发很好地实现了内容和表示、业务逻辑的分离,但JavaBean只是可复用组件,无法获取运行环境信息。基于以上问题,引入自定义标签是一个很好的解决途径,可以使Web组件开发中的表示和业务逻辑代码相分离,大大增强了代码的可读性、可维护性和可复用性。
当一个含有自定义标签的JSP页面被编译成servlet时,实际上标签被转化成了对标签处理类的操作,即JSP引擎会根据页面指令<%@ taglib>去访问TLD的相关处理程序信息,调用它的构造器方法,启动标签处理程类,读取其属性和相应值。如果是第一次使用,则对每个属性都调用set方法。然后JSP服务器调用处理程序的doStartTag(),再调用doEndTag()方法。最后在页面结尾调用release ()方法,清理占用的所有资源。
因此定制标签的首要组件就是要写一个实现标签功能的JAVA类,即标签处理器。它必须实现Tag接口或BodyTag接口[2],但通常的做法是继承TagSupport或BodyTagSupport类,具体使用什么接口或者抽象类取决于开发人员定义的方法所预期的生命周期。
其次写一个.tld格式的标签库描述器组件。它定义了标签库的名称、版本信息、描述信息各标签的名称、对应的标签处理器,标签的属性等内容。
另外,还需要一个部署标签库的xml文档,是用来注册描述标签库相关信息的文档,一般在WEB服务器启动时被加载到内存中。WEB服务器依此来调度资源响应用户的请求。
其通常实现的接口有:javax.servlet.jsp.tagext.Tag以及其扩展接口IterationTag和 BodyTag。为了简化用户开发通常在javax.servlet.jsp.tagext包中还提供了等一些支持类,方便标签处理器实现众多的方法。
通常标签的信息存储在一个用XML编写的TLD文档中。因为标签库是一些标签的集合,它们之间有一定联系,加之标签处理器的方法有很多是容器回调方法,编写完标签后,只有向WEB容器提供关于标签的信息,才能在用户引用时,WEB容器知道怎样调用相应的方法来处理客户的请求[4]。因此将其相应的信息放在一个说明文件里有利于引用时的操作。
由于标签库使用TLD文件描述,因而部署标签库实际上就是要发布TLD文件,通常把它放在WebContent下的WEB-INFO文件夹里。在web.xml文件中提供标签库描述文件的路径[4]。
本文结合的项目是ShipDB_Test系统的子系统,船舶性能试验数据库系统,其具体需求见图1。
船舶试验数字化平台是一个基于SOA(Service-Oriented Architecture)架构的试验数据服务平台系统,它实现了多个实验室的试验系统的无缝集成。由于该服务平台包含多个子系统,因此在项目的集成过程中,项目的登录成了需要解决的焦点,既不能影响其它系统的登录,还得在登录后能实现原本的权限控制以及功能实现。因此,在平台的开发过程中定义了一个标签库,实现的主要功能有:实现不同用户登录及操作的权限控制,与其他系统集成后的单点登录(集成后,标签中需要修改相应的参数)等。在此仅给出选择不同的语言登录标签的实现过程。
图1 船舶性能试验数据库系统结构
相应标签处理程序的部分源码如下:
public class EnglishTag extends TagSupport {
/** Instance Variables、Property
* Public Methods */
public int doStartTag() throws JspException {HttpServletRequest request=(HttpServletRequest) pageContext.getRequest(); getAttribute(org.apache.struts.Globals.MODULE_KEY);
HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
String path = new String();
path = request.getServletPath();
String html = “”;
html=“
StringBuffer results = new StringBuffer(html);
/* jspwriter是一个隐含对象,用于向jsp网页输出内容 */
JspWriter writer = pageContext.getOut();
try {
writer.print(results.toString());
} catch (IOException e) {
throw new JspException (messages.getMessage(“linkItem.io”, e.toString())); return (EVAL_BODY_INCLUDE);}
public int doEndTag() throws JspException {
JspWriter writer = pageContext.getOut();
try {
writer.print(“”);
} catch (IOException e) {
throw new JspException
(messages.getMessage(“link.io”, e.toString()));}
return (EVAL_PAGE);}
public void release() {
super.release();//释放占用的所有资源
action = “Show”; }
}
选择其他语言登录的标签处理器类和以上是很相似的,其不同点是 “language=eng&path”中value值的变化。
/*标头信息 */
/*标签库定义信息 */
/*标签库信息*/
/*标签属性集*/
将rmc.tld文件放在WebContent下的WEB-INFO目录下,接下来就可以在JSP文件中导入定制的标签库和定制的标签了,操作的语法如下:
/*导入标签库*/
<%@ taglib uri=“/WEB-INF/rmc.tld” prefix=“rmc” %>
/*引用标签*/
本文的创新之处在于以简单明了的自定义标签来实现原来项目中繁杂冗余的源码功能。例如,在SOA开发中,多个系统集成而又不影响各个独立系统的登录的做法可以利用单点登录的解决方案,即所有应用系统的“统一登录”,用户一次登录即可访问其有权访问的系统资源,提高了企业的工作效率。通常的做法就是利用专门的工具来实现。而在本系统中采用标签来实现如此繁琐的功能,不仅使项目变得简单易维护,也使得Web组件开发中的表示和业务逻辑相分离[5],提高了开发效率。
[1] David M geary.JSP高级开发与应用[M].贺民,译.北京:科学出版社,2006.
[2] 戎 伟,张 双.精通Struts-java流行服务器、框架、工具及整合的应用[M].北京:人民邮电出版社,2006.
[3] 张新曼.精通JSP—WEB开发技术与典型应用[M].北京:人民邮电出版社,2006.
[4] 邱 哲,王俊标,马 斗.Struts Web设计与开发大全[M].北京:清华大学出版社,2006.
[5] 梁爱虎.基于服务总线的Struts+EJB+Web Service整合应用开发[M].北京:电子工业出版社,2006.