概述
<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
4 表现层的实现
表现层主要使用Struts框架完成,Struts分两个部分,一部分是视图View的实现,基本是JSP页面。另外一部分是控制器部分,具体是Action类的继承者,通过Action类的实现将前台界面用户输入的表单数据打包成基本对象,递送到后台处理。
在图4-11中,pageAction负责两种页面的输出。一个是创建页面;另一个是编辑页面。由于这两个页面数据结构类似,因此采取统一的Action控制输出。当用户填写好创建页面或编辑页面的表单数据后,单击确认按钮后提交,系统将由SavePageAction负责表单数据的处理和保存。
在SavePageAction中,主要是从用户输入的表单数据PageForm中获取数据,检查合法性,然后将这些数据封装成基本业务对象(Menu或Bodu等),委托给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文件如下。
内容管理系统
action
org.apache. Strutss.action.ActionServlet
config
/WEB-INF/ Strutss-config.xml
debug
2
2
action
*.do
/WEB-INF/ Strutss-bean.tld
/WEB-INF/ Strutss-bean.tld
/WEB-INF/ Strutss-html.tld
/WEB-INF/ Strutss-html.tld
/WEB-INF/ Strutss-logic.tld
/WEB-INF/ Strutss-logic.tld
/WEB-INF/ Strutss-template.tld
/WEB-INF/ Strutss-template.tld
关于编码问题。由于Java是跨平台的,Java在进行需要读取文件或显示等操作时,往往是按照运行平台的默认编码方式。在中文Winodws平台是GBK, 在Linux平台是ISO8859-1。由于在项目中用到很多组件框架,比如Strutss等,这些软件的默认编码方式都可能是ISO8859-1。英语环境下的人们还没习惯用UTF-8,那么就很容易造成在整个项目系统中编码不统一,从而导致乱码。
统一编码就成了编写支持中文系统的重要而且棘手的任务,本项目中将使用UTF-8为统一编码。这包括几个方面:JSP输入输出、Javabeans的编译、数据库或文件系统的访问、配置文件的设置。
2 创建PageForm
Struts中需要一个ActionForm,它实际是一个Javabean,字段都是JSP中form的一些字段,如果有过JSP/Javabeans开发经验的人会使用下列代码:
这里UserInfoBean的字段对应HTML中Form表单的字段,从而在Form提交时,表单字段自动映射到UserInfoBean的字段。如果不清楚,可以参阅有关JSP/Javabeans的章节。
将要建立的PageForm其实就是类似UserInfoBean的Javabean:
//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;
}
}
在PageForm中validate方法要启用,需要几个步骤:
首先在validate方法中加入字段检验,如果返回不为空,Struts将启动错误输出。
然后,为Strutss错误输出做准备工作,比如上面的name为空,则Struts会到一个ApplicationResources.properties 文件中查询error.name.required对应的值。
在ApplicationResources.properties输入error.name.required的值:
error.name.required=
- need name
-
error.title.required=
- need title
-
error.id.required=
- need id
-
error.action.required=
- need action
-
# 下面是错误输出时的标题和尾部,可以自己定制
errors.footer=
errors.header=
Validation Error
You must correct the following error这样,Struts会取得error.name.required对应的值
- need name
- 输出。那么输出到哪里?当然是JSP页面,只要在需要错误输出的JSP页面中增加如下语句:
至于具体位置加在JSP页面的哪个部位,则取决于页面设计了。
注意,最后还有最重要的一步,必须告诉Struts这个ApplicationResources.properties在什么位置。一般ApplicationResources.properties是放在项目的classes目录下和具体的class绑定在一起。
在WEB-INF下建立Strutss-config.xml文件,这是Struts最重要的核心配置文件,需要在其中加入:
<Strutss-config>
Strutss-config>
在Struts-config.xml中,定义了一个FormBean是PageForm。它实际是PageForm类。同时,还需要告诉Struts,ApplicationResources.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控制create或edit页面输出,前者会输出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中设置的:
type="com.jdon.cms.events.PageAction"
validate="false"
scope="request"
path="/pageAction">
在action-mappings中指定了“/admin/createPage.jsp”等几个跳转的JSP文件。
PageAction的第一个功能实现是事先创建好一个ActionForm实例,根据id查询数据源,获得相应的数据并将其赋值到这个实例中,名称为pageForm。
这样,就得到了一个装有数据的pageForm实例,PageAction在自己结束后,如何让editPage.jsp自动获取这个有数据的pageForm,这里有一个Hook钩子,Struts是通过使用Servlet的request或session的attribute来作为中转储存空间的。
如果当前的Scope是request,那么使用
request.setAttribute(actionMapping.getAttribute(), pageForm);
如果当前Scope是session,那么使用
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.jsp和editPage.jsp的功能合并在一起,因为这两个页面的数据布局和结构非常相似。page.jsp代码如下:
<%@ 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" %>
scope="request" value="create">
新增页面 scope="request" value="edit">
编辑页面 scope="request" value="delete">
删除页面 scope="request" value="create">
新增页面
scope="request" value="edit">
编辑页面/
删除本页
scope="request" value="create">
页面名称:
页面标题:
页面内容:
从上面JSP页面看出来,这个页面中已经全部没有了Java代码,代替以特有的标签语句,其中典型html:text语法如下:
相当于以前的代码:
<% if (pageForm.getName() ! = null) %>
”>
<% else %>
由此可见,标签库使用是简单方便的。一开始有很多人并不喜欢标签库,从而在JSP中嵌入Java,这种倾向导致的危险是非常大的。可以毫不夸张地说,这样做最终会将Java体系的优越性丧失殆尽。
5 自定义标签库
在page.jsp中,还自定义了一个自己特定的标签:
删除本页
这相当于:
删除本页
可见,为了完全取消JSP中的Java代码,必要时,要亲自动手定制标签库。下面看看如何实现。
在page.jsp的顶部引入了app.tld:
<%@ taglib uri="/WEB-INF/app.tld" prefix="app" %>
这需要在WEB-INF目录下建立app.tld:
1.0
1.1
Application Tag Library
http://jakarta.apache.org/taglibs/
linkHtml
com.jdon.cms.tags.LinkHtmlTag
name
true
true
page
true
true
在这个app.tld中定义了linkHtml标签,这样在page.jsp中就可以使用
。
其中page和name是两个属性参数,需要输入到类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中写
*
*/
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中写入:
//
HttpServletResponse response =
(HttpServletResponse) pageContext.getResponse();
StringBuffer results = new StringBuffer("
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页面中代码
* 这里是以替代
*/
public int doEndTag() throws JspException {
JspWriter writer = pageContext.getOut();
try {
writer.print("");
} 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,主要负责将表单提交的数据实现保存。
SavePageAction和PageAction的用途不一样。后者是主要控制创建或编辑功能页面的输出。这两种Action基本包括了Action的用途,在其他项目中可以参照这两个Action做法,其实这也算一个模式了吧?这个模式主要是对数据创建或编辑功能适用。
为了激活savePageAction.do,需要在Strutss-config.xml中的action-mappings加入:
type="com.jdon.cms.events.SavePageAction"
validate="true"
input="/admin/pageError.jsp"
scope="request"
path="/savePageAction" />
应该注意到这里使用了name="pageForm",不同于PageAction中使用的attribute="pageForm",两者目的各有不同,因为PageForm和SavePageAction联系了在一起,当page.jsp向SavePageAction提交表单数据时,Strutss将数据保存在PageForm对应的字段中,如果validate又设置为true,那么就会执行PageForm中的validate方法。SavePageAction可以从PageForm中获取page.jsp中的表单数据。
这里还有一层含义,因为page.jsp中提交Action是savePageAction.do,而上面关于savePageAction.do的设置是使用com.jdon.cms.events.SavePageAction类,同时name是pageForm,那么就是说pageForm指向的实例是page.jsp 的FormAction了,或者说是Form Bean,这也说明了为什么在page.jsp显示之前,PageAction将包含数据的PageForm实例保存在request或session的attribute中,而page.jsp在显示时,会自动读取PageForm实例中的数据。
在上面的action-mappings配置中,激活了校验功能。如果Struts校验发现了错误,Struts将会输出错误,那么错误输出哪个JSP页面?就是input中输入的值/admin/pageError.jsp,这里专门做了一个出错页面pageError.jsp。当然不能忘记在pageError.jsp中加入 。
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就类似JSP的include,在这个JSP运行时,Strutss-Tiles将分别将header.jsp body.jsp和footer.jsp一起输出。
但是,如果每次将其他不变部分都写入JSP,也将是比较琐碎的事情,在配置文件tiles-defs.xml中定义将更加灵活,还可以有继承的概念。
在WEB-INF下建立tiles-defs.xml文件,当然建立好tiles-defs.xml后,还要记得告诉Strutss-Tiles,那么就要在Strutss-config.xml中加入下列语句:
Strutss.tiles.TilesPlugin">
这表示将要使用插件TilesPlugin,插件的配置文件是/WEB-INF/tiles-defs.xml。
下面建立/WEB-INF/tiles-defs.xml:
在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" %>
在上面布局中,分别使用tiles:insert 来代替这里的区域,这个页面布局模板是可以自己调换,例如可以将菜单栏搬迁到右边或上下边,这些都可以根据美工师设计要求进行设计,一旦确定4个部分后,只要使用tiles:insert来放置在这些HTML语法中,将来JSP显示时,Strutss-Tiles会用实际的页面自动替代这些tiles:insert。例如:
表示分别用相对目录/tempalte/no1/下header.jsp和footer.jsp替代 和 部分。
那么,菜单部分使用什么JSP来替代?
8 创建cmsMenu.jsp
菜单不像header.jsp那样,可能不是动态变化的。由于管理员随时可能会增加或删除页面,那么肯定引起指向页面的菜单按钮的变化,所以菜单的显示是需要和本项目中的逻辑处理层联系在一起的。
在上面的tiles-defs.xml中有一句定义菜单的配置:
这表示菜单是从目录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 对象 --%>
property="menus" type="com.jdon.cms.Menu" >
<%
String link = menu.getLink();
%>
<%=menu.getName()%>
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-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中程序会自动写入:
这样,图4-12屏幕中的“This is body”部分将被管理员输入的有关“产品和服务”的实际内容所代替。
10 小技巧
在J2EE应用中经常需要知道某个文件的绝对路径,如何能从Web容器中自动获得某文件的绝对路径?
例如上节中,1.jsp中body指向的是“/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方法中将系统的绝对根目录获得就可以了。有兴趣者可以进一步参考Plu
<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script><script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
最后
以上就是殷勤马里奥为你收集整理的Struts应用系统案例设计编程讲解(4)的全部内容,希望文章能够帮你解决Struts应用系统案例设计编程讲解(4)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复