一、关于一个web站点的欢迎页面
1.什么是一个web站点的欢迎页面?
-
对于一个webapp来说,我们是可以设置它的欢迎页面的。
-
设置了欢迎页面之后,当你访问这个webapp的时候,或者访问这个web站点的时候,没有指定任何"资源路径",这个时候会默认访问你的欢迎页面。
-
我们一般的访问方式是:
- http://localhost:8080/servlet06/login.html 这种方式是指定了要访问的就是login.html资源。
-
如果我们访问的方式是:
-
http://localhost:8080/servlet06 如果我们访问的就是这个站点,没有指定具体的资源路径。它默认会访问谁呢?
-
默认会访问你设置的欢迎页面。
-
2.怎么设置欢迎页面呢?
- 第一步:我在IDEA工具的web目录下新建了一个文件login.html

- 第二步:在web.xml文件中进行了以下的配置

<welcome-file-list>
<welcome-file>login.html</welcome-file>
</welcome-file-list>
-
- 注意:设置欢迎页面的时候,这个路径不需要以"/"开始。并且这个路径默认是从webapp的根下开始查找。
-
第三步:启动服务器,浏览器地址栏输入地址

3.如果在webapp的根下新建一个目录,目录中再给一个文件,那么这个欢迎页该如何设置呢?
-
在webapp根下新建page1
-
在page1下新建page2目录
-
在page2目录下新建page.html页面

-
在web.xml文件中应该这样配置
<welcome-file-list> <welcome-file>page1/page2/page.html</welcome-file> </welcome-file-list>

注意:路径不需要以"/"开始,并且路径默认从webapp的根下开始找。

4.一个webapp是可以设置多个欢迎页面的
<welcome-file-list>
<welcome-file>page1/page2/page.html</welcome-file>
<welcome-file>login.html</welcome-file>
</welcome-file-list>
- 注意:越靠上的优先级越高。找不到的继续向下找。
5.你有没有注意一件事:当我的文件名设置为index.html的时候,不需要在web.xml文件中进行配置欢迎页面。这是为什么?
演示:



-
这是因为小猫咪Tomcat服务器已经提前配置好了。
-
实际上配置欢迎页面有两个地方可以配置:
-
一个是在webapp内部的web.xml文件中。(在这个地方配置的属于局部配置)
-
一个是在CATALINA_HOME/conf/web.xml文件中进行配置。(在这个地方配置的属于全局配置)
-

<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
-
- Tomcat服务器的全局欢迎页面是:index.html index.htm index.jsp。如果你一个web站点没有设置局部的欢迎页面,Tomcat服务器就会以index.html index.htm index.jsp作为一个web站点的欢迎页面。

- 注意原则:局部优先原则。(就近原则)
6.欢迎页可以是一个Servlet吗?
-
当然可以。
-
你不要多想,欢迎页就是一个资源,既然是一个资源,那么可以是静态资源,也可以是动态资源。
-
静态资源:index.html welcome.html .....
-
动态资源:Servlet类。
-
步骤:
-
第一步:写一个Servlet
public class WelcomeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("welcome to 世界!
");
}
}
-
第二步:在web.xml文件中配置servlet
第三步:在web.xml文件中配置欢迎页

WelcomeServlet类
package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class WelcomeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("<h1>welcome to 世界!</h1>");
}
}


二、关于WEB-INF目录
-
在WEB-INF目录下新建了一个文件:welcome.html
-
打开浏览器访问:http://localhost:8080/servlet07/WEB-INF/welcome.html 出现了404错误。
-
注意:放在WEB-INF目录下的资源是受保护的。在浏览器上不能够通过路径直接访问。所以像HTML、CSS、JS、image等静态资源一定要放到WEB-INF目录之外。


三、HttpServletRequest接口详解
-
HttpServletRequest是一个接口,全限定名称:jakarta.servlet.http.HttpServletRequest
-
HttpServletRequest接口是Servlet规范中的一员。
-
HttpServletRequest接口的父接口:ServletRequest

1.HttpServletRequest接口的实现类谁写的? HttpServletRequest对象是谁给创建的?

RequestTestServlet类
package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class RequestTestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// org.apache.catalina.connector.RequestFacade@642e8513
out.print(request);
}}


-
通过测试:org.apache.catalina.connector.RequestFacade 实现了 HttpServletRequest接口
public class RequestFacade implements HttpServletRequest {}
-
测试结果说明:Tomcat服务器(WEB服务器、WEB容器)实现了HttpServletRequest接口,还是说明了Tomcat服务器实现了Servlet规范。而对于我们javaweb程序员来说,实际上不需要关心这个,我们只需要面向接口编程即可。
-
我们关心的是HttpServletRequest接口中有哪些方法,这些方法可以完成什么功能!!!!
2.HttpServletRequest对象中都有什么信息?都包装了什么信息?
-
HttpServletRequest对象是Tomcat服务器负责创建的。这个对象中封装了什么信息?封装了HTTP的请求协议。
-
实际上是用户发送请求的时候,遵循了HTTP协议,发送的是HTTP的请求协议,Tomcat服务器将HTTP协议中的信息以及数据全部解析出来,然后Tomcat服务器把这些信息封装到HttpServletRequest对象当中,传给了我们javaweb程序员。
-
javaweb程序员面向HttpServletRequest接口编程,调用方法就可以获取到请求的信息了。
3.request和response对象的生命周期?
-
request对象和response对象,一个是请求对象,一个是响应对象。这两个对象只在当前请求中有效。
-
一次请求对应一个request。
-
两次请求则对应两个request。
-
.....
4.HttpServletRequest接口中有哪些常用的方法?
怎么获取前端浏览器用户提交的数据?
Map<String,String[]> getParameterMap() 这个是获取Map
Enumeration<String> getParameterNames() 这个是获取Map集合中所有的key
String[] getParameterValues(String name) 根据key获取Map集合的value
String getParameter(String name) 获取value这个一维数组当中的第一个元素。这个方法最常用。
// 以上的4个方法,和获取用户提交的数据有关系。
思考:如果是你,前端的form表单提交了数据之后,你准备怎么存储这些数据,你准备采用什么样的数据结构去存储这些数据呢?
代码测试:

