MVC模式:
MVC模式一般由JSP+Servlet+JavaBean组成。其中JSP用于表示数据;Servlet用于处理客户请求,充当控制器的角色;JavaBean用于数据的存取。其运行机制如下:
![](https://img-blog.csdnimg.cn/20190526204546581.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1FfTV9YX0RfRF8=,size_16,color_FFFFFF,t_70)
下面用一个简单的留言板例子来实现一下MVC模型,功能是登录账号之后可以看到所有留言的内容。那么这个模型的业务流程大概就是:
![](https://img-blog.csdnimg.cn/20190526211110299.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1FfTV9YX0RfRF8=,size_16,color_FFFFFF,t_70)
首先在login页面输入账号和密码,再将输入的送到loginServlet中,loginServlet先调用Users类来验证账号和密码是否正确,若不正确则返回login重新输入;若正确则调用Message类来取出数据库中的所有留言信息,最后将这些留言信息送到MessageList页面显示出来。代码如下:
1、登录页面,账号与密码信息已经存到了数据库中,这里就不细说了。用户填好账号和密码之后将调用Servlet来处理请求。调用Servlet方法就是将表单的action设为Servlet的名字。本页面还加入了检查输入的函数,检查完之后再提交表单。
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="UTF-8" import="java.util.*"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>用户登录</title>
</head>
<script type="text/javaScript">
function checkLogin() {
var username = document.getElementById("username_id");
if (username.value == "") {
alert("请输入用户名!");
username.focus();
return;
}
var password = document.getElementById("password_id");
if (password.value == "") {
alert("密码不能为空");
password.focus();
return;
}
login_form.submit();
}
</script>
<body>
<center>
<form name="login_form" action="loginservlet" method="post">
<table border=1>
<tr>
<td align="center" colspan=2 style="font-size: 30px">用户登录</td>
</tr>
<tr>
<td>用户名:</td>
<td><input type="text" id="username_id" name="username"
size="25" /></td>
</tr>
<tr>
<td>密 码:</td>
<td><input type="password" id="password_id" name="password"
size="25"></td>
</tr>
</table>
<input type="button" value="登录" style="height: 30px; width: 80px"
onclick="checkLogin()">
</form>
</center>
</body>
</html>
2、loginServlet,这个Servlet首先new一个Users对象,将该对象的属性值设成用户输入的账号和密码,再调用他的check方法来验证账号和密码是否正确。若不正确则返回重新登录;若正确则调用Message类来访问数据库,将所有留言信息存到一个List中,然后通过request的setAttribute方法将该List传递给MessageList.jsp页面上来显示。
PS:为了防止中文账号传过来之后乱码,加入request.setCharacterEncoding("utf-8");这一句,使JSP和此Servlet的字符集相同。否则程序会用乱码去和数据库中的账号做比较,结果肯定出错。
package com.servlet;
import java.io.IOException;
import java.util.*;
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 javax.swing.JOptionPane;
import com.bean.Message;
import com.bean.Users;
/**
* Servlet implementation class loginservlet
*/
@WebServlet("/loginservlet")
public class loginservlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public loginservlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
request.setCharacterEncoding("utf-8");
Users user = new Users();
String name = request.getParameter("username");
String password = request.getParameter("password");
user.setUsername(name);
user.setPassword(password);
if (!user.check()) {
JOptionPane.showMessageDialog(null, "用户名或密码错误! ", "登录失败 ", JOptionPane.ERROR_MESSAGE);
request.getRequestDispatcher("login.jsp").forward(request, response);
} else {
Message m = new Message();
List<Message> list = m.searchall();
request.setAttribute("list", list);
request.getRequestDispatcher("MessageList.jsp").forward(request, response);
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
3、Users和Message类,这是MVC中的JavaBean,用来实现数据库的数据存取。这两个类中我都调用了DButil这个类,这是个工具类,用来连接数据库,下面再说。
Users类,实现的功能是检查用户登录时输入的账号和密码是否正确,用户在输入账号和密码之后会定义一个Users对象,通过调用他的check()方法,来将输入的信息与数据库中的信息作对比。
验证账号密码的时候,我是先从数据库中,将用户输入的用户名所在的这一行取出来。若数据库中不存在输入的用户名,即结果集的行数为0,则登录失败(求结果集的行数之前必须先调用last方法);若有数据,但是数据库中存着的密码与输入的不符,则登录失败(java中判断两个字符串相等不能用==,要调用其中一个字符串的equal方法,getString用于取出该列的值)。
package com.bean;
import com.util.DButil;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;
@SuppressWarnings("unused")
public class Users {
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;
}
public boolean check() {
Connection conn = DButil.open();
String sql = "select * from user where name=" + "'" + this.Username + "'";
try {
PreparedStatement ps = (PreparedStatement) conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery(sql);
rs.last();
int row = rs.getRow();
if (row == 0) {
JOptionPane.showMessageDialog(null, "无数据", "登录失败 ", JOptionPane.ERROR_MESSAGE);
return false;
}
if (!rs.getString(2).equals(this.password)) {
JOptionPane.showMessageDialog(null, "密码错误", "登录失败 ", JOptionPane.ERROR_MESSAGE);
return false;
}
} catch (SQLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
return false;
}
return true;
}
}
Message类,实现的是将所有留言信息从数据库中读出来,返回一个List。
package com.bean;
import java.util.*;
import javax.swing.JOptionPane;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.util.DButil;
public class Message {
String title;
String context;
String author;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public List<Message> searchall() {
String sql = "select * from message";
Connection conn = DButil.open();
try {
PreparedStatement ps = (PreparedStatement) conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery(sql);
List<Message> list = new ArrayList<Message>();
while (rs.next()) {
Message m = new Message();
m.setTitle(rs.getString(1));
m.setContext(rs.getString(2));
m.setAuthor(rs.getString(3));
list.add(m);
}
return list;
} catch (SQLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
return null;
}
}
4、MessageList.jsp,此页面用于显示从数据库中取出的所有留言信息。刚才在loginServlet中已经将结果集list传递了过来,那么我们这里就直接调用getAttribute方法来接收,再遍历输出即可。输出方法就是在java代码中插入HTML标签,一边遍历一边输出。代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*" import="com.bean.Message"
import="javax.swing.*"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>留言列表</title>
</head>
<body>
<table align="center" border="1" width="50%" cellpadding="8">
<tr>
<th align="center" colspan=6>留言列表</th>
</tr>
<tr>
<th align="center" width=100>序号</th>
<th align="center" width=500>标题</th>
<th align="center" width=100>作者</th>
<th align="center" width=100>操作</th>
</tr>
<%
List<Message> list = (List<Message>) request.getAttribute("list");
int i = 1;
for (Message m : list) {
%>
<tr align="center">
<td><%=i++%></td>
<td><%=m.getContext()%></td>
<td><%=m.getAuthor()%></td>
<td>
<form action="" method="post">
<input type="button" value="查看" onclick="" />
</form>
</td>
</tr>
<%
}
%>
</table>
</body>
</html>
5、BDutil类,这是个工具类,只封装了一个方法,就是获取一个与数据库的连接Connection。代码如下:
package com.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@SuppressWarnings("unused")
public class DButil {
private static String url = "jdbc:mysql://127.0.0.1:3306/实验?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false";// 连接数据库的驱动
private static String driver = "com.mysql.cj.jdbc.Driver";
private static String username = "root";
private static String password = "123456";
public static Connection open() {
try {
Class.forName(driver);
return (Connection) DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException | SQLException e) {
// TODO 自动生成的 catch 块
System.out.print("数据库打开失败!");
e.printStackTrace();
}
return null;
}
public static void close(Connection conn) {
try {
conn.close();
} catch (SQLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
实现效果大致如下,(一些前段的代码为了简练并没有贴上来,但实现功能的代码是齐全的):
1、用户登录
![](https://img-blog.csdnimg.cn/20190526213754423.png)
2、瞎输入用户名和密码的话会显示登录错误
![](https://img-blog.csdnimg.cn/20190526213928777.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1FfTV9YX0RfRF8=,size_16,color_FFFFFF,t_70)
3、登录成功后显示留言列表(第一条就是数据库中的数据,是我随便插入的):
![](https://img-blog.csdnimg.cn/20190526213824880.png)
209年6月22日更新:
前几天学习了DAO模式,然后才发现我的MVC其实不是很正宗的MVC,正宗的MVC是将与数据库做数据交换的函数放到了Servlet中,而我是放进了java bean中。然后我就更加感受到了MVC模式的弊端,Servlet本来是实现业务控制的地方,但是加入了与数据库的访问操作,这两个模块的耦合度太高了,不利于分工。
可能当时写这个留言板的时候已经感觉到这个问题了,所以我很自觉地把对数据库的访问放在了java bean中,但是今天回过头发现我这既不是DAO也不是MVC。我又写了一篇将MVC扩展为DAO的博客,可以很好地对数据层和控制层降耦,并将留言板的最终版本放到了GitHub上。
链接:https://blog.csdn.net/Q_M_X_D_D_/article/details/93315608