Servlet
一:什么是servlet:
Servlet是Java编写的服务器端程序,用于处理客户端请求并生成响应。Servlet通常运行在支持Java的Web服务器上,如Tomcat、Jetty等。Servlet可以与数据库交互、处理表单数据、生成动态内容等。通过Servlet,开发人员可以编写灵活、高效的服务器端程序来实现各种Web应用程序。
二:流程介绍:
2.1.开发一个web类型的module。
2.2. 开发一个UserServlet:
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.service(req, resp);
String username = req.getParameter("username");
String info = "<h1>YES</h1>";
if("Shelly".equals(username)){
info = "NO";
}
resp.getWriter().println(info);
}
}
- 自定义一个类,要继承HttpServlet类
- 重写service方法,该方法主要就是用于处理用户请求的服务方法
- HttpServletRequest 代表请求对象,是有请求报文经过tomcat转换而来的,通过该对象可以获取请求中的信息
- HttpServletResponse 代表响应对象,该对象会被tomcat转换为响应的报文,通过该对象可以设置响应中的信息
- Servlet对象的生命周期(创建,初始化,处理服务,销毁)是由tomcat管理的,无需我们自己new
- HttpServletRequest HttpServletResponse 两个对象也是有tomcat负责转换,在调用service方法时传入给我们用的
2.3.在web.xml为UseServlet配置请求的映射路径:
<servlet>
<servlet-name>userServlet</servlet-name>
<servlet-class>servlet.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>userServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>
- Servlet并不是文件系统中实际存在的文件或者目录,所以为了能够请求到该资源,我们需要为其配置映射路径
- servlet的请求映射路径配置在web.xml中
- servlet-name作为servlet的别名,可以自己随意定义,见名知意就好
- url-pattern标签用于定义Servlet的请求映射路径
- 一个servlet可以对应多个不同的url-pattern
- 多个servlet不能使用相同的url-pattern
- url-pattern中可以使用一些通配写法
- / 表示通配所有资源,不包括jsp文件
- /* 表示通配所有资源,包括jsp文件
- /a/* 匹配所有以a前缀的映射路径
- *.action 匹配所有以action为后缀的映射路径
2.4.开发一个form表单,向servlet发送一个get请求并携带username参数:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="get" action="userServlet">
用户名:<input type="text" name="username"><br>
<input type="submit" value="校验">
</form>
</body>
</html>
注意,这里表单两种提交方式均可,均是键值对的形式,重要的是,action必须与前面的url-pattern对应的上。
在这段HTML代码中,<form>
元素中的action
属性指定了表单数据提交的目标URL。具体来说,action="userServlet"
表示当用户提交表单时,表单数据将被发送到名为userServlet
的URL路径。
与之前的Servlet配置相关联,如果在web.xml文件中已经配置了Servlet,并且将其映射到了/user
路径,那么action="userServlet"
将与这个Servlet映射关系有关。当用户提交表单时,数据将被发送到/user
路径,容器会将请求交给该Servlet来处理。
注意,在2.1中,项目添加依赖有两种方式,第一种是在项目结构中添加,第二种是导入外部jar包(servlet-api-jar),在实际运用中,我们一般使用第1种方法,第二种方法在打包项目时,会携带该lib,然而在实际应用中,该lib是会自动被携带的,不需要再次携带。
Content-Type:
Tomcat会在XML自动识别请求的文件格式,并设置其Content-Type。如果不设置,默认为HTML文件。因此我们可以手动设置:
resp.setHeader("Content-Type","text/html");
//或者这样设置:
resp.setContentType("text/html");
举例:
<servlet>
<servlet-name>kk</servlet-name>
<servlet-class>servlet.UserServlet</servlet-class>
</servlet>
解释一下每个部分的含义:
<servlet>
: 这是Servlet配置的开始标签,表示要定义一个Servlet。<servlet-name>
: 这个标签定义了Servlet的名称,这里是kk
。<servlet-class>
: 这个标签定义了Servlet类的完整路径,这里是servlet.UserServlet
,表示UserServlet
类在servlet
包下。
因此,这段配置代码的含义是在web.xml文件中定义了一个名为kk
的Servlet,它的类是servlet.UserServlet
。当这个Servlet被请求时,容器将会实例化UserServlet
类的一个对象来处理请求。
<servlet>
<servlet-name>kk</servlet-name>
<servlet-class>servlet.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>kk</servlet-name>
<url-pattern>/user</url-pattern>
</servlet-mapping>
在这个配置中,<servlet-mapping>
元素用于将名为kk
的Servlet与URL路径/user
进行映射。解释如下:
<servlet-mapping>
: 这是Servlet映射配置的开始标签。<servlet-name>
: 指定要映射的Servlet的名称,这里是kk
,与之前定义的<servlet-name>
一致。<url-pattern>
: 指定了要映射到Servlet的URL路径,这里是/user
。当用户请求路径为/user
时,容器会将请求交给名为kk
的Servlet处理。
因此,通过这个Servlet映射配置,当用户访问URL路径为/user
时,容器会调用UserServlet
类来处理该请求。这样就实现了Servlet与URL路径之间的映射关系,使得用户请求能够正确地被分发到相应的Servlet进行处理。
即在url中,先找到s1,在找到servlet别名,最后再用反射加载方法。
注意:一个servlet-name可以对应多个url-pattern,一个servlet可以对应多个mapping标签。
url-pattern模糊匹配:
- / 表示通配所有资源,不包括jsp文件
- /* 表示通配所有资源,包括jsp文件
- /a/* 匹配所有以a前缀的映射路径
- *.action 匹配所有以action为后缀的映射路径
三:注解配置:
@WebServlet("/userServlet")
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.service(req, resp);
String username = req.getParameter("username");
String info = "YES";
if("Shelly".equals(username)){
info = "NO";
}
resp.setHeader("Content-Type","text/html");
resp.getWriter().println(info);
}
}
在这种情况下,注解中的内容取决于UserServlet
类的映射路径。在@WebServlet
注解中,你需要指定UserServlet
类在Web应用中的访问路径。这个路径将与HTML表单中form标签的action
属性相对应。
在这个例子中,@WebServlet
中的路径应该与HTML表单中action
属性的值相匹配,以确保表单数据将被发送到正确的Servlet。因此,你应该在@WebServlet
中指定一个与HTML表单中action
属性相匹配的路径,例如@WebServlet("/userServlet")
。
@WebServlet(
name = "userServlet",
//value = "/user",
urlPatterns = {"/userServlet1","/userServlet2","/userServlet"},
initParams = {"@WebInitParam(name = "encoding",value = "UTF-8")},
loadOnStartup = 6
)
四:生命周期:
@WebServlet(urlPatterns = "/s",loadOnStartup = 6)
public class UserServlet extends HttpServlet {
public UserServlet(){
System.out.println("构造器");
}
@Override
public void init() throws ServletException {
System.out.println("初始化方法");
}
@Override
protected void service(HttpServletRequest reS, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("service方法");
}
@Override
public void destroy() {
System.out.println("销毁方法");
}
}
在这个注解中,重点是loadOnStartup = 6
这部分。这个参数指定了Servlet在应用程序启动时加载的顺序。当一个Servlet的loadOnStartup
值大于0时,容器会在应用程序启动时立即加载这个Servlet,而不是等到第一次请求到达时再加载。值较小的Servlet将先加载,值相同的情况下按照Servlet在web.xml中的顺序加载。这个参数对于那些需要在应用程序启动时进行初始化的Servlet非常有用,例如加载一些配置信息或者预先进行一些计算。
我们可以发现,在第一次部署之前,即输出service方法之前,就已经输出了构造器…当然,我们也可以修改XML文件来进行配置:
<!--load-on-startup 如果配置的是正整数则表示容器在启动时就要实例化Servlet, 数字表示的是实例化的顺序 -->
- 通过生命周期测试我们发现Servlet对象在容器中是单例的
- 容器是可以处理并发的用户请求的,每个请求在容器中都会开启一个线程
- 多个线程可能会使用相同的Servlet对象,所以在Servlet中,我们不要轻易定义一些容易经常发生修改的成员变量
- load-on-startup中定义的正整数表示实例化顺序,如果数字重复了,容器会自行解决实例化顺序问题,但是应该避免重复
- Tomcat容器中,已经定义了一些随系统启动实例化的servlet,我们自定义的servlet的load-on-startup尽量不要占用数字1-5
补充:
DefaultServlet
是 Apache Tomcat 中的一个内置 Servlet,用于处理静态资源的请求。当 Tomcat 收到一个请求时,如果没有匹配到其他 Servlet 处理该请求,就会交给 DefaultServlet
处理。DefaultServlet
负责返回 Web 应用程序中的静态资源,比如 HTML、CSS、JavaScript、图片等文件。
五:继承结构:
5.1 Servlet 接口
接口及方法说明
- Servlet 规范接口,所有的Servlet必须实现
- public void init(ServletConfig config) throws ServletException;
- 初始化方法,容器在构造servlet对象后,自动调用的方法,容器负责实例化一个ServletConfig对象,并在调用该方法时传入
- ServletConfig对象可以为Servlet 提供初始化参数
- public ServletConfig getServletConfig();
- 获取ServletConfig对象的方法,后续可以通过该对象获取Servlet初始化参数
- public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
- 处理请求并做出响应的服务方法,每次请求产生时由容器调用
- 容器创建一个ServletRequest对象和ServletResponse对象,容器在调用service方法时,传入这两个对象
- public String getServletInfo();
- 获取ServletInfo信息的方法
- public void destroy();
- Servlet实例在销毁之前调用的方法
- public void init(ServletConfig config) throws ServletException;
5.2 GenericServlet 抽象类
源码解释
- GenericServlet 抽象类是对Servlet接口一些固定功能的粗糙实现,以及对service方法的再次抽象声明,并定义了一些其他相关功能方法
- private transient ServletConfig config;
- 初始化配置对象作为属性
- public GenericServlet() { }
- 构造器,为了满足继承而准备
- public void destroy() { }
- 销毁方法的平庸实现
- public String getInitParameter(String name)
- 获取初始参数的快捷方法
- public Enumeration< String> getInitParameterNames()
- 返回所有初始化参数名的方法
- public ServletConfig getServletConfig()
- 获取初始Servlet初始配置对象ServletConfig的方法
- public ServletContext getServletContext()
- 获取上下文对象ServletContext的方法
- public String getServletInfo()
- 获取Servlet信息的平庸实现
- public void init(ServletConfig config) throws ServletException()
- 初始化方法的实现,并在此调用了init的重载方法
- public void init() throws ServletException
- 重载init方法,为了让我们自己定义初始化功能的方法
- public void log(String msg)
- public void log(String message, Throwable t)
- 打印日志的方法及重载
- public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
- 服务方法再次声明
- public String getServletName()
- 获取ServletName的方法
- private transient ServletConfig config;
两个init方法,并且其中有一个为空参的,且无参调用了有参的,为了方便我们在继承该类时,方便我们去重写方法,因为无参的方法重写肯定比有参的简单,而且有参的方法调用了无参的方法,因此,修改无参的方法还会参与到初始化。
5.3 HttpServlet 抽象类
解释
abstract class HttpServlet extends GenericServlet HttpServlet抽象类,除了基本的实现以外,增加了更多的基础功能
private static final String METHOD_DELETE = “DELETE”;
private static final String METHOD_HEAD = “HEAD”;
private static final String METHOD_GET = “GET”;
private static final String METHOD_OPTIONS = “OPTIONS”;
private static final String METHOD_POST = “POST”;
private static final String METHOD_PUT = “PUT”;
private static final String METHOD_TRACE = “TRACE”;
- 上述属性用于定义常见请求方式名常量值
public HttpServlet() {}
- 构造器,用于处理继承
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
- 重载的service方法,被重写的service方法所调用
- 在该方法中,通过请求方式判断,调用具体的do***方法完成请求的处理
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
- 对应不同请求方式的处理方法
- 除了doOptions和doTrace方法,其他的do*** 方法都在故意响应错误信息
5.4 自定义Servlet
- 自定义Servlet中,必须要对处理请求的方法进行重写
- 要么重写service方法
- 要么重写doGet/doPost方法
如果我们不重写方法,那么会进入到默认父类的service中去,也就是说,无论是哪一种提交方式,都会故意响应405。
六:ServletConfig和ServletContext:
6.1.ServletConfig:
ServletConfig是什么
为Servlet提供初始配置参数的一种对象,每个Servlet都有自己独立唯一的ServletConfig对象
容器会为每个Servlet实例化一个ServletConfig对象,并通过Servlet生命周期的init方法传入给Servlet作为属性
简单使用:
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = this.getServletConfig();
// 根据参数名获取单个参数
String value = servletConfig.getInitParameter("param1");
System.out.println("param1:"+value);
// 获取所有参数名
Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
// 迭代并获取参数名
while (parameterNames.hasMoreElements()) {
String paramaterName = parameterNames.nextElement();
System.out.println(paramaterName+":"+servletConfig.getInitParameter(paramaterName));
}
}
}
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = this.getServletConfig();
// 根据参数名获取单个参数
String value = servletConfig.getInitParameter("param1");
System.out.println("param1:"+value);
// 获取所有参数名
Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
// 迭代并获取参数名
while (parameterNames.hasMoreElements()) {
String paramaterName = parameterNames.nextElement();
System.out.println(paramaterName+":"+servletConfig.getInitParameter(paramaterName));
}
}
}
配置:
<servlet>
<servlet-name>ServletA</servlet-name>
<servlet-class>com.atguigu.servlet.ServletA</servlet-class>
<!--配置ServletA的初始参数-->
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>value2</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>ServletB</servlet-name>
<servlet-class>com.atguigu.servlet.ServletB</servlet-class>
<!--配置ServletB的初始参数-->
<init-param>
<param-name>param3</param-name>
<param-value>value3</param-value>
</init-param>
<init-param>
<param-name>param4</param-name>
<param-value>value4</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ServletA</servlet-name>
<url-pattern>/servletA</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletB</servlet-name>
<url-pattern>/servletB</url-pattern>
</servlet-mapping>
6.2 ServletContext的使用
ServletContext是什么
- ServletContext对象有称呼为上下文对象,或者叫应用域对象(后面统一讲解域对象)
- 容器会为每个app创建一个独立的唯一的ServletContext对象
- ServletContext对象为所有的Servlet所共享
- ServletContext可以为所有的Servlet提供初始配置参数
配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<context-param>
<param-name>paramA</param-name>
<param-value>valueA</param-value>
</context-param>
<context-param>
<param-name>paramB</param-name>
<param-value>valueB</param-value>
</context-param>
</web-app>
获取参数:
package com.atguigu.servlet;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
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.util.Enumeration;
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从ServletContext中获取为所有的Servlet准备的参数
ServletContext servletContext = this.getServletContext();
String valueA = servletContext.getInitParameter("paramA");
System.out.println("paramA:"+valueA);
// 获取所有参数名
Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
// 迭代并获取参数名
while (initParameterNames.hasMoreElements()) {
String paramaterName = initParameterNames.nextElement();
System.out.println(paramaterName+":"+servletContext.getInitParameter(paramaterName));
}
}
}
6.3.其他API:
获取资源的真实路径
String realPath = servletContext.getRealPath(资源在web目录中的路径);
- 例如我们的目标是需要获取项目中某个静态资源的路径,不是工程目录中的路径,而是部署目录中的路径;我们如果直接拷贝其在我们电脑中的完整路径的话其实是有问题的,因为如果该项目以后部署到公司服务器上的话,路径肯定是会发生改变的,所以我们需要使用代码动态获取资源的真实路径. 只要使用了servletContext动态获取资源的真实路径,那么无论项目的部署路径发生什么变化,都会动态获取项目运行时候的实际路径,所以就不会发生由于写死真实路径而导致项目部署位置改变引发的路径错误问题
获取项目的上下文路径
String contextPath = servletContext.getContextPath();
- 项目的部署名称,也叫项目的上下文路径,在部署进入tomcat时所使用的路径,该路径是可能发生变化的,通过该API动态获取项目真实的上下文路径,可以帮助我们解决一些后端页面渲染技术或者请求转发和响应重定向中的路径问题。
域对象的相关API
- 域对象: 一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域,不同的域对象代表不同的域,共享数据的范围也不同
- ServletContext代表应用,所以ServletContext域也叫作应用域,是webapp中最大的域,可以在本应用内实现数据的共享和传递
- webapp中的三大域对象,分别是应用域,会话域,请求域
后续我们会将三大域对象统一进行讲解和演示
,三大域对象都具有的API如下
API | 功能解释 |
---|---|
void setAttribute(String key,Object value); | 向域中存储/修改数据 |
Object getAttribute(String key); | 获得域中的数据 |
void removeAttribute(String key); | 移除域中的数据 |
七 HttpServletRequest:
7.1 HttpServletRequest简介:
HttpServletRequest是什么
- HttpServletRequest是一个接口,其父接口是ServletRequest
- HttpServletRequest是Tomcat将请求报文转换封装而来的对象,在Tomcat调用service方法时传入
- HttpServletRequest代表客户端发来的请求,所有请求中的信息都可以通过该对象获得
7.2 HttpServletRequest常见API:
HttpServletRequest怎么用
- 获取请求行信息相关(方式,请求的url,协议及版本)
API | 功能解释 |
---|---|
StringBuffer getRequestURL(); | 获取客户端请求的url |
String getRequestURI(); | 获取客户端请求项目中的具体资源 |
int getServerPort(); | 获取客户端发送请求时的端口 |
int getLocalPort(); | 获取本应用在所在容器的端口 |
int getRemotePort(); | 获取客户端程序的端口 |
String getScheme(); | 获取请求协议 |
String getProtocol(); | 获取请求协议及版本号 |
String getMethod(); | 获取请求方式 |
API | 功能解释 |
---|---|
String getHeader(String headerName); | 根据头名称获取请求头 |
Enumeration< String> getHeaderNames(); | 获取所有的请求头名字 |
String getContentType(); | 获取content-type请求头 |
API | 功能解释 |
---|---|
String getParameter(String parameterName); | 根据请求参数名获取请求单个参数值 |
String[] getParameterValues(String parameterName); | 根据请求参数名获取请求多个参数值数组 |
Enumeration getParameterNames(); | 获取所有请求参数名 |
Map<String, String[]> getParameterMap(); | 获取所有请求参数的键值对集合 |
BufferedReader getReader() throws IOException; | 获取读取请求体的字符输入流 |
ServletInputStream getInputStream() throws IOException; | 获取读取请求体的字节输入流 |
int getContentLength(); | 获得请求体长度的字节数 |
API | 功能解释 |
---|---|
String getServletPath(); | 获取请求的Servlet的映射路径 |
ServletContext getServletContext(); | 获取ServletContext对象 |
Cookie[] getCookies(); | 获取请求中的所有cookie |
HttpSession getSession(); | 获取Session对象 |
void setCharacterEncoding(String encoding); | 设置请求体字符集 |
- URL(统一资源定位符)是一种URI,它包含了资源的位置和访问方式。例如:
https://www.example.com/image.jpg
是一个URL,它指定了资源的位置(www.example.com服务器上的image.jpg文件)和访问方式(使用HTTPS协议)。(https)-z00rx9ct00awpeg86ci8w2za685sbf4e4jav19q./) - URI(统一资源标识符)是用来唯一标识资源的字符串,可以是URL或URN。URN(统一资源名称)是URI的另一种形式,用于唯一标识资源而不指定如何定位资源。例如:
urn:isbn:0451450523
是一个URN,它唯一标识了一个ISBN号为0451450523的资源,但没有指定如何访问该资源。
因此,URL是一种具体的URI,它除了唯一标识资源外,还包含了访问该资源的具体方式;而URI是一个更通用的概念,可以是URL或URN。因此,我们就可以对比:
再来看看它们三个的区别:
getServerPort()
:getServerPort()
返回的是客户端发送请求时使用的端口号,即客户端请求的URL中的端口号。这个端口号通常是客户端浏览器发送请求时使用的端口号,可以是标准的HTTP端口80或HTTPS端口443,也可以是其他自定义端口号。- 举例:如果客户端通过浏览器访问一个网站,URL为
http://www.example.com:8080/index.html
,那么getServerPort()
返回的值就是8080。
getLocalPort()
:getLocalPort()
返回的是本应用程序在所在容器中监听的端口号,即应用程序所使用的端口号。这个端口号是服务器端应用程序监听的端口号,用于接收客户端的请求。- 举例:如果一个Java Web应用部署在Tomcat容器上,Tomcat监听的端口号为8080,那么
getLocalPort()
返回的值就是8080。
getRemotePort()
:getRemotePort()
返回的是客户端程序的端口号,即客户端程序所使用的端口号。这个端口号是客户端程序(如浏览器)连接服务器时所使用的本地端口号。- 举例:当客户端浏览器向服务器发送HTTP请求时,浏览器会使用一个本地端口号作为源端口,
getRemotePort()
返回的就是这个本地端口号。
总结:
getServerPort()
返回客户端请求URL中的端口号。getLocalPort()
返回服务器端应用程序所监听的端口号。getRemotePort()
返回客户端程序(如浏览器)的本地端口号。
这些方法提供了不同层面的端口信息,用于在Java应用程序中获取和处理与客户端通信的相关端口信息。
八 HttpServletResponse
8.1 HttpServletResponse简介
HttpServletResponse是什么
- HttpServletResponse是一个接口,其父接口是ServletResponse
- HttpServletResponse是Tomcat预先创建的,在Tomcat调用service方法时传入
- HttpServletResponse代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,通过该对象我们可以设置响应信息
8.2 HttpServletResponse的常见API
HttpServletRequest怎么用
- 设置响应行相关
API | 功能解释 |
---|---|
void setStatus(int code); | 设置响应状态码 |
- 设置响应头相关
API | 功能解释 |
---|---|
void setHeader(String headerName, String headerValue); | 设置/修改响应头键值对 |
void setContentType(String contentType); | 设置content-type响应头及响应字符集(设置MIME类型) |
- 设置响应体相关
API | 功能解释 |
---|---|
PrintWriter getWriter() throws IOException; | 获得向响应体放入信息的字符输出流 |
ServletOutputStream getOutputStream() throws IOException; | 获得向响应体放入信息的字节输出流 |
void setContentLength(int length); | 设置响应体的字节长度,其实就是在设置content-length响应头 |
- 其他API
API | 功能解释 |
---|---|
void sendError(int code, String message) throws IOException; | 向客户端响应错误信息的方法,需要指定响应码和响应信息 |
void addCookie(Cookie cookie); | 向响应体中增加cookie |
void setCharacterEncoding(String encoding); | 设置响应体字符集 |
MIME类型
- MIME类型,可以理解为文档类型,用户表示传递的数据是属于什么类型的文档
- 浏览器可以根据MIME类型决定该用什么样的方式解析接收到的响应体数据
- 可以这样理解: 前后端交互数据时,告诉对方发给对方的是 html/css/js/图片/声音/视频/… …
- tomcat/conf/web.xml中配置了常见文件的拓展名和MIMIE类型的对应关系
- 常见的MIME类型举例如下
文件拓展名 | MIME类型 |
---|---|
.html | text/html |
.css | text/css |
.js | application/javascript |
.png /.jpeg/.jpg/… … | image/jpeg |
.mp3/.mpe/.mpeg/ … … | audio/mpeg |
.mp4 | video/mp4 |
.m1v/.m1v/.m2v/.mpe/… … | video/mpeg |
9.1 概述
什么是请求转发和响应重定向
- 请求转发和响应重定向是web应用中间接访问项目资源的两种手段,也是Servlet控制页面跳转的两种手段
- 请求转发通过HttpServletRequest实现,响应重定向通过HttpServletResponse实现
- 请求转发生活举例: 张三找李四借钱,李四没有,李四找王五,让王五借给张三
- 响应重定向生活举例:张三找李四借钱,李四没有,李四让张三去找王五,张三自己再去找王五借钱
请求转发:
请求转发特点(背诵)
- 请求转发通过HttpServletRequest对象获取请求转发器实现
- 请求转发是服务器内部的行为,对客户端是屏蔽的
- 客户端只发送了一次请求,客户端地址栏不变
- 服务端只产生了一对请求和响应对象,这一对请求和响应对象会继续传递给下一个资源
- 因为全程只有一个HttpServletRequset对象,所以请求参数可以传递,请求域中的数据也可以传递
- 请求转发可以转发给其他Servlet动态资源,也可以转发给一些静态资源以实现页面跳转
- 请求转发可以转发给WEB-INF下受保护的资源
- 请求转发不能转发到本项目以外的外部资源
代码测试:
ServletA:
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求转发器
// 转发给servlet ok
RequestDispatcher requestDispatcher = req.getRequestDispatcher("servletB");
// 转发给一个视图资源 ok
//RequestDispatcher requestDispatcher = req.getRequestDispatcher("welcome.html");
// 转发给WEB-INF下的资源 ok
//RequestDispatcher requestDispatcher = req.getRequestDispatcher("WEB-INF/views/view1.html");
// 转发给外部资源 no
//RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.atguigu.com");
// 获取请求参数
String username = req.getParameter("username");
System.out.println(username);
// 向请求域中添加数据
req.setAttribute("reqKey","requestMessage");
// 做出转发动作
requestDispatcher.forward(req,resp);
}
}
ServletB:
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
System.out.println(username);
// 获取请求域中的数据
String reqMessage = (String)req.getAttribute("reqKey");
System.out.println(reqMessage);
// 做出响应
resp.getWriter().write("servletB response");
}
}
响应重定向:
响应重定向特点(背诵)
- 响应重定向通过HttpServletResponse对象的sendRedirect方法实现
- 响应重定向是服务端通过302响应码和路径,告诉客户端自己去找其他资源,是在服务端提示下的,客户端的行为
- 客户端至少发送了两次请求,客户端地址栏是要变化的
- 服务端产生了多对请求和响应对象,且请求和响应对象不会传递给下一个资源
- 因为全程产生了多个HttpServletRequset对象,所以请求参数不可以传递,请求域中的数据也不可以传递
- 重定向可以是其他Servlet动态资源,也可以是一些静态资源以实现页面跳转
- 重定向不可以到给WEB-INF下受保护的资源
- 重定向可以到本项目以外的外部资源
测试代码:
ServletA:
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
System.out.println(username);
// 向请求域中添加数据
req.setAttribute("reqKey","requestMessage");
// 响应重定向
// 重定向到servlet动态资源 OK
resp.sendRedirect("servletB");
// 重定向到视图静态资源 OK
//resp.sendRedirect("welcome.html");
// 重定向到WEB-INF下的资源 NO
//resp.sendRedirect("WEB-INF/views/view1");
// 重定向到外部资源
//resp.sendRedirect("http://www.atguigu.com");
}
}
SeverleB:
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
System.out.println(username);
// 获取请求域中的数据
String reqMessage = (String)req.getAttribute("reqKey");
System.out.println(reqMessage);
// 做出响应
resp.getWriter().write("servletB response");
}
}
重点:同样情况均满足下,优先使用重定向。
十 web乱码和路径问题总结:
乱码问题产生的根本原因是什么
- 数据的编码和解码使用的不是同一个字符集
- 使用了不支持某个语言文字的字符集
前端相对路径:
- 相对路径的规则是: 以当前资源所在的路径为出发点去寻找目标资源
- 相对路径不以 / 开头
- 在file协议下,使用的是磁盘路径
- 在http协议下,使用的是url路径
- 相对路径中可以使用 ./表示当前资源所在路径,可以省略不写
- 相对路径中可以使用…/表示当前资源所在路径的上一层路径,需要时要手动添加
相对路径情况1:web/index.html中引入web/static/img/logo.png
- 访问index.html的url为 : http://localhost:8080/web03_war_exploded/index.html
- 当前资源为 : index.html
- 当前资源的所在路径为 : http://localhost:8080/web03_war_exploded/
- 要获取的目标资源url为 : http://localhost:8080/web03_war_exploded/static/img/logo.png
- index.html中定义的了 :
<img src="static/img/logo.png"/>
- 寻找方式就是在当前资源所在路径(http://localhost:8080/web03_war_exploded/)后拼接src属性值(static/img/logo.png),正好是目标资源正常获取的url(http://localhost:8080/web03_war_exploded/static/img/logo.png)
注意,在计算路径时,不要计算src目录与web目录,虽然在项目的目录结构中有src和web这两个目录,但在部署到Tomcat后,直接访问的是Web应用程序的内容,而不需要显示这两个路径。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img src="static/img/logo.png">
</body>
</html>
相对路径情况2:web/a/b/c/test.html中引入web/static/img/logo.png
- 访问test.html的url为 : http://localhost:8080/web03_war_exploded/a/b/c/test.html
- 当前资源为 : test.html
- 当前资源的所在路径为 : http://localhost:8080/web03_war_exploded/a/b/c/
- 要获取的目标资源url为 : http://localhost:8080/web03_war_exploded/static/img/logo.png
- test.html中定义的了 :
<img src="../../../static/img/logo.png"/>
- 寻找方式就是在当前资源所在路径(http://localhost:8080/web03_war_exploded/a/b/c/)后拼接src属性值(…/…/…/static/img/logo.png),其中 …/可以抵消一层路径,正好是目标资源正常获取的url(http://localhost:8080/web03_war_exploded/static/img/logo.png)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- ../代表上一层路径 -->
<img src="../../../static/img/logo.png">
</body>
</html>
相对路径情况3:web/WEB-INF/views/view1.html中引入web/static/img/logo.png
- view1.html在WEB-INF下,需要通过Servlet请求转发获得
@WebServlet("/view1Servlet")
public class View1Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher requestDispatcher = req.getRequestDispatcher("WEB-INF/views/view1.html");
requestDispatcher.forward(req,resp);
}
}
- 访问view1.html的url为 : http://localhost:8080/web03_war_exploded/view1Servlet
- 当前资源为 : view1Servlet
- 当前资源的所在路径为 : http://localhost:8080/web03_war_exploded/
- 要获取的目标资源url为 : http://localhost:8080/web03_war_exploded/static/img/logo.png
- view1.html中定义的了 :
<img src="static/img/logo.png"/>
- 寻找方式就是在当前资源所在路径(http://localhost:8080/web03_war_exploded/)后拼接src属性值(static/img/logo.png),正好是目标资源正常获取的url(http://localhost:8080/web03_war_exploded/static/img/logo.png)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img src="static/img/logo.png">
</body>
</html>
前端绝对路径
绝对路径情况1:web/index.html中引入web/static/img/logo.png
- 访问index.html的url为 : http://localhost:8080/web03_war_exploded/index.html
- 绝对路径的基准路径为 : http://localhost:8080
- 要获取的目标资源url为 : http://localhost:8080/web03_war_exploded/static/img/logo.png
- index.html中定义的了 :
<img src="/web03_war_exploded/static/img/logo.png"/>
- 寻找方式就是在基准路径(http://localhost:8080)后面拼接src属性值(/web03_war_exploded/static/img/logo.png),得到的正是目标资源访问的正确路径
base标签的使用
base标签定义页面相对路径公共前缀
- base 标签定义在head标签中,用于定义相对路径的公共前缀
- base 标签定义的公共前缀只在相对路径上有效,绝对路径中无效
- 如果相对路径开头有 ./ 或者…/修饰,则base标签对该路径同样无效
index.html 和a/b/c/test.html 以及view1Servlet 中的路径处理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--定义相对路径的公共前缀,将相对路径转化成了绝对路径-->
<base href="/web03_war_exploded/">
</head>
<body>
<img src="static/img/logo.png">
</body>
</html>
在响应重定向中,相对路径规则与前端相对路径规则是一模一样的,但是绝对路径需要加上代码以便动态的获取上下文路径。
//绝对路径中,要写项目上下文路径
//resp.sendRedirect("/web03_war_exploded/a/b/c/test.html");
// 通过ServletContext对象动态获取项目上下文路径
//resp.sendRedirect(getServletContext().getContextPath()+"/a/b/c/test.html");
// 缺省项目上下文路径时,直接以/开头即可
resp.sendRedirect("/a/b/c/test.html");
但是,在请求转发中,相对路径还是一样的,而绝对路径是不需要添加上下文的。
请求转发只能转发到项目内部的资源,其绝对路径无需添加项目上下文路径
请求转发绝对路径的基准路径相当于http://localhost:8080/web03_war_exploded
在项目上下文路径为缺省值时,也无需改变,直接以/开头即可
@WebServlet("/x/y/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/a/b/c/test.html");
requestDispatcher.forward(req,resp);
}
}
缺省上下文:
项目上下文路径变化问题
- 通过 base标签虽然解决了相对路径转绝对路径问题,但是base中定义的是项目的上下文路径
- 项目的上下文路径是可以随意变化的
- 一旦项目的上下文路径发生变化,所有base标签中的路径都需要改
解决方案
- 将项目的上下文路径进行缺省设置,设置为 /,所有的绝对路径中就不必填写项目的上下文了,直接就是/开头即可
十一:MVC架构模式:
MVC(Model-View-Controller)是一种软件架构模式,用于将应用程序分为三个主要部分:模型(Model)、视图(View)和控制器(Controller)。这种架构模式有助于实现代码的分离,提高应用程序的可维护性和可扩展性。
模型(Model):
模型代表应用程序的数据结构和业务逻辑。它负责处理数据的存储、检索、更新和删除,以及定义应用程序的行为。模型通常不直接与用户界面交互,而是通过控制器来调用。视图(View):
视图是用户界面的表示,负责展示数据给用户。视图通常是通过模型来获取数据,并将数据以用户友好的方式呈现出来。视图不包含业务逻辑,只负责展示数据。控制器(Controller):
控制器是模型和视图之间的桥梁,负责处理用户的输入并作出相应的响应。控制器接收用户的请求,调用模型来处理数据,然后选择合适的视图来展示结果。控制器将用户的操作转化为对模型和视图的操作。
MVC架构模式的优点包括:
- 提高代码的可维护性:将应用程序分为不同的部分,使得修改一个部分不会影响其他部分。
- 提高代码的可扩展性:可以更容易地添加新的功能或修改现有功能。
- 促进团队合作:不同的团队成员可以专注于各自的部分,提高开发效率。
总之,MVC架构模式是一种有助于组织和管理应用程序代码的有效方式,被广泛应用于各种软件开发项目中。