Java Web项目记录——书城项目

600 阅读12分钟

书城项目——阶段1

练习任务:使用Vue.js实现以下功能:

  1. 显示表格;
  2. 显示四个文本框;
  3. 创建一个p标签来显示用户在文本框中实时输入的内容呢;
  4. 点击添加记录按钮实现记录的添加。

代码示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试一个Vue.js</title>
</head>
<body>
<div id="app" style="text-align: center">
    <table style="text-align: center" border="1" cellspacing="0" width="500px">
        <thead>
            <th>编号</th>
            <th>姓名</th>
            <th>年龄</th>
            <th>专业</th>
            <th>操作</th>
        </thead>
        <tbody>
            <tr v-for="(user,index) in userList">
                <td>{{user.id}}</td>
                <td>{{user.name}}</td>
                <td>{{user.age}}</td>
                <td>{{user.major}}</td>
                <td><button @click="deleteUser(index)">删除</button></td>
            </tr>
        </tbody>
    </table>
    <form method="post" action="www.baidu.com">
        编号:<input type="text" v-model="user.id" name="id"><br>
        姓名:<input type="text" v-model="user.name" name="name"><br>
        年龄:<input type="text" v-model="user.age" name="age"><br>
        专业:<input type="text" v-model="user.major" name="major"><br>
        <input type="button" @click="addUser" value="添加数据">
    </form>
</div>
</body>
<script type="text/javascript" src="../js/vue.min.js"></script>
<script type="text/javascript">
    var vue = new Vue({
        el:"#app",
        data:{
            user:[],
            userList:[
                {
                    "id":1,
                    "name":"张三",
                    "age":18,
                    "major":"Java"
                },
                {
                    "id":2,
                    "name":"李四",
                    "age":19,
                    "major":"php"
                },
                {
                    "id":3,
                    "name":"王五",
                    "age":17,
                    "major":"object-c"
                },
                {
                    "id":3,
                    "name":"王五",
                    "age":17,
                    "major":"object-c"
                },
                {
                    "id":3,
                    "name":"王五",
                    "age":17,
                    "major":"object-c"
                }
            ],
        },
        methods:{
            addUser: function(){
                event.preventDefault();
                this.userList.push(this.user);
                this.user = [];
            },
            deleteUser(i){
                this.userList.splice(i,1);

            }
        }

    });
</script>
</html>

Bug记录

函数名不要起delete,delete是会导致函数失效。

上手任务:

  1. 实现登录表单校验
  2. 实现注册表单校验
<body>
<div id="app">
    <div id="login_header">
        <a href="../../index.html">
            <img class="logo_img" alt="" src="../../static/img/logo.gif"/>
        </a>
    </div>

    <div class="login_banner">
        <div id="l_content">
            <span class="login_word">欢迎登录</span>
        </div>

        <div id="content">
            <div class="login_form">
                <div class="login_box">
                    <div class="tit">
                        <h1>尚硅谷会员</h1>
                    </div>
                    <div class="msg_cont">
                        <b></b>
                        <span class="errorMsg" style="color: red">{{errorMsg}}</span>
                    </div>
                    <div class="form">
                        <form action="login_success.html">
                            <label>用户名称:</label>
                            <input
                                    v-model="username"
                                    class="itxt"
                                    type="text"
                                    placeholder="请输入用户名"
                                    autocomplete="off"
                                    tabindex="1"
                                    name="username"
                                    id="username"

                            />
                            <br/>
                            <br/>
                            <label>用户密码:</label>
                            <input
                                    v-model="password"
                                    class="itxt"
                                    type="password"
                                    placeholder="请输入密码"
                                    autocomplete="off"
                                    tabindex="1"
                                    name="password"
                                    id="password"

                            />
                            <br/>
                            <br/>
                            <input type="submit" value="登录" id="sub_btn" @click="login"/>
                        </form>
                        <div class="tit">
                            <a href="regist.html">立即注册</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div id="bottom">
      <span>
        尚硅谷书城.Copyright &copy;2015
      </span>
    </div>
</div>

