点击查看【music163】

Web入门

  1. 创建Web项目并添加servlet依赖

    1. <dependency>
    2. <groupId>javax.servlet</groupId>
    3. <artifactId>javax.servlet-api</artifactId>
    4. <version>4.0.1</version>
    5. <scope>provided</scope>
    6. </dependency>
  2. 创建普通Java类

  3. 实现Servlet的规格—(继承HttpServlet类(常用方便)、继承GenericService、实现Servlet接口 )
  4. 重写doGet方法,用来处理请求
  5. 设置注解,指定访问路劲 / 配置XML
  • ①通过注解 配置路径

    1. @WebServlet(name = "MyServlet",urlPatterns = {"/getUser","/userInfo"})
    2. public class MyServlet extends HttpServlet {
    3. @Override
    4. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    5. response.getWriter().println("hello,world!");
    6. }
    7. @Override
    8. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    9. }
    10. }
  • ②通过配置XML 配置路径

    1. public class FirstServlet extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    4. this.doPost(request,response);
    5. }
    6. @Override
    7. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    8. response.setHeader("Content-Type","text/html;charset=utf-8");
    9. response.getWriter().println("hello,world!你好吗?");
    10. }
    11. }
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    5. version="4.0">
    6. <servlet>
    7. <servlet-name>ServletTest</servlet-name>
    8. <servlet-class>top.lionkliu.servlet.ServletTest</servlet-class>
    9. </servlet>
    10. <servlet>
    11. <servlet-name>FirstServlet</servlet-name>
    12. <servlet-class>cn.niit.FirstServlet</servlet-class>
    13. </servlet>
    14. <servlet-mapping>
    15. <servlet-name>FirstServlet</servlet-name>
    16. <url-pattern>/first01</url-pattern>
    17. </servlet-mapping>
    18. <!--多个URL映射同一个Servlet-->
    19. <servlet-mapping>
    20. <servlet-name>FirstServlet</servlet-name>
    21. <url-pattern>/hello/first01</url-pattern>
    22. </servlet-mapping>
    23. </web-app>
  1. 启动Tomcat

访问: localhost:8080/{webContext}/{urlPatterns}

一、Servlet入门

1. Servlet生命周期

  1. 加载Servlet。当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例
  2. 初始化。当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象
  3. 处理服务。当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求
  4. 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源。一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁
  5. 卸载。当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 接口,该接口中提供了以下方法。
Servlet开发 - 图1

返回值类型 方法 功能描述
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() 方法中提取

  1. public class ServletConfigDemo extends HttpServlet {
  2. private ServletConfig servletConfig;
  3. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  4. //获取Servlet得名字
  5. this.servletConfig.getServletName();
  6. }
  7. @Override
  8. public void init(ServletConfig config) throws ServletException {
  9. //从带参init方法中,提取ServletConfig对象
  10. this.servletConfig = config;
  11. }
  12. }

② GenericServlet 提供的 getServletConfig() 方法获得

  1. //调用 GenericServlet 提供的 getServletConfig 方法获得 ServletConfig 对象
  2. ServletConfig servletConfig = this.getServletConfig();

2.3 配置 Servlet 初始化参数

① 通过配置web.xml中servlet的init-param

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. id="WebApp_ID" metadata-complete="false" version="4.0">
  6. <!--<init-param> 元素是 <servlet> 的子元素,
  7. 需要在 <servlet> 元素内使用,表示只对当前 Servlet 有效 。-->
  8. <servlet>
  9. <servlet-name>MyServlet</servlet-name>
  10. <servlet-class>cn.niit.MyServlet</servlet-class>
  11. <!-- Servlet 初始化参数 -->
  12. <init-param>
  13. <!--参数的名称-->
  14. <param-name>name</param-name>
  15. <!--参数的值-->
  16. <param-value>lionkliu</param-value>
  17. </init-param>
  18. <!-- Servlet 初始化参数 -->
  19. <init-param>
  20. <param-name>URL</param-name>
  21. <param-value>blog.lionkliu.top</param-value>
  22. </init-param>
  23. </servlet>
  24. </web-app>

② 通过配置@WebServlet注解 中initParams参数

  1. @WebServlet(urlPatterns = {"/MyServlet"}, initParams = {@WebInitParam(name = "name", value = "lionkliu"),
  2. @WebInitParam(name = "URL", value = "blog.lionkliu.top")})
  3. public class MyServlet extends HttpServlet {
  4. @Override
  5. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  6. }
  7. @Override
  8. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  9. doGet(req, resp);
  10. }
  11. }

