18_2Servlet学习笔记

           Servlet学习笔记

文章目录

1 Web开发简介

1.1 web开发介绍

Web,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源,即供浏览器访问的资源。

。Internet上供外界访问的Web资源分为:

​ 静态web资源(如html页面):指web页面中供人们浏览的数据始终是不变。

​ 动态web资源:指web页面中供人们浏览的数据是由程序产生的,不同时间点访问web页面看到的内容各不相同。

。静态web资源开发技术

​ html

。常用动态web资源开发技术

​ JSP/Servlet、PHP、asp.net等 (Linux+tomcat+MySQL+jsp+servlet)

​ 在Java中,动态web资源开发技术统称为Javaweb,

​ 我们课程的重点也是教大家如何使用Java技术开发动态的web资源,即动态web页面。

1.2 动态网页技术的发展

​ 当www网初见江湖时,当时主要是html,由于html只支持静态的文字和图片,不能与用户进行交互,为了弥补这个不足,

陆续出现了下列网页技术:

​ (1) asp

​ 微软的产品

​ (2) php

​ (3) jsp

​ jsp = html+java片段+jsp语法+javascript

​ jsp优势:

​ 1.一次编写,到处运行

​ 2.良好的跨平台性 (linux或windows都行)

​ 3.多种开发工具支持

​ 4.强大的可伸缩性(jsp+javabean)的方式

​ jsp的不足:

​ 1.jsp产品的复杂度高

​ 2.jsp要求运行的机器配置要高,

​ 因为jsp是用class常驻内存的方式运行的,效率高,但是需要占用更多的内存

1.3 B/S与C/S的介绍

B/S: browser/server

C/S: Client/Sesver

image-20220131154411947

image-20220131155209672

B/S的优势:

​ 1.开发成本低

​ 2.管理维护简单

​ 3.产品升级便利

​ 4.对用户的培训费用低

​ 5.用户使用方便,出现故障的概率小

B/S的不足:

​ 1.安全性不足 (别人能看到html代码)

​ 2.客户端不能随心变化,受浏览器的限制

2 Servlet简介

2.1 为什么会出现servlet?

需求:请你用现有的Java技术,开发一个动态网页,比如:可以让用户留言,其他人可以回复。

​ 用Java实现不了

解决方案:servlet 2000年 大行其道

​ sun公司就开发了servlet供开发人员使用。

2.2 什么是servlet

Servlet技术是在Java EE出现之前就存在了,在开发动态网页中,得到了广泛的应用,直到现在的Java EE项目中,

​ 也是非常重要的,同时jsp也是在servlet基础上发展起来的。因此,掌握好servlet太重要了。

​ Servlet(java服务器小程序)是用Java编写的服务器程序,它的特点:

​ 1.他是由服务器端(tomcat)调用和执行的

​ 2.他是用Java语言编写的 Servlet其实就是Java程序(Java类)

​ 3.他是按照Servlet规范开发的 该Java类要遵循Servlet开发规范

​ 4.功能强大,可以完成几乎所有的网站功能

​ 5.是学习JSP的基础

2.3 Servlet开发工具

1.常用的开发工具

普通的文本编辑器:notePad++,editplus

集成开发工具:jcreateor,jbuilder,eclipse,Idea

2.常用的web服务器

浏览器Web服务器数据库
所有浏览器Tomcat Weblogic Jboss WebsphereSQL2000 Oracle MySQL

2.4 tomcat和servlet在网络中的位置

image-20220201221747284

3 IDEA新建Servlet项目

IDEA新建Servlet项目步骤:

3.1 建立web工程

​ 1.1 点击File–>New–>Project…

image-20220202173553039

​ 1.2 在弹出的界面中,选择“Java Enterprise”,选择JDK版本,选择Tomcat版本,勾选“Web Application”,点击“Next”

image-20220202173927740

​ 1.3 在接下来的界面中,填写项目名称,选择项目存放的路径,点击“Finish”

image-20220202174225650

​ 1.4 生成的jsp_servlet_project项目结构

image-20220202174432613

3.2 在src目录下新建包

​ 在src目录下新建包com.tangguanlin

image-20220202174615201

3.3 开发一个Servlet

package com.tangguanlin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 说明:第一个servlet工程
 * 作者:汤观林
 * 日期:2022年02月02日 17时
 */
public class MyHttpServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("hello,servlet");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

3.4 在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>MyHttpServlet</servlet-name>
        <servlet-class>com.tangguanlin.MyHttpServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>MyHttpServlet</servlet-name>
        <url-pattern>/MyHttpServlet</url-pattern>
    </servlet-mapping>

</web-app>

3.5 部署到tomcat

5.1 选择tomcat版本

image-20220202175928841

5.2 添加要部署的项目,填写发布后的访问路径名jsp_servlet_project

image-20220202180450099

5.3 点击"运行"图标,运行项目

image-20220202180838849

3.6 测试项目

浏览器输入:http://localhost:8080/jsp_servlet_project/MyHttpServlet

image-20220202181026691

3.7 扩展:发布到webapp目录下

1.点击“Project Structure”,选择左侧的Artifacts,输入部署后的项目名称,输出文件夹选择Tomcat的webapp目录,

​ 注意:webapp后面要加上项目名称

image-20220202182837915

2.部署后,在webapp目录下,生成了jsp_servlet_project项目

image-20220202183352705

3.8 IDEA发布后不在webapp目录

​ IDEA发布后,为什么不在webapp目录下

问题:将 tomcat 集成到IDEA中,并且在 tomcat 中部署了 web 项目,在IDEA中运行 tomcat,

​ 但发现IDEA使用 tomcat 部署的项目在 tomcat 安装目录下的 webapps 中无法找到,那么它到底在哪里呢?

结论:IDEA 使用 tomcat 部署项目后不会把编译后的项目复制到 tomcat 的 webapps 目录下,

​ 而是复制一份配置文件到指定目录下,让 tomcat 找到这个项目,

​ 也就是说每个项目都有属于自己的一份 tomcat 配置,互不干扰。

在IDEA安装目录下,

在C:\Users\Administrator.IntelliJIdea2019.3\system\tomcat目录下:

image-20220202183730287

在C:\Users\Administrator.IntelliJIdea2019.3\system\tomcat\Tomcat_8_0_12_jsp_servlet_project_5\conf\Catalina\localhost目录下:

image-20220202183900618

映射文件:jsp_servlet_project.xml

<Context path="/jsp_servlet_project"      
                 docBase="D:\05project\jsp_servlet_project\out\artifacts\jsp_servlet_project_war_exploded" />

其中,path 指虚拟目录名称;docBase 指IDEA使用 tomcat 部署项目的路径。

因此,整个项目的运行过程:

​ IDEA 复制一份 tomcat 的配置文件到指定目录中,

​ 之后启动 tomcat 安装目录下的 catalina.bat 文件,

​ tomcat读取配置文件,找到项目位置,然后就运行起来了。

特别说明:jsp_servlet_project.xml这个配置文件是IDEA的,不是Tomcat的,只有Idea才认识。

相当于是IDEA内部的一个映射转移,

如果关闭IDEA,想要项目在Tomcat中运行,

需要将项目拷贝到Webapp目录下,启动Tomcat,才能访问。

4 Http协议

4.1 什么是HTTP协议

1.HTTP协议:HyperText Transfer Protocol 超文本传输协议,是互联网上应用最为广泛的一种网络协议。

​ 是工作在TCP/IP协议基础之上的,所有的www文件都必须遵守这个标准。

​ 设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。

2.通过httpwatch插件来抓取http请求内容

3.http1.0短连接 http1.1 长连接 所谓长和短,是指持续时间的长度

​ http1.1 持续时间30秒 短连接是发完数据就断掉

4.http是TCP/IP协议的一个应用协议,http也是我们web开发的基础

4.2 http请求

4.2.1 http请求介绍

客户端连上服务器后,向服务器请求某个web资源,称之为客户端向服务器发送了一个http请求。

一个完整的http请求包含了如下内容:

​ 一个请求行,若干消息头,以及实体内容。

​ 其中消息头和实体内容都是可选的,消息头和实体内容之间要用空行隔开。

如下所示:

GET /test/hello.html HTTP/1.1    请求行
Accept: */*
Referer: http://localhost:8080/test/abc.html
Accept-Language:zh-cn
User-Agent:Mozilla/4.0
Accept-Encoding:gzip,deflate
Host:localhost:8080
Connection:Keep-Alive     消息头
                          空格
                          消息内容

image-20220203143148002

消息头格式 消息名:内容

特别说明:消息头并不是每一次请求内容都是一样的。有时候多,有时候少

如果Java发起的http请求,Referer是空的。可以防机器人不停的请求。

4.2.2 请求行

。请求行中的get称之为请求方式,请求方式有:

​ GET,POST,HEAD,PUT,DELETE,TRACE,OPTIONS

​ 常用的有GET,POST

。get和post区别是程序员常常讨论的问题,总结区别如下:

​ 2.get提交,请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),

​ 以?分隔URL和传输数据,多个参数用&连接,例如:

login.action?name=abc&password=kkk

​ post提交:把提交的数据放置在http包的包体中,比如:

POST /servlet/ParamsServlet HTTP/1.1
Host:
Content-Type:application/x-www-form-urlencoded
Content-Length:28

name=abc&password=xyz     消息内容

因此,get提交的数据会在地址栏中显示出来,而post提交,地址栏不会改变

​ 2.传输数据的大小:

​ 首先声明:http协议没有对传输的数据大小进行限制,http协议规范也没有对URL长度进行限制。

​ 而在实际开发中存在的限制主要有;

​ get: 特定浏览器和服务器对URL长度有限制。例如IE对URL长度的限制是2083字节(2K+35)。

​ 对于其他浏览器,如FireFox,理论上没有长度限制,其限制取决于操作系统的支持。

​ 因此get提交时,传输数据就会受到URL长度的限制。

​ post: 由于不是通过URL传输,理论上数据不受限。

​ 3.安全性:相对而言,post提交,安全高

4.2.3 请求消息头

#告诉服务器,我可以接收文本,网页,图片
Accept: text/html,application/xml;q=0.9,image/avif,image/webp,image/apng
#接收字符编码:ISO-8859-1
Accept-Charset:ISO-8859-1
#可以接收gzip 压缩后的数据
Accept-Encoding: gzip, deflate, br
#浏览器支持的语言:中文 英文
Accept-Language: zh-CN,zh;q=0.9
#保存连接,发完数据不关闭连接
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
#我要找的主机是谁
Host: localhost:8080
Origin: http://localhost:8080
#告诉服务器,我来自哪里 常用于防止盗链 Referer为空,代表不是本网站过来的,只有本网站请求才有值
Referer: http://localhost:8080/UserManager/loginJsp
#告诉服务器,我的浏览器的内核
User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Mobile Safari/537.36
#cookie
Cookie:

Java中获取消息头信息:

String host = req.getHeader("Host"); //localhost:8080
out.println("host:"+host);
String UserAgent = req.getHeader("User-Agent"); //
out.println("User-Agent:"+UserAgent);

4.3 http响应

4.3.1 http响应介绍

一个http响应代表服务器向客户端回送的数据,它包括:

一个状态行、若干消息头、以及实体内容,其中的一些消息头和实体内容都是可选的,消息头和实体内容之间要用空格隔开。

举例:

image-20220203162542732

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html,charset=utf-8;charset=ISO-8859-1
Content-Length: 168
Date: Thu, 03 Feb 2022 07:59:17 GMT

4.3.2 状态行

​ 格式: http版本号 状态码 原因叙述

​ 比如: HTTP/1.1 200 ok

​ 状态码用于表示服务器对请求的处理结果,它是一个3位数的十进制数。

​ 响应状态码分为5类,如下所示:

状态码含义
100~199表示成功接收请求,要求客户端继续提交下一次请求才能完成整个处理过程
200~299 200表示成功接收请求并已完成整个处理过程,常用200
300~399为完成请求,客户需进一步细化请求。例如,请求的资源已经移动一个新地址,常用302、307
400~499 404客户端的请求有错误,常用404
500~599 500服务器出现错误,常用500

Java端改写http响应信息:

//重定向
resp.setStatus(302);
resp.setHeader("Location","http://localhost:8080/jsp_servlet_project/MyHttpServlet");
//等价
resp.sendRedirect("/jsp_servlet_project/MyHttpServlet");

4.3.3 响应消息头

HTTP/1.1 302 Found
#告诉浏览器,我是Tomcat类型
Server: Apache-tomcat/1.1
#让浏览器重新定位到url去
Location: http://localhost:8080/jsp_servlet_project/MyHttpServlet
#内容的格式 text/html 编码是ISO-8859-1
Content-Type: text/html,charset=utf-8;charset=ISO-8859-1
#告诉浏览器,我使用了gzip
Content-Encoding:gzip
#告诉浏览器,回送的数据大小80字节
Content-Length: 0
#支持中文
Content-Language:zh-cn
#过多久,刷新到百度
Refresh:1;url=http://www.baidu.com
#告诉浏览器,有文件需要下载
Content-Disposition:attachment;filename=aaa.zip
#cookie信息
Set-Cookie:SS=Q0=5Lb_nQ;path=/search
#指定不要缓存
Expires:-1
#指定不要缓存
Cache-Control:no-cache
#指定不要缓存
Pragma:no-cache
#保存连接,发完数据不关闭连接
Connection: keep-alive
#返回送时间
Date: Thu, 03 Feb 2022 08:57:11 GMT
//定时刷新网页
resp.setHeader("Refresh","5;url=http://www.baidu.com");

4.3.4 禁止缓存

提出问题:

​ 我们的浏览器在默认的情况下,会缓存我们的页面,这样就会出现一个小问题:

​ 如果我们的用户习惯把光标停在地址栏,然后回车去取页面,就会默认从cache中取数据。

image-20220203184159351

第1次:

image-20220203190141071

第2次:

image-20220203190141071

(1)股票,基金系统,有些系统对及时性要求很高,要求我们不缓存页面?

package com.tangguanlin;
import javax.servlet.ServletException;
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.Date;
/**
 * 说明: 页面不缓存
 * 作者:汤观林
 * 日期:2022年02月03日 18时
 */
