JavaWeb

19 篇文章 0 订阅

文章目录

JavaWeb 的概念

什么是 JavaWeb

JavaWeb 是指,所有通过 Java 语言编写可以通过浏览器访问的程序的总称叫 JavaWeb
JavaWeb 是基于请求和响应来开发的

什么是请求

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

什么是响应

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

请求和响应的关系

请求和响应是成对出现的,有请求就有响应
在这里插入图片描述

Web 资源的分类

web 资源按实现的技术和呈现的效果的不同,又分为静态资源和动态资源两种。
静态资源: html、css、js、txt、mp4 视频 , jpg 图片
动态资源: jsp 页面、Servlet

TomCat服务器

常用的 Web 服务器

Tomcat:由 Apache 组织提供的一种 Web 服务器,提供对 jsp 和 Servlet 的支持。它是一种轻量级的 javaWeb 容器(服务器),也是当前应用最广的 JavaWeb 服务器(免费)。
Jboss:是一个遵从 JavaEE 规范的、开放源代码的、纯 Java 的 EJB 服务器,它支持所有的 JavaEE 规范(免费)。
GlassFish: 由 Oracle 公司开发的一款 JavaWeb 服务器,是一款强健的商业服务器,达到产品级质量(应用很少)。
Resin:是 CAUCHO 公司的产品,是一个非常流行的服务器,对 servlet 和 JSP 提供了良好的支持,性能也比较优良,resin 自身采用 JAVA 语言开发(收费,应用比较多)。
WebLogic:是 Oracle 公司的产品,是目前应用最广泛的 Web 服务器,支持 JavaEE 规范,
而且不断的完善以适应新的开发要求,适合大型项目(收费,用的不多,适合大公司)。

Tomcat 服务器和 Servlet 版本的对应关系

当前企业常用的版本 7.、8.
Servlet 程序从 2.5 版本是现在世面使用最多的版本(xml 配置)
到了 Servlet3.0 之后。就是注解版本的 Servlet 使用。
在这里插入图片描述

Tomcat 的使用

安装

找到你需要用的 Tomcat 版本对应的 zip 压缩包,解压到需要安装的目录即可。

目录介绍

bin 专门用来存放 Tomcat 服务器的可执行程序
conf 专门用来存放 Tocmat 服务器的配置文件
lib 专门用来存放 Tomcat 服务器的 jar 包
logs 专门用来存放 Tomcat 服务器运行时输出的日记信息
temp 专门用来存放 Tomcdat 运行时产生的临时数据
webapps 专门用来存放部署的 Web 工程。
work 是 Tomcat 工作时的目录,用来存放 Tomcat 运行时 jsp 翻译为 Servlet 的源码,和 Session 钝化的目录。

启动 Tomcat 服务器

找到 Tomcat 目录下的 bin 目录下的 startup.bat 文件,双击,就可以启动 Tomcat 服务器。
如何测试 Tomcat 服务器启动成功???

打开浏览器,在浏览器地址栏中输入以下地址测试:
1、http://localhost:8080
2、http://127.0.0.1:8080
3、http://真实 ip:8080

当出现如下界面,说明 Tomcat 服务器启动成功!!!
在这里插入图片描述

常见的启动失败的情况有

  1. 双击 startup.bat 文件,就会出现一个小黑窗口一闪而来。
    这个时候,失败的原因基本上都是因为没有配置好 JAVA_HOME 环境变量。
    配置 JAVA_HOME 环境变量:
    在这里插入图片描述
    常见的 JAVA_HOME 配置错误有以下几种情况:

一:JAVA_HOME 必须全大写。
二:JAVA_HOME 中间必须是下划线,不是减号-
三:JAVA_HOME 配置的路径只需要配置到 jdk 的安装目录即可。不需要带上 bin

另一种启动 tomcat 服务器的方式

1、打开命令行
2、cd 到 你的 Tomcat 的 bin 目录下
3、敲入启动命令: catalina
在这里插入图片描述

Tomcat 的停止

1、点击 tomcat 服务器窗口的 x 关闭按钮
2、把 Tomcat 服务器窗口置为当前窗口,然后按快捷键 Ctrl+C
3、找到 Tomcat 的 bin 目录下的 shutdown.bat 双击,就可以停止 Tomcat 服务

如何修改 Tomcat 的端口号

找到 Tomcat 目录下的 conf 目录,找到 server.xml 配置文件
在这里插入图片描述

Mysql 默认的端口号是:3306
Tomcat 默认的端口号是:8080

平时上百度:http://www.baidu.com:80
HTTP 协议默认的端口号是:80

如何部暑 web 工程到 Tomcat 中

第一种部署方法:只需要把 web 工程的目录拷贝到 Tomcat 的 webapps 目录下
即可。
1、在 webapps 目录下创建一个目录:
2、写一个index.html
3、如何访问 Tomcat 下的 web 工程。
只需要在浏览器中输入访问地址格式如下:
http://ip:port/工程名/
即可访问到index页面

第二种部署方法:
找到 Tomcat 下的 conf 目录\Catalina\localhost\ 下,创建如下的配置文件:
abc.xml 配置文件内容如下:

<!-- Context 表示一个工程上下文
path 表示工程的访问路径:/abc
docBase 表示你的工程目录在哪里
-->
<Context path="/abc" docBase="E:\book" />

访问这个工程的路径如下:http://ip:port/abc/ 就表示访问 E:\book 目录

IDEA整合Tomcat

File -> Setttings -> Build, Execution, Deployment -> Application Servers
在这里插入图片描述
创建项目时
在这里插入图片描述

Web 工程的目录介

在这里插入图片描述

Servlet

什么是 Servlet

1、Servlet 是 JavaEE 规范之一。规范就是接口
2、Servlet 就 JavaWeb 三大组件之一。三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。
3、Servlet 是运行在服务器上的一个 java 小程序,它可以接收客户端发送过来的请求,并响应数据给客户端。

需要的jar包,这里用的是maven项目,也可以下载对应的jar包导入到项目中

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.1</version>
</dependency>

手动实现 Servlet 程序

1、编写一个类去实现 Servlet 接口
2、实现 service 方法,处理请求,并响应数据

public class HelloServlet implements Servlet {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("3 处理请求和响应");
    }
}

3、到 web.xml 中去配置

<servlet>
   <servlet-name>hello</servlet-name>
    <servlet-class>com.wcy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

web.xml 中的配置出现的错误
常见的错误 1:url-pattern 中配置的路径没有以斜杠打头
常见错误 2:servlet-name 配置的值不存在
常见错误 3:servlet-class 标签的全类名配置错误

url 地址到 Servlet

在这里插入图片描述

Servlet 的生命周期

1、执行 Servlet 构造器方法
2、执行 init 初始化方法
第一、二步,是在第一次访问,的时候创建 Servlet 程序会调用。
3、执行 service 方法
第三步,每次访问都会调用。
4、执行 destroy 销毁方法
第四步,在 web工程被停止时调用

/**
 * Servlet的生命周期探究
 * 通过实现Servlet的方式来实现
 */
public class HelloServlet implements Servlet {

    /**
     * 第一步:当第一次请求这个Servlet会调用构造器,之后不会执行
     */
    public HelloServlet() {
        System.out.println("1 构造器");
    }

    /**
     * 第二步:当第一次请求这个Servlet会调用init初始化,之后不会执行
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("2 初始化");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 第三步:使用service方法来处理request和response
     */
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("3 处理请求和响应");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     * 第四步:当服务器关闭 会调用destroy销毁
     */
    @Override
    public void destroy() {
        System.out.println("4 销毁");
    }
}

GET 和 POST 请求的分发处理

/**
 * 第三步:使用service方法来处理request和response
 */
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    System.out.println("3 处理请求和响应");
    /*
     *   无论发的是什么请求 都是先调用service方法来处理 会导致该方法很臃肿
     *   所以可以做请求的分发处理
     */

    // 获取请求的方法 通过ServletRequest的子类HttpServletRequest获取
    // 获取的方法的类型是大写字母的形式
    HttpServletRequest request = (HttpServletRequest) req;
    String method = request.getMethod();
    if("GET".equals(method)){
        doGet();
    }else if ("POST".equals(method)){
        doPost();
    }

}

public void doPost() {
    System.out.println("执行了POST方法");
}

public void doGet() {
    System.out.println("执行了GET方法");
}

通过继承 HttpServlet 实现 Servlet 程序(常用)

一般在实际项目开发中,都是使用继承 HttpServlet 类的方式去实现 Servlet 程序。
1、编写一个类去继承 HttpServlet 类
2、根据业务需要重写 doGet 或 doPost 方法
3、到 web.xml 中的配置

/**
 * 通过继承Servlet的子类HttpServlet来实现
 */
public class HelloServlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("GET方法");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("POST方法");
    }
}

Servlet类的继承关系

在这里插入图片描述
一般都使用继承GenericServlet和HttpServlet的方式来编写Servlet
最常用的是Httpservlet

ServletConfig

ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类。
Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用。
Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象

ServletConfig 类的三大作用
1、可以获取 Servlet 程序的别名 servlet-name 的值
2、获取初始化参数 init-param
3、获取 ServletContext

