基于MVC模式模拟实现登录注册开发过程(一)

需求:模拟前端后台登录注册的效果(MVC模式)

准备工作:

1.搭建开发环境

1.1 导入开发包


*dom4j.jar
*jstl.jar
*beanUtils.jar
*log4j.jar

1.2 创建组织程序的包


*com.Cecilia.domain
*com.Cecilia.dao
*com.Cecilia.dao.impl
*com.Cecilia.service
*com.Cecilia.service.impl
*com.Cecilia.web.controller(处理请求的servlet)
*com.Cecilia.web.UI
*com.Cecilia.utils
*junit4.test(测试)

1.3 创建代表数据库的xml

MVC模式图

基于MVC模式模拟实现登录注册开发过程(一)_第1张图片

实现过程

*Dao层:

1.首先从Domain实体类开始,登录注册需要用户相关信息,因此构建User类。

User类:

/**
 * 实体类
 * @author 芷若初荨
 *
 */
public class User {
    private String id;
    private String username;
    private String password;
    private String nickname;
    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 getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getNickname() {
        return nickname;
    }
    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
    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;
    }


}

2.由于这次只是使用XML模拟数据库存储数据的效果,因此构建一个XMLUtils工具类,实现获取文件和将获得数据写入XML文件中。

XmlUtils类:

/**
 * 工具类
 * @author 芷若初荨
 *
 */
public class XmlUtils {
    private static String filepath;
    //静态代码块,只执行一次
    static{
        filepath=XmlUtils.class.getClassLoader().getResource("users.xml").getPath();
    }
    //获取文件
    public static Document getDocument() throws Exception{
        SAXReader reader=new SAXReader();
        Document document=reader.read(new File(filepath));
        return document;


    }
    //将文件写入XML中
    public static void write2Xml(Document document) throws IOException, FileNotFoundException{
        //获得格式化器对象
        OutputFormat format=OutputFormat.createPrettyPrint();
        //设置编码
        format.setEncoding("UTF-8");
        //获得写入流
        XMLWriter writer=new XMLWriter(new FileOutputStream(filepath),format);
        //写到XML文件中
        writer.write(document);

        //美化代码,使之变得紧凑
        format=OutputFormat.createCompactFormat();
        writer=new XMLWriter(System.out,format);
        writer.write(document);
    }
}

3.针对User对象,接下来就是实现具体操作,在这里可以先构建构建实现类,然后再抽取实现类为接口,也可以交换顺序

UserDaoImpl类:

/**
 * 实现类
 * @author 芷若初荨
 *
 */
public class UserDaoImpl implements UserDao {
    //添加功能
    @Override
    public void add(User user){
        try {
            Document document=XmlUtils.getDocument();
            //添加节点
            Element root=document.getRootElement();
            Element user_tag=root.addElement("root");
            user_tag.setAttributeValue("id",user.getId());
            user_tag.setAttributeValue("username",user.getUsername());
            user_tag.setAttributeValue("password",user.getPassword());
            user_tag.setAttributeValue("email",user.getEmail());
            user_tag.setAttributeValue("birthday",user.getBirthday()==null?"":user.getBirthday().toString());
            user_tag.setAttributeValue("nickname",user.getNickname());
            //写到XML文件中
            XmlUtils.write2Xml(document);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            throw new RuntimeException(e);
        }

    }
    //查询功能
    @Override
    public User find(String username,String password){
        try {
            Document document=XmlUtils.getDocument();
            //搜索
            Element e=(Element) document.selectSingleNode("//user[@username+']"+username+"and @password='"+password+"']");
            if(e==null){
                return null;
            }
            User user=new User();
            String date=e.attributeValue("brthday");
            if(date==null||date.equals("")){
                user.setBirthday(null);
            }else{
                SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
                user.setBirthday(sdf.parse(date));
            }
            user.setEmail(e.attributeValue("email"));
            user.setId(e.attributeValue("id"));
            user.setPassword(e.attributeValue("password"));
            user.setUsername(e.attributeValue("username"));
            user.setNickname(e.attributeValue("nickname"));


            return user;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            throw new RuntimeException(e);
        }

    }
    //查询指定用户在注册表中是否存在
    @Override
    public boolean check(String username){

        Document document;
        try {
            document = XmlUtils.getDocument();
            Element e=(Element) document.selectSingleNode("//user[@username+']"+username+"']");
            if(e==null){
                return false;
            }
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            throw new RuntimeException(e1);
        }
        return true;
    }
}