public class NoCacheServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        resp.setDateHeader("Expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");

        PrintWriter out = resp.getWriter();
        out.println("hello world"+new Date());

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

运行结果:

image-20220203190824726

(2)有些系统要求网页缓存一定时间,比如缓存一个小时

//指定缓存时间
resp.setDateHeader("Expires",System.currentTimeMillis()+3600*1000);

4.3.5 通用信息头

通用信息头指既能用于请求,又能用于响应的一些消息头。

#保存连接,发完数据不关闭连接
Connection: keep-alive
#返回送时间
Date: Thu, 03 Feb 2022 08:57:11 GMT

5 Servlet

5.1 开发Servlet有三种方法

a.实现servlet接口

b.继承GenericServlet

c.继承HttpServlet

使用eclipse开发servlet非常容易,但是,它隐藏了太多的细节。
为了让大家可以学到Servlet的底层机制和运行原理,我们先用记事本开发,
然后使用eclipse开发。
#补充:如果使用javac去编译一个java文件,则需要带命令参数
javac -d . MyFirstServlet.java

5.2 实现servlet接口

实现servlet接口来编写一个servlet

​ 介绍servlet的生命周期,该servlet完成一个非常简单的功能,向浏览器回送hello,world!

步骤:

1.建立一个web应用hspWeb1

2.在hspWeb1下建立 WEB-INF/web.xml(可以从别的地方拷贝)

3.在hspWeb1下建立 WEB-INF/classes,我们的Servlet就要在这个目录开发

4.开发MyFirstServlet.java

package com.tangguanlin;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyFirstServlet implements Servlet{
	
	//得到ServletConfig对象
	public ServletConfig getServletConfig(){
	
         return null;	
	}
	
	//该函数用于初始化servlet,就是把该servlet装载到内存中
	//该函数只会调用一次
	public void init(ServletConfig config){
		
	}
	
	//该函数是服务函数
	//我们的业务逻辑代码就是写在这里
	//该函数每次都会调用
	public void service(ServletRequest req,ServletResponse res) throws ServletException,java.io.IOException{
		System.out.println("hello,world");
		res.getWriter().println("hello world");  //向页面输出hello world
	}
	
	//该函数是得到servlet配置信息
	public String getServletInfo(){
		
		return null;
	}
	
	//销毁该Servlet,从内存中清除
	//该函数只会调用一次
	public void destroy(){
		
	}
}

5.根据Servlet规范,我们还需要在web.xml中部署Servlet

web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<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_3_1.xsd"
  version="3.1"
  metadata-complete="true">

     <!--根据servlet规范,需要将servlet部署到web.xml文件-->
	 <servlet>  
	    <!--servlet名称 是该servlet取个名字,名字可以自定义,默认就使用该servlet的名字-->
		<servlet-name>MyFirstServlet</servlet-name>  
		<!--指明该servlet放在哪个包下-->
		<servlet-class>com.tangguanlin.MyFirstServlet</servlet-class>  
	</servlet> 
	
	<!--servlet的映射-->
	<servlet-mapping>  
	     <!--这个servlet的名字,要和上面的servlet名字一样-->
		<servlet-name>MyFirstServlet</servlet-name>  
		<!--是将来访问servlet的请求路径,默认命名规范:就是该Servlet的名字-->
		<url-pattern>/MyFirstServlet</url-pattern>  
	</servlet-mapping>
    
</web-app>

6.测试

http://localhost:8080/hspWeb1/MyFirstServlet

image-20220202011610046

5.3 实现GenericServlet接口

​ 实现GenericServlet接口,了解即可。

5.4 继承HttpServlet

使用继承HttpServlet的方法开发Servlet

​ 1.在软件公司,90%是通过该方法开发

​ 2.举例说明,还是显示 hello world

步骤:

1.建立一个web应用hspWeb1

2.在hspWeb1下建立 WEB-INF/web.xml(可以从别的地方拷贝)

3.在hspWeb1下建立 WEB-INF/classes,我们的Servlet就要在这个目录开发

4.开发MyHttpServlet.java

package com.tangguanlin;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class MyHttpServlet extends HttpServlet{
	
	//在HttpServlet中,设计者对get提交和post提交 分别处理
	//其实,doGet和doPost底层都是调用了Servlet的service()方法
	protected void doGet(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException{
		res.getWriter().println("I am httpServlet doGet");
	}
	
	protected void doPost(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException{
		res.getWriter().println("I am httpServlet doPost");
	}
}

5.根据Servlet规范,我们还需要在web.xml中部署Servlet

web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<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_3_1.xsd"
  version="3.1"
  metadata-complete="true">

	 <servlet>  
		<servlet-name>MyHttpServlet</servlet-name>  
		<servlet-class>com.tangguanlin.MyHttpServlet</servlet-class>  
	</servlet> 
	<servlet-mapping>  
		<servlet-name>MyHttpServlet</servlet-name>  
		<url-pattern>/MyHttpServlet</url-pattern>  
	</servlet-mapping>
</web-app>

6.测试

http://localhost:8080/hspWeb1/MyHttpServlet

image-20220202080519281

get提交和post提交的区别:

​ 1.从安全性看,get<post,get提交的数据会在浏览器的地址栏显示

​ 2.从提交的内容大小,get<post,get提交的数据不能大于2K,而post提交的数据理论上不受限制,但是实际编程中建议不要大于64K。

​ 3.从请求响应速度看,get>post,get要求服务器立即响应,而post请求可能形成一个队列请求。

5.5 Servlet生命周期

Servlet的生命周期是怎样的?

Servlet究竟是怎样工作的?

FastStoneEditor3

Servlet运行原理:

​ 1.当Servlet第一次被调用的时候,会触发init函数,该函数会把Servlet实例装载到内存,init函数只会被调用一次

​ 2.然后去调用Servlet的service函数

​ 3.当第二次后访问该Servlet,就直接调用service函数

​ 4.当web应用reload或者关闭tomcat,或者关机,都会去调用destroy函数,该函数就会去销毁Servlet。

Servlet运行过程:

Servlet程序是由Web服务器调用,web服务器收到客户端的Servlet访问请求后:

​ 1.web服务器首先检查是否已经装载并创建了该Servlet的实例对象。

​ 如果是则直接执行第4步,否则,执行第2步

​ 2.装载并创建该Servlet的一个实例对象

​ 3.调用Servlet实例对象的init()方法

​ 4.创建一个用于封装http请求消息的HttpServletRequest对象和一个代表http响应消息的HTTPServletResponse对象,

​ 然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。

​ 5.web应用程序被停止或者重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

​ 以下情况会导致destroy()方法调用:

​ (1)tomcat重新启动 (2)reload该Webapp (3)重新启动电脑

5.6 Servlet细节

一般开发人员习惯把doGet()和doPost合二为一

。HttpServlet指能够处理http请求的servlet,它在原有Servlet接口上添加了一些与http协议处理方法,

​ 它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,

​ 而避免直接去实现Servlet接口。

。HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,

​ 如为Get请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。

​ 因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。

Servlet细节:

1.由于客户端是通过URL地址访问web服务器中的资源,

所以Servlet程序若想被外界访问,必须把Servlet程序映射到一个URL地址上,

这个工作在web.xml文件中使用元素和元素完成。

2.元素用于注册Servlet,它包含有两个主要的子元素:和,

分别用于设置Servlet的注册名称和Servlet的完整类名。

3.一个元素用于映射一个已注册的Servlet的一个对外访问路径。

​ 它包含有两个子元素:和,分别用于指定Servlet的注册名称和Servlet的对外访问路径。

​ 例如:

<web-app>
    <servlet>
          <!--servlet的注册名-->
        <servlet-name>MyHttpServlet</servlet-name>
          <!--servlet类的全路径-->
        <servlet-class>com.tangguanlin.MyHttpServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <!--servlet的注册名-->
        <servlet-name>MyHttpServlet</servlet-name>
        <!--servlet的访问路径-->
        <url-pattern>/MyHttpServlet</url-pattern>
    </servlet-mapping>
</web-app>

4.当映射一个servlet的时候,可以多层,比如

​ /servlet/index.html

从这里可以看出,后缀名是html不一定就是html。

5.一个已经注册的servlet,可以被映射到多个URL上,

​ 即多个元素的子元素的设置值可以是同一个Servlet的注册名。

6.在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:

​ 一种格式是 *.扩展名

<url-pattern>*.do</url-pattern>

​ 另一种格式是以正斜扛(/)开头并以“/*”结尾

比如 /* /news/*

<url-pattern>/news/*</url-pattern>

在匹配的时候,要参考的标准:

​ (1) 看谁的匹配度高,谁就被选择

​ (2) *.do的优先级最低

7.Servlet是一个供其他Java程序(Servlet引擎,其实就是Web服务器Tomcat)调用的Java类,它不能独立运行,

​ 它的运行完全由Servlet引擎来控制和调度。

8.Servlet单例问题

​ 针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例,也就是说Servlet实现对象一旦创建,它就会

​ 驻留在内存中,为后续的其他请求服务,直至web容器退出/或者reload该web应用,Servlet实例对象才会销毁。

​ 当Servlet被第一次访问后,就被加载到内存,以后该实例对各个请求服务,即Servlet在使用中是单例。

​ 因为Servlet是单例,因此会出现线程安全问题。

​ 比如售票系统,如果不加同步机制,则会出现问题。

​ 原则:

​ (1)如果一个变量需要多个用户共享,则应作为Servlet的成员变量,在访问该变量的时候,要加同步机制,才不会出现线程安全问题。

​ 全局变量 + 加锁

synchronized (this){
    //业务逻辑
}

​ (2)如果一个变量不需要共享,则直接在doGet()或者doPost()定义。

​ 局部变量

9.在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次Servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,

然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。

10.如果元素中配置了一个元素,

​ 那么web应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。

​ 用途:为web应用写一个InitServlet,这个Servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。

​ 或者启动一个后台线程,定时去完成某些工作(比如每个10秒发送一封电子邮件)。

​ 案例:定时发送电子邮件功能

web.xml

<servlet>
    <servlet-name>MyInitServlet</servlet-name>
    <servlet-class>com.tangguanlin.MyInitServlet</servlet-class>
    <!--1表示Servlet被启动的顺序-->
    <load-on-startup>1</load-on-startup>
</servlet>

MyInitServlet.java

package com.tangguanlin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
/**
 * 说明:<load-on-startup>的使用
 * 作者:汤观林
 * 日期:2022年02月02日 23时
 */
public class MyInitServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        System.out.println("MyInitServlet init被调用");
        //完成一些初始化任务
        System.out.println("创建数据库,表,读取参数");
        SendMailThread sendMailThread = new SendMailThread();
        sendMailThread.run();
    }
}