web.xml 中的配置:

<!-- servlet 标签给 Tomcat 配置 Servlet 程序 -->
<servlet>
	<!--servlet-name 标签 Servlet 程序起一个别名(一般是类名) -->
	<servlet-name>HelloServlet</servlet-name>
	<!--servlet-class 是 Servlet 程序的全类名-->
	<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
	<!--init-param 是初始化参数-->
	<init-param>
	<!--是参数名-->
	<param-name>username</param-name>
	<!--是参数值-->
	<param-value>root</param-value>
	</init-param>
	<!--init-param 是初始化参数-->
	<init-param>
	<!--是参数名-->
	<param-name>url</param-name>
	<!--是参数值-->
	<param-value>jdbc:mysql://localhost:3306/test</param-value>
	</init-param>
</servlet>
<!--servlet-mapping 标签给 servlet 程序配置访问地址-->
<servlet-mapping>
	<!--servlet-name 标签的作用是告诉服务器,我当前配置的地址给哪个 Servlet 程序使用-->
	<servlet-name>HelloServlet</servlet-name>
	<!--
	url-pattern 标签配置访问地址 <br/>
	/ 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 <br/>
	/hello 表示地址为:http://ip:port/工程路径/hello <br/>
	-->
	<url-pattern>/hello</url-pattern>
</servlet-mapping>

Servlet 中的代码:

@Override
public void init(ServletConfig servletConfig) throws ServletException {
	System.out.println("2 init 初始化方法");
	// 1、可以获取 Servlet 程序的别名 servlet-name 的值
	System.out.println("HelloServlet 程序的别名是:" + servletConfig.getServletName());
	// 2、获取初始化参数 init-param
	System.out.println("初始化参数 username 的值是;" + servletConfig.getInitParameter("username"));
	System.out.println("初始化参数 url 的值是;" + servletConfig.getInitParameter("url"));
	// 3、获取 ServletContext 对象
	System.out.println(servletConfig.getServletContext());
}

如果要重写init方法
在这里插入图片描述

继承HttpServlet可以使用getServletConfig()方法来获取Servlet对象

ServletContext

什么是 ServletContext?
1、ServletContext 是一个接口,它表示 Servlet 上下文对象
2、一个 web 工程,只有一个 ServletContext 对象实例。
3、ServletContext 对象是一个域对象。
4、ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。

什么是域对象?
域对象,是可以像 Map 一样存取数据的对象,叫域对象。
这里的域指的是存取数据的操作范围,整个 web 工程。

类型存数据取数据删除数据
Mapput()get()remove()
域对象setAttribute()getAttribute()removeAttribute()

ServletContext 类的四个作用
1、获取 web.xml 中配置的上下文参数 context-param
2、获取当前的工程路径,格式: /工程路径
3、获取工程部署后在服务器硬盘上的绝对路径
4、像 Map 一样存取数据

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
	// 1、获取 web.xml 中配置的上下文参数 context-param
	ServletContext context = getServletConfig().getServletContext();
	String username = context.getInitParameter("username");
	System.out.println("context-param 参数 username 的值是:" + username);
	System.out.println("context-param 参数 password 的值是:" +
	context.getInitParameter("password"));
	// 2、获取当前的工程路径,格式: /工程路径
	System.out.println( "当前工程路径:" + context.getContextPath() );
	// 3、获取工程部署后在服务器硬盘上的绝对路径
	/**
	* / 斜杠被服务器解析地址为:http://ip:port/工程名/ 映射到 IDEA 代码的 web 目录<br/>
	*/
	System.out.println("工程部署的路径是:" + context.getRealPath("/"));
	System.out.println("工程下 css 目录的绝对路径是:" + context.getRealPath("/css"));
	System.out.println("工程下 imgs 目录 1.jpg 的绝对路径是:" + context.getRealPath("/imgs/1.jpg"));
}
<!--context-param 是上下文参数(它属于整个 web 工程)-->
<context-param>
<param-name>username</param-name>
<param-value>context</param-value>
</context-param>
<!--context-param 是上下文参数(它属于整个 web 工程)-->
<context-param>
<param-name>password</param-name>
<param-value>root</param-value>
</context-param>

ServletContext 像 Map 一样存取数据:
ContextServlet1 代码:

public class ContextServlet1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
	// 获取 ServletContext 对象
	ServletContext context = getServletContext();
	System.out.println(context);
	System.out.println("保存之前: Context1 获取 key1 的值是:"+ context.getAttribute("key1"));
	context.setAttribute("key1", "value1");
	System.out.println("Context1 中获取域数据 key1 的值是:"+ context.getAttribute("key1"));
}

}
ContextServlet2 代码:

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
	IOException {
	ServletContext context = getServletContext();
	System.out.println(context);
	System.out.println("Context2 中获取域数据 key1 的值是:"+ context.getAttribute("key1"));
}

HTTP 协议

什么是 HTTP 协议

什么是协议?
协议是指双方,或多方,相互约定好,大家都需要遵守的规则,叫协议。
所谓 HTTP 协议,就是指,客户端和服务器之间通信时,发送的数据,需要遵守的规则,叫 HTTP 协议。
HTTP 协议中的数据又叫报文。

Request请求的 HTTP 协议格式

客户端给服务器发送数据叫请求。
服务器给客户端回传数据叫响应。

请求又分为 GET 请求,和 POST 请求两种

i. GET 请求
1、请求行
(1) 请求的方式 GET
(2) 请求的资源路径[+?+请求参数]
(3) 请求的协议的版本号 HTTP/1.1

2、请求头
key : value 组成 不同的键值对,表示不同的含义。
在这里插入图片描述

ii. POST 请求
1、请求行
(1) 请求的方式 POST
(2) 请求的资源路径[+?+请求参数]
(3) 请求的协议的版本号 HTTP/1.1

2、请求头

  1. key : value 不同的请求头,有不同的含义
    空行

3、请求体 ===>>> 就是发送给服务器的数据

在这里插入图片描述

iii. 常用请求头的说明
Accept: 表示客户端可以接收的数据类型
Accpet-Languege: 表示客户端可以接收的语言类型
User-Agent: 表示客户端浏览器的信息
Host: 表示请求时的服务器 ip 和端口号
iv. 哪些是 GET 请求,哪些是 POST 请求
GET 请求有哪些:
1、form 标签 method=get
2、a 标签
3、link 标签引入 css
4、Script 标签引入 js 文件
5、img 标签引入图片
6、iframe 引入 html 页面
7、在浏览器地址栏中输入地址后敲回车

POST 请求有哪些:
8、form 标签 method=post
9、ajax请求

Response响应的 HTTP 协议格式

1、响应行
(1) 响应的协议和版本号
(2) 响应状态码
(3) 响应状态描述符

2、响应头
(1) key : value 不同的响应头,有其不同含义
空行

3、响应体 ---->>> 就是回传给客户端的数据
在这里插入图片描述

常用的响应码说明

200 表示请求成功
302 表示请求重定向(明天讲)
404 表示请求服务器已经收到了,但是你要的数据不存在(请求地址错误)
500 表示服务器已经收到请求,但是服务器内部错误(代码错误)

MIME 类型说明

MIME 是 HTTP 协议中数据类型。
MIME 的英文全称是"Multipurpose Internet Mail Extensions" 多功能 Internet 邮件扩充服务。MIME 类型的格式是“大类型/小类型”,并与某一种文件的扩展名相对应。
常见的 MIME 类型:

文件文件后缀MIME 类型
超文本标记语言文本.html , .htmtext/html
普通文本.txttext/plain
RTF 文本.rtfapplication/rtf
GIF 图形.gifimage/gif
JPEG 图形.jpeg,.jpgimage/jpeg
au 声音文件.auaudio/basic
MIDI 音乐文件mid,.midiaudio/midi,audio/x-midi
RealAudio 音乐文件.ra, .ramaudio/x-pn-realaudio
MPEG 文件.mpg,.mpegvideo/mpeg
AVI 文件.avivideo/x-msvideo
GZIP 文件.gzapplication/x-gzip
TAR 文件.tarapplication/x-tar

可以通过ServletContext来获取Mime类型

servletContext.getMimeType(fileName)

HttpServletRequest

HttpServletRequest 类有什么作用。

每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中。
然后传递到 service 方法(doGet 和 doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的
信息。

HttpServletRequest 类的常用方法

getRequestURI() 获取请求的资源路径
getRequestURL()获取请求的统一资源定位符(绝对路径)
getRemoteHost() 获取客户端的 ip 地址
getHeader() 获取请求头
getParameter()获取请求的参数
getParameterValues()获取请求的参数(多个值的时候使用)
getMethod() 获取请求的方式 GET 或 POST
setAttribute(key, value); 设置域数据
getAttribute(key); 获取域数据
getRequestDispatcher() 获取请求转发对象

public class RequestAPIServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
	IOException {
		// i.getRequestURI() 获取请求的资源路径
		System.out.println("URI => " + req.getRequestURI());
		// ii.getRequestURL() 获取请求的统一资源定位符(绝对路径)
		System.out.println("URL => " + req.getRequestURL());
		// iii.getRemoteHost() 获取客户端的 ip 地址
		/*
		* 在 IDEA 中,使用 localhost 访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1
		* 在 IDEA 中,使用 127.0.0.1 访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1
		* 在 IDEA 中,使用 真实 ip 访问时,得到的客户端 ip 地址是 ===>>> 真实的客户端 ip 地址
		*/
		System.out.println("客户端 ip 地址 => " + req.getRemoteHost());
		// iv.getHeader() 获取请求头
		System.out.println("请求头 User-Agent ==>> " + req.getHeader("User-Agent"));
		// vii.getMethod() 获取请求的方式 GET 或 POST
		System.out.println( "请求的方式 ==>> " + req.getMethod() );
	}
}