RequestTestServlet类:
package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class RequestTestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// org.apache.catalina.connector.RequestFacade@642e8513
out.print(request);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
register.html


-
前端提交的数据格式:username=abc&userpwd=111&aihao=s&aihao=d&aihao=tt
-
我会采用Map集合来存储:

注意:前端表单提交数据的时候,假设提交了120这样的"数字",其实是以字符串"120"的方式提交的,所以服务器端获取到的一定是一个字符串的"120",而不是一个数字。(前端永远提交的是字符串,后端获取的也永远是字符串。)
5.手工开发一个webapp。测试HttpServletRequest接口中的相关方法。


test.html
<!DOCTYPE html>
<html>
<head>
<!-- 设置字符编码为 UTF-8 -->
<meta charset="UTF-8">
<title>测试 Servlet</title>
</head>
<body>
<form action="test" method="post">
<label for="name">姓名:</label>
<input type="text" id="name" name="name"><br>
<label for="email">邮箱:</label>
<input type="text" id="email" name="email"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
TestServlet类
package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.Map;
public class TestServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 测试 request.getParameterMap()
Map<String, String[]> parameterMap = request.getParameterMap();
out.println("<h2>参数映射:</h2>");
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
out.print(key + ": ");
for (String val : values) {
out.print(val + " ");
}
out.println();
}
// 测试 request.getParameterNames()
Enumeration<String> names = request.getParameterNames();
out.println("<h2>参数名称:</h2>");
while (names.hasMoreElements()) {
String name = names.nextElement();
out.println(name);
}
// 测试 request.getParameterValues()
String[] values = request.getParameterValues("name");
out.println("<h2>参数 'name' 的值:</h2>");
if (values != null) {
for (String val : values) {
out.println(val);
}
} else {
out.println("'name' 没有值");
}
// 测试 request.getParameter()
String value = request.getParameter("name");
out.println("<h2>参数 'name' 的单个值:</h2>");
if (value != null) {
out.println(value);
} else {
out.println("'name' 没有值");
}
}
}


6.request对象实际上又称为"请求域"对象。
⑴应用域对象是什么?
-
ServletContext (Servlet上下文对象。)
-
什么情况下会考虑向ServletContext这个应用域当中绑定数据呢?
-
第一:所有用户共享的数据。
-
第二:这个共享的数据量很小。
-
第三:这个共享的数据很少的修改操作。
-
在以上三个条件都满足的情况下,使用这个应用域对象,可以大大提高我们程序执行效率。
-
实际上向应用域当中绑定数据,就相当于把数据放到了缓存(Cache)当中,然后用户访问的时候直接从缓存中取,减少IO的操作,大大提升系统的性能,所以缓存技术是提高系统性能的重要手段。
-
⑵你见过哪些缓存技术呢?
-
字符串常量池
-
整数型常量池 [-128~127],但凡是在这个范围当中的Integer对象不再创建新对象,直接从这个整数型常量池中获取。大大提升系统性能。
-
数据库连接池(提前创建好N个连接对象,将连接对象放到集合当中,使用连接对象的时候,直接从缓存中拿。省去了连接对象的创建过程。效率提升。)
-
线程池(Tomcat服务器就是支持多线程的。所谓的线程池就是提前先创建好N个线程对象,将线程对象存储到集合中,然后用户请求过来之后,直接从线程池中获取线程对象,直接拿来用。提升系统性能)
-
后期你还会学习更多的缓存技术,例如:redis、mongoDB.....
⑶ServletContext当中有三个操作域的方法:

⑷"请求域"对象
-
"请求域"对象要比"应用域"对象范围小很多。生命周期短很多。请求域只在一次请求内有效。
-
一个请求对象request对应一个请求域对象。一次请求结束之后,这个请求域就销毁了。
-
请求域对象也有这三个方法:

-
-
请求域和应用域的选用原则?
- 尽量使用小的域对象,因为小的域对象占用的资源较少。
-

AServlet 类
package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取系统当前时间
Date nowTime = new Date();
// 将系统当前时间绑定到请求域当中
request.setAttribute("sysTime", nowTime);
// 取出来
Object sysTime = request.getAttribute("sysTime");
//输出到浏览器
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("系统当前时间是:" + sysTime);
}}