SendMailThread.java 线程类

package com.tangguanlin;
import java.util.Date;
/**
 * 说明:用线程实现定时任务
 * 作者:汤观林
 * 日期:2022年02月02日 23时
 */
public class SendMailThread extends Thread {
    @Override
    public void run() {
        while(true){
            try {
                System.out.println("每分钟执行一次"+new Date());
                Thread.sleep(60*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:

MyInitServlet init被调用
创建数据库,表,读取参数
每分钟执行一次Thu Feb 03 00:21:54 CST 2022
每分钟执行一次Thu Feb 03 00:22:54 CST 2022

6 HttpServletRequest

Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象和代表响应的response对象。

request对象和response对象即代表请求和响应,

​ 那我们要获取客户端提交过来的数据,只需要找request对象就行了,

​ 要向客户端输出数据,只需要找response对象就行了。

httpServletRequest对象代表客户端的请求,当客户端通过http协议访问服务器时,

http请求头中的所有信息都封装在这个对象中,

开发人员通过这个对象的方法,可以获得客户这些信息。

该对象表示浏览器的请求(http请求),当web服务器得到该请求后,会把请求信息封装成一个httpServletRequest对象。

6.1 获得客户机信息

getRequestURL();  //方法返回客户端发出请求时的完整URL
getRequestURI();  //方法返回请求行中的资源名部分
getQueryString(); //方法返回请求行汇总的参数部分(参数名+值)  获取get方式提交的参数

getRemoteAddr();  //方法返回发出请求的客户端的IP地址
getRemoteHost();  //方法返回发出请求的客户端的完整主机名  要在dns注册了才能返回主机名,没有注册就返回IP地址
getRemotePort();  //方法返回客户端所使用的网络端口号

getLocalPort();   //方法返回web服务器所使用的网络端口号
getLocalName();   //方法返回web服务器的主机名
getLocalAddr();   //方法返回web服务器的IP地址

代码:

String url = request.getRequestURL().toString();
System.out.println("url="+url);
String uri = request.getRequestURI();
System.out.println("uri="+uri);
String queryString = request.getQueryString();
System.out.println("queryString="+queryString);

String clientAddr = request.getRemoteAddr();
System.out.println("clientAddr="+clientAddr);
String clientHost = request.getRemoteHost();
System.out.println("clientHost="+clientHost);
int clientPort = request.getRemotePort();
System.out.println("clientPort="+clientPort);

String localAddr = request.getLocalAddr();
System.out.println("localAddr="+localAddr);
String localName = request.getLocalName();
System.out.println("localName="+localName);
int localPort = request.getLocalPort();
System.out.println("localPort="+localPort);

http请求:http://localhost:8080/jsp_servlet_project/myHttpServletRequest?username=tangguanlin&password=123

运行结果:

url=http://localhost:8080/jsp_servlet_project/myHttpServletRequest
uri=/jsp_servlet_project/myHttpServletRequest
queryString=username=tangguanlin&password=123

clientAddr=127.0.0.1
clientHost=127.0.0.1
clientPort=56023

localAddr=127.0.0.1
localName=www.tangguanlin.com
localPort=8080

6.2 getMethod()

获取http请求方式,比如get请求,post请求

String httpMethod = request.getMethod();

6.3 getHeader()

getHeader()获取请求头的内容

GET /test/hello.html HTTP/1.1    请求行
Accept: */*
Referer: http://localhost:8080/test/abc.html
Accept-Language:zh-cn
User-Agent:Mozilla/4.0
Accept-Encoding:gzip,deflate
Host:localhost:8080

获取请求头信息:

String host = req.getHeader("Host");
String accept = req.getHeader("Accept");
String referer = req.getHeader("Referer");

6.4 getHeaderNames()

把整个http请求头的消息全部获取

//得到所有的消息头信息
Enumeration<String> headerNames = req.getHeaderNames();
while(headerNames.hasMoreElements()){
    //取出消息头的名字
    String headerName = headerNames.nextElement();
    System.out.println(headerName+"="+request.getHeader(headerName));
}

运行结果:

host=localhost:8080
connection=keep-alive
upgrade-insecure-requests=1
user-agent=Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36
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
referer=http://localhost:8080/jsp_servlet_project/myform.html
accept-encoding=gzip, deflate, br
accept-language=zh-CN,zh;q=0.9
cookie=JSESSIONID=15C0191BBC468655F6E9CDF8DED11071

6.5 getParameter(String)

获取客户端请求参数,客户端提交的数据 和http协议有关的

String username = request.getParameter("username");
System.out.println(username);
        
String password = request.getParameter("password");
System.out.println(password);

6.6 getParameterNames()

获取客户端所有请求参数,客户端提交的所有数据

//取出所有的字段数据
Enumeration<String> parameterNames = request.getParameterNames();
while(parameterNames.hasMoreElements()){
    String nextElement = parameterNames.nextElement();
    String value = request.getParameter(nextElement);
    System.out.println(nextElement+"="+value);
}

6.7 getParameterValues(String)

如果接收复选框的内容,则使用getParameterValues,得到是数组数据,多个选项数据

<form>
    爱好(复选框):
    <input type="checkbox" name="hobby" value="音乐">音乐
    <input type="checkbox" name="hobby" value="体育">体育
    <input type="checkbox" name="hobby" value="唱歌">唱歌
    <input type="checkbox" name="hobby" value="爬山">爬山
    <input type="checkbox" name="hobby" value="旅游">旅游    
    所在城市(下拉列表):
      <select name="city">
         <option value="bj">北京</option>
         <option value="sh">上海</option>
         <option value="tj">天津</option>
         <option value="gz">广州</option>
    </select>
</form>
//如果接收复选框的内容,则使用getParameterValues,得到是数组数据
String[] hobbies = request.getParameterValues("hobby");
for(String hobby:hobbies){
    System.out.println(hobby);
}

6.8 setAttribute(key,value)

把数据设置到request域对象的属性中

request.setAttribute("useranme","韩顺平");

6.9 getAttribute(key)

获取request域对象的属性值,获取和http协议无关的值

String username = (String)request.getAttribute("username");

6.10 getServletContext()

获取ServletContext对象

ServletContext servletContext = request.getServletContext();

6.11 getContextPath()

获取项目资源路径

String contextPath = request.getContextPath();
System.out.println(contextPath);  

运行结果:

/jsp_servlet_project

6.12 getRequestDispatcher()

通过request对象实现请求转发功能

request.setAttribute("useranme","韩顺平");
request.getRequestDispatcher("/myHttpServlet").forward(request,resp);

6.13 getSession()

获取session对象

HttpSession session = request.getSession();

6.14 getCookies()

获取客户端所有的cookie对象

Cookie[] cookies = request.getCookies();

7 HttpServletResponse

HttpServletResponse对象服务器的响应。

​ 这个对象中封装了向客户端发送数据,发送响应头,发送响应状态码的方法。

​ Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,

​ Servlet引擎将这些数据当做响应消息的正文,当然再与响应状态行和各响应头组合后输出到客户端。

​ Servlet的service方法结束后,

​ Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用close方法,

​ 如果没有,Servlet引擎将调用close方法关闭该输出流对象。

​ 对于发送数据,服务器按照response.setCharacterEncoding—contentType—pageEncoding的优先顺序,对要发送的数据进行编码。

7.1 getWriter()

回送文本,回送字符数据

如果我们要回送字符数据,则使用PrintWriter对象,效率高

PrintWriter和outputStream两个流不能同时使用

PrintWriter out = response.getWriter();
out.println("hello world");

7.2 getOutputStream()

回送二进制数据,可以回送字节数据,也可以回送字符数据

如果我们是要回送字节数据,则只能只用OutputStream。

PrintWriter和outputStream两个流不能同时使用

ServletOutputStream outputStream = response.getOutputStream();
outputStream.write("hello world2".getBytes());

7.3 sendRedirect(String url)

请求重定向:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源。

通过该方法将信息传送给下一个页面。

比如:sendRedirect(“welcome?username=shunping”);的形式

 response.sendRedirect("/UserManager/mainJsp?username="+username);

优点:传送信息的速度比较快

缺点:它只能传送字符串,而不能传送一个对象

​ 如果要传对象,可以将对象放入session中,在下一个servlet中将对象从session中取出来即可。

注意点:

​ 1.welcome代表你要跳转的那个servlet的url

​ 2.servlet url名和变量之间有?

​ 3.如要传递两个以上的值,它们间要用&号分开:比如

response.sendRedirect("welcome?username=shunping&password=ok");

​ 4.如果传递的是中文,那你将得到乱码,需要处理一下

7.4 setHeader(String,String)

发送http头,控制浏览器禁止缓存当前文档内容

通过HttpServletResponse回送的http头,可以控制浏览器的行为

//禁止缓存
response.setDateHeader("Expires",-1);
//resp.setDateHeader("Expires",System.currentTimeMillis()+3600*1000);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
//刷新
resp.setHeader("Refresh","5;url=http://www.baidu.com");
//重定向
response.setHeader("Location","http://localhost:8080/jsp_servlet_project/MyHttpServlet");
//文件下载
resp.setHeader("Content-Disposition","attachment;filename=hourse.jpg");

7.5 setContentType(String)

设置响应给客户端的文件格式

response.setContentType(“text/html;charset=utf-8”)的作用是指定服务器响应给浏览器的编码,

同时,浏览器也是根据这个参数来对其接收到的数据进行重新编码(或者称为解码)

text/html           HTML
text/plain          TXT
text/xml            XML
application/json    json字符串

text/html的意思是将文件的content-type设置为text/html的形式,

​ 浏览器在获取到这种文件时会自动调用html的解析器对文件进行相应的处理
text/plain的意思是将文件设置为纯文本的形式,浏览器在获取到这种文件时并不会对其进行处理

response.setContentType("text/html;charset=utf-8");
response.setContentType("text/plain;charset=utf-8");
response.setContentType("text/xml;charset=utf-8");
response.setContentType("application/json; charset=GBK");

7.6 setCharacterEncoding(String)

response.setCharacterEncoding(“UTF-8”)的作用是指定服务器响应给浏览器的编码。

response.setCharacterEncoding(“UTF-8)

8 请求转发和重定向

8.1 请求转发forword

​ 一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理,称之为请求转发。

HTTPServletRequest对象(也叫Request对象)提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,

调用这个对象的forward方法可以实现请求转发。

​ request对象同时也是一个域对象,开发人员通过request对象在实现转发时,把数据通过request对象带给其他web资源处理。

request.setAttribute("useranme","韩顺平");
request.getRequestDispatcher("/myHttpServlet").forward(request,resp);

请求转发request对象传数据原理图:

image-20220204190357185

请求转发原理分析:

image-20220204190543644

8.2 请求重定向sendRedirect

​ 一个web资源收到客户端请求后,通知浏览器去访问另外一个web资源,称之为请求重定向。

 response.sendRedirect("/UserManager/loginJsp");

8.3 请求转发和请求重定向的区别

总结:

1.使用forward不能转发到该web应用外的url

2.因为forward是发生在web服务器,所有servlet1和servlet2使用的是同一个request和response

3.使用sendRedirect()方法不能通过request.setAttribute()把属性传递给下一个Servlet

什么是一次http请求?

只要没有停止,也没有回到浏览器重定向,就算一次。比如:

image-20220204191809892

​ 如果转发多次,浏览器的地址栏保留的是第一次转发的那个Servlet的URL

sendRedirect()和forward的区别是什么?

1.叫法不一样 sendRedirect叫请求重定向,forward叫请求转发

2.实际发生的位置不一样

​ sendRedirect发生在浏览器

​ forword发生在web服务器

3.用法不一样

​ request.getRequestDispatcher("/myHttpServlet").forward(request,resp);

​ response.sndRedirect("/web应用/资源URI");

4.能够去URL范围不一样

sendRedirect可以去任何URL,如果要跳转到本web应用外的url,就要用sendRedirect

forward只能去当前的web应用的资源

9 ServletConfig

1.在Servlet的配置文件中,可以使用一个或多个标签为servlet配置一些初始化参数

2.当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,

​ 并在调用Servlet的init方法时,将ServletConfig对象传递给Servlet。

​ 进而,程序员通过ServletConfig对象就可以得到当前Servlet的初始化参数信息。

设置:

<!--这里配置的参数,可以被所有servlet读取-->
<context-param>
	<param-name>username</param-name>
	<param-value>root</param-value>
</context-param>
<servlet>
    <servlet-name>MyServletConfig</servlet-name>
    <servlet-class>com.tangguanlin.MyServletConfig</servlet-class>
    <!--这里可以给servlet配置信息,这里配置的信息,只能被该servlet读取-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</servlet>

读取:

//获取设置的单个Servlet参数
String encoding = this.getServletConfig().getInitParameter("encoding");
System.out.println(encoding);

//获取设置的全局参数 ServletContext
String username = (String) this.getServletConfig().getServletContext().getInitParameter("username");
System.out.println(username);

代码:

web.xml

<web-app>
    <!--这里配置的参数,可以被所有servlet读取-->
    <context-param>
        <param-name>username</param-name>
        <param-value>root</param-value>
    </context-param>

    <servlet>
        <servlet-name>MyServletConfig</servlet-name>
        <servlet-class>com.tangguanlin.MyServletConfig</servlet-class>
        <!--这里可以给servlet配置信息,这里配置的信息,只能被该servlet读取-->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>MyServletConfig</servlet-name>
        <url-pattern>/MyServletConfig</url-pattern>
    </servlet-mapping>
</web-app>

MyServletConfig.java

package com.tangguanlin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 说明:ServletConfig读取配置属性
 * 作者:汤观林
 * 日期:2022年02月03日 00时
 */
public class MyServletConfig extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取设置的单个Servlet参数
        String encoding = this.getServletConfig().getInitParameter("encoding");
        System.out.println(encoding);

        //获取设置的全局参数
        String username = (String) this.getServletConfig().getServletContext().getInitParameter("username");
        System.out.println(username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req,resp);
    }
}

运行结果:

utf-8
root

10 ServletContext

10.1 为什么需要ServletContext

image-20220208001643451

10.2 解决之道ServletContext

什么是ServletContext:

要里面ServletContext就必须和cookie、Session做一个对比,如下图所示:

你可以把它想象成是一个共用的空间,可以被所有的客户访问:

​ 也就是说,A客户可以访问D,B客户也能访问D,C客户也能访问D。

image-20220208002904283

可以这么理解:session是属于单个的用户,但ServletContext每个用户都能访问,是一个公共区域。

ServletContext工作原理:

image-20220208011346394

10.3 this.getServletContext()

获取ServletContext

ServletContext servletContext = this.getServletContext();

10.4 servletConfig.getServletContext()

通过ServletConfig获取ServletContext

ServletConfig servletConfig = this.getServletConfig();
ServletContext servletContext = servletConfig.getServletContext();

10.5 request.getServletContext()

通过HttpServletRequest对象获取ServletContext

ServletContext servletContext = request.getServletContext();

10.6 session.getServletContext()

通过HttpSession获取ServletContext

HttpSession session = request.getSession();
ServletContext servletContext = session.getServletContext();

10.7 setAttribute(name,value)

将数据存入ServletContext中

servletContext.setAttribute("name","韩顺平");

10.8 getAttribute(name)

将数据从ServletContext中取出来

String name = (String)servletContext.getAttribute("name");

10.9 getRealPath(filepath)

获取文件的全路径(tomcat服务器下的全路径)

String realPath = servletContext.getRealPath("/images/hourse.jpg");

10.10 getResourceAsStream(filepath)

获取web目录下下的文件,src目录下的要用类加载器获取

InputStream inputStream = servletContext.getResourceAsStream("/db.properties");
Properties properties = new Properties();
properties.load(inputStream);
String username = properties.getProperty("username");

10.11 getInitParameter(name)

获取web.xml文件中配置的全局参数

web.xml

<!--这里配置的参数,可以被所有servlet读取-->
<context-param>
    <param-name>username</param-name>
    <param-value>root</param-value>
</context-param>

Java获取:

String value = servletContext.getInitParameter("username");

10.12 ServletContext小结

1.ServletContext是在服务器

2.ServletContext是被所有客户端共享

3.ServletContext是当web应用启动的时候,自动创建

4.ServletContext当web应用关闭/tomcat关闭时,会造成ServletContext销毁

5.web容器在启动时,它就会为每个web应用程序都创建一个对应的ServletContext对象,它代表当前web应用

6.ServletContext对象可以通过ServletConfig.getServletContext方法获得对ServletContext对象的引用,

​ 也可以通过this.getServletContext()来获得其对象的引用

7.由于web应用中的所有servlet共享一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。

ServletContext对象通讯也被称之为Context域对象,公共聊天室就会用到它。web网页版聊天室,不是QQ客户端聊天室。

8.多个Servlet通过ServletContext对象实现数据共享

9.获取web应用的初始化参数,全局的参数

<!--这里配置的参数,可以被所有servlet读取-->
<context-param>
    <param-name>username</param-name>
    <param-value>root</param-value>
</context-param>

servletContext获取:

String value = servletContext.getInitParameter("username");  //root

10.实现servlet的转发

ServletContext servletContext = this.getServletContext();
//请求转发
//ServletContext的servlet转发和forword原理是一样的
servletContext.getRequestDispatcher("/myHttpServlet").forward(request,response);

11.利用ServletContext对象读取资源文件(properties)

image-20220208022040435

​ 。读取资源文件(读web目录下的文件)

//web目录下才能用servletContext读取,src目录下的文件读不到
InputStream inputStream = servletContext.getResourceAsStream("/db.properties");
Properties properties = new Properties();
properties.load(inputStream);
String username = properties.getProperty("username");

​ 。得到文件路径

String realPath = servletContext.getRealPath("/images/hourse.jpg");

​ 。用类加载器读取src目录下的资源文件

src目录下的文件要用类加载器获取,servletContex读不到

10.13 ServletContext应用

在系统开发中,有很多功能需要使用ServletContext,比如:

1.系统技术器

2.系统在线人数的显示

3.简单的聊天系统

总之,如果是涉及到不同用户共享数据,而这些数据量不大,同时又不希望写入数据库中,

我们就可以考虑使用ServletContext来实现。

系统计数器:

在系统建设中,经常会统计某个页面被浏览的次数,我们把这个功能叫计数器:

那么这个计数器是怎么实现的?怎么样才是一个有效的点击?

1.只要访问过该网页,就算一次,刷新一次也算,当然这是最简单的,不过这有点虚假的成分
2.不同的ip访问该网页,算一次有效点击,如果是同一个ip在一定时间(比如1t天),不管浏览该网页多少次都算一次
3.用户退出网站,再次访问也算一次

image-20220208215557709

代码实现:

登录LoginControllerServlet.java

ServletContext servletContext = this.getServletContext();
String visitCount = (String)servletContext.getAttribute("visitCount");
if(visitCount==null){
    visitCount = "1";
    servletContext.setAttribute("visitCount",visitCount);
}else{
    String newVisitCount = (Integer.parseInt(visitCount)+1)+"";
    servletContext.setAttribute("visitCount",newVisitCount);
}

显示MainJspServlet.java

ServletContext servletContext = this.getServletContext();
String visitCount = (String)servletContext.getAttribute("visitCount");

当web服务器关闭后,我们的计数器就被清空了,请大家思考一下,如何能够保证计数器的稳定增长?

思路:

image-20220208205753154

代码实现:

1.web/record.text

0

2.web.xml配置初始化servlet

<servlet>
    <servlet-name>initServlet</servlet-name>
    <servlet-class>com.tangguanlin.controller.InitServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>initServlet</servlet-name>
    <url-pattern>/initServlet</url-pattern>
</servlet-mapping>

3.InitServlet.java

tomcat启动时,从record.text文件中读取访问记录数visitCount放入ServletContext中,
tomcat关闭时,从ServletContext取出访问记录数visitCount写入record.text文件中
优点: 不想频繁读读写数据库,不然数据库压力太大,写入项目的web目录下的record.text文件中
package com.tangguanlin.controller;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
/**
 * 说明:初始化servlet
 * 作者:汤观林
 * 日期:2022年02月08日 15时
 */
public class InitServlet  extends HttpServlet {

    @Override
    public void init() throws ServletException {
        //从record.text文件中,读取浏览器
        //1.首先得到文件的真实路径
       String filepath =  this.getServletContext().getRealPath("/record.text");
       //2.打开文件
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader(filepath));
            String line = "";
            line = bufferedReader.readLine();
            System.out.println("line="+line);
            this.getServletContext().setAttribute("visitCount",line);
            bufferedReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 	{

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 	{
        doGet(req,resp);
    }

    @Override
    public void destroy() {
        System.out.println("initServlet destroy()被调用......");
        //从record.text文件中,读取浏览器
        //1.首先得到文件的真实路径
        String filepath =  this.getServletContext().getRealPath("/record.text");
        try {
            FileWriter fileWriter = new FileWriter(filepath);
            String visitCount = (String)this.getServletContext().getAttribute("visitCount");
            fileWriter.write(visitCount);
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

问:如果tomcat异常退出,暴力关闭,比较杀进程,怎么办?

解决办法:可以使用线程,在init方法中,开启一个线程,定时把ServletContext的值,写入到record.text文件中,

​ 比如10min写一次,这样tomcat关闭,对访问数量影响也不大。

10.14 ServletContext注意事项

​ 因为存在ServletContext中的数据会长时间的保存在服务器,会占用内存,

​ 因此我们建议不要向ServletContext中添加过大的数据,

11 Session

11.1 什么是session

session:保存会话数据

不同的用户登录网站后,不管该用户浏览该网站的哪个页面,都可显示登录人的名字。

session是服务端技术,利用这种机制,服务端在运行时可以为每一个用户的浏览器创建一个其独享的session对象,

由于session为用户浏览器独享,所有用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,

当用户再去访问服务器中的其他web资源时,其他web资源再从用户各自的session中取出数据为用户服务。

当用户打开浏览器,访问某个系统操作session时,服务器就会在服务器(tomcat)的内存为该浏览器分一个session对象,

该session对象被这个浏览器独享。

这个session对象也可看做是一个容器,session默认存在时间是30min,你也可以修改。

session工作原理:

image-20220207235013596

image-20220207143903656

使用浏览器访问某一个servlet,其他浏览器可以取到这个servlet存放数据吗?

—答:不能

当发现没有session的时候,就会自动创建session。

//获取session
HttpSession session = request.getSession();
//将数据放入session
session.setAttribute("username","宋江");

11.2 request.getSession()

获取session

HttpSession session = request.getSession();

11.3 setAttribute(name,value)

将数据放入session中

session.setAttribute("username","宋江");

11.4 getAttribute(name)

从session中获取属性的值

String username = (String)session.getAttribute("username");

11.5 removeAttribute(“name”)

将数据从session中删除,让session中的单个session失效

session.removeAttribute("username");

11.6 session.getServletContext()

获取Servlet上下文 ServletContext对象

ServletContext servletContext = session.getServletContext();

11.7 session.setMaxInactiveInterval(60)

发呆时间

session.setMaxInactiveInterval(60);

11.8 session.invalidate()

让session失效,该方法让session中所有属性失效,通常用于安全退出

session.invalidate();

11.9 session.getId()

获取sessionId

String sessionId = session.getId();  //48407A4E7288895936AB3AA1D7EB02A1

11.10 对session的说明

1.session是存放在服务器内存中的

2.一个用户浏览器,独享一个session域对象

​ 电脑1的IE浏览器和电脑2的IE浏览器,是不同的session对象

​ 电脑1的IE浏览器和电脑1的谷歌浏览器,也是不同的session对象

3.session生命周期默认是30min,可以修改

​ 注意:这个的生命周期是指session中所有属性的生命周期,而不是某一个属性的生命周期,session失效了,那就是所有属性都失效了。

image-20220207142845913

​ (1)tomcat的web.xml文件 -----修改的是 所有的web应用的session生命周期

#tomcat的web.xml文件   
<session-config>
        <session-timeout>30</session-timeout>
</session-config>

​ (2)在单个web应用的web.xml中 -----修改的是单个web应用的session生命周期

​ 如果发生冲突,那就以单个web应用的web.xml为准

#单个web应用的web.xml文件  
<session-config>
    <session-timeout>60</session-timeout>
</session-config>

image-20220207133420192

​ (3)发呆时间

//发呆时间
session.setMaxInactiveInterval(60);

(4)让session中所有属性失效

 session.invalidate();

(5)让session中的单个属性失效

session.removeAttribute("username");

11.11 session小结

session用来做什么

1.保存登录用户的信息

2.防止用户非法登录到某个页面

3.将某些数据放入到session中,供同一用户的各个页面使用

如何理解session:

session不是特别好理解,可以把session看作是一个容器,类似HashMap,有两列。

每一行就是session的一个属性。

每个属性包含两个部分,一个是该属性的名字(String),另外一个是它的值(Object)。

名字 String值 Object
username韩顺平

session小结:

1.session中如果名字相同,会替换

2.session是存在服务器tomcat内存中的

3.session中的属性的默认生命周期是30min,可以通过web.xml来修改

4.session可以存放多个属性

5.session可以存放对象

如何防止用户非法登录到某个页面

​ 用户必须登录后,才能操作管理页面

思路:

当用户登录后,可以把该用户信息存放到session,

然后在需要验证的页面中获取用户信息,如果null,说明用户非法,可以让其重现登录。

防止非法登录:

login.java

HttpSession session = request.getSession();
session.setAttribute("user",user);

每个页面的拦截代码:

HttpSession session = request.getSession();
User user = (User)session.getAttribute("user");
if(user==null){
    //跳
    request.setAttribute("errorInfo","请输入用户名和密码登录");
    request.getRequestDispatcher("/loginJsp").forward(request,response);
    //必须加return 不然 要等执行完后面的代码结束后,才会再跳转页面
    return;
}

11.12 一个用户浏览器独享一个session

服务器是如何实现一个session为一个用户浏览器服务的?

image-20220207162914334

问题:关掉IE后,再访问ID,上次购买的商品还在,怎么实现?

image-20220207234321970

//方式一  实践中采用这种方式
HttpSession session = request.getSession();
session.setAttribute("name","韩顺平");
Connection: close
Content-Language: en
Content-Length: 2118
Content-Type: text/html;charset=utf-8
Date: Tue, 08 Feb 2022 15:58:37 GMT
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=C4222064039307660A2124A0CDEC401A; Path=/UserManager2/; HttpOnly

上面这段代码,创建了session,同时也会返回Set-Cookie: JSESSIONID=232EAA85BEC6306CF1D4A00C147F1537给前端,但因为没有给cookie设定生命周期,那就相当于就生命周期为0,只存在于浏览器内存中,没有写cookie临时文件。当本次浏览器关闭后,浏览器里面的cookie就失效了。请求后端,因为cookie失效了,没有sessionId传给服务器,所有也找不到session了。

现在的开发实践基本使用这种,关闭浏览器后,需要重现输入用户名和密码登录。

//方式二
HttpSession session = request.getSession();
session.setAttribute("name","韩顺平");

Cookie cookie =  new Cookie("JSESSIONID",sessionId);
cookie.setMaxAge(3*60*60);
response.addCookie(cookie);

上面这段代码,服务器创建了session,同时给cookie设定了生命周期3小时,因为有生命周期,cookie会写到浏览器的临时文件。

当浏览器关闭,再打开后,浏览量还是能到cookie临时文件中获取到sessionId,请求服务器,能找回session对象。

12 Cookie

12.1 为什么需要cookie

cookie:保存会话数据

1.什么是会话?

会话可以简单理解为:

​ 用户开一个浏览器访问某个网站,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为会话。

​ 只要不关闭浏览器,不管该用户点击多少链接,访问了多少资源,直到关闭浏览器,都算一次会话。

​ 比喻:打电话

​ 同一个用户,同一时间通过2个浏览器,访问一个网站,是不同的会话。

会话过程中要解决的一些问题:

​ 每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,服务器要想办法为每个用户保存这些数据。

比如:多个用户点击超链接通过一个servlet各自购买了一个商品,服务器应该想办法把每一个用户购买的商品保存在各自的地方,

以便于这些用户点结账Servlet时,结账servlet可以得到用户各自购买的商品为用户结账。

提问:这些数据保存在request行不行?

引子:为什么需要cookie?

1.大家访问某个网站的时候,是否看到提示你上次登录网站的时间,而且要注意,不同用户上次登录的时间肯定是不一样的。

这是怎么实现的?

image-20220206181118133

如何保持用户上次登录时间?

解决方法:

image-20220206181737194

2.大家在访问某个购物网站的时候,是否能看到提示你曾经浏览过的商品,同样也是不同用户浏览过的商品不一样,

这是怎么实现的?

image-20220206181811844

没有登录,也能看到浏览历史,怎么处理?

如何显示用户浏览历史?

3.我们在登录某个网站的时候,往往都可以选择保存登录信息多久,不用重复输入登录信息,这个又是怎么实现的?

image-20220206181531545

如何把登录的用户名和密码保存到电脑,下次登录,不需要重新登录。

总结:如果保存用户的登录信息?

12.2 解决之道cookie

Cookie是客户端技术,服务器把每个用户的数据以cookie的形式写给用户各自的浏览器。

当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。

这样,web资源处理的ius用户各自的数据了。

保存会话数据的计算—cookie

网站可以使用 Cookie 来提升您的浏览体验,例如让您保持登录状态或记住您购物车中的商品

网站可以使用 Cookie 查看您在各个不同网站上的浏览活动,以便实现某些功能或目的(例如为您展示个性化广告)

什么是cookie

​ 服务器在客户端保存用户的信息,比如登录名,密码等,就是cookie。

​ 这些信息就像是小甜饼一样,数据量并不大,服务器端在需要的时候可以从客户端取,保存子啊客户端的浏览器缓存目录下。

cookie原理示意图:

image-20220206194859453

cookie可以用来做什么

​ 1.保存上次登录时间等信息

​ 2.保存用户名、密码,在一定时间不用重新登录

​ 3.记录用户访问网站的喜好(比如有无背景音乐、网页的背景色是什么)

​ 4.网站的个性化,比如定制网站的服务、内容

cookie的基本使用:

​ 1.cookie有点像一张表,分两列,一个是名字,一个是值,数据类型都是String

​ 2.如何创建cookie(在服务端创建的)

Cookie cookie = new Cookie("name","hanshunping");
cookie.setMaxAge(3600); //一天

​ 3.如何将cookie添加到客户端

response.addCookie(cookie);  

4.如何读取cookie(从客户端读到服务器)

Cookie[] cookies = request.getCookies();

12.3 写入cookie

代码生成cookie:

 //创建cookie(api)
 Cookie cookie = new Cookie("name","hanshunping");

//设置cookie有效时间  生命周期  单位是 s 秒
cookie.setMaxAge(3600); //一天
//把cookie信息回写给浏览器
response.addCookie(cookie);  

带cookie数据的http响应格式:

Content-Length: 0
Content-Type: text/html;charset=utf-8
Date: Sun, 06 Feb 2022 10:47:22 GMT
Server: Apache-Coyote/1.1
#cookie数据
Set-Cookie: name=hanshunping; Expires=Sun, 06-Feb-2022 11:47:22 GMT

浏览器查看cookie数据:

image-20220206190503323

image-20220206193303362

12.4 读取cookie

含cookie数据的http请求格式:

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
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
#cookie数据
Cookie: name=hanshunping; name2=hanshunping2
Host: localhost:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36

浏览器查看cookie:

image-20220206192756040

代码读取cookie:

//读取cookie
Cookie[] cookies = request.getCookies();
for(Cookie cookie:cookies){
    if("name".equals(cookie.getName())){
        System.out.println(cookie.getName()+"="+cookie.getValue());
    }
}

运行结果:

name=hanshunping
name2=hanshunping2

12.5 new Cookie(name,value)

新建,生成一个cookie

Cookie cookie = new Cookie("name2","hanshunping2");

12.6 cookie.setValue(String)

给当前cookie设新的值,相当于修改cookie的值。

//给当前cookie设置新值 
cookie.setValue("hanshunping");

12.7 cookie.setMaxAge()

设置cookie的生命周期,单位是秒

//设置cookie有效时间  生命周期  单位是 s 秒
cookie.setMaxAge(24*60*60); //一天
//删除该cookie
cookie.setMaxAge(0);

12.8 response.addCookie(cookie)

添加cookie到response对象中,cookie才会生效

//把cookie信息回写给浏览器
response.addCookie(cookie);

12.9 request.getCookies()

从浏览器中获取所有的cookie,没有获取指定cookie的方法,只能通过遍历获取指定cookie

//读取cookie
Cookie[] cookies = request.getCookies();
for(Cookie cookie:cookies){
    System.out.println(cookie.getName()+"="+cookie.getValue());
}

12.10 cookie.getName()

获取当前cookie的名称

String name =cookie.getName()

12.11 cookie.getValue(String)

获取当前cookie的值

String value = cookie.getValue();

12.12 cookie.getMaxAge()

获取cookie的生命周期

int maxAge = cookie.getMaxAge();

12.13 cookie小结

​ 1.cookie是服务端创建的

​ 2.cookie是保存在浏览器端的

​ 3.cookie的生命周期设定,实践中,一般保存1周

​ cookie默认生命周期是会话级别(存储在浏览器的内存中),用户退出浏览器之后即被删除。

​ 当然,可以你可以通过setMaxAge()设置cookie的生命周期

​ 特别说明:如果该web应用只有一个cookie,则删除cookie后,在浏览器的临时文件夹下没有该cookie文件,

​ 如果该web应用由多个cookie,则删除一个cookie后,文件还在,只是该cookie没有了。

cookie.setMaxAge(3600);   //单位是秒
cookie.setMaxAge(0);  //值为0,代表删除该cookie  
cookie.setMaxAge(负数); //当值为负数时,该浏览器退出时,就会删除,相当于会话级。

如果不设置setMaAge,则该cookie的生命周期当浏览器关闭时,就消亡

​ 4.cookie可以被多个浏览器共享

​ 5.怎么理解

我们可以把cookie想成一张表

名字 String值 String
shunshunping123456

如果重名了,就会替换存在的cookie值。

​ 6.一个web应用可以保存多个cookie,都存放到一个cookie文件中。一个浏览器也可以保存多个web应用提供的cookie。

一般只允许存放300个cookie,每个站点最多存放20个cookie,每个cookie的大小限制为4KB.
因此cookie不会塞满你的硬盘,更不会被用作“拒绝服务”攻击手段。

​ 7.cookie存放的时候,是以明文方式存放的,因此安全性较低。我们可以通过加密后再保存。

​ MD5算法,不可逆。

​ 用户密码 MD5加密后存入数据库,(数据库管理也无法知道明文密码,数据库泄漏了也没事),

​ 用户支付时,输入明文密码,通过MD5加密后,和数据库的MD5密码进行对比。

​ 相当于虽然系统保存了你的密码,但没有任何人知道你的明文密码,只有你自己知道明文密码。

​ 8.cookie存放中文,怎么处理

​ 中文值放入cookie:

String value = URLEncoder.encode("韩顺平", "utf-8");
Cookie cookie = new Cookie("name2",value);

从cookie取出中文值:

String value = URLDecoder.decode(cookie.getValue(),"utf-8");

12.14 cookie vs session

1.存在的位置

​ cookie保存在客户端,session保存在服务器,服务器可以为每个用户浏览器创建一个会话对象(session对象),

注意:一个浏览器独占一个session对象(默认情况下).

因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中。

当用户使用浏览器访问其他模块代码时,其他模块可以从用户的session中取出该用户的数据,为用户服务。

session的数据不宜过多。

2.安全性

比较而言,cookie是以明文方式存放在客户端的,可以通过MD5加密后再存放,

session是存放在服务器端内存中的,安全性好,

所以,cookie的安全性比session要弱。

3.网络传输量

cookie通过网络在客户端与服务器端传输。

而session保存在服务器,不需要传输。

4.生命周期不一样(20分钟为例)

​ (1)cookie的生命周期是累计的,从创建时,就开始计时,20分钟后cookie生命周期结束,cookie就无效。

​ (2)session的生命周期是间隔的,从创建时,开始计时如在20分钟,没有访问过session,那么session信息无效,

​ 如果在20分钟内,比如第19分钟时,访问过session,那么,它的生命周期将重新开始计算。

​ (3)另外,关机会造成session生命周期结束,但是对cookie没有任何影响。

5.从访问范围来看

​ session为一个用户浏览器独享

​ cookie为多个用户浏览器共享

6.使用原则:

​ 因为session会占用 服务器的内存,因此,不要向session中存放过多,过大的对象,会影响性能。

13 文件上传下载

13.1 文件下载

文件下载原理:

image-20220203182258748

代码:

package com.tangguanlin;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
/**
 * 说明:文件下载
 * 作者:汤观林
 * 日期:2022年02月03日 18时
 */
public class FileDownloadServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String temp = java.net.URLEncoder.encode("传奇.mp3","utf-8"); //防止中文乱码
        resp.setHeader("Content-Disposition","attachment;filename=hourse.jpg");
        ServletOutputStream outputStream = resp.getOutputStream();

        //1.获取到要下载文件的全路径
        String filepath = req.getServletContext().getRealPath("/images/hourse.jpg");

        //2.创建文件输入流
		FileInputStream fis = new FileInputStream(filepath);
        
        byte[] buff = new byte[1024];
        int length = 0;
        while((length=fis.read(buff))!=-1){
            outputStream.write(buff,0,length);
        }

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

运行结果:

http://localhost:8080/jsp_servlet_project/fileDownloadServlet

image-20220203181611663

image-20220203181734261

13.2 文件上传

(省略)

14 中文乱码处理专题

乱码产生的原理:

image-20220203235302711

发生中文乱码有三种情况:

14.1 表单form提交

​ (1)post提交

<head>
    <meta charset="UTF-8">
</head>
<form action='/UserManager/loginController' method='post'>
	用户名:<input type='text' name='username' /></br>
	密  码:<input type='password' name='password'/></br>
	      <input type='submit' value='登录' />
</form>

解决方法:

接收时,按页面一样的编码格式utf-8接收即可。

request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");

(2)get提交

http://localhost:8080/UserManager/mainJsp?username=韩顺平

解决办法:

​ 因为数据不是包含在包里面

String username = new String(request.getParameter("username").getBytes("ISO-8859-1"),"utf-8");

14.2 超链接

<a href="http://www.sohu.com?username=韩顺平">测试</a>

解决办法:

​ 超链接本质是get提交,处理和get提交一样

14.3 sendRedirect()

response.sendRedirect("/jsp_servlet_project/MyHttpServlet?name=韩顺平");

解决办法:

​ 重定向本质是get提交,处理和get提交一样

特别说明:如果您的浏览器是IE6或以下版本,则我们的2和3中情况还是会出现乱码

解决方法是:请求时要编码,接收时不变

String info = java.net.URLEncoder.encode("你好吗","utf-8");

说明:我们尽量用post提交

14.4 Response回送乱码

解决方法:指定浏览器显示编码格式

response.setContentType("text/html,charset=utf-8");

14.5 下载文件提示框乱码

当我们下载文件的时候,可能提示框是中文乱码

解决方法:

String temp = java.net.URLEncoder.encode("传奇.mp3","utf-8"); //防止中文乱码
resp.setHeader("Content-Disposition","attachment;filename="+temp);

15 Servlet操作数据库

因为Servlet本身就是一个Java类,因此我们Servlet操作数据库和普通java类是一样的。

​ 1.如何在servlet中显示图片

​ 2.分页技术详解(Oracle分页)

3.用户登录系统功能改进

4.网站架构的改进

发现问题---->处理问题---->解决问题

15.1 分页技术算法

思路:

​ 定义4个变量: 死去活来法

pageNow:第几页 ----该变量是用户决定的,变化的

pageCount : 总页数 ----程序指定 常量设定

pageSize:每页显示几条记录 ----计算出来的

rowCount:总共有多少记录 -----该变量是查询数据库得到的

1.算法1

 if(rowCount%pageSize==0){
     pageCount = rowCount/pageSize;
 }else{
     pageCount = rowCount/pageSize+1;
 }

比如: users表 9条记录 pageSize = 3 —> pageCount = 3

​ users表 10条记录 pageSize = 3 —> pageCount = 4

2.算法2

//算法1 等价
pageCount = rowCount%pageSize==0?rowCount/pageSize:rowCount/pageSize+1;

3.算法3

简洁的写法

pageCount = (rowCount-1)/pageSize + 1;

比如: users表 9条记录 pageSize = 3 —> pageCount = 3

​ users表 11条记录 pageSize = 3 —> pageCount = 4

15.2 页面 manager.jsp

out.println("<table border = 1 width=500px >");
out.println("<tr><th>id</th><th>用户名</th><th>email</th><th>级别</th></tr>");
//5.拿到结果集
if(rs.next()){
    //进来,说明该用户合法
    out.println("<tr><td>"+rs.getInt(1)+
                "</td><td>"+rs.getString(2)+
                "</td><td>"+rs.getString(4)+
                "</td><td>"+rs.getString(5)+
                "</td></tr>");
}
out.println("</table>");
//显示分页
out.println("\n<a href='/UserManager/managerJsp?pageNow="+(pageNow-1)+"'>上一页</a>");
for(int i=1;i<=pageCount;i++){
    out.println("<a href='/UserManager/managerJsp?pageNow="+i+"'>第"+i+"页</a>\n");
}
out.println("\n<a href='/UserManager/managerJsp?pageNow="+(pageNow+1)+"'>下一页</a>");
out.println("\n共"+pageCount+"页");

15.3 JDBC

    Connection connection = null;
        ResultSet rs = null;
        PreparedStatement ps = null;
          try {
              //1.加载驱动
              Class.forName("oracle.jdbc.driver.OracleDriver");

              //2.得到连接
              connection = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl","scott","admin");

              //3.创建preparedSatement
              ps = connection.prepareStatement("select * from users where id=? and password=?");
              //给?赋值
              ps.setObject(1,userId);
              ps.setObject(2,password);

              //4.执行操作
              rs = ps.executeQuery();

              //5.拿到结果集
              if(rs.next()){
                  //进来,说明该用户合法
                  //跳转到下一个页面
                  //resp.sendRedirect("/UserManager/mainJsp?userId="+userId);
                  request.getRequestDispatcher("/mainJsp").forward(request,response);
              }else{
                  //跳回
                  //resp.sendRedirect("/UserManager/loginJsp");
                  request.setAttribute("errorInfo","用户Id或者密码有误");
                  request.getRequestDispatcher("/loginJsp").forward(request,response);
              }
          }catch (Exception e){
              e.printStackTrace();
          }finally {
              //关闭资源
              if(rs!=null){
                  try {
                      rs.close();
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }
              }
              if(ps!=null){
                  try {
                      ps.close();
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }
              }
              if(connection!=null){
                  try {
                      connection.close();
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }
              }
          }

15.4 分页sql(oracle)

select t2.* from (select t1.*,rownum rn from users t1) t2  where t2.rn >3 and t2.rn <7;

"select t2.* from (select t1.*,rownum rn from users t1) t2  
              where t2.rn >=("+pageSize+"*("+pigeNow"+"-1)+1) and t2.rn <="+pageSize+"*"+pageNow;

当我们使用Java web的时候,读取文件要使用类加载器

因为类加载器去读取资源的时候,默认的主目录是src.

image-20220206004505967

properties =  new Properties();
InputStream is = SqlHelper.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(is);
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
String username = properties.getProperty("username");
String password = properties.getProperty("password");

16 Servlet开发项目

16.1 用户管理系统框架图

简单的用户登录系统(servlet版本1.0)

通过HttpServlet去开发Servlet

​ 现在我们已经初步了解了Servlet的基本原理和机制,现在开始做一个比较综合的案例—用户登录系统

v1.0包含的功能:

​ 1.进行简单的用户验证

​ 2.如何使用Servlet开发动态网页

​ 3.如何从一个页面跳转到另外一个页面

注意重要提示:怎样去确定需要一个新的控制器,

​ 原则:一类事务请求,对应一个控制器,比如用户的增删改查,都写到一个控制器里

1.一个请求对应一个控制器

​ 优点:逻辑清晰

​ 缺点:会造成控制器过多

2.可以这样考虑:一类事务请求,我们做一个控制器,即让该控制器可以处理多个请求

​ 为了让一个控制器去区分不同的请求,我们可以这样做,

​ 在发出请求的同时,再带一个type=add或者 type=update 或者 type =delete 或者type=query

​ 在控制器中我们接收type的值,从而判断用户希望做什么事情。

关于跳转到修改用户界面有两种思路:

(1)传递用户id号的同时,把用户的其他信息一并传递,这样可以减少数据库查询次数

​ 缺点:增加网络开销; 有可能中文乱码; 把对象字段信息暴露在地址栏

​ 优点:减少对数据库的一次操作

(2)只传递用户Id号,控制器接收到id后,再查询数据库,从而显示。

​ 推荐使用这种方式

用户管理系统框架图:

image-20220208222303698

16.2 项目界面

登录界面:

image-20220208223044775

主界面:

image-20220208223512788

用户管理界面:

image-20220208223553413

16.3 项目结构

image-20220208222805467

16.4 实现代码

见代码仓库

https://gitee.com/tangguanlin2006/UserManager2(gitee仓库)

17 过滤器Filter

image-20220208024415113

​ Servlet + Filter + Listener = Servlet规范的三大组件

17.1 Filter概述

17.1.1 是什么

过滤器:能够完成筛选不需要数据的工具

web中:过滤器其实就是服务端的一个程序(程序最小单元是类)

​ 在web中,过滤器其实就是一个web组件(Servlet/Filter/Listener),其实就是一个抽象类

​ 一个类实现java.servlet.servlet接口 ------>Servlet类

​ 一个类实现java.servlet.Filter接口 ------>Filter类

17.1.2 有什么作用

过滤器的作用:过滤请求和响应

​ 可以在请求到达目标资源之前先对请求进行拦截过滤 ,即对请求进行一些处理,

​ 也可以在响应到达客户端之前先对响应进行拦截过滤,即对响应进行一些处理。

image-20220212135452733

17.1.3 应用场景

​ 。自动登录

​ 。统一编码

​ 。过滤一些敏感词

17.2 入门程序

1.编写一个类

​ —实现一个Filter接口

​ —重写所有的方法

package com.tangguanlin;
import javax.servlet.*;
import java.io.IOException;
/**
 * 说明:过滤器
 * 作者:汤观林
 * 日期:2022年02月12日 14时
 */
public class Demo1Filter  implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
         //创建Filter
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //拦截处理
        System.out.println("Demo1Filter 过滤到请求");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
        //拦截响应
        System.out.println("Demo1Filter 过滤到响应");
    }

    @Override
    public void destroy() {
        //销毁Filter
    }
}

2.编写配置文件

​ —注册filter

​ —绑定路径

web.xml

<!--过滤器-->
<filter>
    <filter-name>demo1Filter</filter-name>
    <filter-class>com.tangguanlin.Demo1Filter</filter-class>
</filter>

<filter-mapping>
    <filter-name>demo1Filter</filter-name>
    <url-pattern>/demo1Servlet</url-pattern>
</filter-mapping>

17.3 Filter生命周期

image-20220212135519903

1.Filter在tomcat启动时就创建Filter

2.Filter在tomcat启动时就初始化Filter

3.在每次http请求时,doFilter时被执行

4.Filter在tomcat关闭时销毁

总结:Filter是单实例,

​ Filter是多线程,每次请求,都会调用doFilter方法

​ 当项目启动的时候,服务器创建Filter对象,调用init方法进行初始化操作,

​ 每当请求来的时候,服务器获取一个线程,执行doFilter方法,实现过滤的逻辑,每请求一次,就会执行一次doFilter方法

​ 当服务器正常关闭的时候,服务器调用destroy方法,实现销毁操作

由于Filter是单例多线程的,为了保证其线程安全性,一般是不会为Filter添加可修改的成员变量

17.4 Filter方法

17.4.1 init()

初始化Filter

17.4.2 doFilter()

过滤器业务处理

17.4.3 destroy()

销毁Filter

17.5 FilterChain过滤链

​ FilterChain过滤链:多个Filter组合在一起

​ 执行顺序:多个Filter的执行顺序是由web.xml中filter-mapping的位置决定的,

​ 当一个filter收到请求的时候,调用chainFilter才可以访问下一个匹配的Filter,

​ 如果当前的filter是最后一个filter,调用chainFilter.doFilter才能访问目标资源。

​ 当应用中存在多个Filter时,其执行顺序与其注册顺序一致。

image-20220212155216243

​ 将请求放行到下一个过滤器

filterChain.doFilter(servletRequest,servletResponse);

web.xml

<!--人脸识别过滤器2-->
<filter>
    <filter-name>demo2Filter</filter-name>
    <filter-class>com.tangguanlin.Demo2Filter</filter-class>
</filter>
<!--指纹识别过滤器1-->
<filter>
    <filter-name>demo1Filter</filter-name>
    <filter-class>com.tangguanlin.Demo1Filter</filter-class>
</filter>

<filter-mapping>
    <filter-name>demo2Filter</filter-name>
    <url-pattern>/demo1Servlet</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>demo1Filter</filter-name>
    <url-pattern>/demo1Servlet</url-pattern>
</filter-mapping>

运行结果:

人脸识别过滤器2 接收到请求
指纹识别过滤器1 过滤到请求
Demo1Servlet 接收到请求
指纹识别过滤器1 过滤到响应
人脸识别过滤器2 接收到响应

17.6 Filter-mapping中的子标签

filter-mapping中可以不使用url-pattern,但需要指定servlet-name,即当前过滤器拦截的是对指定的servlet的请求。

17.6.1 url-pattern

1.完全匹配

​ 以“/”开始, 比如: /demo1/demo2/aa/bb/demo3

2.目录匹配

​ 以“/”开始,以*结束, 比如:/aa/* /*

​ 若Filter为全路径匹配方式,那么url-pattern只能为/*,而不能写为/ 。

​ /* :表示当前Servlet可以匹配所有的请求,既可以拦截所有的请求,无论发出的是静态资源,还是动态资源的访问,统统会被拦截

3.后缀名匹配

​ 以*开始 比如: *.jsp *.html *.do *.action

17.6.2 servlet-name

​ 指定具体过滤哪个servlet

<filter>
    <filter-name>demo2Filter</filter-name>
    <filter-class>com.tangguanlin.Demo2Filter</filter-class>
</filter>
<filter-mapping>	
    <filter-name>demo2Filter</filter-name>       
    <servlet-name>demo1Servlet</servlet-name>
</filter-mapping>

17.6.3 dispatcher

​ request: 表示当前过滤器会拦截普通请求,但对于forward()的跳转不进行拦截,默认值

​ forward: 表示当前过滤器只会拦截由一个servlet通过request.getRequestDispatcher的forward()完成的跳转

<filter-mapping>
    <filter-name>demo1Filter</filter-name>
    <url-pattern>/demo1Servlet</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

17.7 FilterConfig

​ FilterConfig指的是Filter在web.xml中的注册信息,初始化参数

<filter>
    <filter-name>demo1Filter</filter-name>
    <filter-class>com.tangguanlin.Demo1Filter</filter-class>
    <init-param>
        <param-name>username</param-name>
        <param-value>tangguanlin</param-value>
    </init-param>
    <init-param>
        <param-name>db</param-name>
        <param-value>oracle</param-value>
    </init-param>
</filter>

17.7.1 getFilterName()

​ 获取filter的名称

 String filterName = filterConfig.getFilterName();

17.7.2 getServletContext()

​ 获取上下文servletContext对象

ServletContext servletContext = filterConfig.getServletContext();

17.7.3 getInitParameter(name)

​ 获取指定的filter初始化参数

String username = filterConfig.getInitParameter("username");

17.7.4 getInitParameterNames()

​ 获取filter所有初始化名称

Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();
while (initParameterNames.hasMoreElements()){
    String nextElement = initParameterNames.nextElement();
    String value = filterConfig.getInitParameter(nextElement);
    System.out.println(nextElement+"="+value);
}

17.8 全局统一错误页面

当获取不到请求的路径对应的资源时,就跳到这个404.jsp页面

web.xml

<error-page>
    <error-code>404</error-code>
    <location>/404.jsp</location>
</error-page>

404.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>404</title>
</head>
<body>
      <h1>这个通过error-page设置的页面</h1>
</body>
</html>

运行结果:

http://localhost:8080/jsp_servlet_project/111 没有111这个路径对应的资源

image-20220212180718073

17.9 应用案例

完成自动登录

17.9.1 需求

​ 当用户第一次登录该网站的时候,如果勾选了‘自动登录’复选框,

​ 那么浏览器关闭后,下次再访问该网站任何资源的时候,都会显示用户在线。

17.9.2 技术分析

​ cookie(保存用户第一次访问网站的用户名和密码)

​ session(保证用户在线)

​ Filter过滤器

17.9.3 流程图

image-20220213020100434

17.9.4 代码实现

(省略)

18 监听器Listener

Servlet规范中已经定义好了8个监听器接口,

它们要监听的对象分别是request,session,ServletContext对象,触发监听器的实际是这三个对象的创建与销毁,

它们的域属性控件中属性的添加,删除,修改,及session的钝化与活化操作。

​ 在Java web项目中使用监听器,需要在web.xml文件中读监听器进行注册。

web.xml 注册监听器

<listener>
    <listener-class>com.tangguanlin.listener.Listenerable</listener-class>
</listener>

下面分别对着8个监听器进行学习。

18.1 ServletRequestListener

​ 该监听器用于完成度request对象的创建和销毁的监听。

​ 即当request对象被创建或被销毁时,会触发该监听器中相应方法的执行。

18.1.1 requestInitialized()

请求对象被创建

18.1.2 requestDestroyed()

请求对象被销毁

18.1.3 代码实现

web.xml 注册监听器

<listener>
    <listener-class>com.tangguanlin.listener.MyRequestListener</listener-class>
</listener>

监听器类MyRequestListener.java

package com.tangguanlin.listener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
/**
 * 说明:RequestListener监听器
 * 作者:汤观林
 * 日期:2022年02月13日 16时
 */
public class MyRequestListener implements ServletRequestListener {

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        System.out.println("请求对象被创建");
    }

    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        System.out.println("请求对象被销毁");
    }
}

运行结果:

http://localhost:8080/jsp_servlet_project/demo1Servlet

请求对象被创建
Demo1Servlet 接收到请求
请求对象被销毁

18.2 ServletRequestAttributeListener

该监听器用于完成对request域属性空间中属性的添加、修改、删除操作的监听。

18.2.1 attributeAdded()

ServletRequest属性被添加

18.2.2 attributeRemoved()

ServletRequest属性被删除

18.2.3 attributeReplaced()

ServletRequest属性被替换

18.2.4 代码实现

web.xml 注册监听器

<listener>
    <listener-class>com.tangguanlin.listener.MyRequestAttributeListener</listener-class>
</listener>

MyRequestAttributeListener.java

package com.tangguanlin.listener;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
/**
 * 说明:RequestAttributeListener监听器
 * 作者:汤观林
 * 日期:2022年02月13日 17时
 */
public class MyRequestAttributeListener  implements ServletRequestAttributeListener {
    @Override
    public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("request属性"+servletRequestAttributeEvent.getName()+"被添加"+servletRequestAttributeEvent.getValue());
    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("request属性"+servletRequestAttributeEvent.getName()+"被删除"+servletRequestAttributeEvent.getValue());
    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("request属性"+servletRequestAttributeEvent.getName()+"被替换");
    }
}

HttpRequestServlet1.java

req.setAttribute("username","tangguanlin");
req.setAttribute("username","tangguanlin2006");
req.removeAttribute("username");

运行结果:

http://localhost:8080/jsp_servlet_project/HttpRequestServlet1

request属性username被添加tangguanlin
request属性username被替换
request属性username被删除tangguanlin2006

18.3 HttpSessionListener

该监听器用于完成度session对象的创建和销毁的监听

18.3.1 sessionCreated()

​ session对象被创建

18.3.2 sessionDestroyed()

session对象被销毁

18.3.3 代码实现

注册监听器

<listener>
    <listener-class>com.tangguanlin.listener.MySessionListener</listener-class>
</listener>

MySessionListener.java session监听器

package com.tangguanlin.listener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
 * 说明:SessionListener监听器
 * 作者:汤观林
 * 日期:2022年02月13日 17时
 */
public class MySessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println("session被创建");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println("session被销毁");
    }
}

HttpRequestServlet1.java

HttpSession session = req.getSession();
session.invalidate();

运行结果:

session被创建
session被销毁

18.4 HttpSessionAttributeListener

该监听器用于完成session域属性空间中属性的添加,修改,删除操作的监听。

当向session域中添加属性时触发

18.4.1 attributeAdded()

​ 添加了session属性

18.4.2 attributeRemoved()

​ 删除了session属性

18.4.3 attributeReplaced()

​ session的属性被替换了

18.4.4 代码实现

注册监听器

<listener>
    <listener-class>com.tangguanlin.listener.MySessionAttributeListener</listener-class>
</listener>

MySessionAttributeListener.java session属性监听器

package com.tangguanlin.listener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
/**
 * 说明:HttpSessionAttributeListener监听器
 * 作者:汤观林
 * 日期:2022年02月13日 17时
 */
public class MySessionAttributeListener  implements HttpSessionAttributeListener {
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        System.out.println("向session中添加了属性:"+se.getName()+"="+se.getValue());
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        System.out.println("向session中删除了属性:"+se.getName()+"="+se.getValue());
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        System.out.println("session中属性"+se.getName()+"值被替换");
    }
}

HttpRequestServlet1.java

HttpSession session = req.getSession();
session.setAttribute("username","tangguanlin");
session.setAttribute("username","tangguanlin2006");
session.removeAttribute("username");

运行结果:

http://localhost:8080/jsp_servlet_project/HttpRequestServlet1

向session中添加了属性:username=tangguanlin
session中属性username值被替换
向session中删除了属性:username=tangguanlin2006

18.5 ServletContextListener

​ 该监听器用于完成对ServletContext对象的创建和销毁的监听。

​ 不过需要注意,由于ServletContext在一个应用中只有一个,且是在服务器启动时创建。

​ 另外,servletContext的生命周期和整个应用的相同,所以当项目重现部署,或者tomcat正常关闭,

​ 可以销毁servletContext。

18.5.1 contextInitialized()

​ tomcat服务器启动时调用,创建ServletContext对象

14.5.2 contextDestroyed()

​ tomcat服务器关闭时调用,销毁ServletContext对象

18.5.3 代码实现

注册监听器

<listener>
    <listener-class>com.tangguanlin.listener.MyServletContextListener</listener-class>
</listener>

MyServletContextListener.java 监听器

package com.tangguanlin.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
 * 说明:ServletContextListener监听器
 * 作者:汤观林
 * 日期:2022年02月13日 18时
 */
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext对象被创建");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext对象被销毁");
    }
}

