JavaWeb知识回顾及查漏补缺

基本概念

JavaWeb是指所有通过Java语言编写的,可以通过浏览器访问的程序的总称;

JavaWeb是基于请求和响应来开发的;

什么是请求

请求是指客户端给服务器发送数据

什么是响应

响应是指服务器给客户端回传数据

请求和响应的关系

请求和响应是成对出现的,有请求就有响应

Tomcat

安装目录详解

  • bin:用来存放Tomcat服务器的可执行程序
  • conf:用来存放Tomcat服务器的配置文件
  • lib:用来存放Tomcat服务器的jar包
  • logs:用来存放Tomcat服务器运行时输出的日志信息
  • temp:用来存放Tomcat运行时产生的临时数据
  • webapps:用来存放部署的web工程
  • work:是Tomcat工作时的目录,用来存放Tomcat运行时jsp翻译为servlet的源码,和session钝化的目录

Servlet

基本概念

  • Servlet是JavaEE规范之一,是一个接口
  • Servlet是Javaweb三大组件之一,三大组件分别是:Servlet程序,Filter过滤器,Listener监听器
  • Servlet是运行在服务器上的一个Java程序,它可以接受客户端发送过来的请求,并响应数据给客户端

访问地址配置

  • xml方式:在web.xml中配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <web-app>
    <!-- servlet标签给Tomcat配置servlet程序 -->
    <servlet>
    <!-- 给servlet程序起唯一标志名 -->
    <servlet-name>CustomServlet</servlet-name>
    <!-- servlet程序类的全路径 -->
    <servlet-class>com.zhuweitung.javaweb.custom.CustomServlet</servlet-class>
    </servlet>

    <!-- 给servlet程序配置访问地址 -->
    <servlet-mapping>
    <!-- 关联上面的唯一标志名 -->
    <servlet-name>CustomServlet</servlet-name>
    <!-- 配置访问地址 -->
    <url-pattern>/custom</url-pattern>
    </servlet-mapping>

    </web-app>
  • 注解方式:在Servlet实现类上添加@WebServlet注解,value属性为访问路径

    1
    2
    3
    @WebServlet(name = "CustomServlet", value = "/custom")
    public class CustomServlet implements Servlet {
    }

生命周期

  • 执行Servlet构造器方法:若没有配置自动加载,在第一次请求servlet时调用
  • 执行init初始化方法:若没有配置自动加载,在第一次请求servlet时调用
  • 执行service方法
  • 执行destroy销毁方法

继承体系

  • Servlet:接口,只负责定义Servlet程序的规范

  • ServletConfig:Servlet程序的配置信息类

    • 获取Servlet程序唯一标识名
    • 获取初始化参数 getInitParameter
    • 获取ServletContext对象
  • GenericServlet:抽象类,做了一些空实现,并持有一个ServletConfig类的引用

  • ServletContext:接口,表示Servlet的上下文,一个web工程只有一个ServletContext对象实例(是一个域对象),在工程启动时创建,工程停止时销毁

    • 获取web.xml中配置的上下文参数 context-param

      1
      2
      3
      4
      <context-param>
      <param-name>jdbcUrl</param-name>
      <param-value>jdbc:mysql://localhost:3306/tmp</param-value>
      </context-param>
      1
      String jdbcUrl = ctx.getInitParameter("jdbcUrl");
    • 获取当前工程路径(发布名)

      1
      String contextPath = ctx.getContextPath();
    • 获取工程部署后再服务器上的绝对路径

      1
      String realPath = ctx.getRealPath("/");
    • 像Map类一样存取数据,setAttribute、getAttribute、removeAttribute

  • HttpServlet:抽象类,实现了service方法,并实现了请求的分发处理

  • 继承HttpServlet的类根据需要实现doGet和doPost方法

HTTP协议

请求的HTTP协议格式

GET请求报文示例:

1
2
3
4
5
6
7
8
9
10
11
12
GET /custom HTTP/1.1
Host: localhost:8123
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.56
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://localhost:8123/custom
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
Cookie: xxx
Accept-Encoding: gzip, deflate