这段代码是典型的Java Web开发(如Servlet)中处理请求和响应的逻辑,主要作用是通过请求域(Request Scope)传递数据,并将服务器当前时间输出到浏览器。以下是逐行解释:
①. 获取系统当前时间
Date nowTime = new Date();
- 作用 :创建一个
Date
对象,表示代码执行时的系统当前时间(精确到毫秒)。
②. 将时间绑定到请求域
request.setAttribute("sysTime", nowTime);
-
作用 :将当前时间存入请求域(Request Scope) ,键为
"sysTime"
,值为nowTime
对象。 -
请求域特点:
-
数据仅在当前请求生命周期内有效(如转发到JSP或另一个Servlet时仍可访问)。
-
若直接返回响应(非转发),则请求域数据在响应完成后自动销毁。
-
③. 从请求域中取出时间
Object sysTime = request.getAttribute("sysTime");
-
作用 :通过键
"sysTime"
从请求域中获取存储的对象(此处为Date
类型)。 -
注意 :返回类型是
Object
,需根据实际类型强制转换(如Date sysTime = (Date) request.getAttribute("sysTime");
)。
④. 输出到浏览器
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("系统当前时间是:" + sysTime);
-
作用:
-
response.setContentType("text/html")
:设置响应内容类型为HTML(告知浏览器按HTML解析)。 -
response.getWriter()
:获取响应输出流PrintWriter
对象。 -
out.print(...)
:将字符串"系统当前时间是:+ sysTime"
写入响应体,发送给浏览器。
-
-
实际效果 :浏览器会显示类似
系统当前时间是:Mon Jul 01 14:30:45 CST 2024
的内容(Date
默认调用toString()
方法)。
⑤完整流程图示
客户端发起请求 → Servlet获取当前时间 → 存入请求域 → 取出数据 → 通过响应输出到浏览器
⑥关键细节
-
请求域 vs. 其他作用域:
-
请求域(Request):数据仅在一次请求中有效(适用于转发场景)。
-
会话域(Session):数据在一次会话中有效(如用户登录状态)。
-
应用域(Application):数据在整个Web应用生命周期中有效(全局共享)。
-
-
直接响应 vs. 请求转发:
-
若直接通过
response
输出(如本例),请求域数据仅在当前Servlet中有效。 -
若使用
RequestDispatcher
转发请求(如request.getRequestDispatcher("page.jsp").forward(request, response);
),则目标页面(JSP/Servlet)仍可访问请求域数据。
-
-
潜在问题:
-
未处理字符编码 :中文字符可能乱码,需添加
response.setCharacterEncoding("UTF-8")
。 -
未关闭流 :
PrintWriter
通常无需手动关闭,容器会自动处理。
-
⑦示例改进建议
// 设置响应编码防止中文乱码
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 强制类型转换(更安全)
Date sysTime = (Date) request.getAttribute("sysTime");
out.print("系统当前时间是:" + sysTime);
⑧典型应用场景
-
Servlet到JSP的数据传递:
- Servlet处理业务逻辑并存储结果到请求域 → 转发到JSP渲染页面。
-
中间件数据传递:
- 在过滤器(Filter)或拦截器中修改请求域数据,供后续Servlet使用。
-
API返回动态数据:
- 直接通过响应输出动态生成的内容(如实时时间、计算结果)。
⑨总结
这段代码的核心目的是通过请求域传递服务器时间,并将结果输出到浏览器,展示了Java Web开发中请求域的基本使用和响应操作。理解请求域的生命周期和与其他作用域的区别,是掌握Java Web数据传递的关键。
7.跳转

BServlet 类
package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 从请求域当中取出绑定的数据
Object sysTime = request.getAttribute("sysTime");
//输出到浏览器
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("系统当前时间是:" + sysTime);
}
}
AServlet类
package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取系统当前时间
Date nowTime = new Date();
// 将系统当前时间绑定到请求域当中
request.setAttribute("sysTime", nowTime);
}}


出现 sysTime
为 null
的原因是 AServlet 和 BServlet 不在同一个请求链中,导致 BServlet 无法访问 AServlet 设置的请求域数据。以下是详细分析和解决方案:
⑴.问题原因
请求域的生命周期:
请求域(
request
对象)的数据仅在当前请求范围内有效。如果直接通过浏览器访问
BServlet
(例如输入http://localhost:8080/app/BServlet
),则BServlet
处理的是一个全新的请求 ,其请求域中自然没有sysTime
数据。未触发数据传递:
- 若未通过**请求转发(Forward)或 包含(Include)**将请求从
AServlet
传递到BServlet
,则两个 Servlet 的request
对象是独立的。
⑵.代码示例分析
代码结构:
// AServlet(未完整)
public class AServlet extends HttpServlet {
protected void doGet(...) {
Date nowTime = new Date();
request.setAttribute("sysTime", nowTime); // 存入请求域
// 缺少转发到 BServlet 的代码!
}
}
// BServlet
public class BServlet extends HttpServlet {
protected void doGet(...) {
Object sysTime = request.getAttribute("sysTime"); // 新请求中取不到值
out.print("系统当前时间是:" + sysTime); // 输出 null
}
}
执行流程:
浏览器直接访问 BServlet → 创建新请求 → 请求域无数据 → sysTime 为 null
⑶.思考
// 这样做可以吗?
// 在AServlet当中new一个BServlet对象,然后调用BServlet对象的doGet方法,把request对象传过去。
// 这个代码虽然可以实现功能,但是Servlet对象不能自己由程序员来new。自己new的Servlet对象生命周期不受Tomcat服务器的管理。
/*BServlet bServlet = new BServlet();
bServlet.doGet(request, response);*/
⑷怎么转发?代码怎么写?

