EL表达式,全称是Expression Language。意为表达式语言。它是Servlet规范中的一部分,是JSP2.0规范加入的内容。其作用是用于在JSP页面中获取数据,从而让我们的JSP脱离java代码块和JSP表达式。
lambda表达式
xml(xpath路径表达式)
正则表达式
表达式: 以简短的符号表示复杂的内容
作用:作用:在 JSP 页面中获取数据。替换和简化jsp页面中Java代码的编写
语法:${表达式内容}
EL表达式主要简化从域对象(4个域)中获取数据
语法
* 标准(了解)
1. ${pageScope.键名}
从page域中获取指定键名对应的值
2. ${requestScope.键名}
从request域中获取指定键名对应的值
3. ${sessionScope.键名}
从session域中获取指定键名对应的值
4. ${applicationScope.键名}
从servletContext域中获取指定键名对应的值
* 简化(掌握)
${键名}
特点:默认从最小域开始找,找到后直接显示,不在继续寻找
小结:要求四个域键名唯一
创建两个实体类,User和Address**
/**
* 用户的实体类
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class User implements Serializable{
private String name = "黑马程序员";
private int age = 18;
private Address address = new Address();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
/**
* 地址的实体类
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class Address implements Serializable {
private String province = "北京";
private String city = "昌平区";
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
1. 获取字符串
${键名}
2. 获取对象(User)
${键名.属性名}
3. 获取List(Array)集合
${键名[索引]}
4. 获取Map集合
${键名.key}
${键名["key"]}
5. 补充
el不会出现null和索引角标越界问题
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>demo1</title>
</head>
<body>
<%--EL表达式概念:
它是Expression Language的缩写。它是一种替换jsp表达式的语言。
EL表达式的语法:
${表达式}
表达式的特点:有明确的返回值。
EL表达式就是把内容输出到页面上
EL表达式的注意事项:
1.EL表达式没有空指针异常
2.EL表达式没有数组下标越界
3.EL表达式没有字符串拼接
EL表达式的数据获取:
它只能在四大域对象中获取数据,不在四大域对象中的数据它取不到。
它的获取方式就是findAttribute(String name)
<h3>el表达式基本语法</h3>
<br/>-----------获取对象数据---------------------<br/>
<% //1.把用户信息存入域中
User user = new User();
pageContext.setAttribute("u",user);
%>
${u}===============输出的是内存地址<%--就相当于调用此行代码<%=pageContext.findAttribute("u")%> --%><br/>
${u.name}<%--就相当于调用此行代码<% User user = (User) pageContext.findAttribute("u");out.print(user.getName());%> --%><br/>
${u.age}
<br/>-----------获取关联对象数据------------------<br/>
${u.address}==========输出的address对象的地址<br/>
${u.address.province}${u.address.city}<br/>
${u["address"]['province']}
<br/>-----------获取数组数据---------------------<br/>
<% String[] strs = new String[]{"He","llo","Expression","Language"};
pageContext.setAttribute("strs", strs);
%>
${strs[0]}==========取的数组中下标为0的元素<br/>
${strs[3]}
${strs[5]}===========如果超过了数组的下标,则什么都不显示<br/>
${strs["2"]}=========会自动为我们转换成下标<br/>
${strs['1']}
<br/>-----------获取List集合数据-----------------<br/>
<% List<String> list = new ArrayList<String>();
list.add("AAA");
list.add("BBB");
list.add("CCC");
list.add("DDD");
pageContext.setAttribute("list", list);
%>
${list}<br/>
${list[0] }<br/>
${list[3] }<br/>
<br/>-----------获取Map集合数据------------------<br/>
<% Map<String,User> map = new HashMap<String,User>();
map.put("aaa",new User());
pageContext.setAttribute("map", map);
%>
${map}<br/>
${map.aaa}<%--获取map的value,是通过get(Key) --%><br/>
${map.aaa.name}${map.aaa.age}<br/>
${map["aaa"].name }
</body>
</html>
在使用EL表达式时,它帮我们做了一些处理,使我们在使用时可以避免一些错误。它没有空指针异常,没有数组下标越界,没有字符串拼接。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>EL表达式的注意事项</title>
</head>
<body>
<%--EL表达式的三个没有--%>
第一个:没有空指针异常<br/>
<% String str = null;
request.setAttribute("testNull",str);
%>
${testNull}
<hr/>
第二个:没有数组下标越界<br/>
<% String[] strs = new String[]{"a","b","c"};
request.setAttribute("strs",strs);
%>
取第一个元素:${strs[0]}
取第六个元素:${strs[5]}
<hr/>
第三个:没有字符串拼接<br/>
<%--${strs[0]+strs[1]}--%>
${strs[0]}+${strs[1]}
</body>
</html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="com.itheima.domain.User" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>EL两个特殊的运算符</title>
</head>
<body>
<%--empty运算符:
它会判断:对象是否为null,字符串是否为空字符串,集合中元素是否是0个
--%>
<% String str = null;
String str1 = "";
List<String> slist = new ArrayList<String>();
pageContext.setAttribute("str", str);
pageContext.setAttribute("str1", str1);
pageContext.setAttribute("slist", slist);
%>
${empty str}============当对象为null返回true<br/>
${empty str1 }==========当字符串为空字符串是返回true(注意:它不会调用trim()方法)<br>
${empty slist}==========当集合中的元素是0个时,是true
<hr/>
<%--三元运算符
条件?真:假
--%>
<% request.setAttribute("gender", "female"); %>
<input type="radio" name="gender" value="male" ${gender eq "male"?"checked":""} >男
<input type="radio" name="gender" value="female" ${gender eq "female"?"checked":""}>女
</body>
</html>
隐式对象介绍
EL表达式也为我们提供隐式对象,可以让我们不声明直接来使用,十一个对象见下表,需要注意的是,它和JSP的隐式对象不是一回事:
1. pageContext
就是jsp九大内置对象之一,这哥们可以获得其他八个内置对象
2. cookie
可以获取浏览器指定cookie名称的值
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,值是value(一个) |
HeaderValues | Java.util.Map | 没有 | 请求消息头key,值是数组(一个头多个值) |
Param | Java.util.Map | 没有 | 请求参数key,值是value(一个) |
ParamValues | Java.util.Map | 没有 | 请求参数key,值是数组(一个名称多个值) |
InitParam | Java.util.Map | 没有 | 全局参数,key是参数名称,value是参数值 |
Cookie | Java.util.Map | 没有 | Key是cookie的名称,value是cookie对象 |
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>demo4</title>
</head>
<body>
<h3>el隐式对象..</h3>
${pageContext.request.contextPath} 动态获取:项目名称(虚拟路径) <br>
${cookie.JSESSIONID.value} 获取指定cookie名称的值... <br>
</body>
</html>
* jsp默认支持el表达式
servlet2.3规范中,默认不支持el表达式
* 如果要忽略el表达式
1)忽略当前jsp页面中所有的el表达式
设置jsp中page指令中:isELIgnored="true" 属性
2)忽略单个el表达式
\${表达式}
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>demo4</title>
</head>
<body>
<h3>el隐式对象..</h3>
${pageContext.request.contextPath} 动态获取:项目名称(虚拟路径) <br>
\${cookie.JSESSIONID.value} 获取指定cookie名称的值... <br>
</body>
</html>
所以的javaBean指的是符合特定规范的java标准类
使用规范
例如:下面User类,有四个字段(成员变量),有无参构造方法,有一个属性(username)
public class User implements Serializable {
private String username;
private Integer age;
private String gender;
public User() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
Jsp 标准标签库(Jsp Standard Tag Library),是由Apache组织提供的开源的jsp标签库
作用:替换和简化jsp页面中java代码的编写
一般要配合EL表达式一起使用,来实现在jsp中不出现java代码段。
问题: jsp过度使用,html和java代码混杂,不利于阅读和长期维护
书写: html + java -> html标签
解决: 标签库(用标签代替java代码书写)
写起来像html,本质还是servlet
JSTL标准标签库有5个子库,但随着发展,目前常使用的是它的核心库(Core)
标签库 | 标签库的URI | 前缀 |
---|---|---|
Core | http://java.sun.com/jsp/jstl/core | c |
国际化(几乎不用) | http://java.sun.com/jsp/jstl/fmt | fmt |
SQL(过时) | http://java.sun.com/jsp/jstl/sql | sql |
XML(过时) | http://java.sun.com/jsp/jstl/xml | x |
Functions(几乎不用) | http://java.sun.com/jsp/jstl/functions | fn |
1.要想使用JSTL标签库,在javaweb工程中需要导入坐标。首先是在工程的WEB-INF目录中创建一个lib目录,接下来把jstl的jar拷贝到lib目录中,最后在jar包上点击右键,然后选择【Add as Libary】添加。如下图所示:
* 相当于,java中 if(){}
语法
<c:if test="boolean值">c:if>
true:显示标签体内容
false:隐藏标签体内容
通常与el表达式一起使用
注意:此标签没有else功能,如果想实现else效果,请让条件取反
<%@ page import="cn.itcast.domain.User" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>demo1</title>
</head>
<body>
<%
User user = new User();
user.setUsername("jack");
request.setAttribute("user", user);
%>
<c:if test="${empty user}">
你好,请登录...
</c:if>
<c:if test="${not empty user}">
你好,${user.username}
</c:if>
<c:choose>
<c:when test="${pageScope.score eq 'A' }">
AAA
</c:when>
<c:when test="${pageScope.score eq 'B' }">BBB
</c:when>
<c:when test="${pageScope.score eq 'C' }">CCC
</c:when>
<c:when test="${pageScope.score eq 'D' }">DDD
</c:when>
<c:otherwise>其他</c:otherwise>
</c:choose>
</body>
</html>
② c:forEach标签
* 相当于java中的for循环
1)普通for
for(int i=1; i<=5; i++){
i
}
<c:forEach begin="1" end="5" step="1" var="i">
${i}
c:forEach>
begin="1" 起始值(包含)
end="5" 结束值(包含)
step="1" 步长为1
var="i" 当前输出临时变量
2)增强for
for(User user : list){
user
}
<c:forEach items="${list}" var="user" varStatus="vs">
${user}
c:forEach>
items="list" 集合
var="user" 当前输出的临时变量
varStatus="vs" 变量状态
index 当前索引 从0开始
count 计数器 从1开始
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.Collections" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
1. prefix : 前缀 (区分不同的库)
2. uri : 库的标识
core : java核心代码 -> 标签
xml : xml语法 -> 标签
<c:if>
<x:if>
--%>
<html>
<head>
<title>Title</title>
</head>
<body>
<!--
使用jstl标签库
1. web-inf/lib 导入jstl两个库
2. 指令 taglib
学习两个标签
1. c:if
a. 必要属性 test= el表达式
b. test为true,才会执行标签体的内容
2. c:foreach
a. 普通for循环
b. 增强for循环
-->
<div>
<%
int n = 10;
request.setAttribute("n",n);
%>
<%
if(n > 5){
out.print("呵呵");
}
%>
<c:if test="${n > 6}">
嘻嘻
</c:if>
</div>
<hr>
<div>
<%--
2. c:foreach
a. 普通for循环
var : variable 被循环赋值的变量
(会被存进pageContext域对象中,所以用el表达式取)
begin: 起始值
end : 结束值
step : 步长(默认为1)
b. 增强for循环
var : 同上
items : 被遍历的集合或数组
varStatus : 记录当前的循环的状态
index : 索引
count : 计数
--%>
<%
for (int i = 0; i <= 9; i+=2) {
out.print(i + " ");
}
%>
<br>
<c:forEach var="i" begin="0" end="9" step="2">
${i}
</c:forEach>
<br>
<%
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张三","李四","王五");
request.setAttribute("list",list);
for (String element : list) {
out.print(element + " ");
}
%>
<br>
<c:forEach var="element" items="${list}" varStatus="status">
${status.index}, ${status.count}, ${element} <br>
</c:forEach>
</div>
</body>
</html>
三 c:out输出和c:set往域中设置值
<%-- 向域对象中设置值,默认pageScope --%>
<c:set var="username" value="abc"/>
<%-- 向指定的域对象中设置值--%>
<c:set var="username" value="bcd" scope="application"/>
<%-- 字符串拼接--%>
<c:set var="username" value="${'123'.concat('456')}" scope="request"/>
<%-- 获取值 --%>
<br><br><br><br><br>
username:${username} <br>
pageScope.username:${pageScope.username} <br>
requestScope.username:${requestScope.username} <br>
sessionScope.username:${sessionScope.username} <br>
applicationScope.username:${applicationScope.username} <br>
<%-- 向域对象中设置值,默认pageScope --%>
<c:set var="username" value="abc"/>
<%-- 向指定的域对象中设置值--%>
<c:set var="username" value="bcd" scope="application"/>
<%-- 字符串拼接--%>
<c:set var="username" value="${'123'.concat('456')}" scope="request"/>
<%-- 获取值 --%>
<br><br><br><br><br>
username:${username} <br>
pageScope.username:${pageScope.username} <br>
requestScope.username:${requestScope.username} <br>
sessionScope.username:${sessionScope.username} <br>
applicationScope.username:${applicationScope.username} <br>
组件 | 作用 | 实现接口 |
---|---|---|
Servlet | 小应用程序,在JavaWeb中主要做为控制器来使用 可以处理用户的请求并且做出响应 | javax.servlet.Servlet |
Filter | 过滤器,对用户发送的请求或响应进行集中处理,实现请求的拦截 | javax.servlet.Filter |
Listener | 监听器,在某些框架中会使用到监听器(比如spring),在Web执行过程中,引发一些事件,对相应事件进行处理 | javax.servlet.XxxListener 每个事件有一个接口 |
过滤器——Filter,它是JavaWeb三大组件之一。另外两个是Servlet和Listener。
它是在2000年发布的Servlet2.3规范中加入的一个接口。是Servlet规范中非常实用的技术。
它可以对web应用中的所有资源进行拦截,并且在拦截之后进行一些特殊的操作。
常见应用场景:URL级别的权限控制;过滤敏感词汇;中文乱码问题等等。
生活中的过滤器
净水器、空气净化器、地铁安检
web中的过滤器
当用户访问服务器资源时,过滤器将请求拦截下来,完成一些通用的操作
应用场景
//@WebServlet(value = {"/MyServlet"})
//@WebServlet(value = "/MyServlet") //注解的数组属性,只有一个值,可以去掉{}
//@WebServlet(value = "/MyServlet") //注解只有一个属性需要赋值,并且属性名为value,value=可以省略
//@WebServlet("/MyServlet")
@WebServlet(value = {"/MyServlet","/MyServlet2"}) //一个servlet配置多个虚拟路径
public class MyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("资源: servlet被访问了");
}
}
① 编写java类,实现filter接口
/*
* 1. 定义一个类实现Filter接口(注意包名)
* 2. 配置web.xml(或注解)
* */
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器拦截请求了");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
② 配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--
filter的web.xml配置
1. filter和filter-mapping的子标签filter-name必须一致(可以自定义,通常与类名相同)
2. url-pattern : 当前filter要拦截的虚拟路径
-->
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.itheima01.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/MyServlet</url-pattern>
</filter-mapping>
</web-app>
编写java类,实现filter接口
@WebFilter(value = "/MyServlet2")
public class MyFilter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器拦截请求了2");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
1)Filter
2)FilterConfig
3)FilterChain
1)生命周期
出生——活着——死亡
**出生:**当应用加载的时候执行实例化和初始化方法。
**活着:**只要应用一直提供服务,对象就一直存在。
**死亡:**当应用卸载时,或者服务器宕机时,对象消亡。
Filter的实例对象在内存中也只有一份。所以也是单例的。
// 初始化方法
public void init(FilterConfig config);
// 执行拦截方法
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain);
// 销毁方法
public void destroy();
* 创建
服务器启动项目加载,创建filter对象,执行init方法(只执行一次)
* 运行(过滤拦截)
用户访问被拦截目标资源时,执行doFilter方法
* 销毁
服务器关闭项目卸载时,销毁filter对象,执行destroy方法(只执行一次)
* 补充:
过滤器一定是优先于servlet创建的,后于Servlet销毁
/*
* Filter的生命周期方法
* 1. init
* a. filter是tomcat启动加载时创建(先于servlet创建)
* b. 此方法只会执行一次, 一般用来初始化数据的
* 2. doFilter
* a. 浏览器每访问一次就会执行一次
* b. 其中有一个方法 chain.doFilter(req, resp);
* 如果不调用,后续资源不再执行
* 如果调用,后续资源会执行
* c. 此方法会拦截请求, 也会拦截响应
* 3. destroy
* a. tomcat关闭之前,调用此方法(后于servlet销毁的)
* b. 此方法只会执行一次,一般用来序列化数据和释放资源的
* */
@WebFilter(urlPatterns = "/LifeServlet")
public class LifeFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
System.out.println("filter init");
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("filter doFilter before");
/*
放行: 允许请求继续往后传递
a. 很像之前的请求转发
b. 如果后续资源是servlet的话,执行service方法
*/
chain.doFilter(req, resp);
System.out.println("filter doFilter after");
}
public void destroy() {
System.out.println("filter destroy");
}
}
// load-on-starup : tomcat启动就加载
@WebServlet(value = "/LifeServlet",loadOnStartup = 4)
public class LifeServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("servlet init");
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("servlet service");
}
@Override
public void destroy() {
System.out.println("servlet destroy");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
}
在开发时,我们可以指定过滤器的拦截路径来定义拦截目标资源的范围
* 精准匹配
用户访问指定目标资源(/show.jsp)时,过滤器进行拦截
* 目录匹配
用户访问指定目录下(/user/*)所有资源时,过滤器进行拦截
* 后缀匹配
用户访问指定后缀名(*.html)的资源时,过滤器进行拦截
* 匹配所有
用户访问该网站所有资源(/*)时,过滤器进行拦截
/*
#拦截路径的匹配模式
* 精准匹配
用户访问指定目标资源(/show.jsp)时,过滤器进行拦截
* 目录匹配
用户访问指定目录下(/user/*)所有资源时,过滤器进行拦截
* 后缀匹配
用户访问指定后缀名(*.html)的资源时,过滤器进行拦截
* 匹配所有
用户访问该网站所有资源(/*)时,过滤器进行拦截
url格式:
协议://ip:port/资源位置
* */
//@WebFilter(urlPatterns = "/doc/hello.html") //精准拦截
//@WebFilter(urlPatterns = "/doc/*") //拦截doc目录下的所有资源
//@WebFilter("*.html") // 拦截所有为html所有资源
//@WebFilter("/*")
@WebFilter(urlPatterns = {"/doc/hello.html","/index.jsp"})
public class UrlFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("拦截了");
chain.doFilter(req, resp);
}
public void destroy() {
}
}
在开发时,我们可以指定过滤器的拦截方式来处理不同的应用场景,比如:只拦截从浏览器直接发送过来的请求,或者拦截内部转发的请求
总共有五种不同的拦截方式,我们这里学习常见的两种
1. request(默认拦截方式)
浏览器直接发送请求时,拦截
2. forward
请求转发的时候,拦截
比如: 资源A转发到资源B时
我们可以配置 二个同时存在...
① xml版本
public class MethodFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("method 拦截了");
chain.doFilter(req, resp);
}
public void destroy() {
}
}
<filter>
<filter-name>MethodFilterfilter-name>
<filter-class>com.itheima04.method.MethodFilterfilter-class>
filter>
<filter-mapping>
<filter-name>MethodFilterfilter-name>
<url-pattern>/doc/hello.htmlurl-pattern>
<dispatcher>REQUESTdispatcher>
<dispatcher>FORWARDdispatcher>
filter-mapping>
② 注解版本
//@WebFilter(urlPatterns = "/doc/hello.html",dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})
@WebFilter(urlPatterns = "/doc/hello.html",dispatcherTypes = DispatcherType.FORWARD)
public class MethodFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("method 拦截了");
chain.doFilter(req, resp);
}
public void destroy() {
}
}
<!--配置过滤器-->
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.itheima.web.filter.FilterDemo1</filter-class>
<!--配置开启异步支持,当dispatcher配置ASYNC时,需要配置此行-->
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/ServletDemo1</url-pattern>
<!--过滤请求:默认值。-->
<dispatcher>REQUEST</dispatcher>
<!--过滤全局错误页面:当由服务器调用全局错误页面时,过滤器工作-->
<dispatcher>ERROR</dispatcher>
<!--过滤请求转发:当请求转发时,过滤器工作。-->
<dispatcher>FORWARD</dispatcher>
<!--过滤请求包含:当请求包含时,过滤器工作。它只能过滤动态包含,jsp的include指令是静态包含-->
<dispatcher>INCLUDE</dispatcher>
<!--过滤异步类型,它要求我们在filter标签中配置开启异步支持-->
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
在一次请求中,若我们请求匹配到了多个filter,通过请求就相当于把这些filter串起来了,形成了过滤器链
* 需求
用户访问目标资源 show.jsp时,经过 FilterA FilterB
* 过滤器链执行顺序 (先进后出)
1.用户发送请求
2.FilterA拦截,放行
3.FilterB拦截,放行
4.执行目标资源 show.jsp
5.FilterB增强响应
6.FilterA增强响应
7.封装响应消息格式,返回到浏览器
* 过滤器链中执行的先后问题....
配置文件
谁先声明,谁先执行
<filter-mapping>
注解【不推荐】
根据过滤器类名进行排序,值小的先执行
FilterA FilterB 进行比较, FilterA先执行...
//@WebFilter(urlPatterns = "/ServletA")
public class FilterA implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("FilterA执行了");
/*
* 放行: 允许请求往后继续传递
* 1. 等价于请求转发
* 2. 如果后续还有过滤器,先执行过滤器, 知道过滤器执行完毕,才到资源里去
* */
chain.doFilter(req, resp);
}
public void destroy() {
}
}
//@WebFilter(urlPatterns = "/ServletA")
public class FilterB implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("FilterB执行了");
chain.doFilter(req, resp);
}
public void destroy() {
}
}
<filter>
<filter-name>FilterB</filter-name>
<filter-class>com.itheima05.chain.FilterB</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterB</filter-name>
<url-pattern>/ServletA</url-pattern>
</filter-mapping>
<filter>
<filter-name>FilterA</filter-name>
<filter-class>com.itheima05.chain.FilterA</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterA</filter-name>
<url-pattern>/ServletA</url-pattern>
</filter-mapping>
多个过滤器的先后执行顺序
web.xml配置
和配置文件的编写顺序决定运行的顺序,准确的说是,根据mapping的顺序决定(由上到下执行)
注解开发
注解开发没有配置文件的
按照类名的自然顺序决定:A-B-C
如果存在配置文件,配置文件优先
需求
tomcat8.5版本中已经将get请求的中文乱码解决了,但是post请求还存在中文乱码
浏览器发出的任何请求,通过过滤器统一处理中文乱码
@WebFilter(urlPatterns = "/*") // 全站拦截
public class PostFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req; // 引用类型 req地址 0x0001
HttpServletResponse response = (HttpServletResponse) resp;
String method = request.getMethod();
if("POST".equalsIgnoreCase(method)){
request.setCharacterEncoding("utf-8");
}
chain.doFilter(req, resp);
}
public void destroy() {
}
}
需求
当用户发出非法言论的时候,提示用户言论非法警告信息
需求分析
代码实现
① 非法词库
注意修改读properties的格式 ,统一utf-8,不然会乱码
② WordsFilter
@WebFilter("/WordsServlet")
public class WordsFilter implements Filter {
private List<String> wordList;
public void init(FilterConfig config) throws ServletException {
// 1.加载配置文件
/*
ResourceBundle专门读取src目录下的properties配置文件,不需要写后缀名
*/
ResourceBundle words = ResourceBundle.getBundle("words");
// 2.读取keyword关键字内容
String keyword = words.getString("keyword"); // 傻叉,大爷的,二大爷的
// 3.split切割,转为list集合
wordList = Arrays.asList(keyword.split(","));
System.out.println("加载非法词库:"+wordList);
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException {
// 向下转型
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 1.获取用户输入的值
String content = request.getParameter("content");
// 2.拦截非法内容,提示
for (String word : wordList) { // 遍历非法词库
if(content.contains(word)){ // 判断是否包含非法词汇
response.getWriter().write("你输入的词汇敏感,拦截了。。。");
return;
}
}
// 3.放行
chain.doFilter(request, response);
}
public void destroy() {
}
}
生活中的监听器
我们很多商场有摄像头,监视着客户的一举一动。如果客户有违法行为,商场可以采取相应的措施。
观察者设计模式
观察者设计模式。因为所有的监听器都是观察者设计模式的体现。
那什么是观察者设计模式呢?
它是事件驱动的一种体现形式。就好比在做什么事情的时候被人盯着。当对应做到某件事时,触发事件。
观察者模式通常由以下三部分组成:
事件源:触发事件的对象。
事件:触发的动作,里面封装了事件源。
监听器:当事件源触发事件时,要做的事情。一般是一个接口,由使用者来实现。
javaweb中的监听器
在我们的java程序中,有时也需要监视某些事情,一旦被监视的对象发生相应的变化,我们应该采取相应的操作。
监听web三大域对象:HttpServletRequest、HttpSession、ServletContext (创建和销毁)
场景
历史访问次数、统计在线人数、系统启动时初始化配置信息
监听器的接口分类
事件源 | 监听器接口 | 时机 |
---|---|---|
ServletContext | ServletContextListener | 上下文域创建和销毁 |
ServletContext | ServletContextAttributeListener | 上下文域属性增删改的操作 |
**HttpSession ** | HttpSessionListener | 会话域创建和销毁 |
**HttpSession ** | HttpSessionAttributeListener | 会话域属性增删改的操作 |
HttpServletRequest | ServletRequestListener | 请求域创建和销毁 |
HttpServletRequest | ServletRequestAttributeListener | 请求域属性增删改的操作 |
监听器在web开发中使用的比较少,见的机会就更少了,今天我们使用ServletContextListenner来带领大家学习下监听器,因为这个监听器是监听器中使用率最高的一个,且监听器的使用方式都差不多。
我们使用这个监听器可以在项目启动和销毁的时候做一些事情,例如,在项目启动的时候加载配置文件。
步骤分析
1. 创建一个普通类,实现ServletContextListenner
2. 重写抽象方法
监听ServletContext创建
监听ServletContext销毁
3. 配置
web.xml
注解
① xml版本
/*
* 1. 定义一个类实现 ServletContextListener 接口, 重写方法
* (监听ServletContext的创建和销毁)
*
* 2. 配置web.xml(或注解)
*
* */
public class MyServletContextListener implements ServletContextListener {
/*
* ServletContext创建时,执行一次
* */
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext创建了");
}
/*
* ServletContext销毁时,执行一次
* */
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext销毁了");
}
}
<listener>
<listener-class>com.itheima07.listener.MyServletContextListenerlistener-class>
listener>
② 注解版本
@WebListener
public class MyListener implements ServletContextListener {
// 监听ServletContext创建
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext已经创建了...");
}
// 监听ServletContext销毁
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext已经销毁了....");
}
}
监听对象创建的
1)ServletContextListener
/**
* 用于监听ServletContext对象创建和销毁的监听器
* @since v 2.3
*/
public interface ServletContextListener extends EventListener {
/**
* 对象创建时执行此方法。该方法的参数是ServletContextEvent事件对象,事件是【创建对象】这个动作
* 事件对象中封装着触发事件的来源,即事件源,就是ServletContext
*/
public default void contextInitialized(ServletContextEvent sce) {
}
/**
* 对象销毁执行此方法
*/
public default void contextDestroyed(ServletContextEvent sce) {
}
}
2)HttpSessionListener
/**
* 用于监听HttpSession对象创建和销毁的监听器
* @since v 2.3
*/
public interface HttpSessionListener extends EventListener {
/**
* 对象创建时执行此方法。
*/
public default void sessionCreated(HttpSessionEvent se) {
}
/**
* 对象销毁执行此方法
*/
public default void sessionDestroyed(HttpSessionEvent se) {
}
}
3)ServletRequestListener
/**
* 用于监听ServletRequest对象创建和销毁的监听器
* @since Servlet 2.4
*/
public interface ServletRequestListener extends EventListener {
/**
* 对象创建时执行此方法。
*/
public default void requestInitialized (ServletRequestEvent sre) {
}
/**
* 对象销毁执行此方法
*/
public default void requestDestroyed (ServletRequestEvent sre) {
}
}
监听域中属性发生变化的
1)ServletContextAttributeListener
/**
* 用于监听ServletContext域(应用域)中属性发生变化的监听器
* @since v 2.3
*/
public interface ServletContextAttributeListener extends EventListener {
/**
* 域中添加了属性触发此方法。参数是ServletContextAttributeEvent事件对象,事件是【添加属性】。
* 事件对象中封装着事件源,即ServletContext。
* 当ServletContext执行setAttribute方法时,此方法可以知道,并执行。
*/
public default void attributeAdded(ServletContextAttributeEvent scae) {
}
/**
* 域中删除了属性触发此方法
*/
public default void attributeRemoved(ServletContextAttributeEvent scae) {
}
/**
* 域中属性发生改变触发此方法
*/
public default void attributeReplaced(ServletContextAttributeEvent scae) {
}
}
2)HttpSessionAttributeListener
/**
* 用于监听HttpSession域(会话域)中属性发生变化的监听器
* @since v 2.3
*/
public interface HttpSessionAttributeListener extends EventListener {
/**
* 域中添加了属性触发此方法。
*/
public default void attributeAdded(HttpSessionBindingEvent se) {
}
/**
* 域中删除了属性触发此方法
*/
public default void attributeRemoved(HttpSessionBindingEvent se) {
}
/**
* 域中属性发生改变触发此方法
*/
public default void attributeReplaced(HttpSessionBindingEvent se) {
}
}
3)ServletRequestAttributeListener
/**
* 用于监听ServletRequest域(请求域)中属性发生变化的监听器
* @since Servlet 2.4
*/
public interface ServletRequestAttributeListener extends EventListener {
/**
* 域中添加了属性触发此方法。
*/
public default void attributeAdded(ServletRequestAttributeEvent srae) {
}
/**
* 域中删除了属性触发此方法
*/
public default void attributeRemoved(ServletRequestAttributeEvent srae) {
}
/**
* 域中属性发生改变触发此方法
*/
public default void attributeReplaced(ServletRequestAttributeEvent srae) {
}
}
和会话相关的两个感知型监听器
此处要跟同学们明确一下,和会话域相关的两个感知型监听器是无需配置的,直接编写代码即可。
1)HttpSessionBinderListener
/**
* 用于感知对象和和会话域绑定的监听器
* 当有数据加入会话域或从会话域中移除,此监听器的两个方法会执行。
* 加入会话域即和会话域绑定
* 从会话域移除即从会话域解绑
*/
public interface HttpSessionBindingListener extends EventListener {
/**
* 当数据加入会话域时,也就是绑定,此方法执行
*/
public default void valueBound(HttpSessionBindingEvent event) {
}
/**
* 当从会话域移除时,也就是解绑,此方法执行
*/
public default void valueUnbound(HttpSessionBindingEvent event) {
}
}
2)HttpSessionActivationListener
/**
* 用于感知会话域中对象钝化和活化的监听器
*/
public interface HttpSessionActivationListener extends EventListener {
/**
* 当会话域中的数据钝化时,此方法执行
*/
public default void sessionWillPassivate(HttpSessionEvent se) {
}
/**
* 当会话域中的数据活化时(激活),此方法执行
*/
public default void sessionDidActivate(HttpSessionEvent se) {
}
}
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<context-param>
<param-name>configLocationparam-name>
<param-value>words.propertiesparam-value>
context-param>
web-app>
@WebListener
public class MyListener implements ServletContextListener {
// 监听ServletContext创建
/*
ServletContextEvent 上下文事件对象,获取ServletContext
*/
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext已经创建了...");
// 通过servletContextEvent获取上下文对象
ServletContext servletContext = servletContextEvent.getServletContext();
// 我可以去加载公司定义配置文件的名称....
String configLocation = servletContext.getInitParameter("configLocation");
System.out.println("动态获取配置文件名称:" + configLocation);
}
// 监听ServletContext销毁
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext已经销毁了....");
}
}
需求
有用户使用网站,在线人数就+1;用户退出网站,在线人数就-1
技术分析
使用 ServletContext域对象 存储在线总人数
使用 ServletContextListener监听器,在项目启动时,初始化总人数为0
使用 HttpSessionListener监听器,用户访问,人数+1,用户退出,人数-1
使用 LogoutServlet控制器,对当前会话的session销毁
① InitNumberListener
@WebListener
public class InitNumberListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
// 获取上下文域对象
ServletContext servletContext = servletContextEvent.getServletContext();
// 初始化在线人数
servletContext.setAttribute("number", 0);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
② NumberChangeListener
@WebListener
public class NumberChangeListener implements HttpSessionListener {
// 会话建立,在线人数+1
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
// 获取session域对象
HttpSession session = httpSessionEvent.getSession();
// 获取上下文域对象
ServletContext servletContext = session.getServletContext();
// 取出在线人数
Integer number = (Integer) servletContext.getAttribute("number");
// +1
servletContext.setAttribute("number", number + 1);
}
// 会话销毁,在线人数-1
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
// 获取session域对象
HttpSession session = httpSessionEvent.getSession();
// 获取上下文域对象
ServletContext servletContext = session.getServletContext();
// 取出在线人数
Integer number = (Integer) servletContext.getAttribute("number");
// -1
servletContext.setAttribute("number", number - 1);
}
}
③ index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
${NAME}
listener知识学习
在线人数:${applicationScope.number}
用户退出
④ LogoutServlet
@WebServlet("/LogoutServlet")
public class LogoutServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 销毁session
request.getSession().invalidate();
response.getWriter().write("logout");
}
}
el是JSP内置的主要用于简化jsp页面中Java代码的编写
语法 ${表达式内容}
从四大域对象中获取数据,从小到大获取
没有空指针,越界和拼接
JSTL是Apache提供的,需要导包,替换和简化页面中java代码的,本质是servlet
<c:if 标签>
语句体
c:if>
Filter过滤器,实现Filter接口,可以注解配置,也可以web.xml配置
登录权限效验,全站解决乱码,敏感字符过滤
多个过滤器形成链
1.用户发送请求
2.FilterA拦截,放行
3.FilterB拦截,放行
4.执行目标资源 show.jsp
5.FilterB增强响应
6.FilterA增强响应
7.封装响应消息格式,返回到浏览器
执行顺序:web.xml是由上往下执行,注解的话是字典顺序A-B-C ,配置优先
Listener监听器,实现对应的接口
历史访问次数,统计在线人户,初始化配置信息
ServletContextListener是创建和销毁
ServletContextAttributeListerner是增删覆盖的操作
@webListener注册