上一级首页下一级

4 表现层的实现

表现层主要使用Struts框架完成,Struts分两个部分,一部分是视图View的实现,基本是JSP页面。另外一部分是控制器部分,具体是Action类的继承者,通过Action类的实现将前台界面用户输入的表单数据打包成基本对象,递送到后台处理。

在图4-11中,pageAction负责两种页面的输出。一个是创建页面;另一个是编辑页面。由于这两个页面数据结构类似,因此采取统一的Action控制输出。当用户填写好创建页面或编辑页面的表单数据后,单击确认按钮后提交,系统将由SavePageAction负责表单数据的处理和保存。

在SavePageAction中,主要是从用户输入的表单数据PageForm中获取数据,检查合法性,然后将这些数据封装成基本业务对象(MenuBodu等),委托给PageHandler实现真正的逻辑处理。而从图4-10中可以看到PageHandler以后的处理过程。

4-11 表现层的流程图

关于Struts的开发工具Jbuilder 8以后版本支持Struts开发也可以使用其他工具甚至文本编辑器进行相关JSP 和Javabean的编辑。Struts的使用涉及面比较多,而且有不少类似“暗沟”的机制,只要耐心地去理解,相信会熟练掌握。

1 Struts相关设置

1.创建一个自己的项目目录。

要在自己硬盘上建立如下图结构的目录系统:

CMS

|

|--- WEB-INF

| |

| |--- classes

| |

| |--- lib

|

|--- navlink (菜单JSP所在目录)

|--- template (布局模板目录)

|--- admin (管理目录)

|--- body (页面内容所在目录)

2.加入所需要的Strutss1.1文件。

将Struts中的*.tld文件加入WEB-INF目录下:

Strutss-bean.tld

Strutss-html.tld

Strutss-logic.tld

Strutss-nested.tld

Strutss-template.tld

Strutss-tiles.tld

Struts的*.jar的库文件加入lib目录下。

3.建立web.xml文件如下。

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

<description>内容管理系统</description>

<servlet>

<servlet-name>action</servlet-name>

<servlet-class>org.apache.Strutss.action.ActionServlet</servlet-class>

<init-param>

<param-name>config</param-name>

<param-value>/WEB-INF/Strutss-config.xml</param-value>

</init-param>

<init-param>

<param-name>debug</param-name>

<param-value>2</param-value>

</init-param>

<load-on-startup>2</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>action</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

<taglib>

<taglib-uri>/WEB-INF/Strutss-bean.tld</taglib-uri>

<taglib-location>/WEB-INF/Strutss-bean.tld</taglib-location>

</taglib>

<taglib>

<taglib-uri>/WEB-INF/Strutss-html.tld</taglib-uri>

<taglib-location>/WEB-INF/Strutss-html.tld</taglib-location>

</taglib>

<taglib>

<taglib-uri>/WEB-INF/Strutss-logic.tld</taglib-uri>

<taglib-location>/WEB-INF/Strutss-logic.tld</taglib-location>

</taglib>

<taglib>

<taglib-uri>/WEB-INF/Strutss-template.tld</taglib-uri>

<taglib-location>/WEB-INF/Strutss-template.tld</taglib-location>

</taglib>

</web-app>

关于编码问题。由于Java是跨平台的,Java在进行需要读取文件或显示等操作时,往往是按照运行平台的默认编码方式。在中文Winodws平台是GBKLinux平台是ISO8859-1。由于在项目中用到很多组件框架,比如Strutss等,这些软件的默认编码方式都可能是ISO8859-1。英语环境下的人们还没习惯用UTF-8,那么就很容易造成在整个项目系统中编码不统一,从而导致乱码。

统一编码就成了编写支持中文系统的重要而且棘手的任务,本项目中将使用UTF-8为统一编码。这包括几个方面:JSP输入输出、Javabeans的编译、数据库或文件系统的访问、配置文件的设置。

2 创建PageForm