如何获取请求参数

表单:

<body>
<form action="http://localhost:8080/07_servlet/parameterServlet" method="get">
	用户名:<input type="text" name="username"><br/>
	密码:<input type="password" name="password"><br/>
	兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
	<input type="checkbox" name="hobby" value="java">Java
	<input type="checkbox" name="hobby" value="js">JavaScript<br/>
	<input type="submit">
</form>
</body>

Java 代码:

public class ParameterServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
	IOException {
		// 获取请求参数
		String username = req.getParameter("username");
		String password = req.getParameter("password");
	
		String[] hobby = req.getParameterValues("hobby");
		System.out.println("用户名:" + username);
		System.out.println("密码:" + password);
		System.out.println("兴趣爱好:" + Arrays.asList(hobby));
	}
}

doGet 请求的中文乱码解决:

// 获取请求参数
String username = req.getParameter("username");
//1 先以 iso8859-1 进行编码
//2 再以 utf-8 进行解码
username = new String(username.getBytes("iso-8859-1"), "UTF-8");

d)POST 请求的中文乱码解决

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	// 设置请求体的字符集为 UTF-8,从而解决 post 请求的中文乱码问题
	req.setCharacterEncoding("UTF-8");
	System.out.println("-------------doPost------------");
	// 获取请求参数
	String username = req.getParameter("username");
	String password = req.getParameter("password");
	String[] hobby = req.getParameterValues("hobby");
	System.out.println("用户名:" + username);
	System.out.println("密码:" + password);
	System.out.println("兴趣爱好:" + Arrays.asList(hobby));
}

请求的转发

什么是请求的转发?
请求转发是指,服务器收到请求后,从一次资源跳转到另一个资源的操作叫请求转发。
在这里插入图片描述

Servlet1 代码:

public class Servlet1 extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
	IOException {
		// 获取请求的参数(办事的材料)查看
		String username = req.getParameter("username");
		System.out.println("在 Servlet1(柜台 1)中查看参数(材料):" + username);
		// 给材料 盖一个章,并传递到 Servlet2(柜台 2)去查看
		req.setAttribute("key1","柜台 1 的章");
		// 问路:Servlet2(柜台 2)怎么走
		/*
		* 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到 IDEA 代码的 web 目录
		*/
		RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
		// RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.baidu.com");
		// 走向 Sevlet2(柜台 2)
		requestDispatcher.forward(req,resp);
	}
}

Servlet2 代码:

public class Servlet2 extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
	IOException {
		// 获取请求的参数(办事的材料)查看
		String username = req.getParameter("username");
		System.out.println("在 Servlet2(柜台 2)中查看参数(材料):" + username);
		// 查看 柜台 1 是否有盖章
		Object key1 = req.getAttribute("key1");
		System.out.println("柜台 1 是否有章:" + key1);
		// 处理自己的业务
		System.out.println("Servlet2 处理自己的业务 ");
	}
}

base 标签的作用

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh_CN">
	<head>
		<meta charset="UTF-8">
		<title>Title</title>
		<!--base 标签设置页面相对路径工作时参照的地址
		href 属性就是参数的地址值
		-->
		<base href="http://localhost:8080/07_servlet/a/b/">
	</head>
	<body>
		这是 a 下的 b 下的 c.html 页面<br/>
		<a href="../../index.html">跳回首页</a><br/>
	</body>
</html>

Web 中的相对路径和绝对路径

在 javaWeb 中,路径分为相对路径和绝对路径两种:

相对路径是:
. 表示当前目录
.. 表示上一级目录
资源名 表示当前目录/资源名

绝对路径:
http://ip:port/工程路径/资源路径
在实际开发中,路径都使用绝对路径,而不简单的使用相对路径。
1、绝对路径
2、base+相对

web 中 / 斜杠的不同意义

在 web 中/斜杠 是一种绝对路径。
/斜杠 如果被浏览器解析,得到的地址是:http://ip:port/

<a href="/">斜杠</a>

/斜杠 如果被服务器解析,得到的地址是:http://ip:port/工程路径

例如服务器中的这几种情况

1、<url-pattern>/servlet1</url-pattern>
2、servletContext.getRealPath(“/”)
3、request.getRequestDispatcher(“/”)

特殊情况: response.sendRediect(“/”); 把斜杠发送给浏览器解析。得到 http://ip:port

HttpServletResponse

HttpServletResponse 类的作用

HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息

HttpServletResponse 表示所有响应的信息,我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置

两个输出流

字节流 getOutputStream() 常用于下载(传递二进制数据)
字符流 getWriter();常用于回传字符串(常用)
两个流同时只能使用一个
使用了字节流,就不能再使用字符流,反之亦然,否则就会报错。

如何往客户端回传数据

要求 : 往客户端回传 字符串 数据。

public class ResponseIOServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
	IOException {
		// 要求 : 往客户端回传 字符串 数据。
		PrintWriter writer = resp.getWriter();
		writer.write("response's content!!!");
	}
}

响应的乱码解决

解决响应中文乱码方案一(不推荐使用):

// 设置服务器字符集为 UTF-8
resp.setCharacterEncoding("UTF-8");
// 通过响应头,设置浏览器也使用 UTF-8 字符集
resp.setHeader("Content-Type", "text/html; charset=UTF-8");

解决响应中文乱码方案二(推荐):

此方法一定要在获取流对象之前调用才有效

// 它会同时设置服务器和客户端都使用 UTF-8 字符集,还设置了响应头
// 此方法一定要在获取流对象之前调用才有效
resp.setContentType("text/html; charset=UTF-8");

请求重定向

请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些地址。你去新地址访问。叫请求重定向(因为之前的地址可能已经被废弃)

  • 重定向之后发送的是一个新的请求

在这里插入图片描述

请求重定向的第一种方案:

// 设置响应状态码 302 ,表示重定向,(已搬迁)
resp.setStatus(302);
// 设置响应头,说明 新的地址在哪里
resp.setHeader("Location", "http://localhost:8080");

请求重定向的第二种方案(推荐使用):

resp.sendRedirect("http://localhost:8080")

JSP

什么是 jsp,它有什么用?

jsp 的全换是 java server pages。Java 的服务器页面
jsp 的主要作用是代替 Servlet 程序回传 html 页面的数据
Servlet 程序回传 html 页面数据是一件非常繁锁的事情。开发成本和维护成本都极高

servlet回传html页面:

public class PringHtml extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
	IOException {
		// 通过响应的回传流回传 html 页面数据
		resp.setContentType("text/html; charset=UTF-8");
		PrintWriter writer = resp.getWriter();
		writer.write("<!DOCTYPE html>\r\n");
		writer.write(" <html lang=\"en\">\r\n");
		writer.write(" <head>\r\n");
		writer.write(" <meta charset=\"UTF-8\">\r\n");
		writer.write(" <title>Title</title>\r\n");
		writer.write(" </head>\r\n");
		writer.write(" <body>\r\n");
		writer.write(" 这是 html 页面数据 \r\n");
		writer.write(" </body>\r\n");
		writer.write("</html>\r\n");
		writer.write("\r\n");
	}
}

jsp 回传一个简单 html 页面的代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
	<head>
	<title>Title</title>
	</head>
	<body>
	这是 html 页面数据
	</body>
</html>

jsp 如何访问

jsp 页面和 html 页面一样,都是存放在 web 目录下。访问也跟访问 html 页面一样。

比如:
在 web 目录下有如下的文件:
web 目录
a.html 页面 访问地址是 =======>>>>>>http://ip:port/工程路径/a.html
b.jsp 页面 访问地址是 =======>>>>>> http://ip:port/工程路径/b.jsp

jsp 的本质

jsp 页面本质上是一个 Servlet 程序

当我们第一次访问 jsp 页面的时候。Tomcat 服务器会帮我们把 jsp 页面翻译成为一个 java 源文件。并且对它进行编译成为.class 字节码程序。
我们打开 java 源文件不难发现其里面的内容是:
在这里插入图片描述

我们跟踪原代码发现,HttpJspBase 类。它直接地继承了 HttpServlet 类。也就是说。jsp 翻译出来的 java 类,它间接了继承了 HttpServlet 类。也就是说,翻译出来的是一个 Servlet 程序

在这里插入图片描述

总结:通过翻译的 java 源代码我们就可以得到结果:jsp 就是 Servlet 程序

大家也可以去观察翻译出来的 Servlet 程序的源代码,不难发现。其底层实现,也是通过输出流。把 html 页面数据回传给客户端。