2.4 获取 Servlet 初始化参数

  1. @WebServlet(urlPatterns = {"/MyServlet"}, initParams = {@WebInitParam(name = "name", value = "lionkliu"),
  2. @WebInitParam(name = "URL", value = "blog.lionkliu.top")})
  3. public class ReadConfigServlet extends HttpServlet {
  4. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  5. throws ServletException, IOException {
  6. response.setContentType("text/html;charset=UTF-8");
  7. PrintWriter writer = response.getWriter();
  8. // 获取ServletConfig对象
  9. ServletConfig config = getServletConfig();
  10. // 获取servletName
  11. String servletName = config.getServletName();
  12. // 返回 servlet 的初始化参数的名称的集合
  13. Enumeration<String> initParameterNames = config.getInitParameterNames();
  14. // 遍历集合获取初始化参数名称
  15. while (initParameterNames.hasMoreElements()) {
  16. // 获取初始化参数名称
  17. String initParamName = initParameterNames.nextElement();
  18. // 获取相应的初始参数的值
  19. String initParamValue = config.getInitParameter(initParamName);
  20. // 向页面输出
  21. writer.write(initParamName + " : " + initParamValue + "<br/>");
  22. }
  23. // 关闭流
  24. writer.close();
  25. }
  26. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  27. throws ServletException, IOException {
  28. doGet(request, response);
  29. }
  30. }

3. ServletContext接口

Servlet 容器启动时,会为每个 Web 应用(webapps 下的每个目录都是一个 Web 应用)创建一个唯一的 ServletContext 对象,该对象一般被称为“Servlet 上下文”。工程内部的所有servlet都共享这个对象,所以叫全局应用程序共享对象。因此 ServletContext 对象也被称为 Context 域对象。

域对象是服务器在内存上创建的存储空间,用于在不同动态资源(servlet)之间传递与共享数据。

  • 一个web项目只有一个ServletContext对象
  • ServletContext 对象的生命周期从 Servlet 容器启动时开始,到容器关闭或应用被卸载时结束。

3.1 获得 ServletContext 对象

① 通过 GenericServlet 提供的 getServletContext() 方法

  1. //通过 GenericServlet的getServletContext方法获取ServletContext对象ServletContext
  2. servletContext = this.getServletContext();

② 通过 ServletConfig 提供的 getServletContext() 方法

  1. //通过 ServletConfig的 getServletContext方法获取ServletContext对象ServletContext
  2. servletContext = this.getServletConfig().getServletContext();

③ 通过 HttpSession 提供的 getServletContext() 方法

  1. //通过 HttpSession的 getServletContext方法获取ServletContext对象ServletContext
  2. servletContext = req.getSession().getServletContext();

④ 通过 HttpServletRequest 提供的 getServletContext() 方法

  1. //通过 HttpServletRequest的 getServletContext方法获取ServletContext对象ServletContext
  2. servletContext = req.getServletContext();

3.2 ServletContext 的作用

① 获取或设置上下文初始化参数

  • 设置上下文初始化参数
    • 通过 web.xml 中的<context-param>元素可以为 Web 应用设置一些全局的初始化参数,这些参数被称为上下文初始化参数。
    • 与 Servlet 的初始化参数不同,应用中的所有 Servlet 都共享同一个上下文初始化参数。在 Web 应用的整个生命周期中,上下文初始化参数会一直存在,并且可以随时被任意一个 Servlet 访问。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
  5. http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  6. version="4.0" metadata-complete="false">
  7. <!--设置全局初始化参数 -->
  8. <context-param>
  9. <param-name>name</param-name>
  10. <param-value>lionkliu</param-value>
  11. </context-param>
  12. <context-param>
  13. <param-name>url</param-name>
  14. <param-value>blog.lionkliu.top</param-value>
  15. </context-param>
  16. </web-app>
  • 获取上下文初始化参数
    • Servlet 容器启动时,会为容器内每个 Web 应用创建一个 ServletContext 对象,并将 <context-param>元素中的上下文初始化参数以键值对的形式存入该对象中,因此我们可以通过 ServletContext 的相关方法获取到这些初始化参数。 | 返回值 | 方法 | 描述 | | —- | —- | —- | | String | getInitParameter(String name) | 根据初始化参数名 name,返回对应的初始化参数值。 | | Enumeration<String> | getInitParameterNames(); | 返回 Web 应用所有上下文初始化参数名的枚举集合,如果该 Web 应用没有上下文初始化参数,则返回一个空的枚举集合。 |
  1. @WebServlet("/ReadContextServlet")
  2. public class ReadContextServlet extends HttpServlet {
  3. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  4. throws ServletException, IOException {
  5. response.setContentType("text/html;charset=UTF-8");
  6. PrintWriter writer = response.getWriter();
  7. // 调用httpServlet父类GenericServlet的getServletContext方法获取ServletContext对象
  8. ServletContext context = super.getServletContext();
  9. // 返回 context 上下文初始化参数的名称
  10. Enumeration<String> initParameterNames = context.getInitParameterNames();
  11. while (initParameterNames.hasMoreElements()) {
  12. // 获取初始化参数名称
  13. String initParamName = initParameterNames.nextElement();
  14. // 获取相应的初始参数的值
  15. String initParamValue = context.getInitParameter(initParamName);
  16. // 向页面输出
  17. writer.write(initParamName + " : " + initParamValue + "<br/>");
  18. }
  19. // 关闭流
  20. writer.close();
  21. }
  22. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  23. throws ServletException, IOException {
  24. doGet(request, response);
  25. }
  26. }