UserDao接口:

package com.Cecilia.dao;
/**
 * 抽出方法成接口
 */
import com.Cecilia.domain.User;

public interface UserDao {

    //添加功能
    void add(User user);

    //查询功能
    User find(String username, String password);

    //查询指定用户在注册表中是否存在
    boolean check(String username);

}

4.测试类:

UserDaoTest类:


/**
 * 测试UserDao类
 * @author 芷若初荨
 *
 */
public class UserDaoTest {
    public void testAdd(){
        User user=new User();
        user.setBirthday(new Date());
        user.setEmail("[email protected]");
        user.setId("2");
        user.setUsername("Nacy");
        user.setPassword("456");
        user.setNickname("nn");

        UserDao dao=new UserDaoImpl();
        dao.add(user);
    }
    public void testFind(){
        UserDao dao=new UserDaoImpl();
        dao.find("Cecilia", "123");

    }
}

5.此外在用户的信息处理的过程中需要自己写个异常来对其进行处理,而不能直接上抛给界面。

UserExistException类:

/**
 * 自定义异常
 * @author 芷若初荨
 *
 */
public class UserExistException extends Exception {

    public UserExistException() {
        // TODO Auto-generated constructor stub
    }

    public UserExistException(String message) {
        super(message);
        // TODO Auto-generated constructor stub
    }

    public UserExistException(Throwable cause) {
        super(cause);
        // TODO Auto-generated constructor stub
    }

    public UserExistException(String message, Throwable cause) {
        super(message, cause);
        // TODO Auto-generated constructor stub
    }

    public UserExistException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
        // TODO Auto-generated constructor stub
    }

}

*Service层:

1.针对服务层的操作,需要写一个ServiceUtils层;

ServiceUtils类:

/**
 * 服务层的工具类
 * @author 芷若初荨
 *
 */
public class ServiceUtils {
    public static String md5(String message){
        try {
            MessageDigest md=MessageDigest.getInstance("md5");
            byte md5[]=md.digest(message.getBytes());//转化成字节
            BASE64Encoder encoder=new BASE64Encoder();
            /*在eclipse和MyEclipse中直接使用,却找不到该类的解决方法:
             * 右键项目-》属性-》java bulid path-》jre System Library-》access rules-》resolution选择accessible,
             * 下面填上** 点击确定即可!!!
            */
            return encoder.encode(md5);
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
         throw new RuntimeException(e);
        }


    }

2.服务层的具体实现类

ServiceImpl类:


/**
 * 业务服务层:注册和登录
 * @author 芷若初荨
 *
 */
public class ServiceImpl {
    //要实现注册和登录就需要数据访问对象
    private UserDao dao=new UserDaoImpl();
    //使用这种方式如果底层出现改变,那么也需要改变,解决方法是:工厂模式、Spring


