J2EE初级学习项目——JSP+Servlet+JavaBean开发模式+Filter+(Listener)+Jdbc的用户登录注册系统

xiaoxiao2021-02-28  11

前言:这是一篇新手在浏览了别的大神的作品后总结出的项目,建议大家都动手实践一下。需要对JSP+Servlet+JSTL+mysql+jdbc技术有了解,会使用juint测试,如果有能力的可以在本篇基础上使用Ajax、jQuery、json和JavaScript做出更好的界面和效果,再者可使用工厂模式来完成。(以后学习后会在本文结尾进行技术的更新)

1.搭建开发环境

 

要引入的jar包有:

可以去我的资源里下载(本来想免费提供,发现csdn不能免费上传)、也可以到我的源码内获取

 

 

2.分层架构的代码编写

分层架构的代码是按照【域模型层(domain)】—【数据访问层(dao、dao.impl)】—【业务处理层(service、service.impl)】—【表现层(web.controller、web.UI、web.filter、web.listener)】—【工具类(util)】—【测试类(junit.test)】的顺序编写的。

3.开始编写

一、域模型层(domain层的开发)

User用户实体类代码如下:

 

package me.TiHom.domain; import java.util.Date; /** * 用户实体类 */ public class User { private String id; //用户ID private String username; //用户的用户名 private String password; //用户的密码 private String email; //用户的邮箱 private Date birthday; //用户的生日 private String nickname; //用户的昵称 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 getPassword() { return password; } public void setPassword(String password) { this.password = password; } 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; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } }

二、介于要与数据库连接,我们需要先编写数据库连接类JdbcUtils,因为只是简单案例,这里就采用最普通的jdbc连接方法而不采用连接池。

 

对于我这个新手,在一开始写的时候总是忘了导包,记住要把mysql的驱动包放到web包的WEB-INF下,否则虽然在main中是显示连接成功但是在web应用时实际是没连接上的

JdbcUtils数据库连接类代码如下:(写的有点粗糙,以后的项目会改进写法)

 

package me.TiHom.web.utils; import java.sql.*; /** * 数据库连接类 */ public class JdbcUtils { public static final String driver = "com.mysql.jdbc.Driver"; public static final String URL = "jdbc:mysql://127.0.0.1:3306/这是你表的名字"; public static final String USERNAME = "root"; public static final String PASSWORD = "这是你的密码"; public static Connection connection = null; static { try { /* 加载驱动 */ Class.forName(driver); /* 使用驱动类连接数据库 */ connection = DriverManager.getConnection(URL,USERNAME,PASSWORD); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } /* 方便其他包调用来连接数据库 */ public static Connection getConnection(){ return connection; } /* 方便其他包直接调用释放连接资源 */ public static void releaseJdbc(Statement statement, ResultSet resultSet){ try { if(statement!=null){ statement.close(); } if(resultSet!=null){ resultSet.close(); } } catch (Exception e) { e.printStackTrace(); } } /* 测试数据库的连接是否成功 */ public static void main(String[] args) { try { Connection connection = JdbcUtils.getConnection(); if(connection != null){ System.out.println("数据库连接正常!"); }else { System.out.println("数据库连接异常!"); } }catch (Exception ex){ ex.printStackTrace(); } } }

 

 

 

 

 

三、数据访问层的开发(与数据库交互dao、dao.impl)

对于Dao层的作用,建议观看我之前总结过的MVC模式小结。总结不好之处欢迎指出。

我的习惯是在dao上实现接口;在dao.impl上写实现接口的类,这里面包含对数据库的CURD(增删改查)操作和数据业务逻辑处理。

UserDao接口类代码如下:

 

package me.TiHom.dao; import me.TiHom.domain.User; public interface UserDao { /** * 添加用户 * @param user */ void add(User user); /** * 根据用户名和密码来查找用户 * @param username * @param password * @return 查到的用户 */ User find(String username,String password); /** * 根据用户名来查找用户 * @param username * @return 查找到的用户 */ boolean find(String username); }

 

 

 

UserDaoJdbcImpl接口实现类的代码如下:

 

package me.TiHom.dao.impl; import me.TiHom.web.utils.JdbcUtils; import me.TiHom.dao.UserDao; import me.TiHom.domain.User; import me.TiHom.exception.DaoException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; /** * UserDao接口的实现类 */ public class UserDaoJdbcImpl implements UserDao{ @Override public void add(User user){ Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "insert into users(id,username,password,email,birthday,nickname) values(?,?,?,?,?,?)"; ps = conn.prepareStatement(sql); ps.setString(1,user.getId()); ps.setString(2,user.getUsername()); ps.setString(3,user.getPassword()); ps.setString(4,user.getEmail()); ps.setDate(5,new java.sql.Date(user.getBirthday().getTime())); ps.setString(6,user.getNickname()); int num = ps.executeUpdate(); if(num < 1){ throw new RuntimeException("注册用户失败!!"); } } catch (Exception e) { /* 我们的处理方法——你这个有程序有异常,你拿到这个异常,怎么做呢?就看异常你希不希望上一层程序处理? 如果你不希望上一层程序处理,免得给上一层程序带来麻烦,就转为运行时异常抛出去, 如果你希望上一层程序处理,就转为编译时异常直接往上抛出去。 */ throw new DaoException(e); } finally { //释放资源 JdbcUtils.releaseJdbc(ps,rs); } } @Override public User find(String username,String password){ Connection conn = null; //PreparedStatement防止sql注入 PreparedStatement ps = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "select * from users where username=? and password=?"; ps = conn.prepareStatement(sql); ps.setString(1,username); ps.setString(2,password); rs = ps.executeQuery(); while(rs.next()){ User user = new User(); user.setId(rs.getString("id")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); user.setEmail(rs.getString("email")); user.setBirthday(rs.getDate("birthday")); user.setNickname(rs.getString("nickname")); return user; } return null; } catch (Exception e) { //异常直接往上面抛,除了给上层带来麻烦,没有任何好处,所以可将异常转型然后往业务层抛 throw new DaoException(e); } finally { JdbcUtils.releaseJdbc(ps,rs); } } @Override public boolean find(String username){ Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "select * from users where username=?"; ps = conn.prepareStatement(sql); ps.setString(1,username); rs = ps.executeQuery(); while(rs.next()){ return true; } return false; } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.releaseJdbc(ps,rs); } } }

 

 

 

这里可能会对我抛异常这里有疑惑,因为我抛的是自定义的异常,在参考了别的博主的文章后得到的解释:

1.问:在实际开发过程中,经常自定义异常的作用?

a.最大的好处是我把自定义异常抛出去的时候,人家收到这个异常,一看到这个异常的名字就可以知道到底是哪一层出现的问题,就可以快速定位到这一层来找问题,最好每一层都有个自定义异常

b.你这个程序有异常,你拿到这个异常,怎么做呢?就看异常你希不希望上一层程序去处理?如果你不希望上一层程序处理,免得给上一层程序带来麻烦,就转为运行时异常抛出,如果你希望上一层程序处理就转为编译时异常直接往上抛出。

2.问:Statement和PrepareStatement的区别:

a.PrepareStatement是Statement的子类 b.PrepareStatement可以防止sql注入的问题 c.PrepareStatement会对sql语句进行预编译,以减轻数据库服务器的压力

3.子类在覆盖父类的方法时,不能抛出比父类更多的异常。

我看了一个例子描述的挺好的,就是比如你爸爸做了坏事,他生的孩子应该尽量不要比他爸爸更坏,应该向好的发展。

 

四、service层的开发(service层对web层提供所有的业务服务)

UserService的代码如下:

 

package me.TiHom.web.service; import me.TiHom.domain.User; import me.TiHom.exception.UserExistException; public interface UserService { /** * 提供注册服务 * @param user * @throws UserExistException */ void register(User user) throws UserExistException; /** * 提供登录服务 * @param username * @param password * @return */ User login(String username,String password); }

BusinessServiceImpl接口实现类的代码如下:

 

 

package me.TiHom.web.service.impl; import me.TiHom.web.service.UserService; import me.TiHom.web.utils.ServiceUtils; import me.TiHom.dao.UserDao; import me.TiHom.domain.User; import me.TiHom.exception.UserExistException; import factory.DaoFactory; public class BusinessServiceImpl implements UserService { /* * 业务逻辑层和数据访问层要解耦——希望底层Dao层代码换了,业务逻辑层的代码一行不改,这时要用到工厂设计模式 * 要解耦,由两种方法: * 1.工厂模式 * 2.spring */ private UserDao dao = DaoFactory.getInstance().createDao(UserDao.class); @Override //对web层提供注册服务 public void register(User user) throws UserExistException{ //先判断当前要注册的用户是否存在 if(dao.find(user.getUsername())){ /* * service层是由web层来调用的 * 发现当前要注册的用户已存在,要提醒给web层,web层给用户一个友好提示 * 希望web层一定要处理,处理之后给用户一个友好提示,所以抛一个编译的异常 * 执行时异常是不行的,因为web层可处理可不处理 */ throw new UserExistException(); } else { //先对密码进行MD5加密 user.setPassword(ServiceUtils.md5(user.getPassword())); dao.add(user); } } @Override public User login(String username,String password){ //先把密码md5一把再找 password = ServiceUtils.md5(password); return dao.find(username,password); } }

这里用了工厂模式和MD5加密,先往下看,等等会解释。

 

五、web层开发

1.开发注册功能

 

 

创建一个RegisterUIServlet为用户提供注册用户界面,当RegisterUIServlet收到用户请求后,就跳转到register.jsp界面,在开发中如果项目中有一些敏感的web资源不想被外界直接访问,那么可以考虑将这些敏感的web资源放到WEB-INF目录下,这样就可以禁止外界直接通过URL来访问了。

RegisterUIServlet的代码如下:

 

package me.TiHom.web.UI; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 为用户提供注册用户界面的Servlet * RegisterUIServlet负责为用户输出注册界面 * 当用户访问RegisterUIServlet时,就跳转到WEB-INF/pages目录下的register.jsp页面 */ public class RegisterUIServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(req,resp); } }

 

 

 

register.jsp的代码如下:

 

<%-- Created by IntelliJ IDEA. User: lenovo Date: 2018/2/5 Time: 13:31 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>注册用户界面</title> </head> <body> <center> <h1>欢迎来到注册用户界面!!!</h1> <form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post"> <table width="60%" border="1"> <tr> <td>用户名:</td> <td> <input type="text" name="username" placeholder="用户名不能有特殊字符"> </td> </tr> <tr> <td>昵称:</td> <td> <input type="text" name="nickname"> </td> </tr> <tr> <td>密码:</td> <td> <input type="password" name="password" placeholder="密码长度在6-16位字符内"> </td> </tr> <tr> <td>确认密码:</td> <td> <input type="password" name="confirmPassword"> </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="submit" value="注册"> </td> <td> <input type="reset" value="重置"> </td> </tr> </table> </form> </center> </body> </html>

这里就做一个简单的界面就好,你们如果有较好的前端基础可以进行改进,原谅博主对前端只懂个皮毛

 

疑问:

针对上面的

<form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post">

 

这里用了EL表达式,使用${pageContext.request.contextPath}等价于<%=request.getContextPath()%>,比如我的项目名是day01,那么在浏览器输出时是http://localhost:8080/day01/register.jsp

${pageContext.request.contextPath}取出来的就是/day01,而”/“代表的的就是http://localhost:8080。

2.在控制层中编写用于处理用户注册的Servlet

RegisterServlet的主要作用:

①接受客户端提交到服务器的表单数据

②校检表单数据的合法性,如果校检失败就返回register.jsp,并回显错误信息

③如果校检成功,调用service层向数据库中注册用户

为了方便Servlet中接收表单并且校检表单数据,我设计了一个用于校检注册表单数据的RegisterFormbean,再写WebUtils工具类,封装客户端提交的表单数据到formbean中。

RegisterFormbean的代码如下:

 

package me.TiHom.web.formbean; import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; import java.util.HashMap; import java.util.Map; /** * 封装的用户注册表单bean,用来接收register.jsp中的表单输入项的值 * RegisterFormBean中的属性与register.jsp中的表单输入项的name一一对应 * RegisterFormBean的职责除了负责接收register.jsp中的表单输入项的值之外还担任这校检表单输入值的合法性 */ public class RegisterFormBean { //这里的属性要与表单对应的name的值相同 private String username; //用户名 private String password; //密码 private String confirmPassword; //确认密码 private String email; //邮箱 private String birthday; //生日 private String nickname; //昵称 /** * 存储校检时不通过时给用户的错误提示信息 */ 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; } /** * 负责校验输入项的合法性 * 用户名不能为空且要是3-16位字符的数字或字母 * 密码不能为空 * 两次密码要一致 * 邮箱可以为空,但是不为空时要合法 * 生日可以为空,但是不为空时要合法 * 昵称不能为空且要是3-16位的字符的数字或字母 * @return 表单是否合法 */ public boolean validate() { boolean isOk = true; //对用户名合法性的校验 if (this.username == null || this.username.trim().equals("")) { isOk = false; errors.put("username", "用户名不能为空!!!"); } else { if (!this.username.matches("[0-9a-zA-Z]{3,16}")) { isOk = false; errors.put("username", "用户名必须是3-16位的字母或数字!!!"); } } //对密码的合法性校验 if (this.password == null || this.password.trim().equals("")) { isOk = false; errors.put("password", "密码不能为空!!!"); } //确认密码 if (!this.confirmPassword.equals(this.password)) { isOk = false; errors.put("confirmPassword", "两次密码不一致!!!"); } //对邮箱的校验 if (this.email != null && !this.email.trim().equals("")) { /* * 邮箱在现实中存在的几种情况 * xxx@163.com * xxx@163.com.cn * xx_xx.xx@163.com.cn */ if (!this.email.matches("\\w+@\\w+(\\.\\w+)+")) { isOk = false; errors.put("email", "邮箱不是一个合法的邮箱!!!"); } } //对生日日期的校检 if (this.birthday != null && this.birthday.trim().equals("")) { try { //这里是将字符串转换成日期,如果转换出错则会抛出异常进入catch区域 DateLocaleConverter conver = new DateLocaleConverter(); conver.convert(this.birthday,"yyyy-MM-dd HH:mm:ss"); } catch (Exception e) { isOk = false; errors.put("birthday", "生日必须要是一个日期!!!"); } } //对昵称的校检 if(this.nickname==null || this.nickname.trim().equals("")){ isOk = false; errors.put("nickname","昵称不能为空!!!"); }else { /* * 昵称必须是中文 * 汉字区间:[\u4e00-\u9fa5] */ if(!this.nickname.matches("^([\\u4e00-\\u9fa5]+)$")){ isOk = false; errors.put("nickname","昵称必须是汉字!!!"); } } return isOk; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getConfirmPassword() { return confirmPassword; } public void setConfirmPassword(String confirmPassword) { this.confirmPassword = confirmPassword; } 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; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } }

这里面涉及了正则表达式和对日期的校检:

 

1.解释下日期有关的问题:

编写一个测试类

会报异常

因此需要改进代码:只需改动x->8

正常运行

但是如果输入1980-10-32的话上述也不会出现异常,但是明显日期是非法的,所以继续往下

使用BeanUtils的日期转换器DateLocaleConverter

报异常

因此用这个方法来校检日期是最正确的!!!

2.关于正则表达式,我学的比较少,所以在以后深入学习后再补充~

 

3.创建WebUtils工具类

WebUtils工具类的的作用就是封装客户端提交的表单数据到formbean中

这里贴上WebUtils的代码:

 

package me.TiHom.web.utils; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.Converter; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.InvocationTargetException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; import java.util.UUID; /** * 把request对象中的请求参数封装到bean中 */ public class WebUtils { /** * 将request对象转换成T对象 * @param request * @param clazz * @param <T> * @return */ public static <T> T request2Bean(HttpServletRequest request,Class<T> clazz){ try { //创建要封装数据的bean T bean = clazz.newInstance(); //把request中的数据整合到bean中 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); } } public static void copyBean(Object src,Object dest){ //注册日期转换器 ConvertUtils.register(new Converter() { @Override public Object convert(Class type, Object value) { if(value==null){ return null; } String str = (String) value; if(str.trim().equals("")){ return null; } SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); try { return df.parse(str); } catch (ParseException e) { throw new RuntimeException(e); } } }, Date.class); try { /* * 在此项目中,是从formbean将属性拷贝到user对象中去,bean的拷贝 * 在实际开发中,是非常实用的 */ BeanUtils.copyProperties(dest,src); } catch (Exception e) { throw new RuntimeException(e); } } /** * 产生全球唯一的id * @return */ public static String makeId(){ //UUID算法根据你系统的网卡的xx地址、CPU、机器的型号等等生成一个128位长的字符串 return UUID.randomUUID().toString(); } }/** * 把request对象中的请求参数封装到bean中 */ public class WebUtils { /** * 将request对象转换成T对象 * @param request * @param clazz * @param <T> * @return */ public static <T> T request2Bean(HttpServletRequest request,Class<T> clazz){ try { //创建要封装数据的bean T bean = clazz.newInstance(); //把request中的数据整合到bean中 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); } } public static void copyBean(Object src,Object dest){ //注册日期转换器 ConvertUtils.register(new Converter() { @Override public Object convert(Class type, Object value) { if(value==null){ return null; } String str = (String) value; if(str.trim().equals("")){ return null; } SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); try { return df.parse(str); } catch (ParseException e) { throw new RuntimeException(e); } } }, Date.class); try { /* * 在此项目中,是从formbean将属性拷贝到user对象中去,bean的拷贝 * 在实际开发中,是非常实用的 */ BeanUtils.copyProperties(dest,src); } catch (Exception e) { throw new RuntimeException(e); } } /** * 产生全球唯一的id * @return */ public static String makeId(){ //UUID算法根据你系统的网卡的xx地址、CPU、机器的型号等等生成一个128位长的字符串 return UUID.randomUUID().toString(); } }

 

 

 

再来看看RegisterServlet的代码:

 

package me.TiHom.controller; import me.TiHom.domain.User; import me.TiHom.exception.UserExistException; import me.TiHom.web.formbean.RegisterFormBean; import me.TiHom.web.service.UserService; import me.TiHom.web.service.impl.BusinessServiceImpl; import me.TiHom.web.utils.WebUtils; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Date; /** * 处理用户注册的Servlet */ public class RegisterServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //将客户端提交的表单数据封装到RegisterFormBean对象中 RegisterFormBean formBean = WebUtils.request2Bean(req,RegisterFormBean.class); //校检用户注册填写的表单数据 if(!formBean.validate()){ //将封装了用户填写的表单数据的formBean对象发送回register.jsp页面的form表单中进行显示 req.setAttribute("formBean",formBean); //校检失败就说明是用户填写的表单数据有问题,那么就跳转回register.jsp req.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(req,resp); return; } User user = new User(); try { //WebUtils.copyBean(formBean,user); //注册字符串到日期的转换器 ConvertUtils.register(new DateLocaleConverter(), Date.class); //把表单数据填充到JavaBean中 BeanUtils.copyProperties(user,formBean); //设置用户的ID属性 user.setId(WebUtils.makeId()); UserService service = new BusinessServiceImpl(); //调用service层提供的注册用户服务实现用户注册 service.register(user); String message = String.format( "注册成功!!3秒后为您自动跳转到登录界面!!<meta http-equiv='refresh' content='3;url=%s' />", req.getContextPath()+"/servlet/LoginUIServlet"); req.setAttribute("message",message); req.getRequestDispatcher("/message.jsp").forward(req,resp); } catch (UserExistException e){ formBean.getErrors().put("username","注册用户已经存在!!!"); req.setAttribute("formBean",formBean); req.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(req,resp); } catch (Exception e) { e.printStackTrace(); //在后台记录异常 req.setAttribute("message","对不起,注册失败!!!"); req.getRequestDispatcher("/message.jsp").forward(req,resp); } } } /** * 处理用户注册的Servlet */ public class RegisterServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //将客户端提交的表单数据封装到RegisterFormBean对象中 RegisterFormBean formBean = WebUtils.request2Bean(req,RegisterFormBean.class); //校检用户注册填写的表单数据 if(!formBean.validate()){ //将封装了用户填写的表单数据的formBean对象发送回register.jsp页面的form表单中进行显示 req.setAttribute("formBean",formBean); //校检失败就说明是用户填写的表单数据有问题,那么就跳转回register.jsp req.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(req,resp); return; } User user = new User(); try { //WebUtils.copyBean(formBean,user); //注册字符串到日期的转换器 ConvertUtils.register(new DateLocaleConverter(), Date.class); //把表单数据填充到JavaBean中 BeanUtils.copyProperties(user,formBean); //设置用户的ID属性 user.setId(WebUtils.makeId()); UserService service = new BusinessServiceImpl(); //调用service层提供的注册用户服务实现用户注册 service.register(user); String message = String.format( "注册成功!!3秒后为您自动跳转到登录界面!!<meta http-equiv='refresh' content='3;url=%s' />", req.getContextPath()+"/servlet/LoginUIServlet"); req.setAttribute("message",message); req.getRequestDispatcher("/message.jsp").forward(req,resp); } catch (UserExistException e){ formBean.getErrors().put("username","注册用户已经存在!!!"); req.setAttribute("formBean",formBean); req.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(req,resp); } catch (Exception e) { e.printStackTrace(); //在后台记录异常 req.setAttribute("message","对不起,注册失败!!!"); req.getRequestDispatcher("/message.jsp").forward(req,resp); } } }

用户注册时如果填写的表单数据校验不通过,那么服务器端就将一个存储了错误提示消息和表单数据的formbean对象存储到request对象中,然后发送回register.jsp页面,因此我们需要在register.jsp页面中取出request对象中formbean对象,然后将用户填写的表单数据重新回显到对应的表单项上面,将出错时的提示消息也显示到form表单上面,让用户知道是哪些数据填写不合法! (其实这里可以使用Ajax技术,有时间补上)

 

修改register.jsp页面,代码如下:

 

<%-- Created by IntelliJ IDEA. User: lenovo Date: 2018/2/5 Time: 13:31 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>注册用户界面</title> </head> <body> <center> <h1>欢迎来到注册用户界面!!!</h1> <form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post"> <table width="60%" border="1"> <tr> <td>用户名:</td> <td> <input type="text" name="username" placeholder="用户名不能有特殊字符"> <span id="testChange">${formBean.errors.username}</span> </td> </tr> <tr> <td>昵称:</td> <td> <input type="text" name="nickname"> <span id="">${formBean.errors.nickname}</span> </td> </tr> <tr> <td>密码:</td> <td> <input type="password" name="password" placeholder="密码长度在6-16位字符内"> <span>${formBean.errors.password}</span> </td> </tr> <tr> <td>确认密码:</td> <td> <input type="password" name="confirmPassword"> <span>${formBean.errors.confirmPassword}</span> </td> </tr> <tr> <td>邮箱:</td> <td> <input type="text" name="email"> <span>${formBean.errors.email}</span> </td> </tr> <tr> <td>生日:</td> <td> <input type="text" name="birthday"> <span>${formBean.errors.birthday}</span> </td> </tr> <tr> <td> <input type="submit" value="注册"> </td> <td> <input type="reset" value="重置"> </td> </tr> </table> </form> </center> </body> </html>

 

 

到这里基本注册功能就已经完善好了。

4.开发登录功能

直接贴代码不多说:

login.jsp

 

<%-- Created by IntelliJ IDEA. User: lenovo Date: 2018/2/6 Time: 11:21 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>用户登录</title> </head> <body> <form action="${pageContext.request.contextPath}/servlet/LoginServlet" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="登录"> </form> </body> </html>

LoginUIServlet的代码:

 

 

package me.TiHom.web.UI; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class LoginUIServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher("/WEB-INF/pages/login.jsp").forward(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }

LoginServlet的代码:

 

 

package me.TiHom.controller; import me.TiHom.domain.User; import me.TiHom.web.service.UserService; import me.TiHom.web.service.impl.BusinessServiceImpl; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取用户输入的用户名 String username = req.getParameter("username"); //获取用户输入的密码 String password = req.getParameter("password"); UserService service = new BusinessServiceImpl(); //用户登录 User user = service.login(username,password); if(user==null){ String message = String.format( "对不起,用户名或密码有错误!请重新登录!2秒后为您自动跳转到登录界面!!<meta http-equiv='refresh' content='2;url=%s'", req.getContextPath()+"/servlet/LoginUIServlet"); req.setAttribute("message",message); req.getRequestDispatcher("/WEB-INF/pages/login.jsp").forward(req,resp); return; } //登录成功后,就将用户存入session中 req.getSession().setAttribute("user",user); String message = String.format( "恭喜:%s,登陆成功!本页将在3秒后跳转到首页!! <meta http-equiv='refresh' content='3;url=%s'", user.getUsername(), req.getContextPath()+"/index.jsp" ); req.setAttribute("message",message); req.getRequestDispatcher("/index.jsp").forward(req,resp); } }

 

这样登录功能就开发好了。

 

 

5.开发注销功能

这里直接贴代码:

LogoutServlet的代码如下:

 

package me.TiHom.controller; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; public class LogoutServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); if(session!=null){ session.removeAttribute("user"); } //注销成功 req.setAttribute("message", "注销成功,浏览器将在3秒后跳转,如果没有跳转,你就点...!!<meta http-equiv='refresh' content='3;url="+req.getContextPath()+"/index.jsp'>"); req.getRequestDispatcher("/message.jsp").forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }

message.jsp的代码:

 

 

<%-- Created by IntelliJ IDEA. User: lenovo Date: 2018/2/6 Time: 12:05 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>全局消息显示界面</title> </head> <body> ${message} </body> </html>

 

 

 

4.一些重要的代码

web.xml的代码

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>LoginUIServlet</servlet-name> <servlet-class>me.TiHom.web.UI.LoginUIServlet</servlet-class> </servlet> <servlet> <servlet-name>RegisterUIServlet</servlet-name> <servlet-class>me.TiHom.web.UI.RegisterUIServlet</servlet-class> </servlet> <servlet> <servlet-name>RegisterServlet</servlet-name> <servlet-class>me.TiHom.controller.RegisterServlet</servlet-class> </servlet> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>me.TiHom.controller.LoginServlet</servlet-class> </servlet> <servlet> <servlet-name>LogoutServlet</servlet-name> <servlet-class>me.TiHom.controller.LogoutServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginUIServlet</servlet-name> <url-pattern>/servlet/LoginUIServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>RegisterUIServlet</servlet-name> <url-pattern>/servlet/RegisterUIServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>RegisterServlet</servlet-name> <url-pattern>/servlet/RegisterServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/servlet/LoginServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>LogoutServlet</servlet-name> <url-pattern>/servlet/LogoutServlet</url-pattern> </servlet-mapping> <!-- 使用过滤器定义全局编码 --> <filter> <filter-name>EncodingFilter</filter-name> <filter-class>me.TiHom.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>EncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>

这里过滤器的作用是设置全局编码格式,代码如下:

 

package me.TiHom.web.filter; import javax.servlet.*; import java.io.IOException; public class CharacterEncodingFilter implements Filter { //存储系统使用的字符编码 private String encoding = null; @Override public void init(FilterConfig filterConfig) throws ServletException { this.encoding = filterConfig.getInitParameter("encoding"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { servletRequest.setCharacterEncoding(encoding); filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { } }

 

index.jsp的代码也贴上:

 

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%-- Created by IntelliJ IDEA. User: lenovo Date: 2018/2/4 Time: 10:43 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>首页</title> <script> function doLogout() { //访问LogoutServlet注销当前用户 window.location.href="${pageContext.request.contextPath}/servlet/LogoutServlet" } </script> </head> <body> <h1>TiHom的网站</h1> <hr/> <c:if test="${user==null}"> <a href="${pageContext.request.contextPath}/servlet/RegisterUIServlet" target="_blank">注册</a> <a href="${pageContext.request.contextPath}/servlet/LoginUIServlet">登陆</a> </c:if> <c:if test="${user!=null}"> 欢迎您:${user.username} <input type="button" value="退出登陆" onclick="doLogout()"> </c:if> <hr/> </body> </html

 

经过我的测试没有任何错误,大家可以放心使用,随后我也会把源码放在GitHub上供大家下载。

5.开发总结和问题的解疑

1.搭建开发环境:博主使用的是较为普遍的IDEA、Tomcat服务器、Mysql数据库。

先把lib导入,然后将各个层进行分包,并不是说这些都是必备的,但是这样分包的好处是在以后的维护和管理上清晰明了,而不会造成多重逻辑的混乱。

缺点是在小项目这样分包会导致包过多而显得过于复杂。

 

部署tomcat服务器

2.开发数据库连接类

我本人倾向于先开发数据库连接类,然后在类中定义main方法先进行测试,成功才进行下一步编辑。(仅属于个人习惯)。推荐有能力的多学习用数据库连接池技术,目前很多使用阿里的druid连接池技术,通用的有c3p0和dbcp连接池技术。(如果有时间我会总结一下,博主是大一的新手,如果谁有好的总结可以在评论推荐一下)

3.开发domain

这里面主要是一些实体类,如User类。

4.开发dao层

①dao操作接口:每一个dao操作接口规定了一张表在一个项目中的具体操作方法,此接口的名称最好按照如下的格式编写:”表名称Dao“

dao接口方法如下名编写:

 

更新数据库:doXxx()查询数据库:findXxx()或getXxx()

 

②dao层接口的实现类:开发与数据库进行数据交互的类,因为层层递进,所以业务逻辑处理应该从dao层开始开发。一般包含数据库的CURD操作。

流程:注册就对应的先将用户输入的数据接收后转为User对象,然后在dao层的doAdd方法中添加用户进入数据库中(这里可以在juint中创建一个专门检验是否能够正常操作数据库的测试类)

查询findUser(String username,String password)方法和findUser(String username)方法,前者在登录时需要在数据库中检查是否有这个用户,后者是在注册时查询是否造成用户用户名重复。

5.开发service层

先写到这里明天介绍工厂模式...

大家可以看我下一篇文章关于工厂模式和单例模式的。

 

 

 

 

 

 

 

转载请注明原文地址: https://www.6miu.com/read-2150372.html

最新回复(0)