public void _jspService(final javax.servlet.http.HttpServletRequest request, final
javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
	final java.lang.String _jspx_method = request.getMethod();
	if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)
	&& !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
	response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or
	HEAD");
	return;
	}
	final javax.servlet.jsp.PageContext pageContext;
	javax.servlet.http.HttpSession session = null;
	final javax.servlet.ServletContext application;
	final javax.servlet.ServletConfig config;
	javax.servlet.jsp.JspWriter out = null;
	final java.lang.Object page = this;
	javax.servlet.jsp.JspWriter _jspx_out = null;
	javax.servlet.jsp.PageContext _jspx_page_context = null;
	try {
		response.setContentType("text/html;charset=UTF-8");
		pageContext = _jspxFactory.getPageContext(this, request, response,
		null, true, 8192, true);
		_jspx_page_context = pageContext;
		application = pageContext.getServletContext();
		config = pageContext.getServletConfig();
		session = pageContext.getSession();
		out = pageContext.getOut();
		_jspx_out = out;
		out.write("\r\n");
		out.write("\r\n");
		out.write("<html>\r\n");
		out.write("<head>\r\n");
		out.write(" <title>Title</title>\r\n");
		out.write("</head>\r\n");
		out.write("<body>\r\n");
		out.write(" a.jsp 页面\r\n");
		out.write("</body>\r\n");
		out.write("</html>\r\n");
	} catch (java.lang.Throwable t) {
	if (!(t instanceof javax.servlet.jsp.SkipPageException)){
		out = _jspx_out;
	if (out != null && out.getBufferSize() != 0)
	try {
		if (response.isCommitted()) {
			out.flush();
		} else {
			out.clearBuffer();
		}
	} catch (java.io.IOException e) {}
		if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
		else throw new ServletException(t);
	}
	} finally {
		_jspxFactory.releasePageContext(_jspx_page_context);
	}
}

jsp 的三种语法

jsp 头部的 page 指令

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

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

i.language属性 表示 jsp 翻译后是什么语言文件。暂时只支持 java。
ii. contentType 属性 表示 jsp 返回的数据类型是什么。也是源码中 response.setContentType()参数值
iii. pageEncoding 属性 表示当前 jsp 页面文件本身的字符集。
iv. import属性 跟 java 源代码中一样。用于导包,导类。

两个属性是给 out 输出流使用
v. autoFlush 属性 设置当 out 输出流缓冲区满了之后,是否自动刷新冲级区。默认值是 true。
vi. buffer属性 设置 out 缓冲区的大小。默认是 8kb

vii. errorPage 属性 设置当 jsp 页面运行时出错,自动跳转去的错误页面路径。

errorPage 表示错误后自动跳转去的路径 
这个路径一般都是以斜杠打头,它表示请求地址为 http://ip:port/工程路径/
映射到代码的 Web 目录

viii. isErrorPage 属性 设置当前 jsp 页面是否是错误信息页面。默认是 false。如果是 true 可以
获取异常信息。
ix. session属性 设置访问当前 jsp 页面,是否会创建 HttpSession 对象。默认是 true。
x. extends 属性 设置 jsp 翻译出来的 java 类默认继承

jsp声明脚本(极少使用)

声明脚本的格式是: <%! 声明 java 代码 %>
作用:可以给 jsp 翻译出来的 java 类定义属性和方法甚至是静态代码块。内部类等。

1、声明类属性
2、声明 static 静态代码块
3、声明类方法
4、声明内部类
代码示例:

<%--1、声明类属性--%>
<%!
	private Integer id;
	private String name;
	private static Map<String,Object> map;
%>
<%--2、声明 static 静态代码块--%>
<%!
static {
	map = new HashMap<String,Object>();
	map.put("key1", "value1");
	map.put("key2", "value2");
	map.put("key3", "value3");
}
%>
<%--3、声明类方法--%>
<%!
	public int abc(){
	return 12;
	}
%>
<%--4、声明内部类--%>
<%!
public static class A {
	private Integer id = 12;
	private String abc = "abc";
}
%>

声明脚本代码翻译对照
在这里插入图片描述

表达式脚本(常用)

表达式脚本的格式是:<%=表达式%>
表达式脚本的作用是:的 jsp 页面上输出数据。

表达式脚本的特点:
1、所有的表达式脚本都会被翻译到_jspService() 方法中
2、表达式脚本都会被翻译成为 out.print()输出到页面上
3、由于表达式脚本翻译的内容都在_jspService() 方法中,所以_jspService()方法中的对象都可以直接使用。
4、表达式脚本中的表达式不能以分号结束

  1. 输出整型
  2. 输出浮点型
  3. 输出字符串
  4. 输出对象
    示例代码:
<%=12 %> <br>
<%=12.12 %> <br>
<%="我是字符串" %> <br>
<%=map%> <br>
<%=request.getParameter("username")%>

代码脚本

代码脚本的格式是:
<% java 语句 %>
代码脚本的作用是:可以在 jsp 页面中,编写我们自己需要的功能(写的是 java 语句)。

代码脚本的特点是:
1、代码脚本翻译之后都在_jspService 方法中
2、代码脚本由于翻译到_jspService()方法中,所以在_jspService()方法中的现有对象都可以直接使用。
3、还可以由多个代码脚本块组合完成一个完整的 java 语句。
4、代码脚本还可以和表达式脚本一起组合使用,在 jsp 页面上输出数据

  1. 代码脚本----if 语句
  2. 代码脚本----for 循环语句
  3. 翻译后 java 文件中_jspService 方法内的代码都可以写
    示例代码:
<%--练习:--%>
<%--1.代码脚本----if 语句--%>
<%
int i = 13 ;
if (i == 12) {
%>

<h1>国哥好帅</h1>

<%
} else {
%>
<h1>国哥又骗人了!</h1>

<%
}
%>
<br>


<%--2.代码脚本----for 循环语句--%>
<table border="1" cellspacing="0">
<%
for (int j = 0; j < 10; j++) {
%>
<tr>
<td><%=j + 1%></td>
</tr>
<%
}
%>
</table>

<%--3.翻译后 java 文件中_jspService 方法内的代码都可以写--%>
<%
String username = request.getParameter("username");
System.out.println("用户名的请求参数值是:" + username);
%>

jsp 中的三种注释

i. html 注释

<!-- 这是 html 注释 -->

html 注释会被翻译到 java 源代码中。在_jspService 方法里,以 out.writer 输出到客户端。
ii. java 注释

<%
// 单行 java 注释
/* 多行 java 注释 */
%>

java 注释会被翻译到 java 源代码中。

iii. jsp 注释

<%-- 这是 jsp 注释 --%>

jsp 注释可以注掉,jsp 页面中所有代码

jsp 九大内置对象

jsp 中的内置对象,是指 Tomcat 在翻译 jsp 页面成为 Servlet 源代码后,内部提供的九大对象,叫内置对象
在这里插入图片描述

jsp 四大域对象

四个域对象分别是:
pageContext (PageContextImpl 类) 当前 jsp 页面范围内有效
request (HttpServletRequest 类)、 一次请求内有效
session (HttpSession 类)、 一个会话范围内有效(打开浏览器访问服务器,直到关闭浏览器)
application (ServletContext 类) 整个 web 工程范围内都有效(只要 web 工程不停止,数据都在)

域对象是可以像 Map 一样存取数据的对象。四个域对象功能一样。不同的是它们对数据的存取范围。
虽然四个域对象都可以存取数据。在使用上它们是有优先顺序的。

四个域在使用的时候,优先顺序分别是,他们从小到大的范围的顺序。
pageContext ====>>> request ====>>> session ====>>> application

scope.jsp 页面

<body>
<h1>scope.jsp 页面</h1>
<%
	// 往四个域中都分别保存了数据
	pageContext.setAttribute("key", "pageContext");
	request.setAttribute("key", "request");
	session.setAttribute("key", "session");
	application.setAttribute("key", "application");
%>
	pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br>
	request 域是否有值:<%=request.getAttribute("key")%> <br>
	session 域是否有值:<%=session.getAttribute("key")%> <br>
	application 域是否有值:<%=application.getAttribute("key")%> <br>
<%
	request.getRequestDispatcher("/scope2.jsp").forward(request,response);
%>
</body>

scope2.jsp 页面

<body>
<h1>scope2.jsp 页面</h1>
	pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br>
	request 域是否有值:<%=request.getAttribute("key")%> <br>
	session 域是否有值:<%=session.getAttribute("key")%> <br>
	application 域是否有值:<%=application.getAttribute("key")%> <br>
</body>

out 输出和 response.getWriter 输出的区别

response 中表示响应,我们经常用于设置返回给客户端的内容(输出)
out 也是给用户做输出使用的。
在这里插入图片描述

由于 jsp 翻译之后,底层源代码都是使用 out 来进行输出,所以一般情况下,我们在 jsp 页面中统一使用 out 来进行输出。避免打乱页面输出内容的顺序。
out.write()输出字符串没有问题
out.print() 输出任意数据都没有问题(都转换成为字符串后调用的 write 输出)

深入源码,浅出结论:在 jsp 页面中,可以统一使用 out.print()来进行输

jsp 的常用标签

jsp 静态包含