</body>
<script type="text/javascript" src="../../static/script/vue.min.js"></script>
<script type="text/javascript">
    let vue = new Vue({
        el: "#app",
        data: {
            "username": "",
            "password": "",
            "errorMsg": ""

        },
        methods: {
            login() {
                if (this.username != "") {
                    if (this.password == "") {
                        event.preventDefault();
                        this.errorMsg = "密码不能为空";
                    }else{
                      this.errorMsg = "";
                    }
                } else {
                    event.preventDefault();
                    this.errorMsg = "用户名不能为空";
                }
            }
        }
    });
</script>

描述:

  1. 使用Vue时,先向这个文件导入Vue.js。【不导入不能使用】
  2. 在js中创建Vue对象。【创建实体,承载el,data,methods】
  3. 设置挂载点。【搞错会失效】
  4. 判断双向绑定还是单向绑定,在html中和data项中一一对应,注意数据名称要对应。【搞错会失效】
  5. 在html对应控件上绑定事件驱动,在methods中写对应方法,注意事件名要对应上。【搞错会失效】

书城项目——阶段2

1. 回顾JDBC

JDBC全称:Java DataBase Connectivity【java数据库连接技术】

常用API

  • DriverManager:驱动管理器
  • Connection:连接对象
  • Statement【PreparedStatement】:操作数据库【执行sql语句】对象
  • ResultSet:处理结果集对象

2. 使用JDBC及相关知识点

准备工作

  • 一个配置文件:druid.properties
  • 两个工具类
    • BaseDao
    • JdbcUtils
  • 三个jar包
    • commons-dbutils-1.6.jar
    • druid-1.1.10.jar
    • mysql-connector-java-5.1.37-bin.jar

注意事项

  • web应用导入jar包,需要在WEB-INF下创建lib包,在lib包下导入所需jar包

JDBC练习需求及步骤

  • 需求:实现student的CRUD
  • 创建student表
  • 创建Student类
  • 创建StudentDao及StudentDaoImpl实现类

3. JavaBean

JavaBean:在不同层【包】之间传递数据

  • 数据bean:数据库中存在对应表结构,数据bean
  • 业务bean:数据库中不存在对应表结构,业务bean

4. 在bean中使用包装类优势

  • 包装类方便类型转换
  • 包装类方便空值判断
    • 如:int默认值:0 Integer默认值:null

5. 面向接口编程优势

优势

  • 提高程序扩展性

  • 降低程序维护成本

接口定义

  • 返回值:增删改均设置为void
  • 参数:参数与sql中占位符一一对应

6. 三层研发

为什么分层

  • 高内聚,低耦合【降低耦合度】
  • 降低程序维护成本
  • 提高研发效率

三层

  • 顾客->服务员 厨师 采购员
  • 表示层 业务逻辑层 数据访问层
  • html &servlet service dao

7.base标签

为什么使用base标签同一页面访问路径

​ 因为在页面中有很多的a标签、表单以及Ajax请求都需要写访问路径,而在访问路径中项目路径是一样的,所以如果不统一编写项目路径的话,就会发生当项目路径发生改变的时候该页面所有用到项目路径的地方都需要更改的情况。

base标签的语法规则

  • base标签要写在head标签内
  • base标签必须写在所有其他有路径的标签的前面
  • base标签使用href属性设置路径的基准
  • base标签生效的机制是:最终的访问地址=base标签href属性设置的基准+具体标签内的路径
  • 如果某个路径想要基于base中的路径进行路径编写,那么它不能以/开头

代码示例

原页面

 <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>书城首页</title>
    <link rel="stylesheet" href="./static/css/minireset.css" />
    <link rel="stylesheet" href="./static/css/common.css" />
    <link rel="stylesheet" href="./static/css/iconfont.css" />
    <link rel="stylesheet" href="./static/css/index.css" />
    <link rel="stylesheet" href="./static/css/swiper.min.css" />
  </head>

使用base后的页面

<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>书城首页</title>
    <base href="/bookstore/"/>
    <link rel="stylesheet" href="static/css/minireset.css"/>
    <link rel="stylesheet" href="static/css/common.css"/>
    <link rel="stylesheet" href="static/css/iconfont.css"/>
    <link rel="stylesheet" href="static/css/index.css"/>
    <link rel="stylesheet" href="static/css/swiper.min.css"/>
</head>

注意

base标签

<base href="/bookstore/"/>//项目名

替换

替换.png

8.上手任务