    //对Web层进行注册服务
    public void register(User user) throws UserExistException{
        //先判断注册的用户是否存在
        boolean b=dao.check(user.getUsername());
        if(b){
            //希望用户能够一定处理,那么需要抛出编译时异常
            throw new UserExistException();//发现注册用户已存在,则给web层抛出一个异常,给用户友好提示

        }else{
            //用户不存在,则设置密码和用户名
            user.setPassword(ServiceUtils.md5(user.getPassword()));
            dao.add(user);
        }
    }
    //对web层进行登录服务
    public User login(String username,String password){
        password=ServiceUtils.md5(password);
        return  dao.find(username, password);

    }
}

3.测试类:

ServiceTest类:

/**
 * 服务层的测试
 * @author 芷若初荨
 *
 */
public class ServiceTest {
    //测试注册
    @Test
    private void testRegister(){
        User user=new User();
        user.setUsername("cc");
        user.setId("3");
        user.setPassword("123");
        user.setNickname("cc");
        user.setEmail("[email protected]");
        user.setBirthday(new Date());

        ServiceImpl service=new ServiceImpl();
        try {
            service.register(user);
            System.out.println("注册成功!");
        } catch (UserExistException e) {
            // TODO Auto-generated catch block
            System.out.println("用户已存在!");
        }
    }
    //测试登录
    @Test
    public void testLogin(){
        ServiceImpl service=new ServiceImpl();
        User user=service.login("cc", "123");
        System.out.println(user);
    }

}

*Web层:

1.对于Web层的数据可以放在一个WebUtils类,具体达到封装数据的效果。

WebUtils类:

/**
 * 针对Web层写一个封装数据的工具类
 * @author 芷若初荨
 *
 */
public class WebUtils {
    //参数设为class可以避免在调用类中new一个对象,
    public static  T request2Bean(HttpServletRequest req,Class beanClass){
        try{
        //1.创建封装数据的bean
        T bean=beanClass.newInstance();

        //2.把request中数据添加到bean中
        Enumeration e=req.getParameterNames();
        while(e.hasMoreElements()){
            String name=(String) e.nextElement();
            String value=req.getParameter(name);
            BeanUtils.setProperty(bean,name,value);//导包org.apache.commons.beanutils.BeanUtils
        }

        return bean;
        }catch(Exception e){
            throw new RuntimeException(e);
        }

    }
    //实现两个Bean的copy
    public static void copyBean(Object src,Object dest){
        //注册个转换器
        ConvertUtils.register(new Converter(){
            //错误:缺少commons-beanutils.jar
            public Object convert(Class type,Object value){
                if(value==null){
                    return null;
                }
                String str=(String)value;
                if(str.trim().equals("")){
                    return null;
                }
                SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
                try{
                    return sdf.parse(str);
                }catch(Exception e){
                    throw new RuntimeException(e);
                }
            }

        },Date.class);
        try{
            //PropertyUtils.copyProperties(dest,src);//需要导包commons-beanutils包BeanUtils和PropertyUtils都有这种功能
        }catch(Exception e){
            e.printStackTrace();
        }

    }
    //产生全世界唯一的id
    public static String generateID(){
        return UUID.randomUUID().toString();
    }
}

2.在Web层需要和用户打交道,包括登录、注册、注销等操作,因此需要构建三个处理类。

LoginServlet类:

/**
 * 登录具体处理类
 * @author 芷若初荨
 *
 */
public class LoginServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        String username=req.getParameter("username");
        String password=req.getParameter("password");

        ServiceImpl service=new ServiceImpl();
        User user=service.login(username, password);
        if(user!=null){
            req.getSession().setAttribute("user", user);
            //让用户登录,跳转首页
            resp.sendRedirect("request.getContextPath()"+"index.jsp");
            return;
        }
        req.setAttribute("message", "用户名或密码错误!");
        req.getRequestDispatcher("/message.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(req, resp);
    }



}

RegisterServlet类:

/**
 * 处理注册的Servlet
 * @author 芷若初荨
 *
 */
