文章目錄
- 專案需求分析
- 概要設計
- 三層架構搭建專案結構
- 三層架構簡介
- 后端注冊功能實作
- eclipse配置
- 創建web專案
- 用戶表設計與創建資料庫和表
- 創建用戶物體類
- 控制層(controller)實作
- 業務邏輯層(service)實作
- 資料訪問層(dao)實作
- 使用jdbc工具類和匯入mysql驅動包
- 前端注冊功能實作
- Bootstrap框架下載與使用
- 前端頁面結構
- 前端頁面表單驗證
- 注冊功能綜合測驗
專案需求分析
(1)用戶注冊與登錄模塊:未注冊用戶可以通過注冊用戶資訊,登錄到資源交流平臺,達到相互交流學習的目的,已有賬號密碼的學生或老師可以直接登錄課程資源庫系統,進行資源瀏覽,
- 學生用戶成功登陸Web資源庫后可以上傳學生資源,收藏和下載系統內所有資源,發布學生帖子和評論所有帖子,關注其他用戶,
- 教師用戶成功登陸Web資源庫后可以上傳教師資源,收藏和下載系統內所有資源,發布教師帖子和評論所有帖子,關注其他用戶,
- 管理員用戶具有用戶資訊管理、學生資訊批量匯入、資源管理、交流區管理的權限,
(2)資源瀏覽與下載模塊:用戶可以直接在web資源的首頁查看到所有發布的檔案和圖片,對于共享的資源檔案可以直接下載保存,
(3)資源上傳模塊:用戶可以直接上傳資源檔案,上傳檔案時需要錄入資源的名字,類別,等級和描述,
(4)學習推薦模塊:隨著Web資源庫的內容增加,用戶需要從大量資源檔案中獲取自己想要的資源,學習推薦模塊,是對用戶實施的一個推薦功能,用戶在推薦的目錄檔案中也可以選擇關注自己感興趣的用戶,一起參與討論,
(5)用戶交流區模塊:在論壇中,學生用戶可以直接發貼提問,回復和評論所有帖子,教師用戶可以發帖回答問題,回復和評論所有帖子,幫助學生解決學習上的疑惑,
(6)后臺管理模塊:管理員進行Web資源庫的后臺管理,管理員可以在此模塊管理用戶賬號,進行增刪查改的操作,并且批量匯入學生資訊,同時也要講系統內的資源構件分門別類,洗掉出錯的構件,將優質資源標“優”,將優質的資源摘錄到Web資源庫首頁并且置頂,將交流區的優質回復摘錄到Web資源庫首頁置頂的用戶評論區,對交流區內違規操作的用戶進行禁言處理,
概要設計
經過需求分析后,按照系統實作的功能進行模塊劃分,將相關功能的實作劃分到同一模塊中,
(1)按照管理系統的要求將模塊劃分為六大部分,用戶注冊與登錄模塊,資源瀏覽與下載模塊,資源上傳模塊和推薦學習模塊,用戶交流區模塊,后臺管理模塊,

(2)將六個模塊細化
| 用戶注冊與登錄-注冊 | 注冊成為資源庫的一個用戶,可以共享web資源內容 |
| 用戶注冊與登錄-登錄 | 學生用戶、教師用戶和管理員用戶登錄賬號,分別擁有不同的權限 |
| 后臺管理-用戶管理 | 對用戶資訊進行管理,批量匯入學生,增刪查改 |
| 后臺管理-資源管理 | 對資源資訊進行管理,洗掉出錯的構件,將優質資源標“優” |
| 后臺管理-論壇管理 | 對發布的帖子進行管理,對交流區內違規操作的用戶進行禁言處理 |
| 資源瀏覽與下載-資源中心 | 包含所有用戶已經上傳的資源資訊,可以讓用戶瀏覽 |
| 資源瀏覽與下載-資源下載 | 將感興趣的內容進行資源下載保存到本地 |
| 資源上傳-上傳 | 教師可以上傳教師資源檔案,學生可以上傳個人檔案 |
| 推薦學習-資源置頂推薦 | 系統將推薦的資源置頂,讓用戶方便搜索到想要的內容 |
| 用戶交流區-論壇帖子 | 匯總了所有學生和教師的帖子,方便大家在論壇內展開討論 |
| 用戶交流區-我要發帖 | 學生和教師可以發布個人的帖子,評論他人的帖子,關注其他用戶 |
三層架構搭建專案結構
三層架構簡介
首先來說,三層架構與MVC的目標一致:都是為了解耦和、提高代碼復用,MVC是一種設計模式,而三層架構是一種軟體架構,
三層架構分為:控制層(controller層即servlet)、業務邏輯層(service層)、資料訪問層(dao層) ,再加上物體類(Model)和表現層(web層即jsp)
物體類(Model):
在Java中,往往將其稱為Entity物體類,資料庫中用于存放資料,而我們通常選擇會用一個專門的類來抽象出資料表的結構,類的屬性就一對一的對應這表的屬性,
Model物體類需要被dao層,service層和web層參考,
資料訪問層(dao):
主要是存放對資料類的訪問,即對資料庫的添加、洗掉、修改、更新等基本操作
dao就是根據業務需求,構造SQL陳述句,構造引數,呼叫幫助類,獲取結果,dao層被service層呼叫
業務邏輯層(service)
service層好比是橋梁,將web表示層與dao資料訪問層之間聯系起來,所要負責的,就是處理涉及業務邏輯相關的問題,比如在呼叫訪問資料庫之前,先處理資料、判斷資料,
后端注冊功能實作
eclipse配置
雙擊eclipse,選擇自己的作業空間

