Servlet
Servlet 是 java语言 中 JavaEE 的一个入门级 web框架, 是为了后期 SpringMVC , SpringBoot 框架 打下基础 。是一个 Web 服务、采用 B/S (浏览器/服务器) 架构
B/S架构
互联网中项目 主要 包含 B/S架构 和 C/S架构 , B/S 架构 是 基于 浏览器服务器的架构,是 JavaEE 最大的特点。 C/S架构 是基于 客户端服务器的架构, 安卓开发是 C/S架构。
网址URL的组成
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E7%BE%8E%E5%A5%B3&fenlei=256
- 协议: http 、https
- 域名/主机: www.baidu.com , localhost , 127.0.0.1, 192.168.10.11
- 端口号 : http协议默认端口号 80 , https 协议 默认端口号 443
- 请求地址: /s , 是用来 定位 服务器的资源
- 请求参数: ie=utf-8&f=8 , 浏览器 向 服务器 发送的数据。 请求参数 和 请求地址 以 ? 分割, 多个请求参数 使用 & 分割, 一个请求参数包含 2部分,键和值,且用 = 进行拼接
- 锚点 : #hash
在浏览器中输入一个网址 、按下回车后会发生什么?
搭建 Servlet 项目
File -> new Project -> Maven -> 勾选 create from archetype -> 在下方的列表中 选中 maven-archetype-webapp -> 下一步 -> 输入 项目名 和 groupId -> 下一步 -> maven
JSP
是 SUN 推出的一款 在 服务端 进行 页面渲染的 技术 、可以用来 替代 静态页面 HTML , jsp 是一个 服务端的 语言。
JSP 是 Servlet 的 页面 表现形式 。 Servlet 是 JSP 的 底层实现 。
jsp中常见的脚本指令
- <%! %> : 在 该脚本中, 可以 用来 定义 java 中的 类 、方法 、属性 等信息
- <% %> : 该脚本中 可以 编写 java 语句 。
- <%= %> : 将 等号 后面的 表达式 的 结果 输出到 浏览器中。
Tomcat 服务器
JSP 是后端服务语言,所以 不能直接通过浏览器访问、需要将项目部署到 服务器 上,才能正常访问 。 而 tomcat 是 Java语言 最受欢迎的 web 服务器,没有之一。
java 语言 常用的 web 服务器有 tomcat , jboss , weblogic 等
安装 tomcat
- 网址: https://tomcat.apache.org
Servlet 的生命周期方法
- init() : 初始化方法、一个Servlet 只会被初始化 一次。 Servlet 默认是在 第一次 请求的时候调用, 也可以通过配置 将初始化的时间 提升到 随 tomcat 服务器的 启动 而初始化。
- service() : 每次请求 都会 执行的方法 。
- destroy() : 销毁、 一个 Servlet 只有在销毁的时候才会被调用, 随 tomcat 的关闭 而 销毁 。
编写一个 Servlet 程序的步骤
- 在 servlet 包下, 新建一个类 、继承 HttpServlet (需要添加 tomcat-servlet-api 依赖包)
- 重新 HttpServlet 中的 init , service , destory 方法 (按需引入,通常 init , destory 可以 不提供)
- 在 web.xml 中 中, 配置 servlet 和 请求地址的 映射关系 (关键步骤)
<servlet>
<!-- 一个Servlet类在进行配置的时候,必须设置一个唯一的名字 -->
<servlet-name>UserServlet</servlet-name>
<!-- 配置 Servlet 类的 全名 -->
<servlet-class>com.qikux.servlet.UserServlet</servlet-class>
<!-- 配置 servlet 随 tomcat 的启动而进行初始化操作 -->
<load-on-startup>1</load-on-startup>
<!-- 给 servlet 传递格外的数据、设置字符集编码 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
<servlet-mapping>
<!-- 必须和 servlet标签中的 名字 保持 完全一致 -->
<servlet-name>UserServlet</servlet-name>
<!-- 配置一个 请求地址,用来访问 该 Servlet, 必须以 / 开头 -->
<url-pattern>/user</url-pattern>
</servlet-mapping>
- 也可以使用 注解 @WebServlet 来 提供 上面的 XML 配置
@WebServlet(value="/user" , loadOnStartup = 1, initParams = {@WebInitParam(name="encoding", value="UTF-8")})
public class UserServlet extends HttpServlet{
...
}
HttpServletRequest (request) 常见的属性和方法
- request.getMethod() : 获取 请求方式, 返回值 常见的有 GET , POST , PUT , DELETE , PATCH , OPTIONS
- GET : 从 服务器 获取数据 、在 请求地址 中的表现是 以 ? (之后) 分割的 参数
- POST : 表单提交 常用的常用 POST, 一般 做 新增 数据
- PUT : 传统的 web请求不支持 、 PUT 通常 用来做 修改 数据
- DELETE : 传统的 web 请求不支持 ,DELETE 通常做 删除数据
- PATCH : 一般做 修改数据,和 PUT 区别在于 PUT 往往 是 整条数据更新, 而 Patch 通常是 部分数据更新
- OPTIONS : 通常是 异步请求 自动发生的,用来检查 服务器是否支持 PUT , DELETE, PATH 或者 POST(JSON格式数据) 等请求的
- request.getServletPath() : 获取 请求 路径地址 (从 项目名 后 获取的内容, 且不包含 请求的参数 ), 例如 /haredot/user?a=1&b=2, 如果项目名为 haredot ,那么 该方法返回 /user
- request.getContextPath() : 获取 项目名 所对应的 地址 , 例如 /haredot/user?a=1&b=2 , 返回 /haredot , 如果 项目没有设置名字,则返回 “” 字符串
- request.getRequestURI() : 获取 从 端口号 后面的内容 到 ? 前面的内容 ,例如 /haredot/user?a=1&b=2 , 返回 /haredot/user (会包含 项目名)
- request.getRequestURL() : 获取 请求的完整例如 , 返回例如 http://localhost:8080/haredot/user , 注意 不带 请求参数
- request.getSchema() : 获取 协议 , 例如 http , https
- request.getServerName() : 获取 主机名 , 例如 localhost
- request.getServerPort() : 获取端口号 , 例如 8080
- request.getParameter(key) : 获取 表单(请求)参数中 key 对应的 值, 如果传入了多个值,则只能获取第 1 个
- request.getParameterValues(key) : 获取 请求参数中 key 对应的值 ,返回一个 字符串数组
- getParameterMap() : 获取 所有的 请求参数,返回一个 Map<String, String[]>
- request.getHeader(key) :根据 指定的 请求头、 获取 请求的头信息 。
- request.getCookies() : 获取所有的 Cookie , 如果没有任何Cookie , 返回 null , 否则 返回一个 数组
- request.getSession() : 获取 session 会话 。
- request.setAttribute(key , value) : 将 数据 存储到 request 作用域中 ,可以通过 forward 跳转的方式 将数据 传递到 .jsp 页面中
- request.getAttribute(key) : 从 request 作用域中, 根据 key 获取 存储的值, 返回 Object 类型,使用的话可以需要强转 。
- requestDispatcher(“/servletPath”).forward(request, response) : 转发跳转
Servlet 层主要的职责
Servlet 是 前端 ~ 后台的 一个 桥梁 。
- 接收 前台 从 浏览器 传递过来 的数据 (头信息、参数、请求方式、路径 等信息)
- 调用 业务逻辑层 处理 业务
- 根据 业务逻辑层 执行的结果 进行 页面 跳转 、返回一个 合适的 资源给 浏览器
Servlet 中 常见的作用域对象
作用域对象 可以 进行 数据的存储 , 拥有 三个 方法 setAttribute(key, val) , getAttribte(key) , removeAttribute(key) ;
- request : 和 请求 有关, 该作用域中存储的数据 ,只能在 一个请求中 使用, 不能 跨 请求访问 . 通常配合 转发 跳转 传递数据 使用
- session : 和 浏览器 或者 用户 有关, 当浏览器关闭的时候, 会话就 结束 . 获取 会话超时 (默认 时间 30分钟), 可以 跨 请求 传递数据 , 通常配合 重定向 跳转 .
- servletContext : 和 应用程序相关, 存储在 该作用域下的数据 被整个网站 共享 .
跳转方式
- 转发
request.getRequestDispatcher(servletPath).forward(request, response)
servletPath 以 / 开头,
/
指的是 从 项目 根 开始的地址 , 如果没有 / 代表 相对路径, Java web框架中 不建议使用 相对路径servletPath 只能传入 服务器的 内部 地址 .
- 重定向
response.sendRedirect(path) ;
path : 以 /开头 ,
/
指的是 从 端口号 后面 的 路径地址path 支持 外部 网址 . 如果 要跳转到 外部 地址 , 地址 必须以 http 或者 https 开头
转发 VS 重定向
- 转发 是 一次 请求 , 跳转由 服务器 内部完成 , 从 开始 请求 到 响应结果 地址栏的 地址 是 发送 请求的 地址 . 可以通过 request 作用域 传递 数据, 只能在服务器内部跳转
- 重定向 是 二次 请求 , 地址栏 会 发生改变 , 不能 使用 request作用域传递数据, 可以跳转到 外部地址 中 .
重定向的原理
当 一个请求 结束后 , 响应 一个 301 / 302 的状态码 给 浏览器 , 浏览器 如果发现 状态为 301/302 , 那么 就会触发 重定向的操作, 浏览器 会从 响应的 头信息 中 找到 Location 并获取对应的值,
将 Location的值 作为 第二次 请求的 请求地址 继续 发送 . 整个 重定向的 动作 是由 浏览器完成 的 .
JSP 中的四大作用域
- pageContext : 存储在该作用域的数据、只在当前页面中有效。不能跨页面,不能跨请求。 pageContext 有一个特殊的方法 findAttribute(key) , 会从四个作用域中依次查找对应的key
- request
- session
- application (ServletContext)
EL 表达式 和 JSTL 标签库
EL 表达式
是 JSP 语言 特有的一种 在 页面上 进行 数据展示(数据输出)的 一种 有效手段 。语法格式为 ${ key } , key 支持 Java表达式 , 变量 来自于 作用域 中 存放的数据
EL 表达式 会 从 作用域 中 获取数据 。${key} 等价于 pageContext.findAttribute(key) 。
EL 表达式 默认 会从 pageContrext -> request -> session -> application 四个作用域中 依次找 满足条件的 key, 如果找到,则 停止查找
如果要从 指定的作用域 获取对应的值,则可以使用 pageScope , requestScope, sessionScope , applicationScope 强制 从指定的作用域中 获取数据
例如 ${sessionScope.a} 代表 从 session作用域中 获取存储的 a 对应的数据
对象获取属性
${obj.propertyName}
propertyName 指的是 obj 对象中定义的 getter 方法, 去掉 get 首字母变小写 (如果去掉get后的属性名 前两个字母都是大写、则不做任何的改变)
Map 根据 键 获取对应的值
${obj.key}
${obj['key']}
List / Array 获取 对应的数据
${list[0]}
隐式对象
- 获取 请求参数
${param} : 获取所有的请求参数,返回一个 Map<String, String>
${param.key}
${paramValues} : 获取所有的请求参数,返回一个 Map<String, String[]>
${paramValues.key}
-
获取请求头信息
${header} : 获取所有的请求头信息 ,返回一个 Map<String, String> ${headerValues} : 获取所有的请求头信息 ,返回一个 Map<String, String[]>
-
获取 Cookie 信息
${cookie} : 获取所有Cookie, 返回一个 Map<String, Cookie>
-
使用 pageContext 获取 request, session , application 对象
${pageContext.request} // 获取 request 对象 ${pageContext.rquest.contextPath} // 获取项目名 ${pageContext.session} // 获取 session 对象 ${pageContext.servletContext} // 获取 application 对象
JSTL 标签库
是 jsp 中 进行数据处理的 一套 标签库 、在使用的时候,需要引入 jstl 依赖包 。
<dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
- c 标签库 (最重要的)
- fn 标签库 (比较重要的)
- fmt 标签库 (内容较少)
C 标签库 的使用
- 使用 @taglib 指令 引入 标签库
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 常见的 c 标签
- c:out
<c:out value="" default="" escapeXml="true" /> - value : 用来设置 输出的 内容, 支持 EL 表达式 - default : 当 value 中提供的 EL表达式 找不到对应的数据, 取 default 设置的值 - escapeXml : 是否转义 标签, 默认 是 true 转义, 如果设置为 false, 在表现上和 EL表达式 类似
- c:set
<c:set var="a" value="${b + 1}" scope="page" /> - var: 将存储的值 放到 指定的 变量中 - value : 要存储的数据、支持 EL 表达式 - scope : 存储的作用域,默认是 page , 支持的值用 page, request, session, application
- c:url
<c:url value="/a" /> : 类似于 c:out ,可以将结果输出到网页中, 会自动在地址前 添加 项目名 , 例如返回 /haredot/a <c:url value="" var="" scope="page" /> : 类似于 c:set , 支持 临时 存储值
- c:if 单分支条件
<c:if test="" /> -test : 定义 判断条件 ,支持 关系运算符 > , >= , < , <= , == , != 支持 逻辑运算符 &&, || , ! - 关系运算符 还支持 - gt : 大于 - ge : 大于等于 - lt : 小于 - le : 小于等于 - eq : 等于 - ne : 不等于 - 逻辑运算符 还支持 - and - or - not - 空值 判断 - empty
- c:choose 多分支条件判断
<c:choose> <c:when test="${score >= 90}">优秀</c:when> <c:when test="${score >= 80}">良好</c:when> <c:when test="${score >= 70}">中等</c:when> <c:when test="${score >= 60}">及格</c:when> <c:otherwise>不及格</c:otherwise> </c:choose>
- c:forTokens 对字符串进行按照指定的分隔符进行拆分后 遍历
<!-- request.setAttribute("hobbies", "爬山、游泳、学Java"); --> <c:forTokens items="${hobbies}" delims="、" var="hobby" varStatus="vs"> ${hobby} </c:forTokens>
- c:forEach 循环
- 列表遍历 <c:forEach items="${userList}" var="user" varStatus="vs"> <p> <label>序号:${vs.last}</label> <label>用户名: ${user.username}</label> <label>性别: ${user.sex}</label> </p> </c:forEach> varStatus 中 常见的属性 - index : 获取 遍历的 数据的索引 ,从 0 开始 - count : 获取 遍历的 数据的 出库顺序, 从 1 开始 ,和 index 在同一条记录中永远相差 1 - first : 遍历的 当前数据 是否 是 第一条数据 - last : 遍历的 当前数据 是否是 最后一条数据 - 数字遍历 <c:forEach var="m" begin="1" end="9" step="1"> <c:forEach var="n" begin="1" end="${m}"> <span>${n} * ${m} = ${ n * m }</span> </c:forEach> <br/> </c:forEach> - begin : 从 几 开始 - end : 到 几 结束 - step : 步长,每次增加多少 , step 值 必须 > 0 , 默认值是 1
- c:out
fn 标签库
对网页中 展示的 字符串 进行 处理 、例如 字符串 替换、截取、脱敏等操作。
-
引入方式
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
-
常见的函数
- contains 包含
- startsWith 以 …开头
- endsWith 以 … 结尾
- indexOf 查找 某个子串 在字符串中出现的索引位置
- join 按照指定的分隔符 拼接字符串
- split 拆分字符串
- length 获取 长度
- replace 替换字符串
- substring(str , start , end)
- 截取字符串 , start 从哪里开始 , end 代表 截取到哪里,不包含 end 所在的位置 , -1 代表截取到尾部
- toLowerCase 转小写
- toUpperCase 转大写
- trim 去除前后空格
fmt 标签库
对页面中要展示的数据 进行格式化 处理
-
引入方式
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
-
常见的标签
- fmt:locale : 设置 国际化语言 环境
<fmt:locale value="zh-CN" /> <!--zh-CN : 中文 , en-US : 英文 -->
- fmt:setBundle : 设置 国际化 配置文件 前缀
<!-- 需要在该标签前使用 fmt:locale 确认语言环境,否则会读取浏览器默认语言环境 , 从 PageContext 中查找 javax.servlet.jsp.jstl.fmt.locale 对应的语言信息 public static Object get(PageContext pc, String name, int scope) { switch (scope) { case PageContext.PAGE_SCOPE: return pc.getAttribute(name + PAGE_SCOPE_SUFFIX, scope); case PageContext.REQUEST_SCOPE: return pc.getAttribute(name + REQUEST_SCOPE_SUFFIX, scope); case PageContext.SESSION_SCOPE: return get(pc.getSession(), name); case PageContext.APPLICATION_SCOPE: return pc.getAttribute(name + APPLICATION_SCOPE_SUFFIX, scope); default: throw new IllegalArgumentException("unknown scope"); } } --> <fmt:setBundle basename="message"/> <!--message_zh_CN.properties : 中文配置文件 , message_en_US.properties : 英文配置文件 , 配置文件中的占位符使用 {n} --> - message_zh_CN.properties login = 登录 msg = 你好,{0} 欢迎登录本系统 - message_en_US.properties login = Sign In msg = Hello , {0} Welcome to My Site
- fmt:message : 读取 国际化配置文件中的数据
// 在 servlet 中读取国际化配置文件,并将其存储到对应的作用域中 (下面的代码写在servlet中后可以在 JSP 中 省略 fmt:locale 和 fmt:bundle 两个标签) Locale locale = new Locale("zh", "CN") ; // zh_CN // 读取 classpath 下的 message 国际化配置文件,并设置 语言环境 ResourceBundle resourceBundle = ResourceBundle.getBundle("message", locale); // ResourceBundle 获取国际化 中的数据 // String message = bundle.getString(key); // 获取 国际化配置文件中 对应键 对应的值 // MessageFormat formatter = new MessageFormat(""); // 获取 消息格式化对象,负责格式化数据 // formatter.applyPattern("你好,{0}欢迎登陆本系统") ; // 设置要格式化的字符串 // formatter.format(new String[]{"张三"}) ; // 传入数据 // 构建一个上下文对象,目的是为了让 fmt:message 能够使用 LocalizationContext localizationContext = new LocalizationContext(resourceBundle, locale); // 将上下文对象存储到 对应的作用域中 // pageContext 作用域 添加 后缀 .page // request 作用域 添加 后缀 .request // session 作用域 添加 后缀 .session // application 作用域 添加 后缀 .application request.setAttribute(Config.FMT_LOCALIZATION_CONTEXT + ".request", localizationContext); // javax.servlet.jsp.jstl.fmt.localizationContext
<!-- JSP 中 获取 国际化消息 --> <fmt:message key="login" /> <fmt:message key="msg"> <fmt:param value="${sessionScope.user.username}" /> <!-- 给第一个占位符 {0} 设置数据 --> <fmt:message>
- fmt:fomratDate
<fmt:formatDate value="${birth}" pattern="yyyy/MM/dd" /> <!-- 格式化 java.util.Date 日期对象 -->
- fmt:formatNumber
<fmt:formatNumber value="${23434323.5632}" pattern="#,###.##"/> <!-- 整数部分 每三位逗号分隔、保留2位小数、四舍五入 -->
- fmt:locale : 设置 国际化语言 环境
自定义标签
-
添加依赖包
<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jsp-api</artifactId> <version>9.0.75</version> <scope>provided</scope> </dependency>
-
在 WEB-INF 下 新建一个 tld (可任意)文件夹、在 文件夹下 新建一个 haredot.tld 文件 (文件名可自定义), 并添加如下内容
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <tlib-version>1.1</tlib-version> <!-- 标签库的简写名称 --> <short-name>h</short-name> <!-- 引入标签库的 网址,自定义即可 --> <uri>http://www.haredot.com/jsp/haredot</uri> <tag> <name>jdk8time</name> <tag-class>com.qikux.taglib.TemporalTag</tag-class> <!-- empty 代表 该标签是 单标签,没有标签体, JSP 代表是 双标签,允许定义标签体 --> <body-content>empty</body-content> <attribute> <name>value</name> <required>true</required> <!-- 是否支持 EL表达式 --> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>pattern</name> <required>false</required> </attribute> </tag> </taglib>
-
编写 LocalDateTag 类 并 继承 TagSupport 类
-
doStartTag
-
doAfterBody
-
doEndTag
上述三个方法 为 编写 自定义标签 的核心方法, 均返回 int 类型 ,返回的常量值 有 SKIP_BODY , EVAL_BODY_INCLUDE , SKIP_PAGE , EVAL_PAGE , EVAL_BODY_AGAIN
- SKIP_BODY : 该值 可以作为 doStartTag 的返回值 ,代表 跳过 标签体中的内容, 意味着 写在 标签体中的内容 不会渲染在页面上 , 在 doAfterBody中使用,代表执行执行 doEndTag
- EVAL_BODY_INCLUDE : 该值 可以作为 doStartTag 的返回值, 代表 渲染 标签体 中定义的内容
- SKIP_PAGE : 该值 可以作为 doEndTag 返回值 , 代表 跳过 标签 后面所有的内容
- EVAL_PAGE : 该值 可以作为 doEndTag 返回值, 代表 正常 结束 标签, 允许 后面的内容 进行渲染
- EVAL_BODY_AGAIN : 该值 主要在 doAfterBody 中 作为返回值 , 可以实现 循环的效果 。
public class TemporalTag extends TagSupport {
private Temporal value ;
private String pattern ;
public void setValue(Temporal value) {
this.value = value;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
@Override
public int doEndTag() throws JspException {
// 根据用户指定的 pattern 进行格式化
String format = DateTimeFormatter.ofPattern(pattern).format(value);
// 将数据写入 到浏览器
try {
this.pageContext.getOut().write(format);
return EVAL_PAGE;
} catch (IOException e) {
throw new JspTagException(e.getMessage(), e);
}
}
}
- 在 web.xml 中, 配置 自定义标签库
<jsp-config> <taglib> <taglib-uri>http://www.haredot.com/jsp/haredot</taglib-uri> <taglib-location>/WEB-INF/tld/haredot.tld</taglib-location> </taglib> </jsp-config>
JSP 中的 九大内置对象
在 JSP 页面中,提供了 9个 内置对象,可以 直接 在 <% %> 中 。
- pageContext
- request
- session
- application
- response
- page : 代表当前页
- config : 和 Servlet中的 ServletConfig 是等价的 , 可以 获取 Servlet 中的一些配置信息。
- out : 输出内容
- exception : 异常对象, 该对象 只能在 错误页面中 使用 。 配合 <%@page isErrorPage=“true” %> 使用
JSP 中 包含 include
将一个 网站中的 多个 相同 或者 相似 的内容 抽取 出来,存放到一个单独的文件中, 通过 include 的技术 将 抽取出来的内容 引入到 需要的文件中 。
- 静态包含
<%@include file="/WEB-INF/pages/common/nav.jsp"%>
- 动态包含
<jsp:include page="/WEB-INF/pages/common/nav.jsp"> <jsp:param name="a" value="1" /> </jsp:include>
动态包含 和 静态包含的区别
- 静态包含 是 将 需要包含的 页面 内容 直接 加载 到 页面中 ,构成一个 大的 完整的页面 后 同意进行 解析 并渲染 。 而 动态包含 是 将 需要 包含的 页面 渲染后的内容 加载到 页面中。
- 静态包含 不能传递 数据 、 而 动态包含 可以通过 jsp:param 传递 数据 , 在 公共页面中,通过 ${param} 来获取对应的参数。
文件上传
从 客户端 中 将 资源 通过 网络 传输的 方式 上传到 资源服务器的 过程
- FileUpload : apache 的子项目 apache-fileupload 实现的。
- Servlet 3.0 文件上传
Servlet 3.0 文件上传 实现 流程
- 在 Servlet 中 ,通过 配置 让 Servlet 支持 文件上传
- 使用注解 允许 Servlet 文件上传
@MultipartConfig(maxRequestSize = -1, maxFileSize = -1)
- XML 配置 Servlet 允许 文件上传
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.qikux.servlet.UserServlet</servlet-class>
<multipart-config>
<!-- 上传资源在服务器中存储的临时位置,默认 window 在 C: 盘 -->
<location>D:/temp</location>
<!-- 允许 请求传递的参数的最大字节数,如果设置为 -1 , 代表没有限制 -->
<max-request-size></max-request-size>
<!-- 允许 单文件的 最大字节数、如果设置为 -1 , 代表没有限制 -->
<max-file-size></max-file-size>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/user</user-pattern>
</servlet-mapping>
- 使用 request.getPart(key) 方法 接收 上传的 文件
- Part 类 常见的 属性 或 方法
- getSubmittedFileName() : 获取 上传的文件名, 例如 img.jpg
- photo.getContentType() : 获取 文件的媒体类型
- getSize() : 获取 上传的文件 大小,单位是 字节, 返回 long
- getInputStream() : 获取上传文件的 文件 输入流,可以通过该流 获取 上传的 文件对象
- write(file) : 将 上传的 文件 写入 到 指定的 位置 。
- form 表单 在上传的时候 需要满足2个条件
- 请求方式 必须是 POST
- form 表单的 enctype 属性的 值 必须为 mutipart/form-data
enctype 可用的值: a) application/x-www-form-urlencoded (默认值): 以表单的形式(键值对)提交数据 b) multipart/form-data : 主要应用于文件上传,表示 以 流的形式 提交数据 c) application/json : 将数据 以 JSON 格式提交到 服务器
上传的文件 存放的方式
- 存数据库 (不推荐)
- 存 服务器 磁盘中 (以后 不推荐中…)
a) 存储的路径可以任意,但需要在 配置文件中进行管理,以方便后期迁移 b) 存储到 当前项目中 (不需要配置文件)、 但是 项目在开发阶段一旦 mvn clear ,那么存储的文件全部就丢失了。
- 文件服务器 (公司自己搭建的服务器)
- oss 云存储服务器 (公司推荐使用的)
a) 阿里云 OSS b) 腾讯云 OSS c) 七牛云 OSS
文件下载
将 服务器 中的 资源 通过 网络 传输 下载 到 客户端 的过程
-
将 下载 的 资源 通过 流 的 技术 写入 到 响应 中
try(FileInputStream in = new FileInputStream(file); // 通过 response 对象 将要下载的 资源 进行写入到 浏览器 ServletOutputStream out = resp.getOutputStream()) { // 边读 边写 int len = -1; // 一次性读取 8K byte[] bytes = new byte[8 << 10]; while ((len = in.read(bytes)) != -1) { // 将读取到的数据 ,通过 输出流写入到 浏览器 out.write(bytes, 0, len); } }
-
设置 下载 资源的 媒体类型 头信息 (目的是为了让浏览器知道资源的类型)
response.setHeader("Content-Type", "image/png"); // 文件的媒体类型 可以自行百度有那些
- 设置 下载 资源的 附件 (主要包含 文件名 和 编码方式 等信息)
String encode = URLEncoder.encode(filename, "UTF-8"); // 解决文件名中文乱码问题 response.setHeader("Content-Disposition", "attachment;filename=" + encode); // 以附件的方式下载文件 - attachement // 附件 - inline // 预览 (文件必须支持预览才有效)
Servlet 响应 JSON格式数据
- 添加 处理 JSON的 依赖库
<!-- 添加 fastjson 依赖库, 处理JSON格式的数据 --> <dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2</artifactId> <version>2.0.32</version> </dependency>
- 响应 JSON格式的数据
ResultVo resultVo = .... ; // 将 业务层返回的结果转换成 JSON 字符串, String json = JSONArray.toJSONString(resultVo) ; // 设置响应头 类型为 JSON response.setHeader("Content-Type", "application/json") ; // 通过 字符流 向 浏览器写入JSON 数据 response.getWriter().print(json) ;
axios 异步请求 官方文档
-
GET 请求
-
POST 请求
axios 库 默认 在 POST , PUT ,DELETE 请求中, 发送的是 JSON 格式的数据、需要服务器支持, Servlet 默认不能处理 JSON 参数,如果需要,服务器需要做特殊的处理。
- json 格式传递数据
axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); // 如果使用 json 格式提交数据,那么 Servlet 服务器 需要 通过 request.getInputStream() 获取 json数据 (需要通过流得到数据) // request.getInputStream() 只能读取 一次,一旦读取过,该流 就无法获取数据。
- 表单格式(键值对)传递数据
// 方式一 let param = new URLSearchParams(); param.append("name", "张三"); param.append("sex", "男"); axios.post("/user", param) .then(res=> console.log(res.data)) // 方式二 (传递的参数必须是一个对象) let param = {name: "张三", sex: "男"} ; axios.post("/user", param, { headers: { "content-type": "application/x-www-form-urlencoded" } }).then(res=> console.log(res.data))
- 文件上传
// 方式一 let formData = new FormData(); formData.append("username", "xxx"); formData.append("photo", photo); // photo 是一个文件对象 axios.post("/user", formData).then(res=> { console.log(res.data); }) // 方式二 let param = {name: "张三", sex: "男", photo: photo} ; // photo 是一个文件对象 axios.postForm("/user", param).then(res=> { console.log(res.data) });
- json 格式传递数据
-
PUT 请求
传统的 表单提交不支持 PUT, DELETE 请求 , 而异步请求是 支持 PUT, DELETE 等操作的。
PUT 请求 通过 表单 发送的 数据, 不能直接通过 request.getParamter(key) 获取到,只能通过 request.getInputStream() 得到数据。
PUT 请求 通过 json 发送的数据, 也不能通过 request.getParamter(key) 获取到,只能通过 request.getInputStream() 得到数据。
解决方案: 自定义过滤器
-
DELETE 请求
用法完全参考PUT请求
跨域
浏览器 具备 同源策略机制, 是一种保护机制, JavaScript 从一个源 向 另一个源 发送 http 请求的时候,就会 发生 跨域。 同源策略会对这种情况进行限制。
同源策略 指的是 协议、域名、端口必须完全相同。
CORS 跨域资源共享
在 请求 响应前 设置 头信息,解决 JS 跨域问题
response.setHeader("Access-Control-Allow-Origin", "*"); // 支持传入 http://localhost:8888 等网址
response.addHeader("Access-Control-Allow-Methods", "*"); // 允许访问的 请求方法
response.addHeader("Access-Control-Allow-Headers", "*"); // 允许访问的 请求头信息
过滤器 Filter
是一种切面技术,可以对 请求 和 响应 进行拦截 。在 请求前 进行 逻辑 处理, 在 响应后 进行逻辑处理 。
-
编写一个类 、实现 一个 Filter 接口
public class CharacterEncodingFilter implements Filter { private String encoding ; @Override public void init(FilterConfig filterConfig) throws ServletException { encoding = filterConfig.getInitParameter("encoding"); if (encoding == null || "".equals(encoding)) { encoding = "UTF-8" ; } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(encoding); response.setCharacterEncoding(encoding); System.out.println("Servlet 执行前 执行的代码"); // 继续 执行请求, 进入下一个过滤器,如果没有下一个过滤器,则 进入 Servlet chain.doFilter(request, response); // 在 此 书写的代码 就是 响应后 执行的代码。。。 System.out.println("Servlet 执行完后 执行的代码"); } }
-
在 web.xml 中 配置过滤器
<filter> <filter-name>encodingFilter</filter-name> <filter-class>com.qikux.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <!-- /* 拦截所有请求 --> <url-pattern>/*</url-pattern> <!--<dispatcher>REQUEST</dispatcher> --> </filter-mapping>
-
dispatcher 的类型
- REQUEST : 默认值, 过滤 的请求 为 普通 请求 (由浏览器发出的请求)
- FORWARD : 过滤的请求 为 转发 forward 请求 , 拦截 request.getDispatcher(“/xxxx”).forward(request, response) ;
- INCLUDE : 过滤的请求为 包含 请求, (动态包含的请求)
- ERROR : 拦截的请求为 错误 请求, 当请求发生异常的时候,被拦截
- ASYNC : 拦截 异步 Servlet 对应的请求, 例如 @WebServlet(asyncSupported=true, …)
监听器 Listener
Servlet 监听器 可以 监听 request 、 session 、 ServletContext(application)
监听器 对应的 类 构建的对象 会随 tomcat的启动而启动, 随 tomcat 的关闭 而 销毁。
-
ServletRequestListener : 监听 请求 的 创建 和 销毁
-
ServletRequestAttributeListener : 监听 向 request 作用域中 添加数据 、 移除数据、 替换数据 三个动作 。
-
HttpSessionListener : 负责监听 session的创建和销毁
-
HttpSessionAttributeListener : 负责 监听 向 session 中 添加数据、移除数据、替换数据 三个动作
-
ServletContextListener : 负责监听 application 的创建和销毁
-
ServletContextAttributeListener 负责 监听 向 application 中 添加数据、移除数据、替换数据 三个动作
@WebListener
public class ClassLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ApplicationContext ctx = new ClasspathApplicationContext();
// 将 构建出来的应用上下问 存储 到 ServletContext 作用域中
sce.getServletContext().setAttribute(SysConst.APPLICATION_CONTEXT, ctx);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// 当销毁 ServletContext 的时候,将 ctx 移除即可
sce.getServletContext().removeAttribute(SysConst.APPLICATION_CONTEXT);
}
}
向 浏览器 写入 Cookie
Cookie cookie = new Cookie(name , value) ;
cookie.setPath("/"); // 设置 Cookie 的访问路径, 如果没有提供,默认是 当前项目 路径
cookie.setDomain("") ; // 设置 Cookie 在 哪一个域 中使用,例如 可以设置为 baid.com , 就代表 www.baidu.com , wenku.baidu.com 可以访问 该 Cookie , 如果不设置,默认为 当前域
cookie.setMaxAge(n) ; // 设置 Cookie 的存活时间,单位是 秒 , 如果 大于 0 代表 存活时间, 如果 等于 0 代表 删除 Cookie , 如果设置为 小于 0 , 代表是一个 会话 Cookie (随浏览器关闭而销毁)
cookie.setHttpOnly(boolean) ; // 设置 Cookie 是否是只读的
response.addCookie(cookie) ;
MVC 设计模式
M : Model (模型) , 完成数据的处理 (实体层 、 持久层、业务逻辑层)
V : View (视图) , 负责 进行页面处理 (主要由 JSP 来完成)
C : Controller (控制器) 、 沟通 前后端 、通常由 Servlet 充当
MVC 设计思想 要求 所有的请求 拥有 同一个 入口 , 由该入口 负责 进行 请求 的 分发、该入口完成 对 响应信息的 统一处理 。