EL&Filter&Listener-授课
1 EL表达式和JSTL
1.1 EL表达式
1.1.1 EL表达式介绍 ***
- EL(Expression Language):表达式语言
- 在 JSP 2.0 规范中加入的内容,也是 Servlet 规范的一部分
- 作用:在 JSP 页面中获取数据。让我们的 JSP 脱离 java 代码块和 JSP 表达式
- 语法:
${ 表达式内容 }
- 例如:
1.1.2 EL表达式的快速入门
-
创建一个 web 项目:el_demo,虚拟目录/el
-
在 web 目录下创建 el01.jsp
-
在文件中向域对象添加数据
-
使用三种方式获取域对象中的数据(java 代码块、JSP 表达式、EL 表达式)
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>EL表达式快速入门</title> </head> <body> <%--1.向域对象中添加数据--%> <% request.setAttribute("username","zhangsan"); %> <%--2.获取数据--%> Java代码块:<% out.println(request.getAttribute("username")); %> <br> JSP表达式:<%= request.getAttribute("username")%> <br> EL表达式:${username} </body> </html>
-
部署并启动项目
-
通过浏览器测试
1.1.3 EL表达式获取不同类型数据 ***
-
要获取的数据类型
- 获取基本数据类型的数据
- 获取自定义对象类型的数据
- 获取数组类型的数据
- 获取 List 集合类型的数据
- 获取 Map 集合类型的数据
-
案例:新建el02.jsp
<%@ page import="com.itheima.bean.Student" %> <%@ page import="java.util.ArrayList" %> <%@ page import="java.util.HashMap" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>EL表达式获取不同类型数据</title> </head> <body> <%--1.获取基本数据类型--%> <% pageContext.setAttribute("num",10); %> 基本数据类型:${num} <br> <%--2.获取自定义对象类型--%> <% Student stu = new Student("张三",23); pageContext.setAttribute("stu",stu); %> 自定义对象:${stu} <br> <%--stu.name 实现原理 getName()--%> 学生姓名:${stu.name} <br> 学生年龄:${stu.age} <br> <%--3.获取数组类型--%> <% String[] arr = {"hello","world"}; pageContext.setAttribute("arr",arr); %> 数组:${arr} <br> 0索引元素:${arr[0]} <br> 1索引元素:${arr[1]} <br> <%--EL表达式中没有字符串拼接--%> 0索引拼接1索引的元素:${arr[0]} + ${arr[1]} <br> <%--4.获取List集合--%> <% ArrayList<String> list = new ArrayList<>(); list.add("aaa"); list.add("bbb"); pageContext.setAttribute("list",list); %> List集合:${list} <br> 0索引元素:${list[0]} <br> <%--5.获取Map集合--%> <% HashMap<String,Student> map = new HashMap<>(); map.put("hm01",new Student("张三",23)); map.put("hm02",new Student("李四",24)); pageContext.setAttribute("map",map); %> Map集合:${map} <br> 第一个学生对象:${map.hm01} <br> 第一个学生对象的姓名:${map.hm01.name} </body> </html>
-
访问
1.1.4 EL表达式的注意事项
-
EL 表达式没有空指针异常
-
EL 表达式没有索引越界异常
-
EL 表达式没有字符串的拼接(el表达式中+只能对数字相加)
-
代码
-
空指针
-
越界和字符串拼接
-
-
访问
1.1.5 EL表达式的运算符
-
关系运算符
-
逻辑运算符
-
其他运算符
- 容器元素是否为0,就是容器的长度是否为0(其实就是检查容器是否为空)
-
案例:新建el03.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>EL表达式运算符</title> </head> <body> <%--empty--%> <% String str1 = null; String str2 = ""; int[] arr = {}; %> ${empty str1} <br> ${empty str2} <br> ${empty arr} <br> <%--三元运算符。获取性别的数据,在对应的按钮上进行勾选 ***--%> <% pageContext.setAttribute("gender","men"); %> <input type="radio" name="gender" value="men" ${gender == "men" ? "checked" : ""}>男 <input type="radio" name="gender" value="women" ${gender == "women" ? "checked" : ""}>女 </body> </html>
-
访问
1.1.6 EL表达式使用细节 ***
-
EL 表达式能够获取四大域对象的数据,根据名称从小到大在域对象中查找
-
还可以获取 JSP 其他八个隐式对象,并调用对象中的方法
-
案例:el04.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>EL表达式使用细节</title> </head> <body> <%--获取四大域对象中的数据--%> <% //pageContext.setAttribute("username","zhangsan"); request.setAttribute("username","zhangsan"); //session.setAttribute("username","zhangsan"); //application.setAttribute("username","zhangsan"); %> ${username} <br> <%--pageContext页面域获取JSP中其他八个隐式对象 获取虚拟目录名称--%> <%= request.getContextPath()%> <%--jsp中可以直接获取request四大域对象,因为有这几个隐式对象--%> ${pageContext.request.contextPath} <%--el表达式,只能通过页面域获取其他八大隐身对象--%> <%--${request.contextPath} <%--el表达式,不能直接访问八大隐式对象–%>--%> ${contextPath} <%--el表达式,可以直接访问四大域中的共享数据,可不是属性--%> </body> </body> </html>
-
访问
1.1.7 EL表达式的11个隐式对象
EL表达式也为我们提供隐式对象,可以让我们不声明直接来使用,十一个对象见下表,需要注意的是,它和JSP的隐式对象不是一回事:
EL中的隐式对象 | 类型 | 对应JSP隐式对象 | 备注 |
---|---|---|---|
PageContext *** | Javax.serlvet.jsp.PageContext | PageContext | 完全一样 |
ApplicationScope | Java.util.Map | 没有 | 操作应用域对象数据 |
SessionScope | Java.util.Map | 没有 | 操作会话域对象数据 |
RequestScope | Java.util.Map | 没有 | 操作请求域对象数据 |
PageScope | Java.util.Map | 没有 | 操作页面域对象数据 |
Header | Java.util.Map | 没有 | 根据key获取请求消息头,值是一个 |
HeaderValues | Java.util.Map | 没有 | 根据key获取请求消息头,值是多个(数组) |
Param | Java.util.Map | 没有 | 根据key获取请求参数,值是一个 |
ParamValues | Java.util.Map | 没有 | 根据key获取请求参数,值是多个(数组) |
InitParam | Java.util.Map | 没有 | 根据key获取全局参数,value是参数值 |
Cookie | Java.util.Map | 没有 | 根据key获取cookie的值 |
-
案例
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>EL表达式11个隐式对象</title> </head> <body> <%--pageContext对象 可以获取其他三个域对象和JSP中八个隐式对象--%> ${pageContext.request.contextPath} <br> <%--applicationScope sessionScope requestScope pageScope 操作四大域对象中的数据--%> <% request.setAttribute("username","zhangsan"); %> ${username} <br> ${requestScope.username} <br> <%--header headerValues 获取请求头数据--%> ${header["connection"]} <br> ${headerValues["connection"][0]} <br> <%--param paramValues 获取请求参数数据--%> ${param.username} <br> ${paramValues.hobby[0]} <br> ${paramValues.hobby[1]} <br> <%--initParam 获取全局配置参数--%> ${initParam["pname"]} <br> <%--cookie 获取cookie信息--%> ${cookie} <br> <%--获取Map集合--%> ${cookie.JSESSIONID} <br> <%--获取map集合中第二个元素--%> ${cookie.JSESSIONID.name} <br> <%--获取cookie对象的名称--%> ${cookie.JSESSIONID.value} <%--获取cookie对象的值--%> </body> </html>
-
访问
-
总结
1. 获取四大域中的共享数据: ${数据的name} (都是通过xxx.setAttribute()) 2. 根据pageContext获取其他隐式对象:${pageContext.request.contextpath} 注意: contextpath 不是共享数据,他是虚拟路径,是一个属性,有对应的一个getter方法:getContextPath <% request.getContextPath() %>
1.2 JSTL
1.2.1 JSTL的介绍
- JSTL(Java Server Pages Standarded Tag Library):JSP 标准标签库
- 主要提供给开发人员一个标准通用的标签库
- 开发人员可以利用这些标签取代 JSP 页面上的 Java 代码,从而提高程序的可读性,降低程序的维护难度
- 它由以下5个部分组成:
1.2.2 核心标签库
1)核心库介绍
在我们实际开发中,用到的jstl标签库主要以核心标签库为准,偶尔会用到国际化标签库的标签。下表中把我们经常可能用到的标签列在此处,其余标签库请同学们参考【JSTL标签库.doc】文档。
标签名称 | 功能分类 | 分类 | 作用 |
---|---|---|---|
<c:if> | 流程控制 | 核心标签库 | 用于判断 |
<c:choose> ,<c:when>,<c:otherwise> | 流程控制 | 核心标签库 | 用于多个条件判断 |
<c:foreache> | 迭代操作 | 核心标签库 | 用于循环遍历 |
2)案例 ***
-
创建一个 web 项目:jstl_demo
-
在 web 目录下创建一个 WEB-INF 目录
-
在 WEB-INF 目录下创建一个 libs 目录,将 JSTL 的 jar 包导入
- (jar包目录:day05_el表达式过滤器监听器\资料\JSTL的jar包)
- 添加引用库
-
创建 JSP 文件,通过 taglib 导入 JSTL 标签库
-
对流程控制和迭代遍历的标签进行使用
-
新建jstl01.jsp:流程控制
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <!--prefix给jstl core核心库起个前缀名,使用的标签就以c开头--> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <head> <title>流程控制</title> </head> <body> <%--向域对象中添加成绩数据--%> ${pageContext.setAttribute("score","T")} <%--对成绩进行判断--%> <c:if test="${score eq 'A'}"> 优秀 </c:if> <%--对成绩进行多条件判断--%> <c:choose> <c:when test="${score eq 'A'}">优秀</c:when> <c:when test="${score eq 'B'}">良好</c:when> <c:when test="${score eq 'C'}">及格</c:when> <c:when test="${score eq 'D'}">较差</c:when> <c:otherwise>成绩非法</c:otherwise> </c:choose> </body> </html>
-
新建jstl02.jsp:迭代遍历
<%@ page import="java.util.ArrayList" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <head> <title>循环</title> </head> <body> <%--向域对象中添加集合--%> <% ArrayList<String> list = new ArrayList<>(); list.add("aa"); list.add("bb"); list.add("cc"); list.add("dd"); pageContext.setAttribute("list",list); %> <%--遍历集合--%> <c:forEach items="${list}" var="str"> ${str} <br> </c:forEach> </body> </html>
-
-
部署并启动项目
-
通过浏览器查看
-
访问jstl01
-
访问jstl02
-
-
问题:项目运行报错
-
解决:
2 Servlet规范中的过滤器-Filter
2.1 过滤器入门
2.1.1 过滤器的介绍 ***
-
过滤器——Filter,它是JavaWeb三大组件之一。另外两个是Servlet和Listener
-
它可以对web应用中的所有资源进行拦截,并且在拦截之后进行一些特殊的操作
-
在程序中访问服务器资源时,当一个请求到来,服务器首先判断是否有过滤器与请求资源相关联,如果有,过滤器可以将请求拦截下来,完成一些特定的功能,再由过滤器决定是否交给请求资源。如果没有则像之前那样直接请求资源了。响应也是类似的!
-
过滤器一般用于完成通用的操作,例如:登录验证、统一编码处理、敏感字符过滤等等~~~
- 处理通用操作: 统一编码处理
- 处理拦截操作:权限校验,如果有权限那就放行,如果没有就拦截
- 过滤器会做两件事情:拦截,筛选/过滤
-
过滤器图示
-
生活中的例子
- 比如一对新人结婚,规定:只有带红包的客人可以吃酒席,不带的就不能吃
- 那么这项工作不可能由新人自己完成,他们请了俩人,一个管收钱,一个管记账(这俩人就是过滤器)
2.1.2 Filter介绍 ***
-
Filter 是一个接口,如果想实现过滤器的功能,必须实现该接口
-
核心方法
-
配置方式
- 注解方式
- 配置文件
-
Filter官网介绍
2.1.3 FilterChain ***
-
FilterChain 是一个接口,代表过滤器链对象。由 Servlet 容器提供实现类对象,直接使用即可。
- chain: [tʃeɪn] 链子
-
过滤器可以定义多个,就会组成过滤器链
-
核心方法
-
官网介绍
2.1.4 过滤器的使用 ***
-
需求说明
- 通过 Filter 过滤器解决多个资源写出中文乱码的问题
-
最终目的
- 通过本需求,最终掌握 Filter 过滤器的使用
-
实现步骤
-
创建一个 web 项目:filter_demo,虚拟路径/filter
-
创建两个 Servlet 功能类,都向客户端写出中文数据
-
新建ServletDemo01:
package com.itheima.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/servletDemo01") public class ServletDemo01 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servletDemo01执行了..."); //resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().write("servletDemo01执行了..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
新建ServletDemo02:
package com.itheima.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/servletDemo02") public class ServletDemo02 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servletDemo02执行了..."); //resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().write("servletDemo02执行了..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
访问
-
发现demo1和demo2都是乱码
-
之前都是在代码里添加:
resp.setContentType("text/html;charset=UTF-8");
去解决乱码问题 -
但是如果有100个Servlet需要解决乱码问题呢?我们要写一百次么?
-
不用,只需要使用过滤器即可
-
-
创建一个 Filter 过滤器实现类,重写 doFilter 核心方法
-
在方法内解决中文乱码,并放行
package com.itheima.filter; import javax.servlet.*; import java.io.IOException; /* 过滤器基本使用 /*:表明访问当前应用下任何资源,此过滤器都会起作用 */ @WebFilter("/*") public class FilterDemo01 implements Filter{ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filterDemo01执行了..."); //处理乱码 servletResponse.setContentType("text/html;charset=UTF-8"); //放行 filterChain.doFilter(servletRequest,servletResponse); } }
-
部署并启动项目
-
通过浏览器测试
-
2.1.5 Filter过滤器的使用细节
-
配置方式
- 注解方式 @WebFilter(拦截路径)
- 配置文件方式
-
多个过滤器使用顺序
- 如果有多个过滤器,取决于过滤器映射的顺序
- 也就是filter-mapping配置的先后顺序
-
案例
-
将demo1的WebFilter注解屏蔽
-
新建FilterDemo02:
package com.itheima.filter; import javax.servlet.*; import java.io.IOException; /* 过滤器基本使用 */ //@WebFilter("/*") public class FilterDemo02 implements Filter{ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filterDemo02执行了..."); //处理乱码 servletResponse.setContentType("text/html;charset=UTF-8"); //放行 filterChain.doFilter(servletRequest,servletResponse); } }
-
配置filter
<filter> <filter-name>filterDemo01</filter-name> <filter-class>com.itheima.filter.FilterDemo01</filter-class> </filter> <filter-mapping> <filter-name>filterDemo01</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>filterDemo02</filter-name> <filter-class>com.itheima.filter.FilterDemo02</filter-class> </filter> <filter-mapping> <filter-name>filterDemo02</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
-
访问
2.1.6 生命周期
-
创建(出生)
- 当应用加载时实例化对象并执行 init 初始化方法
-
服务(活着)
- 对象提供服务的过程,执行 doFilter 方法
- 只要应用一直提供服务,对象就一直存在
-
销毁(死亡)
- 当应用卸载时或服务器停止时对象销毁。执行 destroy 方法
-
Filter的实例对象在内存中也只有一份。所以也是单例的。
-
案例:新建FilterDemo03
package com.itheima.filter; import javax.servlet.*; import java.io.IOException; /* 过滤器生命周期 */ //@WebFilter("/*") public class FilterDemo03 implements Filter{ /* 初始化方法 */ @Override public void init(FilterConfig filterConfig) { System.out.println("对象初始化成功了..."); } /* 提供服务方法 */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filterDemo03执行了..."); //处理乱码 servletResponse.setContentType("text/html;charset=UTF-8"); //放行 filterChain.doFilter(servletRequest,servletResponse); } /* 对象销毁 */ @Override public void destroy() { System.out.println("对象销毁了..."); } }
-
配置:为了不影响测试,给之前的俩过滤器的配置代码屏蔽掉
<filter> <filter-name>filterDemo03</filter-name> <filter-class>com.itheima.filter.FilterDemo03</filter-class> </filter> <filter-mapping> <filter-name>filterDemo03</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
访问
2.1.7 FilterConfig过滤器配置对象
-
FilterConfig 是一个接口。代表过滤器的配置对象,可以加载一些初始化参数。
- 与ServletConfig类似
-
核心方法
-
官网介绍
-
案例:新建FilterDemo04
package com.itheima.filter; import javax.servlet.*; import java.io.IOException; /* 过滤器配置对象的使用 */ //@WebFilter("/*") public class FilterDemo04 implements Filter{ /* 初始化方法 */ @Override public void init(FilterConfig filterConfig) { System.out.println("对象初始化成功了..."); //获取过滤器名称 String filterName = filterConfig.getFilterName(); System.out.println(filterName); //根据name获取value String username = filterConfig.getInitParameter("username"); System.out.println(username); } /* 提供服务方法 */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filterDemo04执行了..."); //处理乱码 servletResponse.setContentType("text/html;charset=UTF-8"); //放行 filterChain.doFilter(servletRequest,servletResponse); } /* 对象销毁 */ @Override public void destroy() { System.out.println("对象销毁了..."); } }
-
filter配置
<filter> <filter-name>filterDemo04</filter-name> <filter-class>com.itheima.filter.FilterDemo04</filter-class> <init-param> <param-name>username</param-name> <param-value>zhangsan</param-value> </init-param> </filter> <filter-mapping> <filter-name>filterDemo04</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
启动tomcat
3 Servlet规范中的监听器-Listener
3.1 观察者设计模式 ***
-
在介绍监听器之前,先跟同学们普及一个知识,观察者设计模式。
-
因为所有的监听器都是观察者设计模式的体现。
-
那什么是观察者设计模式呢?
-
它是事件驱动的一种体现形式。就好比在做什么事情的时候被人盯着,当对应做到某件事时,触发事件。
-
观察者模式通常由以下三部分组成:
- 事件源:触发事件的对象。
- 事件:触发的动作,里面封装了事件源。
- 监听器:当事件源触发事件时,要做的事情。一般是一个接口,由使用者来实现。(此处的思想还涉及了一个设计模式,我们在JDBC的第二天课程中就给同学们讲解,策略模式)
- 事件源触发某个事件之后,监听器监听到了这个过程,进而执行一个逻辑
- 例子:狗发现人从门前过,就会狂吠 (看门狗)
-
生活中的例子
- 老师定了一个规矩,老师一旦发现学生9点之后来的话就算迟到,那就惩罚,9点之前来的学生不惩罚
- 在这个例子中,事件源是学生
- 事件是迟到
- 监听器就是老师
-
监听器介绍
- 在程序当中,我们可以对:对象的创建销毁、域对象中属性的变化、会话相关内容进行监听
- Servlet 规范中共计 8 个监听器,监听器都是以接口形式提供,具体功能需要我们自己来完成
3.2 监听对象的监听器
1) ServletContextListener ***
-
ServletContextListener:用于监听 ServletContext 对象的创建和销毁
-
核心方法
- 参数:ServletContextEvent 代表事件对象
- 事件对象中封装了事件源,也就是 ServletContext
- 真正的事件指的是创建或销毁 ServletContext 对象的操作
2) HttpSessionListener
-
HttpSessionListener:用于监听 HttpSession 对象的创建和销毁
-
核心方法
- 参数:HttpSessionEvent 代表事件对象
- 事件对象中封装了事件源,也就是 HttpSession
- 真正的事件指的是创建或销毁 HttpSession 对象的操作
3) ServletRequestListener
-
ServletRequestListener:用于监听 ServletRequest 对象的创建和销毁
-
核心方法
- 参数:ServletRequestEvent 代表事件对象
- 事件对象中封装了事件源,也就是 ServletRequest
- 真正的事件指的是创建或销毁 ServletRequest 对象的操作
3.3 监听域对象属性变化的监听器
1) ServletContextAttributeListener
-
ServletContextAttributeListener:用于监听 ServletContext 应用域中属性的变化
-
核心方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LtAlAuPS-1644379168213)(D:/%E4%BC%A0%E6%99%BA%E5%B7%A5%E4%BD%9C/Java/02-Javaweb/06-JavaWeb%E6%A0%B8%E5%BF%83%EF%BC%88%E5%8F%8C%E5%85%83%EF%BC%89/day05_el%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9B%91%E5%90%AC%E5%99%A8/%E8%AE%B2%E4%B9%89/img/%E7%9B%91%E5%90%AC%E5%AF%B9%E8%B1%A104.png)]
- 参数:ServletContextAttributeEvent 代表事件对象
- 事件对象中封装了事件源,也就是 ServletContext
- 真正的事件指的是添加、移除、替换应用域中属性的操作
2) HttpSessionAttributeListener
-
HttpSessionAttributeListener:用于监听 HttpSession 会话域中属性的变化
-
核心方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BAnExOb1-1644379168214)(D:/%E4%BC%A0%E6%99%BA%E5%B7%A5%E4%BD%9C/Java/02-Javaweb/06-JavaWeb%E6%A0%B8%E5%BF%83%EF%BC%88%E5%8F%8C%E5%85%83%EF%BC%89/day05_el%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9B%91%E5%90%AC%E5%99%A8/%E8%AE%B2%E4%B9%89/img/%E7%9B%91%E5%90%AC%E5%AF%B9%E8%B1%A105.png)]
- 参数:HttpSessionBindingEvent 代表事件对象
- 事件对象中封装了事件源,也就是 HttpSession
- 真正的事件指的是添加、移除、替换会话域中属性的操作
3) ServletRequestAttributeListener
-
ServletRequestAttributeListener:用于监听 ServletRequest 请求域中属性的变化
-
核心方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tlfgUZeT-1644379168214)(D:/%E4%BC%A0%E6%99%BA%E5%B7%A5%E4%BD%9C/Java/02-Javaweb/06-JavaWeb%E6%A0%B8%E5%BF%83%EF%BC%88%E5%8F%8C%E5%85%83%EF%BC%89/day05_el%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9B%91%E5%90%AC%E5%99%A8/%E8%AE%B2%E4%B9%89/img/%E7%9B%91%E5%90%AC%E5%AF%B9%E8%B1%A106.png)]
- 参数:ServletRequestAttributeEvent 代表事件对象
- 事件对象中封装了事件源,也就是 ServletRequest
- 真正的事件指的是添加、移除、替换请求域中属性的操作
3.4 监听会话相关的感知型监听器
1) HttpSessionBindingListener
-
HttpSessionBindingListener:用于感知对象和会话域绑定的监听器
-
核心方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Au31XF2Z-1644379168215)(D:/%E4%BC%A0%E6%99%BA%E5%B7%A5%E4%BD%9C/Java/02-Javaweb/06-JavaWeb%E6%A0%B8%E5%BF%83%EF%BC%88%E5%8F%8C%E5%85%83%EF%BC%89/day05_el%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9B%91%E5%90%AC%E5%99%A8/%E8%AE%B2%E4%B9%89/img/%E7%9B%91%E5%90%AC%E5%AF%B9%E8%B1%A107.png)]
- 参数:HttpSessionBindingEvent 代表事件对象
- 事件对象中封装了事件源,也就是 HttpSession
- 真正的事件指的是添加、移除会话域中数据的操作
-
HttpSessionBindingListener和HttpSessionAttributeListener区别:
1.只有实现了HttpSessionBindingListener的类,添加移除到session域中才会触发绑定,解绑方法。 2.任何对象(不论其是否实现了HttpSessionAttributeListener)在变化时均触发对应的事件。
2) HttpSessionActivationListener
-
HttpSessionActivationListener:用于感知会话域中对象钝化和活化的监听器
-
核心方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U3lqQfJR-1644379168215)(D:/%E4%BC%A0%E6%99%BA%E5%B7%A5%E4%BD%9C/Java/02-Javaweb/06-JavaWeb%E6%A0%B8%E5%BF%83%EF%BC%88%E5%8F%8C%E5%85%83%EF%BC%89/day05_el%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9B%91%E5%90%AC%E5%99%A8/%E8%AE%B2%E4%B9%89/img/%E7%9B%91%E5%90%AC%E5%AF%B9%E8%B1%A108.png)]
- 参数:HttpSessionEvent 代表事件对象
- 事件对象中封装了事件源,也就是 HttpSession
- 真正的事件指的是会话域中数据钝化、活化的操作
3.4 监听器的使用
- 在实际开发中,我们可以根据具体情况来从这8个监听器中选择使用
- 感知型监听器由于无需配置,只需要根据实际需求编写代码,所以此处我们就不再演示了
- 我们在剩余6个中分别选择一个监听对象创建销毁和对象域中属性发生变化的监听器演示一下
- 演示下边两个
3.4.1 ServletContextListener的使用 ***
-
创建项目:listener_demo,虚拟路径/listener
-
创建com.itheima.listener.ServletContextListenerDemo
package com.itheima.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /* ServletContext对象的创建和销毁的监听器 //配置监听器:@WebListener */ @WebListener public class ServletContextListenerDemo implements ServletContextListener{ /* ServletContext对象创建的时候执行此方法 */ @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("监听到了对象的创建..."); //获取对象 ServletContext servletContext = sce.getServletContext(); System.out.println(servletContext); } /* ServletContext对象销毁的时候执行此方法 */ @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("监听到了对象的销毁..."); } }
-
启动项目:ServletContext是项目启动都会创建的,所以这里只需要启动tomcat
3.4.2 ServletContextAttributeListener的使用
-
新建ServletContextAttributeListenerDemo
package com.itheima.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeListener; /* 应用域对象中的属性变化的监听器 */ @WebListener public class ServletContextAttributeListenerDemo implements ServletContextAttributeListener{ /* 向应用域对象中添加属性时执行此方法 */ @Override public void attributeAdded(ServletContextAttributeEvent scae) { System.out.println("监听到了属性的添加..."); //获取应用域对象 ServletContext servletContext = scae.getServletContext(); //获取属性 Object value = servletContext.getAttribute("username"); System.out.println(value); } /* 向应用域对象中替换属性时执行此方法 */ @Override public void attributeReplaced(ServletContextAttributeEvent scae) { System.out.println("监听到了属性的替换..."); //获取应用域对象 ServletContext servletContext = scae.getServletContext(); //获取属性 Object value = servletContext.getAttribute("username"); System.out.println(value); } /* 向应用域对象中移除属性时执行此方法 */ @Override public void attributeRemoved(ServletContextAttributeEvent scae) { System.out.println("监听到了属性的移除..."); //获取应用域对象 ServletContext servletContext = scae.getServletContext(); //获取属性 Object value = servletContext.getAttribute("username"); System.out.println(value); } }
-
修改ServletContextListenerDemo:在contextInitialized中增加如下代码:
//添加属性 servletContext.setAttribute("username","zhangsan"); //替换属性 servletContext.setAttribute("username","lisi"); //移除属性 servletContext.removeAttribute("username");
-
启动tomcat
-
注解配置监听器,改为xml手动配置
- 注释两个demo中的
//@WebListener
- web.xml增加如下配置
<!--配置监听器--> <listener> <listener-class>com.itheima.listener.ServletContextListenerDemo</listener-class> </listener> <listener> <listener-class>com.itheima.listener.ServletContextAttributeListenerDemo</listener-class> </listener>
- 注释两个demo中的
4 综合案例-学生管理系统改造 ***
4.1 需求说明
4.1.1 解决乱码问题
我们的学生管理系统中,肯定会有请求和响应的中文乱码问题。而乱码问题在学习Servlet的课程中已经讲解了如何解决了。只是在实际开发中,当有很多的Servlet时,肯定不能在每个Servlet中都编写一遍解决乱码的代码。因此,就可以利用我们今天学习的过滤器来实现统一解决请求和响应乱码的问题。
4.1.2 检查登录
在学生管理系统中,它包含了学生信息的录入和学生列表的查询,用户(员工)信息的录入以及查询。当然,我们实际的功能可能远远不止这些。但是就已有功能来说,也不是谁都可以通过地址栏直接输入访问的,它应该有权限的控制,只是我们课程在此处没法深入展开讲解权限,但最起码的登录,身份的认证还是必要的。
由此,就引出来一个问题,是在每次访问Servlet时,在Servlet的代码中加入是否认证过身份的判断吗?显然,是不合理的。那么,既然不是在每个Servlet中编写,就应该是统一管理和维护。此时,我们的过滤器就又可以出场了。
4.1.3 页面的java代码块和jsp表达式改造
我们今天除了学习了过滤器,还学习了EL表达式和JSTL标签库,它们的出现就是避免我们的JSP页面中有过多的java代码或者jsp表达式。我们要运用今天所学知识改造页面。
4.2 案例实现
4.2.1 过滤器解决全局乱码问题
-
代码
package com.itheima.filter; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* 解决全局乱码问题 */ @WebFilter("/*")// /* 代表过滤所有的url public class EncodingFilter implements Filter{ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) { try{ //1.将请求和响应对象转换为和HTTP协议相关 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; //2.设置编码格式 request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); //3.放行 filterChain.doFilter(request,response); } catch (Exception e) { e.printStackTrace(); } } }
4.2.2 过滤解决检查登陆
-
代码
package com.itheima.filter; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* 检查登录 */ @WebFilter(value = {"/addStudent.jsp","/listStudentServlet"})//过滤某些url通过value来指定 public class LoginFilter implements Filter{ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) { try{ //1.将请求和响应对象转换为和HTTP协议相关 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; //2.获取会话域对象中数据 Object username = request.getSession().getAttribute("username"); //3.判断用户名 if(username == null || "".equals(username)) { //重定向到登录页面 response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } //4.放行 filterChain.doFilter(request,response); } catch (Exception e) { e.printStackTrace(); } } }
4.2.3 EL表达式和JSTL优化JSP界面
-
addStudent.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>添加学生</title> </head> <body> <%--注意:el表达式中如果想获取request对象,只能通过pageContext页面域获取 pageContext.request,相当于java代码块里的pageContext.getRequest() --%> <form action="${pageContext.request.contextPath}/addStudentServlet" method="get" autocomplete="off"> 学生姓名:<input type="text" name="username"> <br> 学生年龄:<input type="number" name="age"> <br> 学生成绩:<input type="number" name="score"> <br> <button type="submit">保存</button> </form> </body> </html>
- 重点:在jsp中通过EL表达式获取项目虚拟路径:${pageContext.request.contextPath}
-
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <head> <title>学生管理系统首页</title> </head> <body> <%-- 获取会话域中的数据 如果获取到了则显示添加和查看功能的超链接 如果没获取到则显示登录功能的超链接 --%> <c:if test="${sessionScope.username eq null}"> <a href="${pageContext.request.contextPath}/login.jsp">请登录</a> </c:if> <c:if test="${sessionScope.username ne null}"> <a href="${pageContext.request.contextPath}/addStudent.jsp">添加学生</a> <a href="${pageContext.request.contextPath}/listStudentServlet">查看学生</a> </c:if> </body> </html>
- 注意:EL表达式会自动查找四大域对象中的共享数据,所以
${sessionScope.username eq null}
中的sessionScope
可以省略,写成${username eq null}
- 注意:EL表达式会自动查找四大域对象中的共享数据,所以
-
listStudent.jsp
<%@ page import="com.itheima.bean.Student" %> <%@ page import="java.util.ArrayList" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <head> <title>查看学生</title> </head> <body> <table width="600px" border="1px"> <tr> <th>学生姓名</th> <th>学生年龄</th> <th>学生成绩</th> </tr> <%-- s是students遍历出来的每一个对象,EL表达式中可以使用此遍历 --%> <c:forEach items="${students}" var="s"> <tr align="center"> <td>${s.username}</td> <td>${s.age}</td> <td>${s.score}</td> </tr> </c:forEach> </table> </body> </html>
-
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>学生登录</title> </head> <body> <form action="${pageContext.request.contextPath}/loginStudentServlet" method="get" autocomplete="off"> 姓名:<input type="text" name="username"> <br> 密码:<input type="password" name="password"> <br> <button type="submit">登录</button> </form> </body> </html>