Struts中需要一个ActionForm它实际是一个Javabean字段都是JSP中form的一些字段如果有过JSP/Javabeans开发经验的人会使用下列代码

<jsp:useBean id="userInfo" class="com.ora.jsp.beans.userinfo.UserInfoBean">

<jsp:setProperty name="userInfo" property="*" />

</jsp:useBean>

这里UserInfoBean的字段对应HTMLForm表单的字段,从而在Form提交时,表单字段自动映射到UserInfoBean的字段。如果不清楚,可以参阅有关JSP/Javabeans的章节。

将要建立的PageForm其实就是类似UserInfoBeanJavabean

//pageForm是继承Struts中的ActionForm

public class PageForm extends ActionForm {

// Page的Id

private Integer id;

//Page的名字,就是菜单的名字

private String name;

//Page的Html内容

private String htmlText;

//Page的标题

private String title;

public void setId(Integer id) { this.id = id; }

public Integer getId() { return id; }

public void setName(String name) { this.name = name; }

public String getName() { return name; }

public void setHtmlText(String htmlText) { this.htmlText = htmlText; }

public String getHtmlText() { return htmlText; }

public void setTitle(String title) { this.title = title; }

public String getTitle() { return title; }

/**

* 下面是设置校验,进行表单的错误检查

* 如果本方法返回是null,Struts将不会进行错误输出

* 如果不为空,将会输出错误,错误内容是error.name.required的值

*/

public ActionErrors validate(ActionMapping actionMapping,

HttpServletRequest httpServletRequest) {

ActionErrors errors = new ActionErrors();

//如果没有输入页面的名字,需要报错

if ( (name == null) || (name.length() < 1)){

Debug.logVerbose(" error: name is required ", module);

errors.add("name", new ActionError("error.name.required"));

}

//如果没有输入页面的标题,需要报错

if ( (title == null) || (title.length() < 1)){

errors.add("title", new ActionError("error.title.required"));

}

return errors;

}

//复位,将数据清空

public void reset(ActionMapping actionMapping,

HttpServletRequest httpServletRequest) {

this.id = null;

this.name = null;

this.title = null;

this.htmlText = null;

}

}

在PageFormvalidate方法要启用,需要几个步骤:

首先在validate方法中加入字段检验,如果返回不为空,Struts将启动错误输出。

然后,为Strutss错误输出做准备工作,比如上面的name为空,则Struts会到一个ApplicationResources.properties 文件中查询error.name.required对应的值。

在ApplicationResources.properties输入error.name.required的值:

error.name.required=<li>need name</li>

error.title.required=<li>need title</li>

error.id.required=<li>need id</li>

error.action.required=<li>need action</li>

# 下面是错误输出时的标题和尾部,可以自己定制

errors.footer=<hr>

errors.header=<h3><font color="red">Validation Error</font></h3>You must correct the following error

这样,Struts会取得error.name.required对应的值<li>need name</li>输出。那么输出到哪里?当然是JSP页面,只要在需要错误输出的JSP页面中增加如下语句:

<html:errors/>

至于具体位置加在JSP页面的哪个部位,则取决于页面设计了。

注意,最后还有最重要的一步,必须告诉Struts这个ApplicationResources.properties在什么位置。一般ApplicationResources.properties是放在项目的classes目录下和具体的class绑定在一起。

在WEB-INF下建立Strutss-config.xml文件,这是Struts最重要的核心配置文件,需要在其中加入:

<Strutss-config>

<form-beans>

<form-bean name="pageForm" type="com.jdon.cms.events.PageForm" />

</form-beans>

<message-resources parameter="com.jdon.cms.ApplicationResources" />

</Strutss-config>

在Struts-config.xml中,定义了一个FormBeanPageForm。它实际是PageForm类。同时,还需要告诉StrutsApplicationResources.properties是在类classess目录的com/jdon/cms/events目录下,和com.jdon.cms.events的其他类在一起。


3 创建PageAction

Action是和控制器Servlet在一起工作的Action是整个系统中关键的、起调度控制作用的类。