② 实现 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标签里。

  1. <web-app>
  2. <display-name>Archetype Created Web Application</display-name>
  3. <context-param>
  4. <param-name>encoding</param-name>
  5. <param-value>Utf8</param-value>
  6. </context-param>
  7. <servlet>
  8. <servlet-name>ServletContext01</servlet-name>
  9. <servlet-class>cn.niit.xml.ServletContext01</servlet-class>
  10. <init-param>
  11. <param-name>address</param-name>
  12. <param-value>NIIT</param-value>
  13. </init-param>
  14. </servlet>
  15. <servlet-mapping>
  16. <servlet-name>ServletContext01</servlet-name>
  17. <url-pattern>/ServletContext01</url-pattern>
  18. </servlet-mapping>
  19. </web-app>

<context-param>:context-param是配置在web下面,属于上下文参数,在整个环境中都可使用,存放在getServletContext对像中,通过getServletContext().getInitParameter(“name”)获取

  1. ServletContext servletContext = this.getServletContext();
  2. String encoding = servletContext.getInitParameter("encoding");
  3. response.getWriter().println("encoding:"+encoding);

Servlet开发 - 图2

<init-param>和注解中initParams :由于init-param是配置在servlet中,属于某一下servlet,存放在getServletConfig中,因此使用方法是:getServletConfig().getInitParameter(“user1”);

由于它属于当前的servlet类,所以用this替代getServletConfig(), 使用this.getInitParmeter(“name”) , 如:

  1. /**
  2. * @author : lyj
  3. * @Timer : 2022/3/9
  4. * @Description :
  5. */
  6. @WebServlet(urlPatterns = "/ServletContext02",
  7. initParams = {@WebInitParam(name = "name", value = "lionkliu"),
  8. @WebInitParam(name = "URL", value = "blog.lionkliu.top")})
  9. public class ServletContext02 extends HttpServlet {
  10. @Override
  11. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  12. response.setContentType("text/html;charset=UTF-8");
  13. PrintWriter writer = response.getWriter();
  14. // 获取ServletConfig对象
  15. ServletConfig config = getServletConfig();
  16. // 返回 servlet 的初始化参数的名称的集合
  17. Enumeration<String> initParameterNames = config.getInitParameterNames();
  18. // 遍历集合获取初始化参数名称
  19. while (initParameterNames.hasMoreElements()) {
  20. // 获取初始化参数名称
  21. String initParamName = initParameterNames.nextElement();
  22. // 获取相应的初始参数的值
  23. String initParamValue = config.getInitParameter(initParamName);
  24. //等同于this.getInitParameter(initParamName);
  25. // 向页面输出
  26. writer.write(initParamName + " : " + initParamValue + "<br/>");
  27. }
  28. // 关闭流
  29. writer.close();
  30. }
  31. @Override
  32. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  33. super.doPost(req, resp);
  34. }
  35. }

总结:

  • <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 对象向页面输出时有两种方式:字节流、字符流,这两种方式输出中文时都有可能出现乱码。下面我们针对这两种方式出现乱码的原因以及解决方案进行介绍。

使用字节流输出中文
  1. ServletOutputStream outptuStream = response.getOutputStream();outputStream.write(“编程帮 www.biancheng.net”.getBytes());

使用字节流向页面输出中文是否会出现乱码问题?不一定。

乱码原因:

字节流输出中文是否出现乱码,取决于中文转成字节数组时与浏览器打开时采用的字符集是否一致。若两者保持一致,则不会出现乱码问题,若不一致就会出现乱码问题。

解决方案:

将中文转成字节数组时和浏览器默认采用的字符集保持一致即可,代码如下。

  1. 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 种的方式。

第一种方式:

  1. // 设置response缓冲区的编码response.setCharacterEncoding("UTF-8");// 设置浏览器打开文件所采用的编码response.setHeader("Content-Type", "text/html;charset=UTF-8");// 输出中文response.getWriter().write("编程帮 www.biancheng.net");

第二种方式:

  1. 纯文本复制
  2. response.setContentType("text/html;charset=UTF-8");response.getWriter().write("编程帮 www.biancheng.net");

二、请求转发&重定向

1. 请求转发

Web 应用在处理客户端的请求时,经常需要多个 Web 资源共同协作才能生成响应结果。但由于 Serlvet 对象无法直接调用其他 Servlet 的 service() 方法,所以 Servlet 规范提供了 2 种解决方案:

  1. 请求转发
  2. 请求包含(了解即可)