public class RegisterServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        //首先必须解决中文乱码问题(!!!)
        req.setCharacterEncoding("UTF-8");
        //1.对提交的表单进行校验(formBean)
        FormBean form=WebUtils.request2Bean(req, FormBean.class);
        //在此处需要强转类型,解决的方法是:在原来方法中使用泛型
         boolean b=form.validate();
        //2.如果校验失败,则跳回表单页面,回显示校验失败
         if(!b){
             req.setAttribute("form", form);
             req.getRequestDispatcher("/WEN-INF/jsp/Register.jsp").forward(req, resp);
             return;
         }
        //3.如果校验成功,则调用Service层处理注册请求
         User user=new User();
         WebUtils.copyBean(form, user);
         //设置唯一的id
         user.setId(WebUtils.generateID());
         ServiceImpl service=new ServiceImpl();
         try {
            service.register(user);
            //成功注册
            //6.如果service处理成功,跳转到网站的全局信息显示页面,为用户注册成功的信息
            req.setAttribute("meassage", "恭喜您,注册成功!");
            req.getRequestDispatcher("/message.jsp").forward(req, resp);
        } catch (UserExistException e) {
            // TODO Auto-generated catch block
            //4.如果Service处理成功,并且失败原因是因为注册不成功,则跳回注册页面,显示注册用户已存在
            form.getErrors().put("username", "注册信息已存在!");
            req.setAttribute("form", form);//可以避免jsp中再次修改或添加
            //req.setAttribute("message", "注册用户信息已存在!");
            req.getRequestDispatcher("WEB-INF/jsp/register.jsp").forward(req, resp);

        }catch(Exception e){
            //5.如果处理不成功,并且不成功的原因是其他问题,则跳回网站的全局消息显示页面,为用户显示友好错误信息
            e.printStackTrace();
            req.setAttribute("message", "服务器出现未知错误!");
            req.getRequestDispatcher("/message.jsp").forward(req, resp);
            e.printStackTrace();
            return;
        }



    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(req, resp);
    }

}

LogoutServlet类:

/**
 * 注销
 * @author 芷若初荨
 *
 */
public class LogoutServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        HttpSession session=req.getSession();
        if(session!=null){
            session.removeAttribute("user");
        }
        //注销成功,接着跳转到显示界面,并控制消息显示界面过3秒跳转到首页
        req.setAttribute("message", "注销成功,浏览器将在3秒后跳转,如果没有跳,请点击...");
        req.getRequestDispatcher("/message.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(req, resp);
    }



}

3.处理后需要将其展示给用户,因此需要构建两个实现跳转效果的servlet.

LoginUIServlet类:


/**
 * 登录的处理Servlet类
 * @author 芷若初荨
 *
 */
public class LoginUIServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        req.getRequestDispatcher("/WEB-INF/jsp/Login.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(req, resp);
    }



}

RegisterUIServlet类:

/**
 * 注册界面
 * @author 芷若初荨
 *
 */
public class RegisterUIServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        //转发给
        req.getRequestDispatcher("/WEB-IF/jsp/Register.jsp");//斜杠代表web应用
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(req, resp);
    }

}

4.此外在处理登录、注册等一些信息时需要对其验证是否符合标准,需要使用一个FormBean类来封装其信息和一些需要提示给用户的错误操作信息。

FormBeanl类:

/**
 * 表单实体类
 * @author 芷若初荨
 *
 */
public class FormBean {
    private String username;
    private String password;
    private String repassword;
    private String email;
    private String birthday;
    private String nickname;
    private Map errors=new HashMap();//将错误信息存在一个集合中
    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 getRepassword() {
        return repassword;
    }
    public void setRepassword(String repassword) {
        this.repassword = repassword;
    }
    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;
    }
    public Map getErrors() {
        return errors;
    }
    public void setErrors(Map errors) {
        this.errors = errors;
    }
    //校验方法
    /*
     * 1.用户名不能为空;
     * 2.密码不能为空,并且是3-8位数字;
     * 3.确认密码不能为空,并且和密码一致;
     * 4.电子邮箱不能为空,必须是合法的邮箱;
     * 5.生日可以为空,并且是合法日期;
     * 6.昵称不能为空,并且是汉字
     */ 
    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](3,8)")){
                isOK=false;
                errors.put("username", "用户名必须是3-8位字母!");
            }
        }
        //密码的验证
        if(this.password==null||this.password.trim().equals("")){
            isOK=false;
            errors.put("password", "密码不能为空!");

        }else{
            if(!this.password.matches("\\d(3,8)")){
                isOK=false;
                errors.put("password", "密码必须是3-8位数字");
            }
        }
        //确认密码的验证
                if(this.repassword==null||this.repassword.trim().equals("")){
                    isOK=false;
                    errors.put("repassword", "确认密码不能为空!");

                }else{
                    if(!this.password.equals(this.repassword)){
                        isOK=false;
                        errors.put("repassword", "两次密码要一致!");
                    }
                }
                //邮箱的验证
                if(this.email==null||this.email.trim().equals("")){
                    isOK=false;
                    errors.put("email", "邮箱不能为空!");
                }else{
                    if(!this.email.matches("\\w+@\\w+(\\.\\w+)+")){
                        isOK=false;
                        errors.put("email", "邮箱格式不正确!");
                    }
                }
                //生日可以为空,不为空时,必须要是一个合法的日期
                if(this.birthday!=null&&this.birthday.trim().equals("")){
                    //不为空时,对其进行校验
                    try{
                        //DateLocaleConverter dlc=new DateLocaleConverter();//定义一个日期转换器
                        //dlc.convert(this.birthday,"yyyy-MM-dd");
                    }catch(Exception e){
                        isOK=false;
                        errors.put("birthday","生日输入格式不对!");
                    }

                }
                //校验昵称,不能为空,并且符合汉字
                if(this.nickname==null||this.nickname.trim().equals("")){
                    isOK=false;
                    errors.put("nickname", "昵称不能为空!");
                }else{
                    if(this.nickname.matches("^([\u4e00-\u9fa5]+)$")){//汉字正则表达式
                        isOK=false;
                        errors.put("nickname", "昵称格式不对!");
                    }

                }
        return isOK;

    }
}