示例说明:
<%@ include file=""%>就是静态包含
file 属性指定你要包含的 jsp 页面的路径
地址中第一个斜杠 / 表示为 http://ip:port/工程路径/ 映射到代码的 web 目录

静态包含的特点
1、静态包含不会翻译被包含的 jsp 页面。
2、静态包含其实是把被包含的 jsp 页面的代码拷贝到包含的位置执行输出。

<%@ include file="/include/footer.jsp"%>

jsp 动态包含

示例说明:

<jsp:include page=""></jsp:include> 这是动态包含
page 属性是指定你要包含的 jsp 页面的路径
动态包含也可以像静态包含一样。把被包含的内容执行输出到包含位置
动态包含的特点
1、动态包含会把包含的 jsp 页面也翻译成为 java 代码
2、动态包含底层代码使用如下代码去调用被包含的 jsp 页面执行输出。
JspRuntimeLibrary.include(request, response, “/include/footer.jsp”, out, false);
3、动态包含,还可以传递参数

<jsp:include page="/include/footer.jsp">
	<jsp:param name="username" value="bbj"/>
	<jsp:param name="password" value="root"/>
</jsp:include>

动态包含的底层原理:
在这里插入图片描述

jsp 标签-转发

示例说明:

<jsp:forward page=""></jsp:forward> 是请求转发标签,它的功能就是请求转发
page 属性设置请求转发的路径

<jsp:forward page="/scope2.jsp"></jsp:forward>

Listener 监听器

什么是Listener监听器

1、Listener 监听器它是 JavaWeb 的三大组件之一。
JavaWeb 的三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监
听器。
2、Listener 它是 JavaEE 的规范,就是接口
3、监听器的作用是,监听某种事物的变化。然后通过回调函数,反馈给客户(程序)去做一些相应的处理。

当被监听的事物发生了某个行为时,做相应的处理

ServletContextListener 监听器

ServletContextListener 它可以监听 ServletContext 对象的创建和销毁。
ServletContext 对象在 web 工程启动的时候创建,在 web 工程停止的时候销毁。
监听到创建和销毁之后都会分别调用 ServletContextListener 监听器的方法反馈。
两个方法分别是:

public interface ServletContextListener extends EventListener {
	/**
	* 在 ServletContext 对象创建之后马上调用,做初始化
	*/
	public void contextInitialized(ServletContextEvent sce);
	/**
	* 在 ServletContext 对象销毁之后调用
	*/
	public void contextDestroyed(ServletContextEvent sce);
}

如何使用 ServletContextListener 监听器监听 ServletContext 对象。

使用步骤如下:
1、编写一个类去实现 ServletContextListener
2、实现其两个回调方法
3、到 web.xml 中去配置监听器

监听器实现类:

public class MyServletContextListenerImpl implements ServletContextListener {
	@Override
	public void contextInitialized(ServletContextEvent sce) {
		System.out.println("ServletContext 对象被创建了");
	}
	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		System.out.println("ServletContext 对象被销毁了");
	}
}

web.xml 中的配置:

<!--配置监听器-->
<listener>
	<listener-class>com.atguigu.listener.MyServletContextListenerImpl</listener-class>
</listener>

EL表达式

什么是 EL 表达式

EL 表达式的全称是:Expression Language,是表达式语言。
EL 表达式的什么作用:EL 表达式主要是代替 jsp 页面中的表达式脚本在 jsp 页面中进行数据的输出。
因为 EL 表达式在输出数据的时候,要比 jsp 的表达式脚本要简洁很多。

<body>
<%
	request.setAttribute("key","值");
%>
表达式脚本输出 key 的值是:
<%=request.getAttribute("key1")%><br/>

EL 表达式输出 key 的值是:${key1}
</body>

EL 表达式的格式是:${表达式}

EL 表达式在输出 null 值的时候,输出的是空串。
jsp 表达式脚本输出 null 值的时候,输出的是 null

EL 表达式搜索域数据的顺序

EL 表达式主要是在 jsp 页面中输出数据
主要是输出域对象中的数据

当四个域中都有相同的 key 的数据的时候,EL 表达式会按照四个域的从小到大的顺序去进行搜索,找到就输出
pageContext->request->session->application

<body>
<%
	//往四个域中都保存了相同的 key 的数据, 会从最小的范围pageContext开始使用
	request.setAttribute("key", "request");
	session.setAttribute("key", "session");
	application.setAttribute("key", "application");
	pageContext.setAttribute("key", "pageContext");
%>
	${ key }
</body>

EL 表达式输出

输出 Person 类中普通属性,数组属性。list 集合属性和 map 集合属性
Person 类

public class Person {
	// i.需求——输出 Person 类中普通属性,数组属性。list 集合属性和 map 集合属性。
	private String name;
	private String[] phones;
	private List<String> cities;
	private Map<String,Object> map;
	//getter and setter...
}

输出的代码:

Person person = new Person();
person.setName("国哥好帅!");
person.setPhones(new String[]{"18610541354","18688886666","18699998888"});
List<String> cities = new ArrayList<String>();
cities.add("北京");
cities.add("上海");
cities.add("深圳");
person.setCities(cities);
Map<String,Object>map = new HashMap<>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
person.setMap(map);
pageContext.setAttribute("p", person);

输出 Person:${ p }
输出 Person 的 name 属性:${p.name}
输出 Person 的 pnones 数组属性值:${p.phones[2]}
输出 Person 的 cities 集合中的元素值:${p.cities}
输出 Person 的 List 集合中个别元素值:${p.cities[2]}
输出 Person 的 Map 集合: ${p.map}
输出 Person 的 Map 集合中某个 key 的值: ${p.map.key3}
输出 Person 的 age 属性:${p.age}

注意:需要get和set方法

EL 表达式运算

语法:${ 运算表达式 }

关系运算
在这里插入图片描述
逻辑运算
在这里插入图片描述
算数运算
在这里插入图片描述
empty运算

${ empty obj}

empty 运算可以判断一个数据是否为空,如果为空,则输出 true,不为空输出 false

以下几种情况为空:

  • 值为 null 值的时候,为空
  • 值为空串的时候,为空
  • 值是 Object 类型数组,长度为零的时候
  • list 集合,元素个数为零
  • map 集合,元素个数为零
request.setAttribute("emptyNull", null);
request.setAttribute("emptyStr", "");
request.setAttribute("emptyArr", new Object[]{});
List<String> list = new ArrayList<>();
request.setAttribute("emptyList", list);
Map<String,Object> map = new HashMap<String, Object>();
request.setAttribute("emptyMap", map);

${ empty emptyNull } <br/>
${ empty emptyStr } <br/>
${ empty emptyArr } <br/>
${ empty emptyList } <br/>
${ empty emptyMap } <br/>

三元运算
表达式 1?表达式 2:表达式 3
如果表达式 1 的值为真,返回表达式 2 的值,如果表达式 1 的值为假,返回表达式 3 的值

“.”点运算 和 [] 中括号运算符
.点运算,可以输出 Bean 对象中某个属性的值
[]中括号运算,可以输出有序集合中某个元素的值

并且[]中括号运算,还可以输出 map 集合中 key 里含有特殊字符的 key 的值

<body>
<%
	Map<String,Object> map = new HashMap<String, Object>();
	map.put("a.a.a", "aaaValue");
	map.put("b+b+b", "bbbValue");
	map.put("c-c-c", "cccValue");
	request.setAttribute("map", map);
%>
	<!--特殊字符的取值-->
	${ map['a.a.a'] } <br>
	${ map["b+b+b"] } <br>
	${ map['c-c-c'] } <br>
</body>

EL 表达式的 11 个隐含对象

变量类型作用
pageContextPageContextImpl它可以获取 jsp 中的九大内置对象
pageScopeMap<String,Object>它可以获取 pageContext 域中的数据
requestScopeMap<String,Object>它可以获取 Request 域中的数据
sessionScopeMap<String,Object>它可以获取 Session 域中的数据
applicationScopeMap<String,Object>它可以获取 ServletContext 域中的数据
paramMap<String,String>它可以获取请求参数的值
paramValuesMap<String,String[]>它也可以获取请求参数的值,获取多个值的时候使用。
headerMap<String,String>它可以获取请求头的信息
headerValuesMap<String,String[]>它可以获取请求头的信息,它可以获取多个值的情况
cookieMap<String,Cookie>它可以获取当前请求的 Cookie 信息
initParamMap<String,String>它可以获取在 web.xml 中配置的<context-param>上下文参数

EL 获取四个特定域中的属性

pageScope ====== pageContext 域
requestScope ====== Request 域
sessionScope ====== Session 域
applicationScope ====== ServletContext 域

<body>
	<%
	pageContext.setAttribute("key1", "pageContext1");
	pageContext.setAttribute("key2", "pageContext2");
	request.setAttribute("key2", "request");
	session.setAttribute("key2", "session");
	application.setAttribute("key2", "application");
	%>
	${ applicationScope.key2 }
</body>

pageContext 对象

  1. 协议:
  2. 服务器 ip:
  3. 服务器端口:
  4. 获取工程路径:
  5. 获取请求方法:
  6. 获取客户端 ip 地址:
  7. 获取会话的 id 编号:
<body>
<%--
	request.getScheme() 它可以获取请求的协议
	request.getServerName() 获取请求的服务器 ip 或域名
	request.getServerPort() 获取请求的服务器端口号
	getContextPath() 获取当前工程路径
	request.getMethod() 获取请求的方式(GET 或 POST)
	request.getRemoteHost() 获取客户端的 ip 地址
	session.getId() 获取会话的唯一标识
--%>
<%
	pageContext.setAttribute("req", request);
%>

	1.协议: ${ req.scheme }<br>
	2.服务器 ip:${ pageContext.request.serverName }<br>
	3.服务器端口:${ pageContext.request.serverPort }<br>
	4.获取工程路径:${ pageContext.request.contextPath }<br>
	5.获取请求方法:${ pageContext.request.method }<br>
	6.获取客户端 ip 地址:${ pageContext.request.remoteHost }<br>
	7.获取会话的 id 编号:${ pageContext.session.id }<br>

</body>

EL 表达式其他隐含对象的使用

param Map<String,String> 它可以获取请求参数的值
paramValues Map<String,String[]> 它也可以获取请求参数的值,获取多个值的时候使用
示例代码:

输出请求参数 username 的值:${ param.username } <br>
输出请求参数 password 的值:${ param.password } <br>
输出请求参数 username 的值:${ paramValues.username[0] } <br>
输出请求参数 hobby 的值:${ paramValues.hobby[0] } <br>
输出请求参数 hobby 的值:${ paramValues.hobby[1] } <br>

请求地址:
http://localhost:8080/09_EL_JSTL/other_el_obj.jsp?username=wzg168&password=666666&hobby=java&hobby=cpp

header Map<String,String> 它可以获取请求头的信息
headerValues Map<String,String[]> 它可以获取请求头的信息,它可以获取多个值的情况

示例代码:

输出请求头【User-Agent】的值:${ header['User-Agent'] } <br>
输出请求头【Connection】的值:${ header.Connection } <br>
输出请求头【User-Agent】的值:${ headerValues['User-Agent'][0] } <br>

cookie Map<String,Cookie> 它可以获取当前请求的 Cookie 信息
示例代码:

获取 Cookie 的名称:${ cookie.JSESSIONID.name } <br>
获取 Cookie 的值:${ cookie.JSESSIONID.value } <br>

initParam Map<String,String> 它可以获取在 web.xml 中配置的上下文参数
web.xml 中的配置:

<context-param>
	<param-name>username</param-name>
	<param-value>root</param-value>
</context-param>
<context-param>
	<param-name>url</param-name>
	<param-value>jdbc:mysql:///test</param-value>
</context-param>

示例代码:

输出username 的值:${ initParam.username } <br>
输出url 的值:${ initParam.url } <br>

JSTL标签库

JSTL 标签库 全称是指 JSP Standard Tag Library JSP 标准标签库。是一个不断完善的开放源代码的 JSP 标
签库。
EL 表达式主要是为了替换 jsp 中的表达式脚本,而标签库则是为了替换代码脚本。这样使得整个 jsp 页面
变得更佳简洁。

JSTL 由五个不同功能的标签库组成
在这里插入图片描述
在 jsp 标签库中使用 taglib 指令引入标签库

导入依赖包

<dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>jstl-api</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

jsp页面中

CORE 标签库 只需要这个即可
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
XML 标签库
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
FMT 标签库
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
SQL 标签库
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
FUNCTIONS 标签库
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

core 核心库

<c:set />

作用:set 标签可以往域中保存数据

作用:set 标签可以往域中保存数据
等同于域对象.setAttribute(key,value);

  • scope 属性设置保存到哪个域

    • page 表示 PageContext 域(默认值)
    • request 表示 Request 域
    • session 表示 Session 域
    • application 表示 ServletContext 域
  • var 属性设置 key 是多少

  • value 属性设置值

保存之前:${ sessionScope.abc } <br>
<c:set scope="session" var="abc" value="abcValue"/>
保存之后:${ sessionScope.abc } <br>

<c:if />

if 标签用来做 if 判断。

if 标签用来做 if 判断。
test 属性表示判断的条件(使用 EL 表达式输出)

<c:if test="${ 12 == 12 }">
	<h1>12 等于 12</h1>
</c:if>
<c:if test="${ 12 != 12 }">
	<h1>12 不等于 12</h1>
</c:if>

<c:choose> <c:when> <c:otherwise>

作用:多路判断。跟 switch … case … default 非常接近

choose 标签开始选择判断
when 标签表示每一种判断情况
test 属性表示当前这种判断情况的值
otherwise 标签表示剩下的情况

使用时需要注意的点:
1、标签里不能使用 html 注释,要使用 jsp 注释
2、when 标签的父标签一定要是 choose 标签
3、otherwize里可以继续使用choose…when…otherwize

<%
request.setAttribute("height", 180);
%>

<c:choose>
	<%-- 这是 html 注释 --%>
	<c:when test="${ requestScope.height > 190 }">
		<h2>小巨人</h2>
	</c:when>
	<c:when test="${ requestScope.height > 180 }">
		<h2>很高</h2>
	</c:when>
		<c:when test="${ requestScope.height > 170 }">
	<h2>还可以</h2>
	</c:when>
	<c:otherwise>
		<c:choose>
			<c:when test="${requestScope.height > 160}">
				<h3>大于 160</h3>
			</c:when>
			<c:when test="${requestScope.height > 150}">
				<h3>大于 150</h3>
			</c:when>
			<c:when test="${requestScope.height > 140}">
				<h3>大于 140</h3>
			</c:when>
			<c:otherwise>
				其他小于 140
			</c:otherwise>
		</c:choose>
	</c:otherwise>
</c:choose>

<c:forEach />

作用:遍历输出使用

普通遍历

<%--1.遍历 1 到 10,输出
	begin 属性设置开始的索引
	end 属性设置结束的索引
	var 属性表示循环的变量(也是当前正在遍历到的数据)
	for (int i = 1; i < 10; i++)
--%>
<table border="1">
	<c:forEach begin="1" end="10" var="i">
		<tr>
		<td>第${i}行</td>
		</tr>
	</c:forEach>
</table>

数组遍历

<%-- 2.遍历 Object 数组
	for (Object item: arr)
	items 表示遍历的数据源(遍历的集合)
	var 表示当前遍历到的数据
--%>
<%
	request.setAttribute("arr", new String[]{"18610541354","18688886666","18699998888"});
%>
	<c:forEach items="${ requestScope.arr }" var="item">
	${ item } <br>
	</c:forEach>

遍历 Map 集合

<%
	Map<String,Object> map = new HashMap<String, Object>();
	map.put("key1", "value1");
	map.put("key2", "value2");
	map.put("key3", "value3");
	// for ( Map.Entry<String,Object> entry : map.entrySet()) {
	// }
	request.setAttribute("map", map);
%>
	<c:forEach items="${ requestScope.map }" var="entry">
		<h1>${entry.key} = ${entry.value}</h1>
	</c:forEach>

varStatus 属性表示当前遍历到的数据的状态

<%
    request.setAttribute("list", new String[]{"A","B","C"});
%>
<:c:forEach items="${list}" var="i" varStatus="status">
    ${i}
    ${status.index}
</:c:forEach>

在这里插入图片描述

文件的上传和下载

文件上传的条件

  • 要有一个 form 表单,method=post 请求
  • form 表单的 encType 属性值必须为 multipart/form-data
  • 在 form 表单中使用 <input type=file> 添加上传的文件
  • 编写服务器代码(Servlet 程序)接收,处理上传的数据

encType=multipart/form-data 表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼
接,然后以二进制流的形式发送给服务器

文件上传的HTTP 协议说明

在这里插入图片描述

commons-fileupload常用 API 介绍说明

通常文件的上传和下载我们使用现有的工具来完成
commons-fileupload.jar 需要依赖 commons-io.jar 这个包,所以两个包我们都要引入

<dependency>
   <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>1.4</version>
</dependency>

常用的API
ServletFileUpload 类,用于解析上传的数据。
FileItem 类,表示每一个表单项。

boolean ServletFileUpload.isMultipartContent(HttpServletRequest request)
判断当前上传的数据格式是否是多段的格式。

public List<FileItem> parseRequest(HttpServletRequest request)
解析上传的数据
boolean FileItem.isFormField()
判断当前这个表单项,是否是普通的表单项。还是上传的文件类型。
true 表示普通类型的表单项
false 表示上传的文件类型

String FileItem.getFieldName()
获取表单项的 name 属性值
String FileItem.getString()
获取当前表单项的值。
String FileItem.getName()
获取上传的文件名
void FileItem.write( file )
将上传的文件写到 参数 file 所指向抽硬盘位置

使用组件进行文件上传

<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
    <input type="text" name="username">
    <input type="file" name="avatar">
    <input type="submit" value="提交">
