Servlet技术(@Author: Magi) 一、JavaEE简介 1.什么是javaEE JavaEE(Java Enterprise Edition),java企业版,是一个用于企业版web开发平台。最早由Sun公司定制并发布,后由Oracle负责维护。在Java EE平台规范了在开发企业级web应用中的技术标准。
在JavaEE平台共包含了13个技术规范(随着 JavaEE 版本的变化所包含的技术点的数量会有增多)。他们分别是:JDBC,JNDI,EJB,RMI,SERVLET,JSP,XML,JMS,JAVA IDL,JPA,JTA,JavaMail和JAF。
2.JavaEE版本
二、Servlet简介 1.Web开发历史回顾 1.1CGI 公共网关接口(Common GateWay Interface,CGI)是Web服务器运行时外部程序的规范。
1.2CGI缺点
以进程方式运行,对每一个客户端的请求都要启动一个进程来运行程序,导致用户数目增加时,服务器端资源被大量占用。
不同的CGI之间不能共享资源
由于对操作系统和进程的不深刻理解,使得开发人员开发的CGI程序经常遇到莫名其妙的错误。
1.3FastCGI FastCGI 是对 CGI 模式的一个改进,采用了 Pooling 技术,一定程度上改善了性能,但是 由于仍然是基于进程运行的所以并没有从根本上解决问题。
2.Servlet介绍 *Servlet是Servlet Applet的简称 , 称为服务端小程序。 是JavaEE平台下的技术标准,基本java语言编写的服务端程序。Web容器或应用服务器实现了Servlet标准所以Servlet需要运行在 Web容器或应用服务器 *中, Servlet主要功能在于能够在服务器中执行并生成数据。 **
2.1 Servlet技术特点 Servlet使用单进程多线程方式运行
2.2 Servlet在应用程序中的位置
三、服务器 1.JavaEE应用服务器(Java EE Application Server) 应用服务器是 Java EE 规范的具体实现, 可以执行/驱动基于 JavaEE 平台开发的 web 项 目。绝大部分的应用服务器都是付费产品。
常见的应用服务:
Weblogic(BEA Oracle 收费)
Webshpere(IBM 收费)
JBoss(RedHad 收费)
Resin(Caucho 收费)
JRun(Macromedia 收费)
Geronimo(Apache 免费)
2.Web容器(Web Server) 只实现了JavaEE平台下部分技术标准,如Servlet,Jsp,JNDI,JavaMail,Web容器是开源免费的
Tomcat(Apache 开源免费的)
Jetty(Jetty 开源免费的)
3. Tomcat的使用 3.1 Tomcat下载与安装 3.1.1 下载 下载地址:https://tomcat.apache.org/
3.1.2 安装 Tomcat 是绿色软件,解压就可使用。
3.1.3 Tomcat是用Java语言开发的Web容器,所以在使用Tomcat时需要在操作系统中正确配置环境变量
JAVA_HOME:C:\Program Files\Java\jdk1.8.0_171(jdk的版本)
PATH:%JAVA_HOME%\bin
CLASS_PATH:%JAVA_HOME%\lib
3.2 Tomcat目录结构与介绍
3.2.1. bin bin 目录主要是用来存放 tomcat 的命令文件,主要有两大类,一类是以.sh 结尾的(linux 命令),另一类是以.bat 结尾的(windows 命令)。
3.2.2 conf conf目录主要是用来存放tomcat的一些配置文件。
3.2.3 lib lib目录主要用来存放tomcat运行需要加载的jar包。
3.2.4 logs logs 目录用来存放 tomcat 在运行过程中产生的日志文件。
3.2.5 temp temp 目录用户存放 tomcat 在运行过程中产生的临时文件。(清空不会对 tomcat 运行带来影响)
3.2.6 webapps webapps 目录用来存放应用程序,当 tomcat 启动时会去加载 webapps 目录下的应用程序。可以以文件夹、war 包的形式发布应用。
3.2.7 work work 目录用来存放 tomcat 在运行时的编译后文件,例如 JSP 编译后的文件。
3.3 Tocmat启动与关闭 Tomcat 的启动与关闭需要执行 bin 目录中的命令脚本。
3.3.1 Tomcat启动 运行shutdown.bat文件
3.3.2 Tomcat关闭 3.3.2.1 方式一 运行 shutdown.bat 文件
3.3.2.2 方式二 直接关闭掉启动窗口
3.3.3 访问Tomcat 访问 Tomcat 的 URL 格式:http://ip:port
访问本机 Tomcat 的 URL 格式:http://localhost:8080
3.4 Tomcat的配置 3.4.1 Tomcat配置文件介绍 Tomcat 的配置文件由 4 个 xml 组成,分别是 ***context.xml、web.xml、server.xml、tomcat-users.xml ***。每个文件都有自己的功能与配置方法。
3.4.1.1 context.xml Context.xml 是 Tomcat 公用的环境配置。 Tomcat 服务器会定时去扫描这个文件。一旦发现文件被修改(时间戳改变了),就会自动重新加载这个文件,而不需要重启服务器 。
3.4.1.2 web.xml Web 应用程序描述文件,都是关于是 Web 应用程序的配置文件。所有 Web 应用的web.xml 文件的父文件。
3.4.1.3 server.xml 是 tomcat 服务器的核心配置文件,server.xml 的每一个元素都对应了 tomcat 中的一个组件,通过对 xml 中元素的配置,实现对 tomcat 中的各个组件和端口的配置。
3.4.1.4 tomcat-users.xml 配置访问 Tomcat 的用户以及角色的配置文件。
3.4.2 解决控制台乱码 控制台产生乱码的原因是在 Tomcat 在输出日志中使用的是 UTF-8 编码,而我们中文的Windows 操作系统使用的是 GBK 编码。由于编码格式不统一,所以出现了乱码。
解决方式:
修改 conf 目录中的 logging.properties 文件重新指定的编码方式。
java.util.logging.ConsoleHandler.encoding = GBK
3.4.3 修改Tomcat监听端口 Tomcat 默认监听端口为 8080。可以通过修改 server.xml 文件来改变 Tomcat 的监听端口。
<Connector port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8443" />
3.4.4 配置Tomcat Manager 3.4.4.1 什么是 Tomcat Manager Tomcat Manager 是 Tomcat 自带的、用于对 Tomcat 自身以及部署在 Tomcat 上的应用进行管理的 web 应用。默认情况下,Tomcat Manager 是处于禁用状态的。准确的说,TomcatManager 需要以用户角色进行登录并授权才能使用相应的功能,不过 Tomcat 并没有配置任 何默认的用户,因此我们需要先进行用户配置后才能使用 Tomcat Manager。
3.4.4.2 配置 Tomcat Manager 的访问用户 Tomcat Manager 中没有默认用户,我们需要在 tomcat-users.xml 文件配置。TomcatManager 的用户配置需要配置两个部分:角色配置、用户名及密码配置。
Tomcat Manager中的角色分类
manager-gui 角色:
允许访问 HTML GUI 和状态页面(即 URL 路径为/manager/html/*)
manager-script 角色:
允许访问文本界面和状态页面(即 URL 路径为/manager/text/*)
manager-jmx 角色:
允许访问 JMX 代理和状态页面(即 URL 路径为/manager/jmxproxy/*)
manager- status 角色:
仅允许访问状态页面(即 URL 路径为/manager/status/*)
配置用户及角色:
4. Tomcat版本说明
5.Tomcat工作原理 5.1 Tomcat作用 Tomcat是一个能处理请求并产生响应的应用程序。Tomcat实现了JavaEE平台下的一些技术规范,所以我们可以在Tomcat中运行我们所编写的Jsp、Servlet。
5.2 Tomcat运行原理
6. Tomcat架构讲解 6.1 Tomcat架构图
6.2 Tomcat组件
6.2.1 Server组件 启动一个server实例(即一个JVM),它监听在8005端口以接受shutdown命令。Server的定义不能使用同一个端口,这意味着如果在同一个物理机上启动了多个Server实例,必须配置他们使用不同的端口。
<Server port ="8005" shutdown ="SHUTDOWN" >
port: 接收 shutdown 指令的端口,默认为 8005;
shutdown:发往此 Server 用于实现关闭 tomcat 实例的命令字符串,默认为 SHUTDOWN;
6.2.2 Service组件 Service 主要用于关联一个引擎和与此引擎相关的连接器,每个连接器通过一个特定的 端口和协议接收请求并将其转发至关联的引擎进行处理。困此,Service 要包含一个引擎、一个或多个连接器。
<Service name ="Catalina" >
name:此服务的名称,默认为 Catalina;
6.2.3 Connector组件 支持处理不同请求的组件,一个引擎可以有一个或多个连接器,以适应多种请求方式。默认只开启了处理理 Http 协议的连接器。如果需要使用其他协议,需要在 Tomcat 中配置该协议的连接器。
在 Tomcat 中连接器类型通常有 4 种:
HTTP 连接器
SSL 连接器
AJP 1.3 连接器
proxy 连接器
<Connector port ="8888" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8443" />
port:监听的端口
protocol:连接器使用的协议,默认为 HTTP/1.1;
connectionTimeout:等待客户端发送请求的超时时间,单位为毫秒;
redirectPort:如果某连接器支持的协议是 HTTP,当接收客户端发来的 HTTPS 请求时,
则转发至此属性定义的端口;
maxThreads:支持的最大并发连接数,默认为 200 个;
6.2.4 Engine组件 Engine 是 Servlet 处理器的一个实例,即 servlet 引擎,定义在 server.xml 中的 Service标记中。Engine 需要 defaultHost 属性来为其定义一个接收所有发往非明确定义虚拟主机的请求的 host 组件。
<Engine name ="Catalina" defaultHost ="localhost" >
name:Engine组件的名称;
defaultHost:Tomcat 支持基于 FQDN(Fully Qualified Domain Name 全限定域名)的虚拟主 机,这些虚拟主机可以通过在 Engine 容器中定义多个不同的 Host 组件来实现;但如果此引 擎的连接器收到一个发往非非明确定义虚拟主机的请求时则需要将此请求发往一个默认的 虚拟主机进行处理,因此,在 Engine 中定义的多个虚拟主机的主机名称中至少要有一个跟 defaultHost 定义的主机名称同名;
6.2.5 Host组件 位于 Engine 容器中用于接收请求并进行相应处理的虚拟主机。通过该容器可以运行 Servlet 或者 JSP 来处理请求。
<Host name ="localhost" appBase ="webapps" unpackWARs ="true" autoDeploy ="true" >
name:虚拟主机的名称,Tomcat 通过在请求 URL 中的域名与 name 中的值匹配,用于查找能够处理该请求的虚拟主机。如果未找到则交给在 Engine 中 defaultHost 指定的主机处理;
appBase:此 Host 的 webapps 目录,即指定存放 web 应用程序的目录的路径;
autoDeploy:在 Tomcat 处于运行状态时放置于 appBase 目录中的应用程序文件是否自 动进行 deploy;默认为 true;
unpackWARs:在启用此 webapps 时是否对 WAR 格式的归档文件先进行展开;默认为 true;
6.2.6 Context组件 Context是Host的子标签,代表指定一个Web应用,它运行在某个指定的虚拟主机(Host)上;每个 Web 应用都是一个 WAR 文件,或文件的目录;
<Context path ="/test" docBase ="D:\bjsxt\itbaizhan.war" />
path:context path 既浏览器访问项目的访问路径。
docBase:相应的 Web 应用程序的存放位置;也可以使用相对路径,起始路径为此 Context所属 Host 中 appBase 定义的路径;
6.3 Tomcat处理请求过程
1、用户访问 localhost:8080/test/index.jsp,请求被发送到 Tomcat,被监听 8080 端口并处理 HTTP/1.1 协议的 Connector 获得。
2、Connector 把该请求交给它所在的 Service 的 Engine 来处理,并等待 Engine 的回应。
3、Engine 获得请求 localhost/test/index.jsp,匹配所有的虚拟主机 Host。
4、Engine 匹配到名为 localhost 的 Host 虚拟主机来处理/test/index.jsp 请求(即使匹配
不到会请求交给默认 Host 处理),Host 会根据/test 匹配它所拥有的所有的 Context。
5、匹配到的 Context 获得请求/index.jsp。
6、构造 HttpServletRequest 对象和 HttpServletResponse 对象,作为参数调用 JspServlet 的 doGet()或 doPost() 执行业务逻辑、数据存储等程序。
7、Context 把执行完之后的结果通过 HttpServletResponse 对象返回给 Host。
8、Host 把 HttpServletResponse 返回给 Engine。
9、Engine 把 HttpServletResponse 对象返回 Connector。
10、Connector 把 HttpServletResponse 对象返回给客户 Browser。
四、 Servlet技术详解 Servlet4.0
1.创建第一个Servlet案例 1.1 创建Servlet package com.bjsxt; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HelloWorld extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out .println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">" ); out.println("<HTML>" ); out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>" ); out.println(" <BODY>" ); out.println("<font color=blue>Hello World</font>" ); out.println(" </BODY>" ); out.println("</HTML>" ); out.flush(); out.close(); } }
1.2 编译Servlet E:\>javac -classpath "E:\Tomcat\apache-tomcat-9.0.34\lib\servlet-api.jar" HelloWorld.java
1.3 创建web.xml 1.3.1 什么是web.xml 项目的部署描述文件,是 JavaWeb 工程的配置文件,通过 web.xml 文件可以配置 servlet、filter 等技术。Tomcat 启动时会先解析该配置文件获取项目的配置信息。
1.3.2 web.xml 文件中的头信息 <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > </web-app >
1.3.3 在 web.xml 文件中配置 Servlet <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > HelloWorld</servlet-name > <servlet-class > com.bjsxt.servlet.HelloWorld</servlet-class > </servlet > <servlet-mapping > <servlet-name > HelloWorld</servlet-name > <url-pattern > /helloworld.do</url-pattern > </servlet-mapping > </web-app >
1.4 部署Servlet 1.4.1 Web工程目录结构
1.5 访问Servlet http://localhost:8888/servletdemo/hello
1.6 访问资源测试结果
2. Servlet继承结构
2.1 Servlet接口 1.init(),创建 Servlet 对象后立即调用该方法完成其他初始化工作。
2.service(),处理客户端请求,执行业务操作,利用响应对象响应客户端请求。
3.destroy(),在销毁 Servlet 对象之前调用该方法,释放资源。
4.getServletConfig(),ServletConfig 是容器向 servlet 传递参数的载体。
5.getServletInfo(),获取 servlet 相关信息。
2.2 ServletConfig 接口
Servlet 运行期间,需要一些辅助信息,这些信息可以在 web.xml 文件中,使用一个或多
个元素,进行配置。当 Tomcat 初始化一个 Servlet 时,会将该 Servlet 的配置信息,封装到
一个 ServletConfig 对象中,通过调用 init(ServletConfig config)方法,将 ServletConfig 对称传递给 Servlet
2.3 GenericServlet是个抽象类 GenericServlet 是实现了 Servlet 接口的抽象类。在 GenericServlet 中进一步的定义了Servlet 接口的具体实现,其设计的目的是为了和应用层协议解耦,在 GenericServlet 中包含 一个 Service 抽象方法。我们也可以通过继承 GenericServlet 并实现 Service 方法实现请求的处理 , 但 是 需 要 将 ServletReuqest 和 ServletResponse 转 为 HttpServletRequest 和HttpServletResponse。
2.4 HttpServlet类 继承自 GenericServlet. 针对于处理 HTTP 协议的请求所定制。在 HttpServlet 的 service() 方法中已经把 ServletReuqest 和 ServletResponse转为 HttpServletRequest 和HttpServletResponse。 直接使用 HttpServletRequest 和 HttpServletResponse, 不再需要强 转。实际开发中, 直接继承 HttpServlet, 并根据请求方式复写 doXxx() 方法即可。
3.Servlet的生命周期 Servlet的生命周期是由容器管理的,分别经历三个阶段:
init():初始化
service():服务
destroy():销毁
当客户端浏览器第一次请求 Servlet 时,容器会实例化这个 Servlet,然后调用一次 init
方法,并在新的线程中执行 service 方法处理请求。service 方法执行完毕后容器不会销毁这
个 Servlet 而是做缓存处理,当客户端浏览器再次请求这个 Servlet 时,容器会从缓存中直接
找到这个 Servlet 对象,并再一次在新的线程中执行 Service 方法。当容器在销毁 Servlet 之
前对调用一次 destroy 方法。
4. Servlet处理请求的原理 当浏览器基于 get 方式请求我们创建 Servlet 时,我们自定义的 Servlet 中的 doGet 方法 会被执行。doGet 方法能够被执行并处理 get 请求的原因是,容器在启动时会解析 web 工程 中 WEB-INF 目录中的 web.xml 文件,在该文件中我们配置了 Servlet 与 URI 的绑定,容器通 过对请求的解析可以获取请求资源的 URI,然后找到与该 URI 绑定的 Servlet 并做实例化处理 (注意:只实例化一次,如果在缓存中能够找到这个 Servlet 就不会再做次实例化处理)。在实 例化时会使用 Servlet 接口类型作为引用类型的定义,并调用一次 init 方法,由于 HttpServlet 中重写了该方法所以最终执行的是 HttpServlet 中 init 方法(HttpServlet 中的 Init 方法是一个空 的方法体),然后在新的线程中调用 service 方法。由于在 HttpServlet 中重写了 Service 方法 所以最终执行的是 HttpServlet 中的 service 方法。在 service 方法中通过 request.getMethod()获取到请求方式进行判断如果是 Get 方式请求就执行 doGet 方法,如果是 POST 请求就执行 doPost 方法。如果是基于 GET 方式提交的,并且在我们自定义的 Servlet 中又重写了HttpServlet 中的 doGet 方法,那么最终会根据 Java 的多态特性转而执行我们自定义的 Servlet 中的 doGet 方法。
5. Servlet的使用 5.1 Servlet的作用 获取用户提交的数据
获取浏览器附加的信息
处理数据(访问数据库或调用接口)
给浏览器产生一个响应
在响应中添加附加信息
6 在Idea中开发Servlet 6.1 在Idea中创建Web工程 6.1.1 创建项目
6.1.2 创建Web工程
6.1.3 Web工程结构
6.1.4 为项目添加servlet-api.jar
6.1.5 在Idea中配置Tomcat
6.1.6 修改项目的访问路径(Content Path)
6.2 Idea中的web项目部署 在 Idea 中默认的并不会把 web 项目真正的部署到 Tomcat 的 webapps 目录中,而是通过为每个 web 项目创建一个独立的 Tomcat 副本并在 Tomcat 副本中通过的 Tomcat 的 Context 组件完成项目的目录指定,在 Context 组件的 docBase 属性中会指定 Idea 对 web 项目编译后 的目录 out/artifacts/…..。
6.2.1 默认部署方式 Idea 会在 C:\Users\Administrator.IntelliJIdea2019.2\system\tomcat 中为每个 Web 项目创 建一个独立的 Tomcat 副本。
C:\Users\Administrator.IntelliJIdea2019.2\system\tomcat\Tomcat_9_0_34_demo_4\conf\Catalina\localhost 目录中生成一个该项目的 xml 文件名称为:”项目名.xml”。
在 xml 文件中指定 web 项目编译完后的 artifacts 目录的位置。
Idea 通过执行 Tomcat 的 catalina.bat 启动脚本启动 Tomcat,通过启动参数来指定启动
Tomcat 副本运行指定目录中的 web 项目
Idaa 在启动 Tomcat 之前会先在操作系统中设置一些临时环境变量,这些变量会被
Tomcat 的启动脚本所读取。
CATALINA_BASE:是 Tomcat 副本的工作目录
CATALINA_HOME:是 Tomcat 的安装目录
在 Catalina.bat 启 动 脚 本 运 行 时 , 会 先 去 判 断 脚 本 中 的 CATALINA_HOME 以 及 CATALINA_BASE 是否有默认值,如果没有则直接读取系统环境变量中的值作为他们的默认 值。由于 Idea 在启动 Tomcat 之前已经设置了临时环境变量,所以 tomcat 在启动后就会运 行部署在 Tomcat 副本中的 web 项目。
6.2.2 将web项目部署到Tomcat的webapps中 点击项目结构选项,指定输出 artifacts 的目录为 Tomcat 的 webapps 中的 demo 目录。
启动 Tomcat,查看 demo 目录中的内容。
在 tomcat 的 webapps 中创建一个目录。
7. HttpServletRequest 对象 HttpServletRequest 对象代表客户端浏览器的请求,当客户端浏览器通过 HTTP 协议访问服务器时,HTTP 请求中的所有信息都会被 Tomcat 所解析并封装在这个对象中,通过这个对 象提供的方法,可以获得客户端请求的所有信息。
7.1 获取请求信息 @WebServlet("/demo1.do") public class Demo1 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter out = resp.getWriter(); StringBuffer requestURL = req.getRequestURL(); String requestURI = req.getRequestURI(); String remoteAddr = req.getRemoteAddr(); String localAddr = req.getLocalAddr(); int localPort = req.getLocalPort(); out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">" ); out.println("<HTML>" ); out.println(" <HEAD><TITLE>itbz</TITLE></HEAD>" ); out.println(" <BODY>" ); out.println("<br>" +requestURL+"</br>" ); out.println("<br>" +requestURI+"</br>" ); out.println("<br>" +remoteAddr+"</br>" ); out.println("<br>" +localAddr+"</br>" ); out.println("<br>" +localPort+"</br>" ); out.println(" </BODY>" ); out.println("</HTML>" ); out.flush(); out.close(); } }
7.2 获取请求数据 7.2.1 在Servlet获取请求数据的方式 req.getParameter(“key”)
根据key获取指定value,返回一个字符串
String str = req.getParameter("key" );
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form action ="demo2.do" method ="post" > <input type ="text" name ="A" /> <br /> <input type ="password" name ="B" /> <br /> <input type ="submit" value ="ok" /> </form > </body > </html >
@WebServlet("/demo2.do") public class demo2 extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("A" ); String userwpd = req.getParameter("B" ); PrintWriter out = resp.getWriter(); out.println("USerName" +username); out.println("USerPwd" +userwpd); out.flush(); out.close(); } }
7.2.2 获取复选框(checkbox组件)中的值 req.getParameterValues(“checkboxkey”)
获取复选框(checkbox 组件)中的值,返回一个 String[]。
String[] userlikes = req.getParameterValues("checkboxkey" );
@WebServlet("/demo2.do") public class demo2 extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("A" ); String userwpd = req.getParameter("B" ); String[] userlikes = req.getParameterValues("userlike" ); List<String> list = Arrays.asList(userlikes); PrintWriter out = resp.getWriter(); out.println("USerName" +username); out.println("USerPwd" +userwpd); out.println("UserLike" +list); out.flush(); out.close(); } }
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form action ="demo2.do" method ="post" > <input type ="text" name ="A" /> <br /> <input type ="password" name ="B" /> <br /> 用户爱好:<input type ="checkbox" name ="userlike" value ="sport" > 体育<br > <input type ="checkbox" name ="userlike" value ="music" > 音乐<br > <input type ="checkbox" name ="userlike" value ="music" > 音乐<br > <input type ="submit" value ="ok" /> </form > </body > </html >
7.2.3 获取所有提交数据的 key req.getParameterNames()
获取请求中所有数据的 key,该方法返回一个枚举类型。
Enumeration<String> parameterNames = req.getParameterNames();
@WebServlet("/demo2.do") public class demo2 extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("A" ); String userwpd = req.getParameter("B" ); String[] userlikes = req.getParameterValues("userlike" ); List<String> list = Arrays.asList(userlikes); Enumeration<String> parameterNames = req.getParameterNames(); List<String> list1 = new ArrayList <>(); while (parameterNames.hasMoreElements()) { list1.add(parameterNames.nextElement()); } PrintWriter out = resp.getWriter(); out.println("USerName" +username); out.println("USerPwd" +userwpd); out.println("UserLike" +list); out.println("Names" +list1); out.flush(); out.close(); } }
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form action ="demo2.do" method ="post" > <input type ="text" name ="A" /> <br /> <input type ="password" name ="B" /> <br /> 用户爱好:<input type ="checkbox" name ="userlike" value ="sport" > 体育<br > <input type ="checkbox" name ="userlike" value ="music" > 音乐<br > <input type ="checkbox" name ="userlike" value ="music" > 音乐<br > <input type ="submit" value ="ok" /> </form > </body > </html >
7.2.4 使用 Map 结构获取提交数据 req.getParameterMap()
获取请求中所有的数据并存放到一个 Map 结构中,该方法返回一个 Map,其中 key 为 String 类型 value 为 String[]类型。
Map<String, String[]> parameterMap = req.getParameterMap();
@WebServlet("/demo2.do") public class demo2 extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("A" ); String userwpd = req.getParameter("B" ); String[] userlikes = req.getParameterValues("userlike" ); List<String> list = Arrays.asList(userlikes); Enumeration<String> parameterNames = req.getParameterNames(); List<String> list1 = new ArrayList <>(); while (parameterNames.hasMoreElements()) { list1.add(parameterNames.nextElement()); } Map<String, String[]> parameterMap = req.getParameterMap(); Iterator<Map.Entry<String,String[]>> iterator = parameterMap.entrySet().iterator(); PrintWriter out = resp.getWriter(); out.println("USerName" +username); out.println("USerPwd" +userwpd); out.println("UserLike" +list); out.println("Names" +list1); while (iterator.hasNext()) { Map.Entry<String,String[]> entry = iterator.next(); String key = entry.getKey(); String[] value = entry.getValue(); out.println(key+"=" +Arrays.asList(value)); } out.flush(); out.close(); }
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form action ="demo2.do" method ="post" > <input type ="text" name ="A" /> <br /> <input type ="password" name ="B" /> <br /> 用户爱好:<input type ="checkbox" name ="userlike" value ="sport" > 体育<br > <input type ="checkbox" name ="userlike" value ="music" > 音乐<br > <input type ="checkbox" name ="userlike" value ="music" > 音乐<br > <input type ="submit" value ="ok" /> </form > </body > </html >
7.3 设置请求编码 req.setCharacterEncoding(“utf-8”)
请求的数据包基于字节在网络上传输,Tomcat 接收到请求的数据包后会将数据包中的
字节转换为字符。在 Tomcat 中使用的是 ISO-8859-1 的单字节编码完成字节与字符的转换,
所以数据中含有中文就会出现乱码,可以通过 req.setCharacterEncoding(“utf-8”)方法来对提
交的数据根据指定的编码方式重新做编码处理。
@WebServlet("/demo2.do") public class demo2 extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8" ); String username = req.getParameter("A" ); System.out.println(username); String userwpd = req.getParameter("B" ); String[] userlikes = req.getParameterValues("userlike" ); List<String> list = Arrays.asList(userlikes); Enumeration<String> parameterNames = req.getParameterNames(); List<String> list1 = new ArrayList <>(); while (parameterNames.hasMoreElements()) { list1.add(parameterNames.nextElement()); } Map<String, String[]> parameterMap = req.getParameterMap(); Iterator<Map.Entry<String,String[]>> iterator = parameterMap.entrySet().iterator(); PrintWriter out = resp.getWriter(); out.println("USerName" +username); out.println("USerPwd" +userwpd); out.println("UserLike" +list); out.println("Names" +list1); while (iterator.hasNext()) { Map.Entry<String,String[]> entry = iterator.next(); String key = entry.getKey(); String[] value = entry.getValue(); out.println(key+"=" +Arrays.asList(value)); } out.flush(); out.close(); } }
7.4 资源访问方式 7.4.1 绝对路径 绝对路径访问资源表示示直接以”/”作为项目的 Content Path。该方式适用于以”/”作为项目
的 Content Path。
<form action ="/getInfo.do" method ="post" >
7.4.2 相对路径 相对路径访问资源表示会相对于项目的 Content Path 作为相对路径。该方式适用于为项
目指定的具体的 Content Path。
<form action ="getInfo.do" method ="post" >
上面红箭头所指示的就是Content Path
7.5 获取请求头信息 7.5.1 获取请求头信息 req.getHeader(“headerKey”)
根据请求头中的 key 获取对应的 value。
String headerValue = req.getHeader("headerKey" );
req.getHeaderNames()
获取请求头中所有的 key,该方法返回枚举类型。
Enumeration<String> headerNames = req.getHeaderNames();
@WebServlet("/demo3.do") public class Demo3 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String header = req.getHeader("Accept-Encoding" ); Enumeration<String> headerNames = req.getHeaderNames(); PrintWriter pw = resp.getWriter(); pw.println("Header Info-Accept-Encoding: " +header); while (headerNames.hasMoreElements()) { String key = headerNames.nextElement(); String value = req.getHeader(key); pw.println(key+": " +value); } pw.flush(); pw.close(); } }
7.5.2 获取请求头案例 需求:编写一个 Servlet,如果浏览器的语言是 zh-CN,显示“你好,聪明的中国人!”, 如果浏览器的语言设置为 en-US,那么则显示“Hello,American”。
@WebServlet("/demo4.do") public class demo4 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String header = req.getHeader("Accept-Language" ); resp.setContentType("text/html;charset=utf-8" ); PrintWriter printWriter = resp.getWriter(); if (header.startsWith("zh-CN" )) { printWriter.println("你好,聪明的中国人!" ); }else if (header.startsWith("en-US" )) { printWriter.println("hello,dog" ); }else { System.out.println("无法识别" ); } } }
7.6HttpServletRequest对象的生命周期 当有请求到达 Tomcat 时,Tomcat 会创建 HttpServletRequest 对象,并将该对象通过参数的方式传递到我们 Servlet 的方法中,当处理请求处理完毕并产生响应后该对象生命周期结束
8 HttpServletResponse 对象 HttpServletResponse 对象代表服务器的响应。这个对象中封装了响应客户端浏览器的流对象,以及向客户端浏览器响应的响应头、响应数据、响应状态码等信息。
8.1 设置响应类型 resp.setContentType(“MIME”)
该方法可通过 MIME-Type 设置响应类型。
8.1.1 设置字符型响应 常见的字符型响应类型:
resp.setContentType(“text/html”)
设置响应类型为文本型,内容含有html字符串,是默认的响应类型
resp.setContentType(“text/plain”)
设置响应类型为文本型,内容是普通文本。
resp.setContentType(“application/json”)
设置响应类型为 JSON 格式的字符串。
public class Demo5 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html" ); PrintWriter out = resp.getWriter(); out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">" ); out.println("<HTML>" ); out.println(" <HEAD><TITLE>itbz</TITLE></HEAD>" ); out.println(" <BODY>" ); out.println("<font color=blue>HelloWorld</font>" ); out.println(" </BODY>" ); out.println("</HTML>" ); out.flush(); out.close(); } }
8.1.2 设置字节型响应 常见的字节型响应:
resp.setContentType(“image/jpeg”)
设置响应类型为图片类型,图片类型为 jpeg 或 jpg 格式。
resp.setContentType(“image/gif”)
设置响应类型为图片类型,图片类型为 gif 格式。
@WebServlet("/demo6.do") public class demo6 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { File file = new File ("d:/尚学堂.jpg" ); InputStream is = new FileInputStream (file); byte [] buff = new byte [is.available()]; is.read(buff); resp.setContentType("image/jpg" ); OutputStream outputStream = resp.getOutputStream(); outputStream.write(buff); outputStream.flush(); outputStream.close(); } }
8.2 设置响应编码 response.setCharacterEncoding(“utf-8”);
设置服务端为浏览器产生响应的响应编码,服务端会根据此编码将响应内容的字符转换为字节。
当浏览器的显示编码方式不是UTF-8的时候,就会看到乱码,需要手动指定浏览器编码。
response.setContentType(“text/html;charset=utf-8”);
设置服务端为浏览器产生响应的响应编码,服务端会根据此编码将响应内容的字符转换为字节。同时客户端浏览器会根据此编码方式显示响应内容。
8.3 在响应中添加附加信息 8.3.1 重定向响应 response.sendRedirect(URL地址)
重定向响应会在响应头中添加一个 Location 的 key 对应的 value 是给定的 URL。客户端浏览器在解析响应头后自动向 Location 中的 URL 发送请求。
重定向响应特点:
重定向会产生两次请求两次响应。
重定向的 URL 是由客户端浏览器发送的
浏览器的地址名会有变化
@WebServlet("/demo7.do") public class demo7 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.sendRedirect("https://www.baidu.com/" ); } }
8.3.2 重定向响应案例 需求:创建一个搜索页面,通过百度搜索引擎完成内容搜索
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form action ="demo7.do" method ="post" > 搜索:<input type ="text" name ="search" /> <br /> <input type ="submit" value ="search" > </form > </body > </html >
@WebServlet("/demo7.do") public class demo7 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8" ); String search = req.getParameter("search" ); resp.sendRedirect("https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=" + URLEncoder.encode(search,"utf-8" )); } }
8.3.3文件下载 在实现文件下载时,我们需要修改响应头,添加附加信息。
response.setHeader("Content-Disposition" , "attachment; filename=" +文件名);
Content-Disposition:attachment
该附加信息表示作为对下载文件的一个标识字段。不会在浏览器中显示而是直接做下载处理
filename=文件名
表示指定下载文件的文件名。
@WebServlet("/demo8.do") public class demo8 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { File file = new File ("d:/尚学堂.jpg" ); InputStream is = new FileInputStream (file); byte [] buff = new byte [is.available()]; is.read(buff); resp.addHeader("Content-Disposition" ,"attachment;filename=" +file.getName()); OutputStream os = resp.getOutputStream(); os.write(buff); os.flush(); os.close(); }}
8.3.4 解决文件名中文乱码问题 resp.addHeader("Content-Disposition" ,"attachment;filename=" +new String (file.getName().getBytes("gbk" ),"iso-8859-1" ));
@WebServlet("/demo8.do") public class demo8 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { File file = new File ("d:/尚学堂.jpg" ); InputStream is = new FileInputStream (file); byte [] buff = new byte [is.available()]; is.read(buff); resp.addHeader("Content-Disposition" ,"attachment;filename=" +new String (file.getName().getBytes("gbk" ),"iso-8859-1" )); OutputStream os = resp.getOutputStream(); os.write(buff); os.flush(); os.close(); }}
9 ServletContext对象 9.1 ServletContext对象介绍 ServletContext官方叫Servlet上下文,服务器会为每一个Web应用创建一个ServletContext对象。这个对象全局唯一,而且Web应用中的所有Servlet都共享这个对象。所以叫做全局应用程序共享对象 。
9.2 ServletContext对象的作用 相对路径转化成绝对路径
获取容器的附加信息
读取配置信息
全局容器
9.3 ServletContext对象的应用 9.3.1 相对路径转绝对路径 context.getRealPath(“path”)该方法可以将一个相对路径转换为绝对路径,在文件上传与下载时需要用到该方法做路 径的转换。
@WebServlet("/demo8.do") public class demo8 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this .getServletContext(); String realPath = servletContext.getRealPath("image/尚学堂.jpg" ); System.out.println(realPath); File file = new File (realPath); InputStream is = new FileInputStream (file); byte [] buff = new byte [is.available()]; is.read(buff); resp.addHeader("Content-Disposition" ,"attachment;filename=" +new String (file.getName().getBytes("gbk" ),"iso-8859-1" )); OutputStream os = resp.getOutputStream(); os.write(buff); os.flush(); os.close(); }}
9.3.2 获取容器的附加信息 servletContext.getServerInfo()
返回 Servlet 容器的名称和版本号
servletContext.getMajorVersion()
返回 Servlet 容器所支持 Servlet 的主版本号。
servletContext.getMinorVersion()
返回 Servlet 容器所支持 Servlet 的副版本号。
@WebServlet("/demo9.do") public class demo9 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this .getServletContext(); String serverInfo = servletContext.getServerInfo(); int majorVersion = servletContext.getMajorVersion(); int minorVersion = servletContext.getMinorVersion(); resp.setContentType("text/plain;charset=utf-8" ); PrintWriter out = resp.getWriter(); out.println("容器基本信息:" +serverInfo); out.println("容器支持Servlet的版本号为:" +majorVersion+"." +minorVersion); out.flush(); out.close(); } }
9.3.3 获取 web.xml 文件中的信息 <context-param > <param-name > key</param-name > <param-value > value</param-value > </context-param >
servletContext.getInitParameter(“key”)
该方法可以读取 web.xml 文件中标签中的配置信息。
servletContext.getInitParameterNames()
该方法可以读取 web.xml 文件中所有 param-name 标签中的值
import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.util.Enumeration;@WebServlet("/demo10.do") public class demo10 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this .getServletContext(); Enumeration<String> initParameterNames = servletContext.getInitParameterNames(); PrintWriter pw = resp.getWriter(); while (initParameterNames.hasMoreElements()) { String name = initParameterNames.nextElement(); String value = servletContext.getInitParameter(name); pw.println("Name" +name+"==" +"Value" +value); } pw.flush(); pw.close(); } }
<context-param > <param-name > key1</param-name > <param-value > BJSXT</param-value > </context-param > <context-param > <param-name > key2</param-name > <param-value > ITBJ</param-value > </context-param >
9.3.4 全局容器 servletContext.setAttribute(“key”,ObjectValue)
向全局容器 中存放数据
servletContext.getAttribute(“key”)
从全局容器 中获取数据。
servletContext.removeAttribute(“key”)
根据 key 删除全局容器 中的 value。
@WebServlet("/demo11.do") public class demo11 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override public void init () throws ServletException { ServletContext servletContext = this .getServletContext(); servletContext.setAttribute("key1" ,"BJSXT" ); servletContext.setAttribute("key2" ,"ITBZ" ); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this .getServletContext(); String value1 = (String)servletContext.getAttribute("key1" ); String value2 = (String)servletContext.getAttribute("key2" ); PrintWriter pw = resp.getWriter(); pw.println(value1); pw.println(value2); pw.flush(); pw.close(); } }
9.4 ServletContext对象生命周期 当容器启动时会创建 ServletContext 对象并一直缓存该对象,直到容器关闭后该对象生命周期结束。ServletContext 对象的生命周期非常长,所以在使用全局容器时不建议存放业务数据。
10 ServletConfig对象 ServletConfig对象对应web.xml文件中的节点。当Tomcat初始化一个Servlet时,会将该Servlet的配置信息,封装到一个ServletConfig对象中,我们可以通过该对象读取节点中的配置信息
<servlet > <servlet-name > servletName</servlet-name > <servlet-class > servletClass</servlet-class > <init-param > <param-name > key</param-name > <param-value > value</param-value > </init-param > </servlet >
servletConfig.getInitParameter(“key”)
该方法可以读取 web.xml 文件中标签中标签中的配置信息。
servletConfig.getInitParameterNames()
该方法可以读取 web.xml 文件中当前标签中所有标签中的值。
public class demo12 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletConfig servletConfig = this .getServletConfig(); Enumeration<String> initParameterNames = servletConfig.getInitParameterNames(); PrintWriter pw = resp.getWriter(); while (initParameterNames.hasMoreElements()) { String name = initParameterNames.nextElement(); String value = servletConfig.getInitParameter(name); pw.println("key:" +name+"====" +"value" +value); } pw.flush(); pw.close(); } }
<servlet > <servlet-name > demo12</servlet-name > <servlet-class > com.bjsxt.servlet.demo12</servlet-class > <init-param > <param-name > key1</param-name > <param-value > BJSXT</param-value > </init-param > <init-param > <param-name > key2</param-name > <param-value > BJSXTioioioio</param-value > </init-param > </servlet > <servlet-mapping > <servlet-name > demo12</servlet-name > <url-pattern > /demo12.do</url-pattern > </servlet-mapping >
11 Cookie对象与HttpSession对象 (用于维护客户端与服务端的会话状态的维持) Cookie 对象与 HttpSession 对象的作用是维护客户端浏览器与服务端的会话状态的两个 对象。由于 HTTP 协议是一个无状态的协议,所以服务端并不会记录当前客户端浏览器的访问状态,但是在有些时候我们是需要服务端能够记录客户端浏览器的访问状态的,如获取当前客户端浏览器的访问服务端的次数时就需要会话状态的维持。在 Servlet 中提供了 Cookie 对象与 HttpSession 对象用于维护客户端与服务端的会话状态的维持。二者不同的是 Cookie是通过客户端浏览器实现会话的维持,而 HttpSession 是通过服务端来实现会话状态的维持
11.1 Cookie对象 11.1.1 Cookie对象的特点 Cookie使用字符串存储数据
Cookie使用key与value结构存储数据
单个cookie存储数据大小限制在4097字节
Cookie 存储的数据中不支持中文,Servlet4.0 中支持
Cookie 是与域名绑定所以不支持跨一级域名访问
Cookie 对象保存在客户端浏览器或系统磁盘中
Cookie 分为持久化 Cooke 与状态 Cookie
浏览器在保存同一域名所返回 Cookie 的数量是有限的。不同浏览器支持的数量不同,Chrome 浏览器为 50 个
浏览器每次请求时都会把与当前访问的域名相关的 Cookie 在请求中提交到服务端。
11.1.2 Cookie对象的创建 Cookie cookie = new Cookie(“key”,”value”);
通过 new 关键字创建 Cookie 对象
respon.addCookie(cookie)
通过HttpServletResponse对象将Cookie写回到客户端浏览器
@WebServlet("/demo13.do") public class demo13 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie ("key" , "ITBZ" ); resp.addCookie(cookie); PrintWriter pw = resp.getWriter(); pw.println("Create Cookie" ); pw.flush(); pw.close(); } }
11.1.3 获取Cookie中的数据 通过HttpServletRequest对象中获取Cookie,返回Cookie数组。
Cookie[] cookies = request.getCookies()
@WebServlet("/demo13.do") public class demo13 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie ("key" , "ITBZ" ); resp.addCookie(cookie); PrintWriter pw = resp.getWriter(); pw.println("Create Cookie" ); pw.flush(); pw.close(); } }
@WebServlet("/demo14.do") public class demo14 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies(); PrintWriter pw = resp.getWriter(); for (int i = 0 ;i<cookies.length;i++) { Cookie cookie = cookies[i]; String name = cookie.getName(); String value = cookie.getValue(); pw.println("name" +name+"=====" +"value" +value); } pw.flush(); pw.close(); } }
11.1.4 解决Cookie不支持中文 在 Servlet4.0 版本之前的 Cookie 中是不支持中文存储的,如果存储的数据中含有中文, 代码会直接出现异常。我们可以通过对含有中文的数据重新进行编码来解决该问题。在 Servlet4.0 中的 Cookie 是支持中文存储的(name的值依然不能使用中文,VALUE值可以使用中文)
java.lang.IllegalArgumentException: Control character in cookie value or attribute.
URLEncoder.encode(“content”,”code”)
将内容按照指定的编码方式做 URL 编码处理。
URLDecoder.decode(“content”,”code”)
将内容按照指定的编码方式做 URL 解码处理。
@WebServlet("/demo13.do") public class demo13 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie (URLEncoder.encode("尚学堂" ,"utf-8" ), URLEncoder.encode("百战程序员" ,"utf-8" )); resp.addCookie(cookie); PrintWriter pw = resp.getWriter(); pw.println("Create Cookie" ); pw.flush(); pw.close(); } }
@WebServlet("/demo14.do") public class demo14 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies(); PrintWriter pw = resp.getWriter(); for (int i = 0 ;i<cookies.length;i++) { Cookie cookie = cookies[i]; String name = cookie.getName(); String value = cookie.getValue(); pw.println("name" + URLDecoder.decode(name,"utf-8" ) +"=====" +"value" +URLDecoder.decode(value,"utf-8" )); } pw.flush(); pw.close(); } }
11.1.5 Cookie跨域问题 域名分类:域名分为顶级域、顶级域名(一级域名)、二级域名。
域名等级的区别:一级域名比二级域名更高级,二级域名是依附于一级域名之下的附属 分区域名,即二级域名是一级域名的细化分级。例如:baidu.com 为一级域名,news.baidu.com 为二级域名。Cookie 不支持一级域名的跨域,支持二级域名的跨域。
11.1.6 状态Cookie和持久化Cookie 状态 Cookie:浏览器会缓存 Cookie 对象。浏览器关闭后 Cookie 对象销毁。
持久化 Cookie:浏览器会对 Cookie 做持久化处理,基于文件形式保存在系统的指定目录中。在 Windows10 系统中为了安全问题不会显示 Cookie 中的内容。
当 Cookie 对象创建后默认为状态 Cookie。可以使用 Cookie 对象下的 cookie.setMaxAge(60)方法设置失效时间,单位为秒。一旦设置了失效时间,那么该 Cookie 为持久化 Cookie,浏览器会将 Cookie 对象持久化到磁盘中。当失效时间到达后文件删除。
import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.net.URLEncoder;@WebServlet("/demo13.do") public class demo13 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie (URLEncoder.encode("尚学堂" ,"utf-8" ), URLEncoder.encode("百战程序员" ,"utf-8" )); cookie.setMaxAge(60 ); resp.addCookie(cookie); PrintWriter pw = resp.getWriter(); pw.println("Create Cookie" ); pw.flush(); pw.close(); } }
11.1.7 通过Cookie实现客户端与服务端会话的维持(案例) 需求:当客户端浏览器第一次访问 Servlet 时返回“您好,欢迎您第一次访问!”,第 二次访问时返回“欢迎您回来!”。
@WebServlet("/demo15.do") public class demo15 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies(); boolean flag = false ; if (cookies!=null ) { for (Cookie cookie : cookies) { if ("Welcome" .equals(cookie.getName())) { flag = true ; break ; } } } resp.setContentType("text/plain;charset=utf-8" ); PrintWriter pw = resp.getWriter(); if (flag) { pw.println("欢迎您回来!" ); }else { pw.println("您好,欢迎您第一次访问!" ); Cookie cookie = new Cookie ("Welcome" ,"welcome" ); cookie.setMaxAge(60 ); resp.addCookie(cookie); } } }
11.1.8 Cookie总结 Cookie 对于存储内容是基于明文的方式存储的,所以安全性很低。不要在 Cookie 中存 放敏感数据。在数据存储时,虽然在 Servlet4.0 中 Cookie 支持中文,但是建议对 Cookie 中存放的内容做编码处理,也可提高安全性。
11.2 HttpSession对象 11.2.1 HttpSession对象的特点 HttpSession 保存在服务端
HttpSession 可以存储任何类型的数据
HttpSession 使用 Key 是字符串类型,Value则是Object类型
HttpSession 存储数据大小无限制
11.2.2 HttpSession对象的创建
HttpSession 对象的创建是通过 request.getSession()方法来创建的。客户端浏览器在请求服务端资源时,如果在请求中没有 JSessionID,getSession()方法将会为这个客户端浏览器创 建一个新的 HttpSession 对象,并为这个 HttpSession 对象生成一个 JSessionID,在响应中通过 Cookie 写回给客户端浏览器,如果在请求中包含了 JSessionID,getSession()方法则根据这个ID 返回与这个客户端浏览器对应的 HttpSession 对象。
getSession()方法还有一个重载方法getSession(true|false)。当参数为true时与getSession()方法作用相同。当参数为 false 时则只去根据 JSessionID 查找是否有与这个客户端浏览器对应的 HttpSession,如果有则返回,如果没有 JSessionID 则不会创建新的 HttpSession 对象。
会为每一个浏览器创建独立的不同的HttpSession对象
@WebServlet("/demo16.do") public class demo16 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(true ); System.out.println(session); PrintWriter pw = resp.getWriter(); pw.println("Create the OK" ); pw.flush(); pw.close(); } }
11.2.3 获取HttpSession中的数据 session.setAttribute(“key”,value)
将数据存储到 HttpSession 对象中
Object value = session.getAttribute(“key”)
根据 key 获取 HttpSession 中的数据,返回 Object
Enumeration attributeNames = session.getAttributeNames()
获取 HttpSession 中所有的 key,返回枚举类型
session.removeAttribute(“key”)
根据 key 删除 HttpSession 中的数据
String id = session.getId()
根据获取当前 HttpSession 的 SessionID,返回字符串类型
@WebServlet("/demo16.do") public class demo16 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(true ); session.setAttribute("key" ,"ITBZ" ); String id = session.getId(); System.out.println(session); PrintWriter pw = resp.getWriter(); pw.println("Create the OK" +id); pw.flush(); pw.close(); } }
@WebServlet("/demo17.do") public class demo17 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); String value = (String) session.getAttribute("key" ); PrintWriter printWriter = resp.getWriter(); printWriter.println(value); printWriter.flush(); printWriter.close(); } }
11.2.4 HttpSession的销毁方式 HttpSession 的销毁方式有两种:
通过 web.xml 文件指定超时时间
通过 HttpSession 对象中的 invalidate()方法销毁当前 HttpSession 对象
我们可以在 web.xml 文件中指定 HttpSession 的超时时间,当到达指定的超时时间后,容器就会销该 HttpSession 对象,单位为分钟。该时间对整个 web 项目中的所有 HttpSession 对象有效。时间的计算方式是根据最后一次请求时间作为起始时间。如果有哪个客户端浏览器 对应的 HttpSession 的失效时间已到,那么与该客户端浏览器对应的 HttpSession 对象就会被销毁。其他客户端浏览器对应的 HttpSession 对象会继续保存不会被销毁。
<session-config > <session-timeout > 1</session-timeout > </session-config >
我们也可以在 Tomcat 的 web.xml 文件中配置 HttpSession 的销毁时间。如果在 Tomcat的 web.xml 文件中配置了 HttpSession 的超时时间对应的是 Tomcat 中所有的 Web 项目都有 效。相当于配置了全局的 HttpSession 超时时间。如果我们在 Web 项目中配置了超时时间, 那么会以 Web 项目中的超时时间为准。
invalidate()方法是 HttpSession 对象中所提供的用于销毁当前 HttpSession 的方法。我们通过调用该方法可以销毁当前 HttpSession 对象。
@WebServlet("/demo16.do") public class demo16 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String header = req.getHeader("User-Agent" ); HttpSession session = req.getSession(); if (header.indexOf("Chrome" )!=-1 ) { session.setAttribute("key" ,"Chrome" ); }else { session.setAttribute("key" ,"IE" ); } String id = session.getId(); System.out.println(session); System.out.println(header); PrintWriter pw = resp.getWriter(); pw.println("Create the OK" +id); pw.flush(); pw.close(); } }
@WebServlet("/demo17.do") public class demo17 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); String a = (String)session.getAttribute("A" ); PrintWriter printWriter = resp.getWriter(); printWriter.println(a); printWriter.flush(); printWriter.close(); } }
11.2.5 通过HttpSession实现客户端与服务端会话的维持 需求:当客户端浏览器第一次访问 Servlet 时返回“您好,欢迎您第一次访问!”,第 二次访问时返回“欢迎您回来!”。
@WebServlet("/demo18.do") public class demo18 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(false ); boolean flag = true ; if (session==null ) { flag = false ; req.getSession(); } resp.setContentType("text/plain;charset=utf-8" ); PrintWriter pw = resp.getWriter(); if (flag) { pw.println("欢迎您回来!" ); }else { pw.println("您好,欢迎您第一次访问!" ); } pw.flush(); pw.close(); } }
11.2.6 HttpSession生命周期 在 HttpSession 对象生命周期中没有固定的创建时间与销毁时间。何时创建取决于我们什么时候第一次调用了 getSession()或 getSession(true)的方法。HttpSession 对象的销毁时间 取决于超时时间的到达以及调用了 invalidate()方法。如果没有超时或者没有调用 invalidate()方法,那么 HttpSession 会一直存储。默认超时时间为 30 分钟(Tomcat 的 web.xml 文件配置的时间就是默认超时时间)。
11.3 HttpSession对象总结 11.3.1 HttpSession 与 Cookie 的区别: cookie 数据存放在客户的浏览器或系统的文件中,而 HttpSession 中的数据存放在服务器中。
cookie 不安全,而 HttSession 是安全的。
单个 cookie 保存的数据不能超过 4K,很多浏览器都限制一个域名保存 cookie 的数量。而 HttpSession 没有容量以及数量的限制。
11.3.2 HttpSession的使用建议 12 自启动Servlet 12.1 自启动Servlet特点 自动启动 Servlet 表示在 Tomcat 启动时就会实例化这个 Servlet,他的实例化过程不依赖 于请求,而是依赖容器的启动
可以通过在 web.xml 中的标签中通过1 配置自启动 Servlet。
public class demo19 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter pw = resp.getWriter(); pw.println("AutoStartServlet......" ); pw.flush(); pw.close(); } @Override public void init () throws ServletException { System.out.println("AutoStartServlet Init......" ); } }
<servlet > <servlet-name > demo19</servlet-name > <servlet-class > com.bjsxt.servlet.demo19</servlet-class > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > demo19</servlet-name > <url-pattern > /demo19.do</url-pattern > </servlet-mapping >
12.2 通过自启动Servlet实现配置信息的读取 需求:修改文件下载案例,通过自启动Servlet读取配置信息
@WebServlet("/demo8.do") public class demo8 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this .getServletContext(); String value = (String)servletContext.getAttribute("path" ); System.out.println(value); String realPath = servletContext.getRealPath(value+"/尚学堂.jpg" ); System.out.println(realPath); File file = new File (realPath); InputStream is = new FileInputStream (file); byte [] buff = new byte [is.available()]; is.read(buff); resp.addHeader("Content-Disposition" ,"attachment;filename=" +new String (file.getName().getBytes("gbk" ),"iso-8859-1" )); OutputStream os = resp.getOutputStream(); os.write(buff); os.flush(); os.close(); }}
public class demo19 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter pw = resp.getWriter(); pw.println("AutoStartServlet......" ); pw.flush(); pw.close(); } @Override public void init () throws ServletException { ServletConfig servletConfig = this .getServletConfig(); String value = servletConfig.getInitParameter("path" ); ServletContext servletContext = this .getServletContext(); servletContext.setAttribute("path" ,value); System.out.println("AutoStartServlet Init......" ); } }
<servlet > <servlet-name > demo19</servlet-name > <servlet-class > com.bjsxt.servlet.demo19</servlet-class > <init-param > <param-name > path</param-name > <param-value > image</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > demo19</servlet-name > <url-pattern > /demo19.do</url-pattern > </servlet-mapping >
13 Servlet线程安全问题 在 Servlet 中使用的是多线程方式来执行 service()方法处理请求,所以我们在使用 Servlet时需要考虑到线程安全问题,在多线程中对于对象中的成员变量是最不安全的,所以不要在 Servlet 中通过成员变量的方式来存放数据,如果一定要使用成员变量存储数据,在对数据进行操作时需要使用线程同步的方式来解决线程安全问题,避免出现数据张冠李戴现象。
public class demo19 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter pw = resp.getWriter(); pw.println("AutoStartServlet......" ); pw.flush(); pw.close(); } @Override public void init () throws ServletException { ServletConfig servletConfig = this .getServletConfig(); String value = servletConfig.getInitParameter("path" ); ServletContext servletContext = this .getServletContext(); servletContext.setAttribute("path" ,value); System.out.println("AutoStartServlet Init......" ); } }
14 Servlet的url-pattern配置 14.1 URL的匹配规则 14.1.1 精确匹配 精确匹配是指中配置的值必须与 url 完全精确匹配
<servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > /demo.do</url-pattern > </servlet-mapping >
14.1.2. 拓展名匹配 在允许使用通配符“”作为匹配规则,“ ”表示匹配任意字符。在扩展 名匹配中只要扩展名相同都会被匹配和路径无关。注意,在使用扩展名匹配时在 中不能使用“/”,否则容器启动就会抛出异常。
<servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > *.do</url-pattern > </servlet-mapping >
14.1.3 路径匹配 根据请求路径进行匹配,在请求中只要包含该路径都匹配。“*”表示任意路径以及子 路径
<servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > /suibian/*</url-pattern > </servlet-mapping >
14.1.4 任意匹配 匹配“/”。匹配所有但不包含 JSP 页面
<url-pattern > /</url-pattern >
匹配所有
<url-pattern > /*</url-pattern >
14.1.5 优先顺序 当一个 url 与多个 Servlet 的匹配规则可以匹配时,则按照 “ 精确路径 > 最长路径 > 扩展名”这样的优先级匹配到对应的 Servlet。
14.1.6 考考你 Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
当请求 URL 为“/abc/a.html”,“/abc/*”和“/*”都匹配,Servlet 引擎将调用 Servlet1。
当请求 URL 为“/abc”时,“/abc/*”和“/abc”都匹配,Servlet 引擎将调用 Servlet3。
当请求 URL 为“/abc/a.do”时,“/abc/”和“ .do”都匹配,Servlet 引擎将调用 Servlet1。
当请求 URL 为“/a.do”时,“/”和“ .do”都匹配,Servlet 引擎将调用 Servlet2。
当请求 URL 为“/xxx/yyy/a.do”时,“/”和“ .do”都匹配,Servlet 引擎将调用 Servlet2。
14.2 URL映射方式 在 web.xml 文件中支持将多个 URL 映射到一个 Servlet 中,但是相同的 URL 不能同时映射到两个 Servlet 中。
14.2.1 方式一 <servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > /suibian/*</url-pattern > <url-pattern > *.do</url-pattern > </servlet-mapping >
14.2.2 方式二 <servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > /suibian/*</url-pattern > </servlet-mapping > <servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > *.do</url-pattern > </servlet-mapping >
15 基本注解式开发Servlet 在 Servlet3.0 以及之后的版本中支持注解式开发 Servlet。对于 Servlet 的配置不在依赖于web.xml 配置文件,而是使用@WebServlet 注解完成 Servlet 的配置。
15.1@WebServlet 注解中属性
@WebServlet(urlPatterns = {"*.do","/ann/*","/ann.do"}) public class demo21 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter pw = resp.getWriter(); pw.println("Annotation Servlet!" ); pw.flush(); pw.close(); } }
15.2@WebInitParam 注解中的属性
@WebServlet(urlPatterns = "/initParam.do",initParams = {@WebInitParam(name = "key1",value = "BJSXT"),@WebInitParam(name="key2",value = "ITB")},loadOnStartup = 1) public class InitParamServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletConfig servletConfig = this .getServletConfig(); String key1 = servletConfig.getInitParameter("key1" ); String key2 = servletConfig.getInitParameter("key2" ); PrintWriter pw = resp.getWriter(); pw.println(key1); pw.println(key2); pw.flush(); pw.close(); } @Override public void init () throws ServletException { ServletConfig servletConfig = this .getServletConfig(); String key1 = servletConfig.getInitParameter("key1" ); String key2 = servletConfig.getInitParameter("key2" ); System.out.println(key1); System.out.println(key2); } }
16 文件上传 在 Servlet3.0 之前的版本中如果实现文件上传需要依赖 apache 的 Fileupload 组件,在Servlet3.0 以及之后的版本中提供了 Part 对象处理文件上传,所以不在需要额外的添加 Fileupload 组件。 在 Servlet3.0 以及之后的版本中实现文件上传时必须要在 Servlet 中开启多参数配置:web.xml
<multipart-config > <file-size-threshold > </file-size-threshold > <location > </location > <max-file-size > </max-file-size > <max-request-size > </max-request-size > </multipart-config >
Part对象中常用的方法
String getContentType()
获取上传文件的文件的MIME类型
long getSize()
上传文件的大小
String getSubmittedFileName()
上传文件的原始文件名
String getName()
获取<input name=”upload” …>标签中 name 属性值
String getHeader(String name)
获取请求头部
void write(String path)
保存文件至服务器
InputStream getInputStream()
获取上传文件的输入流
Collection(String) getHeaderNames()
获取所有请求头部名称
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form action ="fileupload.do" method ="post" enctype ="multipart/form-data" > 文件描述:<input type ="text" name ="desc" /> <br /> 上传文件:<input type ="file" name ="file" /> <br /> <input type ="submit" value ="OK" /> </form > </body > </html >
@WebServlet(urlPatterns = "/fileupload.do") @MultipartConfig public class FileUploadServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this .doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8" ); String desc = req.getParameter("desc" ); Part part = req.getPart("file" ); String newName = UUID.randomUUID().toString()+part.getSubmittedFileName().substring(part.getSubmittedFileName().lastIndexOf("." )); ServletContext servletContext = this .getServletContext(); String realPath = servletContext.getRealPath("image/" + newName); System.out.println(realPath); part.write(realPath); resp.setContentType("text/plain;charset=utf-8" ); PrintWriter pw = resp.getWriter(); pw.println(desc); pw.flush(); pw.close(); } }
tips:
html等文件应创建在web文件夹下,而不是创建在WEB-INF文件夹下。在错误的文件夹下创建html等文件会造成Tomcat可以正常访问servlet,但不能正常访问html页面的结果
17 Filter过滤器 Filter过滤器是Servlet中的一个组件。并不是JavaEE平台中的技术规范。
17.1 过滤器作用 对从客户端向服务器端发送的请求进行过滤,也可以对服务器端返回的响应进行处理。
17.2 Filter的使用 17.2.1 Filter对象的创建 创建一个 Class 实现 Filter 接口,并实现接口中三个抽象方法。
init()方法:初始化方法,在创建 Filter 后立即调用。可用于完成初始化动作。
doFilter()方法:拦截请求与响应方法,可用于对请求和响应实现预处理。
destroy()方法:销毁方法,在销毁 Filter 之前自动调用。可用于完成资源释放等动作。
@WebFilter(urlPatterns = "/suibian/*") public class FirstFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { System.out.println("Init........" ); } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("请求被过滤" ); filterChain.doFilter(servletRequest,servletResponse); System.out.println("响应被过滤" ); } @Override public void destroy () { } }
17.2.2 在Filter中设置请求编码 需求:在 Filter 中实现对请求的编码的设置
@WebFilter("/*") public class EncodingFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { servletRequest.setCharacterEncoding("utf-8" ); filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy () { } }
17.2.3 FilterConfig 对象是用来读取中初始化参数的对象。该对象通过参数传递到 init 方法中,用于读取初始化参数。
filterConfig.getInitParameter(“name”)
通过 name 获取对应的 value。
filterConfig.getInitParameterNames()
返回该 Filter 中所有中的值。
@WebFilter(urlPatterns = "/*",initParams = {@WebInitParam(name="code",value = "utf-8")}) public class EncodingFilter implements Filter { private String defaultCode = "utf-8" ; @Override public void init (FilterConfig filterConfig) throws ServletException { String code = filterConfig.getInitParameter("code" ); if (code!=null &&code.length()>0 ) { this .defaultCode = code; } } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { servletRequest.setCharacterEncoding(this .defaultCode); filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy () { } }
17.3 FilterChain(过滤器链) Filter 技术的特点是在对请求或响应做预处理时,可实现“插拔式”的程序设计。我们可以根据自己需求添加多个 Filter,也可以根据需求去掉某个 Filter,通过修改 web.xml 文件 即可实现。那么如果有多个过滤器对某个请求及响应进行过滤,那么这组过滤器就称为过滤 器链。过滤器的执行顺序和有关。
Filter 执行顺序则按照在 web.xml 文件中配置的上下顺序来决定先后。在上的先执行,在下的后执行。
17.4 Filter的生命周期 Filter 的生命周期是由容器管理的。当容器启动时会实例化 Filter 并调用 init 方法完成初始化动作。当客户端浏览器发送请求时,容器会启动一个新的线程来处理请求,如果请求的URI 能够被过滤器所匹配,那么则先调用过滤器中 的 doFilter 方法,再根据是否有chain.doFilter 的指令,决定是否继续请求目标资源。当容器关闭时会销毁 Filter 对象,在销 毁之前会调用 destroy 方法。
17.5 基于注解式开发Filter Filter 支持注解式开发,通过@WebFilter 注解替代 web.xml 中 Filter 的配置。
使用注解式开发 Filter 时,执行顺序会根据 Filter 的名称进行排序的结果决定调用的顺序。
@WebFilter("/*") public class AnnotationFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Annotation Filter....." ); filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy () { } }
18 Listener 监听器 监听器用于监听web应用中某些对象的创建、销毁、增加、修改、删除等动作的发生,然后做出相应的响应处理。当范围对象的状态发生变化的时候,服务器会自动调用监听器对象中的方法。
18.1 监听器分类 按监听的对象划分,可以分为:
ServletContext 对象生命周期监听器与属性操作监听器
HttpSession 对象生命周期监听器与属性操作监听器
ServletRequest 对象生命周期监听器与属性操作监听器
18.2 ServletContext 监听器 18.2.1 ServletContext 对象的生命周期监听器 ServletContextListener 接口定义了 ServletContext 对象生命周期的监听行为。
void contextInitialized(ServletContextEvent sce)
ServletContext 对象创建后会触发该监听方法,并将 ServletContext 对象传递到该方法中。
void contextDestroyed(ServletContextEvent sce)
ServletContext 对象在销毁之前会触发该监听方法,并将 ServletContext 对象传递到该方法中
public class ServletContextLifecycleListener implements ServletContextListener { @Override public void contextInitialized (ServletContextEvent sce) { ServletContext sc = sce.getServletContext(); System.out.println(sc); System.out.println("ServletContext Init......." ); } @Override public void contextDestroyed (ServletContextEvent sce) { ServletContext sc = sce.getServletContext(); System.out.println(sc); System.out.println("ServletContext Destroy......." ); } }
<listener > <listener-class > com.bjsxt.listener.ServletContextLifecycleListener</listener-class > </listener >
18.2.2 ServletContext对象的属性操作监听器 ServletContextAttributeListener 接口定义了对于 ServletContext 对象属性操作的监听行为。
void attributeAdded(ServletContextAttributeEvent scae)
向 ServletContext 对象中添加属性时会触发该监听方法,并将 ServletContext 对象传递到该方法中。触发事件的方法 servletContext.setAttribute(“key”,”value”)。
void attributeRemoved(ServletContextAttributeEvent scae)
当从 ServletContext 对象中删除属性时会触发该监听方法,并将 ServletContext 对象传递 到该方法中。触发事件方法 servletContext.removeAttribute(“key”)。
void attributeReplaced(ServletContextAttributeEvent scae)
当从 ServletContext 对象中属性的值发生替换时会触发该监听方法,并将 ServletContext 对象传递到该方法中。触发事件的方法servletContext.setAttribute(“key”,”value”)。
public class ServletContextAttrListener implements ServletContextAttributeListener { @Override public void attributeAdded (ServletContextAttributeEvent scae) { System.out.println("---------Start--Added-----------------" ); System.out.println("Name:" +scae.getName()+"Value:" +scae.getValue()); System.out.println(scae.getServletContext()); System.out.println("---------End--Added-----------------" ); } @Override public void attributeRemoved (ServletContextAttributeEvent scae) { System.out.println("---------Start--Removed-----------------" ); System.out.println("Name:" +scae.getName()+"Value:" +scae.getValue()); System.out.println(scae.getServletContext()); System.out.println("---------End--Removed-----------------" ); } @Override public void attributeReplaced (ServletContextAttributeEvent scae) { System.out.println("---------Start--Replaced-----------------" ); System.out.println("Name:" +scae.getName()+"Value:" +scae.getValue()); System.out.println(scae.getServletContext()); System.out.println("---------End--Replaced-----------------" ); } }
@WebServlet("/attr.do") public class ServletContextAttr extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this .getServletContext(); servletContext.setAttribute("key" ,"BJSXT" ); servletContext.setAttribute("key" ,"ITBZ" ); servletContext.removeAttribute("key" ); } }
<listener > <listener-class > com.bjsxt.listener.ServletContextAttrListener</listener-class > </listener >
18.3 HttpSession 监听器 18.3.1HttpSession 对象的生命周期监听器 HttpSessionListener 接口定义了 HttpSession 对象生命周期的监听行为。
void sessionCreated(HttpSessionEvent se)
HttpSession 对象创建后会触发该监听方法,并将已创建 HttpSession 对象传递到该方法中。
void sessionDestroyed(HttpSessionEvent se)
HttpSession 对象在销毁之前会触发该监听方法,并将要销毁的 HttpSession 对象传递到 该方法中
public class HttpSessionLifecycleListener implements HttpSessionListener { @Override public void sessionCreated (HttpSessionEvent se) { System.out.println("sessionCreated" ); System.out.println(se.getSession()); } @Override public void sessionDestroyed (HttpSessionEvent se) { System.out.println("sessionDestroyed" ); System.out.println(se.getSession()); } }
<listener > <listener-class > com.bjsxt.listener.HttpSessionLifecycleListener</listener-class > </listener >
18.3.2 HttpSession 对象的属性操作监听器 HttpSessionAttributeListener 接口定义了对于 HttpSession 对象属性操作的监听行为。
void attributeAdded(HttpSessionBindingEvent se)
向 HttpSession 对象中添加属性时会触发该监听方法,并将 HttpSession 对象传递到该方法中。触发事件的方法 HttpSession.setAttribute(“key”,”value”)。
void attributeRemoved(HttpSessionBindingEvent se)
当从 HttpSession 对象中删除属性时会触发该监听方法,并将 HttpSession 对象传递到该方法中。触发事件方法 HttpSession.removeAttribute(“key”)。
void attributeReplaced(HttpSessionBindingEvent se)
当从 HttpSession 对象中属性的值发生替换时会触发该监听方法,并将 HttpSession 对象传递到该方法中。触发事件的方法 HttpSession.setAttribute(“key”,”value”)。
@WebServlet("/attra.do") public class HttpSessionAttr extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); session.setAttribute("key" ,"BJSXT" ); session.setAttribute("key" ,"itbz" ); session.removeAttribute("key" ); } }
public class HttpSessionAttrListener implements HttpSessionAttributeListener { @Override public void attributeAdded (HttpSessionBindingEvent se) { System.out.println("--HttpSessionAttrListener------Start Added------" ); System.out.println("Name:" +se.getName()+"vallue:" +se.getValue()); System.out.println(se.getSession()); System.out.println("--HttpSessionAttrListener------End Added------" ); } @Override public void attributeRemoved (HttpSessionBindingEvent se) { System.out.println("--HttpSessionAttrListener------Start Removed------" ); System.out.println("Name:" +se.getName()+"vallue:" +se.getValue()); System.out.println(se.getSession()); System.out.println("--HttpSessionAttrListener------End Removed------" ); } @Override public void attributeReplaced (HttpSessionBindingEvent se) { System.out.println("--HttpSessionAttrListener------Start Replaced-----" ); System.out.println("Name:" +se.getName()+"vallue:" +se.getValue()); System.out.println(se.getSession()); System.out.println("--HttpSessionAttrListener------End Replaced------" ); } }
<listener > <listener-class > com.bjsxt.listener.HttpSessionAttrListener</listener-class > </listener >
18.4 HttpServletRequest 监听器 18.4.1 HttpServletRequest 对象的生命周期监听器 ServletRequestListener 接口定义了 ServletReqest(是 HttpServletRequest 接口的父接口类型)对象生命周期的监听行为。
void requestInitialized(ServletRequestEvent sre)
HttpServletRequest 对象创建后会触发该监听方法,并将已创建 ServletRequest 对象传递到该方法中。
void requestDestroyed(ServletRequestEvent sre)
HttpServletRequest 对象在销毁之前会触发该监听方法,并将要销毁的 ServletRequest 对 象传递到该方法中。
@WebListener public class HttpServletRequestLifecycleListener implements ServletRequestListener { @Override public void requestDestroyed (ServletRequestEvent sre) { System.out.println("Destroyed Request Started========" ); System.out.println(sre.getServletRequest()); System.out.println("Destroyed Request Ended========" ); } @Override public void requestInitialized (ServletRequestEvent sre) { System.out.println("Initialized Request Started========" ); System.out.println(sre.getServletRequest()); System.out.println("Initialized Request Ended========" ); } }
18.4.2 HttpServletRequest 对象的属性操作监听器 ServletRequestAttributeListener 接口定义了对于 HttpServletRequest 对象属性操作的监听行为。
void attributeAdded(ServletRequestAttributeEvent srae) 向 HttpServletRequest 对象中添加属性时会触发该监听方法,并将 ServletRequest 对象传递到该方法中。触发事件的方法HttpServletRequest.setAttribute(“key”,”value”)。
void attributeRemoved(ServletRequestAttributeEvent srae) 当从HttpServletRequest 对象中删除属性时会触发该监听方法,并将 ServletRequest 对象 传递到该方法中。触发事件方法 HttpServletRequest.removeAttribute(“key”)。
void attributeReplaced(ServletRequestAttributeEvent srae) 当 从HttpServletRequest 对 象 中 属 性 的 值 发 生 替 换 时 会 触 发 该 监 听 方 法 , 并 将 ServletRequest 对象传递到该方法中。 触发事件的方法 HttpServletRequest.setAttribute(“key”,”value”)。
@WebServlet("/attraa.do") public class HttpServletRequestAttr extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("key" ,"BJSXT" ); req.setAttribute("key" ,"ITBZ" ); req.removeAttribute("key" ); } }
@WebListener public class HttpServletRequestAttrListener implements ServletRequestAttributeListener { @Override public void attributeAdded (ServletRequestAttributeEvent srae) { System.out.println("--HttpServletRequestAttrListener------Start Added-----" ); System.out.println("Name:" +srae.getName()+"vallue:" +srae.getValue()); System.out.println(srae.getServletRequest()); System.out.println("--HttpServletRequestAttrListener------End Added------" ); } @Override public void attributeRemoved (ServletRequestAttributeEvent srae) { System.out.println("--HttpServletRequestAttrListener------Start Removed-----" ); System.out.println("Name:" +srae.getName()+"vallue:" +srae.getValue()); System.out.println(srae.getServletRequest()); System.out.println("--HttpServletRequestAttrListener------Start Removed------" ); } @Override public void attributeReplaced (ServletRequestAttributeEvent srae) { System.out.println("--HttpServletRequestAttrListener------Start Replaced-----" ); System.out.println("Name:" +srae.getName()+"vallue:" +srae.getValue()); System.out.println(srae.getServletRequest()); System.out.println("--HttpServletRequestAttrListener------End Replaced------" ); } }
18.5 基于注解式开发监听器 Listener 支持注解式开发,通过@WebListener 注解替代 web.xml 中 Listener 的配置。
19 Filter与Listener设计模式 19.1 Filter 的设计模式 在 Servlet 的 Filter 中使用的责任链设计模式。
19.1.1 责任链模式特点 责任链(Chain of Responsibility):责任链模式也叫职责链模式,是一种对象行为模式 。在责任链模式里,很多对象由每一个对象对其下一个对象的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不需 要知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任
19.2.2 责任链的优缺点 优点:
降低了对象之间的耦合度。
增强了系统的可扩展性。
增强了给对象指派职责的灵活性。
责任链简化了对象之间的连接。
责任分担。每个类只需要处理自己该处理的工作。
缺点:
不能保证请求一定被接收。
对比较长的责任链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。 可能会由于责任链的错误设置而导致系统出错,如可能会造成循环调用。
19.2 Listener 的设计模式 在 Servlet 的 Listener 中使用的观察者设计模式
19.2.1 观测者模式的特点 观察者模式(Observer Pattern):观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
19.2.2 观察者模式的优缺点 优点
观察者和被观察者是抽象耦合的。
建立一套触发机制。
缺点
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知 到会花费很多时间。
如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环 调用,可能导致系统崩溃。
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而 仅仅只是知道观察目标发生了变化。
PROJECT-LINK: https://www.aliyundrive.com/s/1Utbx84RG7i 提取码: zh90