运行结果:

tomcat启动时:

ServletContext对象被创建

tomcat关闭时:

ServletContext对象被销毁

18.6 ServletContextAttributeListener

该监听器用于完成ServletContext域属性空间中属性的添加,修改,删除操作的监听。

18.6.1 attributeAdded()

​ 已经添加了ServletContext对象属性

18.6.2 attributeRemoved()

​ 已经删除了 ServletContext对象属性

18.6.3 attributeReplaced()

​ ServletContext对象属性已经被重置

18.6.4 代码实现

注册监听器

<listener>
    <listener-class>com.tangguanlin.listener.MyServletContextAttributeListener</listener-class>
</listener>

MyServletContextAttributeListener.java 监听器

package com.tangguanlin.listener;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
/**
 * 说明:ServletContextAttributeListener监听器
 * 作者:汤观林
 * 日期:2022年02月13日 18时
 */
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
    @Override
    public void attributeAdded(ServletContextAttributeEvent sce) {
        System.out.println("向ServletContext属性中添加了属性:"+sce.getName()+"="+sce.getValue());
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent sce) {
        System.out.println("向ServletContext属性中删除了属性:"+sce.getName()+"="+sce.getValue());
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent sce) {
        System.out.println("向ServletContext属性中重置了属性:"+sce.getName()+"="+sce.getValue());
    }
}