在Action类中,将要处理前面页面输入本类的参数,进行相关业务逻辑运算。然后根据结果,从Strutss-config.xml中根据用户定制的Forward页面,将Forward页面推到用户的浏览器中去。

PageAction是控制页面输出的,使用pageAction.do?action=create pageAction.do? action=edit控制createedit页面输出,前者会输出createPage.jsp,后者则会输出editPage.jsp

PageAction中主要实现两种功能:

· 如果要求输出的是编辑页面,那么就需要将编辑的数据首先显示出来。

· 查询Strutss-config.xml,决定输出新增页面还是编辑页面。

PageAction主要代码如下:

public class PageAction extends Action {

public ActionForward execute(ActionMapping mapping,

ActionForm form,

HttpServletRequest request,

HttpServletResponse response) {

String action = request.getParameter("action");

//清除之前的Attribute中的数据

FormBeanUtil.remove(mapping, request);

//获得pageForm对象并在attribute中保存起来,实现第一个功能

extractPageForm(action, mapping, form, request);

//根据action的值,从mapping中查询出决定跳转的页面,然后直接跳转

if (action.equals(PageEvent.DELETE))

return (mapping.findForward("deletePage"));

else if (action.equals(PageEvent.EDIT))

return (mapping.findForward("editPage"));

else

return (mapping.findForward("createPage"));

}

}

其中extractPageForm方法主要是实现PageAction的第一个功能,主要内容如下:

private void extractPageForm(action, ActionMapping mapping, ActionForm form,

HttpServletRequest request){

//获得id

Integer id = new Integer(request.getParameter("id"));

//通过PageHandler来获得该id的数据

PageHandler pageHandler = new PageHandler();

Page page = pageHandler.getPage(id);

//使用Javabean复制功能,将page对象中和pageForm一样的字段

//复制到pageForm中,这是一个自动方便的转换工具

PropertyUtils.copyProperties(pageForm, page);

}

PageAction中跳转页面的功能是通过mapping.findForward实现的,这个方法是从ActionMapping中获取跳转页面,而ActionMapping的数据是在Strutss-config.xml中设置的:

<action-mappings>

<action attribute="pageForm"

type="com.jdon.cms.events.PageAction"

validate="false"

scope="request"

path="/pageAction">

<forward name="createPage" path="/admin/createPage.jsp" />

<forward name="editPage" path="/admin/editPage.jsp" />

<forward name="deletePage" path="/admin/deletePage.jsp" />

</action>

</action-mappings>

在action-mappings中指定了“/admin/createPage.jsp”等几个跳转的JSP文件。

PageAction的第一个功能实现是事先创建好一个ActionForm实例,根据id查询数据源,获得相应的数据并将其赋值到这个实例中,名称为pageForm

这样,就得到了一个装有数据的pageForm实例,PageAction在自己结束后,如何让editPage.jsp自动获取这个有数据的pageForm,这里有一个Hook钩子,Struts是通过使用Servletrequestsessionattribute来作为中转储存空间的。

如果当前的Scoperequest,那么使用

request.setAttribute(actionMapping.getAttribute(), pageForm);

如果当前Scopesession,那么使用

session.setAttribute (actionMapping.getAttribute(), pageForm);

那么,actionMapping.getAttribute()的值是什么?这是在Strutss-config.xml里的action-mapping中设置的,设置attribute="pageForm"

注意这里没有设置通常的name="pageForm",这两者是有区别的。

由于没有设置name="pageForm",那么需要同时设定validate="false"。因为这个Action没有与ActionForm相联系,所以不要实现校验功能。

关于使用Action的注意点: Action其实是Servlet的一部分,而Servlet本质就是线程,在默认情况下,Action只有一个实例对象,以多线程方式为所有请求服务,因此编写Action类必须考虑线程安全性:

· 使用局部变量,不要用类变量。

· 要考虑资源争夺的问题。

· 对于Exception的捕获,捕获后输出日志系统,而不要使用throw抛出。

