今天,我又做了一个培训,这次讲的是Java的Struts,康师傅很重视这次Struts的培训,他说我这次做的准备比较充分,可能他是觉得我对Struts已经比较了解了吧,但其实这一次是我最没有做准备的一次,我以前二次培训都会预先写一篇博文或内容大纲,而我这次什么都没有做,虽然这次我能从头到尾不停的讲下去,但这些都是我自己明白大家却没能听懂,没能教会大家,这证明我的表达问题的方法方式还有待改进,没有从大家的角度了看问题,才导致这次培训大家都没有听懂,我之过也。
虽然,康师傅下令叫我下个星期分二次重新讲Struts,但我还是来回顾一下我今天讲的Struts内容。
今天我们主要讲的是Struts,我简单的用pd画了一个时序图,以便了解它的整个流程,这里我们可以看出,其实struts也是java里面MVC模式的一种,jsp或do页面为表现层(View),StrutsForm充当映射模型(Model),而StrutsAction则是控制中心(Control),其中还有FormValidate和ActionValidate的Struts验证。图不是很正规,如下:
这里我会分为增、删、改、查四个功能来分别在Struts里的实现,然后讲述它每一个功能走的流程。这里先来讲增加功能的实现,在项目里面增加Struts的引用,然后新建useradd.jsp页面,在页面最上头添加struts标签的引用,如下:
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
然后我们就可以在页面里面使用强大的Struts标签了,在页面的body内加入一个struts表单,如下:
<html:form action="/struts/useradd.do">
<div class="msg" style="color:red; margin:10px; padding:5px;border:solid <bean:write name="UserAddForm" property="hasErrors" />px green">
<html:errors />
</div>
<p>用户名:
<html:text property="fdUserName"></html:text>
<span style="color:red">
<html:errors property="fdUserName" prefix="" suffix="" header="" footer=""></html:errors>
</span>
</p>
<p>邮 箱:
<html:text property="fdUserEmail"></html:text>
<span style="color:red">
<html:errors property="fdUserEmail" prefix="" suffix="" header="" footer=""></html:errors>
</span>
</p>
<p>性 别:
<html:radio property="fdUserSex" value="1">男</html:radio>
<html:radio property="fdUserSex" value="0">女</html:radio>
<span style="color:red">
<html:errors property="fdUserSex" prefix="" suffix="" header="" footer=""></html:errors>
</span>
</p>
<p>城 市:
<html:text property="fdUserCity"></html:text>
<span style="color:red">
<html:errors property="fdUserCity" prefix="" suffix="" header="" footer=""></html:errors>
</span>
</p>
<html:submit value="提交"></html:submit>
<a href="userlist.jsp">返回列表</a>
</html:form>
上面所使用的输入控件都是struts的自定义标签,html:form action="/struts/useradd.do"是一个struts的form表单标签,它最后产生的就是html的form表单,这里的action是指向.do页面,当服务器端解析到这一行struts自定义标签的时候,它会根据action这个路径找到对应的StrutsForm(这里应该是UserAddForm,在struts-config.xml配置文件里面),并初始化创建实例保存在当前上下文内,那当解析form标签里面<html:text property="fdUserName"></html:text>的时候,则是在ActionForm里面找到fdUserNamea的属性字段(带JavaBean的get和set方法访问规则),如果它不为空的话输出的text标签value将会被赋初始值为ActionForm里对应的字段。html:errors是struts错误信息标签,可以是StrutsFrom表单在执行了validate方法返回的ActionErrors错误信息,如果指定了property则是显示指定的字段的错误信息。在请求jsp页面的时候只会实例化创建html:form的action对应的StrutsFrom类,且不会调用StrutsForm的validate验证方法,也不会跳到StrutsAction里执行,而是直接返回,页面根据StrutsForm属性字段初始话输出值,这就是为什么要用html:text这种struts标签,它既能根据StrutsForm的对应字段初始话值,当用户post请求*.do的时候反向填充到StrutsForm,在validate错误的时候就能保存用户输入的信息,返回页面让用户修改重新输入,当然这里我没有在StrutsAction里面做验证,而是在StrutsForm里面进行,用户第一次请求useradd.jsp页面的时候不会调用StrutsForm里面的validate方法,但当post到*.do时则会先事例化对应StrutsForm表单,如果配置文件内设置了validate=true则会调用validate验证方法,只有验证通过时候才会跳到StrutsAction里执行execute方法,所以在StrutsAction里我是直接获取StrutsForm里面的值插入到数据库,然后通过返回一个ActionForward转发页面,userlist是在struts-config.xml配置一个全局转发的页面url地址,UserAddAction的execute代码如下:
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
UserAddForm userForm = (UserAddForm)form;
bean.User userBean = new bean.User();
userBean.setFdUserName( userForm.getFdUserName() );
userBean.setFdUserEmail( userForm.getFdUserEmail() );
userBean.setFdUserSex( userForm.getFdUserSex() );
userBean.setFdUserCity( userForm.getFdUserCity() );
int record = new bll.UserBLL().UserAdd(userBean);
request.setAttribute("msg", "添加用户" (record >0 ? "成功!" : "失败!") );
return mapping.findForward("userlist");
}
struts-config.xml配置文件的内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd">
<struts-config>
<form-beans>
<form-bean name="UserEditForm" type="struts.form.UserEditForm"/>
<form-bean name="UserAddForm" type="struts.form.UserAddForm"/>
<form-bean name="UserListForm" type="struts.form.UserListForm"/>
</form-beans>
<global-exceptions>
</global-exceptions>
<global-forwards>
<forward name="userlist" redirect="false" path="/struts/userlist.jsp"></forward>
</global-forwards>
<action-mappings>
<action input="/struts/useradd.jsp" validate="true" name="UserAddForm" path="/struts/useradd" scope="request" type="struts.action.UserAddAction"/>
<action validate="false" path="/struts/userdel" scope="request" type="struts.action.UserDelAction"/>
<action input="/struts/useredit.jsp" validate="false" name="UserEditForm" path="/struts/useredit" scope="request" type="struts.action.UserEditAction"/>
<action input="/struts/userlist.jsp" validate="false" name="UserListForm" path="/struts/userlist" scope="request" type="struts.action.UserListAction" />
</action-mappings>
<controller inputForward="false" processorClass="org.apache.struts.tiles.TilesRequestProcessor"/>
<message-resources parameter="ApplicationResource"/>
<plug-in className="org.apache.struts.tiles.TilesPlugin" >
<set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" />
<set-property property="moduleAware" value="true" />
</plug-in>
<!-- ========================= Validator plugin ================================= -->
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property
property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
</plug-in>
</struts-config>
再看一下使用Struts来实现用户编辑的功能,编辑先要查询出用户信息,点击保存后再post回来执行修改。所以这里我们编辑就先链接到useredit.do传递一个id参数过去,并把ActionForm的validate设置为false,让它顺利的执行到StrutsAction的execute方法,这样就能在这个方法里面访问数据库获取用户信息并初始化填充到表单,再转发到原来的编辑页面,那么对应ActionForm已经有值填充,所以页面里面的struts标签就会对应ActionForm有初始值。那我在保存的时候也是post到useredit.do那如何区分是要初始化填充ActionForm还是保存更新到数据库呢?我这里是在execute方里根据HttpMethod做一个简单的判断,如果为post就认为是保存更新,否则是初始化填充ActionForm表单,这里我在保存更新时也做了验证判断,如果验证失败则Forward转发到Input的输入页面(即是在配置文件里面的input="/struts/useredit.jsp"),UserEditAction的execute方法如下:
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
UserEditForm userForm = (UserEditForm)form;
if ( "post".equalsIgnoreCase( request.getMethod() ) )
{
//验证Form
ActionErrors errors = new ActionErrors();
if ( 0 == userForm.getFdUserID() )
{
errors.add( "fdUserID", new ActionMessage("用户不存在!!!", false));
}
if ( util.Convert.isNullorEmpty( userForm.getFdUserName() ) )
{
errors.add( "fdUserName", new ActionMessage("用户名不能为空!", false));
}
if ( util.Convert.isNullorEmpty( userForm.getFdUserEmail() ) )
{
errors.add( "fdUserEmail", new ActionMessage("邮箱地址不能为空!", false));
}
if ( -1 == userForm.getFdUserSex() )
{
errors.add( "fdUserSex", new ActionMessage("性别不能为空!", false));
}
if ( util.Convert.isNullorEmpty( userForm.getFdUserCity() ) )
{
errors.add( "fdUserCity", new ActionMessage("城市不能为空!", false));
}
if ( !util.Convert.isDate( userForm.getFdUserTime() ) )
{
errors.add( "fdUserTime", new ActionMessage("时间为空或格式不正确!", false));
}
if ( errors.size() > 0 )
{
userForm.setHasErrors(1);
this.saveErrors(request, errors);
return mapping.getInputForward();
}
//保存用户
bean.User userBean = new bean.User();
userBean.setFdUserID( userForm.getFdUserID() );
userBean.setFdUserName( userForm.getFdUserName() );
userBean.setFdUserEmail( userForm.getFdUserEmail() );
userBean.setFdUserSex( userForm.getFdUserSex() );
userBean.setFdUserCity( userForm.getFdUserCity() );
userBean.setFdUserTime( new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse( userForm.getFdUserTime()) );
int record = new bll.UserBLL().UserUpdate(userBean);
String msg = "修改用户" (record > 0 ? "成功!" : "失败!");
request.setAttribute("msg", msg);
return mapping.findForward("userlist");
}
bean.User userBean = null;
int id = util.Convert.ParseInt( request.getParameter("id") ,0 );
if ( id == 0 )
{
request.setAttribute("msg", "参数不正确!");
return mapping.findForward("userlist");
}
else
{
userBean = new bll.UserBLL().GetUserInfo( id );
}
if (userBean==null)
{
request.setAttribute("msg", "用户不存在!");
return mapping.findForward("userlist");
}
else
{
//无错误信息
userForm.setFdUserID( userBean.getFdUserID() );
userForm.setFdUserName( userBean.getFdUserName() );
userForm.setFdUserEmail( userBean.getFdUserEmail() );
userForm.setFdUserSex( userBean.getFdUserSex() );
userForm.setFdUserCity( userBean.getFdUserCity() );
if ( userBean.getFdUserTime() != null )
{
userForm.setFdUserTime( new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format( userBean.getFdUserTime() ) );
}
//request.setAttribute("UserEditForm", userForm);
return new ActionForward("/struts/useredit.jsp");
}
}
通过使用if ( "post".equalsIgnoreCase( request.getMethod() ) )的判断来区分StrutsForm的初始化填充值和保存更新,返回不同的Forward转发页面,要注意的是这里我使用了request.setAttribute("msg", msg),把有些信息放在了request上下文,这样我在Forward的页面就能获取msg并在页面上显示出来,在接下来说的userlist.jsp页面就有使用struts标签输出msg的,下面就是使用struts显示列表的做法,页面主要标签代码如下:
<logic:present name="msg">
<div class="msg">
<bean:write name="msg"></bean:write>
</div>
</logic:present>
<html:form action="/struts/userlist.do">
切换数据库:
<html:select name="UserListForm" property="defaultName" onchange="location='userlist.do?type=' this.value">
<html:optionsCollection label="dbName" value="dbName" property="dblist" ></html:optionsCollection>
</html:select>
<br /><br />
<table cellpadding="5">
<tr>
<th>
UserId
</th>
<th>
UserName
</th>
<th>
</th>
<th>
Sex
</th>
<th>
City
</th>
<th>
Time
</th>
<th>
操作
</th>
</tr>
<logic:iterate id="user" name="UserListForm" property="list" offset="0" length="20">
<tr>
<td><bean:write name="user" property="fdUserID"></bean:write></td>
<td><bean:write name="user" property="fdUserName"></bean:write></td>
<td><bean:write name="user" property="fdUserEmail"></bean:write></td>
<td>
<logic:equal name="user" property="fdUserSex" value="1">男</logic:equal>
<logic:notEqual name="user" property="fdUserSex" value="1">女</logic:notEqual>
</td>
<td><bean:write name="user" property="fdUserCity"></bean:write></td>
<td><bean:write name="user" property="fdUserTime" format="yyyy年MM月dd日 HH点mm分ss秒"></bean:write></td>
<td>
<a href="useredit.do?id=<bean:write name="user" property="fdUserID"></bean:write>">编辑</a>
<a href="userdel.do?id=<bean:write name="user" property="fdUserID"></bean:write>" onclick="return confirm('是否确认删除?')">删除</a>
</td>
</tr>
</logic:iterate>
<bean:size id="count" name="UserListForm" property="list"></bean:size>
<logic:equal name="count" value="0">
<tr>
<td colspan="7">
<em>暂无数据,记录为<bean:write name="count"></bean:write>。</em>
</td>
</tr>
</logic:equal>
</table>
<h4><a href="useradd.jsp">添加用户</a></h4>
<h4><a href="/factory/userlist.jsp">转至:/factory/userlist.jsp</a></h4>
</html:form>
标签logic:present就是判断当前上下文环境里是否存在msg的对象,并且不为空,就输出标签体里内容。而这次html:form标签内使用了html:select、html:optionsCollection、logic:iterate等标签,但是它们的property都无非是对应着StrutsForm的字段,在解析输出成html代码的时候就根据StrutsForm对应字段输出值,我在这个struts实例里访问数据库使用的是简单工厂模式,暂只用sqlserver、mysql、derby(NetBeans自带的数据库)实现了接口,经过修改能实现在线切换访问的数据库功能,这个在之后文章里详细介绍,这里我只是在StrutsForm里增加当前使用数据库和切换列表,供在jsp页面输出显示,在StrutsAction里去调用切换数据库的方法,先看我UserListForm类的代码:
package struts.form;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMapping;
/*
* @auther Jonllen
* @create 2009-10-13 14:40:40
* @site http://www.jonllen.com
*/
public class UserListForm extends org.apache.struts.action.ActionForm {
private String defaultName = db.DbManager.GetConfig().getDefaultName();
private java.util.List<db.DbItem> dblist = db.DbManager.GetConfig().getList();
private java.util.List<bean.User> list;
public String getDefaultName() {
return defaultName;
}
public void setDefaultName(String defaultName) {
this.defaultName = defaultName;
}
public java.util.List<db.DbItem> getDblist() {
return dblist;
}
public void setDblist(java.util.List<db.DbItem> dblist) {
this.dblist = dblist;
}
public java.util.List<bean.User> getList() {
return list;
}
public void setList(java.util.List<bean.User> list) {
this.list = list;
}
public UserListForm() {
super();
setList(new bll.UserBLL().GetUserList());
}
/**
* This is the action called from the Struts framework.
* @param mapping The ActionMapping used to select this instance.
* @param request The HTTP Request we are processing.
* @return
*/
@Override
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
return errors;
}
}
可以看到,类字段defaultName、dblist在初试化就已经赋值,在UserListForm()构造函数也对list用户列表进行了赋值,所以在请求userlist.jsp用户列表页面的时候初始化实例StrutsFrom后UserListForm内字段已经全部有值,那这时候不需要通过在StrutsAction里再去填充StrutsFrom,所以html:select、html:optionsCollection、logic:iterate标签的property都有值了,那么select的选中值(defaultName)、列表项(dblist)、用户对象列表(list)也都会对应输出,跌代标签logic:iterate下面的bean:size标签也指向了UserListFrom的list属性字段,是声明一个id为count的变量,通过下面的logic:equal比较判断标签如果记录条数为0则输出暂无数据等字样。至此,Struts的列表显示功能已经实现。
最后,只剩下的删除的功能,也是最简单的一个功能,因为Struts来实现都不需要jsp页面,直接访问userdel.do传一个id过去,看/struts/userdel的path对应配置,没有设置input输入页面属性,也没有对应的StrutsForm类,validate为false,所以它会直接跳到UserDelAction里执行execute方法,在这个方法访问数据库删除掉改id的用户,也在request上下文里放一个msg后转发到userlist.jsp页面,并显示删除用户成功与否的msg信息。
Struts的用户增、删、改、查的功能已基本实现,大家需要先了解Struts的大概工作原理及它的标签使用,如果还有什么疑问的话,我这里提供了NetBeans 6.7工程源文件下载,大家可以对照我上面讲的来看,我事例里面默认是使用mysql数据库,默认连接数据库、连接字符串、驱动类都是在connector.properties资源文件内配置,PowerDesigner目录下有我建的物理模型、工厂模式类图、Struts流程时序图及各种数据库创建FA_User用户表的sql文件,访问数据库的部分使用了工厂模式,factory目录下对应的jsp页面是用纯ScriptLet实现以上Struts增、删、改、查的功能。