实现步骤

  1. 创建动态Web工程
  2. 将第一版书城的静态资源拷贝到web文件夹中
  3. 统一页面的基础访问路径
  4. 使用静态数据进行登录校验
  5. 使用完成注册时校验用户名是否存在

代码示例

1.静态数据登录校验

静态数据:【用户名:zhangsan,密码:123456】

servlet

package com.dyy.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "LoginServlet",urlPatterns = "/static_check_login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        String username = request.getParameter("username");
        String password = request.getParameter("password");

        if("zhangsan".equals(username)&&"123456".equals(password)){
            response.sendRedirect(request.getContextPath()+"/pages/user/login_success.html");
        }else{
            response.getWriter().write("用户名或密码错误,请重试");
            /*另一种错误信息方式,我们让他跳转回登录页面
            *response.sendRedirect(request.getContextPath()+"/pages/user/login.html");
            * */
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

表单

<form action="static_check_login" method="post">
              <label>用户名称:</label>
              <input
                      v-model="username"
                      class="itxt"
                      type="text"
                      placeholder="请输入用户名"
                      autocomplete="off"
                      tabindex="1"
                      name="username"
                      id="username"

              />
              <br/>
              <br/>
              <label>用户密码:</label>
              <input
                      v-model="password"
                      class="itxt"
                      type="password"
                      placeholder="请输入密码"
                      autocomplete="off"
                      tabindex="1"
                      name="password"
                      id="password"

              />
              <br/>
              <br/>
              <input type="submit" value="登录" id="sub_btn" @click="login"/>
            </form>
2.注册校验用户名是否存在

静态数据:【用户名:zhangsan】

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>尚硅谷会员注册页面</title>
    <base href="/pro03/">
    <link type="text/css" rel="stylesheet" href="static/css/style.css"/>
    <link rel="stylesheet" href="static/css/register.css"/>
    <style type="text/css">
        .login_form {
            height: 420px;
            margin-top: 25px;
        }
        /*<!--errorMess这个类样式表中控制的是默认隐藏的,此处使用自定义的一个样式覆盖,
        使其可见,但是在提交表单之前是不可见的,所以可以使用绑定样式表控制它的生效和失效-->*/
        .login_banner .register_form form .form-item .vv{
          visibility: visible;
        }
    </style>
</head>
<body>
<div id="app">
    <div id="login_header">
        <a href="index.html">
            <img class="logo_img" alt="" src="static/img/logo.gif"/>
        </a>
    </div>

    <div class="login_banner">
        <div class="register_form">
            <h1>注册尚硅谷会员</h1>
            <form action="static_check_register" method="post">
                <div class="form-item">
                    <div>
                        <label>用户名称:</label>
                        <input  name="username" v-model="username" type="text" placeholder="请输入用户名"/>
                    </div>

                    <span class="errMess" :class="{vv:isUnVv}">用户名:只能是6-12位,字母(大小写)、数字、_。</span>
                </div>
                <div class="form-item">
                    <div>
                        <label>用户密码:</label>
                        <input name="password" v-model="password" type="password" placeholder="请输入密码"/>
                    </div>
                    <span class="errMess" :class="{vv:isPwdVv}">密码:只能是6位,字母(大小写)、数字。</span>
                </div>
                <div class="form-item">
                    <div>
                        <label>确认密码:</label>
                        <input name="re_password" v-model="re_password" type="password" placeholder="请输入确认密码"/>
                    </div>
                    <span class="errMess" :class="{vv:isRePwdVv}">密码两次输入不一致</span>
                </div>
                <div class="form-item">
                    <div>
                        <label>用户邮箱:</label>
                        <input name="email" v-model="email" type="text" placeholder="请输入邮箱"/>
                    </div>
                    <span class="errMess" :class="{vv:isEmailVv}">请输入正确的邮箱格式</span>
                </div>
                <div class="form-item">
                    <div>
                        <label>验证码:</label>
                        <div class="verify">
                            <input name="code" type="text" v-model="code" placeholder=""/>
                            <img src="static/img/code.bmp" alt=""/>
                        </div>
                    </div>
                    <span class="errMess" :class="{vv:isCodeVv}">请输入正确的验证码</span>
                </div>
                <button type="submit" class="btn" @click="checkRegist">注册</button>
            </form>
        </div>
    </div>
    <div id="bottom">
      <span>
        尚硅谷书城.Copyright &copy;2015
      </span>
    </div>
</div>
</body>

<script type="text/javascript" src="static/script/vue.min.js"></script>
<script type="text/javascript">
    let vue = new Vue({
        el: "#app",
        data: {
            username: "",
            password: "",
            re_password: "",
            email: "",
          code:"",
            //动态绑定样式表
            "isUnVv": false,     //显示用户名合法性
            "isPwdVv": false,     //显示密码合法性
            "isRePwdVv": false,
            "isEmailVv": false,
            "isCodeVv": false
        },
        methods: {
            checkUserName() {
              this.isUnVv=false;
                let reg = /^[a-zA-Z0-9_]{6,12}$/;
                if (!reg.test(this.username)) {
                    event.preventDefault();
                    this.isUnVv = "true";
                }
            },
            checkPassword() {
                //恢复默认隐藏
                this.isPwdVv = false;
                //密码:只能是6位,字母(大小写)、数字。
                var pwdReg = /^[a-zA-Z0-9]{6}$/;
                if (!pwdReg.test(this.password)) {
                    //提示用户【将提示区域显示】
                    this.isPwdVv = true;
                    //取消控件默认行为
                    event.preventDefault();
                }

            },
            checkRePassword() {
                this.isRePwdVv = false;
                if (this.password != this.re_password) {
                    //提示用户两次密码不一致
                    this.isRePwdVv = true;
                    //取消控件默认行为
                    event.preventDefault();
                }
            },
            checkEmail() {
                this.isEmailVv = false;
                //定义验证邮箱正则【c)邮箱:API中的标准验证。】
                var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
                if (!regEmail.test(this.email)) {
                    //提示用户,输入有效格式不正确
                    this.isEmailVv = true;
                    //取消控件默认行为
                    event.preventDefault();
                }
            },
            //验证码非空验证
            checkCode() {
                this.isCodeVv = false;
                if (this.code == "") {
                    //提示用户,验证码不能为空
                    this.isCodeVv = true;
                    //取消控件默认行为
                    event.preventDefault();
                }
            },
          checkRegist(){
                this.checkUserName();
                this.checkPassword();
                this.checkRePassword();
                this.checkEmail();
                this.checkCode();
            }
        }
    });
</script>
</html>

package com.dyy.servlet;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;import java.util.Map;import java.util.Set;
/*静态校验注册*/
@WebServlet(name = "RegistServlet",urlPatterns = "/static_check_register")
public class RegistServlet extends HttpServlet {   
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");        

response.setContentType("text/html;charset=UTF-8");        
String username = request.getParameter("username");        /*System.out.println("尝试输出参数:");        
Map<String, String[]> parameterMap = request.getParameterMap();        
Set<String> p = parameterMap.keySet();        
for (String s : p) {            
String[] strings = parameterMap.get(s);            
System.out.print(s+":");            
for (String string : strings) {               
System.out.print(string+",");
}           
System.out.println();       
}*/       
if("zhangsan".equals(username)){           
response.getWriter().write("用户名重复,请重试");        
}else{            response.sendRedirect(request.getContextPath()+"/pages/user/regist_success.html");        
}        
System.out.println("注册功能使用结束");
}    
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);    
}
}
3.数据库连接池
  1. 导入driud相关jar包
  2. 写properties文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/bookstore?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true
