项目创建

1、在 idea 中创建 maven 项目

2、创建 web 目录:
在 main 目录下创建 webapp 文件夹,然后再 webapp 文件夹内创建 WEB-INF 文件夹

  • webapp 目录就是 web 目录,里面存放动态、静态资源和配置文件,如 jsp、css、html等
  • WEB-INF 目录下只有一个文件,就是web的配置文件:web.xml

image.png

3、idea 自动生成 web.xml:

  • 【File】->【Project Structure】

    image.png

  • 【Modules】-> 选择当前项目下的 web 文件夹,然后点击右边的加号,选择 web.xml

    屏幕截图 2020-11-24 221919.png

  • 调整 web.xml 文件的位置,然后依次点击 OK 即可

    屏幕截图 2020-11-24 221919.png

导入依赖项

在 pom.xml 文件中导入Servlet API:Servlet 有两个版本,一个是3.0版本之前,为 servlet-api;一个是3.0版本之后,为 javax.servlet-api,使用时根据需求进行导入

  1. <!-- Servlet3.0之前的版本为servlet-api -->
  2. <dependency>
  3. <groupId>javax.servlet</groupId>
  4. <artifactId>servlet-api</artifactId>
  5. <version>2.5</version>
  6. <scope>provided</scope>
  7. </dependency>
  8. <!-- Servlet3.0之后的版本为javax.servlet-api -->
  9. <dependency>
  10. <groupId>javax.servlet</groupId>
  11. <artifactId>javax.servlet-api</artifactId>
  12. <version>4.0.1</version>
  13. <scope>provided</scope>
  14. </dependency>

Servlet 创建

image.png
Servlet 的类结构如上,一般是继承 Servlet 的 HTTP 协议的实现类 —— HttpServlet,然后覆写其中相关方法。

Servlet 一般用来处理 HTTP 请求,而 HTTP 有多种请求方式,常见的有 GET、POST、PUT、DELETE 等,因此存在两种实现方式:
1、直接覆写 service() 方法,这样tomcat会根据请求方法自动调用不同的处理方法

  1. public class OtherServlet extends HttpServlet {
  2. @Override
  3. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  4. // 处理请求的业务逻辑
  5. }
  6. }

2、针对每种不同的请求分别作处理:

  1. public class OtherServlet extends HttpServlet {
  2. @Override
  3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  4. // 一般会在doGet方法中进行业务逻辑,然后在其他请求方法中直接调用doGet()
  5. }
  6. @Override
  7. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  8. doGet(req, resp);
  9. }
  10. }

为Servlet配置 url 映射:
1、注解方式,在相关类上添加 @WebServlet 注解
image.png
2、在 web.xml 文件中进行配置:
image.png

HttpServletRequest 的常用方法

1、获取请求行数据:

String getMethod() 获取请求方式
String getContextPath() 获取虚拟目录[重要]
String getServletPath() 获取servlet路径
String getQueryString() 获取get方式请求参数
String getRequestURI() 获取请求URI[重要]
StringBuffer getRequestURL() 获取请求的URL
String getProtocol() 获取协议及版本
String getRemoteAddr() 获取客户端的IP地址
String getRemoteHost() 获取客户端的主机名
int getRemotePort() 获取客户端的端口
  • URI:Uniform Resource Identifier,统一资源标志符
    • 访问资源的命名机制
    • 存放资源的主机名
    • 资源自身的名称
  • URL:Uniform Resource Locator,统一资源定位符

2、获取请求头数据:

String getHeader(String name) 通过请求头的名称获取请求头的值
Enumeration getHeaderNames() 获取所有的请求头名称

3、获取请求体数据:获取流对象

只有POST请求方式,才有请求体,在请求体中重新封装了POST的请求参数

BufferedReader getReader() 获取字符输入流,只能操作字符数据
ServletInputStream getInputStream() 获取字节输入流,可以操作所有类型数据

4、获取请求参数:通用方式,不管GET或POST都可以使用该方法

