开源技术――体验Struts
用户登陆的实现
这不快毕业了吗?我选的题目就和Struts有关,做一个关于学校的毕业设计选题系统,就是B/S结构,访问数据库的一些俗套的东西,为了巩固我这段时间学习Struts,我把这个系统竟往难里做,这样对我这个动手能力差的人,实际工作经验少的人来说,会有点帮助吧?
当初就是这样想的,所以就开始了我的Struts之旅。
那我就从我的第一页讲起吧,当然第一页一般都是登陆,至于怎么配置Struts,您还是参考一些别人的文章吧,我觉得写这些就够土的了,写怎么配置,怎么实现就更土!
<%@ page contentType="text/Html; charset=gb2312"%>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
用户名:
密码:
把控制格式的HTML删除掉,应该剩下这些就是主干了,对于这个毕业设计选题系统,有三种角色,治理员(Admin),教师(Teacher),学生(Student)而我把他们的登陆都做到了一起,在后台这三种角色也是都放在了一个表中,对于他们这三种对象,都是继续于Person的类,所以在登陆时可以忽视他们的具体角色,用多态来实现登陆。
action="/ajax.do?method=login" :将一些关于登陆啊,注册的一些乱七八糟的操作我都放到了一个DispatchAction,之后可以用method的不同来分别调用不同的功能。
onsubmit="return validateLoginForm(this)":这个是用来实现Struts自带的validate验证
在这里需要的Struts相关配置会有如下的几个方面:
首先是要对配置文件进行配置我们登陆时需要的FormBean和Action
(1)struts-config.XML:
(2)validation.xml:
这里是常量配置,因为我们还会需要到用户名的验证,所以把他设置为了常量
下面是对这个bean的具体严整手段了,按字段field分别来写他们所依靠depaends的检验手段,常用的有必须填required,正则表达式验证mask,最大maxlength和最小minlength
对于我们需要的FormBean是这样写的:
package com.boya.subject.view;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.*;
public class LoginForm extends ActionForm
{
private static final long serialVersionUID = 1L;
private String user = null;
private String password = null;
public String getPassword()
{
return password;
}
public void setPassword( String password )
{
this.password = password;
}
public String getUser()
{
return user;
}
public void setUser( String user )
{
this.user = user;
}
public void reset(ActionMapping mapping,HttpServletRequest request)
{
this.password = null;这里很重要,当用户输入有错时,需要返回登陆界面给用户,为了用户填写方便我们可以设置返回给用户的哪部分信息设置为空
}
}
我用来实现登陆的DispatchAction代码如下:
public ActionForward login( ActionMapping mapping, ActionForm form,
HttpServletRequest req, HttpServletResponse res ) throws Exception
{
Service service = getService();调用业务逻辑
LoginForm loginForm = (LoginForm) form;获取formbean
String user = loginForm.getUser();提取用户名
Person person = service.getUser( user );从业务逻辑中查找用户
ActionMessages messages = new ActionMessages();
ActionMessage am;
if ( person == null )假如用户不存在,我们就返回
{
am = new ActionMessage( "index.jsp.fail.user", user );参数的意义:第一个是主串,而后面的作为arg数组
messages.add( "user", am );把错误信息放到errors 属性为user那里去显示
saveErrors( req, messages );
form.reset( mapping, req );假如出现错误,调用formbean的重置功能
return mapping.findForward( ID.FAIL );
}
if ( !person.getPassword().equals( loginForm.getPassword() ) )假如密码不一致
{
am = new ActionMessage( "index.jsp.fail.password", user );
messages.add( "password", am );
saveErrors( req, messages );
form.reset( mapping, req );
return mapping.findForward( ID.FAIL );
}
setSessionObject( req, person.getType(), person );把用户放到session里
return new ActionForward( person.getType() + ".do", true );我在每个类型用户的类中加入了一个getType来在这里调用,之后动态的去对应的admin.do,student.do,teacher.do的主页面,并且这里实现的不是请求转发,而是请求从定向
}
为了让大家更方便的了解我这个设计,我先把我的一些整体的规划都说出来吧,由于我是初学,难免会参照本书籍来看,我买的是那本孙某女的书《精通:*****》,看了看她前面的介绍,我一看了不得,能出书,写的还都不错,这女的可不得了,渐渐迷惑的地方非常多,比如例子里面注释都拽上了英语,搞不懂,而当我从网上下到电子盗版jakarta struts(我已安下栽说明要求的那样在24小时后删除了)这本书的时候我才恍然大悟,原来是抄袭啊?至于是谁抄的谁,口说无凭,不能乱诽谤,不过大家心里都该有杆称!
下面就是代码了:
package com.boya.subject.model;
public interface Person
{
public Long getId();
public void setId( Long id );
public String getName();
public void setName( String name );
public String getPassword();
public void setPassword( String password );
public String getTelphone();
public void setTelphone( String telphone );
public String getUser();
public void setUser( String user );
public String getType();
}
package com.boya.subject.model;
public abstract class User implements Person
{
private Long id;数据库id
private String user;用户名
private String password;密码
private String name;姓名
private String telphone;电话
public Long getId()
{
return id;
}
public void setId( Long id )
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName( String name )
{
this.name = name;
}
public String getPassword()
{
return password;
}
public void setPassword( String password )
{
this.password = password;
}
public String getTelphone()
{
return telphone;
}
public void setTelphone( String telphone )
{
this.telphone = telphone;
}
public String getUser()
{
return user;
}
public void setUser( String user )
{
this.user = user;
}
}
package com.boya.subject.model;
public class Admin extends User
{
private String grade = null; 治理员权限
public String getGrade()
{
return grade;
}
public void setGrade( String grade )
{
this.grade = grade;
}
public String getType()
{
return "admin";
}
}
package com.boya.subject.model;
public class Teacher extends User
{
private String level; 教师职称
public String getLevel()
{
return level;
}
public void setLevel( String level )
{
this.level = level;
}
public String getType()
{
return "teacher";
}
}
package com.boya.subject.model;
public class Student extends User
{
private String sn;学生学号
private SchoolClass schoolClass; 班级
public SchoolClass getSchoolClass()
{
return schoolClass;
}
public void setSchoolClass( SchoolClass schoolClass )
{
this.schoolClass = schoolClass;
}
public String getSn()
{
return sn;
}
public void setSn( String sn )
{
this.sn = sn;
}
public String getType()
{
return "student";
}
}
而对于Action我分别做了一个抽象类,之后别的从这里继续
先是Action的
package com.boya.subject.controller;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import com.boya.subject.frame.ID;
import com.boya.subject.frame.ServiceFactory;
import com.boya.subject.model.Person;
import com.boya.subject.service.Service;
import com.boya.subject.util.HtmlUtil;
public abstract class BaseAction extends Action
{
/**
* 由服务工厂方法创建服务
* @return 数据库操作的服务
* 2006-5-16 18:10:04
*/
public Service getService()
{
ServiceFactory factory = (ServiceFactory) getAppObject( ID.SF );
Service service = null;
try
{
service = factory.createService();
}
catch ( Exception e )
{
}
return service;
}
/**
* 判定用户是否合法登陆
* @param req
* @return 用户是否登陆
* 2006-5-16 18:11:26
*/
public boolean isLogin( HttpServletRequest req )
{
if ( getPerson( req ) != null ) return true;
else
return false;
}
/**
* 抽象方法,子类实现
* @param mapping
* @param form
* @param req
* @param res
* @return
* @throws Exception
* 2006-5-16 18:12:54
*/
protected abstract ActionForward executeAction( ActionMapping mapping,
ActionForm form, HttpServletRequest req, HttpServletResponse res )
throws Exception;
/**
* 获取session范围的用户
* @param req
* @return 当前用户
* 2006-5-16 18:13:35
*/
public abstract Person getPerson( HttpServletRequest req );
/**
* 父类的执行Action
* @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
public ActionForward execute( ActionMapping mapping, ActionForm form,
HttpServletRequest req, HttpServletResponse res ) throws Exception
{
if ( !isLogin( req ) )
{
HtmlUtil.callParentGo( res.getWriter(), ID.M_UNLOGIN, ID.P_INDEX );
return null;
}
return executeAction( mapping, form, req, res );
}
/**
* 删除session中属性为attribute的对象
* @param req
* @param attribute 对象属性
* 2006-5-16 18:16:59
*/
public void removeSessionObject( HttpServletRequest req, String attribute )
{
HttpSession session = req.getSession();
session.removeAttribute( attribute );
}
/**
* 设置session中属性为attribute的对象
* @param req
* @param attribute 设置属性
* @param o 设置对象
* 2006-5-16 18:17:50
*/
public void setSessionObject( HttpServletRequest req, String attribute,
Object o )
{
req.getSession().setAttribute( attribute, o );
}
/**
* 设置application中属性为attribute的对象
* @param req
* @param attribute 设置属性
* @param o 设置对象
* 2006-5-16 18:17:50
*/
public void setAppObject( String attribute, Object o )
{
servlet.getServletContext().setAttribute( attribute, o );
}
public Object getSessionObject( HttpServletRequest req, String attribute )
{
Object obj = null;
HttpSession session = req.getSession( false );
if ( session != null ) obj = session.getAttribute( attribute );
return obj;
}
public Object getAppObject( String attribute )
{
return servlet.getServletContext().getAttribute( attribute );
}
public void callParentGo( HttpServletResponse res, String msg, String url )
throws IOException
{
HtmlUtil.callParentGo( res.getWriter(), msg, url );
}
public void callMeGo( HttpServletResponse res, String msg, String url )
throws IOException
{
HtmlUtil.callMeGo( res.getWriter(), msg, url );
}
public void callBack( HttpServletResponse res, String msg )
throws IOException
{
HtmlUtil.callBack( res.getWriter(), msg );
}
public void callMeConfirm( HttpServletResponse res, String msg, String ok,
String no ) throws IOException
{
HtmlUtil.callMeConfirm( res.getWriter(), msg, ok, no );
}
}
再是DispatchAction的
package com.boya.subject.controller;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;
import com.boya.subject.frame.ID;
import com.boya.subject.frame.ServiceFactory;
import com.boya.subject.model.Person;
import com.boya.subject.service.Service;
import com.boya.subject.util.HtmlUtil;
public abstract class BaseDispatchAction extends DispatchAction
{
/**
* 由服务工厂方法创建服务
* @return 数据库操作的服务
* 2006-5-16 18:10:04
*/
public Service getService()
{
ServiceFactory factory = (ServiceFactory) getAppObject( ID.SF );
Service service = null;
try
{
service = factory.createService();
}
catch ( Exception e )
{
}
return service;
}
/**
* 判定用户是否合法登陆
* @param req
* @return 用户是否登陆
* 2006-5-16 18:11:26
*/
public boolean isLogin( HttpServletRequest req )
{
if ( getPerson( req ) != null ) return true;
else
return false;
}
/**
* 获取session范围的用户
* @param req
* @return 当前用户
* 2006-5-16 18:13:35
*/
public abstract Person getPerson( HttpServletRequest req );
/**
* 父类的执行DispatchAction
* @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
public ActionForward execute( ActionMapping mapping, ActionForm form,
HttpServletRequest req, HttpServletResponse res ) throws Exception
{
try
{
if ( !isLogin( req ) )
{
callParentGo( res, ID.M_UNLOGIN, ID.P_INDEX );
return null;
}
return super.execute( mapping, form, req, res );
}
catch ( NoSUChMethodException e )
{
callBack( res, ID.M_NOMETHOD );
return null;
}
}
/**
* 删除session中属性为attribute的对象
* @param req
* @param attribute 对象属性
* 2006-5-16 18:16:59
*/
public void removeSessionObject( HttpServletRequest req, String attribute )
{
HttpSession session = req.getSession();
session.removeAttribute( attribute );
}
/**
* 设置session中属性为attribute的对象
* @param req
* @param attribute 设置属性
* @param o 设置对象
* 2006-5-16 18:17:50
*/
public void setSessionObject( HttpServletRequest req, String attribute,
Object o )
{
req.getSession().setAttribute( attribute, o );
}
/**
* 设置application中属性为attribute的对象
* @param req
* @param attribute 设置属性
* @param o 设置对象
* 2006-5-16 18:17:50
*/
public void setAppObject( String attribute, Object o )
{
servlet.getServletContext().setAttribute( attribute, o );
}
public Object getSessionObject( HttpServletRequest req, String attribute )
{
Object obj = null;
HttpSession session = req.getSession( false );
if ( session != null ) obj = session.getAttribute( attribute );
return obj;
}
public Object getAppObject( String attribute )
{
return servlet.getServletContext().getAttribute( attribute );
}
public void callParentGo( HttpServletResponse res, String msg, String url )
throws IOException
{
HtmlUtil.callParentGo( res.getWriter(), msg, url );
}
public void callMeGo( HttpServletResponse res, String msg, String url )
throws IOException
{
HtmlUtil.callMeGo( res.getWriter(), msg, url );
}
public void callBack( HttpServletResponse res, String msg )
throws IOException
{
HtmlUtil.callBack( res.getWriter(), msg );
}
public void callMeConfirm( HttpServletResponse res, String msg, String ok,
String no ) throws IOException
{
HtmlUtil.callMeConfirm( res.getWriter(), msg, ok, no );
}
}
对于程序中的一些提示信息,我比较喜欢用JS来写,所以我把这些都放到了一个类中
import java.io.IOException;
import java.io.Writer;
public class HtmlUtil
{
public static void callParentGo( Writer out, String msg, String url )
throws IOException
{
out.write( " " );
}
public static void callMeGo( Writer out, String msg, String url )
throws IOException
{
out.write( " " );
}
public static void callMeConfirm( Writer out, String msg ,String ok, String no )
throws IOException
{
out.write( " " );
}
public static void callBack( Writer out, String msg ) throws IOException
{
out.write( " " );
}
}
比如当用户在注册的时候,用户点一个按纽不用刷新界面就可以获得一句提示,是有这人还是没有这人啊?这次我尝试了用ajax技术来做一个三级要害的下拉列表,而这是我要讲的要害。
其实现在一般的ajax都是向Servlet发出请求,之后服务器响应,再偷摸的把结果传给它,之后显示出来,而换到Struts,有人会发甍,也一样,Action是Servlet,DispatchAction也是,只要把代码往这里写,让它往.do那里请求就行了。
在接下来我就向大家介绍我是怎样实现上述功能的
因为大学里面的结构是这里的
学院-专业-班级-学生
在学生注册的时候他是依靠于上述对象的,所以用户注册就需要一个三级的下拉选择
而ajax就能象变魔术一样,从服务器那里偷摸弄来您需要的列表
下面我先给大家展示一下第一个功能是怎么实现的吧?
当用户在注册的时候,点一个按纽,之后会弹出一个alert来告诉你这个用户是否有人用了,下面就让我们来看看这个功能是怎么实现的吧?
这里定义了按纽,用来测试老师是否已经存在了
大体的ajax的JS代码都上面这四部分,
先是创建XMLHttpRequest,
var xmlHttp;
function createXMLHttpRequest()
{
if (window.XMLHttpRequest)
{
xmlHttp = new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}
之后是客户响应部分的代码
function teacherCheck()
{
var f = document.TeacherRegisterForm 从表单里读字段
var user = f.user.value
if(user=="")
{
window.alert("用户名不能为空!")
f.user.focus()
return false
}
else
{
createXMLHttpRequest() 这里都是精华了
var url = "ajax.do?method=checkUserIsExist&user="+user 定义响应地址
xmlHttp.open("GET",url, true) 发出响应
xmlHttp.onreadystatechange = checkUser 把从服务器得到的响应再传给另个函数
xmlHttp.send(null)
}
}
function checkUser()
{
if (xmlHttp.readyState == 4)
{
if (xmlHttp.status == 200)
{
alert(xmlHttp.responseText) 这里是对响应结果的操作,在这里我们是滩出对话框,并把服务器发来的信息显示出来
}
}
}
我把所有乱七八糟的操作都放到了一个DispatchAction里,所以它也不例外的在这个DA中了
public ActionForward checkUserIsExist( ActionMapping mapping,
ActionForm form, HttpServletRequest req, HttpServletResponse res )
throws Exception
{
Service service = getService();
res.getWriter().write(service.checkUserIsExistForAjax( req.getParameter( "user" ) ) );
return null;
}
它仅仅是把业务逻辑部分的结果发送回去,而真正的判定是在业务逻辑那里实现的,
public String checkUserIsExistForAjax( String user )把结果弄成String的形式传回去
{
Connection connection = null;
PreparedStatement pstmt1 = null;
ResultSet rs = null;
try
{
connection = getConnection();
pstmt1 = connection
.prepareStatement( "select * from user where user=?" );
pstmt1.setString( 1, user );
rs = pstmt1.executeQuery();
rs.last();
if ( rs.getRow() > 0 )
{
return ID.M_EXIST; 用户存在
}
}
catch ( Exception e )
{
e.printStackTrace();
}
finally
{
close( rs );
close( pstmt1 );
close( connection );
}
return ID.M_NOEXIST;用户不存在
}
接着上次的话题,下面的就是学生注册时需要的学院,专业,班级,三层列表,
学院:
专业:
班级:
学院是上来就应该有的,我们把他放到了LabelValueBean里
public Vector
{
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
connection = getConnection();
pstmt = connection.prepareStatement( "select * from institute" );
rs = pstmt.executeQuery();
Vector
institutes.add( new LabelValueBean( "请选择所在学院", "" ) );
while ( rs.next() )
{
institutes.add( new LabelValueBean(
rs.getString( "institute" ), rs.getString( "id" ) ) );
}
return institutes;
}
catch ( Exception e )
{
e.printStackTrace();
}
finally
{
close( rs );
close( pstmt );
close( connection );
}
return null;
}
而当它选择了一个学院后,相应的getDepartments(this.value)的js脚本就该工作了,还是四步
var xmlHttp;
function createXMLHttpRequest()
{
if (window.XMLHttpRequest)
{
xmlHttp = new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}
发出请求
function getDepartments(institute)
{
createXMLHttpRequest()
var url = "ajax.do?institute="+institute+"&method=getDepartments"
xmlHttp.open("GET",url, true)
xmlHttp.onreadystatechange = departments
xmlHttp.send(null)
}
处理响应
function departments()
{
if (xmlHttp.readyState == 4)
{
if (xmlHttp.status == 200)
{
resText = xmlHttp.responseText
each = resText.split("")
buildSelect( each, document.getElementById("departmentId"), "请选择所在专业");
}
}
}
function buildSelect(str,sel,label)
{
sel.options.length=0;
sel.options[sel.options.length]=new Option(label,"")
for(var i=0;i
each=str[i].split(",")
sel.options[sel.options.length]=new Option(each[0],each[1])
}
}
我把从数据库中得到的各个专业进行了编码,之后再这里再回归回去,下面的是编码过程
public StringBuffer getDepartmentsByInstituteIdForAjax( Long instituteId )
{
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
connection = getConnection();
pstmt = connection
.prepareStatement( "select * from department where instituteID=?" );
pstmt.setLong( 1, instituteId );
rs = pstmt.executeQuery();
StringBuffer sb = new StringBuffer();
while ( rs.next() )
{
sb.append( rs.getString( "department" ) + ","
+ rs.getLong( "id" ) );
if ( !rs.isLast() ) sb.append( "" );
}
return sb;
}
catch ( Exception e )
{
e.printStackTrace();
}
finally
{
close( rs );
close( pstmt );
close( connection );
}
return null;
}
当然这些都是由
public ActionForward getDepartments( ActionMapping mapping,
ActionForm form, HttpServletRequest req, HttpServletResponse res )
throws Exception
{
Service service = getService();
res.getWriter().write(
service.getDepartmentsByInstituteIdForAjax(
Long.parseLong( req.getParameter( "institute" ) ) )
.toString() );
return null;
}
来控制
===========班级的再这里
public ActionForward getClasses( ActionMapping mapping, ActionForm form,
HttpServletRequest req, HttpServletResponse res ) throws Exception
{
Service service = getService();
res.getWriter().write(
service.getClassesByDepartmentIdForAjax(
Long.parseLong( req.getParameter( "department" ) ) )
.toString() );
return null;
}
public StringBuffer getClassesByDepartmentIdForAjax( Long departmentId )
{
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
connection = getConnection();
pstmt = connection
.prepareStatement( "select * from class where departmentID=?" );
pstmt.setLong( 1, departmentId );
rs = pstmt.executeQuery();
StringBuffer sb = new StringBuffer();
while ( rs.next() )
{
sb.append( rs.getString( "class" ) + "," + rs.getLong( "id" ) );
if ( !rs.isLast() ) sb.append( "" );
}
return sb;
}
catch ( Exception e )
{
e.printStackTrace();
}
finally
{
close( rs );
close( pstmt );
close( connection );
}
return null;
}
function getClasses(department)
{
createXMLHttpRequest()
var url = "ajax.do?department="+department+"&method=getClasses"
xmlHttp.open("GET",url, true)
xmlHttp.onreadystatechange = classes
xmlHttp.send(null)
}
function classes()
{
if (xmlHttp.readyState == 4)
{
if (xmlHttp.status == 200)
{
resText = xmlHttp.responseText
each = resText.split("")
buildSelect( each, document.getElementById("classid"), "请选择所在班级");
}
}
}
从分页体会MVC
大家都知道Struts是一种基于MVC的结构,而这个MVC又怎么样理解呢?书上阐述的一般都很具体,而我的理解很直白,我们可以把业务逻辑放到每个JSP页面中,当你访问一个JSP页面的时候,就可以看到业务逻辑得到的结果,而把这些业务逻辑与HTML代码夹杂到了一起,一定会造成一些不必要的麻烦,可以不可以不让我们的业务逻辑和那些HTML代码夹杂到一起呢?多少得搀杂一些,那干脆,尽量少的吧,于是我们可以尝试着把业务逻辑的运算过程放到一个Action里,我们访问这个Action,之后Action执行业务逻辑,最后把业务逻辑的结果放到request中,并将页面请求转发给一个用于显示结果的jsp页面,这样,这个页面就可以少去很