只是写来记录一下学习的笔记。
学习内容:
学习完JavaWeb后,使用jsp制作一个简单的小项目。
大致完成的功能是:
1、登录注册,验证码,动态判断用户是否存在
2、用户的增删查改,多表的联查
项目用到的配置:
Tomcat 10.0.x,jdk1.8,Mysql5.0
练习项目前了解一下MVC架构:
一、MVC
1、MVC架构的介绍
MVC模式是一种软件架构,它把软件系统分为三个基本部分:模型Model、视图View和控制器Controller。
MVC 模式的目的是实现一种动态的程序设计,简化后续对程序的修改和扩展,并且使程序某一部分的重复利用成为可能。除此之外,MVC 模式通过对复杂度的简化,使程序的结构更加直观。
介绍一下大致的流程:
首先用户使用浏览器发起请求,请求由controll层接收,然后将数据打包发送至model层,model层处理完数据以后将返回数据给controller层,然后controller增选择将数据直接返回给用户还是将数据送至View层,View层接收到数据后,将数据进行渲染,然后再将视图返回给用户浏览器。
在浏览器完整的展示一个页面需要视图模板和数据,视图层特通视图模板,模型层提供业务数据,而控制层负责协调两者。
2、MVC的优缺点
1、低耦合
通过将视图层和业务层分离,允许更改视图层代码而不必重新编译模型和控制器代码。当然一个业务的流程或者业务规则的改变,只需要改动MVC的模型层和控制器即可。因为模型与控制器的分离,所以很容易去改变程序的业务规则。
2、重用性高
随着技术的不断进步,当前需要使用越来越多的方式来访问应用程序了。MVC模式允许使用各种不同样式的视图来访问同一个服务端的代码,这得益于多个视图(如WEB(HTTP)浏览器或者无线浏览器(WAP))能共享一个模型。
比如,用户可以通过电脑或通过手机来订购某样产品,虽然订购的方式不一样,但处理订购产品的方式(流程)是一样的。由于模型返回的数据没有进行格式化,所以同样的构件能被不同的界面(视图)使用。例如,很多数据可能用 HTML 来表示,但是也有可能用 WAP 来表示,而这些表示的变化所需要的是仅仅是改变视图层的实现方式,而控制层和模型层无需做任何改变。
由于已经将数据和业务规则从表示层分开,所以可以最大化的进行代码重用了。另外,模型层也有状态管理和数据持久性处理的功能,所以,基于会话的购物车和电子商务过程,也能被Flash网站或者无线联网的应用程序所重用。
3、可维护性高
分离视图层和业务逻辑层使得WEB应用更易于维护和修改。
4、有利于软件工程化管理
二、开始做项目
1、基础项目的搭建
数据库的创建
数据库的搭建
总共创建了两张表:user表和dept表
user表:
dept表:
2、根据MVC架构来设计包结构
同时将所有用到的jar包进行导入
3、注册功能的实现
首先创建注册界面register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme
() + "://" + request.getServerName() + ":" + request.getServerPort
() + path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath%>"/>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>用户管理系统</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h1 style="text-align: center;height: 300px;line-height: 300px">用户管理系统</h1>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<form action="user/register" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" name="username" placeholder="用户名"><span hidden id="msg" style="color: red"></span>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" name="password" placeholder="密码">
</div>
<div class="form-group">
<label for="profile">头像</label>
<input type="file" id="profile" name="profile">
</div>
<button type="submit" class="btn btn-default">注册</button>
</form>
</div>
<div class="col-md-6"></div>
</div>
</div>
</body>
</html>
这里用到了Bootstrap,只需要导入对应的css(我直接使用Bootstrap官网的cdn)
创建User实体类和UserController控制层,UserService层,UserDao层
因为是进行的注册操作,为了将数据保存到数据库,创建User实体类,属性名和类型都和数据库表中对应,使用request.getParameter接受表单提交的用户名和密码,然后将User进行封装,将用户数据送至Service层进行业务逻辑的处理,Service层调用Dao层进行对于数据库的访问。
这里使用的注解的方式使用Servlet则需要在web.xml中加入**metadata-complete=“false”**这样就能使用注解了。
实体类User
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String username;
private String password;
private String profile;
private Integer deptId;
public User() {
}
public User(String username, String password, String profile) {
this.username = username;
this.password = password;
this.profile = profile;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", profile='" + profile + '\'' +
", deptId=" + deptId +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer 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 Integer getDeptId() {
return deptId;
}
public String getProfile() {
return profile;
}
public void setProfile(String profile) {
this.profile = profile;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
}
控制层UserController
@WebServlet("/user/register")
@MultipartConfig
public class UserController extends HttpServlet {
UserService userService = new UserService();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
//1.注册用户,获取数据
String username = req.getParameter("username");
String password = req.getParameter("password");
//2.封装user对象
User user = new User(username,password,Constant.BASE_URL_PATH + format + "/" + fileName);
//控制层封装数据,模型层处理数据
try{
userService.register(user);
resp.sendRedirect(req.getContextPath() + "/pages/success.jsp");
}catch (UserIsExistException | IOException e){
try {
resp.sendRedirect(req.getContextPath() + "/pages/error.jsp");
} catch (IOException ioException) {
ioException.printStackTrace();
}
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
Service层UserService 处理所有的业务逻辑
public class UserService {
UserDao userDao = new UserDao();
//执行业务逻辑
public void register(User user) {
//业务实质是向数据库插入一条数据
//设计密码
String password = user.getUsername() + user.getPassword() + Constant.SALT;
user.setPassword(MD5Util.digest(user.getPassword()));
//存之前进行判断,是否用户名重复
List<User> userByName = userDao.findUserByName(user.getUsername());
if (userByName.size() > 0){
throw new UserIsExistException();
}else {
userDao.save(user);
}
}
}
Dao层创建UserDao
为了方便对于数据库的操作,我写了个JDBCUtils工具类。
JDBCUtils
里面创建了getConnection来获取连接和closeAll来关闭所有的流
public class JDBCUtils {
public static Connection getConnection(){
//1.连接数据库四要素
String url = "jdbc:mysql://localhost:3306/ydlclass?useUnicode=true&characterEncoding=utf8";
String username = "root";
String passwd = "mysql";
String driverName = "com.mysql.jdbc.Driver";
Connection connection = null;
try {
//2.实例Driver
Class.forName(driverName);
connection = DriverManager.getConnection(url, username, passwd);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return connection;
}
public static void closeAll(Connection conn, Statement statement, ResultSet resultSet){
try {
if (resultSet!=null){
resultSet.close();
}
if (statement!=null){
statement.close();
}
if (conn!=null){
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
UserDao
public class UserDao {
//保存用户
public int save(User user) {
Connection conn = JDBCUtils.getConnection();
PreparedStatement statement = null;
try {
String sql = "insert into user(username,password,profile) values(?,?,?)";
statement = conn.prepareStatement(sql);
statement.setString(1,user.getUsername());
statement.setString(2,user.getPassword());
statement.setString(3,user.getProfile());
return statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.closeAll(conn,statement,null);
}
return 0;
}
public List<User> findUserByName(String username) {
Connection conn = JDBCUtils.getConnection();
PreparedStatement statement = null;
ResultSet resultSet = null;
List<User> users = new ArrayList<>();
try {
String sql = "select id,username,password,profile,dept_id from user where username = ?";
statement = conn.prepareStatement(sql);
statement.setString(1,username);
resultSet = statement.executeQuery();
while (resultSet.next()){
int id = resultSet.getInt("id");
String username1 = resultSet.getString("username");
String password = resultSet.getString("password");
String profile = resultSet.getString("profile");
int deptId = resultSet.getInt("dept_id");
User user = new User(username1,password,profile);
user.setId(id);
user.setDeptId(deptId);
users.add(user);
};
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.closeAll(conn,statement,resultSet);
}
return users;
}
}
这样一个简单的注册功能就完成了,对登陆用户用户名是否存在进行了判断。
在获得用户密码的时候,进行了密码的加盐加密
密码的加盐加密
编写MD5Util工具类
public class MD5Util {
public static String digest(String content){
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] digest = md5.digest(content.getBytes());
Base64.Encoder encoder = Base64.getEncoder();
byte[] encode = encoder.encode(digest);
return new String(encode);
}
}
在UserService的方法中使用
String password = user.getUsername() + user.getPassword() + Constant.SALT;
user.setPassword(MD5Util.digest(user.getPassword()));
文件上传
如果要使用文件上传则需要在form表单中使用enctype=“multipart/form-data”,然后再控制层使用**@MultipartConfig**注解。
//获取文件的部分
Part profile = null;
InputStream inputStream = null;
try {
profile = req.getPart("profile");
inputStream = profile.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
if (profile == null || inputStream == null){
throw new RuntimeException("您必须上传头像");
}
//使用UUID进行处理文件名
String fileName = UUID.randomUUID().toString() + "_" + profile.getSubmittedFileName();
//给文件创建目录 1、日期方案
String format = DateUtil.getNowString();
String path = Constant.BASE_PATH + format;
File file = new File(path);
//如果路径不存在,就创建
if (!file.exists()){
file.mkdirs();
}
IOUtil.copy(inputStream, path + "/" + fileName);
// //使用hash方法
// int pathInt = fileName.hashCode() % 10;
// String path = Constant.BASE_PATH + pathInt;
// File file = new File(path);
// //如果路径不存在,就创建
// if (!file.exists()){
// file.mkdirs();
// }
request对象有getPart()方法,可以将请求携带的文件进行部分的读取,然后使用输入输出流对文件进行下载,并保存在本地磁盘的某个位置。
为了代码的简约,创建了IOUtil
public class IOUtil {
public static void copy(InputStream inputStream,String path){
OutputStream outputStream = null;
try {
//文件上传功能
outputStream = new FileOutputStream(path);
byte[] buf = new byte[1024*1024];
int len;
while ((len = inputStream.read(buf)) != -1){
outputStream.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream != null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
在将文件进行保存到本地磁盘的时候,我们对文件的路径和名字进行了处理。使用UUID的方式进行了处理可以保证文件的名字不是重复,如果文件名字重复则是会覆盖之前的文件。
当然如果想把文件保存在本地,则需要创建文件夹来保存,而对于文件夹的命名我使用了2种方式,一种是使用日期的方式命名,还有一张使用hash方法命名。
UserService获得文件的名字
//根据用户名获取头像
public String getProfile(String username) {
List<User> users = userDao.findUserByName(username);
if (users.size() > 0){
return users.get(0).getProfile();
}
return null;
}