使用 servlet 和 jsp 创建一个简单的 Web 应用程序(第 1 部分) 理解本文所需的知识水平:您已经或多或少地了解了 Java Core,并且希望了解 JavaEE 技术和 Web 编程。如果您当前正在研究 Java Collections 任务,那么它是最有意义的,它涵盖了与本文接近的主题。
最后 ,如果你想练习这个项目,你可以尝试:
创建实体
在实体包中,我们将创建一个类User,其中有两个私有字符串变量name和password。让我们创建构造函数(默认的和接受两个值的构造函数)、getters/setters、重写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 将用户列表显示到另一个列表中。事实证明,我们需要一个两个 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>
这里的表单有一个值为post 的method属性。这意味着该表单中的数据将以 POST 请求的形式发送到服务器。未指定action属性,这意味着请求将发送到我们访问此页面的同一地址 ( /add )。因此,绑定到该地址的 servlet 在收到 GET 请求后,会返回此 jsp 以及用于添加用户的表单,如果它收到 POST 请求,则意味着该表单已将其数据发送到那里(我们将从doPost() 值得注意的是,输入字段有一个名称参数(对于具有名称的字段,它具有值 name ,对于具有密码的字段,它具有值pass)。这是非常重要的一点。因为为了从请求(已经在 servlet 内部)获取这些数据(将输入的名称和密码),我们将准确使用这些名称和pass。但稍后会详细介绍。用于发送数据的按钮本身再次以按钮的形式制作,而不是像通常习惯那样作为输出字段。我不知道这个选项有多通用,但它对我有用(Chrome 浏览器)。
使用 servlet 处理 POST 请求
让我们回到AddServlet servlet 。让我提醒您:为了让我们的 servlet 能够“捕获”GET 请求,我们重写了HttpServlet类中的doGet()方法。为了教我们的 servlet 也捕获 POST 请求,我们还需要重写doPost()方法。它从Tomcat接收类似的请求和响应对象,我们将使用它。首先,让我们从请求中提取名称并传递表单发送的参数(如果您在表单中对它们进行了不同的命名,那么这些就是您编写的名称)。之后,我们将使用接收到的数据创建用户对象。然后我们将获取模型对象并将创建的用户添加到模型中。@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 servlet 。这里已经实现了doGet()方法,它只是将控制转移到list.jsp视图。如果您还没有,请使用AddServlet servlet 中的相同方法进行类推。现在,最好从模型中获取用户名列表并将它们传递给视图,视图将接收它们并很好地显示它们。为此,我们将再次使用从Tomcat收到的请求对象。我们可以向这个对象添加一个属性,给它一些名称,事实上,我们想要将其传输到view 的对象本身。由于以下事实:当将执行过程从 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 中,我们已经从模型中准备了用户名列表,并将其传递到请求对象中。由于我们有一个名称列表,因此我们可以循环遍历它并打印出每个名称。正如我已经说过的,jsp 文件可以执行 java 代码(这就是它们与静态 html 页面的不同之处)。为了执行一些代码,将以下结构放在我们需要的地方就足够了:<!-- html code -->
<%
// java code
%>
<!-- html code -->
在这个构造中,我们可以访问几个变量:
- request是我们的请求对象,我们从 servlet 传递它,在这里它被简单地称为req;
- responce - 响应对象,在 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 servlet 代码将如下所示:
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-a 带标题;
- 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框架。它已经有了带有样式的现成类;剩下的就是将我们想要应用的 CSS 类放置在正确的位置。为了将它们添加到我们的页面中,首先我们将包含一个带有样式的文件。这可以通过两种方式完成:-
浏览我们的页面,并在头部插入带有样式的文件的直接链接
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
如果您有持续的互联网连接,则此选项适合。然后,当您在本地服务器上打开页面时,样式将从 Internet 中提取。
-
如果您想在本地拥有所有样式而不依赖于 Internet 连接,请下载带有样式的文件并将其放置在 web文件夹中的某个位置(例如web/styles/w3.css),然后浏览我们的所有样式页面(index.html、add.jsp、list.jsp)并输入指向此文件的链接,并在head部分中使用样式
<link rel="stylesheet" href="styles/w3.css">
之后,只需浏览标签并添加您喜欢的样式即可。我不会详细讨论这一点,但会立即提供我的三个文件的现成版本以及已安排的样式类。
索引.html
<!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>
仅此而已 :) 如果您仍有任何问题或有任何意见,或者相反,有些事情没有解决 - 请发表评论。UPD:如果您在单击按钮时遇到 404 错误的问题,尽管一切都正确完成,也许您应该更正 idea 中的部署配置。为此,您需要转到“编辑配置”(位于顶部靠近“开始”按钮的位置),转到窗口右侧的“部署”选项卡,并确保在“应用程序”上下文中简单地指示它/嗯,我'我会附上几张截图来说明这一切的结果。
- 制作一个 servlet 和 jsp 来删除用户,并制作几个来更改/编辑现有用户。您将获得一个真正的 CrUD Web 应用程序 :) 在 servlet 上));
- 将列表(List)替换为使用数据库,以便添加的用户在重新启动服务器后不会消失:)
GO TO FULL VERSION