String getParameter(String name) 根据参数名称获取参数值
String[] getParameterValues(String name) 根据参数名称获取参数值的数组
Enumeration getParameterNames() 获取所有请求的参数名称
Map getParameterMap() 获取所有参数的map集合,key为参数名,value为参数值

请求乱码解决:由于 request 属于接受客户端的参数,所以存在其默认的语言编码(ISO-8859-1,此编码不支持中文),所以解析时会出现乱码,想要解决乱码问题,需要设置 request 中的编码方式,告诉服务器以何种方式解析数据,或接收到乱码数据中,再通过相应的编码格式还原
对于 get 请求:

  1. // tomcat8之前
  2. String str = new String(request.getParameter(name).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
  3. // tomcat8之后已经解决此问题,无需自行设置

对于 post 请求:

  1. // 在获取参数前,设置request的编码与页面编码一致,一般写在filter中
  2. req.setCharacterEncoding("utf-8");

三大域对象

域对象:一个有作用范围的对象,可以在范围内共享数据

Request 域

Request 域:即 javax.servlet.http.HttpServletRequest 对象,可以在一次请求中传递数据,主要用于请求转发的多个资源中共享数据

void setAttribute(String name,Object obj) 存储数据到request域
Object getAttribute(String name) 通过键获取值
void removeAttribute(String name) 通过键移除键值对

请求转发

请求转发:指服务器收到请求后,从一次资源跳转到另一个资源的操作叫做请求转发

请求转发属于服务器内部的资源跳转方式,无法跳转到外部的资源

  1. // 通过request获取请求转发器对象并进行转发
  2. req.getRequestDispatcher("views/index.jsp").forward(req, resp);

请求转发的特点:

  1. 浏览器地址栏路径不会发生变化
  2. 只能转发到服务器的内部资源(如 WEB-INF/ 下的静态资源,另一个 Servlet 等)
  3. 请求转发是一次请求,可以共享 Request 域中的数据
  4. 可以转发到 WEB-INF/ 目录下

注意:

  • 从外部(浏览器)无法直接访问 WEB-INF 目录下的资源,但是可以通过请求转发进行访问
  • 执行完跳转的 Servlet 的业务后,然后再回来继续执行原来的 servlet 的业务

测试一:测试一个 Servlet 存储属性,在另一个 Servlet 进行获取
DemoServletOne:
image.png
DemoServletTwo:
image.png
在浏览器中访问 http://127.0.0.1:8080/one,控制台输出如下:
image.png

测试二:在 servlet 中存储数据,在 jsp 页面获取
DemoServletThree:
image.png
WEB-INF/views/index.jsp:
image.png
在浏览器中访问 http://localhost:8080/three,在控制台输出:
image.png

HttpResponse 对象

HttpResponse对象的目的是为了响应浏览器发送给Web应用程序的HTTP请求,表示Web应用程序发送回浏览器的HTTP响应

常用方法:
1、获取响应流:

  • 字符流:response.getWriter()
  • 字节流:response.getOutputStream()

响应乱码问题:在响应中,如果响应的内容包含中文,则可能出现乱码,这是因为服务器响应的数据会经过编码成二进制进行传输,然后客户端再解码成相应的字符,当编码和解码用的不是同一字符集时则会出现乱码
字符流:getWriter() 获取的字符流,相应中文一定会出乱码,因为服务器端默认会使用 ISO-8859-1格式的编码,该编码不支持中文

  1. // 设置服务器使用utf-8编码
  2. response.setCharacterEncoding("utf-8");
  3. // 指定客户端的编码方式
  4. response.setHeader("content-type","text/html;charset=UTF-8");
  5. // 同时指定两端编码方式,等效于上面两句
  6. response.setContentType("text/html;charset=UTF-8");

字节流:getOutputStream() 同样需要指定两端的编码格式

  1. // 指定客户端的编码方式
  2. response.setHeader("content-type","text/html;charset=UTF-8");
  3. ServletOutputStream os = response.getOutputStream();
  4. // 服务端使用UTF-8编码
  5. os.write("世界".getBytes(StandardCharsets.UTF_8));

2、响应给浏览器要发送的内容类型:response.setHeader(“Content-Type”,”text/html”);
3、设置响应信息的长度:response.setHeader(“Content-Length”,”31654”);
4、重定向:response.sendRedirect(“index.jsp”);

重定向:是服务器直到客户端的行为

当客户端发起第一个请求,被服务器接受处理后,服务器会进行响应,在响应的同时,会给客户端一个新的地址(下次请求的地址 response.sendRedirect(url)),客户端接受到响应后,会根据服务器给的新地址发起第二次请求,服务器接受请求并作出响应,重定向完成

重定向是客户端行为,具有两次请求,第一次响应时,会给出一个含有 location (再次请求的地址)的头信息,并且响应状态码为 301(永久重定向)、302(临时重定向)
重定向与请求转发:

请求转发 重定向
request.getRequestDispatcher().forward() response.sendRedirect()
一次请求,数据在Request域中共享 两次请求,数据在Request域中不共享
服务端行为 客户端行为
地址栏不发生变化 地址栏发生变化
只能跳转到站内资源 可以跳转到任意地址

Session 域

Cookie:即 javax.servlet.http.Cookie,是浏览器提供的一种技术(需要浏览器的支持),通过服务器的程序能将一些只须保存在客户端、或者在客户端进行处理的数据,放在本地的计算机上,不需要通过网络传输
优点:提高网页处理的效率,减少服务器的负载
缺点:Cookie 保存在客户端,安全性较差
应用:常见的记住密码功能
格式:键值对用“=”连接,多个键值对间用“;”隔开
工作流程:第一次请求时服务端创建 Cookie 并返回给客户端,客户端保存该 Cookie,再此请求时会把该 Cookie 待带回服务器

Cookie 的创建与发送:

  1. // javax.servlet.http.Cookie是java中专门用来处理Cookie的类
  2. // 1.创建Cookie
  3. Cookie cookie = new Cookie("username","Lily");
  4. // 2.将Cookie添加到响应头
  5. resp.addCookie(cookie);

Cookie 的获取:在服务端只提供了一个 getCookies() 的方法来获取客户端回传的所有 Cookie 组成的一个数组,如果需要获取单个 Cookie 则需要通过遍历,getName() 获取 Cookie 名称,getValue() 获取Cookie的值

  1. // 1.获取Cookie数组
  2. Cookie[] cookies = req.getCookies();
  3. // 2.Cookie是否为空
  4. if (Objects.nonNull(cookies)) {
  5. // 遍历Cookie数组
  6. for (Cookie cookie : cookies) {
  7. // 获取Cookie名称
  8. System.out.println(cookie.getName());
  9. // 获取Cookie的值
  10. System.out.println(cookie.getValue());
  11. }
  12. }

Cookie 的修改与删除:

  • 修改:创建同名Cookie(该同名指Cookie除了value和maxAge之外,其他属性均相同),进行覆盖
  • 删除:设置Cookie的 maxAge = 0

Cookie 的属性:

  1. String name:该Cookie的名称,一旦创建,不可更改
  2. Object value:该Cookie的值,值为Unicode字符时,需要为字符编码,值为二进制数据时,需要使用BASE64编码
  3. int maxAge:到期时间,指定该Cookie何时有效,通过 setMaxAge(int time) 进行设置,不同的到期时间设置影响Cookie的存活时间
    • 负整数(默认 -1):不存储该Cookie,只在浏览器内存中存活,一旦关闭浏览器窗口,Cookie就会失效
    • 正整数:表示存储的时间,单位为秒,该Cookie会被存储到硬盘中,哪怕关闭浏览器,该Cookie也会存活相应时间
    • 0:表示删除该Cookie,当需要删除某个Cookie时,可以将该Cookie的到期时间设置为0
  4. boolean secure:该Cookie是否仅被安全协议传输,即SSL、https
  5. String path:该Cookie的使用路径
    • 设置为 / ,则本域名下所有的程序都可以访问该 Cookie
    • 设置为 /java/ ,则只有contextPath为 /java 的程序才可以访问该 Cookie
    • 注意:该参数必须以字符 / 结尾
  6. String domain:可以访问该Cookie的域名
    • 如果设置为 .google.com ,则所有以 google.com 结尾的域名均能访问该 Cookie
    • 注意:该参数第一个字符必须为 . 或者完整域名如 www.baidu.com
  7. String comment:该Cookie的用途
  8. int version:该Cookie使用的版本号
    • 0:表示该Cookie遵循Netscape的Cookie规范
    • 1:表示遵循W3C的RFC2019规范

注意事项:

  1. Cookie保存在客户端
  2. 从客户端读取Cookie时,只能读取Cookie的name和value,其他属性都是不可读的,也不会被提交到服务端

Cookie 中存在中文时的处理:Chrome现在支持中文的Cookie值

  1. /* 创建Cookie */
  2. String name = "姓名";
  3. String value = "李四";
  4. // 通过URLEncoder.encode()来进行编码
  5. name = URLEncoder.encode(name, "utf-8");
  6. value = URLEncoder.encode(value, "utf-8");
  7. // 创建Cookie对象
  8. Cookie cookie = new Cookie(name, value);
  9. // 发送Cookie对象
  10. res.addCookie(cookie);
  11. /* 获取Cookie */
  12. URLDecoder.decode(cookie.getName(), "utf-8");
  13. URLDecoder.decode(cookie.getValue(), "utf-8")

3、HttpSession:java.servlet.htto.HttpSession 对象,session 为HTTP协议的一部分,用于标识一次会话,或者说确认一个用户,并且在一次会话(一个用户的多次请求)期间共享数据。session 保存在服务端,客户端仅保留session的标识符 sessionID。

JSESSIONID:标识符,用于标识一次会话,会话过程如下,每当一次请求达到服务器,如果开启了会话(访问了 session),服务器第一步会查看是否从客户端回传了一个名为 JSESSIONID 的 cookie

  • 如果没有则认为这是一个新的会话,则会创建一个新的session对象,并用唯一的 sessionId 为此次会话做一个标志
  • 如果有 JSESSIONID 回传,服务器会根据该值去查看是否含有 id 为 JSESSIONID 值的 session 对象,如果没有,则会认为是一个新的会话,重新创建新的session对象,并标志此次会话;如果找到了相应的session对象,则认为是之前标志过的一次会话,返回该session对象,达到数据共享

JSESSIONID 本质是一个特殊的 Cookie ,当用户请求到达时,如果访问了 session,则服务器会创建一个名为 JSESSIONID,值为获取到的 session(不管是获取到还是新创建)的 sessionId 的 cookie 对象,并添加到 response 对象中,响应给客户端,有效时间为关闭浏览器。

Session 的使用依赖于客户端的支持,当客户端不支持 Cookie 或 Cookie 被禁用时,可以使用另一种解决方案:URL 重写 URL重写的原理:将该用户Session的id信息重写到URL地址中。服务器能够解析重写后的URL获取Session的id。这样即使客户端不支持Cookie,也可以使用Session来记录用户状态。HttpServletResponse 类提供了 encodeURL(Stringurl) 实现URL地址重写

session 域对象:session用来标识一次会话,在一次会话中数据是可以共享的,这是 session 作为域对象存在

  1. // 获取Session对象
  2. // 设置Session域对象
  3. session.setAttribute("uname", "admin");
  4. // 获取指定名称的session对象
  5. String uname = (String) session.getAttribute("uname");
  6. // 移除指定名称的session域对象
  7. session.removeAttribute("uname");

session 对象的销毁:

  • 默认到期时间:当客户端第一次请求servlet并且操作session时,session对象生成,tomcat中session的默认存活时间为30min,即客户端无操作的时间,一旦有操作,则重新计时

    1. <!-- 在web.xml中进行配置 -->
    2. <session-config>
    3. <session-timeout>30</session-timeout>
    4. </session-config>
  • 在程序中自定义:

    • 设置:session.setMaxInactiveInterval(int) 设置session的最大不活动时间,单位为 s
    • 查看:session.getMaxInactiveInterval(int) 获取session的最大不活动时间,单位为 s
  • 立即失效:session.invalidate() 让当前session立即失效
  • 关闭浏览器:session只会存活在浏览器的内存中,一旦关闭浏览器,立即失效

ServletContext 域

每一个 web 应用有且只有一个 servletContext 对象,又称 Application 对象,从对象可知,该对象是与应用程序相关的。在 web 容器启动的时候,会为每一个 web 应用程序创建一个对象的 ServletContext 对象。

获取ServletContext对象的途径:有三种方式

  1. // 通过request对象获取
  2. ServletContext servletContext = req.getServletContext();
  3. // 通过session对象获取
  4. ServletContext servletContext = session.getServletContext();
  5. // 通过servletConfig对象获取,在Servlet标准中提供了ServletConfig方法
  6. ServletConfig servletConfig = getServletConfig();
  7. ServletContext servletContext = servletConfig.getServletContext();
  8. // 直接获取
  9. ServletConfig servletConfig = getServletConfig();

ServletContext 的生命周期:在整个应用程序中有效,服务器关闭后失效

Servlet Context 对象有两大作用:
1、作为域对象用来共享数据,此时数据在整个应用程序中共享

  1. ServletContext servletContext = getServletContext();
  2. // 设置域对象
  3. servletContext.setAttribute("name","value");
  4. // 获取域对象
  5. servletContext.getAttribute("name");
  6. // 移除域对象
  7. servletContext.removeAttribute("name");

2、该对象保存了应用程序相关信息

  1. ServletContext servletContext = getServletContext();
  2. // 获取当前服务器的版本信息
  3. String serverInfo = servletContext.getServerInfo();
  4. // 获取项目的真实路径(会获取该应用的根目录在所在操作系统中的真实目录)
  5. String realPath = servletContext.getRealPath("/");

ServletContext 应用:

  1. 多个Servlet可以通过ServletContext对象来实现数据间的共享:
    • Session 在同一客户端中共享数据
    • ServletContext 在所有客户端中实现数据共享
  2. 实现Servlet的请求转发:两个转发效果是一样的

    1. // request请求转发
    2. request.getRequestDispatcher("/url").forward(request, response);
    3. // servletContext请求转发
    4. this.getServletContext().getRequestDispatcher("/url").forward(request, response);
  3. 获取Web应用的初始化参数:

整个web应用共享的初始化参数:

  • 在web.xml中使用标签为servlet配置初始化参数,然后使用 ServletContext.getInitParameter() 获取 ```xml name Lily

password xxxxxxx

  1. - Servlet中读取配置参数:
  2. ```java
  3. // 在servlet中获取在web.xml中配置使用<context-param>标志配置的初始化参数
  4. String name = this.getServletContext().getInitParameter("name");
  5. String password = this.getServletContext().getInitParameter("password");

每个servlet独享的初始化参数:

  • 在web.xml中使用标签为servlet配置初始化参数,然后使用 servletConfig.getInitParameter() 获取 ```xml demo top.songfang.servlet.DemoServlet encoding utf-8

demo /demo

  1. - 在该servlet中通过 servletConfig.getInitParameter() 获取
  2. ```java
  3. String encoding = this.getServletContext().getInitParameter("encoding");
  1. 利用ServletContext读取资源文件(比如Properties文件)
  • 文件在 webapp 文件夹下,即 WEB 应用的根目录,这时可以使用 ServletContext 来读取该资源文件

image.png

  • 如果文件放在了 src 目录下,则需要使用类加载器去读取:

    1. // 类加载器默认读取路径是 src 目录
    2. InputStream resource = DemoServlet.class.getClassLoader().
    3. getResourceAsStream("db.properties");
  • 如果文件放在了src目录的某个包下,如com.example,则需要加上包的路径:

    1. // 类加载器默认读取路径是 src 目录
    2. InputStream resource = DemoServlet.class.getClassLoader().
    3. getResourceAsStream("com/example/db.properties");

总结

  1. Request域对象:在一次请求中有效,请求转发有效,重定向失效
  2. Session域对象:在一次会话中有效,请求转发和重定向都有效,session销毁后失效
  3. ServletContext域对象:在整个应用程序中有效,服务器关闭后失效