*JSP文件和XML文件

(具体样式和排版并没有具体写,在这里主要是学习如何实现这个流程)

**注册界面 Register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>注册界面title>
head>
<body>
    <form action="$(PageContext.request.contextPath)/servlet/RegisterRevlet" method="post">
        <table id="formtable">
            <tr>
                <td class="td1">登录账号td>
                <td>
                    <input type="text" name="username" value="$(form.username)">
                    <span>$(form.errors.username)span>
                td>   
            tr>
            <tr>
                <td class="td2">重复密码td>
                <td>
                    <input type="password" name="password" value="$(form.password)">
                    <span>$(form.errors.password)span>
                td>   
            tr>
            <tr>
                <td class="td3">确认密码td>
                <td>
                    <input type="password" name="repassword" value="$(form.repassword)">
                    <span>$(form.errors.repassword)span>
                td>   
            tr>
            <tr>
                <td class="td4">邮箱td>
                <td>
                    <input type="text" name="email" value="$(form.email)">
                    <span>$(form.errors.email)span>
                td>   
            tr>
            <tr>
                <td class="td5">生日td>
                <td>
                    <input type="text" name="birthday" value="$(form.birthday)">
                    <span>$(form.errors.birthday)span>
                td>   
            tr>
            <tr>
                <td class="td6">昵称td>
                <td>
                    <input type="text" name="nickname" value="$(form.nickname)">
                    <span>$(form.errors.nickname)span>
                td>   
            tr>

        table>



    form>
body>
html>

**登录界面 login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link type="text/css" style="stylesheet" href="$(pagContext.request.contextPath/css/login.css"/>
<title>登录界面title>
head>
<body>
    <div id="form">
        <form action="$(pageContext.request.contextPath)/servlet/LoginServlet">

        form>

    div>
body>
html>

**首页 index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>首页title>
head>
<body>
    <h2>购物网站h2><br>
    <div style="text-align:right;">
        <c:if test="$(user!=null)" >
            欢迎您:$(user.nickname)  
            <a href="$(pageContext.request.contextPath)/servlet/LogoutServlet">注销a>
        c:if>
        <br>
        
        <c:if test="$(user==null)">
            <a href="$(pageContext.request.contextPath)/servlet/RegisterUIServlet">注册a>
            <a href="$(pageContext.request.ContextPath)/servlet/LoginUIServlet">登录a>
        c:if>
    div>
body>
html>

**网站全局消息显示页面 message.jsp

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>网站全局消息显示页面title>
head>
<body>
    $(message)
body>
html>

**User.xml文件 封装用户信息(以后一般使用数据库来对其操作,在这里只是对其进行模拟)

PS:以上是实现过程,后期会有针对我自己学习实现过程所遇到的一些问题的总结,这也是对自己的一些知识点的弥补,希望也对大家有帮助!如有错误地方,请指正!谢谢!

你可能感兴趣的:(Web前端开发之路,Java修炼之道)