username=root
password=123456
initialSize=10
minIdle=5
maxActive=20
maxWait=5000
  1. 写工具类,获得connection连接
package com.dyy.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtil {
    private static DataSource dataSource;
    static {
        try {
            InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("druid.properties");
            Properties p = new Properties();
            p.load(in);
            dataSource = DruidDataSourceFactory.createDataSource(p);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接
     */

    public static Connection getConnection()  {
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 释放资源
     * @param conn
     * @param stat
     * @param rs
     */
    public static void release(Connection conn, Statement stat, ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(stat!=null){
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

4.Dao
  1. 创建pojo类,用来接收查询数据库返回的数据
package com.dyy.pojo;

public class User {
    private int id;
    private String username;
    private String password;
    private String email;

    public User() {

    }

    public User(int id, String username, String password, String email) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.email = email;
    }

    public int getId() {
        return id;
    }

    public void setId(int 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;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}
  1. 编写BaseDao父类【所有dao接口实现类的父类】
package com.dyy.dao;
import com.dyy.util.JDBCUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class BaseDao<T>{

    private QueryRunner queryRunner = new QueryRunner();
    //定义一个变量来接收泛型的类型
    private Class<T> type;
    // 获取T的Class对象,获取泛型的类型,泛型是在被子类继承时才确定
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public BaseDao() {
//获取子类的类型
        Class clazz = this.getClass();
        //获取父类的类型
        //getGenericSuperclass()用来获取当前类的父类的类型
        //ParameterizedType表示的是带泛型的类型
        ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
        //获取具体的泛型类型 getActualTypeArguments获取具体的泛型的类型
        //这个方法会返回一个Type的数组
        Type[] types = parameterizedType.getActualTypeArguments();
        //获取具体的泛型的类型·
        this.type = (Class<T>) types[0];
    }
    /**
     * 通用的增删改操作
     * 			sql: insert delete update
     * @param sql
     * @param params
     * @return
     */
    public int update(String sql, Object... params) {
        Connection connection = JDBCUtil.getConnection();
        int count = 0;
        try {
            queryRunner.update(connection,sql,params);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtil.release(connection,null,null);
        }
        return count;
    }
    /**
     * 获取单个对象
     * @param sql
     * @param params
     * @return
     */
    public T getBean(String sql, Object... params) {
        Connection connection = JDBCUtil.getConnection();
        T t = null;
        try {
            t = queryRunner.query(connection, sql, new BeanHandler<T>(type),
                    params);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(connection,null,null);
        }
        return t;
    }
    /**
     * 获取所有对象
     * @param sql
     * @param params
     * @return
     */
    public List<T> getBeanList(String sql, Object... params) {
        // 获取连接
        Connection connection = JDBCUtil.getConnection();
        List<T> list = null;
        try {
            list = queryRunner.query(connection, sql, new BeanListHandler<T>(
                    type), params);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(connection,null,null);
        }
        return list;
    }

    /**
     * 获取单个数值的通用方法
     */
    public Object getSingleValue(String sql, Object... params) {
        // 获取连接
        Connection connection = JDBCUtil.getConnection();
        Object o = null;
        try {
            o = queryRunner.query(connection, sql, new ScalarHandler(), params);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(connection,null,null);
        }
        return o;
    }
}

  1. 创建UserDao接口
package com.dyy.dao;

import com.dyy.pojo.User;

import java.sql.SQLException;
import java.util.List;

public interface UserDao {
    public List<User> getAllUser() throws SQLException;
    /**
     * 通过用户名查询用户信息
     * @param username
     * @return
     * @throws SQLException
     */
    public User selectUserByUserName(String username) throws SQLException;

    /**
     * 添加user信息
     */
    public void insertUser(User user) throws SQLException;

}
  1. 用UserDaoImpl类实现UserDao【它同时继承BaseDao】
package com.dyy.dao.impl;

import com.dyy.dao.BaseDao;
import com.dyy.dao.UserDao;
import com.dyy.pojo.User;

import java.sql.SQLException;
import java.util.List;

public class UserDaoImpl extends BaseDao<User> implements UserDao {
    @Override
    public List<User> getAllUser() throws SQLException {
        String sql = "select * from users";
        return getBeanList(sql);
    }

    @Override
    public User selectUserByUserName(String username) throws SQLException {
        String sql = "select * from users where username=?";
        return getBean(sql,username);
    }

    @Override
    public void insertUser(User user) throws SQLException {
        String sql = "insert into users(username,`password`,email) values(?,?,?)";
        update(sql,user.getUsername(),user.getPassword(),user.getEmail());
    }
}

5.使用MD5对用户密码进行加密处理。
  1. 编写一个MD5工具类MD5Util
package com.dyy.util;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Util {
    /**
     * 针对明文字符串执行MD5加密
     * @param source
     * @return
     */
    public static String encode(String source) {
        /*1. 首先判断铭文字符串是否有效*/
        if(source == null || "".equals(source)){
            throw new RuntimeException("要进行加密的明文不能是空的");
        }
        /*2. 声明算法名称*/
        String algorithm = "MD5";
        /*3. 获取MessageDigest对象*/
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        /*4. 获取明文字符串对应的字节数组*/
        byte[] input = source.getBytes();
        /*5. 执行加密*/
        byte[] output = messageDigest.digest(input);
        /*6. 创建BigInteger对象*/
        int signum = 1;
        BigInteger bigInteger = new BigInteger(signum, output);
        /*7. 按照16进制将bigInteger的值转换为字符串*/
        int radix = 16;
        String encoded = bigInteger.toString(radix).toUpperCase();

        return encoded;
    }
}

  1. 创建UserService接口
package com.dyy.service;

import com.dyy.pojo.User;

public interface UserService {
    void doRegister(User userForm);
}
  1. 创建UserServiceImpl实现类
package com.dyy.service.impl;

import com.dyy.dao.UserDao;
import com.dyy.dao.impl.UserDaoImpl;
import com.dyy.pojo.User;
import com.dyy.service.UserService;
import com.dyy.util.MD5Util;

import java.sql.SQLException;

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();
    @Override
    public void doRegister(User userForm) throws SQLException {
        /*在注册之前,要校验用户名是否存在*/
        User existuser = userDao.selectUserByUserName(userForm.getUsername());
        if(existuser!=null){
            throw new RuntimeException("该用户名已经存在,注册失败");
        }
        /*注册的时候,对用户的密码进行加密*/
        String oldPassword = userForm.getPassword();
        /*调用MD5Util类加密密码*/
        String encodePassword = MD5Util.encode(oldPassword);
        /*将加密后的密码重新赋给userForm对象中*/
        userForm.setPassword(encodePassword);
        /*调用dao将数据加入数据库,注册成功*/
        userDao.insertUser(userForm);

    }
}

  1. 写一个BeanUtil工具类,它包含一个populate方法来将传入的数据封装在一个对象中。

    package com.dyy.util;
    import com.dyy.pojo.User;
    import java.util.Map;
    
    public class BeanUtil {
        /**
         * 这个类是用来处理页面传进来的数据集,将它传进User对象中
         * @param user
         * @param map
         */
        public static void populate(User user, Map<String,String[]> map){
            user.setUsername(map.get("username")[0]);
            user.setPassword(map.get("password")[0]);
            user.setEmail(map.get("email")[0]);
        }
    }
    
  2. 修改阶段1写的RegisterServlet处理注册

package com.dyy.servlet;

import com.dyy.pojo.User;
import com.dyy.service.UserService;
import com.dyy.service.impl.UserServiceImpl;
import com.dyy.util.BeanUtil;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Map;
import java.util.Set;

/*静态校验注册*/
@WebServlet(name = "RegisterServlet",urlPatterns = "/check_register")
public class RegisterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        Map<String, String[]> parameterMap = request.getParameterMap();
        User user = new User();
        /*使用工具类处理数据,将数据放进user对象中*/
        BeanUtil.populate(user,parameterMap);
        /*使用Userservice中的doRegister来执行check用户名,插入数据库的操作*/
        UserService userService = new UserServiceImpl();
        try {
            userService.doRegister(user);
            /*如果没有发生异常,就跳转注册成功页面*/
            response.sendRedirect(request.getContextPath()+"pages/user/regist_success.html");
        } catch (SQLException e) {
            e.printStackTrace();
            /*有异常就显示注册失败*/
            response.getWriter().write("很抱歉,注册失败,请重试");
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

6.登录功能
  1. UserService接口添加doLogin()抽象方法

    package com.dyy.service;
    
    import com.dyy.pojo.User;
    
    import java.sql.SQLException;
    
    public interface UserService {
        /**
         * 该方法用于校验注册,检测用户名是否重复,如果不重复就把userForm中的密码加密,然后将数据插入用户表
         * @param userForm
         * @throws SQLException
         */
        void doRegister(User userForm) throws SQLException;
    
        /**
         * 该方法用于校验登录,检查账号密码是否正确
         * @param user
         * @return
         * @throws SQLException
         */
        User doLogin(User user) throws SQLException;
    }
    
  2. UserServiceImpl类实现doLogin()方法

    package com.dyy.service.impl;
    
    import com.dyy.dao.UserDao;
    import com.dyy.dao.impl.UserDaoImpl;
    import com.dyy.pojo.User;
    import com.dyy.service.UserService;
    import com.dyy.util.MD5Util;
    
    import java.sql.SQLException;
    
    public class UserServiceImpl implements UserService {
        private UserDao userDao = new UserDaoImpl();
    
        @Override
        public void doRegister(User userForm) throws SQLException {
            /*在注册之前,要校验用户名是否存在*/
            User existUser = userDao.selectUserByUserName(userForm.getUsername());
            if (existUser != null) {
                throw new RuntimeException("该用户名已经存在,注册失败");
            }
            /*注册的时候,对用户的密码进行加密*/
            String oldPassword = userForm.getPassword();
            /*调用MD5Util类加密密码*/
            String encodePassword = MD5Util.encode(oldPassword);
            /*将加密后的密码重新赋给userForm对象中*/
            userForm.setPassword(encodePassword);
            /*调用dao将数据加入数据库,注册成功*/
            userDao.insertUser(userForm);
    
        }
    
        /**
         * 原理是将传入的账号在数据库中比对,如果没有就直接返回失败【异常】,
         * 如果有,就把传入的密码加密后,和该行数据进行比对,如果密码错误也返回失败,
         * 如果账号密码都正确,那么返回该用户所有信息。
         *
         * @param user
         * @return 返回账号信息
         * @throws SQLException
         */
        @Override
        public User doLogin(User user) throws SQLException {
            User realUser = userDao.selectUserByUserName(user.getUsername());
            if (realUser == null) {
                throw new RuntimeException("账号错误");
            }
            String encodePassword = MD5Util.encode(user.getPassword());
            if(realUser.getPassword().equals(encodePassword)){
                return realUser;
            }else{
                throw new RuntimeException("密码错误");
            }
        }
    }
    
  3. 修改LoginServlet中的doPost()方法

    package com.dyy.servlet;
    
    import com.dyy.pojo.User;
    import com.dyy.service.UserService;
    import com.dyy.service.impl.UserServiceImpl;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.sql.SQLException;
    
    /*静态校验登录*/
    @WebServlet(name = "LoginServlet",urlPatterns = "/check_login")
    public class LoginServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            request.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=UTF-8");
    
            String username = request.getParameter("username");
            String password = request.getParameter("password");
    
            UserService userService = new UserServiceImpl();
            User user = new User();
            user.setUsername(username);
            user.setPassword(password);
    
            try {
                userService.doLogin(user);
                response.sendRedirect(request.getContextPath()+"/pages/user/login_success.html");
            } catch (SQLException e) {
                e.printStackTrace();
                response.getWriter().write("账号或密码错误哦,请再试一下~");
            }
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doPost(request,response);
        }
    }
    

    注:

    ​ LoginServlet和RegisterServlet的urlPatterns修改过,所以表单的action也要跟着改变。

Bug记录

问题1:sevlet收不到表单信息。

描述:在使用request的getParameter(String str)方法时始终获取不到数据,发现这个方法其实是将str作为name名来查找的,中间尝试使用 request.getParameterMap(),来获取表单提交的数据,也是空的。

解决方式:这是由于如果控件没有指定name值的话,是传不进来的【等于说键-值对少了键】。

问题2:数据库编码错误。

描述:使用SHOW VARIABLES LIKE 'character%';来查看数据库编码信息。发现数据库编码都是utf-8,只有一个表不支持中文,说明该表是在数据库更改字符集之前创建的,与数据库默认同一个编码集latin。

解决方式:比较方便的就是drop这个表重新创建一个同样结构的表。如果数据库的编码不正确,先修改数据库编码,然后再重新建表。

问题3:读取不到properties文件。

描述:在读取文件时使用了InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("druid.properties");,同时我的properties文件和该工具类在同一个包util下,在使用测试类测试时,发现产生了空指针异常。

解决方式:将druid.properties移植src目录下后,能够正常使用。比较正规的写法是创建resources目录,标记为资源根目录resources root,然后将properties文件移入resources目录。如图所示:

最终形式: 目录结构.png

问题4:发生javax.servlet.ServletException: Servlet异常,显示根本原因是java.lang.NoClassDefFoundError: org/apache/commons/dbutils/ResultSetHandler

描述:看上去应该是缺少commons-dbutils相关的包,但是看了下目录,已经包含了两个dbutils包了。

解决:尝试重新导入jar,去掉了其中一个版本的dbutils包。成功解决。

未完待续