設定作業空間的字符編碼


設定JSP的字符編碼

配置tomcat服務器


創建web專案
按Ctrl+N輸入dynamic web project



用戶表設計與創建資料庫和表
用戶表設計
學生,教師都有個人資訊(姓名,性別,年齡,生日),但教師不包含班級欄位,但它們都有賬號資訊以及身份標識,管理員只有賬號資訊,身份標識,
注冊時一般只需提供賬號,密碼,身份標識,如果是學生還需要提供班級,而個人資訊是通過登錄后通過個人資訊修改的,
為了簡單起見,可以把學生,教師,管理員放在一張表中,共有的欄位設定為不可為空(賬號,密碼,身份標識),不共有的設定可以為空,表如下
user表
| 欄位名稱 | 型別 | 約束 | 描述 |
|---|---|---|---|
| user_id | varchar(20) | NOT NULL PRIMARY KEY | 用戶ID |
| user_password | varchar(20) | NOT NULL | 用戶密碼 |
| user_name | varchar(20) | NULL | 用戶姓名(默認為空) |
| user_sex | varchar(10) | NULL | 用戶性別(默認為空) |
| user_birthday | date() | NULL | 用戶生日(默認為空) |
| user_class | varchar(20) | NULL | 班級 |
| user_level | varchar(20) | NOT NULL | 用戶標識 |
使用mysql命令列創建資料庫和表
CREATE DATABASE web_resource;
CREATE TABLE `user` (
`user_id` varchar(20) NOT NULL,
`user_password` varchar(20) NOT NULL,
`user_name` varchar(20) NULL,
`user_sex` varchar(10) DEFAULT NULL,
`user_birthday` date DEFAULT NULL,
`user_class` varchar(20) DEFAULT NULL,
`user_level` varchar(20) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
創建用戶物體類
滑鼠左鍵java檔案夾按Ctrl+N輸入class

輸入存放物體類的包名和類名后finish

user.java代碼如下
public class User {
private String userId;
private String userPassword;
private String userName;
private String userSex;
private String userBirthday;
private String userClass;
private String userLevel;
public User() {
super();
}
public User(String userId, String userPassword, String userName, String userSex, String userBirthday,
String userClass, String userLevel) {
super();
this.userId = userId;
this.userPassword = userPassword;
this.userName = userName;
this.userSex = userSex;
this.userBirthday = userBirthday;
this.userClass = userClass;
this.userLevel = userLevel;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(String userBirthday) {
this.userBirthday = userBirthday;
}
public String getUserClass() {
return userClass;
}
public void setUserClass(String userClass) {
this.userClass = userClass;
}
public String getUserLevel() {
return userLevel;
}
public void setUserLevel(String userLevel) {
this.userLevel = userLevel;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userPassword=" + userPassword + ", userName=" + userName + ", userSex="
+ userSex + ", userBirthday=" + userBirthday + ", userClass=" + userClass + ", userLevel=" + userLevel
+ "]";
}
}
控制層(controller)實作
滑鼠左鍵java檔案夾按Ctrl+N輸入servlet
輸入存放servlet的包名(com.zhuo.controller),由于注冊是和用戶相關的,還可以建一個子目錄user,這樣可以和后面各種型別的servlet進行區分,然后輸入類名后next

修改URL映射,這樣瀏覽器可以通過地址欄訪問注冊的servlet

RegisterServlet.java代碼如下
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/* 設定字符編碼,解決中文亂碼問題 */
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
/* 接收客戶端資訊 */
String userId = request.getParameter("userId");
String userPassword = request.getParameter("userPassword");
String userName = request.getParameter("userName");
String userLevel = request.getParameter("userLevel");
String userClass = request.getParameter("userClass");
/* 創建用戶物體,并把請求引數設定為用戶物體成員變數值 */
User user = new User();
user.setUserId(userId);
user.setUserPassword(userPassword);
user.setUserName(userName);
user.setUserLevel(userLevel);
user.setUserClass(userClass);
// 創建用戶service層介面的實作類物件
UserServiceImpl userServiceImpl = new UserServiceImpl();
/* 呼叫業務邏輯方法注冊用戶 */
int i = 0;
try {
i = userServiceImpl.register(user);
} catch (SQLException e) {
e.printStackTrace();
}
/* 若i>0,則重定向到login.jsp */
if (i > 0) {
response.sendRedirect("views/before/login.jsp");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
RegisterServlet的主要作用就是接受客戶提交的表單(通過注冊頁面提交),然后把引數封裝到物體類中,之后就可以以物體類作用引數呼叫service層業務邏輯處理方法,通過回傳值判斷是否注冊成功
業務邏輯層(service)實作
service層分為介面和介面的實作類,為了要定義介面呢?直接用類不行嗎?
我們知道介面是空實作,具體實作由子類繼承,那么這樣的好處就是當我們擴展業務功能時,只需在介面添加即可,具體的實作交給子類實作,由于介面只包含方法簽名,而不包含復雜的邏輯代碼,很容易就可以看出有什么業務功能
定義介面
滑鼠左鍵java檔案夾按Ctrl+N輸入interface
輸入存放service層的介面包名和介面名后finish

在UserService.java中添加一個注冊的抽象方法
// 注冊用戶
public int register(User user) throws SQLException;
定義介面的實作類
滑鼠左鍵java檔案夾按Ctrl+N輸入class
輸入存放service層的介面實作類的包名和類名并繼承UserService介面后finish

在UserServiceImpl實作類中重寫介面的register方法
/*
* 創建用戶dao層介面的實作類,并賦給介面物件變數,
* 實作上轉型,也就面向介面編程,而不關心它的 實作類是誰
*/
UserDao userDao = new UserDaoImpl();
@Override
public int register(User user) throws SQLException {
//把用戶插入到資料庫中
int i = userDao.insertUser(user);
return i;
}
實作類中重寫的register方法,主要功能是封裝了底層操作(即對資料庫的增刪改查),只提現業務功能(即注冊)
資料訪問層(dao)實作
dao層和service層類似,就不再重復闡述了
定義介面
滑鼠左鍵java檔案夾按Ctrl+N輸入interface
輸入存放dao層介面的包名(com.zhuo.dao)和類名后finish
在UserDao.java中添加一個插入用戶到資料庫抽象方法
// 把用戶插入到資料庫中
public int insertUser(User user) throws SQLException;
定義介面的實作類
輸入存放dao層的介面實作類的包名和類名并繼承UserDao介面后finish

在UserDaoImpl實作類中重寫介面的insertUser方法
//存放查詢資料庫回傳的結果集
ResultSet resultSet;
@Override
public int insertUser(User user) throws SQLException {
//sql插入陳述句
String sql = "insert into user (user_id, user_password, user_name, "
+ "user_sex, user_birthday, user_class, user_level) "
+ "values(?,?,?,?,?,?,?);";
// 呼叫jdbc工具類執行sql陳述句,如果操作成功,i就是操作成功的條數(即i > 0)
int i = JDBCUtil.executeUpdate(sql, user.getUserId(),
user.getUserPassword(), user.getUserName(),
user.getUserSex(), user.getUserBirthday(),
user.getUserClass(), user.getUserLevel());
return i;
}
實作類UserDaoImpl重寫了介面的insertUser方法,主要功能是呼叫jdbc工具執行sql插入陳述句添加用戶
使用jdbc工具類和匯入mysql驅動包
創建jdbc工具類
滑鼠左鍵java檔案夾按Ctrl+N輸入class
輸入存放工具類的包名和類名后finish

JDBCUtil.java代碼如下
public class JDBCUtil {
private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://127.0.0.1:3306/web_resource?useUnicode=true&characterEncoding=utf-8";
private static final String USER = "root";
private static final String PASSWORD = "12345";
private static Connection ct;
private static PreparedStatement ps;
private static ResultSet rs;
static {
// 1.加載驅動,只需要加載一次,所以放到靜態代碼塊中
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 描述:封裝一個方法可以獲得連接,目的可以在其他地方之接呼叫
*/
public static Connection getConnection() {
try {
ct = DriverManager.getConnection(URL, USER, PASSWORD);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ct;
}
/**
* 描述:封裝一個方法可以完成查詢操作
*
* @param sql 要查詢的sql陳述句
* @param obj 占位符的具體內容
* @return ResultSet 將查詢到的結果回傳
*/
public static ResultSet executeQuery(String sql, Object... obj) {
// 1.得到連接
ct = getConnection();
// 2.創鍵發送物件
try {
ps = ct.prepareStatement(sql);
// 處理占位符問題
if (obj != null) {
for (int i = 0; i < obj.length; i++) {
ps.setObject(i + 1, obj[i]);
}
}
rs = ps.executeQuery();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return rs;
}
/**
* 描述:封裝一個方法可以完成DDL,DML操作
*
* @param sql 要操作的sql陳述句
* @param obj 占位符
* @return
*/
public static int executeUpdate(String sql, Object... obj) {
// 1.得到連接
ct = getConnection();
// 2.創鍵發送物件
try {
ps = ct.prepareStatement(sql);
// 處理占位符問題
if (obj != null) {
for (int i = 0; i < obj.length; i++) {
ps.setObject(i + 1, obj[i]);
}
}
int in = ps.executeUpdate();
close(ct, ps, null);
return in;
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
/**
* 描述:封裝一個關閉資源的方法
*
* @param ct 連接物件
* @param ps 發送sql陳述句物件
* @param rs 回傳值物件
*/
public static void close(Connection ct, PreparedStatement ps, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ct != null) {
try {
ct.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 給外部一個訪問ct,和ps的方法
public static Connection getCt() {
return ct;
}
public static PreparedStatement getPs() {
return ps;
}
}
JDBCUtil工具類只需把常量PASSWORD修改為自己的mysql密碼即可
匯入mysql驅動包
要想連接資料庫,需要加載mysql驅動包
jar包下載地址:https://repo1.maven.org/maven2/mysql/mysql-connector-java/
選擇對應自己的mysql版本,下載好之后復制到WEB-INF的lib檔案夾即可

至此后臺主要的功能都實作了,等到撰寫前臺代碼時會加些前臺表單驗證的servlet(驗證碼,檢查用戶是否存在),
到目前為止的專案結構如下

前端注冊功能實作
前端頁面包含結構(html,jsp),樣式(css即表現),行為(javascript),
Bootstrap框架下載與使用
我們主要的部分是結構(表單)和行為(驗證表單),而樣式不是重點,可以使用一些開源的css框架或者使用自己喜歡的樣式,這些在網上都可以找到,我使用的Bootstrap框架
Bootstrap框架下載
Bootstrap有壓縮版的,也有沒壓縮版的,如果有興趣可以下載沒壓縮版的閱讀原始碼
Bootstrap下載
Bootstrap框架使用
前端代碼一般是放在webapp目錄下,可以在webapp目錄下創建一個views檔案夾,存放前端代碼
左擊webapp按Ctrl+N輸入folder后next

輸入檔案夾名views后finish

前端分為前臺和后臺,還可以在views檔案下繼續細分為前臺(before檔案夾),后臺(after檔案夾),這樣可以保持專案結構簡潔清晰
由于bootstrap框架,前臺,后臺都會用的到,所以可以在views檔案夾創建一個css子檔案夾存放bootstrap,然后在html檔案中的head標簽內添加參考即可,我的參考如下
<link
href="${pageContext.request.contextPath}/views/css/bootstrap.min.css"
rel="stylesheet">
在EL運算式通過page域獲取請求域,然后通過請求域獲取專案名即contextPath,這樣就可以參考到這個檔案
前端頁面結構
注冊頁面主要的結構就是表單,里面包含一些組件(文本框,密碼框,選擇框,提交按鈕)
左擊before檔案夾按Ctrl輸入jsp,創建register.jsp,添加如下代碼
<body class="gray-bg">
<div class="middle-box text-center loginscreen animated fadeInDown">
<div>
<h3>注冊Resource library+</h3>
<form class="m-t" role="form"
action="${pageContext.request.contextPath}/register"
onsubmit="return checkRegisterForm(this);" method="post">
<div class="form-group">
<p>
<input type="text" class="form-control" name="userId"
onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)"
placeholder="賬號" required><span></span>
</p>
</div>
<div class="form-group">
<p>
<input type="password" class="form-control"
name="userPassword" onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)" placeholder="密碼"
required><span></span>
</p>
</div>
<div class="form-group">
<p>
<input type="password" class="form-control"
onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)"
name="userPasswordConfirm" placeholder="確認密碼"
required> <span></span>
</p>
</div>
<div class="form-group">
<p>
<input type="text" class="form-control" name="userName"
onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)"
placeholder="姓名" required><span></span>
</p>
</div>
<div class="form-group">
<select class="form-control" id="user_level" name="userLevel">
<option value="學生">學生</option>
<option value="教師">教師</option>
</select>
</div>
<br>
<div class="form-group" id="student_class_form">
<p>
<input type="text" class="form-control" id="student_class"
name="userClass" onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)"
placeholder="班級" required>
<span></span>
</p>
</div>
<div class="form-group">
<p>
<input class="form-control" type="text" name="veryCode"
value="" onfocus="focusItem(this)"
onblur="checkBlurRegisterItem(this)" placeholder="驗證碼" />
<img height="25" src="${pageContext.request.contextPath}/getcode"
alt="看不清,換一張" onclick="change(this)"><span></span>
</p>
</div>
<button type="submit" class="btn btn-primary block full-width m-b">
注冊
</button>
<p class="text-muted text-center">
<small>已經有一個賬戶?</small>
</p>
<a class="btn btn-sm btn-white btn-block" href="login.jsp">登錄</a>
</form>
</div>
</div>
</body>
html代碼結構就不詳細說明了,不懂得可以查檔案,這里主要講一下重要的內容
我們給表單,組件,圖片系結的事件處理onfocus(獲取焦點事觸發),onblur(失去焦點時觸發),onsubmit(提交表單時觸發),onclick(點擊時觸發)
后面在js會給出處理函式,就不詳細說明了
在驗證碼的文本框input標簽后添加一個img標簽來顯示驗證碼圖片,src屬性指定的是生成驗證的servlet,下面來說驗證碼的實作
驗證碼的實作
驗證碼是通過在img標簽中的src屬性指定為servlet的url,這樣servlet就會把驗證碼通過輸出流回應給客戶端,下面給出生成驗證碼的servlet
創建servlet的方式在前面我已經講過了,這里就不再重復了
在controller層的user包下創建CodeServlet.java,添加如下代碼
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 呼叫工具類生成的驗證碼和驗證碼圖片
Map<String, Object> codeMap = CodeUtil.generateCodeAndPic();
/* 將四位數字的驗證碼保存到Session中
* 這樣當客戶提交時,會從session中獲取生成的驗證碼并與用戶
* 輸入的驗證碼進行匹配 */
HttpSession session = req.getSession();
session.setAttribute("code", codeMap.get("code").toString());
// 禁止影像快取,
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Cache-Control", "no-cache");
resp.setDateHeader("Expires", -1);
resp.setContentType("image/jpeg");
// 將影像輸出到Servlet輸出流中,
ServletOutputStream sos;
try {
sos = resp.getOutputStream();
ImageIO.write((RenderedImage) codeMap.get("codePic"), "jpeg", sos);
sos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
通過上面代碼我們知道,真正生成驗證碼的是工具類CodeUtil,而servlet主要功能呼叫工具類的方法生成驗證碼并放在session中,起到封裝的作用,然后通過輸出流回應給客戶端
在utils包下創建工具類CodeUtil,代碼如下
public class CodeUtil {
private static int width = 100;// 定義圖片的width
private static int height = 20;// 定義圖片的height
private static int codeCount = 4;// 定義圖片上顯示驗證碼的個數
private static int xx = 15;
private static int fontHeight = 18;
private static int codeY = 16;
private static char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
/**
* 生成一個map集合
* code為生成的驗證碼
* codePic為生成的驗證碼BufferedImage物件
* @return
*/
public static Map<String,Object> generateCodeAndPic() {
// 定義影像buffer
BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Graphics2D gd = buffImg.createGraphics();
// Graphics2D gd = (Graphics2D) buffImg.getGraphics();
Graphics gd = buffImg.getGraphics();
// 創建一個亂數生成器類
Random random = new Random();
// 將影像填充為白色
gd.setColor(Color.WHITE);
gd.fillRect(0, 0, width, height);
// 創建字體,字體的大小應該根據圖片的高度來定,
Font font = new Font("Fixedsys", Font.BOLD, fontHeight);
// 設定字體,
gd.setFont(font);
// 畫邊框,
gd.setColor(Color.BLACK);
gd.drawRect(0, 0, width - 1, height - 1);
// 隨機產生40條干擾線,使圖象中的認證碼不易被其它程式探測到,
gd.setColor(Color.BLACK);
for (int i = 0; i < 30; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
gd.drawLine(x, y, x + xl, y + yl);
}
// randomCode用于保存隨機產生的驗證碼,以便用戶登錄后進行驗證,
StringBuffer randomCode = new StringBuffer();
int red = 0, green = 0, blue = 0;
// 隨機產生codeCount數字的驗證碼,
for (int i = 0; i < codeCount; i++) {
// 得到隨機產生的驗證碼數字,
String code = String.valueOf(codeSequence[random.nextInt(36)]);
// 產生隨機的顏色分量來構造顏色值,這樣輸出的每位數字的顏色值都將不同,
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);
// 用隨機產生的顏色將驗證碼繪制到影像中,
gd.setColor(new Color(red, green, blue));
gd.drawString(code, (i + 1) * xx, codeY);
// 將產生的四個隨機陣列合在一起,
randomCode.append(code);
}
Map<String,Object> map =new HashMap<String,Object>();
//存放驗證碼
map.put("code", randomCode);
//存放生成的驗證碼BufferedImage物件
map.put("codePic", buffImg);
return map;
}
}
完成到這里我們就可以測驗驗證碼的功能了,啟動專案,在瀏覽器地址欄輸入http://localhost:8080/web_resource/views/before/register.jsp
重繪頁面,也就是在請求一次servlet,這樣驗證碼就跟著改變,后面會在js實作點擊圖片更改驗證碼
效果如下

前端頁面表單驗證
瀏覽器對所有get請求都有快取功能,而請求js是get請求,所以可以把全部的js代碼單獨放在一個js檔案中,這樣瀏覽器只需請求一次js,效率就提高了
在views檔案夾下創建js子檔案夾存放js代碼
下面給出的所有js代碼都放在一個js檔案中,我的是function.js,在頁面結構的body標簽之前參考即可,我的參考如下
<script src="${pageContext.request.contextPath}/views/js/function.js"></script>
</body>
使用js點擊圖片改變驗證碼
可以通過js實作點擊圖片改變驗證碼,這很簡單,在img標簽中想更改圖片,其實就改變src的屬性值,
但是我們請求的是一個servlet,所以url不能改,能改的只有查詢串(即請求引數),
請求引數要各不相同,使用Date類獲取本地當前系統時間就可以實作,js代碼如下
// 改變驗證碼圖片
function change(img) {
img.src = getWebRootPath() + "/getcode?" + new Date().getTime();
}
這里使用的獲取web根路徑的函式,js代碼如下
// 獲取根路徑
function getWebRootPath() {
var a = window.document.location.href;//
var b = window.document.location.pathname;
var pos = a.indexOf(b);
var path = a.substring(0, pos);
a = a.substring(a.indexOf("/") + 2, a.length);
a = a.substring(a.indexOf("/") + 1, a.length);
var pathName = a.substring(0, a.indexOf("/"));
return path + "/" + pathName;
}
在注冊頁面結構中已經在img標簽中使用了onclick點擊事件函式,呼叫的正是change函式,所有可以直接測驗了
效果如下

使用jQuery動態改變班級欄位
jQuery是js庫,它可以簡化我們對DOM的操作
有兩個版本的 jQuery 可供下載:
- Production version - 用于實際的網站中,已被精簡和壓縮,
- Development version - 用于測驗和開發(未壓縮,是可讀的代碼)
這兩個版本都可以從 jQuery.com 下載,
在views檔案夾的js子檔案夾中添加jQuery,然后在head標簽中進行參考即可,我的參考如下
<script
src="${pageContext.request.contextPath}/views/js/jquery-2.1.1.js"></script>
當在選擇框選擇學生時有班級文本框,但選擇教師時是沒有班級文本框的,下面通過jQuery實作這種效果,代碼如下
/* 通過id獲取選擇框物件并系結事件change函式,引數為匿名函式
* 匿名函式當change執行時便會執行,這樣當選擇框值改變時便會執行匿名函式*/
$("#user_level").change(function() {
if ($('#user_level').val() == "學生") {
// 通過id獲取存放班級文本框容器div并呼叫可顯函式
$('#student_class_form').show();
} else {
// 呼叫隱藏函式
$('#student_class_form').hide();
// 獲取班級文本框并設定值可以為空
$('#student_class').attr("required", false);
}
});
測驗效果

使用jQuery和Ajax異步驗證表單
jQuery前面已經介紹過了,這里主要講一下Ajax技術
簡短地說,AJAX 是與服務器交換資料的,在不多載整個網頁的情況下,AJAX 通過后臺加載資料,并在網頁上進行顯示,
用戶注冊時用的賬號可能已經是注冊過,由于賬號是唯一的(即主鍵),這時如果不在客戶端先進行檢測的就提交表單到服務器就會報錯
同理,用戶在表單輸入的驗證碼也需要在客戶端先進行檢測
要想在不把表單資料發送到服務器的情況下驗證用戶名是否存在,驗證碼是否輸入正確,就需要使用Ajax
它可以請求服務器(servlet)獲取資料,然后通過后臺判斷資料并進行相應處理,而不是加載整個網頁(重繪,或者跳轉頁面)
下面驗證賬號是否存在的js核心代碼,如下
// 驗證賬號是否存在的servlet路徑
var url = getWebRootPath() + "/check_user_id?id=" + encodeURI($(obj).val()) + "&" + new Date().getTime();
/*.get() 方法通過 HTTP GET 請求從服務器上請求資料,
* 可選的匿名函式引數是請求成功后所執行的函式名,*/
$.get(url, function(data) {
// 服務器回傳false則用戶名已存在
if (data == "false") {
// 在頁面顯示提示資訊
msgBox.html('賬號不能使用!');
msgBox.addClass('error');
// 標記改為false,阻止表單提交
flagId = false;
} else {
flagId = true;
}
});
這里只是單獨給出來說明驗證賬號的原理,實際運行需要添加到事件函式中才能運行,后面會給出完整代碼
同樣的驗證用戶輸入的驗證碼是否正確也是通過Ajax請求servlet,接受服務端回應的資訊來判斷對不對,這里代碼就不再給出
在注冊頁面結構中,已經給表單組件系結了onblur(失去焦點時執行)事件,且事件處理函式都是一樣的checkBlurRegisterItem(this)
這樣就可以在這里集中驗證表單是否為空,密碼和確認密碼是否一樣,賬號是否已存在,驗證碼是否正確了,完整代碼如下
//標記位,只有有一個為false,則阻止表單提交
var flagId = false;
var flagPassword = false;
var flagPasswordConfirm = false;
var flagName = false;
var flagClass = true;
var flagCode = false;
function checkBlurRegisterItem(obj) {
// 獲取表單組件的下一個元素(即span),用來提示資訊
var msgBox = $(obj).next('span');
/* 通過表單組件的name屬性來判斷是那個組件*/
switch ($(obj).attr('name')) {
/* 檢測文本框是否為空,若不為空則驗證賬號是否存在*/
case "userId":
// 獲取賬號文本框值,判斷是否為空
if (obj.value == "") {
// 設定span元素的值
msgBox.html('賬號不能為空');
// 給span元素添加類屬性的值,后面會使用行內樣式修改顯示
msgBox.addClass('error');
// 標記改為false,阻止表單提交
flagId = false;
} else {
// 驗證賬號是否存在的servlet路徑
var url = getWebRootPath() + "/check_user_id?id=" + encodeURI($(obj).val()) + "&" + new Date().getTime();
/*.get() 方法通過 HTTP GET 請求從服務器上請求資料,
* 可選的匿名函式引數是請求成功后所執行的函式名,*/
$.get(url, function(data) {
// 服務器回傳false則用戶名已存在
if (data == "false") {
// 在頁面顯示提示資訊
msgBox.html('賬號不能使用!');
msgBox.addClass('error');
// 標記改為false,阻止表單提交
flagId = false;
} else {
flagId = true;
}
});
}
break;
/* 判斷密碼框是否為空*/
case "userPassword":
if (obj.value == "") {
msgBox.html('密碼不能為空');
msgBox.addClass('error');
flagPassword = false;
} else {
flagPassword = true;
}
break;
/* 判斷密碼和確認密碼是否相同*/
case "userPasswordConfirm":
if (obj.value == "") {
msgBox.html('確認密碼不能為空');
msgBox.addClass('error');
flagPasswordConfirm = false;
} else if ($(obj).val() != $('input[name="userPassword"]').val()) {
msgBox.html('兩次輸入的密碼不一致');
msgBox.addClass('error');
flagPasswordConfirm = false;
} else {
flagPasswordConfirm = true;
}
break;
/* 判斷姓名是否為空*/
case "userName":
if (obj.value == "") {
msgBox.html('姓名不能為空');
msgBox.addClass('error');
flagName = false;
} else {
flagName = true;
}
break;
// 判斷班級是否為空
case "userClass":
if (obj.value == "") {
msgBox.html('班級不能為空');
msgBox.addClass('error');
}
break;
/* 判斷驗證碼是否為空,若不為空則判斷是否正確*/
case "veryCode":
var numshow = $(obj).next().next();
if (obj.value == "") {
numshow.html('驗證碼不能為空');
numshow.addClass('error');
flagCode = false;
} else {
var url = getWebRootPath() + "/check_user_code?num=" + encodeURI($(obj).val()) + "&" + new Date().getTime();
$.get(url, function(data) {
if (data == "false") {
numshow.html('驗證碼輸入有誤');
numshow.addClass('error');
flagCode = false;
} else {
flagCode = true;
}
});
}
break;
}
}
驗證賬號是否存在以及驗證是否正確都是請求servlet來判斷,下面分別給出
檢測驗證碼是否正確的servlet
在controller層的user包下創建CheckUserCode.java,代碼如下
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 接收客戶端資訊(即驗證碼)
String num = request.getParameter("num");
/* 從session獲取生成的驗證碼 */
HttpSession session = request.getSession();
String sysCode = (String) session.getAttribute("code");
// 獲取輸出流
PrintWriter out = response.getWriter();
/* 判斷用戶輸入的驗證和生成的驗證碼是否相同
* 若相同則回應true,否則回應false */
if (sysCode.equals(num)) {
out.print("true");
} else {
out.print("false");
}
out.close();
}
完成到這步,就可以進行小測驗,檢查驗證碼效果,由于撰寫了servlet需要重啟專案
測驗效果

提示不太醒目,我們可以在注冊頁面中添加行內樣式來修改一下,只需在head標簽添加以下代碼即可
<style>
.form-group p .error {
display: inline-block;
border: 1px solid #ff855a;
background-color: #ffe8e0;
height: 25px;
line-height: 25px;
padding: 0px 20px;
margin-left: 20px;
}
</style>
檢測賬號是否存在的servlet
在controller層的user包中創建CheckUserId.java,代碼如下
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/* 設定字符編碼,解決中文亂碼問題 */
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
// 接收客戶端資訊(即賬號)
String id = request.getParameter("id");
//創建用戶service層介面的實作類
UserServiceImpl userServiceImpl = new UserServiceImpl();
/* 呼叫業務邏輯方法檢查用戶是否重復,若查到則回傳count>0 */
int count = 0;
try {
count = userServiceImpl.checkIdRepeated(id);
} catch (SQLException e) {
e.printStackTrace();
}
// 獲取輸出流物件
PrintWriter out = response.getWriter();
/* 向客戶發送查找成功或失敗的標記 */
if(count > 0 ){
out.print("false");
}else{
out.print("true");
}
out.close();
}
代碼的功能注釋已經詳細說明,這里我們在說明一下之前搭建的三層架構:
顯然這里檢查賬號是否存在的servlet是處于控制層,而就是說檢查賬號不是它實作的,它只是起到控制作用,比如它呼叫了service層的業務邏輯方法checkIdRepeated
下面我們只需要在service層的UserService介面中添加檢查賬號是否存在的業務功能,然后在UserServiceImpl實作類中重寫即可
在介面中添加的業務邏輯方法如下
// 檢查賬號是否存在
public int checkIdRepeated(String userId) throws SQLException;
在實作類重寫介面方法
@Override
public int checkIdRepeated(String userId) throws SQLException {
// 通過賬號在資料庫進行檢索
int i = userDao.selectById(userId);
return i;
}
dao層的實作和service層是一樣的,這里我就直接給出代碼
在dao層的UserDao介面中添加檢索賬號的抽象方法
// 通過賬號在資料庫進行檢索
public int selectById(String userId) throws SQLException;
在dao層的UserDaoImpl實作類中重寫介面方法
@Override
public int selectById(String userId) throws SQLException {
// sql檢索陳述句
String sql = "select * from user where user_id=?";
// 呼叫jdbc工具類執行sql陳述句,如果操作成功,i就是操作成功的條數(即i > 0)
resultSet = JDBCUtil.executeQuery(sql, userId);
int i = 0;
if (resultSet.next()) {
i = 1;
}
return i;
}
至此,檢查賬號是否存在的代碼已經全部撰寫完成,測驗之前需要重啟專案,同時需要在資料庫中添加一個賬號

測驗效果

在前面我們給表單組件系結了失去焦點時執行的函式checkRegisterForm(this),我們知道如果表單組件輸入為空等等,則會顯示一個提示框
但是如果用戶再次點擊表單組件時,提示資訊不會消失,所以我們還得給所有表單系結一個獲取焦點時執行的事件函式focusItem(this),然后再js撰寫處理邏輯即可,代碼如下
function focusItem(obj) {
/* 由于驗證input組件還有img元素,它的下下元素才是span
所以要單獨處理*/
if ($(obj).attr('name') == 'veryCode') {
// 把span元素的值啥為空,并把類屬性刪掉
$(obj).next().next().html('').removeClass('error');
} else {
// 把span元素的值啥為空,并把類屬性刪掉
$(obj).next('span').html('').removeClass('error');
}
}
這里就不再演示效果了,等到驗證注冊功能時會看到這個效果的
我們撰寫了表單組件的失去焦點和獲取焦點的js事件函式,但是當點擊提交時表單還需要驗證一次,這里只需給form表單組件系結一個事件onsubmit,處理函式為checkRegisterForm(this),代碼如下
function checkRegisterForm(form) {
// 獲取所有input組件,回傳一個陣列包含input標簽的陣列(即物件)
var elements = form.getElementsByTagName('input');
/* 遍歷input標簽*/
for (var i = 0; i < elements.length; i++) {
// 判斷input物件是否為null
if (elements[i] != null) {
// 檢查input是否有onblur屬性
if (elements[i].getAttribute("onblur")) {
checkBlurRegisterItem(elements[i]);
}
}
}
/*判斷所有組件的標記,若為false,則說明有的組件為慷訓者輸入有誤
* 直接 return false 阻止表單提交*/
if (flagId && flagPassword && flagPasswordConfirm &&
flagName && flagClass && flagCode) {
return true;
} else {
return false;
}
}
通過獲取所有input標簽并回傳一個包含input物件的陣列,然后遍歷判斷input標簽是否包含onblur屬性,若有則呼叫失去焦點時執行的函式checkBlurRegisterItem
注冊功能綜合測驗
ok,大功告成,到現在注冊功能所有代碼均撰寫完成,現在就可以來測驗注冊功能了
測驗效果


教師注冊效果就不再演示了,下一篇會更新在此三層架構完善登錄功能
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/302638.html
標籤:其他