HttpRequestServlet1.java

ServletContext servletContext = this.getServletContext();
servletContext.setAttribute("username","tangguanlin");
servletContext.setAttribute("username","tangguanlin2006");
servletContext.removeAttribute("username");

运行结果:

http://localhost:8080/jsp_servlet_project/HttpRequestServlet1

ServletContext属性中添加了属性:username=tangguanlin
向ServletContext属性中重置了属性:username=tangguanlin
向ServletContext属性中删除了属性:username=tangguanlin2006

18.7 HttpSessionBindingListener

该监听器用于监听指定类型对象与session的绑定和解绑,即该类型对象被放入到session域中,

或从session域中删除该类型对象,均会引发该监听器中相应方法的执行。

它与HttpSessionAttributeListener的不同之处是,该监听器监听的是指定类型的对象在session域中的操作,

而HttpSessionAttributeListener监听的是session域属性空间的变化,无论是什么类型的对象。

​ 另外,需要强调两点:

​ 。该监听器是由实体类实现

​ 。该监听器无需在web.xml中注册

18.7.1 valueBound()

当当前类的对象绑定到session时(放入到session域中)会触发valueBound方法

18.7.2 valueUnbound()

当当前类的对象与session解绑时(从session域中删除)会触发valueUnbound方法

18.7.3 代码实现

