文章目录
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 服务器启动成功!!!
常见的启动失败的情况有
- 双击 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 工程。
类型 | 存数据 | 取数据 | 删除数据 |
Map | put() | 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、请求头
- 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 , .htm | text/html |
普通文本 | .txt | text/plain |
RTF 文本 | .rtf | application/rtf |
GIF 图形 | .gif | image/gif |
JPEG 图形 | .jpeg,.jpg | image/jpeg |
au 声音文件 | .au | audio/basic |
MIDI 音乐文件 | mid,.midi | audio/midi,audio/x-midi |
RealAudio 音乐文件 | .ra, .ram | audio/x-pn-realaudio |
MPEG 文件 | .mpg,.mpeg | video/mpeg |
AVI 文件 | .avi | video/x-msvideo |
GZIP 文件 | .gz | application/x-gzip |
TAR 文件 | .tar | application/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、表达式脚本中的表达式不能以分号结束
- 输出整型
- 输出浮点型
- 输出字符串
- 输出对象
示例代码:
<%=12 %> <br>
<%=12.12 %> <br>
<%="我是字符串" %> <br>
<%=map%> <br>
<%=request.getParameter("username")%>
代码脚本
代码脚本的格式是:
<% java 语句 %>
代码脚本的作用是:可以在 jsp 页面中,编写我们自己需要的功能(写的是 java 语句)。
代码脚本的特点是:
1、代码脚本翻译之后都在_jspService 方法中
2、代码脚本由于翻译到_jspService()方法中,所以在_jspService()方法中的现有对象都可以直接使用。
3、还可以由多个代码脚本块组合完成一个完整的 java 语句。
4、代码脚本还可以和表达式脚本一起组合使用,在 jsp 页面上输出数据
- 代码脚本----if 语句
- 代码脚本----for 循环语句
- 翻译后 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 个隐含对象
变量 | 类型 | 作用 |
pageContext | PageContextImpl | 它可以获取 jsp 中的九大内置对象 |
pageScope | Map<String,Object> | 它可以获取 pageContext 域中的数据 |
requestScope | Map<String,Object> | 它可以获取 Request 域中的数据 |
sessionScope | Map<String,Object> | 它可以获取 Session 域中的数据 |
applicationScope | Map<String,Object> | 它可以获取 ServletContext 域中的数据 |
param | Map<String,String> | 它可以获取请求参数的值 |
paramValues | Map<String,String[]> | 它也可以获取请求参数的值,获取多个值的时候使用。 |
header | Map<String,String> | 它可以获取请求头的信息 |
headerValues | Map<String,String[]> | 它可以获取请求头的信息,它可以获取多个值的情况 |
cookie | Map<String,Cookie> | 它可以获取当前请求的 Cookie 信息 |
initParam | Map<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 对象
- 协议:
- 服务器 ip:
- 服务器端口:
- 获取工程路径:
- 获取请求方法:
- 获取客户端 ip 地址:
- 获取会话的 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 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在,只要路径匹配上都会经过过滤器