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

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

1.搭建开发环境

 

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

要引入的jar包有:

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

可以去我的资源里下载(本来想免费提供,发现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应用时实际是没连接上的

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

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(增删改查)操作和数据业务逻辑处理。

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

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.开发注册功能

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

 

创建一个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" %>


  注册用户界面


  

欢迎来到注册用户界面!!!

用户名:
昵称:
密码:
确认密码:
邮箱:
生日:


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

 

疑问:

针对上面的

 

这里用了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 errors = new HashMap();
    public Map getErrors(){
        return errors;
    }

    public void setErrors(Map 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("")) {
            /*
             * 邮箱在现实中存在的几种情况
             * [email protected]
             * [email protected]
             * [email protected]
             */
            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.解释下日期有关的问题:

编写一个测试类

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

会报异常

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

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

正常运行

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

使用BeanUtils的日期转换器DateLocaleConverter

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

报异常

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

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

 

3.创建WebUtils工具类

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

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

这里贴上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 
     * @return
     */
    public static  T request2Bean(HttpServletRequest request,Class clazz){
        try {
            //创建要封装数据的bean
            T bean = clazz.newInstance();
            //把request中的数据整合到bean中
            Enumeration 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 
     * @return
     */
    public static  T request2Bean(HttpServletRequest request,Class clazz){
        try {
            //创建要封装数据的bean
            T bean = clazz.newInstance();
            //把request中的数据整合到bean中
            Enumeration 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秒后为您自动跳转到登录界面!!",
                    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秒后为您自动跳转到登录界面!!",
                    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" %>


  注册用户界面


  

欢迎来到注册用户界面!!!

用户名: ${formBean.errors.username}
昵称: ${formBean.errors.nickname}
密码: ${formBean.errors.password}
确认密码: ${formBean.errors.confirmPassword}
邮箱: ${formBean.errors.email}
生日: ${formBean.errors.birthday}

 

 


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

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" %>


  用户登录


  
用户名:
密码:

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秒后为您自动跳转到登录界面!!

 


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

 

 

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秒后跳转,如果没有跳转,你就点...!!");
        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" %>


  全局消息显示界面


  ${message}

 

 

 

4.一些重要的代码

web.xml的代码



    
        LoginUIServlet
        me.TiHom.web.UI.LoginUIServlet
    
    
        RegisterUIServlet
        me.TiHom.web.UI.RegisterUIServlet
    
    
        RegisterServlet
        me.TiHom.controller.RegisterServlet
    
    
        LoginServlet
        me.TiHom.controller.LoginServlet
    
    
        LogoutServlet
        me.TiHom.controller.LogoutServlet
    

    
        LoginUIServlet
        /servlet/LoginUIServlet
    
    
        RegisterUIServlet
        /servlet/RegisterUIServlet
    
    
        RegisterServlet
        /servlet/RegisterServlet
    
    
        LoginServlet
        /servlet/LoginServlet
    
    
        LogoutServlet
        /servlet/LogoutServlet
    

    
    
        EncodingFilter
        me.TiHom.web.filter.CharacterEncodingFilter
        
            encoding
            UTF-8
        
    
    
        EncodingFilter
        /*
    

    
        index.jsp
    

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

 

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" %>

  
    首页
    
  
  
  

TiHom的网站


注册 登陆 欢迎您:${user.username}

 

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

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

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

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

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

 

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

部署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层

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

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

 

 

 

 

 

 

 

你可能感兴趣的:(企业级项目开发)