4 创建page.jsp页面

在JSP中消除Java代码分两个步骤:

· 能够在Action中实现的功能移动到Action中实现。

· 使用Struts标签语句替代Java代码实现的功能。在JSP中使用Struts特殊标签语句,参考http://jakarta.apache.org/Strutss/userGuide/dev_html.html

page.jsp是将createPage.jspeditPage.jsp的功能合并在一起,因为这两个页面的数据布局和结构非常相似。page.jsp代码如下:

<!-- Strutss需要的标识库支持 -->

<%@ page contentType="text/html; charset=UTF-8" %>

<%@ taglib uri="/WEB-INF/Strutss-logic.tld" prefix="logic" %>

<%@ taglib uri="/WEB-INF/Strutss-template.tld" prefix="template" %>

<%@ taglib uri="/WEB-INF/Strutss-bean.tld" prefix="bean" %>

<%@ taglib uri="/WEB-INF/Strutss-html.tld" prefix="html" %>

<%@ taglib uri="/WEB-INF/app.tld" prefix="app" %>

<html:html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<!-- 根据action判断是否创建? 如果是,标题显示新增页面 -->

<logic:equal name="pageForm" property="action"

scope="request" value="create">

<title>新增页面</title>

</logic:equal>

<!-- 根据action判断是否编辑? 如果是,标题显示编辑页面 -->

<logic:equal name="pageForm" property="action"

scope="request" value="edit">

<title>编辑页面</title>

</logic:equal>

<!-- 根据action判断是否删除? 如果是,标题显示删除页面 -->

<logic:equal name="pageForm" property="action"

scope="request" value="delete">

<title>删除页面</title>

</logic:equal>

<html:base/>

</head>

<body>

<center>

<logic:equal name="pageForm" property="action"

scope="request" value="create">

<h3>新增页面</h3>

</logic:equal>

<logic:equal name="pageForm" property="action"

scope="request" value="edit">

<h3>编辑页面/

<!-- 这里使用了自己定义的标签库,下面章节会解释 -->

<app:linkHtml page="/pageAction.do?action=delete" name="pageForm" >

删除本页

</app:linkHtml></h3>

</logic:equal>

<p>

<!-- 开始Form表单 是向savePageAction提交-->

<html:form action="/savePageAction.do" method="POST">

<!-- 除了创建功能 其他需要id的值 -->

<logic:notEqual name="pageForm" property="action"

scope="request" value="create">

<html:hidden property="id" />

</logic:notEqual>

<!-- 下列语句将从pageForm对象中获取相应的值 -->

<html:hidden property="action" />

<table><tr><td>

页面名称:</td><td><html:text property="name"/>

</td></tr><tr><td>

页面标题:</td><td><html:text property="title"/>

</td></tr><tr><td>

页面内容:</td><td>

<html:textarea property="htmlText" cols="50" rows="20"></html:textarea>

</td></tr></table>

<html:submit property="submit" value="确定"/><html:reset value ="复位"/>

</html:form>

</center>

</body></html:html>

从上面JSP页面看出来,这个页面中已经全部没有了Java代码,代替以特有的标签语句,其中典型html:text语法如下:

<html:text property="name"/>

相当于以前的代码:

<% if (pageForm.getName() ! = null) %>

<input type="text" name=”name" value=”<% pageForm.getName() %>”>

<% else %>

<input type="text" name=”name" >

由此可见,标签库使用是简单方便的。一开始有很多人并不喜欢标签库,从而在JSP中嵌入Java,这种倾向导致的危险是非常大的。可以毫不夸张地说,这样做最终会将Java体系的优越性丧失殆尽。

5 自定义标签库

在page.jsp中,还自定义了一个自己特定的标签:

<app:linkHtml page="/pageAction.do?action=delete" name="pageForm" >

删除本页

</app:linkHtml>

这相当于:

<a href="<%request.getContextPath()%>

/pageAction.do?action=delete&id=<%id%>" >

删除本页

</a>

