JavaWeb实现用户登录注册功能实例
一、Servlet+JSP+JavaBean开发模式(MVC)介绍
Servlet+JSP+JavaBean模式(MVC)适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp负责数据显示,javabean负责封装数据。 Servlet+JSP+JavaBean模式程序各个模块之间层次清晰,web开发推荐采用此种模式。
这里以一个最常用的用户登录注册程序来讲解Servlet+JSP+JavaBean开发模式,通过这个用户登录注册程序综合案例,把之前的学过的XML、Xpath、Servlet、jsp的知识点都串联起来。
二、创建MVC架构的Web项目
在MyEclipse中新创建一个webmvcframework项目,导入项目所需要的开发包(jar包),创建项目所需要的包,在java开发中,架构的层次是以包的形式体现出来的
| 项目所需要的开发包(jar包) | ||
| 序号 | 开发包名称 | 描述 |
| 1 | dom4j-1.6.1.jar | dom4j用于操作XML文件 |
| 2 | jaxen-1.1-beta-6.jar | 用于解析XPath表达式 |
| 3 | commons-beanutils-1.8.0.jar | 工具类,用于处理bean对象 |
| 4 | commons-logging.jar | commons-beanutils-1.8.0.jar的依赖jar包 |
| 5 | jstl.jar | jstl标签库和EL表达式依赖包 |
| 6 | standard.jar | jstl标签库和EL表达式依赖包 |
一个良好的JavaWeb项目架构应该具有以上的11个包,这样显得层次分明,各个层之间的职责也很清晰明了,搭建JavaWeb项目架构时,就按照上面的1~11的序号顺序创建包:domain→dao→dao.impl→service→service.impl→web.controller→web.UI→web.filter→web.listener→util→junit.test,包的层次创建好了,项目的架构也就定下来了,当然,在实际的项目开发中,也不一定是完完全全按照
| 项目所需要的包 | |||
| 序号 | 包名 | 描述 | 所属层次 |
| 1 | me.gacl.domain | 存放系统的JavaBean类(只包含简单的属性以及属性对应的get和set方法,不包含具体的业务处理方法),提供给【数据访问层】、【业务处理层】、【Web层】来使用 | domain(域模型)层 |
| 2 | me.gacl.dao | 存放访问数据库的操作接口类 | 数据访问层 |
| 3 | me.gacl.dao.impl | 存放访问数据库的操作接口的实现类 | |
| 4 | me.gacl.service | 存放处理系统业务接口类 | 业务处理层 |
| 5 | me.gacl.service.impl | 存放处理系统业务接口的实现类 | |
| 6 | me.gacl.web.controller | 存放作为系统控制器的Servlet | Web层(表现层) |
| 7 | me.gacl.web.UI | 存放为用户提供用户界面的servlet(UI指的是user interface) | |
| 8 | me.gacl.web.filter | 存放系统的用到的过滤器(Filter) | |
| 9 | me.gacl.web.listener | 存放系统的用到的监听器(Listener) | |
| 10 | me.gacl.util | 存放系统的通用工具类,提供给【数据访问层】、【业务处理层】、【Web层】来使用 | |
| 11 | junit.test | 存放系统的测试类 | |
上面说的来创建包的层次结构,而是根据项目的实际情况,可能还需要创建其
他的包,这个得根据项目的需要来定了
在src目录(类目录)下面,创建用于保存用户数据的xml文件(DB.xml)
在WEB-INF目录下创建一个pages目录,pages目录存放系统的一些受保护(不允许用户直接通过URL地址访问)的jsp页面,用户要想访问这些受保护的jsp页面,那么只能通过me.gacl.web.UI这个包里面的Servlet
创建好的项目如下图(图-1)所示:

图-1
三、分层架构的代码编写
分层架构的代码也是按照【域模型层(domain)】→【数据访问层(dao、dao.impl)】→【业务处理层(service、service.impl)】→【表现层(web.controller、web.UI、web.filter、web.listener)】→【工具类(util)】→【测试类(junit.test)】的顺序进行编写的。
3.1、开发domain层
在me.gacl.domain包下创建一个User类