Student.java

package com.tangguanlin.listener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
/**
 * 说明:HttpSessionBindingListener监听器
 *      实体类实现HttpSessionBindingListener接口
 *      该监听器不需要注册
 * 作者:汤观林
 * 日期:2022年02月13日 19时
 */
public class Student implements HttpSessionBindingListener {

    private  String name;
    private  int age;

    //当当前类的对象绑定到session时(放入到session域中)会触发该方法
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println("student对象放入到session域");
        System.out.println(event.getName()+"="+event.getValue().toString());
    }

    //当当前类的对象与session解绑时(从session域中删除)会触发该方法
    @Override
    public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println("student对象从session域中删除");
    }

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

HttpRequestServlet1.java

Student student = new Student("zhangsan", 22);
HttpSession session = req.getSession();
session.setAttribute("student",student);
session.removeAttribute("student");

运行结果:

http://localhost:8080/jsp_servlet_project/HttpRequestServlet1

student对象放入到session域
student=Student{name='zhangsan', age=22}
student对象从session域中删除

18.8 HttpSessionActivationListener

​ 该监听器用于监听在Session中存放的指定类型对象的钝化和活化。

​ 钝化是指将内存中的数据写入到硬盘中,

​ 而活化是指将硬盘中的数据恢复到内存中。