执行流程:
浏览器访问 AServlet → AServlet 设置请求域 → 转发到 BServlet
→ BServlet 读取同一请求域 → 输出时间
第一步:获取请求转发器对象
RequestDispatcher dispatcher = request.getRequestDispatcher("/b");
作用 :
这行代码就像你打电话给前台说:"帮我叫一下隔壁办公室的小王过来处理这个任务!"
request
:当前用户的请求(比如用户访问了一个网页)。/b
:告诉服务器要找的目标资源(比如一个Servlet或JSP页面,路径以/
开头表示从Web应用的根目录开始)。RequestDispatcher
:就是一个"传话筒"或"调度员",它负责找到目标资源(小王),并把当前的任务(请求)交给它。
第二步:调用转发方法完成跳转
dispatcher.forward(request, response);
作用 :
这行代码就像前台把用户交给小王处理,但用户全程以为自己一直在和你(当前组件)打交道。
forward()
:让服务器内部把当前请求和响应的"接力棒"传递给目标资源(/b
)。request
和response
:当前用户的请求和响应对象被原封不动地传递给目标资源,目标资源可以继续处理这个请求,并生成最终的响应结果。
整体效果
-
服务器内部协作:
- 你(当前组件)把任务转交给小王(目标组件),但用户完全不知道这个过程。
- 浏览器的地址栏不会变(还是原来的URL),用户感觉就像在和一个页面互动。
-
数据共享:
- 如果你在转发前在请求中存了数据(比如
request.setAttribute("name", "Alice")
),目标资源(/b
)可以直接读取这些数据。 - 这就像你和小王共享同一个笔记本,写在笔记本上的内容彼此都能看到。
- 如果你在转发前在请求中存了数据(比如
-
与"重定向"的区别:
- 如果用"重定向"(
response.sendRedirect("/b")
),浏览器会收到一个"跳转指令",必须重新发送请求到/b
,地址栏会变成/b
的URL。 - 而
forward()
是服务器内部跳转,只发生一次请求,地址栏保持原URL。
- 如果用"重定向"(
举个栗子
假设你是一个外卖平台的Servlet,用户点了一个披萨:
- 用户访问你的Servlet(
/order
),你发现需要计算配送费。 - 你通过
RequestDispatcher
找到"配送费计算器"组件(/calculator
),并调用forward()
将请求转交给它。 - "配送费计算器"计算完成后,把结果返回给用户,但用户全程看到的URL还是
/order
,以为是同一个页面在处理。
关键点总结
RequestDispatcher
:是服务器内部的"调度员",负责传递请求。forward()
:服务器端跳转,共享请求和响应对象,地址栏不变。- 适用场景:需要多个组件协作处理请求,且希望用户感觉是"一个页面"在工作(比如表单处理后跳转到结果页)。

代码示例演示:
AServlet类
package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取系统当前时间
Date nowTime = new Date();
// 将系统当前时间绑定到请求域当中
request.setAttribute("sysTime", nowTime);
// 一行代码搞定转发。
request.getRequestDispatcher("/b").forward(request, response);
}
}
BServlet类
package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 从请求域当中取出绑定的数据
Object sysTime = request.getAttribute("sysTime");
//输出到浏览器
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("系统当前时间是:" + sysTime);
}
}

8.两个Servlet怎么共享数据?
-
将数据放到ServletContext应用域当中,当然是可以的,但是应用域范围太大,占用资源太多。不建议使用。
-
可以将数据放到request域当中,然后AServlet转发到BServlet,保证AServlet和BServlet在同一次请求当中,这样就可以做到两个Servlet,或者多个Servlet共享同一份数据。
9.转发的下一个资源必须是一个Servlet吗?
-
不一定,只要是Tomcat服务器当中的合法资源,都是可以转发的。例如:html....
-
注意:转发的时候,路径的写法要注意,转发的路径以"/"开始,不加项目名。

AServlet类
package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取系统当前时间
Date nowTime = new Date();
// 将系统当前时间绑定到请求域当中
request.setAttribute("sysTime", nowTime);
// 转发到一个Servlet,也可以转发到一个HTML,只要是WEB容器当中的合法资源即可。
request.getRequestDispatcher("/test.html").forward(request, response);
}
}
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test html</title>
</head>
<body>
<h1>test html page</h1>
</body>
</html>

10.关于request对象中两个非常容易混淆的方法:

11.HttpServletRequest接口的其他常用方法:


package oop1;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class RequestTestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取客户端的IP地址
String remoteAddr = request.getRemoteAddr();
System.out.println("客户端的IP地址:" + remoteAddr);
// 这个方法使用比较多。(动态获取应用的根路径。)
String contextPath = request.getContextPath();
System.out.println("应用的根路径:" + contextPath);
// 获取请求方式
String method = request.getMethod();
System.out.println(method); // GET
// 获取请求的URI
String requestURI = request.getRequestURI();
System.out.println(requestURI); // /aaa/testRequest
// 获取servlet路径
String servletPath = request.getServletPath();
System.out.println(servletPath);
}
}


二、使用纯Servlet做一个单表的CRUD操作
1.使用纯粹的Servlet完成单表【对部门的】的增删改查操作。(B/S结构的。)
实现步骤
-
第一步:准备一张数据库表。(sql脚本)
部门表
drop table if exists dept;
create table dept(
deptno int primary key,
dname varchar(255),
loc varchar(255)
);
insert into dept(deptno, dname, loc) values(10, 'XiaoShouBu', 'BEIJING');
insert into dept(deptno, dname, loc) values(20, 'YanFaBu', 'SHANGHAI');
insert into dept(deptno, dname, loc) values(30, 'JiShuBu', 'GUANGZHOU');
insert into dept(deptno, dname, loc) values(40, 'MeiTiBu', 'SHENZHEN');
commit;
select * from dept; -
第二步:准备一套HTML页面(项目原型)【前端开发工具使用HBuilder】
-
把HTML页面准备好
-
然后将HTML页面中的链接都能够跑通。(页面流转没问题。)
-
应该设计哪些页面呢?
-
欢迎页面:index.html
-
列表页面:list.html(以列表页面为核心,展开其他操作。)
-
新增页面:add.html
-
修改页面:edit.html
-
详情页面:detail.html
-
-
列表页面 :list.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>部门列表页面</title>
<meta name="viewport"
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<style>
table {
border-collapse: collapse;
width: 80%;
margin: 0 auto;
}
th,
td {
border: 1px solid #ccc;
padding: 8px;
text-align: center;
}
</style>
</head>
<body>
<h1>部门列表</h1>
<hr />
<table>
<tr>
<th>序号</th>
<th>部门编号</th>
<th>部门名称</th>
<th>操作</th>
</tr>
<tr>
<td>1</td>
<td>10</td>
<td>销售部</td>
<td>
<a href="">删除</a>
<a href="edit.html">修改</a>
<a href="detail.html">详情</a>
</td>
</tr>
<tr>
<td>2</td>
<td>20</td>
<td>研发部</td>
<td>
<a href="">删除</a>
<a href="edit.html">修改</a>
<a href="detail.html">详情</a>
</td>
</tr>
<tr>
<td>3</td>
<td>30</td>
<td>运营部</td>
<td>
<a href="">删除</a>
<a href="edit.html">修改</a>
<a href="detail.html">详情</a>
</td>
</tr>
</table>
<hr />
<a href="add.html">新增部门</a>
</body>
</html>