User类具体代码如下:
?| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950 | package me.gacl.domain;import java.io.Serializable;import java.util.Date;/*** @author gacl* 用户实体类*/public class User implements Serializable {private static final long serialVersionUID = -L;// 用户IDprivate String id;// 用户名private String userName;// 用户密码private String userPwd;// 用户邮箱private String email;// 用户生日private Date birthday;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getUserPwd() {return userPwd;}public void setUserPwd(String userPwd) {this.userPwd = userPwd;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}} |
3.2、开发数据访问层(dao、dao.impl)
在me.gacl.dao包下创建一个IUserDao接口类,对于开发接口类,我习惯以字母I作类的前缀,这样一眼就看出当前这个类是一个接口,这也算是一种良好的开发习惯吧,通过看类名就可以方便区分出是接口还是具体的实现类。

IUserDao接口的具体代码如下:
?| 123456789101112131415161718192021 | package me.gacl.dao;import me.gacl.domain.User;public interface IUserDao {/*** 根据用户名和密码来查找用户* @param userName* @param userPwd* @return 查到到的用户*/User find(String userName, String userPwd);/*** 添加用户* @param user*/void add(User user);/**根据用户名来查找用户* @param userName* @return 查到到的用户*/User find(String userName);} |
对于接口中的方法定义,这个只能是根据具体的业务来分析需要定义哪些方法了,但是无论是多么复杂的业务,都离不开基本的CRUD(增删改查)操作,Dao层是直接和数据库交互的,所以Dao层的接口一般都会有增删改查这四种操作的相关方法。
在me.gacl.dao.impl包下创建一个UserDaoImpl类

UserDaoImpl类是IUserDao接口的具体实现类,对于接口的实现类命名方式,我习惯以"接口名(去除前缀I)+impl"形式或者"接口名+impl"形式来命名:IUserDao(接口)→UserDaoImpl(实现类)或者IUserDao(接口)→IUserDaoImpl(实现类),这也算是一些个人的编程习惯吧,平时看到的代码大多数都是以这两种形式中的一种来来命名接口的具体实现类的,反正就是要能够一眼看出接口对应的实现类是哪一个就可以了。
UserDaoImpl类的具体代码如下:
?| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 | package me.gacl.dao.impl;import java.text.SimpleDateFormat;import org.domj.Document;import org.domj.Element;import me.gacl.dao.IUserDao;import me.gacl.domain.User;import me.gacl.util.XmlUtils;/*** IUserDao接口的实现类* @author gacl*/public class UserDaoImpl implements IUserDao {@Overridepublic User find(String userName, String userPwd) {try{Document document = XmlUtils.getDocument();//使用XPath表达式来操作XML节点Element e = (Element) document.selectSingleNode("//user[@userName=""+userName+"" and @userPwd=""+userPwd+""]");if(e==null){return null;}User user = new User();user.setId(e.attributeValue("id"));user.setEmail(e.attributeValue("email"));user.setUserPwd(e.attributeValue("userPwd"));user.setUserName(e.attributeValue("userName"));String birth = e.attributeValue("birthday");SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");user.setBirthday(sdf.parse(birth));return user;}catch (Exception e) {throw new RuntimeException(e);}}@SuppressWarnings("deprecation")@Overridepublic void add(User user) {try{Document document = XmlUtils.getDocument();Element root = document.getRootElement();Element user_node = root.addElement("user"); //创建user结点,并挂到rootuser_node.setAttributeValue("id", user.getId());user_node.setAttributeValue("userName", user.getUserName());user_node.setAttributeValue("userPwd", user.getUserPwd());user_node.setAttributeValue("email", user.getEmail());SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");user_node.setAttributeValue("birthday", sdf.format(user.getBirthday()));XmlUtils.writeXml(document);}catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic User find(String userName) {try{Document document = XmlUtils.getDocument();Element e = (Element) document.selectSingleNode("//user[@userName=""+userName+""]");if(e==null){return null;}User user = new User();user.setId(e.attributeValue("id"));user.setEmail(e.attributeValue("email"));user.setUserPwd(e.attributeValue("userPwd"));user.setUserName(e.attributeValue("userName"));String birth = e.attributeValue("birthday");SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");user.setBirthday(sdf.parse(birth));return user;}catch (Exception e) {throw new RuntimeException(e);}}} |
3.3、开发service层(service层对web层提供所有的业务服务)
在me.gacl.service包中创建IUserService接口类

IUserService接口的具体代码如下:
?| 123456789101112131415161718 | package me.gacl.service;import me.gacl.domain.User;import me.gacl.exception.UserExistException;public interface IUserService {/*** 提供注册服务* @param user* @throws UserExistException*/void registerUser(User user) throws UserExistException;/*** 提供登录服务* @param userName* @param userPwd* @return*/User loginUser(String userName, String userPwd);} |
在me.gacl.service.impl包中创建UserServiceImpl类

UserServiceImpl类为IUserService接口的具体实现类,具体代码如下:
?| 1234567891011121314151617181920212223 | package me.gacl.service.impl;import me.gacl.dao.IUserDao;import me.gacl.dao.impl.UserDaoImpl;import me.gacl.domain.User;import me.gacl.exception.UserExistException;import me.gacl.service.IUserService;public class UserServiceImpl implements IUserService {private IUserDao userDao = new UserDaoImpl();@Overridepublic void registerUser(User user) throws UserExistException {if (userDao.find(user.getUserName())!=null) {//checked exception //unchecked exception//这里抛编译时异常的原因:是我想上一层程序处理这个异常,以给用户一个友好提示throw new UserExistException("注册的用户名已存在!!!");}userDao.add(user);}@Overridepublic User loginUser(String userName, String userPwd) {return userDao.find(userName, userPwd);}} |
3.4、开发web层
3.4.1、 开发注册功能
1、在me.gacl.web.UI包下写一个RegisterUIServlet为用户提供注册界面

RegisterUIServlet收到用户请求后,就跳到register.jsp
RegisterUIServlet的代码如下:
?| 12345678910111213141516171819202122 | package me.gacl.web.UI;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/*** @author gacl* 为用户提供注册的用户界面的Servlet* RegisterUIServlet负责为用户输出注册界面* 当用户访问RegisterUIServlet时,就跳转到WEB-INF/pages目录下的register.jsp页面*/public class RegisterUIServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {request.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(request, response);}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}} |
2、在/WEB-INF/pages/目录下编写用户注册的jsp页面register.jsp

凡是位于WEB-INF目录下的jsp页面是无法直接通过URL地址直接访问的,

在开发中如果项目中有一些敏感web资源不想被外界直接访问,那么可以考虑将这些敏感的web资源放到WEB-INF目录下,这样就可以禁止外界直接通过URL来访问了。
register.jsp页面的代码如下:
?| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 | <%@ page language="java" pageEncoding="UTF-"%><!DOCTYPE HTML><html><head><title>用户注册</title></head><body style="text-align: center;"><form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post"><table width="%" border=""><tr><td>用户名</td><td><input type="text" name="userName"></td></tr><tr><td>密码</td><td><input type="password" name="userPwd"></td></tr><tr><td>确认密码</td><td><input type="password" name="confirmPwd"></td></tr><tr><td>邮箱</td><td><input type="text" name="email"></td></tr><tr><td>生日</td><td><input type="text" name="birthday"></td></tr><tr><td><input type="reset" value="清空"></td><td><input type="submit" value="注册"></td></tr></table></form></body></html> |
register.jsp中的<form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post">指明表单提交后,交给RegisterServlet进行处理
3、在me.gacl.web.controller包下编写用于处理用户注册的RegisterServlet

RegisterServlet担任着以下几个职责:
1、接收客户端提交到服务端的表单数据。
2、校验表单数据的合法性,如果校验失败跳回到register.jsp,并回显错误信息。
3、如果校验通过,调用service层向数据库中注册用户。
为了方便RegisterServlet接收表单数据和校验表单数据,在此我设计一个用于校验注册表单数据RegisterFormbean,再写WebUtils工具类,封装客户端提交的表单数据到formbean中。
在me.gacl.web.formbean包下创建一个用于校验注册表单数据RegisterFormbean

RegisterFormbean代码如下:
?| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 | package me.gacl.web.formbean;import java.util.HashMap;import java.util.Map;import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;/*** 封装的用户注册表单bean,用来接收register.jsp中的表单输入项的值* RegisterFormBean中的属性与register.jsp中的表单输入项的name一一对应* RegisterFormBean的职责除了负责接收register.jsp中的表单输入项的值之外还担任着校验表单输入项的值的合法性* @author gacl**/public class RegisterFormBean {//RegisterFormBean中的属性与register.jsp中的表单输入项的name一一对应//<input type="text" name="userName"/>private String userName;//<input type="password" name="userPwd"/>private String userPwd;//<input type="password" name="confirmPwd"/>private String confirmPwd;//<input type="text" name="email"/>private String email;//<input type="text" name="birthday"/>private String birthday;/*** 存储校验不通过时给用户的错误提示信息*/private Map<String, String> errors = new HashMap<String, String>();public Map<String, String> getErrors() {return errors;}public void setErrors(Map<String, String> errors) {this.errors = errors;}/** validate方法负责校验表单输入项* 表单输入项校验规则:* private String userName; 用户名不能为空,并且要是-的字母 abcdABcd * private String userPwd; 密码不能为空,并且要是-的数字* private String confirmPwd; 两次密码要一致* private String email; 可以为空,不为空要是一个合法的邮箱 * private String birthday; 可以为空,不为空时,要是一个合法的日期*/public boolean validate() {boolean isOk = true;if (this.userName == null || this.userName.trim().equals("")) {isOk = false;errors.put("userName", "用户名不能为空!!");} else {if (!this.userName.matches("[a-zA-Z]{,}")) {isOk = false;errors.put("userName", "用户名必须是-位的字母!!");}}if (this.userPwd == null || this.userPwd.trim().equals("")) {isOk = false;errors.put("userPwd", "密码不能为空!!");} else {if (!this.userPwd.matches("\d{,}")) {isOk = false;errors.put("userPwd", "密码必须是-位的数字!!");}}// private String password; 两次密码要一致if (this.confirmPwd != null) {if (!this.confirmPwd.equals(this.userPwd)) {isOk = false;errors.put("confirmPwd", "两次密码不一致!!");}}// private String email; 可以为空,不为空要是一个合法的邮箱if (this.email != null && !this.email.trim().equals("")) {if (!this.email.matches("\w+@\w+(\.\w+)+")) {isOk = false;errors.put("email", "邮箱不是一个合法邮箱!!");}}// private String birthday; 可以为空,不为空时,要是一个合法的日期if (this.birthday != null && !this.birthday.trim().equals("")) {try {DateLocaleConverter conver = new DateLocaleConverter();conver.convert(this.birthday);} catch (Exception e) {isOk = false;errors.put("birthday", "生日必须要是一个日期!!");}}return isOk;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getUserPwd() {return userPwd;}public void setUserPwd(String userPwd) {this.userPwd = userPwd;}public String getConfirmPwd() {return confirmPwd;}public void setConfirmPwd(String confirmPwd) {this.confirmPwd = confirmPwd;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public String getBirthday() {return birthday;}public void setBirthday(String birthday) {this.birthday = birthday;}} |
在me.gacl.util包下创建一个WebUtils工具类,该工具类的功能就是封装客户端提交的表单数据到formbean中

| 1234567891011121314151617181920212223242526272829303132333435363738 | package me.gacl.util;import java.util.Enumeration;import java.util.UUID;import javax.servlet.http.HttpServletRequest;import org.apache.commons.beanutils.BeanUtils;/*** @author gacl* 把request对象中的请求参数封装到bean中*/public class WebUtils {/*** 将request对象转换成T对象* @param request * @param clazz* @return*/public static <T> T requestBean(HttpServletRequest request,Class<T> clazz){try{T bean = clazz.newInstance();Enumeration<String> e = request.getParameterNames(); while(e.hasMoreElements()){String name = (String) e.nextElement();String value = request.getParameter(name);BeanUtils.setProperty(bean, name, value);}return bean;}catch (Exception e) {throw new RuntimeException(e);}}/*** 生成UUID* @return*/public static String makeId(){return UUID.randomUUID().toString();}} |
最后看一下负责处理用户注册的RegisterServlet完整代码:
?| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 | package me.gacl.web.controller;import java.io.IOException;import java.util.Date;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.beanutils.BeanUtils;import org.apache.commons.beanutils.ConvertUtils;import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;import me.gacl.domain.User;import me.gacl.exception.UserExistException;import me.gacl.service.IUserService;import me.gacl.service.impl.UserServiceImpl;import me.gacl.util.WebUtils;import me.gacl.web.formbean.RegisterFormBean;/*** 处理用户注册的Servlet* @author gacl**/public class RegisterServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//将客户端提交的表单数据封装到RegisterFormBean对象中RegisterFormBean formbean = WebUtils.requestBean(request,RegisterFormBean.class);//校验用户注册填写的表单数据if (formbean.validate() == false) {//如果校验失败//将封装了用户填写的表单数据的formbean对象发送回register.jsp页面的form表单中进行显示request.setAttribute("formbean", formbean);//校验失败就说明是用户填写的表单数据有问题,那么就跳转回register.jsprequest.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(request, response);return;}User user = new User();try {// 注册字符串到日期的转换器ConvertUtils.register(new DateLocaleConverter(), Date.class);BeanUtils.copyProperties(user, formbean);//把表单的数据填充到javabean中user.setId(WebUtils.makeId());//设置用户的Id属性IUserService service = new UserServiceImpl();//调用service层提供的注册用户服务实现用户注册service.registerUser(user);String message = String.format("注册成功!!秒后为您自动跳到登录页面!!<meta http-equiv="refresh" content=";url=%s"/>", request.getContextPath()+"/servlet/LoginUIServlet");request.setAttribute("message",message);request.getRequestDispatcher("/message.jsp").forward(request,response);} catch (UserExistException e) {formbean.getErrors().put("userName", "注册用户已存在!!");request.setAttribute("formbean", formbean);request.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(request, response);} catch (Exception e) {e.printStackTrace(); // 在后台记录异常request.setAttribute("message", "对不起,注册失败!!");request.getRequestDispatcher("/message.jsp").forward(request,response);}}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}} |
用户注册时如果填写的表单数据校验不通过,那么服务器端就将一个存储了错误提示消息和表单数据的formbean对象存储到request对象中,然后发送回register.jsp页面,因此我们需要在register.jsp页面中取出request对象中formbean对象,然后将用户填写的表单数据重新回显
- 上一篇:没有了
- 下一篇:没有了