对应着javax.servlet.RequestDispatcher接口中的:

返回值 方法 功能描述
void forward(ServletRequest request, ServletResponse response); 用于将请求转发给另一个 Web 资源。该方法必须在响应提交给客户端之前被调用,否则将抛出 IllegalStateException 异常
void include(ServletRequest request, ServletResponse response) ; 用于将其他的资源作为当前响应内容包含进来

获取 RequestDispatcher 对象

  1. 调用 ServletContext 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,必须为绝对路径;
  2. 调用 ServletRequest 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,可以为绝对路径,也可以为相对路径。

绝对路径是指以符号“/”开头的路径,“/”表示当前 Web 应用的根目录。相对路径是指相对当前 Web 资源的路径,不以符号“/”开头。

请求转发的特点

请求转发具有以下特点:

  1. 请求转发不支持跨域访问,只能跳转到当前应用中的资源。
  2. 请求转发之后,浏览器地址栏中的 URL 不会发生变化,因此浏览器不知道在服务器内部发生了转发行为,更无法得知转发的次数。
  3. 参与请求转发的 Web 资源之间共享同一 request 对象和 response 对象。
  4. 由于 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 对象。

重定向流程:

  1. 用户在浏览器中输入 URL,请求访问服务器端的 Web 资源。
  2. 服务器端的 Web 资源返回一个状态码为 302 的响应信息,该响应的含义为:通知浏览器再次发送请求,访问另一个 Web 资源(在响应信息中提供了另一个资源的 URL)。
  3. 当浏览器接收到响应后,立即自动访问另一个指定的 Web 资源。
  4. 另一 Web 资源将请求处理完成后,由容器把响应信息返回给浏览器进行展示。

实现重定向

HttpServletResponse 接口中的 sendRedirect() 方法用于实现重定向。

返回值类型 方法 描述
void sendRedirect(String location) 向浏览器返回状态码为 302 的响应结果,让浏览器访问新的 URL。若指定的 URL 是相对路径,Servlet 容器会将相对路径转换为绝对路径。参数 location 表示重定向的URL。

3. 请求转发和重定向的区别

转发和重定向都能实现页面的跳转,但是两者也存在以下区别。

区别 请求转发 重定向
浏览器地址栏 URL 是否发生改变
是否支持跨域跳转
请求与响应的次数 一次请求和一次响应 两次请求和两次响应
是否共享 request 对象和 response 对象
是否能通过 request 域对象传递数据
速度 相对要快 相对要慢
行为类型 服务器行为 客户端行为

三、Cookie&Session

1. 为什么要有会话技术

因为HTTP是一个基于请求与响应模式的无状态协议。

无状态主要指 2 点:

  1. 协议对于事务处理没有记忆能力,服务器不能自动维护用户的上下文信息,无法保存用户状态;
  2. 每次请求都是独立的,不会受到前面请求的影响,也不会影响后面的请求。

当浏览器发送 HTTP 请求到服务器时,服务器会响应客户端的请求,但当同一个浏览器再次发送请求到该服务器时,服务器并不知道它就是刚才那个浏览器,即 HTTP 协议的请求无法保存用户状态。

通常情况下,用户通过浏览器访问 Web 应用时,服务器都需要保存和跟踪用户的状态。例如,用户在某购物网站结算商品时,Web 服务器必须根据请求用户的身份,找到该用户所购买的商品。由于 HTTP 协议是无协议的,无法保存和跟踪用户状态,所以需要其他的方案来解决问此题,它就是会话技术。

2. 什么是会话技术

从打开浏览器访问某个网站,到关闭浏览器的过程,称为一次会话。会话技术是指在会话中,帮助服务器记录用户状态和数据的技术。

常用的会话技术分为两种:

  1. Cookie :客户端会话技术
  2. 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工作流程

Servlet开发 - 图3

  1. 客户端请求访问容器内的 Web 资源。
  2. Servlet 容器接收请求,并针对本次请求分别创建一个 request 对象和 response 对象。
  3. 请求到达 Web 资源之前,先调用 Filter 的 doFilter() 方法,检查 request 对象,修改请求头和请求正文,或对请求进行预处理操作。
  4. 在 Filter 的 doFilter() 方法内,调用 FilterChain.doFilter() 方法,将请求传递给下一个过滤器或目标资源。
  5. 目标资源生成响应信息返回客户端之前,处理控制权会再次回到 Filter 的 doFilter() 方法,执行 FilterChain.doFilter() 后的语句,检查 response 对象,修改响应头和响应正文。
  6. 响应信息返回客户端。

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


参考:

廖雪峰官网

servlet教程

JSP教程

web注解:https://segmentfault.com/a/1190000017139837?utm_source=sf-similar-article