可见,为了完全取消JSP中的Java代码,必要时,要亲自动手定制标签库。下面看看如何实现。

在page.jsp的顶部引入了app.tld

<%@ taglib uri="/WEB-INF/app.tld" prefix="app" %>

这需要在WEB-INF目录下建立app.tld:

<taglib>

<tlibversion>1.0</tlibversion>

<jspversion>1.1</jspversion>

<shortname>Application Tag Library</shortname>

<uri>http://jakarta.apache.org/taglibs/</uri>

<info></info>

<!-- 以下是自己定义的标签 -->

<tag>

<name>linkHtml</name>

<!-- 标签类是 com.jdon.cms.tags.LinkHtmlTag -->

<tagclass>com.jdon.cms.tags.LinkHtmlTag</tagclass>

<info> </info>

<!-- 以下是标签类的两个输入参数name 和page -->

<attribute>

<name>name</name>

<required>true</required>

<rtexprvalue>true</rtexprvalue>

</attribute>

<attribute>

<name>page</name>

<required>true</required>

<rtexprvalue>true</rtexprvalue>

</attribute>

</tag>

</taglib>

在这个app.tld中定义了linkHtml标签,这样在page.jsp中就可以使用

<app:linkHtml page="XXX" name="XXX">

其中pagename是两个属性参数,需要输入到类com.jdon.cms.tags.LinkHtmlTag进行处理的。实际上app.tld是在JSP页面和Javabean之间作了一个连接。编写LinkHtmlTag的代码:

public class LinkHtmlTag extends TagSupport {

protected String name = null;

protected String page = null;

public String getName() { return (this.name); }

public void setName(String name) { this.name = name; }

public String getPage() { return (this.page); }

public void setPage(String page) { this.page = page; }

/**

* 生成标签开始 也就是在JSP中写<app:linkHtml的地方

*

*/

public int doStartTag() throws JspException {

//相当于在JSP中写入<%request.getContextPath()%>

HttpServletRequest request =

(HttpServletRequest) pageContext.getRequest();

StringBuffer url = new StringBuffer(request.getContextPath());

url.append(page);

//根据name属性值,获取pageForm对象

PageForm pageForm = null;

try {

pageForm = (PageForm) pageContext.findAttribute(name);

} catch (ClassCastException e) {

pageForm = null;

}

if (page.indexOf("?") < 0)

url.append("?");

else

url.append("&");

url.append("id=");

url.append(pageForm.getId());

// 产生连接内容,相当于在JSP中写入:

//<a href=”<%request.getContextPath()%>

HttpServletResponse response =

(HttpServletResponse) pageContext.getResponse();

StringBuffer results = new StringBuffer("<a href=\"");

results.append(response.encodeURL(url.toString()));

results.append("\">");

// results字符串输出到JSP页面 JspWriter writer = pageContext.getOut();

try {

writer.print(results.toString());

} catch (IOException e) {

throw new JspException("LinkHtmlTag error");

}

return (EVAL_BODY_INCLUDE);

}

/**

* 生成替代JSP页面中</app:linkHtml>代码

* 这里是以</a>替代

*/

public int doEndTag() throws JspException {

JspWriter writer = pageContext.getOut();

try {

writer.print("</a>");

} catch (IOException e) {

throw new JspException ("LinkHtmlTag error");

}

return (EVAL_PAGE);

}

public void release() {

super.release();

this.page = null;

}

}

标签库表面上好像比较繁琐,没有在JSP直接写Java代码来得直接方便。但是,J2EE的整个体系本身的特点就是细化、细分,这也符合面向对象分派和封装的原则,因此在J2EE中到处可以看到一点一点的“碎片”,XML配置文件和Java代码在J2EE中往往是自然地组合在一起。当然,这样带来的缺点是增加了复杂性。

6 创建SavePageAction

上面的page.jsp是向savePageAction.do提交表单Form的,SavePageAction也是一个Action,主要负责将表单提交的数据实现保存。

