Web入门
创建Web项目并添加servlet依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
创建普通Java类
- 实现Servlet的规格—(继承HttpServlet类(常用方便)、继承GenericService、实现Servlet接口 )
- 重写doGet方法,用来处理请求
- 设置注解,指定访问路劲 / 配置XML
①通过注解 配置路径
@WebServlet(name = "MyServlet",urlPatterns = {"/getUser","/userInfo"})
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().println("hello,world!");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
②通过配置XML 配置路径
public class FirstServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Content-Type","text/html;charset=utf-8");
response.getWriter().println("hello,world!你好吗?");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>ServletTest</servlet-name>
<servlet-class>top.lionkliu.servlet.ServletTest</servlet-class>
</servlet>
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>cn.niit.FirstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/first01</url-pattern>
</servlet-mapping>
<!--多个URL映射同一个Servlet-->
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/hello/first01</url-pattern>
</servlet-mapping>
</web-app>
- 启动Tomcat
访问: localhost:8080/{webContext}/{urlPatterns}
一、Servlet入门
1. Servlet生命周期
- 加载Servlet。当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例
- 初始化。当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象
- 处理服务。当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求
- 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源。一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁
- 卸载。当Servlet调用完destroy()方法后,等待垃圾回收。如果有需要再次使用这个Servlet,会重新调用init()方法进行初始化操作。
2. ServletConfig
接口
Servlet 容器初始化 Servlet 时,会为这个 Servlet 创建一个 ServletConfig 对象,并将 ServletConfig 对象作为参数传递给 Servlet 。通过 ServletConfig 对象即可获得当前 Servlet 的初始化参数信息。
- 一个 Web 应用中可以存在多个 ServletConfig 对象
- 一个 Servlet 只能对应一个 ServletConfig 对象,即 Servlet 的初始化参数仅对当前 Servlet 有效。
2.1 ServletConfig 接口
javax.servlet
包提供了一个 ServletConfig 接口,该接口中提供了以下方法。
返回值类型 | 方法 | 功能描述 |
---|---|---|
String | getInitParameter(String name) | 根据初始化参数名 name,返回对应的初始化参数值。 |
Enumeration<String> |
getInitParameterNames() | 返回 Servlet 所有的初始化参数名的枚举集合,如果该 Servlet 没有初始化参数,则返回一个空的集合。 |
ServletContext | getServletContext() | 返回一个代表当前 Web 应用的 ServletContext 对象。 |
String | getServletName() | 返回 Servlet 的名字,即 web.xml 中 <servlet-name> 元素的值。 |
2.2 获得 ServletConfig 对象
① 从带参的 init() 方法中提取
public class ServletConfigDemo extends HttpServlet {
private ServletConfig servletConfig;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取Servlet得名字
this.servletConfig.getServletName();
}
@Override
public void init(ServletConfig config) throws ServletException {
//从带参init方法中,提取ServletConfig对象
this.servletConfig = config;
}
}
② GenericServlet 提供的 getServletConfig() 方法获得
//调用 GenericServlet 提供的 getServletConfig 方法获得 ServletConfig 对象
ServletConfig servletConfig = this.getServletConfig();
2.3 配置 Servlet 初始化参数
① 通过配置web.xml中servlet的init-param
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
id="WebApp_ID" metadata-complete="false" version="4.0">
<!--<init-param> 元素是 <servlet> 的子元素,
需要在 <servlet> 元素内使用,表示只对当前 Servlet 有效 。-->
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>cn.niit.MyServlet</servlet-class>
<!-- Servlet 初始化参数 -->
<init-param>
<!--参数的名称-->
<param-name>name</param-name>
<!--参数的值-->
<param-value>lionkliu</param-value>
</init-param>
<!-- Servlet 初始化参数 -->
<init-param>
<param-name>URL</param-name>
<param-value>blog.lionkliu.top</param-value>
</init-param>
</servlet>
</web-app>
② 通过配置@WebServlet
注解 中initParams
参数
@WebServlet(urlPatterns = {"/MyServlet"}, initParams = {@WebInitParam(name = "name", value = "lionkliu"),
@WebInitParam(name = "URL", value = "blog.lionkliu.top")})
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
2.4 获取 Servlet 初始化参数
@WebServlet(urlPatterns = {"/MyServlet"}, initParams = {@WebInitParam(name = "name", value = "lionkliu"),
@WebInitParam(name = "URL", value = "blog.lionkliu.top")})
public class ReadConfigServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
// 获取ServletConfig对象
ServletConfig config = getServletConfig();
// 获取servletName
String servletName = config.getServletName();
// 返回 servlet 的初始化参数的名称的集合
Enumeration<String> initParameterNames = config.getInitParameterNames();
// 遍历集合获取初始化参数名称
while (initParameterNames.hasMoreElements()) {
// 获取初始化参数名称
String initParamName = initParameterNames.nextElement();
// 获取相应的初始参数的值
String initParamValue = config.getInitParameter(initParamName);
// 向页面输出
writer.write(initParamName + " : " + initParamValue + "<br/>");
}
// 关闭流
writer.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
3. ServletContext
接口
Servlet 容器启动时,会为每个 Web 应用(webapps 下的每个目录都是一个 Web 应用)创建一个唯一的 ServletContext 对象,该对象一般被称为“Servlet 上下文”。工程内部的所有servlet都共享这个对象,所以叫全局应用程序共享对象。因此 ServletContext 对象也被称为 Context 域对象。
域对象是服务器在内存上创建的存储空间,用于在不同动态资源(servlet)之间传递与共享数据。
- 一个web项目只有一个
ServletContext
对象 - ServletContext 对象的生命周期从 Servlet 容器启动时开始,到容器关闭或应用被卸载时结束。
3.1 获得 ServletContext 对象
① 通过 GenericServlet 提供的 getServletContext() 方法
//通过 GenericServlet的getServletContext方法获取ServletContext对象ServletContext
servletContext = this.getServletContext();
② 通过 ServletConfig 提供的 getServletContext() 方法
//通过 ServletConfig的 getServletContext方法获取ServletContext对象ServletContext
servletContext = this.getServletConfig().getServletContext();
③ 通过 HttpSession 提供的 getServletContext() 方法
//通过 HttpSession的 getServletContext方法获取ServletContext对象ServletContext
servletContext = req.getSession().getServletContext();
④ 通过 HttpServletRequest 提供的 getServletContext() 方法
//通过 HttpServletRequest的 getServletContext方法获取ServletContext对象ServletContext
servletContext = req.getServletContext();
3.2 ServletContext 的作用
① 获取或设置上下文初始化参数
- 设置上下文初始化参数
- 通过 web.xml 中的
<context-param>
元素可以为 Web 应用设置一些全局的初始化参数,这些参数被称为上下文初始化参数。 - 与 Servlet 的初始化参数不同,应用中的所有 Servlet 都共享同一个上下文初始化参数。在 Web 应用的整个生命周期中,上下文初始化参数会一直存在,并且可以随时被任意一个 Servlet 访问。
- 通过 web.xml 中的
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0" metadata-complete="false">
<!--设置全局初始化参数 -->
<context-param>
<param-name>name</param-name>
<param-value>lionkliu</param-value>
</context-param>
<context-param>
<param-name>url</param-name>
<param-value>blog.lionkliu.top</param-value>
</context-param>
</web-app>
- 获取上下文初始化参数
- Servlet 容器启动时,会为容器内每个 Web 应用创建一个 ServletContext 对象,并将
<context-param>
元素中的上下文初始化参数以键值对的形式存入该对象中,因此我们可以通过 ServletContext 的相关方法获取到这些初始化参数。 | 返回值 | 方法 | 描述 | | —- | —- | —- | | String | getInitParameter(String name) | 根据初始化参数名 name,返回对应的初始化参数值。 | |Enumeration<String>
| getInitParameterNames(); | 返回 Web 应用所有上下文初始化参数名的枚举集合,如果该 Web 应用没有上下文初始化参数,则返回一个空的枚举集合。 |
- Servlet 容器启动时,会为容器内每个 Web 应用创建一个 ServletContext 对象,并将
@WebServlet("/ReadContextServlet")
public class ReadContextServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
// 调用httpServlet父类GenericServlet的getServletContext方法获取ServletContext对象
ServletContext context = super.getServletContext();
// 返回 context 上下文初始化参数的名称
Enumeration<String> initParameterNames = context.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
// 获取初始化参数名称
String initParamName = initParameterNames.nextElement();
// 获取相应的初始参数的值
String initParamValue = context.getInitParameter(initParamName);
// 向页面输出
writer.write(initParamName + " : " + initParamValue + "<br/>");
}
// 关闭流
writer.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
② 实现 Servlet 之间的数据通讯
- 通过ServletContext 接口的
setAttribute()
方法可以创建一些属性,这些属性被存放在 ServletContext 对象中。应用中所有 Servlet 都可以对这些属性进行访问和操作,通过它们可以实现应用内不同 Servlet 之间的数据通讯。 | 返回值 | 方法 | 描述 | | —- | —- | —- | | void | setAttribute(String name, Object object); | 把一个 Java 对象与一个属性名绑定,并将它作为一个属性存放到 ServletContext 中。 参数 name 为属性名,参数 object 为属性值。 | | void | removeAttribute(String name); | 从 ServletContext 中移除属性名为 name 的属性。 | | Object | getAttribute(String name); | 根据指定的属性名 name,返回 ServletContext 中对应的属性值。 | |Enumeration<String>
| getAttributeNames(); | 返回一个枚举,其中包含此 ServletContext 中可用的属性名称。使用带有属性名称的 getAttribute 方法来获取属性的值。 |
③ 读取 Web 应用下的资源文件
返回值 | 方法 | 方法描述 |
---|---|---|
Set<String> |
getResourcePaths(String path); | 返回一个 Set 集合,该集合中包含资源目录中的子目录和文件的名称。 |
String | getRealPath(String path); | 返回资源文件的真实路径(文件的绝对路径)。 |
Set<String> |
getResourcePaths(String path); | 返回映射到资源文件的 URL 对象。 |
InputStream | getResourceAsStream(String path); | 返回映射到资源文件的 InputStream 输入流对象。 |
4. <init-param>
与<context-param>
区别
init-param是定义在servlet标签里,而context-param是定义在web-app标签里。
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>encoding</param-name>
<param-value>Utf8</param-value>
</context-param>
<servlet>
<servlet-name>ServletContext01</servlet-name>
<servlet-class>cn.niit.xml.ServletContext01</servlet-class>
<init-param>
<param-name>address</param-name>
<param-value>NIIT</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ServletContext01</servlet-name>
<url-pattern>/ServletContext01</url-pattern>
</servlet-mapping>
</web-app>
① <context-param>
:context-param是配置在web下面,属于上下文参数,在整个环境中都可使用,存放在getServletContext对像中,通过getServletContext().getInitParameter(“name”)
获取
ServletContext servletContext = this.getServletContext();
String encoding = servletContext.getInitParameter("encoding");
response.getWriter().println("encoding:"+encoding);
② <init-param>
和注解中initParams
:由于init-param是配置在servlet中,属于某一下servlet,存放在getServletConfig中,因此使用方法是:getServletConfig().getInitParameter(“user1”);
由于它属于当前的servlet类,所以用this替代getServletConfig(), 使用this.getInitParmeter(“name”) , 如:
/**
* @author : lyj
* @Timer : 2022/3/9
* @Description :
*/
@WebServlet(urlPatterns = "/ServletContext02",
initParams = {@WebInitParam(name = "name", value = "lionkliu"),
@WebInitParam(name = "URL", value = "blog.lionkliu.top")})
public class ServletContext02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
// 获取ServletConfig对象
ServletConfig config = getServletConfig();
// 返回 servlet 的初始化参数的名称的集合
Enumeration<String> initParameterNames = config.getInitParameterNames();
// 遍历集合获取初始化参数名称
while (initParameterNames.hasMoreElements()) {
// 获取初始化参数名称
String initParamName = initParameterNames.nextElement();
// 获取相应的初始参数的值
String initParamValue = config.getInitParameter(initParamName);
//等同于this.getInitParameter(initParamName);
// 向页面输出
writer.write(initParamName + " : " + initParamValue + "<br/>");
}
// 关闭流
writer.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
总结:
<context-param>
在所有的servlet中都能使用<init-param>
只能在当前的servlet中使用,如果不在当前的servlet中使用,取的值为null
5. HttpServletRequest
接口
HttpServletRequest
封装了一个HTTP请求,它实际上是从ServletRequest
继承而来。最早设计Servlet时,设计者希望Servlet不仅能处理HTTP,也能处理类似SMTP等其他协议,因此,单独抽出了ServletRequest
接口,但实际上除了HTTP外,并没有其他协议会用Servlet处理,所以这是一个过度设计。
我们通过HttpServletRequest
提供的接口方法可以拿到HTTP请求的几乎全部信息,常用的方法有:
- getMethod():返回请求方法,例如,
"GET"
,"POST"
; - getRequestURI():返回请求路径,但不包括请求参数,例如,
"/hello"
; - getQueryString():返回请求参数,例如,
"name=Bob&a=1&b=2"
; - getParameter(name):返回请求参数,GET请求从URL读取参数,POST请求从Body中读取参数;
- getContentType():获取请求Body的类型,例如,
"application/x-www-form-urlencoded"
; - getContextPath():获取当前Webapp挂载的路径,对于ROOT来说,总是返回空字符串
""
; - getCookies():返回请求携带的所有Cookie;
- getHeader(name):获取指定的Header,对Header名称不区分大小写;
- getHeaderNames():返回所有Header名称;
- getInputStream():如果该请求带有HTTP Body,该方法将打开一个输入流用于读取Body;
- getReader():和getInputStream()类似,但打开的是Reader;
- getRemoteAddr():返回客户端的IP地址;
- getScheme():返回协议类型,例如,
"http"
,"https"
;
获取请求行信息
返回值类型 | 方法声明 | 描述 |
---|---|---|
String | getMethod() | 该方法用于获取 HTTP 请求方式(如 GET、POST 等)。 |
String | getRequestURI() | 该方法用于获取请求行中的资源名称部分,即位于 URL 的主机和端口之后,参数部分之前的部分。 |
String | getQueryString() | 该方法用于获取请求行中的参数部分,也就是 URL 中“?”以后的所有内容。 |
String | getContextPath() | 返回当前 Servlet 所在的应用的名字(上下文)。对于默认(ROOT)上下文中的 Servlet,此方法返回空字符串””。 |
String | getServletPath() | 该方法用于获取 Servlet 所映射的路径。 |
String | getRemoteAddr() | 该方法用于获取客户端的 IP 地址。 |
String | getRemoteHost() | 该方法用于获取客户端的完整主机名,如果无法解析出客户机的完整主机名,则该方法将会返回客户端的 IP 地址。 |
获取请求头信息
返回值类型 | 方法声明 | 描述 |
---|---|---|
String | getHeader(String name) | 该方法用于获取一个指定头字段的值。 如果请求消息中包含多个指定名称的头字段,则该方法返回其中第一个头字段的值。 |
Enumeration | getHeaders(String name) | 该方法返回指定头字段的所有值的枚举集合, 在多数情况下,一个头字段名在请求消息中只出现一次,但有时可能会出现多次。 |
Enumeration | getHeaderNames() | 该方法返回请求头中所有头字段的枚举集合。 |
String | getContentType() | 该方法用于获取 Content-Type 头字段的值。 |
int | getContentLength() | 该方法用于获取 Content-Length 头字段的值 。 |
String | getCharacterEncoding() | 该方法用于返回请求消息的字符集编码 。 |
获取 form 表单的数据
返回值类型 | 方法声明 | 功能描述 |
---|---|---|
String | getParameter(String name) | 返回指定参数名的参数值。 |
String [ ] | getParameterValues (String name) | 以字符串数组的形式返回指定参数名的所有参数值(HTTP 请求中可以有多个相同参数名的参数)。 |
Enumeration | getParameterNames() | 以枚举集合的形式返回请求中所有参数名。 |
Map | getParameterMap() | 用于将请求中的所有参数名和参数值装入一个 Map 对象中返回。 |
中文乱码问题
6. HTTPServletResponse
接口
在 Servlet API 中,定义了一个 HttpServletResponse 接口,它继承自 ServletResponse 接口。HttpServletResponse 对象专门用来封装 HTTP 响应消息,简称 response 对象。
Servlet 容器会针对每次请求创建一个 response 对象,并把它作为参数传递给 Servlet 的 service 方法。Servlet 处理请求后,会将响应信息封装到 response 对象中,并由容器解析后返回给客户端。
由于 HTTP 响应消息由响应行、响应头、消息体三部分组成,所以 HttpServletResponse 接口中定义了向客户端发送响应状态码、响应头、响应体的方法,
HttpServletResponse
封装了一个HTTP响应。由于HTTP响应必须先发送Header,再发送Body,所以,操作HttpServletResponse
对象时,必须先调用设置Header的方法,最后调用发送Body的方法。
常用的设置Header的方法有:
- setStatus(sc):设置响应代码,默认是
200
; - setContentType(type):设置Body的类型,例如,
"text/html"
; - setCharacterEncoding(charset):设置字符编码,例如,
"UTF-8"
; - setHeader(name, value):设置一个Header的值;
- addCookie(cookie):给响应添加一个Cookie;
- addHeader(name, value):给响应添加一个Header,因为HTTP协议允许有多个相同的Header;
响应行相关的方法
当 Servlet 返回响应消息时,需要在响应消息中设置状态码。因此,HttpServletResponse 接口定义了发送状态码的方法,如下表。
返回值类型 | 方法 | 描述 |
---|---|---|
void | setStatus(int status) | 用于设置 HTTP 响应消息的状态码,并生成响应状态行。 |
void | sendError(int sc) | 用于发送表示错误信息的状态码。 |
响应头相关的方法
HttpServletResponse 接口中定义了一系列设置 HTTP 响应头字段的方法,如下表所示。
返回值类型 | 方法 | 描述 |
---|---|---|
void | addHeader(String name,String value) | 用于增加响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值。 |
void | setHeader (String name,String value) | 用于设置响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值。 |
void | addIntHeader(String name,int value) | 用于增加值为 int 类型的响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值,类型为 int。 |
void | setIntHeader(String name, int value) | 用于设置值为 int 类型的响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值,类型为 int。 |
void | setContentType(String type) | 用于设置 Servlet 输出内容的 MIME 类型以及编码格式。 |
void | setCharacterEncoding(String charset) | 用于设置输出内容使用的字符编码。 |
响应体相关的方法
由于在 HTTP 响应消息中,大量的数据都是通过响应消息体传递的。因此 ServletResponse 遵循以 I/O 流传递大量数据的设计理念,在发送响应消息体时,定义了两个与输出流相关的方法。
返回值类型 | 方法 | 描述 |
---|---|---|
ServletOutputStream | getOutputStream() | 用于获取字节输出流对象。 |
PrintWriter | getWriter() | 用于获取字符输出流对象。 |
response 中文乱码问题
response 对象向页面输出时有两种方式:字节流、字符流,这两种方式输出中文时都有可能出现乱码。下面我们针对这两种方式出现乱码的原因以及解决方案进行介绍。
使用字节流输出中文
ServletOutputStream outptuStream = response.getOutputStream();outputStream.write(“编程帮 www.biancheng.net”.getBytes());
使用字节流向页面输出中文是否会出现乱码问题?不一定。
乱码原因:
字节流输出中文是否出现乱码,取决于中文转成字节数组时与浏览器打开时采用的字符集是否一致。若两者保持一致,则不会出现乱码问题,若不一致就会出现乱码问题。
解决方案:
将中文转成字节数组时和浏览器默认采用的字符集保持一致即可,代码如下。
response.setHeader("Content-Type", "text/html;charset=UTF-8");// 获取字节输出流OutputStream os = response.getOutputStream();byte[] str = "编程帮 www.biancheng.net".getBytes("UTF-8");// 输出中文os.write(str);
使用字符流输出中文
使用字符流向页面输出中文是否会出现乱码问题?一定乱码。
乱码原因:
通过字符流输出的内容是存放在 response 缓冲区的,response 缓冲区的默认字符集是 ISO-8859-1,该字符集不支持中文。
解决方案:
将 response 缓冲区和浏览器采用的字符集保持一致即可,有如下 2 种的方式。
第一种方式:
// 设置response缓冲区的编码response.setCharacterEncoding("UTF-8");// 设置浏览器打开文件所采用的编码response.setHeader("Content-Type", "text/html;charset=UTF-8");// 输出中文response.getWriter().write("编程帮 www.biancheng.net");
第二种方式:
纯文本复制
response.setContentType("text/html;charset=UTF-8");response.getWriter().write("编程帮 www.biancheng.net");
二、请求转发&重定向
1. 请求转发
Web 应用在处理客户端的请求时,经常需要多个 Web 资源共同协作才能生成响应结果。但由于 Serlvet 对象无法直接调用其他 Servlet 的 service() 方法,所以 Servlet 规范提供了 2 种解决方案:
- 请求转发
- 请求包含(了解即可)
对应着javax.servlet.RequestDispatcher
接口中的:
返回值 | 方法 | 功能描述 |
---|---|---|
void | forward(ServletRequest request, ServletResponse response); | 用于将请求转发给另一个 Web 资源。该方法必须在响应提交给客户端之前被调用,否则将抛出 IllegalStateException 异常 |
void | include(ServletRequest request, ServletResponse response) ; | 用于将其他的资源作为当前响应内容包含进来 |
获取 RequestDispatcher 对象
- 调用 ServletContext 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,必须为绝对路径;
- 调用 ServletRequest 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,可以为绝对路径,也可以为相对路径。
绝对路径是指以符号“/”开头的路径,“/”表示当前 Web 应用的根目录。相对路径是指相对当前 Web 资源的路径,不以符号“/”开头。
请求转发的特点
请求转发具有以下特点:
- 请求转发不支持跨域访问,只能跳转到当前应用中的资源。
- 请求转发之后,浏览器地址栏中的 URL 不会发生变化,因此浏览器不知道在服务器内部发生了转发行为,更无法得知转发的次数。
- 参与请求转发的 Web 资源之间共享同一 request 对象和 response 对象。
- 由于 forward() 方法会先清空 response 缓冲区,因此只有转发到最后一个 Web 资源时,生成的响应才会被发送到客户端。
request 域对象
request 是 Servlet 的三大域对象之一,它需要与请求转发配合使用,才可以实现动态资源间的数据传递。
在 ServletRequest 接口中定义了一系列操作属性的方法,如下表。
返回值类型 | 方法 | 描述 |
---|---|---|
void | setAttribute(String name, Object o) | 将 Java 对象与属性名绑定,并将它作为一个属性存放到 request 对象中。参数 name 为属性名,参数 object 为属性值。 |
Object | getAttribute(String name) | 根据属性名 name,返回 request 中对应的属性值。 |
void | removeAttribute(String name) | 用于移除 request 对象中指定的属性。 |
Enumeration | getAttributeNames() | 用于返回 request 对象中的所有属性名的枚举集合。 |
Context 域对象和 request 域对象对比,具有以下 4 点差异:
1) 生命周期不同
Context 域对象的生命周期从容器启动开始,到容器关闭或者 Web 应用被移除时结束;
request 域对象的生命周期从客户端向容器发送请求开始,到对这次请求做出响应后结束。
2) 作用域不同
Context 域对象对整个 Web 应用内的所有Servlet都有效;
request 域对象只对本次请求涉及的 Servlet 有效。
3) Web 应用中数量不同
整个 Web 应用中只有一个 Context 域对象;
由于 Servlet 能处理多个请求,因此 Web 应用中的每个 Servlet 实例都可以有多个 request 域对象。
4) 实现数据共享的方式不同
Context 域对象可以独立完成动态资源之间的数据共享;
Request 域对象需要与请求转发配合使用才能实现动态资源之间的数据共享。
2. 重定向
重定向属于客户端行为。服务器在收到客户端请求后,会通知客户端浏览器重新向另外一个 URL 发送请求,这称为请求重定向。它本质上是两次 HTTP 请求,对应两个 request 对象和两个 response 对象。
重定向流程:
- 用户在浏览器中输入 URL,请求访问服务器端的 Web 资源。
- 服务器端的 Web 资源返回一个状态码为 302 的响应信息,该响应的含义为:通知浏览器再次发送请求,访问另一个 Web 资源(在响应信息中提供了另一个资源的 URL)。
- 当浏览器接收到响应后,立即自动访问另一个指定的 Web 资源。
- 另一 Web 资源将请求处理完成后,由容器把响应信息返回给浏览器进行展示。
实现重定向
HttpServletResponse 接口中的 sendRedirect() 方法用于实现重定向。
返回值类型 | 方法 | 描述 |
---|---|---|
void | sendRedirect(String location) | 向浏览器返回状态码为 302 的响应结果,让浏览器访问新的 URL。若指定的 URL 是相对路径,Servlet 容器会将相对路径转换为绝对路径。参数 location 表示重定向的URL。 |
3. 请求转发和重定向的区别
转发和重定向都能实现页面的跳转,但是两者也存在以下区别。
区别 | 请求转发 | 重定向 |
---|---|---|
浏览器地址栏 URL 是否发生改变 | 否 | 是 |
是否支持跨域跳转 | 否 | 是 |
请求与响应的次数 | 一次请求和一次响应 | 两次请求和两次响应 |
是否共享 request 对象和 response 对象 | 是 | 否 |
是否能通过 request 域对象传递数据 | 是 | 否 |
速度 | 相对要快 | 相对要慢 |
行为类型 | 服务器行为 | 客户端行为 |
三、Cookie&Session
1. 为什么要有会话技术
因为HTTP是一个基于请求与响应模式的无状态协议。
无状态主要指 2 点:
- 协议对于事务处理没有记忆能力,服务器不能自动维护用户的上下文信息,无法保存用户状态;
- 每次请求都是独立的,不会受到前面请求的影响,也不会影响后面的请求。
当浏览器发送 HTTP 请求到服务器时,服务器会响应客户端的请求,但当同一个浏览器再次发送请求到该服务器时,服务器并不知道它就是刚才那个浏览器,即 HTTP 协议的请求无法保存用户状态。
通常情况下,用户通过浏览器访问 Web 应用时,服务器都需要保存和跟踪用户的状态。例如,用户在某购物网站结算商品时,Web 服务器必须根据请求用户的身份,找到该用户所购买的商品。由于 HTTP 协议是无协议的,无法保存和跟踪用户状态,所以需要其他的方案来解决问此题,它就是会话技术。
2. 什么是会话技术
从打开浏览器访问某个网站,到关闭浏览器的过程,称为一次会话。会话技术是指在会话中,帮助服务器记录用户状态和数据的技术。
常用的会话技术分为两种:
- Cookie :客户端会话技术
- Session :服务端会话技术
3. 什么是Cookie
- 网页之间的交互是通过HTTP协议传输数据的,而Http协议是无状态的协议。无状态的协议是什么意思呢?一旦数据提交完后,浏览器和服务器的连接就会关闭,再次交互的时候需要重新建立新的连接。
- 服务器无法确认用户的信息,于是乎,W3C就提出了:给每一个用户都发一个通行证,无论谁访问的时候都需要携带通行证,这样服务器就可以从通行证上确认用户的信息。通行证就是Cookie
Cookie的流程:浏览器访问服务器,如果服务器需要记录该用户的状态,就使用response向浏览器发送一个Cookie,浏览器会把Cookie保存起来。当浏览器再次访问服务器的时候,浏览器会把请求的网址连同Cookie一同交给服务器。
4. 什么是Session
Session域对象
5. Session 与 Cookie 对比
Session 和 Cookie 都属于会话技术,都能帮助服务器保存和跟踪用户状态,但两者也存在差异,如下表。
不同点 | Cookie | Session |
---|---|---|
存储位置不同 | Cookie 将数据存放在客户端浏览器内存中或硬盘上。 | Session 将数据存储在服务器端。 |
大小和数量限制不同 | 浏览器对 Cookie 的大小和数量有限制。 | Session 的大小和数量一般不受限制。 |
存放数据类型不同 | Cookie 中保存的是字符串。 | Session 中保存的是对象。 |
安全性不同 | Cookie 明文传递,安全性低,他人可以分析存放在本地的 Cookie 并进行 Cookie 欺骗。 | Session 存在服务器端,安全性较高。 |
对服务器造成的压力不同 | Cookie 保存在客户端,不占用服务器资源。 | Session 保存在服务端,每一个用户独占一个 Session。若并发访问的用户十分多,就会占用大量服务端资源。 |
跨域支持上不同 | Cookie 支持跨域名访问。 | Session 不支持跨域名访问。 |
四、Filter&Listener
1. Filter过滤器
1.1 什么是Filter过滤器
- 在 Web 资源被访问前,检查 request 对象,修改请求头和请求正文,或对请求进行预处理操作。
- 将请求传递到下一个过滤器或目标资源。
- 在 Web 资源被访问后,检查 response 对象,修改响应头和响应正文。
1.2 Filter工作流程
- 客户端请求访问容器内的 Web 资源。
- Servlet 容器接收请求,并针对本次请求分别创建一个 request 对象和 response 对象。
- 请求到达 Web 资源之前,先调用 Filter 的 doFilter() 方法,检查 request 对象,修改请求头和请求正文,或对请求进行预处理操作。
- 在 Filter 的 doFilter() 方法内,调用 FilterChain.doFilter() 方法,将请求传递给下一个过滤器或目标资源。
- 目标资源生成响应信息返回客户端之前,处理控制权会再次回到 Filter 的 doFilter() 方法,执行 FilterChain.doFilter() 后的语句,检查 response 对象,修改响应头和响应正文。
- 响应信息返回客户端。
1.3 Filter 的生命周期
① 初始化阶段
Servlet 容器负责加载和实例化 Filter。容器启动时,读取 web.xml 或 @WebFilter 的配置信息对所有的过滤器进行加载和实例化。
加载和实例化完成后,Servlet 容器调用 init() 方法初始化 Filter 实例。在 Filter 的生命周期内, init() 方法只执行一次。
② 拦截和过滤阶段
该阶段是 Filter 生命周期中最重要的阶段。当客户端请求访问 Web 资源时,Servlet 容器会根据 web.xml 或 @WebFilter 的过滤规则进行检查。当客户端请求的 URL 与过滤器映射匹配时,容器将该请求的 request 对象、response 对象以及 FilterChain 对象以参数的形式传递给 Filter 的 doFilter() 方法,并调用该方法对请求/响应进行拦截和过滤。
③ 销毁阶段
Filter 对象创建后会驻留在内存中,直到容器关闭或应用被移除时销毁。销毁 Filter 对象之前,容器会先调用 destory() 方法,释放过滤器占用的资源。在 Filter 的生命周期内,destory() 只执行一次。
返回值类型 | 方法 | 功能描述 |
---|---|---|
void | init (FilterConfig filterConfig) | 该方法用于初始化过滤器。 |
void | doFilter(ServletRequest request,SeivletResponse response, FilterChain chain) | 该方法完成实际的过滤操作,当客户端请求的 URL 与过滤器映射的 URL 匹配时,容器会先调用该方法对请求进行拦截。 参数 request 和 response 表示请求和响应对象。 参数 chain 代表当前 Filter 链对象,在该方法内部,调用 chain.doFilter() 方法,才能把请求交付给 Filter 链中的下一个 Filter 或者 Web 资源。 |
void | destroy() | 该方法在销毁 Filter 对象之前被调用,用于释放被 Filter 对象占用的资源。 |
1.4 实现过滤器功能
① 通过 web.xml 配置
② 通过 @WebFilter 注解配置
Listener监听器
五、文件上传&下载
六、Servlet 常见的注解
@WebServlet
属性名 | 类型 | 属性描述 |
---|---|---|
name | String | 指定servlet的name属性,等价于<Servlet-name> 如果没有显示指定,则该servlet的取值即为类的全限定名. |
value | String[] | 等价于urlPatterns,二者不能共存. |
urlPatterns | String[] | 指定一组servlet的url的匹配模式,等价于 <url-pattern> 标签. |
loadOnStartup | int | 指定servlet的加载顺序,等价于 <load-on-startup> 标签. |
initParams | WebInitParam[] | 指定一组初始化参数,等价于 <init-param> 标签. |
asyncSupported | boolean | 申明servlet是否支持异步操作模式,等价于 <async-supported> 标签. |
displayName | String | servlet的显示名,等价于 <display-name> 标签. |
description | String | servlet的描述信息,等价于 <description> 标签. |
@WebFilter
@WebListener
https://segmentfault.com/a/1190000017139837
七、JSP
参考:
- Tomcat就是这么简单
- Servlet第一篇【介绍Servlet、HTTP协议、WEB目录结构、编写入门Servlet程序、Servlet生命周期】
- Servlet第二篇【Servlet调用图、Servlet细节、ServletConfig、ServletContext】
- Servlet第三篇【request和response简介、response的常见应用】
- Servlet第四篇【request对象常用方法、应用】
- Servlet第五篇【介绍会话技术、Cookie的API、详解、应用】
- Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】
- Tomcat+Servlet面试题都在这里
web注解:https://segmentfault.com/a/1190000017139837?utm_source=sf-similar-article