经过一段时间的学习,实现了第一个JSP程序、简单的Servlet程序以及JSP+Javabean的简单Web计算器。
这里作为一个综合性的练习,利用经典的MVC设计理念,实现一个用户注册模块的设计。
在这里,JavaBean由于具有良好的重用性和扩展性,作为数据的逻辑处理充当模型层(Model);JSP和HTML专门负责与用户交互的视图,不包含任何的业务逻辑,充当(View)层;最后后端的请求控制等事务交给Servlet的控制层(Controller)来做。
我们注册界面预览如下:
注册失败,用户名已存在:
我们在注册页面提交注册信息,然后发送到后台数据库进行比对,如果该用户名不存在同时两次输入的密码相同,注册成功,将新的用户信息存储在数据库中,否则返回注册失败信息。
程序及源码会附在文末。
接下来我们看具体的实现,首先准备工作:
可以参考一下JDBC常用类和接口及基本操作中JDBC基本操作示例中的MySQL用户账户、数据库、数据表的创建。
我们这里用已经建好的easy用户登录,然后新建一个名为user的数据库。
mysql -ueasy -p123456;
create database user;
然后在该数据库中创建一张数据表用来存放我们的用户信息:
use user;
create table tb_user (
username varchar(20) primary key,
password varchar(20));
这里只存放了用户名和密码,其中username为主键,必须唯一。
进入Tomcat安装目录的webapps目录下,新建一个文件夹reg,这个reg文件夹就作为这个应用模块的根目录。进入该文件夹。新建一个reg.jsp:
<%@ page contentType="text/html;charset=gb2312" language="java" %>
<html>
<head>
<title>Reg</title>
</head>
<body>
<center><img src="images/login.jpg"/></center>
<center><font size=5 color="blue"><B>用户注册页面</B></font></center>
<form action="RegServlet" method="post">
<table align="center" width="450" border="0">
<!--用户名行-->
<tr>
<td align="right">Username:</td>
<td>
<input type="text" name="username">
</td>
</tr>
<!--密码行-->
<tr>
<td align="right">Password:</td>
<td>
<input type="password" name="password">
</td>
</tr>
<!--确认密码行-->
<tr>
<td align="right">Confirm Password:</td>
<td>
<input type="password" name="repassword">
</td>
</tr>
<!--提交 重置行-->
<tr>
<td colspan="2" align="center">
<input type="submit" value="Reg">
<input type="reset" value="Reset">
</td>
</tr>
</table>
</form>
</body>
</html>
这是一个简单的jsp程序,里面只有一个图表。
9行,指定将该表单交给RegServlet来处理。
11~17行,在图表第一行建立一个用户名行。
18~24行,在图表第二行建立一个设置密码行。
25~31行,在图表第三行建立一个重复确认密码行。
32~38行,在图表第四行建议一个“提交”和“重置”按钮。
为了美观,在7行加了一个漂亮的图片,正如预览中的大图一样。
注意要把下载好的login.jpg图片放在/webapps/reg/images/路径下。也就是要在/reg目录下新建images文件夹并把login.jpg放入其中即可。
这样我们运行Tomcat服务器并在浏览器中输入
localhost:8080/reg/reg.jsp
就可以看到我们预览的第一个图片了。但此时还不能注册提交,因为后面的JavaBean和Servlet并没有实现。
接下来同样在/reg目录下,新建一个message.jsp:
<body>
<center><img src="images/regfail.jpg"/></center>
<% String info = (String)request.getAttribute("info"); if(info != null) { out.print("<center>"); out.print("<font size=10>"); out.print("<B>"); out.print(info); out.print("</B>"); out.print("</font>"); out.print("</center>"); } %>
</body>
这个页面是用于信息提示的。比如注册成功,用户名已存在,两次输入密码不一致等。该页面由控制层Servlet转发。
4行,请求信息被封装在request对象中,这里从该对象里获取info属性的信息(注册成功,用户名已存在,密码确认不正确),后面可以看到该信息由JavaBean返回结果,Servlet中被写入。
5~13行,输出info信息。
为了美观,我们同样在2行插入一个漂亮的图片。
首先我们建立一个用户信息的UserBean,这个类封装了用户实体的对象,代码十分简单:只有两个属性,username和password以及他们的get、set方法。
UserBean.java
package beans;
public class UserBean {
private String username;
private String password;
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;
}
}
接下来是比较重要的用于数据库连接的类:
DataBaseUtil.java
package utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DataBaseUtil {
public static Connection getConnection() {
Connection connection = null;
try{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost/user";
connection = DriverManager.getConnection(url, "easy", "123456");
return connection;
}catch(ClassNotFoundException nfe) {
nfe.printStackTrace();
}catch(SQLException sqle) {
sqle.printStackTrace();
}
return connection;
}
public static void closeConnection(Connection connection) {
if(connection != null) {
try{
connection.close();
}catch(SQLException sqle) {
sqle.printStackTrace();
}
}
}
}
该类主要封装了数据库连接及断开连接操作。
12~15行加载数据库驱动,建立并返回一个连接到user数据库的连接。
25~33行,提供断开数据库连接的方法。
同样可以参考一下JDBC常用类和接口及基本操作。
接下来是与用户相关的数据库操作,包括数据库查询,添加,用户信息持久化等方法。这些操作被封装在UserDao类中:
UserDao.java
package dao;
import utils.DataBaseUtil;
import beans.UserBean;
import java.sql.*;
public class UserDao {
public boolean isUserExist(String username) {
//create a connection
Connection connection = DataBaseUtil.getConnection();
String sql ="select * from tb_user WHERE username=?";
try{
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, username);
ResultSet resultSet = ps.executeQuery();
if(!resultSet.next()) {
return true;
}
}catch(SQLException e) {
System.out.println(e.toString());
e.printStackTrace();
}finally {
DataBaseUtil.closeConnection(connection);
}
return false;
}
public boolean isPasswordConfirm(String password, String repassword) {
return password.equals(repassword);
}
public void saveUser(UserBean user) {
Connection connection = DataBaseUtil.getConnection();
String sql = "insert into tb_user (username, password) values(?,?) ";
try {
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.executeUpdate();
}catch(SQLException e) {
e.printStackTrace();
}finally {
DataBaseUtil.closeConnection(connection);
}
}
}
10~29行,在数据库查询比对是否存在和username重名的用户名。如果没有则返回true,有重名返回false。
12行,调用DataBaseUtil中的静态方法getConnection()返回一个数据库连接。
16~17行,获取一个该连接的PrepareStatement,并将参数username写入sql语句中的‘?’占位符。
18行,查询并将查询结果返回到resultSet对象中。
19~21行,若未查询到与username匹配的结果,返回true,否则在28行返回flase。
26行,断开数据库连接。
31~33行,判断两次输入的密码是否相等,返回boolean型结果。相等为true,不相等为false。
35~50行,将注册用户信息写入到tb_user数据表中。流程同判断用户名重名方法。首先获取连接,然后将包含参数的sql语句写入到PrepareStatement中,然后执行插入数据库的操作。
48行,断开数据库连接。
至此,我们已经将和用户交互的视图层,以及具体数据库的操作都封装完毕。接下来只剩下控制层的Servlet。
在reg.jsp中提交表单,给Servlet进行处理。Servlet获得表单中的信息,然后调用JavaBean来具体比对用户名是否相同,密码是否确认最终决定是否将注册信息保存入库。同时将最终结果通过message.jsp呈现给用户。
SignInServlet.java
package service;
import beans.UserBean;
import dao.UserDao;
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 SignInServlet extends HttpServlet{
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
String username = request.getParameter("username");
String password = request.getParameter("password");
String repassword = request.getParameter("repassword");
UserDao userDao = new UserDao();
if(username != null && !username.isEmpty()) {
//用户名不存在可以注册
if(userDao.isUserExist(username) && userDao.isPasswordConfirm(password, repassword)) {
UserBean userBean = new UserBean();
userBean.setPassword(password);
userBean.setUsername(username);
userDao.saveUser(userBean);
request.setAttribute("info", "Congratulations! Reg successfully!");
}else {
if(!userDao.isUserExist(username))
request.setAttribute("info", "Sorry! Username exists!");
if(!userDao.isPasswordConfirm(password, repassword))
request.setAttribute("info", "Sorry! Password not confirmed!");
}
}
//forward to message.jsp
request.getRequestDispatcher("message.jsp").forward(request, response);
}
}
这段代码可以参考一个简单的Servlet程序
在这里我们只对doPost()方法进行了重写。
13~15行,获取表单传来的三个参数值,也就是我们输入的username,password以及confirm password。
17行,新建一个用户数据库操作对象userDao。
18行,判断用户名是否存在,两次输入密码是否相同。
21~24行,如果注册信息有效,将注册信息写入到数据库。
25行,在request中写入info属性,值为”Congratulations! Reg successfully!”
27~30行,如果注册信息无效,分别对应将info属性写入”Sorry! Username exists!”以及”Sorry! Password not confirmed!”
34行,转发到message.jsp页面。
现在,视图、模型层、控制层都已经写好了。接下来就是程序的部署执行。
我们创建以下的目录结构:
首先在reg根目录下创建WEB-INF以及images文件夹。将我们上文中说到的两个jpg图片放入到images中。
然后进入WEB-INF,创建classes和lib文件夹。将准备工作中下载的JDBC-MySQL驱动包放入到lib中。
接着进入到classes文件夹,创建UserBean.java,DataBaseUtil.java,UserDao.java和SignInServlet.java对应的package名。package是为了更明朗的结构。对照代码中的package xxx分别创建beans,dao,service,utils文件夹。然后将以上4个java文件编译成class文件放到对应的文件夹中。
然后回到WEB-INF/目录下。创建web.xml配置文件。这里需要对Servlet进行配置,以便服务器知道如何查找对应的Servlet来处理请求响应。在/WEB-INF文件夹中创建web.xml,url-pattern将该Servlet映射到/RegServlet对应reg.jsp中的<form action="RegServlet" method="post">
。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
<servlet>
<servlet-name>SignInServlet</servlet-name>
<servlet-class>service.SignInServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SignInServlet</servlet-name>
<url-pattern>/RegServlet</url-pattern>
</servlet-mapping>
</web-app>
message.jsp和reg.jsp放在webapps/reg/目录下,上文已经说了,不再赘述。
现在就可以来测试刚才做的登录模块了。
运行Tomcat,在浏览器输入
localhost:8080/reg/reg.jsp
出现预览中的注册界面。
然后输入我们的用户名密码,再确认密码。
点击reg,注册成功!显示以下界面:
如果注册失败,则会显示对应的失败信息。
尝试使用已注册过的用户名再次注册,会出现用户名已存在界面。
使用新的用户名,但是两次输入密码不相同,会出现密码确认不正确页面。
程序及源码