POST请求报文示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /x/click-interface/web/heartbeat HTTP/1.1
Host: api.bilibili.com
Connection: keep-alive
Accept: application/json, text/plain, */*
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.56
Origin: https://www.bilibili.com
Referer: https://www.bilibili.com/video/xxx
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
Cookie: xxx
Accept-Encoding: gzip, deflate
Content-Length: 336

start_ts=1677388143&mid=1861509
报文格式
  • 请求行

    • 请求方式:POST/GET
    • 请求的资源路径[?请求参数],如 /custom?id=1
    • 请求的协议的版本号,如 HTTP/1.1
  • 请求头:键值对形式,常见键如下

    • Accept:告诉服务器,客户端可以接收的数据类型
    • Accept-Encoding:告诉服务器,客户端可以接收的数据编码格式
    • Accept-Language:告诉服务器,客户端可以接收的语言类型
    • User-Agent:浏览器信息
    • Host:请求的服务器ip端口或域名
    • Connection:告诉服务器请求连接如何处理
      • keep-alive:回传数据不要马上关闭,保持一小段时间的连接
      • closed:马上关闭
    • Cache-Control:表示如何控制缓存,no-cache表示不缓存
    • Cookie
    • Referer:请求发起时浏览器地址栏中的地址(请求来源)
    • Content-Type:表示发送的数据的类型,格式为 type/subtype;parameter;
      • application/x-www-form-urlencoded:提交的数据格式为 name=value&name=value,然后对其进行url编码
      • multipart/form-data:表示以多段的形式提交数据给服务器(以流的形式提交,用于上传)
      • application/json:提交的数据以json形式传输,会将数据序列化为json字符串放进请求体中
    • Content-Length:表示请求体的长度
  • 空行,这个空行属于请求头

  • 请求体:发送给服务器的数据,若没有数据也是空行

响应的HTTP协议格式

报文示例:

1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Date: Tue, 5 Jun 2021 05:23:05 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: https://www.bilibili.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin,No-Cache,X-Requested-With,If-Modified-Since,Pragma,Last-Modified,Cache-Control,Expires,Content-Type,Access-Control-Allow-Credentials,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Cache-Webcdn,x-bilibili-key-real-ip,x-backend-bili-real-ip,x-risk-header
Cross-Origin-Resource-Policy: cross-origin
Content-Length: 32

{"code":0,"message":"0","ttl":1}
报文格式
  • 响应行
    • 协议和版本号,如 HTTP/1.1
    • 响应状态码,如 200
    • 响应状态描述符。如 OK
  • 响应头:键值对形式,常见键如下
    • Date:响应时间
    • Content-Type:表示响应数据的类型,格式为 type/subtype;parameter;
    • Content-Length:响应体的长度
  • 空行,这个空行属于响应头
  • 响应体
常见响应码
  • 200:请求成功
  • 302:重定向
  • 404:服务器收到请求,但是请求的资源不存在
  • 500:服务器收到请求,但是服务器内部错误(代码错误)

HttpServlet

HttpServletRequest

继承ServletRequest接口

作用

当有请求进入tomcat服务器时,tomcat服务器就会把请求过来的http协议信息解析好封装到request对象中;

然后传递到Servlet的service方法中;

通过HttpServletRequest对象可以获取到所有请求的信息;

常用方法
  • getRequestURI:获取请求的资源路径
  • getRequestURL:获取请求的统一资源定位符(绝对路径)
  • getRemoteHost:客户端的ip地址
  • getHeader:获取请求头
  • getParameter:获取请求参数
  • getParameterValues:获取请求参数,多值
  • getMethod:获取请求方式,POST/GET
  • setAttribute:设置属性
  • getAttribute:获取属性
  • removeAttribute:移除属性
乱码问题解决
1
request.setCharacterEncoding("utf-8");
请求转发
1
request.getRequestDispatcher("/other").forward(req, resp);

特点:

  • 地址栏不发生变化
  • 只有一次请求
  • 转发地址中不需要加上下文地址
  • 请求域中的数据不会丢失
  • 是服务端行为
  • 不能访问工程以外的资源

HttpServletResponse

继承ServletResponse接口

作用

通过HttpServletResponse对象来设置需要响应给客户端的数据

输出流
  • 字节流:响应二进制数据,常用于下载
  • 字符流:响应字符数据

一个响应同时只能使用一个流

乱码问题解决
1
2
3
4
5
6
// 设置服务器字符集
response.setCharacterEncoding("utf-8");
// 通过响应头设置浏览器使用的字符集
response.setHeader("Content-Type", "text/html; charset=utf-8;");
// 同时设置服务器和浏览器使用的字符集,等价于上面两行代码,此方法必须在获取输出流之前调用才有效
response.setContentType("text/html; charset=utf-8;");
请求重定向
1
response.sendRedirect("/other");

特点:

  • 地址栏中地址变为重定向后的地址
  • 请求两次(客户端第一次请求后响应302状态,再请求重定向地址)
  • 重定向地址中需要加上下文地址以保证路径的正确
  • 请求域中的数据丢失
  • 是客户端行为

JSP

基本概念

JSP全称为 Java server pages;

JSP的主要作用是代替servlet程序回传html页面的数据;

本质

JSP页面本质上是一个servlet程序;

当第一次访问JSP页面的时候,tomcat服务器会把JSP页面翻译为一个Java源文件(.java文件),并将它编译为字节码文件(.class文件);具体的文件可以在tomcat的work目录下查看到;

index.jsp翻译的Java源文件示例:

1
2
3
4
5
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
// ...
}

其中HttpJspBase类继承自HttpServlet类

语法

page指令

page指令可以修改JSP页面中一些重要的属性或者行为

1
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
  • contentType:响应数据类型,是 response.setContentType() 方法的参数值
  • pageEncoding:JSP页面本身字符集
  • language:JSP翻译后的文件格式,暂时只支持Java
  • import:导入包、类
  • autoFlush:设置输出流缓冲区满了之后,是否自动刷新缓冲区,默认为true
  • buffer:设置输出流缓冲区大小,默认8kb
  • errorPage:设置当前JSP页面出错时,自动跳转去的错误页面路径
  • isErrorPage:设置当前JSP页面是否错误信息页面,默认是false,若为true可以获取异常信息
  • session:设置当前JSP页面是否会创建HttpSession对象,默认是ture
  • extends:设置JSP页面翻译出来的Java类默认继承的类
常用脚本
  • 声明脚本:<%!%>,其中写的内容将来会直接翻译在Servlet类中,可以声明类、方法、属性、全局变量

  • 代码脚本:<%%>

    • 代码脚本会被翻译在_jspService方法中

    • 可以使用声明中的局部变量或者方法以及_jspService方法中能访问的对象

    • 可以和表达式脚本组合使用

      1
      2
      3
      4
      5
      6
      7
      <ul>
      <%for (User user : users) {%>
      <li>
      <%=user.name%>,<%=user.age%>
      </li>
      <%}%>
      </ul>
  • 表达式脚本:<%=%>,将已经声明的变量或者表达式输出到网页上

    • 所有表达式脚本都会被翻译到_jspService方法中
    • 表达式脚本都会被翻译成为out.write()输出到页面上
    • 表达式脚本中可以使用_jspService方法中能访问的对象
    • 表达式脚本中的表达式不能以分号结束

内置对象

  • request:HttpServletRequest,请求对象
  • response:HttpServletResponse,响应对象
  • pageContext:PageContext,上下文对象
  • session:HttpSession,会话对象
  • application:ServletContext对象
  • config:ServletConfig对象
  • out:JspWriter,输出流对象
  • page:Object,指向当前jsp的对象
  • exception:Throwable,异常对象,isErrorPage开启后才有
域对象

内置对象中request、pageContext、session、application为域对象

四大域对象都可以像Map一样存取数据,功能一样,但对数据的存取范围不一样

  • pageContext:jsp页面范围内

  • request:一次请求内

  • session:一个会话内

  • application:整个工程范围内(只要web工程不停止,数据都在)

response与out对象

这两个对象都可以向页面输出内容,并且各自持有一个缓冲区

当jsp页面代码执行完成后会依次执行

  • 执行out.flush()操作,会把out缓冲区中的数据追加写入到response缓冲区

  • 会执行response的刷新操作,把全部数据写给客户端

所以当out没有手动调用out.flush()方法时,它输出的内容都在response输出内容的后面

常用标签

静态包含

语法:

1
<%@include file="header.jsp" %>

特点

  • 不会翻译被包含的jsp页面
  • 静态包含其实是把被包含的jsp页面的代码拷贝到包含的位置执行输出
动态包含

语法:

1
<jsp:include page="header2.jsp"></jsp:include>

特点:

  • 会将被包含的jsp页面也翻译为Java代码

  • 底层使用如下代码实现

    1
    org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "header2.jsp", out, false);
转发标签

语法:

1
<jsp:forward page="page2.jsp"></jsp:forward>

Listener监听器

作用是监听变化,然后通过回调函数反馈给程序去做一些处理

ServletContextListener

ServletContextListener对象可以监听ServletContext对象的创建和销毁

接口包含方法
  • contextInitialized:在ServletContext对象创建之后马上调用,做初始化
  • contextDestroyed:在ServletContext对象对象销毁之后调用
使用方式
  • 编写一个类去实现ServletContextListener接口,并实现需要的方法

  • 在实现类上加上@WebListener注解或者在web.xml中加上以下内容

    1
    2
    3
    <listener>
    <listener-class>com.zhuweitung.javaweb.listener.WebServletContextListener</listener-class>
    </listener>

EL表达式

EL表达式主要是代替jsp页面中表达式脚本在jsp页面中进行数据的输出;

主要是输出域对象中的数据;

若四个域中都存入了相同key的数据,则按pageContext>request>session>application的顺序取,找到就输出;

隐含对象

变量 类型 作用
pageContext PageContextImpl 可以获取jsp中的九大内置对象
pageScope Map<String, Object> 可以获取pageContext域中的数据
requestScope Map<String, Object> 可以获取request域中的数据
sessionScope Map<String, Object> 可以获取session域中的数据
applicationScope Map<String, Object> 可以获取application域中的数据
param Map<String, String> 可以获取请求参数的值
paramValues Map<String, String[]> 可以获取请求参数的值,值为数组
header Map<String, String> 可以获取请求头的信息
headerValues Map<String, String[]> 可以获取请求头的信息,值为数组
cookie Map<String, Cookie> 可以获取当前请求的Cookie信息
initParam Map<String, String> 可以获取在web.xml中配置的上下文参数

JSTL标签库

标签库为了替代代码脚本,可以使整个页面变得更加简洁

功能范围 URI 前缀
核心标签库 http://java.sun.com/jsp/jstl/core c
格式化 http://java.sun.com/jsp/jstl/fmt fmt
函数 http://java.sun.com/jsp/jstl/functions fn
数据库(不使用) http://java.sun.com/jsp/jstl/sql sql
XML(不使用) http://java.sun.com/jsp/jstl/xml x

使用方式

  • 导入依赖(引入jar包)

  • 页面引入

1
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

核心标签库常用标签

标签 作用 示例
c:set 往域中保存数据 <c:set scope=“page” var=“key” value=“value”/>
c:if 做if判断 <c:if test=“${ true or false }”></c:if>
c:choose c:when c:otherwise 做多路判断 <c:choose><c:when test=${confition}></c:when><c:otherwise></c:otherwis></c:choose>
c:forEach 遍历输出 <c:forEach items=“${list}” var=“item” varStatus=“status”>${item.name}</c:forEach>

文件上传

上传报文解析

Servlet处理

思路就是从request中获取字节输入流,然后将输入流数据写入到输出流,保存到服务器;

常规开发可以使用现有工具类;

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (ServletFileUpload.isMultipartContent(request)) {
ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory());
try {
List<FileItem> fileItems = servletFileUpload.parseRequest(request);
for (FileItem item : fileItems) {
if (item.isFormField()) {
System.out.println("表单项" + item.getFieldName() + "值为" + item.getString("utf-8"));
} else {
item.write(new File("D:\\tmp\\upload\\" + item.getName()));
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

文件下载

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ServletContext ctx = getServletContext();
// 获取下载文件
String fileName = "图.jpg";
String filePath = "/file/" + fileName;
// 读取要下载的文件内容
InputStream is = ctx.getResourceAsStream(filePath);
// 通过响应头设置返回数据类型
response.setContentType(ctx.getMimeType(filePath));
// 通过响应头设置数据用于下载
String headerFilename = fileName;
String ua = request.getHeader("User-Agent");
if (ua.toLowerCase().contains("firefox")) {
// 火狐浏览器,文件名base64编码防止乱码
headerFilename = "=?utf-8?B?" + new BASE64Encoder().encode(fileName.getBytes(StandardCharsets.UTF_8)) + "?=";
} else {
// IE、谷歌浏览器,文件名url编码防止乱码
headerFilename = URLEncoder.encode(fileName, "utf-8");
}
response.setHeader("Content-Disposition", "attachment;filename=" + headerFilename);
// 获取响应的输出流
ServletOutputStream os = response.getOutputStream();
// 复制输入流到输出流
IOUtils.copy(is, os);

Cookie是服务器通知客户端保存键值对数据的一种技术;

客户端有了Cookie后,每次请求都发送给服务器;

每个Cookie的大小不能超过4kb;

Cookie的创建

1
response.addCookie(new Cookie("cookieKey", "value"));

浏览器收到的响应头中包含Set-Cookie属性

服务器获取客户端的Cookie

1
Cookie[] cookies = request.getCookies();

修改Cookie

方式一

服务器重新创建一个同名Cookie

方式二

从request中获取需要修改的Cookie,调用 setValue() 方法设置新值

1
2
cookie.setValue("newValue");
response.addCookie(cookie);

生命周期控制

通过 setMaxAge() 方法来设置Cookie最大生存时间,以秒为单位

  • 正数:表示cookie在经过该秒数后过期
  • 负数:表示cookie不会被持久存储,在浏览器退出时删除,也就是session级别的cookie
  • 0:删除cookie

Path属性

path属性可以有效的过滤哪些cookie可以发送给服务器;

path属性是通过请求的地址来进行有效的过滤;

Session

Session是一个接口,HttpSession是它的实现类;

用来维护一个客户端和服务器之间关联的一种技术;

每个客户端都有自己的一个session;

每个session都有一个唯一的标识;

1
2
3
4
HttpSession session = request.getSession();
String sessionId = session.getId();
boolean isNew = session.isNew();
session.setAttribute("key1", "123456");

jsp页面可以从session域中获取已设置的值

生命周期控制

通过 setMaxInactiveInterval() 方法来设置Session超时时间,以秒为单位,默认为30分钟;

  • 正数表示设定session超时时长
  • 负数表示永不超时

通过 invalidate() 方法立即销毁session;

可以通过修改tomcat配置文件中的web.xml或者工程的web.xml文件来更改默认超时时长;

1
2
3
<session-config>
<session-timeout>30</session-timeout>
</session-config>

session的超时指的是,客户端两次请求的最大间隔时长;

浏览器与Session关联的原理

浏览器请求服务器后,服务器创建session(存储在内存中),并创建一个cookie对象,这个cookie对象的key值永远为JSESSIONID,value值为新创建的session的id;

服务器再把cookie信息通过响应发给客户端;

客户端有了cookie后,每次请求都会吧session的id以cookie的形式发给服务器;

服务器通过session的id在内存中查找对应的session对象;

Filter过滤器

作用是拦截请求,过滤响应

拦截请求常见场景

  • 权限检查
  • 日志记录
  • 事务管理

使用方式

  • 编写一个类去实现Filter接口,并实现需要的方法

  • 在实现类上加上@WebFilter注解或者在web.xml中加上以下内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!-- 配置拦截器 -->
    <filter>
    <filter-name>AdminFilter</filter-name>
    <filter-class>com.zhuweitung.javaweb.filter.AdminFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>AdminFilter</filter-name>
    <!-- 拦截资源路径的匹配规则 -->
    <url-pattern>/admin/*</url-pattern>
    </filter-mapping>
注意事项

若开发使用4.x以上版本的serlvet-api,而tomcat的serlvet-api的版本是3.x,需要重写Filter的init方法和destroy方法;

因为4.x后这两个方法有default关键字修饰,而3.x版本没有;

生命周期

  • 构造器
  • init初始化方法,在工程启动后执行
  • doFilter方法,每次拦截到请求就会执行
  • destroy方法,停止工程就会执行

FilterConfig

用于获取filter过滤器的配置内容

  • getFilterName():获取过滤器名称
  • getServletContext():获取上下文
  • getInitParameter(String name):获取初始化参数
  • getInitParameterNames():获取初始化参数,Enumeration<String>格式

FilterChain

作用是执行下一个过滤器,直到没有过滤器为止,再返回请求资源;

所有filter和目标资源默认都执行在同一个线程中;

所有filter都使用同一个request对象;

执行顺序
  • 若使用web.xml方式配置过滤器,则按照xml中配置的顺序执行
  • 若使用@WebFilter 注解方式配置,按过滤器名称自然排序

拦截路径匹配模式

  • 精确匹配,如 /module/index.jsp
  • 目录匹配,如 /module/*
  • 后缀名匹配,如 *.htm

过滤器不检测资源是否存在,且匹配模式不能混合使用

其他用法

  • 使用过滤器+ThreadLocal可以实现全局事务提交和回滚
    • 过滤器和请求的资源在同一个线程,将业务中的jdbc连接存储在ThreadLocal,用过滤器捕获全局异常
    • 当过滤器没有捕获异常时,获取当前线程的jdbc连接并提交事务
    • 出现异常时,获取当前线程的jdbc连接并回滚事务

JavaWeb知识回顾及查漏补缺
https://blog.kedr.cc/posts/3001875945/
作者
zhuweitung
发布于
2021年1月5日
许可协议