欢迎页面:index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>欢迎使用oa系统</title>
</head>
<body>
<a href="list.html">查看列表详情</a>
</body>
</html>

-
新增页面:add.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>新增部门</title> </head> <body>新增部门
<form action="list.html" method="get"> 部门编号
部门名称
部门位置
</form> </body> </html>

-
修改页面:edit.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>修改部门</title> </head> <body>修改部门
<form action="list.html" method="get"> 部门编号
部门名称
部门位置
</form> </body> </html>

-
详情页面:detail.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>部门详情</title> </head> <body>部门详情
部门编号:20
部门名称:销售部
部门位置:北京
</body> </html>

-
第三步:分析我们这个系统包括哪些功能?
-
什么叫做一个功能呢?
- 只要 这个操作连接了数据库,就表示一个独立的功能。
-
包括哪些功能?
-
查看部门列表
-
新增部门
-
删除部门
-
查看部门详细信息
-
跳转到修改页面
-
修改部门
-
-
-
第四步:在IDEA当中搭建开发环境
-
创建一个webapp(给这个webapp添加servlet-api.jar和jsp-api.jar到classpath当中。)
-
向webapp中添加连接数据库的jar包(mysql驱动)
- 必须在WEB-INF目录下新建lib目录,然后将mysql的驱动jar包拷贝到这个lib目录下。这个目录名必须叫做lib,全部小写的。
-

-
- JDBC的工具类

jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
user=root
password=abc123
DBUtil工具类
package utils;
import java.sql.*;
import java.util.ResourceBundle;
/**
* JDBC的工具类
*/
public class DBUtil {
// 静态变量:在类加载时执行。
// 并且是有顺序的。自上而下的顺序。
// 属性资源文件绑定
private static ResourceBundle bundle = ResourceBundle.getBundle("resources.jdbc");
// 根据属性配置文件key获取value
private static String driver = bundle.getString("driver");
private static String url = bundle.getString("url");
private static String user = bundle.getString("user");
private static String password = bundle.getString("password");
static {
// 注册驱动(注册驱动只需要注册一次,放在静态代码块当中。DBUtil类加载的时候执行。)
try {
// "com.mysql.jdbc.Driver" 是连接数据库的驱动,不能写死。因为以后可能还会连接Oracle数据库。
// 如果连接oracle数据库的时候,还需要修改java代码,显然违背了OCP开闭原则。
// OCP开闭原则:对扩展开放,对修改关闭。(什么是符合OCP呢?在进行功能扩展的时候,不需要修改java源代码。)
//Class.forName("com.mysql.jdbc.Driver");
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接对象
* @return conn 连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
// 获取连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
/**
* 释放资源
* @param conn 连接对象
* @param ps 数据库操作对象
* @param rs 结果集对象
*/
public static void close(Connection conn, Statement ps, ResultSet rs){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
-
- 将所有HTML页面拷贝到web目录下。
-
第五步:实现第一个功能:查看部门列表
-
我们应该怎么去实现一个功能呢?
- 建议:你可以从后端往前端一步一步写。也可以从前端一步一步往后端写。都可以。但是千万要记住不要想起来什么写什么。你写代码的过程最好是程序的执行过程。也就是说:程序执行到哪里,你就写哪里。这样一个顺序流下来之后,基本上不会出现什么错误、意外。
-
2.从哪里开始?
-
-
- 假设从前端开始,那么一定是从用户点击按钮那里开始的。
-
-
第一:先修改前端页面的超链接,因为用户先点击的就是这个超链接。
第二:编写web.xml文件

第三:编写DeptListServlet类继承HttpServlet类。然后重写doGet方法。
package com.bjpowernode.oa.web.action;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public class DeptListServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
第四:在DeptListServlet类的doGet方法中连接数据库,查询所有的部门,动态的展示部门列表页面.
-
分析list.html页面中哪部分是固定死的,哪部分是需要动态展示的。
-
list.html页面中的内容所有的双引号要替换成单引号,因为out.print("")这里有一个双引号,容易冲突。
-
现在写完这个功能之后,你会有一种感觉,感觉开发很繁琐,只使用servlet写代码太繁琐了。
while(rs.next()){
String deptno = rs.getString("a");
String dname = rs.getString("dname");
String loc = rs.getString("loc");out.print(" <tr>"); out.print(" <td>"+(++i)+"</td>"); out.print(" <td>"+deptno+"</td>"); out.print(" <td>"+dname+"</td>"); out.print(" <td>"); out.print(" <a href=''>删除</a>"); out.print(" <a href='edit.html'>修改</a>"); out.print(" <a href='detail.html'>详情</a>"); out.print(" </td>"); out.print(" </tr>");
}
第五步:查看部门详情。
-
建议:从前端往后端一步一步实现。首先要考虑的是,用户点击的是什么?用户点击的东西在哪里?
-
一定要先找到用户点的"详情"在哪里。找了半天,终于在后端的java程序中找到了
-
-
-
详情 是需要连接数据库的,所以这个超链接点击之后也是需要执行一段java代码的。所以要将这个超链接的路径修改一下。
-
注意:修改路径之后,这个路径是需要加项目名的。"/oa/dept/detail"
-
-
技巧:
out.print("详情");
-
重点:向服务器提交数据的格式:uri?name=value&name=value&name=value&name=value
-
这里的问号,必须是英文的问号。不能中文的问号。
解决404的问题。写web.xml文件。
<servlet>
<servlet-name>detail</servlet-name>
<servlet-class>oop1.web.action.DeptDetailServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>detail</servlet-name>
<url-pattern>/dept/detail</url-pattern>
</servlet-mapping>
编写一个类:DeptDetailServlet继承HttpServlet,重写doGet方法。

-
- 在doGet方法当中:连接数据库,根据部门编号查询该部门的信息。动态展示部门详情页。
-
第六步:删除部门
-
怎么开始?从哪里开始?从前端页面开始,用户点击删除按钮的时候,应该提示用户是否删除。因为删除这个动作是比较危险的。任何系统在进行删除操作之前,是必须要提示用户的,因为这个删除的动作有可能是用户误操作。(在前端页面上写JS代码,来提示用户是否删除。)
<script type="text/javascript"> function del(dno){ if(window.confirm("亲,删了不可恢复哦!")){ document.location.href = "/oa/dept/delete?deptno=" + dno; } } </script>
-
-
以上的前端程序要写到后端的java代码当中:
- DeptListServlet类的doGet方法当中,使用out.print()方法,将以上的前端代码输出到浏览器上。
-
解决404的问题:
-
web.xml文件
<servlet> <servlet-name>delete</servlet-name> <servlet-class>oop1.web.action.DeptDelServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>delete</servlet-name> <url-pattern>/dept/delete</url-pattern> </servlet-mapping>
-
编写DeptDelServlet继承HttpServlet,重写doGet方法。

删除成功或者失败的时候的一个处理(这里我们选择了转发,并没有使用重定向机制。)

-
第七步:新增部门
-
注意:最后保存成功之后,转发到 /dept/list 的时候,会出现405,为什么?
-
第一:保存用的是post请求。底层要执行doPost方法。
-
第二:转发是一次请求,之前是post,之后还是post,因为它是一次请求。
-
第三:/dept/list Servlet当中只有一个doGet方法。
-
怎么解决?两种方案
-
第一种:在/dept/list Servlet中添加doPost方法,然后在doPost方法中调用doGet。
-
第二种:重定向。
-
-
-
-
第八步:跳转到修改部门的页面
-
第九步:修改部门
3.全部代码示例:

DBUtil、jdbc.properties上面有了
⑴ DeptListServlet类
package oop1.web.action;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import oop1.utils.DBUtil;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DeptListServlet extends HttpServlet {
// 处理post请求
/*@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//根据数据库动态输出内容
// 获取应用的根路径
String contextPath = request.getContextPath();
// 设置响应的内容类型以及字符集。防止中文乱码问题。
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("<!DOCTYPE html>");
out.print("<html>");
out.print(" <head>");
out.print(" <meta charset='utf-8'>");
out.print(" <title>部门列表页面</title>");
out.print("<script type='text/javascript'>");
out.print(" function del(dno){");
out.print(" if(window.confirm('亲,删了不可恢复哦!')){");
out.print(" document.location.href = '"+contextPath+"/dept/delete?deptno=' + dno");
out.print(" }");
out.print(" }");
out.print("</script>");
out.print(" </head>");
out.print(" <body>");
out.print(" <h1 align='center'>部门列表</h1>");
out.print(" <hr >");
out.print(" <table border='1px' align='center' width='50%'>");
out.print(" <tr>");
out.print(" <th>序号</th>");
out.print(" <th>部门编号</th>");
out.print(" <th>部门名称</th>");
out.print(" <th>操作</th>");
out.print(" </tr>");
/*上面一部分是死的*/
// 连接数据库,查询所有的部门
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 获取连接
conn = DBUtil.getConnection();
// 获取预编译的数据库操作对象
String sql = "select deptno as a,dname,loc from dept";
ps = conn.prepareStatement(sql);
// 执行SQL语句
rs = ps.executeQuery();
// 处理结果集
int i = 0;
while(rs.next()){
String deptno = rs.getString("a");
String dname = rs.getString("dname");
String loc = rs.getString("loc");
out.print(" <tr>");
out.print(" <td>"+(++i)+"</td>");
out.print(" <td>"+deptno+"</td>");
out.print(" <td>"+dname+"</td>");
out.print(" <td>");
out.print(" <a href='javascript:void(0)' onclick='del("+deptno+")'>删除</a>");
out.print(" <a href='"+contextPath+"/dept/edit?deptno="+deptno+"'>修改</a>");
out.print(" <a href='"+contextPath+"/dept/detail?fdsafdsas="+deptno+"'>详情</a>");
out.print(" </td>");
out.print(" </tr>");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放资源
DBUtil.close(conn, ps, rs);
}
/*下面一部分是死的*/
out.print(" </table>");
out.print(" <hr >");
out.print(" <a href='"+contextPath+"/add.html'>新增部门</a><br/>");
out.print(contextPath);
out.print(" </body>");
out.print("</html>");
}
}
⑵DeptDetailServlet类
package oop1.web.action;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import oop1.utils.DBUtil;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DeptDetailServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("<!DOCTYPE html>");
out.print("<html>");
out.print(" <head>");
out.print(" <meta charset='utf-8'>");
out.print(" <title>部门详情</title>");
out.print(" </head>");
out.print(" <body>");
out.print(" <h1>部门详情</h1>");
out.print(" <hr >");
// 获取部门编号
// /oa/dept/detail?fdsafdsas=30
// 虽然是提交的30,但是服务器获取的是"30"这个字符串。
String deptno = request.getParameter("fdsafdsas");
// 连接数据库,根据部门编号查询部门信息。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "select dname,loc from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
rs = ps.executeQuery();
// 这个结果集一定只有一条记录。
if(rs.next()){
String dname = rs.getString("dname");
String loc = rs.getString("loc");
out.print("部门编号:"+deptno+" <br>");
out.print("部门名称:"+dname+"<br>");
out.print("部门位置:"+loc+"<br>");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
out.print(" <input type='button' value='后退' onclick='window.history.back()'/>");
out.print(" </body>");
out.print("</html>");
}
}
⑶ DeptEditServlet 类
package oop1.web.action;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import oop1.utils.DBUtil;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DeptEditServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取应用的根路径。
String contextPath = request.getContextPath();
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("<!DOCTYPE html>");
out.print("<html>");
out.print(" <head>");
out.print(" <meta charset='utf-8'>");
out.print(" <title>修改部门</title>");
out.print(" </head>");
out.print(" <body>");
out.print(" <h1>修改部门</h1>");
out.print(" <hr >");
out.print(" <form action='"+contextPath+"/dept/modify' method='post'>");
// 获取部门编号
String deptno = request.getParameter("deptno");
// 连接数据库,根据部门编号查询部门的信息。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "select dname, loc as location from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
rs = ps.executeQuery();
// 这个结果集中只有一条记录。
if(rs.next()){
String dname = rs.getString("dname");
String location = rs.getString("location"); // 参数"location"是sql语句查询结果列的列名。
// 输出动态网页。
out.print(" 部门编号<input type='text' name='deptno' value='"+deptno+"' readonly /><br>");
out.print(" 部门名称<input type='text' name='dname' value='"+dname+"'/><br>");
out.print(" 部门位置<input type='text' name='loc' value='"+location+"'/><br>");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
out.print(" <input type='submit' value='修改'/><br>");
out.print(" </form>");
out.print(" </body>");
out.print("</html>");
}
}
⑷DeptDelServlet类
package oop1.web.action;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import oop1.utils.DBUtil;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DeptDelServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 根据部门编号,删除部门。
// 获取部门编号
String deptno = request.getParameter("deptno");
// 连接数据库删除数据
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
// 开启事务(自动提交机制关闭)
conn.setAutoCommit(false);
String sql = "delete from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
// 返回值是:影响了数据库表当中多少条记录。
count = ps.executeUpdate();
// 事务提交
conn.commit();
} catch (SQLException e) {
// 遇到异常要回滚
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
// 判断删除成功了还是失败了。
if (count == 1) {
//删除成功
//仍然跳转到部门列表页面
//部门列表页面的显示需要执行另一个Servlet。怎么办?转发。
//request.getRequestDispatcher("/dept/list").forward(request, response);
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 删除失败
//request.getRequestDispatcher("/error.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
}
⑸DeptModifyServlet类
package oop1.web.action;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import oop1.utils.DBUtil;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DeptModifyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 解决请求体的中文乱码问题。
request.setCharacterEncoding("UTF-8");
// 获取表单中的数据
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 连接数据库执行更新语句
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "update dept set dname = ?, loc = ? where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, dname);
ps.setString(2, loc);
ps.setString(3, deptno);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
if (count == 1) {
// 更新成功
// 跳转到部门列表页面(部门列表页面是通过Java程序动态生成的,所以还需要再次执行另一个Servlet)
//request.getRequestDispatcher("/dept/list").forward(request, response);
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 更新失败
//request.getRequestDispatcher("/error.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
}
⑹DeptSaveServlet类
package oop1.web.action;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import oop1.utils.DBUtil;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DeptSaveServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取部门的信息
// 注意乱码问题(Tomcat10不会出现这个问题)
request.setCharacterEncoding("UTF-8");
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 连接数据库执行insert语句
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "insert into dept(deptno, dname, loc) values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
ps.setString(2, dname);
ps.setString(3, loc);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
if (count == 1) {
// 保存成功跳转到列表页面
// 转发是一次请求。
//request.getRequestDispatcher("/dept/list").forward(request, response);
// 这里最好使用重定向(浏览器会发一次全新的请求。)
// 浏览器在地址栏上发送请求,这个请求是get请求。
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 保存失败跳转到错误页面
//request.getRequestDispatcher("/error.html").forward(request, response);
// 这里也建议使用重定向。
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
}
add.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>新增部门</title>
</head>
<body>
<h1>新增部门</h1>
<hr >
<form action="/oa/dept/save" method="post">
部门编号<input type="text" name="deptno"/><br>
部门名称<input type="text" name="dname"/><br>
部门位置<input type="text" name="loc"/><br>
<input type="submit" value="保存"/><br>
</form>
</body>
</html>
edit.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>修改部门</title>
</head>
<body>
<h1>修改部门</h1>
<hr >
<form action="list.html" method="get">
部门编号<input type="text" name="deptno" value="20" readonly /><br>
部门名称<input type="text" name="dname" value="销售部"/><br>
部门位置<input type="text" name="loc" value="北京"/><br>
<input type="submit" value="修改"/><br>
</form>
</body>
</html>
error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>error</title>
</head>
<body>
<h1>操作失败,<a href="javascript:void(0)" onclick="window.history.back()">返回</a></h1>
</body>
</html>
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>欢迎使用OA系统</title>
</head>
<body>
<!--前端超链接发送请求的时候,请求路径以"/"开始,并且要带着项目名-->
<a href="/oa/dept/list">查看部门列表</a>
</body>
</html>
4.如何串起来


以下是用户点击部门详情链接后的具体流程及代码执行步骤:
⑴ 用户点击详情链接
用户在前端页面点击如下链接:
<a href="/项目名/dept/detail?fdsafdsas=40">详情</a>
- 触发操作 :浏览器发送 GET 请求 到 URL
/项目名/dept/detail
,并携带参数fdsafdsas=30
。
⑵ Servlet 匹配与请求处理
-
Servlet 映射 :根据
web.xml
或注解配置,URL/dept/detail
映射到DeptDetailServlet
。 -
调用
doGet
方法:


protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// 处理请求逻辑
}
⑶ 获取请求参数
-
参数提取:
String deptno = request.getParameter("fdsafdsas"); // 获取参数值 "40"
-
注意:参数名
fdsafdsas
是随意命名的,实际应为deptno
(代码中存在命名问题)。
⑷ 连接数据库查询部门详情
-
执行 SQL 查询:
Connection conn = DBUtil.getConnection();
String sql = "select dname, loc from dept where deptno = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, deptno); // 参数值 "40"
ResultSet rs = ps.executeQuery(); -
数据库操作 :查询
deptno=30
的部门名称(dname
)和位置(loc
)。
⑸动态生成 HTML 响应
-
若查询到结果:
if (rs.next()) {
String dname = rs.getString("dname"); // 部门名称
String loc = rs.getString("loc"); // 部门位置
out.print("部门编号:" + deptno + "
");
out.print("部门名称:" + dname + "
");
out.print("部门位置:" + loc + "
");
}
输出示例:

-
若未查询到结果:
- 当前代码未处理空结果,页面会显示空白(需改进)。
⑹释放数据库资源
-
关闭连接:
DBUtil.close(conn, ps, rs); // 释放数据库连接、语句和结果集
⑺总结流程
- 用户点击链接 → 2. 发送 GET 请求 → 3. Servlet 处理参数 → 4. 查询数据库 → 5. 生成动态页面 → 6. 返回响应 → 7. 用户查看详情。
- 关键点 :所有逻辑由
DeptDetailServlet
的doGet()
方法驱动,数据库查询结果直接决定页面内容。
5.为什么写在Doget方法里
在Servlet中,将逻辑写在 doGet()
方法中的原因主要有以下几点:
⑴. HTTP请求方法的语义匹配
-
GET请求的特点:
-
用于获取数据(如加载页面、查询信息)。
-
对服务器资源是只读操作,不会修改数据。
-
请求参数通过URL传递(如
/dept/list?page=1
)。
-
-
POST请求的特点:
-
用于提交数据(如表单提交、删除操作)。
-
可能修改服务器资源。
-
请求参数通过请求体传递,不可见且更安全。
-
示例代码分析:
-
DeptListServlet
的doGet()
用于生成部门列表页面(数据展示),符合GET的语义。 -
DeptEditServlet
的doGet()
用于加载编辑表单页面(展示待修改的数据)。 -
DeptDelServlet
的doGet()
处理删除操作,这是不安全的(应使用POST或DELETE方法)。
⑵. 前端操作触发的方式
-
通过超链接或直接访问URL :
例如,点击
<a href="/dept/list">部门列表</a>
会发送GET请求,触发doGet()
。 -
通过表单提交(默认GET) :
如果表单未指定
method="post"
,提交时会发送GET请求。
代码中的体现:
-
部门列表、编辑页面、详情页面均通过链接访问,因此使用
doGet()
。 -
新增部门(
DeptSaveServlet
)和修改部门(DeptModifyServlet
)通过表单提交,使用doPost()
。
⑶. 代码结构与规范
-
职责分离:
-
doGet()
处理页面渲染和数据查询。 -
doPost()
处理数据修改(增删改)。
-
-
安全性:
-
将敏感操作(如删除)放在
doPost()
中,可减少CSRF攻击风险。 -
用户提供的代码中,删除操作错误地使用了
doGet()
(见DeptDelServlet
),需改进为doPost()
。
-
⑷. 用户实际场景举例
-
场景1:查看部门列表
用户访问
/dept/list
→ 浏览器发送GET请求 →DeptListServlet
的doGet()
执行 → 返回HTML页面。 -
场景2:删除部门
用户点击删除链接 → 触发JavaScript跳转 → 发送GET请求到
/dept/delete?deptno=30
→DeptDelServlet
的doGet()
执行 → 删除数据。
问题:删除操作本应通过POST请求发送,此处设计不安全。
⑸总结
-
写在
doGet()
中的原因:-
匹配GET请求的语义(数据展示、无副作用)。
-
响应前端通过链接或URL直接触发的操作。
-
-
需注意的问题:
-
避免在
doGet()
中执行敏感操作(如删除),应改用doPost()
或其他安全方法。 -
遵循RESTful设计原则,使代码更规范、安全。
-
三、在一个web应用中应该如何完成资源的跳转
1.在一个web应用中通过两种方式,可以完成资源的跳转:
-
第一种方式:转发
-
第二种方式:重定向
2.转发和重定向有什么区别?
⑴代码上有什么区别?
转发

重定向

⑵形式上有什么区别?
-
-
转发(一次请求)
- 在浏览器地址栏上发送的请求是:http://localhost:8080/servlet10/a ,最终请求结束之后,浏览器地址栏上的地址还是这个。没变。
-
重定向(两次请求)
- 在浏览器地址栏上发送的请求是:http://localhost:8080/servlet10/a ,最终在浏览器地址栏上显示的地址是:http://localhost:8080/servlet10/b
-
-
转发和重定向的本质区别?
-
转发:是由WEB服务器来控制的。A资源跳转到B资源,这个跳转动作是Tomcat服务器内部完成的。
-
重定向:是浏览器完成的。具体跳转到哪个资源,是浏览器说了算。
-
-
使用一个例子去描述这个转发和重定向
-
借钱(转发:发送了一次请求)
- 杜老师没钱了,找张三借钱,其实张三没有钱,但是张三够义气,张三自己找李四借了钱,然后张三把这个钱给了杜老师,杜老师不知道这个钱是李四的,杜老师只求了一个人。杜老师以为这个钱就是张三的。
-
借钱(重定向:发送了两次请求)
- 杜老师没钱了,找张三借钱,张三没有钱,张三有一个好哥们,叫李四,李四是个富二代,于是张三将李四的家庭住址告诉了杜老师,杜老师按照这个地址去找到李四,然后从李四那里借了钱。显然杜老师在这个过程中,求了两个人。并且杜老师知道最终这个钱是李四借给俺的。
-
-
转发和重定向应该如何选择?什么时候使用转发,什么时候使用重定向?
-
如果在上一个Servlet当中向request域当中绑定了数据,希望从下一个Servlet当中把request域里面的数据取出来,使用转发机制。
-
剩下所有的请求均使用重定向。(重定向使用较多。)
-
-
跳转的下一个资源有没有要求呢?必须是一个Servlet吗?
- 不一定,跳转的资源只要是服务器内部合法的资源即可。包括:Servlet、JSP、HTML.....
-
转发会存在浏览器的刷新问题。