SavePageActionPageAction的用途不一样。后者是主要控制创建或编辑功能页面的输出。这两种Action基本包括了Action的用途,在其他项目中可以参照这两个Action做法,其实这也算一个模式了吧?这个模式主要是对数据创建或编辑功能适用。

为了激活savePageAction.do,需要在Strutss-config.xml中的action-mappings加入:

<action name="pageForm"

type="com.jdon.cms.events.SavePageAction"

validate="true"

input="/admin/pageError.jsp"

scope="request"

path="/savePageAction" />

应该注意到这里使用了name="pageForm",不同于PageAction中使用的attribute="pageForm",两者目的各有不同,因为PageFormSavePageAction联系了在一起,当page.jspSavePageAction提交表单数据时,Strutss将数据保存在PageForm对应的字段中,如果validate又设置为true,那么就会执行PageForm中的validate方法。SavePageAction可以从PageForm中获取page.jsp中的表单数据。

这里还有一层含义,因为page.jsp中提交Action是savePageAction.do,而上面关于savePageAction.do的设置是使用com.jdon.cms.events.SavePageAction类,同时namepageForm,那么就是说pageForm指向的实例是page.jsp FormAction了,或者说是Form Bean,这也说明了为什么在page.jsp显示之前,PageAction将包含数据的PageForm实例保存在requestsessionattribute中,而page.jsp在显示时,会自动读取PageForm实例中的数据。

在上面的action-mappings配置中,激活了校验功能。如果Struts校验发现了错误,Struts将会输出错误,那么错误输出哪个JSP页面?就是input中输入的值/admin/pageError.jsp,这里专门做了一个出错页面pageError.jsp。当然不能忘记在pageError.jsp中加入<html:errors/>

SavePageAction主要是委托业务逻辑层的PageHandler再通过数据层实现数据保存和持久化:

public class SavePageAction extends Action {

public ActionForward execute(ActionMapping actionMapping,

ActionForm actionForm,

HttpServletRequest request,

HttpServletResponse response) {

//生成PageEvent

PageEvent pageEvent = new PageEvent();

Page page = new Page();

try {

//pageForm中的数据复制到Page对象中

PropertyUtils.copyProperties(page, pageForm);

//使PageEvent装载Page数据对象

pageEvent.setPage(page);

//设置操作类型是创建还是编辑

pageEvent.setAction(pageForm.getAction());

}

catch (Exception e) {

Debug.logError("copyProperties Error:" + e, module);

}

//委托pageHandler实现具体数据操作

PageHandler pageHandler = new PageHandler();

if (pageHandler.perform(pageEvent)){

Debug.logVerbose("update page success " , module);

return actionMapping.findForward("pageOk");

}else

return actionMapping.findForward("pageError");

}

}

当然页面提交后台实现操作后,如果成功,SavePageAction就向前台用户界面输出pageOk.jsp页面,表示操作成功。

至此,本项目中关于Page的内容操作流程基本结束,下面将讨论模板的实现。

7 Tile模板

在前面设计中,一个页面被分为几个区域:标题栏、页尾栏、菜单栏以及内容栏部分。如何使用Tile将这几个部分组合在一起?

Tile的页面组装配置可以在JSP,也可以在tiles-defas.xml中。

关于在JSP中定义已经在前面介绍中看到,只要输入下列语句:

<tiles:insert page="/layout.jsp" flush="true">

<tiles:put name="header" value="/header.jsp"/>

<tiles:put name="body" value="/body.jsp"/>

<tiles:put name="footer" value="/footer.jsp"/>

</tiles:insert>

tiles:insert就类似JSP的include,在这个JSP运行时,Strutss-Tiles将分别将header.jsp body.jspfooter.jsp一起输出。

但是,如果每次将其他不变部分都写入JSP,也将是比较琐碎的事情,在配置文件tiles-defs.xml中定义将更加灵活,还可以有继承的概念。

