使用 servlet 和 JSP 创建一个简单的 Web 应用程序(第 1 部分) 理解本文所需的知识:您已经或多或少地了解了 Java Core,并希望了解 JavaEE 技术和 Web 编程。对您来说,目前正在研究 Java Collections 任务是最有意义的,它处理的主题与本文很接近。
最后,如果你想练习这个项目,你可以尝试以下方法:

创建实体
在entities包中,我们将创建一个User
具有两个私有字符串变量的类:name和password。创建构造函数(默认构造函数和一个同时接受两个值的构造函数)和 getter/setter,并重写该toString()
方法以防万一,以及equals()
和hashCode()
方法。换句话说,我们将做一个受人尊敬的 Java 开发人员在创建类时所做的一切。
public class User {
private String name;
private String password;
public User() {
}
public User(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (name != null ? !name.equals(user.name) : user.name != null) return false;
return password != null ? password.equals(user.password) : user.password == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (password != null ? password.hashCode() : 0);
return result;
}
}
现在我们可以开始创建用户列表了。我们将向其中添加用户,并从中获取用户以显示它们。但是,我们确实有一个问题。我们不创建我们的 servlet 对象。Tomcat为我们做了这件事。我们在其中重写的方法已经为我们定义好了,我们不能传参。那么我们如何创建一个在我们的两个 servlet 中都可见的共享列表呢?如果我们只是在每个 servlet 中创建一个列表对象,那么我们会将用户添加到一个列表中,但会在 ListServlet 中显示另一个列表中的用户. 所以我们需要一个由两个 servlet 共享的对象。一般来说,我们需要一个由我们程序中的所有类共享的对象:一个对象用于整个程序。我希望你听说过一些关于设计模式的事情。对于某些人来说,这可能是他们程序中第一次真正需要单例模式。您可以疯狂地制作一些带有双重检查和同步的甜蜜单例(是的,我们的应用程序是多线程的,因为 Tomcat servlet 在单独的线程上运行)。但我将使用早期初始化技术,因为它完全可以满足我们的目的。
创建模型
在模型包中创建一个类(并实现单例模式)并将其命名为不寻常的东西。例如,模型。我们将在我们的类中创建一个私有用户列表,并实现两个方法:一个添加用户,另一个返回字符串列表(用户名)。由于我们的用户对象包含用户名和密码,并且我们不想泄露用户密码,因此我们只有一个名称列表。
public class Model {
private static Model instance = new Model();
private List<User> model;
public static Model getInstance() {
return instance;
}
private Model() {
model = new ArrayList<>();
}
public void add(User user) {
model.add(user);
}
public List<String> list() {
return model.stream()
.map(User::getName)
.collect(Collectors.toList());
}
}
关于MVC的一点
由于您已经听说过singleton,因此您可能听说过另一种设计模式模型-视图-控制器(MVC)。其目的是将业务逻辑与视图分开。也就是说,将决定做什么的代码与决定如何显示东西的代码分开。视图负责数据的呈现方式。在我们的例子中,视图是我们的JSP 页面。这正是我将它们放在名为views的文件夹中的原因。 模型是程序实际使用的数据。在我们的例子中,这是用户(用户列表)。而控制器是它们之间的纽带。他们从模型中获取数据并将其传递给视图(或者从Tomcat中获取一些数据,对其进行处理,并将其传递给模型)。您在其中定义您的业务逻辑(程序应该做什么),而不是在模型或视图中。因此,每个部分处理自己的业务:- 模型存储数据;
- 视图呈现数据的漂亮表示;
- 控制器处理数据处理。
<form method="post">
<label>Name:
<input type="text" name="name"><br />
</label>
<label>Password:
<input type="password" name="pass"><br />
</label>
<button type="submit">Submit</button>
</form>
这里的表单有一个method属性,其值为post。这表示此表单中的数据将作为POST 请求发送到服务器。未指定action属性,这意味着请求将发送到我们访问此页面的相同地址 ( / add )。因此,在收到GET 请求后,绑定到该地址的 servlet 会返回带有添加用户表单的JSP 。如果它收到一个POST 请求,那么我们就知道表单在这里发送了它的数据(我们从doPost()
方法、过程,并传递给模型进行保存)。值得注意的是,输入字段有一个名为name的参数(用于用户名,或pass用于密码)。这是非常重要的一点。因此,要从请求(在 servlet 内)接收此数据(将输入的用户名和密码),我们将使用这些名称和传递字段。但稍后会详细介绍。我用于发送数据的按钮再次被制作为按钮,而不是通常的输出字段。我不知道这种方法的采用有多广泛,但它适用于我(Chrome 浏览器)。
POST 请求的 Servlet 处理
让我们回到AddServlet。我提醒您,为了让我们的 servlet 能够“捕获” GET 请求,我们覆盖了HttpServletdoGet()
类中的方法。为了让我们的 servlet 也能捕获POST 请求,我们还必须覆盖该方法。Tomcat向它传递我们将使用的类似请求和响应对象。首先,提取请求的名称并传递表单发送的参数(如果您在表单中指定了不同的名称,则使用这些名称)。之后,使用接收到的数据创建一个用户对象。然后我们获取模型对象并将创建的用户添加到模型中。 doPost()
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
String password = req.getParameter("pass");
User user = new User(name, password);
Model model = Model.getInstance();
model.add(user);
}
将数据传递给视图
让我们继续讨论ListServlet。该doGet()
方法已经实施。它只是将控制转移到视图 ( list.jsp )。如果您还没有这个,则类比 AddServlet 中的方法创建它。现在最好从模型中获取用户名列表并将它们传递给视图,视图将接收它们并漂亮地显示它们。为此,我们将再次使用从Tomcat收到的请求对象。我们可以给这个对象添加一个属性,给它起一个名字。其实我们可以把我们要传递的对象添加到视图中. 由于在将控制从 servlet 转移到视图时,我们向视图传递了与 servlet 接收到的相同的请求和响应对象,因此我们可以将名称列表添加到请求对象,然后从请求中获取用户名列表视图中的对象。我们已经完成了ListServlet类,所以我将在这里展示整个类的代码:
package app.servlets;
import app.model.Model;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class ListServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Model model = Model.getInstance();
List<String> names = model.list();
req.setAttribute("userNames", names);
RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/list.jsp");
requestDispatcher.forward(req, resp);
}
}
在 JSP 文件中运行 Java 代码
是时候查看list.jsp了。它只会在ListServlet将控制权转移给它时执行。此外,我们已经从 servlet 中的模型准备了用户名列表,并将其传递到请求对象中。由于我们有姓名列表,我们可以使用循环对其进行迭代for
并显示每个姓名。正如我之前所说,JSP 文件可以执行Java 代码(这就是它们与静态 HTML 页面的不同之处)。要执行一些代码,我们需要做的就是将以下结构放在适当的位置:
<!-- html code -->
<%
// Java code
%>
<!-- html code -->
在这个结构中,我们可以访问几个变量:
- request — 我们的请求对象,我们从 servlet 传递过来的,它被简称为req;
- response — 响应对象(在 servlet 中称为resp );
- out — 一个JspWriter对象(它继承了一个普通的Writer),我们可以使用它来将某些内容直接“写入”到HTML 页面本身。语句out.println("Hello, World!")与System.out.println("Hello, World!")非常相似,但不要混淆它们!
- out.println() “写入”到HTML 页面,而System.out.println写入系统输出流。如果您使用Java 代码在JSP部分中调用System.out.println() ,您将在Tomcat控制台中看到结果,而不是在页面上。
<ul>
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
for (String s : names) {
out.println("<li>" + s + "</li>");
}
}
%>
</ul>
如果我们只需要在有用户时显示列表,否则显示没有用户的警告,那么我们可以稍微改写这部分:
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
out.println("<ui>");
for (String s : names) {
out.println("<li>" + s + "</li>");
}
out.println("</ui>");
} else out.println("<p>There are no users yet!</p>");
%>
现在我们知道如何将数据从 servlet 传递到视图,我们可以改进我们的AddServlet以便它显示有关成功添加用户的通知。为此,在doPost()
方法中,在将新用户添加到模型后,我们可以将此用户名添加到req对象的属性并将控制权传回视图( add.jsp )。现在我们将向其中添加一个带有 Java 代码的部分,我们将在其中检查请求是否具有这样的属性,如果有,那么我们将显示一条消息,表明用户已成功添加。完成这些更改后,AddServlet的完整代码将如下所示:
package app.servlets;
import app.entities.User;
import app.model.Model;
import javax.servlet.RequestDispatcher;
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 AddServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/add.jsp");
requestDispatcher.forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
String password = req.getParameter("pass");
User user = new User(name, password);
Model model = Model.getInstance();
model.add(user);
req.setAttribute("userName", name);
doGet(req, resp);
}
}
在这里,在方法的末尾,doPost()
我们创建了一个属性,其中包含添加到模型中的用户的名称,然后调用该doGet()
方法,我们将当前请求和响应传递给该方法。该doGet()
方法现在将控制权转移到视图,该视图还接收请求对象,并将添加的用户的名称附加为属性。我们剩下要做的是修复add.jsp,以便在没有此类属性时显示通知。这是add.jsp的最终版本:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Add new user</title>
</head>
<body>
<div>
<h1>Super app!</h1>
</div>
<div>
<%
if (request.getAttribute("userName") != null) {
out.println("<p>User '" + request.getAttribute("userName") + "' added!</p>");
}
%>
<div>
<div>
<h2>Add user</h2>
</div>
<form method="post">
<label>Name:
<input type="text" name="name"><br />
</label>
<label>Password:
<input type="password" name="pass"><br />
</label>
<button type="submit">Submit</button>
</form>
</div>
</div>
<div>
<button onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
页面主体由以下部分组成:
- 带有标题的 div;
- 内容的 div 容器,其中包括检查是否存在具有用户名的属性;
- 带有添加用户表单的 div;
- 在底部,一个带有返回主页按钮的页脚。
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Users</title>
</head>
<body>
<div>
<h1>Super app!</h1>
</div>
<div>
<div>
<div>
<h2>Users</h2>
</div>
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
out.println("<ui>");
for (String s : names) {
out.println("<li>" + s + "</li>");
}
out.println("</ui>");
} else out.println("<p>There are no users yet!</p>");
%>
</div>
</div>
<div>
<button onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
因此,我们有一个可以保存和添加用户并显示他们姓名列表的完整工作的 Web 应用程序。现在我们只需要让它漂亮...... :) 
添加样式。我们将使用 W3.CSS 框架
目前,我们的应用程序可以运行,但它看起来绝对离谱。因此,让我们添加背景、为文本和按钮着色、为列表添加样式、对齐元素、添加缩进等等。手动编写样式会花费大量时间并耗费我们的精力。所以我建议使用W3.CSS框架。它已经有带有样式的现成类。我们只需要将要使用的 CSS 类安排在正确的位置即可。要将它们添加到我们的页面,我们首先连接样式文件。有两种方法可以做到这一点:-
浏览我们的页面并在标题部分插入以下样式文件的直接链接
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
如果您有永久的 Internet 连接,则此选项适用。当您在本地服务器上打开您的页面时,样式将从 Internet 中提取。
-
但是如果你想在本地拥有所有样式而不依赖于 Internet 连接,请下载样式文件并将其放在 web文件夹中的某个位置(例如web/styles/w3.css)。然后浏览我们所有的页面(index.html、add.jsp、list.jsp)并将以下链接添加到head部分内的样式文件:
<link rel="stylesheet" href="styles/w3.css">
之后,只需浏览标签并添加您喜欢的样式即可。这个我就不详细说了。相反,我将只提供我的三个带有光栅样式类的文件的即用型版本。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Super app!</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body class="w3-light-grey">
<div class="w3-container w3-blue-grey w3-opacity w3-right-align">
<h1>Super app!</h1>
</div>
<div class="w3-container w3-center">
<div class="w3-bar w3-padding-large w3-padding-24">
<button class="w3-btn w3-hover-light-blue w3-round-large" onclick="location.href='/list'">List users</button>
<button class="w3-btn w3-hover-green w3-round-large" onclick="location.href='/add'">Add user</button>
</div>
</div>
</body>
</html>
添加.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Add new user</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body class="w3-light-grey">
<div class="w3-container w3-blue-grey w3-opacity w3-right-align">
<h1>Super app!</h1>
</div>
<div class="w3-container w3-padding">
<%
if (request.getAttribute("userName") != null) {
out.println("<div class=\"w3-panel w3-green w3-display-container w3-card-4 w3-round\">\n" +
" <span onclick=\"this.parentElement.style.display='none'\"\n" +
" class=\"w3-button w3-margin-right w3-display-right w3-round-large w3-hover-green w3-border w3-border-green w3-hover-border-grey\">×</span>\n" +
" <h5>User '" + request.getAttribute("userName") + "' added!</h5>\n" +
"</div>");
}
%>
<div class="w3-card-4">
<div class="w3-container w3-center w3-green">
<h2>Add user</h2>
</div>
<form method="post" class="w3-selection w3-light-grey w3-padding">
<label>Name:
<input type="text" name="name" class="w3-input w3-animate-input w3-border w3-round-large" style="width: 30%"><br />
</label>
<label>Password:
<input type="password" name="pass" class="w3-input w3-animate-input w3-border w3-round-large" style="width: 30%"><br />
</label>
<button type="submit" class="w3-btn w3-green w3-round-large w3-margin-bottom">Submit</button>
</form>
</div>
</div>
<div class="w3-container w3-grey w3-opacity w3-right-align w3-padding">
<button class="w3-btn w3-round-large" onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
列表.jsp
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Users list</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body class="w3-light-grey">
<div class="w3-container w3-blue-grey w3-opacity w3-right-align">
<h1>Super app!</h1>
</div>
<div class="w3-container w3-center w3-margin-bottom w3-padding">
<div class="w3-card-4">
<div class="w3-container w3-light-blue">
<h2>Users</h2>
</div>
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
out.println("<ul class=\"w3-ul\">");
for (String s : names) {
out.println("<li class=\"w3-hover-sand\">" + s + "</li>");
}
out.println("</ul>");
} else out.println("<div class=\"w3-panel w3-red w3-display-container w3-card-4 w3-round\">\n"
+
" <span onclick=\"this.parentElement.style.display='none'\"\n" +
" class=\"w3-button w3-margin-right w3-display-right w3-round-large w3-hover-red w3-border w3-border-red w3-hover-border-grey\">×</span>\n" +
" <h5>There are no users yet!</h5>\n" +
"</div>");
%>
</div>
</div>
<div class="w3-container w3-grey w3-opacity w3-right-align w3-padding">
<button class="w3-btn w3-round-large" onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
就是这样。:) 如果您还有任何问题或意见,或者有什么不对,请发表评论。我将附上几张截图,说明结果如何。



- 制作一个 servlet 和 JSP 来删除用户,并添加另一对来编辑现有用户。结果将是使用 servlet 构建的真正的 CRUD Web 应用程序。;)
- 将List替换为数据库,这样添加的用户不会在服务器重启后消失。:)
GO TO FULL VERSION