</form>
public class FileUploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 先判断上传的数据是否为多段的数据
        if (!ServletFileUpload.isMultipartContent(req)) {
            return;
        }
        // 创建FileItemFactory实现类
        FileItemFactory factory = new DiskFileItemFactory();
        // 创建用于解析文件上传的类
        ServletFileUpload fileUpload = new ServletFileUpload(factory);
        try {
            // 获取表单项
            List<FileItem> fileItems = fileUpload.parseRequest(req);
            // 遍历
            for (FileItem fileItem : fileItems) {
                // 如果是普通表单项
                if (fileItem.isFormField()) {
                    String fieldName = fileItem.getFieldName();
                    String string = fileItem.getString("UTF-8");
                    System.out.println(fieldName + "=" + string);
                // 如果是文件表单项
                } else {
                    // 解决乱码
                    String name = new String(fileItem.getName().getBytes(), StandardCharsets.UTF_8);
                    System.out.println("fileName=" + name);
                    // 写入磁盘
                    fileItem.write(new File("e:\\" + name));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

文件下载

response.getOutputStream()
获取输出流

servletContext.getResourceAsStream()
获取资源将其转为输入流

servletContext.getMimeType()
根据文件名获取Mime类型

response.setContentType()
设置响应内容格式

response.setHeader("Content-Disposition", "attachment; fileName="+fileName)
这个响应头告诉浏览器。这是需要下载的。而 attachment 表示附件,也就是下载的一个文件。fileName=后面,表示下载的文件名

但是如果我们要下载的文件是中文名的话,下载无法正确显示出正确的中文名。
原因是在响应头中,不能包含有中文字符,只能包含 ASCII 码

public class FileDownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 文件名
        String fileName = "ASCII码表I.jpg";
        ServletContext servletContext = getServletContext();
        // 获取文件流
        InputStream in = servletContext.getResourceAsStream("/" + fileName);
        ServletOutputStream out = resp.getOutputStream();
        // 获取Mime类型 并设置到Content-Type中
        resp.setContentType(servletContext.getMimeType(fileName));
        // 设置响应头告诉用户这是需要下载的 中文的话需要使用URL编码
        resp.setHeader("Content-Disposition", "attachment; fileName="+ URLEncoder.encode(fileName, "UTF-8"));
        // 将文件的输入流使用response的输出流输出
        IOUtils.copy(in, out);
    }
}

URLEncoder 解决 IE 和谷歌浏览器的附件中文乱码

如果客户端浏览器是 IE 浏览器 或者 是谷歌浏览器。我们需要使用 URLEncoder 类先对中文名进行 UTF-8 的编码操作。

因为 IE 浏览器和谷歌浏览器收到含有编码后的字符串后会以 UTF-8 字符集进行解码显示。

// 把中文名进行 UTF-8 编码操作。
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");
// 然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition", str);

BASE64 编解码 解决 火狐浏览器的附件中文乱码

如果客户端浏览器是火狐浏览器。 那么我们需要对中文名进行 BASE64 的编码操作。
这时候需要把请求头 Content-Disposition: attachment; filename=中文名
编码成为:Content-Disposition: attachment; filename==?charset?B?xxxxx?=

BASE64 编解码操作:
因为火狐使用的是 BASE64 的编解码方式还原响应中的汉字。所以需要使用 BASE64Encoder 类进行编码操作。

// 使用下面的格式进行 BASE64 编码后
String str = "attachment; fileName=" + "=?utf-8?B?"
+ new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";
// 设置到响应头中
response.setHeader("Content-Disposition", str);

那么我们如何解决上面两种不同编解码方式呢。我们只需要通过判断请求头中 User-Agent这个请求头携带过来的浏览器信息即可判断出是什么浏览器。
如下:

String ua = request.getHeader("User-Agent");
// 判断是否是火狐浏览器
if (ua.contains("Firefox")) {
	// 使用下面的格式进行 BASE64 编码后
	String str = "attachment; fileName=" + "=?utf-8?B?"
	+ new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";
	// 设置到响应头中
	response.setHeader("Content-Disposition", str);
} else {
	// 把中文名进行 UTF-8 编码操作。
	String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");
	// 然后把编码后的字符串设置到响应头中
	response.setHeader("Content-Disposition", str);
}

Cookie

什么是 Cookie

1、Cookie 翻译过来是饼干的意思。
2、Cookie 是服务器通知客户端保存键值对的一种技术。
3、客户端有了 Cookie 后,每次请求都发送给服务器。
4、每个 Cookie 的大小不能超过4kb

如何创建cookie

如何创建 Cookie
在这里插入图片描述

protected void createCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	//1 创建 Cookie 对象
	Cookie cookie = new Cookie("key4", "value4");
	//2 通知客户端保存 Cookie 必须要这一步 否则客户端无法知道这个cookie
	resp.addCookie(cookie);
	//1 创建 Cookie 对象
	Cookie cookie1 = new Cookie("key5", "value5");
	//2 通知客户端保存 Cookie
	resp.addCookie(cookie1);
	resp.getWriter().write("Cookie 创建成功");
}

如何获取cookie

服务器如何获取 Cookie
服务器获取客户端的 Cookie 只需要一行代码:req.getCookies()
在这里插入图片描述

Cookie 的工具类:

public class CookieUtils {
	/**
	* 查找指定名称的 Cookie 对象
	* @param name
	* @param cookies
	* @return
	*/
	public static Cookie findCookie(String name , Cookie[] cookies){
		if (name == null || cookies == null || cookies.length == 0) {
			return null;
		}
		for (Cookie cookie : cookies) {
			if (name.equals(cookie.getName())) {
			return cookie;
			}
		}
		return null;
	}
}

Servlet 程序中的代码:

protected void getCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	// 获取客户端传过来的全部cookie
	Cookie[] cookies = req.getCookies();
	for (Cookie cookie : cookies) {
	// getName 方法返回 Cookie 的 key 名
	// getValue 方法返回 Cookie 的 value 值
	resp.getWriter().write("Cookie[" + cookie.getName() + "=" + cookie.getValue() + "] <br/>");
	}
	
	// 查找指定的名称的cookie
	Cookie iWantCookie = CookieUtils.findCookie("key1", cookies);
	
	// 如果不等于 null,说明赋过值,也就是找到了需要的 Cookie
	if (iWantCookie != null) {
		resp.getWriter().write("找到了需要的 Cookie");
	}
}

Cookie 值的修改

方案一:
1、先创建一个要修改的同名(指的就是 key)的 Cookie 对象
2、在构造器,同时赋于新的 Cookie 值。
3、调用 response.addCookie( Cookie );

// 方案一:
// 1、先创建一个要修改的同名的 Cookie 对象
// 2、在构造器,同时赋于新的 Cookie 值。
Cookie cookie = new Cookie("key1","newValue1");
// 3、调用 response.addCookie( Cookie ); 通知 客户端 保存修改
resp.addCookie(cookie);

方案二:
1、先查找到需要修改的 Cookie 对象
2、调用 setValue()方法赋于新的 Cookie 值。
3、调用 response.addCookie()通知客户端保存修改

// 方案二:
// 1、先查找到需要修改的 Cookie 对象
Cookie cookie = CookieUtils.findCookie("key2", req.getCookies());
if (cookie != null) {
	// 2、调用 setValue()方法赋于新的 Cookie 值。
	cookie.setValue("newValue2");
	// 3、调用 response.addCookie()通知客户端保存修改
	resp.addCookie(cookie);
}

Cookie 生命控制

Cookie 的生命控制指的是如何管理 Cookie 什么时候被销毁(删除)
setMaxAge()

  • 正数,表示在指定的秒数后过期
  • 负数,表示浏览器一关,Cookie 就会被删除(默认值是-1)
  • 零,表示马上删除 Cookie
