准备工作
开发环境准备
- 开发工具:eclipse 4.3
- 技术语言:Java SE 1.7
- 依赖控制:Maven 3.0.4
- 服务器:Tomcat 7.0.68
- 操作系统:Windows 10
- 版本管理:项目源码地址
搭建一个web项目
参考文档:快速搭建web项目
项目需求
实现一个标准的用户登录注册功能,详细需求见easyPassport需求说明。
开发实现
设计架构思想
如上图所示,由Servlet提供后台服务,由JSP完成前端的数据展示,JavaBean作为数据存储和传输的介质,并最终持久化至数据库。
模版层设计实现
创建用户信息表:
用户信息表用来保存和记录用户的信息
create table users{
id int,
userid varchar(50),
username varchar(50),
email varchar(50),
phone varchar(11),
password varchar(50),
status varchar(2),
create_datet varchar(20),
mod_datet varchar(20),
func_arr varchar(20) default '00000000000000000000'
}
对应的数据字典:
字段 | 数据类型 | 字段说明 | 是否可以为空 | 备注 |
---|---|---|---|---|
id | int | 数据库主键ID | 否 | 数据自动生成 |
userid | varchar(50) | 业务主键ID | 否 | 根据业务规则生成的主键ID |
username | varchar(50) | 用户名称 | 否 | 用户注册时输入的用户名 |
varchar(50) | 用户邮箱 | 否 | 用户注册时输入的邮箱 | |
phone | varchar(11) | 手机号 | 是 | 手机号的相关功能本期暂不涉及 |
password | varchar(50) | 密码 | 否 | 密码保存用户密码 |
status | varchar(2) | 用户状态:0-正常;1停用 | 否 | |
create_datet | varchar(20) | 用户创建时间 | 否 | |
mod_datet | varchar(20) | 用户修改时间 | 是 | |
func_arr | varchar(20) | 特殊状态位 | 是 | 用来区分特殊也属性的用户 |
JavaBean、JDBC和DAO
JavaBean是数据传输的介质,JDBC是Java应用连接数据库的标准实现,DAO(Data Access Objects)则提供了对原子表的增删查改等功能。三者共同组成了一个Java应用的模版层(也叫持久层)。
JavaBean的实现
public class User implements Serializable{
private static final long serialVersionUID = 1L;
private long id;
private String userId;
private String userName;
private String email;
private String phone;
private String password;
private String status;
private String createDatet;
private String modDatet;
private String funcArr;
// get、set方法省略
....
}
使用JDBC技术访问数据库
JDBC是Java EE标准规范的一部分,是Java应用操作数据库时最重要的方法(甚至可以说是唯一的方法,ODBC/OCI等一堆东西除非极端情况,已经基本没人使用了),基于JDBC技术衍生出很多框架,在这里就不做演示了。
一个web项目使用JDBC连接数据库的标准步骤如下:
- 引入必要的jar包
<!-- 省略pom.xml文件中的其他内容 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.191</version>
</dependency>
- 编写JDBC程序
//引入必要的包
import java.sql.*;
public class JDBCDemo {
// JDBC 驱动名及数据库 URL(以H2为例)
static final String JDBC_DRIVER = "org.h2.Driver";
static final String DB_URL = "jdbc:h2:tcp://localhost/~/test";
// 数据库的用户名与密码,需要根据自己的设置
static final String USER = "sa";
static final String PASS = "sa";
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try{
// 注册 JDBC 驱动
Class.forName(JDBC_DRIVER);
// 打开链接
System.out.println("连接数据库...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
// 执行查询
System.out.println(" 实例化Statement对...");
stmt = conn.createStatement();
String sql;
sql = "SELECT * FROM USERS";
ResultSet rs = stmt.executeQuery(sql);
// 展开结果集数据库
while(rs.next()){
// 通过字段检索
int id = rs.getInt("id");
String name = rs.getString("username");
// 输出数据
System.out.print("ID: " + id);
System.out.print(", 用户名称: " + name);
System.out.print("\n");
}
// 完成后关闭
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){
// 处理 JDBC 错误
se.printStackTrace();
}catch(Exception e){
// 处理 Class.forName 错误
e.printStackTrace();
}finally{
// 关闭资源
try{
if(stmt!=null) stmt.close();
}catch(SQLException se2){
}// 什么都不做
try{
if(conn!=null) conn.close();
}catch(SQLException se){
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
}
使用DAO提供基础功能
DAO通常有两部分组成,一个是通用的接口,提供标准的服务;一个是对应接口的实现类,方便扩展和更新。
接口类的设计如下:
import java.util.List;
import java.util.Map;
import javabean.User;
public interface UserDao {
// 用户信息查询
public User findById(long id);
public User findOne(User user);
public User findOne(Map<String, Object> paramMap);
public List<User> findList(User user);
public List<User> findList(Map<String, Object> paramMap);
public Map<String, Object> findMap(User user);
public Map<String, Object> findMap(Map<String, Object> paramMap);
// 用户信息新增
public int save(User user);
public int save(Map<String, Object> paramMap);
// 用户信息修改
public int update(User user);
public int update(Map<String, Object> paramMap);
}
DAO接口的实现类如下:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import javabean.User;
public class UserDaoImpl implements UserDao {
private static final String JDBC_DRIVER = "org.h2.Driver";
private static final String DB_URL = "jdbc:h2:tcp://localhost/~/test";
private static final String USER = "sa";
private static final String PASS = "sa";
private Connection conn = null;
private static String findUserById = " select id, userid, username, email, phone, password, status, create_datet, mod_datet, func_arr from users where id = ? ";
private static String findUsers = " select id, userid, username, email, phone, password, status, create_datet, mod_datet, func_arr from users ";
// 创建获取数据库链接的方法
private Connection getConnection() {
if (null != conn) {
return conn;
} else {
try {
Class.forName(JDBC_DRIVER);
conn = DriverManager.getConnection(DB_URL, USER, PASS);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
}
@Override
public User findById(long id) {
if(null == conn)
conn = this.getConnection();
User user = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(findUserById);
ps.setLong(1, id);
rs = ps.executeQuery();
while(rs.next()){
user = new User(
rs.getLong(1), // id
rs.getString(2), // userid
rs.getString(3), // userName
rs.getString(4), // email
rs.getString(5), // phone
rs.getString(6), // password
rs.getString(7), // status
rs.getString(8), // createDatet
rs.getString(9), // modDatet
rs.getString(10)); // funcArr
}
rs.close();
ps.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 确保资源关闭
try {
if (rs != null) rs.close();
} catch (SQLException e) {
}
try {
if (ps != null) ps.close();
} catch (SQLException e) {
}
}
return user;
}
......
}
控制层设计实现
控制层的功能主要由Servlet实现,通过分析需求文档可以得出,要实现本系统,至少需要Servlet提供以下服务:
- 用户信息验证
- 用户信息查询
- 用户信息新增
- 用户信息修改
- 密码重置
使用eclipse新建Servlet类
以实现“用户信息验证”服务为例,控制层实现应首先新建Servlet类。按以下步骤操作“File” -> “New” -> “Servlet”得到如下弹出框。
选择包结构,输入自定义的Servlet名称,点击“Next”,输入对这个Servlet类的描述,如果有需要,可以添加初始化参数。
点击“Next”,根据自己需要扩展Servlet类,最后点击“Finish”完成类的新建。
此时eclipse会自动将上述配置的内容转换为web.xml里的配置,接下来我们需要做的,就是在新增的Servlet类中,实现对应的服务。
<!-- web.xml里新增的内容 -->
<servlet>
<description></description>
<display-name>UserCheckServlet</display-name>
<servlet-name>UserCheckServlet</servlet-name>
<servlet-class>servlet.UserCheckServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserCheckServlet</servlet-name>
<url-pattern>/UserCheckServlet</url-pattern>
</servlet-mapping>
实现业务功能
下述代码简单描述了“用户信息验证”的业务流程,即获取参数 -> 获取后台数据 -> 对比用户信息与后台保存是否一致 -> 跳转到指定页面。
/**
* 用户信息验证
*/
public class UserCheckServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 默认调用doPost方法,免得同一段代码写多次
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 获取用户输入的信息
String userName = (String)request.getParameter("userName");
String passWord = (String)request.getParameter("passWord");
// 将用户名称作为参数传递给后台DAO
UserDao userDao = new UserDaoImpl();
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("userName", userName);
// DAO完成用户用户信息检索
User user = userDao.findOne(paramMap);
// 根据查询的结果判断用户是否真实存在,并跳转到指定的页面
if(user.getPassword().equals(passWord)){
response.sendRedirect("登录成功跳转到首页");
} else {
response.sendRedirect("登陆不成功则停留在登录页面");
}
}
}
JSP实现前端页面展示
eclipse新建一个JSP文件时会有很多问题,比如默认不支持中文,要解决这类问题就要设置一下eclipse的默认配置。
按照以下步骤“Windows -> “Preferences” -> “Web” -> “JSP File” -> “Editor” -> “Templates”,找到对应的页面。
点击“Editor”编辑JSP文件模版,这样再次新建文件时就不会有问题了。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>标题</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
${cursor}
</body>
</html>
总结
至此,我们已经基本实现了用户登录注册系统涉及的功能(项目源码地址)。但一个完善的,工业级别的系统远非仅仅“实现功能”这么简单,以本项目为例,应至少满足以下几点要求才能最终上线。
- 系统的性能要足够好
- 系统的安全性要有保障
性能是衡量一个软件产品的终极指标,世界上任何一款软件产品都会在满足所以业务功能基础上,尽力提升性能,提高程序的运转效率。JSP和Servlet技术是Java Web技术的基础,它有着上手简单,文档资源丰富等优点,可以让稍有Java基础的同学在搜索引擎的帮助下,快速实现功能。但也正是因为它“太基础”,很多性能相关的内容都需要程序员自己设计实现,而这些内容,恰恰是网上“稀有资源”。
web应用通常会暴露在公网环境中,安全性也不容忽视。安全问题是相对的,不存在“绝对安全”一说,但一个需要正式上线运行的产品一定要有对应的安全规格和标准。这么做既是对产品负责,更是对用户负责。
所以,这个“登录注册系统”下一阶段的目标,就是提升性能表现,提高安全等级。