​ 当用户正在访问的应用或该应用所在的服务器由于种种原因被停掉,然后再短时间内又重启,

​ 此时用户在访问时session中的数据是不能丢掉的,在应用关闭之前,需要将数据写入到硬盘,

​ 在重启后应可以立即重新恢复Session中的数据,这就称为session的钝化和活化。

​ 那么session中的哪些数据能够钝化呢?只有存放在JVM堆内存中的实现了Serializable类的

对象能够被钝化,也就是说,对于字符串常量,基本数据类型常量存放在JVM方法区常量池中的常量,

是无法被钝化的。

​ 实体类实现HttpSessionActivationListener接口,同时还要实现Serializable接口

18.8.1 sessionDidActivate()

当当前类的对象被钝化时(内存中的数据写入到硬盘),会触发该方法的执行

18.8.2 sessionWillPassivate()

当当前类的对象被活化时(硬盘中的数据恢复到内存),会触发该方法的执行

18.8.3 代码实现

Student2.java

package com.tangguanlin.listener;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import java.io.Serializable;
/**
 * 说明:HttpSessionActivationListener监听器
 *      实体类实现HttpSessionActivationListener接口,同时还要实现Serializable接口
 *      该监听器不需要注册
 * 作者:汤观林
 * 日期:2022年02月13日 19时
 */
public class Student2 implements HttpSessionActivationListener, Serializable {

    private  String name;
    private  int age;


    //当当前类的对象被钝化时(内存中的数据写入到硬盘),会触发该方法的执行
    @Override
    public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {
        System.out.println("Student2已经活化");
    }

    //当当前类的对象被活化时(硬盘中的数据恢复到内存),会触发该方法的执行
    @Override
    public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {
        System.out.println("Student2将要被钝化");
    }


    public Student2() {
    }

    public Student2(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student2{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

HttpRequestServlet1.java

Student2 student2 = new Student2("zhangsan", 22);
HttpSession session = req.getSession();
session.setAttribute("student2",student2);

运行结果:

tomcat关闭时:

Student2将要被钝化

tomcat启动时:

Student2已经活化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值