在WEB-INF下建立tiles-defs.xml文件,当然建立好tiles-defs.xml后,还要记得告诉Strutss-Tiles,那么就要在Strutss-config.xml中加入下列语句:

<plug-in className="org.apache.Strutss.tiles.TilesPlugin">

<set-property value="/WEB-INF/tiles-defs.xml" property="definitions-config" />

</plug-in>

这表示将要使用插件TilesPlugin,插件的配置文件是/WEB-INF/tiles-defs.xml

下面建立/WEB-INF/tiles-defs.xml

<tiles-definitions>

<!-- 定义名叫site.mainLayout 实际是classicLayout.jsp -->

<definition name="site.mainLayout" path="/template/no1/layouts/classicLayout.jsp">

<put name="title" value="" />

<put name="header" value="/template/no1/header.jsp" />

<put name="footer" value="/template/no1/footer.jsp" />

<put name="menu" value="site.menu.bar" />

<put name="body" value="" />

</definition>

<!-- 定义名叫site.index.page 继承的是上面site.mainLayout -->

<definition name="site.index.page" extends="site.mainLayout" >

<put name="title" value="Home Page" />

<put name="body" value="/body/body.jsp" />

</definition>

<definition name="site.menu.bar" path="/navlink/cmsMenu.jsp" />

</tiles-definitions>

在tiles-defs.xml中,定义了一个叫site.mainLayout的页面,实际页面是来自相对目录/template/no1/layout/下的classicLayout.jsp

那么应该在classicLayout.jsp中定义整个页面的大体布局,如下:

<%@ page contentType="text/html; charset=UTF-8" %>

<%@ taglib uri="/WEB-INF/Strutss-tiles.tld" prefix="tiles" %>

<html><head>

<title> </title>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

</head>

<body topmargin="5" marginheight="5" leftmargin="5" marginwidth="5">

<table border="0" width="100%" cellspacing="0" cellpadding="0"><tr> <td >

<!-- 标题栏放置这里 -->

<tiles:insert attribute="header" />

</td></tr><tr> <td colspan="2">

<table width="100%" border="0" cellspacing="0" cellpadding="0">

<tr><td bgcolor="#666666"><font color="#666666">&nbsp;</font></td> </tr>

</table></td></tr><tr><td colspan="2">

<table width="100%"> <td width="140" valign="top">

<!-- 菜单栏放置这里 -->

<tiles:insert attribute='menu'/>

</td> <td valign="top" align="left">

<!-- 内容栏放置这里 -->

<tiles:insert attribute='body' />

</td></table></td></tr><tr> <td colspan="2">

<table width="100%" border="0" cellspacing="0" cellpadding="0">

<tr> <td bgcolor="#666666"><font color="#666666">&nbsp;</font></td> </tr>

</table></td></tr><tr> <td colspan="2">

<!-- 页尾栏放置这里 -->

<tiles:insert attribute="footer" />

</td></tr></table>

</body></html>

在上面布局中,分别使用tiles:insert 来代替这里的区域,这个页面布局模板是可以自己调换,例如可以将菜单栏搬迁到右边或上下边,这些都可以根据美工师设计要求进行设计,一旦确定4个部分后,只要使用tiles:insert来放置在这些HTML语法中,将来JSP显示时,Strutss-Tiles会用实际的页面自动替代这些tiles:insert。例如:

<put name="header" value="/template/no1/header.jsp" />

<put name="footer" value="/template/no1/footer.jsp" />

表示分别用相对目录/tempalte/no1/header.jspfooter.jsp替代<tiles:insert attribute="header" /><tiles:insert attribute="footer" />部分。

那么,菜单部分使用什么JSP来替代?

8 创建cmsMenu.jsp

菜单不像header.jsp那样,可能不是动态变化的。由于管理员随时可能会增加或删除页面,那么肯定引起指向页面的菜单按钮的变化,所以菜单的显示是需要和本项目中的逻辑处理层联系在一起的。

在上面的tiles-defs.xml中有一句定义菜单的配置:

<definition name="site.menu.bar" path="/navlink/cmsMenu.jsp" />

这表示菜单是从目录navlink下的cmsMenu.jsp中读取的,因此需要创建cmsMenu.jsp

<%

//获得PageFactory指向

PageFactory pageFactory = PageFactory.getInstance();

//pageFactory中获得Navlink实例

Navlink navlink = pageFactory.getNavlink();

request.setAttribute("navlink", navlink);

%>

<%-- 遍历 Navlink对象中menus集合,取出com.jdon.cms.Menu 对象 --%>

<logic:iterate id="menu" name="navlink"

property="menus" type="com.jdon.cms.Menu" >

<%

String link = menu.getLink();

%>

<tr> <td >

<a href="<%=link%>"><%=menu.getName()%> </a>

</td></tr>

</logic:iterate>

cmsMenu.jsp是将页面Page的Navlink中menus集合逐个读出来,然后从上向下显示。当然具体显示方式(如并排显示)可以自己定制。这个JSP含有Java代码,因此需要重整一下。类似前面章节Page操作一样,使用ActionForm或标识库实现优化。

9 创建index.jsp

为了验证上述配置和设计,可以在根目录下创建index.jsp作为模拟显示,测试观察效果。在index.jsp中写入:

<%@ page contentType="text/html; charset=UTF-8" %>

<%@ taglib uri="/WEB-INF/Strutss-tiles.tld" prefix="tiles" %>

<tiles:insert definition="site.index.page" flush="true" />

这表示,在这里插入tiles-defs中的site.index.page页面,而site.index.page是从tiles-defa.xml中得知,正好继承的是site.mainLayout,而site.mainLayout指向classicLayout.jsp,得到index.jsp的效果图如图4-12所示。

4-12 index.jsp的效果图

可见,使用Tiles实现本项目中的模板功能是成功的。当管理员创建一个新的页面时,本系统将依次创建菜单、HTML内容以及模板JSP,在模板JSP中只要写入index.jsp中类似内容。例如,管理员创建了一个叫“产品和服务”的新页面,得到的页面id是2,那么在2.jsp中程序会自动写入:

<tiles:insert definition="site.mainLayout" flush="true">

<tiles:put name="title" value="产品与服务" />

<tiles:put name="body" value="/data/1.jsp" />

</tiles:insert>

这样,图4-12屏幕中的“This is body”部分将被管理员输入的有关“产品和服务”的实际内容所代替。

10 小技巧

在J2EE应用中经常需要知道某个文件的绝对路径,如何能从Web容器中自动获得某文件的绝对路径?

例如上节中,1.jspbody指向的是“/data/1.jsp”,在相对目录data下的1.jsp是系统程序自动生成的。也就是说使用了File有关API操作,而File操作必须提供绝对路径。例如,在项目系统建立c:\cms下,目录结构见前面章节,那么“/data/1.jsp”的绝对路径应该是c:\cms\data\1.jsp。注意,如果有本地文件操作,那么系统就不能打包成*.war,必须提供完整的目录路径。

那么如何获取根目录c:\cms?通过配置文件设定也可以,但是不灵活,每次移动项目都要修改配置文件。

Servlet中有servlet.getServletContext().getRealPath(pathname);,可以得到pathname的绝对路径,因为每个Web应用中,肯定有WEB-INF/web.xml文件,而通过上句就可以获得web.xml的绝对路径。经过简单处理就可以获得系统的绝对路径。

但是在哪里执行语句:servlet.getServletContext().getRealPath(“web.xml”)?

专门写个初始化的Servlet不是很合适;如果在JSP中写入显然不很规范。 Strutss 1.1提供了Plug-in功能,一般是在系统启动先执行Plug-in,前面定义的tiles-defa.xml就是通过Plug-in启动使用的。

只要创建一个类FileEnvPlugIn来继承org.apache.Strutss.action.PlugIn,在init方法中将系统的绝对根目录获得就可以了。有兴趣者可以进一步参考PlugInAPI


上一级首页下一级