JavaWeb
javase、javaweb、javaee
-
javase :就是 Java 基础技术栈,做 Java 相关开发的基础,比如 javaweb、javaee
-
javaweb:使用 Java 开发网站相关技术,比如 Servlet、JDBC、Tomcat、Session/Cookie 等技术栈,javaweb 里面很多技术,但有部分慢慢被弃用了,比如 JSP 等技术点企业很少用了
-
javaee:全称 Java Platform,Enterprise Edition,可以构建企业级的面向服务体系结构(service-oriented architecture,SOA)微服务、组件等的技术栈,更多的是框架层面开发构建大型应用。
- 服务架构:MicroService : SpringCloud /Alibaba Cloud
无服务架构:ServerLess
服务网格:Service Mesh - 主流框架(当然现在还有少数公司用老旧项目):
2015 年:Struts + Hibernate + Spring SSH
2015~2018 之间:SpringMVC + Spring + MyBatis SSM
2018 年到现在:SpringBoot + Spring + Myabtis 新版 SSM
- 服务架构:MicroService : SpringCloud /Alibaba Cloud
静态网页、动态网页
-
静态网页:
- 主要指的是网页中没有程序代码,主要是 HTML+CSS+JS,一般后缀为.html,htm。
- 任何人任何时间打开的页面的内容都是不变的。
-
动态网页:
- 前端:HTML + CSS + JS + HTTP
- 后端:Servlet+Request+Response+Cookie+Session
- 数据库(类似 excel):MySQL
- 浏览器和服务器进行数据交互,服务器端根据客户的不同请求动态的生成网页内容
- 用户通过浏览器-》后端程序-》数据库的数据
BS、CS 架构
- CS 架构 :客户机-服务器,即 Client-Server(C/S)结构 但是缺少通用性,系统维护、升级需要重新设计和开发,增加了维护和管理的难度,用户体验更加流畅。
- BS 架构 :B/S 架构即浏览器和服务器架构模式,是 Web 兴起后的一种网络架构模式 Web 浏览器是客户端最主要的应用软件 统一了客户端,将系统功能实现的核心部分集中到服务器上,简化了系统的开发、维护和使用,但比较依赖网络环境。
URL 统⼀资源定位符
-
标准格式: 协议://服务器 IP:端⼝/路径 1/路径 N ? key1=value1 & key2=value2
- 协议:不同的协议有不同的解析方式
- 服务器 ip: ⽹络中存在⽆数的主机,要访问的哪⼀台, 通过公⽹ ip 区分
- 端⼝: ⼀台主机上运⾏着很多的进程,为了区分不同进程,⼀个端⼝对应⼀个进程,http 默认的端口是 80
- 路径: 资源 N 多种,为了更进⼀步区分资源所在的路径(后端接⼝,⼀般称为 “接⼝路径”,“接口”)
http 协议
-
即超⽂本传送协议(Hypertext Transfer Protocol ),是 Web 联⽹的基础,也是⼿机 PC 联⽹常⽤的协议之⼀,HTTP 协议是建⽴在 TCP 协议之上的⼀种应⽤
-
HTTP 连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,从建⽴连接到关闭连接的过程称为“⼀次连接”
-
Http 请求消息结构
- 请求行:请求方法 + URL 地址 + 协议名
- 请求头:报文头包含若干个属性 格式为“属性名:属性值”,服务端据此获取客户端的基本信息。
- 请求体:请求的参数,可以是 JSON 对象,也可以是前端表单生成的 key=value&key=value 的字符串
-
HTTP 响应消息结构
- 响应行:报文协议 + 状态码
- 响应头:报文头包含若干个属性 格式为“属性名:属性值”
- 响应正文:我们需要的内容、多种形式,比如:HTML、JSON、图片、视频。
-
HTTP 请求-HTTP 响应
-
响应码:
- 1xx:信息
- 2xx:成功 200 OK,请求正常
- 3xx:重定向
- 4xx:客户端错误 404 Not Found 服务器⽆法找到被请求的⻚⾯
- 5xx:服务器错误 503 Service Unavailable,服务器挂了或者不 可⽤
-
-
HTTPS 协议
- Hyper Text Transfer Protocol over SecureSocket Layer
- 主要由两部分组成:HTTP + SSL / TLS
- 比 HTTP 协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性,增加破解成本
- 缺点:相同网络环境下,HTTPS 协议会使页面的加载时间延长近 50%,增加额外的计算资源消耗,增加 10% 到 20% 的耗电等;不过利大于弊,所以 Https 是趋势,相关资源损耗也在持续下降
- 如果做软件压测:直接压测内网 ip,通过压测公网域名,不管是 http 还是 https,都会带来额外的损耗导致结果不准确
HTTP 常见响应状态码
-
1XX: 收到请求,需要请求者继续执行操作,比较少用
-
2XX: 请求成功,常用的 200
-
3XX: 重定向,浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的 URL 地址,这个地址可以从响应的 Location 首部中获取;
- 好处:网站改版、域名迁移等,多个域名指向同个主站导流
- 必须记住:
- 301:永久性跳转,比如域名过期,换个域名
- 302:临时性跳转
-
4XX: 客服端出错,请求包含语法错误或者无法完成请求
- 必须记住:
- 400: 请求出错,比如语法协议
- 403: 没权限访问
- 404: 找不到这个路径对应的接口或者文件
- 405: 不允许此方法进行提交,Method not allowed,比如接口一定要 POST 方式,而你是用了 GET
- 必须记住:
-
5XX: 服务端出错,服务器在处理请求的过程中发生了错误
- 必须记住:
- 500: 服务器内部报错了,完成不了这次请求
- 503: 服务器宕机
- 必须记住:
HTTP 的九种请求方法
-
http1.0 定义了三种:
- GET: 向服务器获取资源,比如常见的查询请求
- POST: 向服务器提交数据而发送的请求
- Head: 和 get 类似,返回的响应中没有具体的内容,用于获取报头
-
http1.1 定义了六种
- PUT:一般是用于更新请求,比如更新个人信息、商品信息全量更新
- PATCH:PUT 方法的补充,更新指定资源的部分数据
- DELETE:用于删除指定的资源
- OPTIONS: 获取服务器支持的 HTTP 请求方法,服务器性能、跨域检查等
- CONNECT: 方法的作用就是把服务器作为跳板,让服务器代替用户去访问其它网页,之后把数据原原本本的返回给用户,网页开发基本不用这个方法,如果是 http 代理就会使用这个,让服务器代理用户去访问其他网页,类似中介
- TRACE:回显服务器收到的请求,主要用于测试或诊断
HTTP 常见请求头、响应头
常见的请求头
- Accept: 览器支持的 MIME 媒体类型, 比如 text/html,application/json,image/webp,/ 等
- Accept-Encoding: 浏览器发给服务器,声明浏览器支持的编码类型,gzip, deflate
- Accept-Language: 客户端接受的语言格式,比如 zh-CN
- Connection: keep-alive , 开启 HTTP 持久连接
- Host:服务器的域名
- Origin:告诉服务器请求从哪里发起的,仅包括协议和域名 CORS 跨域请求中可以看到 response 有对应的 header,Access-Control-Allow-Origin
- Referer:告诉服务器请求的原始资源的 URI,其用于所有类型的请求,并且包括:协议 + 域名 + 查询参数; 很多抢购服务会用这个做限制,必须通过某个入来进来才有效
- User-Agent: 服务器通过这个请求头判断用户的软件的应用类型、操作系统、软件开发商以及版本号、浏览器内核信息等; 风控系统、反作弊系统、反爬虫系统等基本会采集这类信息做参考
- Cookie: 表示服务端给客户端传的 http 请求状态,也是多个 key=value 形式组合,比如登录后的令牌等
- Content-Type: HTTP 请求提交的内容类型,一般只有 post 提交时才需要设置,比如文件上传,表单提交等
常见的响应头
- Allow: 服务器支持哪些请求方法
- Content-Length: 响应体的字节长度
- Content-Type: 响应体的 MIME 类型
- Content-Encoding: 设置数据使用的编码类型
- Date: 设置消息发送的日期和时间
- Expires: 设置响应体的过期时间,一个 GMT 时间,表示该缓存的有效时间
- cache-control: Expires 的作用一致,都是指明当前资源的有效期, 控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据,优先级高于 Expires,控制粒度更细,如 max-age=240,即 4 分钟
- Location:表示客户应当到哪里去获取资源,一般同时设置状态代码为 3xx
- Server: 服务器名称
- Transfer-Encoding:chunked 表示输出的内容长度不能确定,静态网页一般没,基本出现在动态网页里面
- Access-Control-Allow-Origin: 定哪些站点可以参与跨站资源共享
Http 常见请求/响应头 content-type 内容类型
用来指定不同格式的请求响应信息,俗称 MIME 媒体类型
- 常见取值
- text/html :HTML 格式
- text/plain :纯文本格式
- text/xml : XML 格式
- image/gif :gif 图片格式
- image/jpeg :jpg 图片格式
- image/png:png 图片格式
- application/json:JSON 数据格式
- application/pdf :pdf 格式
- application/octet-stream :二进制流数据,一般是文件下载
application/x-www-form-urlencoded:form 表单默认的提交数据的格式,会编码成 key=value 格式
multipart/form-data: 表单中需要上传文件的文件格式类型
Apache Tomcat
Web 服务器和应用服务器是有很大的交集,没有很严格的区别,更多的是 Web 服务器处理的是 Http 协议,应用服务器还可以处理其他协议。
官网
1# web服务器
2 Apache、IIS、Nginx等
3
4# 应用服务器
5 Tomcat、Jboss等
6
7# Apache 组织
8 Apache软件基金会(也就是Apache Software Foundation,简称为ASF)是专门为运作一个开源软件项目的Apache 的团体提供支持的非盈利性组织,也是我们java开发里面顶级的组织,阿里也向apache组织贡献了多个开源项目。
9
10# Apache tomcat
11 Apache是web服务器,Tomcat是应用(java)服务器,它是一个servlet容器,是Apache的扩展。
12 简单来说:是一个开放源代码的轻量级 Web应用服务器,目前javaweb开发里面用的最多一个web服务器之一。
13
14# 启动脚本
15 mac或者linux: bin/startup.sh (需要增加执行权限:chmod 777 ./*)
16 windows: bin/startup.bat
17
18# 关闭脚本
19 mac或者linux: bin/shutdown.sh
20 windows: bin/shutdown.bat 或者关闭CMD窗口
21
22# Windows启动乱码
23 原因:控制台展示编码问题
24 编辑 conf/logging.properties 文件
25 java.util.logging.ConsoleHandler.encoding = UTF-8 改为 GBK
Tomcat 目录结构
1# bin
2 启动和关闭tomcat脚本
3
4# conf
5 存放配置文件
6 server.xml: 配置整个服务器信息 比如修改端口号,添加虚拟主机
7
8# lib
9 存放Tomcat运行需要的第三方包,这个目录中的jar所有项目都可以共享
10 如果需要添加Tomcat都依赖的jar文件,可以把它放到这个目录中
11
12# log
13 运行的相关日志文件,Tomcat启动和关闭的信息,如果启动Tomcat时有错误
14 catalina.out 运行日志文件,即标准输出和标准出错,也包含tomcat运行自己输出的日志以及应用里向console输出的日志
15
16# webapps
17 Tomcat的主要Web发布目录,存放web项目,其中每个文件夹都是一个项目
18 其中ROOT是一个特殊的项目,在地址栏中没有给出具体项目名时,默认会加载ROOT项目
19
20# temp
21 存放tomcat运行时的临时性数据
22
23# work
24 编译后产生的class文件
Servlet 快速上手
-
简介:是 JavaServlet 的简称,用 Java 编写的运行在 Web 服务器或应用服务器上的程序,具有独立于平台和协议的特性, 主要功能在于交互式地浏览和生成动态 Web 内容
-
作用:接收用户通过浏览器传来的表单数据,或者读取数据库信息返回给浏览器查看,创建动态网页
-
接口路径:package javax.servlet
- 有两个常见的子类:HttpServlet、GenericServlet
-
创建 servlet 类
1import javax.servlet.ServletException; 2import javax.servlet.http.HttpServlet; 3import javax.servlet.http.HttpServletRequest; 4import javax.servlet.http.HttpServletResponse; 5import java.io.IOException; 6import java.io.PrintWriter; 7 8public class UserServlet extends HttpServlet { 9 @Override 10 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 11 resp.setContentType("text/html;charset=utf-8"); 12 PrintWriter printWriter = resp.getWriter(); 13 printWriter.write("<div>This is a servlet test! </div>"); 14 } 15} 16
-
修改 web.xml
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>userServlet</servlet-name> 8 <servlet-class>net.xdclass.web.UserServlet</servlet-class> 9 </servlet> 10 11 <servlet-mapping> 12 <servlet-name>userServlet</servlet-name> 13 <url-pattern>/userServlet</url-pattern> 14 </servlet-mapping> 15</web-app>
-
启动 Tomcat,访问 http://localhost:8080/userServlet
Servlet 生命周期
Servlet 接口里面有 5 个方法,其中:三个生命周期方法、两个普通方法
- 实例化-> 使用构造方法创建对象
- 初始化-> 执行 init 方法:Servlet 的生命期中,仅执行一次 init() 方法,它是在服务器装入 Servlet 时执行的,即第一次访问这个 Servlet 才执行
- 服务-> 执行 service 方法,service() 方法是 Servlet 的核心。每当一个客户请求一个 HttpServlet 对象,该对象的 service() 方法就要被调用
- 销毁-> 执行 destroy 方法,destroy() 方法仅执行一次,即在服务器停止且卸装 Servlet 时执行该方法
1public interface Servlet {
2 void init(ServletConfig var1) throws ServletException;
3
4 ServletConfig getServletConfig();
5
6 void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
7
8 String getServletInfo();
9
10 void destroy();
11}
12
- 查看继承和实现关系 XXXServlet->HttpServlet->GenericServlet->implements Servlet
- HttpServlet 里面实现了 service 方法,里面会根据不同的 http method 调用不同的方法,所以我们自定义 servlet 只要重写对应的 doXXX 方法即可
Servlet3.0 ~ Servlet5.0 VS 旧版
- 旧版 Servlet,在 XML 里面配置类目和路径,不方便
- 新版 Servlet,支持注解的方式,提高开发效率
1@WebServlet("/userServlet/find")
2//@WebServlet(name = "userServlet",urlPatterns = {"/user1","/user2","/test"})
3public class UserServlet extends HttpServlet {
4 @Override
5 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
6 resp.setContentType("text/html;charset=utf-8");
7 PrintWriter printWriter = resp.getWriter();
8 printWriter.write("<div>This is a servlet test! from doGet!</div>");
9 }
10
11 @Override
12 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
13 resp.setContentType("text/html;charset=utf-8");
14 PrintWriter printWriter = resp.getWriter();
15 printWriter.write("<div>This is a servlet test! from doPost!</div>");
16 }
17}
@WebServlet 的元注解配置项
1//servlet名称,若不指定,则为Servlet的完整类名
2String name() default "";
3
4//路径映射,配置多个,需要/开头
5 String[] value() default {};
6
7//路径映射,支持配置多个,需要/开头
8 String[] urlPatterns() default {};
9
10 //标记容器是否在启动应用时就加载Servlet,默认或数值为负数时表示第一次请求Servlet时再加载;0或正数表示启动应用就加载
11 int loadOnStartup() default -1;
12
13 //配置初始参数
14 WebInitParam[] initParams() default {};
15
16//是否支持异步操作模式
17 boolean asyncSupported() default false;
作用域对象
就是对象的生命周期,在 javaweb 开发里面有多个不同生命周期的对象。
比如:PageContext(当前页面)、ServletRequest(一次请求)、HttpSession(同一会话)、ServletContext(整个应用)
对象里面包含属性和对应的数据,所以不同作用域对象使用场景会不同。
ServletContext(应用上下文)
- 它代表了 servlet 环境的上下文,相当于一个全局存储空间
- 同一个 Web 应用程序中,所有的 Servlet 和 JSP 都可以共享同一个区域,是最大的作用域对象
(webapps 下的每个目录就是一个应用程序)
- 使用场景:加载应用参数
web.xml1 <context-param> 2 <param-name>url</param-name> 3 <param-value>test.net</param-value> 4 </context-param> 5 <context-param> 6 <param-name>username</param-name> 7 <param-value>jack</param-value> 8 </context-param>
servlet 中可以从 ServletContext 中获取和存储数据
1 @Override
2 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
3 resp.setContentType("text/html;charset=utf-8");
4 PrintWriter printWriter = resp.getWriter();
5 printWriter.write("<div>This is a servlet test! from doGet!</div>");
6
7 //获取servletContext中的共享数据
8 ServletContext servletContext = req.getServletContext();
9 String url = servletContext.getInitParameter("url");
10 System.out.println(url);
11
12 //向servletContext中存储数据
13 servletContext.setAttribute("appInfo","xdclass.net info");
14 String appInfo = (String)servletContext.getAttribute("appInfo");
15 System.out.println("TestServlet appInfo="+appInfo);
16 }
JSP 和 Servlet 的关系
-
什么是 JSP: 全称 Java Server Pages,是一种动态网页开发技术;
- 使用 JSP 标签在 HTML 网页中插入 Java 相关代码,标签通常以 <% 开头 以 %> 结束
- JSP 本身就是一种 Servlet, JSP 在第一次被访问的时候会被编译为 HttpJspPage 类,是 HttpServlet 的一个子类
- 为什么用这个:和原生 Servle 相比 JSP 可以很方便的编写 HTML 网页而不用去大量的用 println 语句输出 HTML 代码
- 通俗来说:JSP 就是在 HTML 里面写 Java 代码,servlet 就是在 Java 里面写 HTML 代码
-
添加 jsp-api.jar 到项目里面,和添加 servlet-api.jar 一样的步骤
-
JSP 内置了 9 个对象可以直接用:out、session、response、request、config、page、application、pageContext、exception
1
2request HttpServletRequest类的实例
3
4response HttpServletResponse类的实例
5
6out PrintWriter类的实例,用于把结果输出至网页上
7
8session HttpSession类的实例
9
10application ServletContext类的实例,与应用上下文有关
11
12config ServletConfig类的实例
13
14pageContext PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问
15
16page Java类中的this关键字
17
18Exception Exception类的对象,代表发生错误的JSP页面中对应的异常对象
- JSP 脚本程序
1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2<html>
3 <head>
4 <title>$Title$</title>
5 </head>
6 <body>
7 <div>test</div>
8
9 <div>
10 <%
11 out.println("getRequestURL " + request.getRequestURL());
12 %>
13 </div>
14
15 </body>
16</html>
- JSP 表达式的语法格式:(不能用分号结束)
1 <div>
2 <%=request.getRequestURL()%>
3 </div>
- 中文编码问题,顶部添加这些信息
1<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
HttpServletRequest 请求对象
1代表浏览器客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求中的所有信息都封装在这个对象中,通过这个对象提供的方法可以获得请求的所有信息。
常用核心 API 测试
1http://127.0.0.1:8080/request?userName=jack&age=11&sport=ball&sport=sleep
1 //客户端请求信息
2 System.out.println("应用上下文路径getContextPath=" + request.getContextPath());//应用上下文路径getContextPath=
3 System.out.println("客户端发出请求时的完整URL getRequestURL=" + request.getRequestURL());//客户端发出请求时的完整URL getRequestURL=http://127.0.0.1:8080/request
4 System.out.println("请求行中的资源名部分 getRequestURI=" + request.getRequestURI());//请求行中的资源名部分 getRequestURI=/request
5 System.out.println("请求行中的参数部分 getQueryString=" + request.getQueryString());//请求行中的参数部分 getQueryString=userName=jack&age=11&sport=ball&sport=sleep
6 System.out.println("发出请求的客户机的IP地址 getRemoteAddr=" + request.getRemoteAddr());//发出请求的客户机的IP地址 getRemoteAddr=127.0.0.1
7 System.out.println("客户机发请求使用的网络端口号 getRemotePort=" + request.getRemotePort());//客户机发请求使用的网络端口号 getRemotePort=2636
8 //获取请求头
9 System.out.println("获取请求头 getHeader(Accept)=" + request.getHeader("Accept"));//获取请求头 getHeader(Accept)=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
10 //获取请求参数
11 System.out.println("客户端请求参数 getParameter=" + request.getParameter("userName"));//客户端请求参数 getParameter=jack
12 String[] sport = request.getParameterValues("sport");
13 System.out.println("客户端请求参数列表,多个值 getParameterValues="+sport.toString());//客户端请求参数列表,多个值 getParameterValues=[Ljava.lang.String;@34cfebb3
14 Map<String, String[]> map = request.getParameterMap();
15 System.out.println("客户端请求参数封装成的map类型 getParameterMap=" + map.toString());//客户端请求参数封装成的map类型 getParameterMap=org.apache.catalina.util.ParameterMap@67adac54
- 四大作用域对象:PageContext->【ServletRequest】->HttpSession->ServletContext;
1//ServletRequest作用域:对像生命周期存在一次请求里面
2//存储java对象到request作用域
3request.setAttribute("name","jack");
HttpServletRequest 表单提交案例
- JSP 页面
1<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8"%>
2<html>
3 <head>
4 <title>$Title$</title>
5 </head>
6 <body>
7 <div>test</div>
8
9 <form action="/user/login" method="post">
10 <div>用户名:<input type="text" name="userName"></div>
11 <div>密码: <input type="password" name="pwd"></div>
12
13 <div><input type="submit" value="登录"></div>
14 </form>
15
16 </body>
17</html>
- servlet
1@WebServlet("/user/login")
2public class UserServlet extends HttpServlet {
3 @Override
4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
5 this.doPost(req,resp);
6
7 }
8
9 @Override
10 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
11 //解决中文乱码问题
12 resp.setContentType("text/html;charset=utf-8");
13 String userName = req.getParameter("userName");
14 String pwd = req.getParameter("pwd");
15
16 if (userName.equals("Howard") && pwd.equals("123")) {
17 resp.getWriter().write("登录成功");
18 } else {
19 resp.getWriter().write("帐号或密码错误");
20 }
21 }
22}
- 乱码问题解决的核心:通过字节数组以指定的编码构建字符串,这里指定的编码是根据客户端那边提交数据时使用的字符编码来定。
1//POST方式遇到中文乱码,如果客户端是以UTF-8字符编码,需要服务器以UTF-8的编码接收数据,
2req.setCharacterEncoding("UTF-8");
3
4//GET方式传输的中文数据乱码需要另外一种方式,默认的还是使用ISO8859-1这个字符编码来接收数据
5//办法:对接收到的原始数据的字节数组,然后通过字节数组以指定的编码构建字符串,解决乱码问题
6String name = request.getParameter("name");//接收数据
7name = new String(name.getBytes("ISO8859-1"), "UTF-8")
HttpServletResponse 响应对象
简介:代表服务器的响应,封装了向客户端发送数据、发送响应头,发送响应状态码的方法
1@WebServlet("/response")
2public class ResponseServlet extends HttpServlet {
3 @Override
4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
5
6 //设置有多个值的http响应头(客户单收到的响应头中就会有以下两个自定义头信息)
7 resp.addHeader("name", "howard");
8 resp.addHeader("name", "alice");
9 //设置响应头
10 resp.setHeader("age", "2");
11 //设置http状态码
12 resp.setStatus(1000);
13
14/* //getOutputStream():创建的字节输出流对象,可以按字节形式输出响应正文,直接输出字节数组中的二进制数据,更多用于下载输出文件流
15 //设置编码格式为UTF-8
16 resp.setCharacterEncoding("UTF-8");
17 //通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
18 resp.setHeader("Content-Type", "text/html;charset=utf-8");
19 //响应的数据
20 String data = "Java学习";
21 //获取OutputStream输出流
22 OutputStream outputStream = resp.getOutputStream();
23 //将字符转换成字节数组,指定以UTF-8编码进行转换
24 byte[] dataByteArr = data.getBytes("UTF-8");
25 //使用OutputStream流向客户端输出字节数组
26 outputStream.write(dataByteArr);*/
27
28 //getWriter():创建的字符输出流对象,可以按字符形式输出响应正文,只能输出输出字符文本内容,和上面的互斥
29 //设置编码格式为UTF-8
30 resp.setCharacterEncoding("UTF-8");
31 //通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
32 resp.setHeader("content-type", "text/html;charset=UTF-8");
33 String data2 = "学习java";
34 //获取PrintWriter输出流
35 PrintWriter out = resp.getWriter();
36 //使用PrintWriter流向客户端输出字符
37 out.write(data2);
38 }
39}
RequestDispatcher 请求转发
**什么是请求转发:**在浏览器地址栏中不会显示出转发后的地址,属于服务器内部转发,整个过程处于同一个请求当中,所以转发中数据的存取可以用 request 作用域
- 客户端发送请求,Servlet 做出业务逻辑处理。
- Servlet 调用 forword()方法,服务器 Servlet 把目标资源返回给客户端浏览器
- 可以访问 WEB-INF 下的文件,WEB-INF 的文件一般是需要一定的权限才可以访问
- 例子:req.getRequestDispatcher("/WEB-INF/admin.jsp").forward(req,resp);
在 WEB-INF 目录下创建 JSP
1<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
2<html>
3<head>
4 <title>Title</title>
5</head>
6<body>
7 index.jsp
8 <%=request.getAttribute("name")%>
9</body>
10</html>
1
创建 servlet 做转发处理
1@WebServlet("/forward")
2public class DispatchServlet extends HttpServlet {
3 @Override
4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
5 System.out.println("DispatchServlet doGet");
6 req.setAttribute("name","howard");
7 req.getRequestDispatcher("/WEB-INF/admin.jsp").forward(req,resp);
8 }
9}
EL 表达式
让 JSP 访问 JavaBean 中的数据更简单,全称 Expression Language,让 JSP 写起来更加简单。表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方法,让 JSP 的代码更加简化。
-
语法
1EL表达式的格式都是以 ${ }表示。例如 $ {userinfo}代表获取变量userinfo的值,${对象.属性},可以有多层操作 2 3当EL表达式中的变量不给定范围时,则默认在page范围查找,然后依次在request、session、application范围查找,如果找到不再继续找下去,但是假如全部的范围都没有找到时,就回传"" 4 5可以用范围作为前缀表示属于哪个范围的变量,例如:${pageScope.userinfo}表示访问page范围中的userinfo变量
-
属性范围在 EL 中的名称
1【jsp中】【EL表达式中】 2Page pageScope 3 4Request requestScope 5 6Session sessionScope 7 8Application applicationScope
-
对比
1<%= (String)request.getAttribute("name")%> 等价于 ${name}
sendRedirect 请求重定向
- 客户端发送请求,Servlet 做出业务逻辑处理
- Servlet 调用 response.sendRedirect("xxx.jsp")方法,把要访问的目标资源作为 response 响应信息发给客户端浏览器
- 客户端浏览器重新访问服务器资源 xx.jsp,服务器再次对客户端浏览器做出响应
- 请求重定向,不能访问 WEB-INF 下的文件,浏览器上的窗口地址会改版,可以用于跳转第三方地址或者应用里面的其他 Servelt、JSP 等
1@WebServlet("/response") 2public class ResponseServlet extends HttpServlet { 3 @Override 4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 5 6 resp.sendRedirect("/index.jsp");resp.sendRedirect("/index.jsp"); 7 8 } 9}
注意点
-
重定向是取不到 request 中的存储的数据,如果当前 servlet 是重定向,浏览器可以看到两个请求
- 案例测试:在 reqeust 中设置值,然后在请求转发到页面,使用 EL 表达式取值
-
调用 sendRedirect()方法,会在响应中设置 Location 响应报头,这个过程对于用户来说是透明的,浏览器会自动完成新的访问
-
重定向路径问题:如果没有加 http 开头,则认为是当前应用里面的 servlet 重定向,默认加上应用上下文;如果有加 http 则会使用配置的全路径进行跳转,如果需要跳转到第三方站点,应改使用 http 开头。
-
如果请求转发可以满足需要时,尽量使用请求转发,而不是重定向,效率性能更好
Cookie
1
HTTP 协议作是无状态协议,无状态指每次 request 请求之前是相互独立的,当前请求并不会记录它的上一次请求信息。 存在这样的问题,既然无状态,那完成一套完整的业务逻辑,需要发送多次请求,那么怎么标识这些请求都是同个浏览器操作呢?
-
解决方案
- 浏览器发送 request 请求到服务器,服务器除了返回请求的 response 之外,还给请求分配一个唯一标识 ID 和 response 一并返回给浏览器
- 服务器在本地创建一个 map 结构,专门以 key-value 存储这个 ID 标识和浏览器的关系
- 当浏览器的第一次请求后已经分配一个 ID,当第二次访问时会自动带上这个标识 ID,服务会获取这个标识 ID 去 map 里面找上一次 request 的信息状态且做对应的更新操作 服务端生成这个全局的唯一标识,传递给客户端用于标记这次请求就是 cookie; 服务器创建的那个 map 结构就是 session。
- cookies 由服务端生成,用于标记客户端的唯一标识,在每次网络请求中,都会被传送。
- session 服务端自己维护的一个 map 数据结构,记录 key-Object 上下文内容状态
- 核心:Cookie 用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。 浏览器查看多个站点的 cookie
1@WebServlet("/cookie_test")
2public class CookieServlet extends HttpServlet {
3 @Override
4 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
5 //获取cookie,cookie有很多,是一个数组
6 Cookie[] cookies = request.getCookies();
7 for (Cookie cookie : cookies) {
8 cookie.getDomain();
9 }
10
11 Cookie cookie = new Cookie("token","sfwerawefewadaewfafewafa");
12 //20秒过期时间,过期后访问服务器时则不会携带(token可以传输在URL中或在head中)
13 cookie.setMaxAge(20);
14 response.addCookie(cookie);
15
16 request.getRequestDispatcher("/index.jsp").forward(request,response);
17 }
18}
cookie 的属性
-
Name : 名称
-
Value : 值
-
Domain:表示当前 cookie 所属于哪个域或子域下面
-
Expires/Max-age:表示了 cookie 的有效期,是一个时间,过了这个时间,该 cookie 就失效了
-
Path:表示 cookie 的所属路径。
-
size: 大小,多数浏览器都是 4000 多个字节
-
http-only: 表示这个 cookie 不能被客户端使用 js 读取到,是不公开的 cookie
- (chrome 调试器的 console 中输入 document.cookie 将得不到标记为 HttpOnly 的字段)
-
Secure: 标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端,
- 从 Chrome 52 和 Firefox 52 开始,不安全的站点(http:)无法使用 Cookie 的 Secure 标记
-
SameSite(特有的,可以忽略)
Cookie 的缺陷
- cookie 会被附加在每个 HTTP 请求中,增加了流量。
- 在 HTTP 请求中的 cookie 是明文传递的,所以安全性成问题,除非用 HTTPS
- Cookie 的大小有限制,对于复杂的存储需求来说不满足。
- 如果 cookie 泄露,会造成安全性问题。
浏览器允许每个域名所包含的 cookie 数量
- 多数浏览器允许最多是 50 个,部分浏览器是 30 或者 20;
- 满后会有多种剔除策略,比如 LRU,权重等
Cookie 的现状
- Cookie 曾一度用于客户端数据的存储,因当时并没有其它合适的存储办法而作为唯一的存储手段
- 现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被少用了,新的浏览器 API 已经允许开发者直接将数据存储到本地,比如 localStorage、SessionStorage 等 参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies
Session
-
什么是 Session
- 背景:HTTP 协议作是无状态协议,无状态指每次 request 请求之前是相互独立的,当前请求并不会记录它的上一次请求信息。
- 存在这样的问题,既然无状态,那完成一套完整的业务逻辑,需要发送多次请求,那么怎么标识这些请求都是同个浏览器操作呢?
- cookie 和 session 都是为了弥补 http 协议的无状态特性,对 server 端来说无法知道两次 http 请求是否来自同一个用户,利用 cookie 和 session 就可以让 server 端知道多次 http 请求是否来自同一用户
-
生成和使用流程(和 Cookie 知识点一样,两者互相配合)
- 浏览器第一次发送 request 请求到服务器,服务器除了返回请求的 response 之外,还给请求分配一个唯一标识 sessionId 和 response 一并返回给浏览器
- 服务器在本地创建一个 map 结构,专门以 key-value 存储这个 sessionId 和浏览器的关系
- 当浏览器的第一次请求后已经分配一个 sessionId,当第二次访问时会自动带上这个标识 sessionId
- 服务器通过查找这个 sessionId 就知道用户状态了,并更新 sessionId 的最后访问时间。
- 注意: Session 是有时限性的:比如如果 30 分钟内某个 session 都没有被更新,服务器就会删除这个它。
-
总结:
- 服务端生成这个全局的唯一标识,传递给客户端用于标记这次请求就是 cookie;
- 服务器创建的那个 map 结构就是 session。
- cookies 由服务端生成,用于标记客户端的唯一标识,在每次网络请求中,都会被传送。
- session 服务端自己维护的一个 map 数据结构,记录 key-Object 上下文内容状态
- 总言之 cookie 是保存在客户端,session 是存在服务器,session 依赖于 cookie
- cookie 里面存储的就是 JSESSIONID
-
session 的现状
- session 是存储在服务端的内存中,在 javaweb 里面叫 HttpSession 也是一个作用域
- PageContext(页面)->ServletRequest(请求)->【HttpSession】(会话)->ServletContext(一个应用)
- 是可以存储很多 key-value 的,作用域比较广,所以也不能存储过多内容,因为内存是有限制的,互联网企业使用比较少,传统 IT 公司使用比较多
-
知识延伸:
- 服务端是单机情况下 session 是可以很用的使用的,但是分布式(多台机器)情况下就存在不能共享的问题。
- 用户 A 在当前机器登录,突然某次请求到 B 机器,由于 B 服务器不存在这个用户的登录信息,所以就会提示重新登录
- 这个场景下就用到分布式存储方案-比如 Redis(跟着学习路线,后续的专题路线课程会讲)
HttpSession 类操作 API
1@WebServlet("/session_servlet")
2public class SessionServlet extends HttpServlet {
3 @Override
4 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
5 HttpSession session = request.getSession();
6
7 //获取sessionid,java里面叫jsessionid
8 System.out.println("sessionid="+session.getId());//sessionid=AB61B40183F4105E61E64726C13EB476
9
10 //创建时间戳,毫秒
11 System.out.println("getCreationTime="+session.getCreationTime());//getCreationTime=1603336135984
12
13 //是否是初次创建,记得清空浏览器的cookie,验证sessionid
14 System.out.println("isNew="+session.isNew());//isNew=true
15
16 //往session存储东西
17 session.setAttribute("name","学习java test.net");
18 }
19}
登录 JSP
1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2<html>
3<head>
4 <title>Title</title>
5</head>
6<body>
7
8
9<form action="<%=request.getContextPath()%>/loginServlet" method="post">
10
11
12 名称:<input type="text" name="name"/>
13
14 <br/>
15 密码:<input type="password" name="pwd"/>
16
17 <input type="submit" value="登录">
18
19 消息提示 ${msg}
20</form>
21
22</body>
23</html>
登录成功 JSP
1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2<html>
3<head>
4 <title>Title</title>
5</head>
6<body>
7
8
9id:${loginUser.id}
10<br>
11name:${loginUser.name}
12
13<a href="/logout_servlet">退出</a>
14</body>
15</html>
JavaBean
1public class User {
2
3 private int id;
4
5 private String name;
6
7 private String host;
8
9 public int getId() {
10 return id;
11 }
12
13 public void setId(int id) {
14 this.id = id;
15 }
16
17 public String getName() {
18 return name;
19 }
20
21 public void setName(String name) {
22 this.name = name;
23 }
24
25 public String getHost() {
26 return host;
27 }
28
29 public void setHost(String host) {
30 this.host = host;
31 }
32}
LoginServlet
1@WebServlet("/loginServlet")
2public class LoginServlet extends HttpServlet {
3 @Override
4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
5 doPost(req,resp);
6 }
7
8 @Override
9 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
10 String name = req.getParameter("name");
11 String pwd = req.getParameter("pwd");
12
13 if(name.equals("howard") && pwd.equals("123")){
14
15 User user = new User();
16 user.setId(121);
17 user.setName(name);
18 user.setHost("test.net");
19 req.getSession().setAttribute("loginUser",user);
20 req.getRequestDispatcher("/WEB-INF/user.jsp").forward(req,resp);
21
22 }else{
23 req.setAttribute("msg","账号密码错误");
24 req.getRequestDispatcher("/login.jsp").forward(req,resp);
25 }
26
27 }
28}
LogoutServlet
1@WebServlet("/logout_servlet")
2public class LogoutServlet extends HttpServlet {
3
4 @Override
5 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
6
7 HttpSession session = request.getSession();
8 //吊销session,下次客户端请求将会重新生成新的jsession
9 session.invalidate();
10 request.getRequestDispatcher("/login.jsp").forward(request,response);
11 }
12}
13
访问 http://localhost:8080/loginServlet
Filter 过滤器
-
什么是过滤器:(检验是否符合要求,或者 对内容做二次处理,设置编码响应等)
- filter 简单理解:人---> 检票员(filter)---> 景点
-
Servlet 里面的过滤器作用
- 动态地拦截请求和响应,变换或使用包含在请求或响应中的信息
- 在客户端的请求访问后端资源之前,拦截这些请求。
- 在服务器的响应发送回客户端之前,处理这些响应。
-
Filter 的生命周期
- init(FilterConfig filterConfig) //只容器初始化的时候调用一次,即应用启动的时候加载一次
- doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 只要命中过滤规则就触发,可以在 filter 中根据条件决定是否调用 chain.doFilter(request, response)方法, 即是否让目标资源执行
- destroy() //只容器销毁的时候调用一次,即应用停止的时候调用一次
-
元注解: @WebFilter
cookieServlet
1@WebServlet(name="cookieServlet", urlPatterns = "/cookie_servlet")
2public class CookieServlet extends HttpServlet {
3 @Override
4 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
5 //获取cookie,cookie有很多,是一个数组
6 Cookie[] cookies = request.getCookies();
7 for (Cookie cookie : cookies) {
8 cookie.getDomain();
9 }
10 }
11}
CustomFilter
1//servletNames 和 urlPatterns 可以同时使用
2//@WebFilter(filterName = "xxx",servletNames = {"cookieServlet"},urlPatterns = {"/user/*","/order/*"})
3//@WebFilter(servletNames = {"cookieServlet"})
4@WebFilter(filterName = "CustomFilter",urlPatterns = {"/*"})
5public class CustomFilter implements Filter {
6 //只容器初始化的时候调用一次,即应用启动的时候加载一次
7 @Override
8 public void init(FilterConfig filterConfig) throws ServletException {
9 System.out.println("CustomFilter init");
10 }
11
12 //只要命中过滤规则就触发,可以在filter中根据条件决定是否调用chain.doFilter(request, response)方法, 即是否让目标资源执行
13 @Override
14 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
15 System.out.println("CustomFilter doFilter");
16
17 //统一设置请求、响应编码
18 request.setCharacterEncoding("UTF-8");
19 response.setCharacterEncoding("UTF-8");
20
21 //统一设置响应类型
22 response.setContentType("text/html;charset=utf-8");
23
24 //放行
25 chain.doFilter(request,response);
26 }
27
28 //只容器销毁的时候调用一次,即应用停止的时候调用一次
29 @Override
30 public void destroy() {
31 System.out.println("CustomFilter destroy");
32 }
33}
元注解:@WebFilter
1//该Filter是否支持异步操作模式
2asyncSupported
3
4//指定Filter对那种dispatcher模式进行过滤 该属性支持 Async,Error Forward,include,request
5dispatcherType
6
7//Filter 显示的名称
8displayName
9
10//Filter的名称
11filterName
12
13//Filter的配置参数
14initParams
15
16//过滤的Servlet可以指定多个,表示对这几个特定的的servlet 进行过滤
17servletNames
18
19//指定 Filter拦截的 URL,和上面的servletNames配置一样,用*可以表示通配符,但是不用字母后加*,应该按照模块划分,比如/user/*
20urlPatterns/value
Servlet Filter 配置参数
FilterConfig 类
- 过滤器配置类,可以通过这个获取过滤器基本信息
dispatcherTypes 参数
- 指定 Filter 对那种 dispatcher 模式进行过滤,不符合的则不进行过滤
- REQUEST:默认值,浏览器直接请求的资源会被过滤器拦截
- FORWARD:转发访问资源会被过滤器拦截
- INCLUDE:包含访问资源
- ERROR:错误跳转资源
- ASYNC:异步访问资源
1@WebFilter(filterName = "xxx",urlPatterns = {"/*"},initParams = {
2 @WebInitParam(name = "encoding",value = "UTF-8"),
3 @WebInitParam(name = "loginPage",value = "/login.jsp"),
4},dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
5
6public class CustomFilter implements Filter {
7
8
9 private FilterConfig filterConfig;
10
11 private String encoding;
12
13 private String loginPage;
14
15
16 @Override
17 public void init(FilterConfig filterConfig) throws ServletException {
18
19 System.out.println("CustomFilter init ");
20
21 String filterName = filterConfig.getFilterName();
22 System.out.println("filterName="+filterName);
23
24 this.filterConfig = filterConfig;
25 this.encoding = filterConfig.getInitParameter("encoding");
26 this.loginPage = filterConfig.getInitParameter("loginPage");
27
28 }
29
30 @Override
31 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
32
33 System.out.println("CustomFilter doFilter ");
34
35 //统一进行设置
36 request.setCharacterEncoding(encoding);
37 response.setCharacterEncoding(encoding);
38 response.setContentType("text/html;charset=utf-8");
39
40 //让请求继续往下走
41 chain.doFilter(request,response);
42 }
43
44 @Override
45 public void destroy() {
46
47 System.out.println("CustomFilter destroy ");
48 }
49}
50
用户登录访问个人页面拦截示例
login.jsp
1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2<html>
3<head>
4 <title>Title</title>
5</head>
6<body>
7
8
9<form action="<%=request.getContextPath()%>/loginServlet" method="post">
10
11
12 名称:<input type="text" name="name"/>
13
14 <br/>
15 密码:<input type="password" name="pwd"/>
16
17 <input type="submit" value="登录">
18
19 消息提示 ${msg}
20</form>
21
22</body>
23</html>
个人页面
1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2<html>
3<head>
4 <title>Title</title>
5</head>
6<body>
7
8
9id:${loginUser.id}
10<br>
11name:${loginUser.name}
12
13<a href="/logout_servlet">退出</a>
14</body>
15</html>
LoginServlet
1@WebServlet("/loginServlet")
2public class LoginServlet extends HttpServlet {
3 @Override
4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
5 this.doPost(req,resp);
6 }
7
8 @Override
9 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
10 String name = req.getParameter("name");
11 String pwd = req.getParameter("pwd");
12
13 if(name.equals("howard") && pwd.equals("123")){
14
15 User user = new User();
16 user.setId(121);
17 user.setName(name);
18 user.setHost("test.net");
19 req.getSession().setAttribute("loginUser",user);
20 req.getRequestDispatcher("/user/user.jsp").forward(req,resp);
21
22 }else{
23 req.setAttribute("msg","账号密码错误");
24 req.getRequestDispatcher("/login.jsp").forward(req,resp);
25 }
26 }
27}
LoginFilter
1@WebFilter(filterName = "loginFilter",urlPatterns = {"/user/*","/order/*"},initParams = {
2 @WebInitParam(name = "encoding",value = "UTF-8"),
3 @WebInitParam(name = "loginPage",value = "/login.jsp"),
4})
5public class LoginFilter implements Filter {
6 private FilterConfig filterConfig;
7 private String encoding;
8 private String loginPage;
9
10
11 @Override
12 public void init(FilterConfig filterConfig) throws ServletException {
13
14 System.out.println("CustomFilter init ");
15 this.filterConfig = filterConfig;
16
17 this.encoding = filterConfig.getInitParameter("encoding");
18 this.loginPage = filterConfig.getInitParameter("loginPage");
19 }
20
21 //被拦截的URL会进入此方法
22 @Override
23 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
24 System.out.println("CustomFilter doFilter ");
25
26 request.setCharacterEncoding(encoding);
27 response.setCharacterEncoding(encoding);
28 response.setContentType("text/html;charset=utf-8");
29
30 //将ServletRequest => HttpServletRequest
31 HttpServletRequest httpServletRequest = (HttpServletRequest)request;
32 HttpServletResponse httpServletResponse = (HttpServletResponse)response;
33
34 //session里面有用户信息
35 if(httpServletRequest.getSession().getAttribute("loginUser") !=null){
36 //则放行
37 chain.doFilter(request,response);
38
39 }else {
40 httpServletRequest.setAttribute("msg","非法访问,请登录");
41 httpServletRequest.getRequestDispatcher(loginPage).forward(httpServletRequest,httpServletResponse);
42 }
43 }
44
45 //停止tomcat的时候会执行
46 @Override
47 public void destroy() {
48 System.out.println("CustomFilter destroy");
49 }
50}
Listener 监听器
1监听器是一个实现了特定接口的普通Java类,用于监听其他对象的创建和销毁,监听其他对象的方法执行和属性改变;
javaweb 里面的监听器
-
作用:监听域对象的创建和销毁,比如 request/session/context
-
分类:
- ServletContextLitener(应用上下文)
- HttpSessionListener(会话)
- ServletRequestListener(请求)
-
监听器的实现步骤
(1)创建一个普通的 Java 类
(2)让该类实现监听器的接口
(3)在该类中实现监听器接口的所有方法
(4)旧版的在 web.xml 文件中通过标签来配置监听器,新版使用 @WebListener
ServletContextListener 全局配置加载
1使用场景:加载全局配置,初始化项目信息
配置应用上下文信息 web.xml
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 <context-param>
7 <param-name>url</param-name>
8 <param-value>https://xdclass.net</param-value>
9 </context-param>
10 <context-param>
11 <param-name>topic</param-name>
12 <param-value>学习Java</param-value>
13 </context-param>
14</web-app>
ContextListener
1@WebListener
2public class ContextListener implements ServletContextListener {
3 /**
4 * 初始化
5 * @param sce
6 */
7 @Override
8 public void contextInitialized(ServletContextEvent sce) {
9 System.out.println("ContextListener contextInitialized");
10
11 //获取上下文对象
12 ServletContext servletContext = sce.getServletContext();
13 String url = servletContext.getInitParameter("url");
14 String topic = servletContext.getInitParameter("topic");
15
16 //自定义Config类
17 Config config = new Config();
18 config.setTopic(topic);
19 config.setUrl(url);
20 //将config类设置到应用上下文中,方便读取
21 servletContext.setAttribute("config",config);
22 }
23
24 /**
25 *销毁
26 * @param sce
27 */
28 @Override
29 public void contextDestroyed(ServletContextEvent sce) {
30 System.out.println("ContextListener contextDestroyed");
31 }
32}
在 servlet 中可以获取全局配置,避免每次使用都需要重新加载的繁琐,显然使用 ContextListener 将 JavaBean 设置到应用上下文更便捷。
1 Config config = (Config) req.getServletContext().getAttribute("config");
2 System.out.println(config.getTopic());
HttpSessionListener 统计当前在线人数
-
session 使用场景
- 和 session 相关操作,比如统计网站在线人数,当前服务器的负载情况等
-
ContextLisener 配置
1//获取上下文对象 2ServletContext sc = sce.getServletContext(); 3sc.setAttribute("onlineNum",0);
-
HttpSessionListener 开发
1@WebListener
2public class SessionListener implements HttpSessionListener {
3
4 //监听session的创建
5 @Override
6 public void sessionCreated(HttpSessionEvent se) {
7 System.out.println("SessionListener sessionCreated");
8
9 ServletContext servletContext = se.getSession().getServletContext();
10
11 //获取在线人数
12 Integer onlineNum = (Integer)servletContext.getAttribute("onlineNum");
13
14 //新增1
15 servletContext.setAttribute("onlineNum",++onlineNum);
16
17 }
18
19 //监听session的销毁
20 @Override
21 public void sessionDestroyed(HttpSessionEvent se) {
22 System.out.println("SessionListener sessionDestroyed");
23
24 ServletContext servletContext = se.getSession().getServletContext();
25
26 //获取在线人数
27 Integer onlineNum = (Integer)servletContext.getAttribute("onlineNum");
28
29 //减少1
30 servletContext.setAttribute("onlineNum",--onlineNum);
31
32 }
33}
34
add.jsp
1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2<html>
3<head>
4 <title>javaweb统计在线人数</title>
5</head>
6<body>
7
8<hr>
9近30分钟在线人数: ${applicationScope.onlineNum}
10</body>
11</html>
delete.jsp
1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2<html>
3<head>
4 <title> 销毁</title>
5</head>
6<body>
7
8销毁session
9<hr>
10
11<% request.getSession().invalidate(); %>
12
13</body>
14</html>
ServletRequestListener 统计网站请求量
ContextLisener 中初始化
1 //设置在线人数的初始值
2 servletContext.setAttribute("totalVist", 0);
RequestListener
1@WebListener
2public class RequestListener implements ServletRequestListener {
3
4 @Override
5 public void requestDestroyed(ServletRequestEvent sre) {
6 System.out.println("RequestListener requestDestroyed");
7 }
8
9 //初始化
10 @Override
11 public void requestInitialized(ServletRequestEvent sre) {
12 System.out.println("RequestListener requestInitialized");
13 Integer totalVisit = (Integer)sre.getServletContext().getAttribute("totalVisit");
14 System.out.println("历史总访问次数:" + totalVisit);
15 totalVisit++;
16 sre.getServletContext().setAttribute("totalVisit",totalVisit);
17 }
18}
add.jsp
1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2<html>
3<head>
4 <title>javaweb统计在线人数</title>
5</head>
6<body>
7
8<hr>
9近30分钟在线人数: ${applicationScope.onlineNum}
10
11<hr>
12应用服务器启动后总访问次数: ${applicationScope.totalVisit}
13</body>
14</html>
文件上传
Web 应用系统开发中,文件上传和下载功能是非常常用的功能,浏览器在上传的过程中是将文件以流的形式提交到服务器端的
前端开发
11)表单的提交方法必须是post
12)需要声明是一个文件上传组件 [ ]
13)必须设置表单的enctype="multipart/form-data
1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2<html>
3<head>
4 <title>文件上传样例</title>
5</head>
6<body>
7
8<form action="<%=request.getContextPath()%>/fileUpload" method="post" enctype="multipart/form-data">
9
10 用户名:<input type="text" name="username"/>
11 头像:<input type="file" name="img">
12 <input type="submit" value="提交">
13
14</form>
15
16
17</body>
18</html>
后端开发
1@WebServlet("/fileUpload")
2@MultipartConfig //专门用于文件上传的注解,有相关文件上传的相关配置(上传大小等…)
3public class FileUploadServlet extends HttpServlet {
4
5
6 @Override
7 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
8
9 //获取上传文件的用户名
10 String username = request.getParameter("username");
11 System.out.println("username="+username);
12
13 //获取Part,用来获取文件名和文件输入流
14 Part part = request.getPart("img");
15
16 //获取真实文件名称 test.png
17 String header = part.getHeader("content-disposition");// form-data; name="img" filename="test.png"
18 String realFileName = header.substring(header.indexOf("filename=")+10,header.length()-1);//test.png
19
20 System.out.println("realFileName="+realFileName);
21
22
23 //获取真实的文件内容
24 InputStream is = part.getInputStream();
25
26 //web-inf目录外界不能直接访问,如果文件机密性强则放这里
27 //String dir = this.getServletContext().getRealPath("/WEB-INF/file");
28
29 //file目录真实路径
30 String dir = this.getServletContext().getRealPath("/file");
31
32 File dirFile = new File(dir);
33
34 //如果目录不存在,则创建
35 if(!dirFile.exists()){
36 dirFile.mkdirs();
37 }
38
39 //UUID + 上传文件的全名
40 String uniqueName = UUID.randomUUID()+realFileName;
41
42 //文件流拷贝
43 //File file = new File(dir,realFileName);
44
45 //创建目标文件
46 File file = new File(dir,uniqueName);
47
48 FileOutputStream out = new FileOutputStream(file);
49
50
51 byte[] buf = new byte[1024];
52 int len;
53
54 while ((len = is.read(buf))!=-1 ){
55 out.write(buf,0,len);
56 }
57
58 out.close();
59 is.close();
60
61 //跳转到刚刚上传的图片访问
62 request.getRequestDispatcher("/file/"+uniqueName).forward(request,response);
63 }
64}
注意点
- 考虑上传文件存储的目录
- 防止文件重名覆盖
- 防止一个目录下面出现太多文件
- 限制上传文件的最大值
- 上传的文件判断后缀名是否合法
文件下载
- 只需通过超链接即可实现,就是通过超链接,在连接地址里写上文件的路径,浏览器会自动打开该文件
- 普通的文本,图片等浏览器能直接显示内容的浏览器都能直接打开并显示
- 如果是浏览器无法打开的文件,比如 exe 等浏览器就会提示你下载改文件或者使用当前系统自带的工具打开该文件
前端开发
1<a href="<%=request.getContextPath()%>/download?file=test1.png">下载</a>
后端开发
1@WebServlet("/download")
2public class FileDownloadServlet extends HttpServlet {
3
4 @Override
5 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
6
7 //客户端传递需要下载的文件名
8 String file = request.getParameter("file");
9
10
11 //获取文件在我们项目中的路径,发布到tomcat的实际路径
12 String path = request.getServletContext().getRealPath("/file/");
13
14 //要下载的文件在项目中的真实路径
15 String filePath = path+file;
16
17 //拿到filePath的输入流
18 FileInputStream fis = new FileInputStream(filePath);
19
20 //设置编码
21 response.setCharacterEncoding("UTF-8");
22
23 //指明响应的配置信息,包含附件:告诉浏览器
24 response.setHeader("Content-Disposition","attachment; filename="+file);
25
26 //如果文件包含中文名称,需要进行编码转换
27 //response.setHeader("Content-Disposition","attachment; filename="+new String(file.getBytes("gb2312"),"ISO-8859-1"));
28
29 //拿到response的输出流
30 ServletOutputStream out = response.getOutputStream();
31
32 byte[] buf = new byte[1024];
33 int len;
34
35 while ((len= fis.read(buf))!=-1){
36 out.write(buf,0,len);
37 }
38
39 out.close();
40 }
41}
42