/**
* 设置存活 1 个小时的 Cooie
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void life3600(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	Cookie cookie = new Cookie("life3600", "life3600");
	cookie.setMaxAge(60 * 60); // 设置 Cookie 一小时之后被删除。无效
	resp.addCookie(cookie);
	resp.getWriter().write("已经创建了一个存活一小时的 Cookie");
}

/**
* 马上删除一个 Cookie
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void deleteNow(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	// 先找到你要删除的 Cookie 对象
	Cookie cookie = CookieUtils.findCookie("key4", req.getCookies());
	if (cookie != null) {
		// 调用 setMaxAge(0);
		cookie.setMaxAge(0); // 表示马上删除,都不需要等待浏览器关闭
		// 调用 response.addCookie(cookie);
		resp.addCookie(cookie);
		resp.getWriter().write("key4 的 Cookie 已经被删除");
	}
}

/**
* 默认的会话级别的 Cookie
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void defaultLife(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	Cookie cookie = new Cookie("defalutLife","defaultLife");
	cookie.setMaxAge(-1);//设置存活时间
	resp.addCookie(cookie);
}

Cookie 有效路径 Path 的设置

Cookie 的 path 属性可以有效的过滤哪些 Cookie 可以发送给服务器,哪些不发。
path 属性是通过请求的地址来进行有效的过滤
通过cookie.setPath()来设置path属性

例如:
CookieA path=/工程路径
CookieB path=/工程路径/abc

请求地址如下:
http://ip:port/工程路径/a.html
CookieA 发送
CookieB 不发送

http://ip:port/工程路径/abc/a.html
CookieA 发送
CookieB 发送

只要path属性能够完全的包含在请求路径中就可以发送

protected void testPath(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	Cookie cookie = new Cookie("path1", "path1");
	// getContextPath() ===>>>> 得到工程路径
	cookie.setPath( req.getContextPath() + "/abc" ); // ===>>>> /工程路径/abc
	resp.addCookie(cookie);
	resp.getWriter().write("创建了一个带有 Path 路径的 Cookie");
	}

cookie保存用户名

通过cookie来实现免用户名登陆
在这里插入图片描述

public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        //登陆成功就保存用户名cookie
        if ("wcy".equals(username) && "123".equals(password)) {
            Cookie cookie = new Cookie("username", username);
            cookie.setMaxAge(60 * 60 * 24);
            resp.addCookie(cookie);
            resp.getWriter().println("登陆成功!");
            return;
        }
        resp.getWriter().println("登陆失败!");
    }
}
<form action="${pageContext.request.contextPath}/login" method="post">
    <input type="text" name="username" value="${cookie.username.value}">
    <input type="text" name="password" value="">
    <input type="submit" value="登陆">
</form>

Session 会话

什么是 Session 会话

1、Session 就一个接口(HttpSession)。
2、Session 就是会话。它是用来维护一个客户端和服务器之间关联的一种技术。
3、每个客户端都有自己的一个 Session 会话。
4、Session 会话中,我们经常用来保存用户登录之后的信息。

如何创建 Session 和获取

创建和获取 Session,它们的 API 是一样的

request.getSession()

第一次调用是:创建 Session 会话

之后调用都是:获取前面创建好的 Session 会话对象

isNew(); 判断到底是不是刚创建出来的(新的)

  • true 表示刚创建
  • false 表示获取之前创建

每个会话都有一个身份证号。也就是 ID 值。而且这个 ID 是唯一的。
getId() 得到 Session 的ID值

Session 域数据的存取

/**
* 往 Session 中保存数据
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void setAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	req.getSession().setAttribute("key1", "value1");
	resp.getWriter().write("已经往 Session 中保存了数据");
}
/**
* 获取 Session 域中的数据
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void getAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	Object attribute = req.getSession().getAttribute("key1");
	resp.getWriter().write("从 Session 中获取出 key1 的数据是:" + attribute);
}

Session 生命周期控制

public void setMaxInactiveInterval(int interval) 设置 Session 的超时时间(以秒为单位),超过指定的时长,Session就会被销毁。

  • 值为正数的时候,设定 Session 的超时时长。
  • 负数表示永不超时(极少使用)

public int getMaxInactiveInterval()获取 Session 的超时时间

public void invalidate() 让当前 Session 会话马上超时无效

Session 默认的超时时间长为 30 分钟
因为在 Tomcat 服务器的配置文件 web.xml中默认有以下的配置,它就表示配置了当前 Tomcat 服务器下所有的 Session
超时配置默认时长为:30 分钟

<session-config>
	<session-timeout>30</session-timeout>
</session-config>

如果说希望你的 web 工程,默认的 Session 的超时时长为其他时长。你可以在你自己的 web.xml 配置文件中做以上相同的配置。就可以修改你的 web 工程所有 Seession 的默认超时时长

<!--表示当前 web 工程。创建出来 的所有 Session 默认是 20 分钟 超时时长-->
<session-config>
	<session-timeout>20</session-timeout>
</session-config>

如果你想只修改个别 Session 的超时时长。就可以使用上面的 APIsetMaxInactiveInterval(int interval)来进行单独的设置
session.setMaxInactiveInterval(int interval)单独设置超时时长

Session 超时的概念介绍

超过设置的最大时长的间隔后再访问就是失效,没有超过会重新设置间隔时间,重新计时
在这里插入图片描述

protected void life3(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	// 先获取 Session 对象
	HttpSession session = req.getSession();
	// 设置当前 Session3 秒后超时
	session.setMaxInactiveInterval(3);
	resp.getWriter().write("当前 Session 已经设置为 3 秒后超时");
}

// Session 马上被超时示例
protected void deleteNow(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
	// 先获取 Session 对象
	HttpSession session = req.getSession();
	// 让 Session 会话马上超时
	session.invalidate();
	resp.getWriter().write("Session 已经设置为超时(无效)");
}

浏览器和 Session 之间关联的技术内幕

Session 技术,底层其实是基于 Cookie 技术来实现
浏览器存储了Session的ID
访问服务器时会将SessionID以cookie的方式发送,服务器通过SessionID来查找对应的Session会话
在这里插入图片描述

Filter

什么是过滤器

  • Filter 过滤器它是 JavaWeb 的三大组件之一,三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器
  • Filter 过滤器它是 JavaEE 的规范。也就是接口
  • Filter 过滤器它的作用是:拦截请求,过滤响应

拦截请求常见的应用场景有:

  • 权限检查
  • 日记操作
  • 事务管理
    ……

Filter 过滤器的使用步骤

1、编写一个类去实现 Filter 接口
2、实现过滤方法 doFilter()
3、到 web.xml 中去配置 Filter 的拦截路径

Filter的工作流程
在这里插入图片描述
案例:在你的 web 工程下,有一个 admin 目录。这个 admin 目录下的所有资源(html 页面、jpg 图片、jsp 文件、等等)都必须是用户登录之后才允许访问

public class AdminFilter implements Filter {
	/**
	* doFilter 方法,专门用于拦截请求。可以做权限检查
	*/
	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
		HttpSession session = httpServletRequest.getSession();
		Object user = session.getAttribute("user");
		// 如果等于 null,说明还没有登录
		if (user == null) {
			servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
			return;
		} 
		// 让程序继续往下访问用户的目标资源
		filterChain.doFilter(servletRequest,servletResponse);
	}
}
<!--filter 标签用于配置一个 Filter 过滤器-->
<filter>
	<!--给 filter 起一个别名-->
	<filter-name>AdminFilter</filter-name>
	<!--配置 filter 的全类名-->
	<filter-class>com.atguigu.filter.AdminFilter</filter-class>
</filter>

<!--filter-mapping 配置 Filter 过滤器的拦截路径-->
<filter-mapping>
	<!--filter-name 表示当前的拦截路径给哪个 filter 使用-->
	<filter-name>AdminFilter</filter-name>
	<!--url-pattern 配置拦截路径
	/ 表示请求地址为:http://ip:port/工程路径/ 映射到 IDEA 的 web 目录
	/admin/* 表示请求地址为:http://ip:port/工程路径/admin/*
	-->
	<url-pattern>/admin/*</url-pattern>
</filter-mapping>

Filter 的生命周期

Filter 的生命周期包含几个方法

1、构造器方法

2、init 初始化方法
第 1,2 步,在 web 工程启动的时候执行(Filter 已经创建)

3、doFilter 过滤方法
第 3 步,每次拦截到请求,就会执行

4、destroy 销毁
第 4 步,停止 web 工程的时候,就会执行(停止 web 工程,也会销毁 Filter 过滤器)

和servletContext的生命周期类似

FilterConfig

FilterConfig 类见名知义,它是 Filter 过滤器的配置文件类

Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。

FilterConfig 类的作用是获取 filter 过滤器的配置内容

  • 获取 Filter 的名称 filter-name 的内容
  • 获取在 Filter 中配置的 init-param 初始化参数
  • 获取 ServletContext 对象
@Override
public void init(FilterConfig filterConfig) throws ServletException {
	System.out.println("2.Filter 的 init(FilterConfig filterConfig)初始化");
	// 1、获取 Filter 的名称 filter-name 的内容
	System.out.println("filter-name 的值是:" + filterConfig.getFilterName());
	// 2、获取在 web.xml 中配置的 init-param 初始化参数
	System.out.println("初始化参数 username 的值是:" + filterConfig.getInitParameter("username"));
	System.out.println("初始化参数 url 的值是:" + filterConfig.getInitParameter("url"));
	// 3、获取 ServletContext 对象
	System.out.println(filterConfig.getServletContext());
}
<!--filter 标签用于配置一个 Filter 过滤器-->
<filter>
	<!--给 filter 起一个别名-->
	<filter-name>AdminFilter</filter-name>
	<!--配置 filter 的全类名-->
	<filter-class>com.atguigu.filter.AdminFilter</filter-class>
	
	<init-param>
		<param-name>username</param-name>
		<param-value>root</param-value>
	</init-param>
	<init-param>
		<param-name>url</param-name>
		<param-value>jdbc:mysql://localhost3306/test</param-value>
	</init-param>
</filter>

FilterChain 过滤器链

多个过滤器一起工作
在这里插入图片描述

Filter 的拦截路径

  • 精确匹配
    <url-pattern>/target.jsp</url-pattern>
    以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp
  • 目录匹配
    <url-pattern>/admin/*</url-pattern>
    以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/*
  • 后缀名匹配
    <url-pattern>*.html</url-pattern>
    以上配置的路径,表示请求地址必须以.html 结尾才会拦截到
    <url-pattern>*.do</url-pattern>
    以上配置的路径,表示请求地址必须以.do 结尾才会拦截到
    <url-pattern>*.action</url-pattern>
    以上配置的路径,表示请求地址必须以.action 结尾才会拦截到

Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在,只要路径匹配上都会经过过滤器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值