1 EL表达式
1.1 EL表达式概述
EL(Express Lanuage)表达式可以嵌入在jsp页面内部,减少jsp脚本的编写,EL 出现的目的是要替代jsp页面中输出脚本<%= 数据 %>的编写。
1.2 EL表达式的格式和作用
EL表达式的格式:
${EL表达式内容}
EL表达式的作用:
- 从域对象中查找指定的数据。
- EL内置对象的使用
- 执行运算符
1.3 EL表达式从域中取出数据
从域对象中取出数据
ServletContext 整个Web程序
session 一次会话
request 一次请求
pageContext 当前jsp页面
举例子:
<%
//ServletContext 域对象存储键值对
application.setAttribute("key1","app");
//session 域对象存储键值对
session.setAttribute("key1","session");
//request 域对象存储键值对
request.setAttribute("key1","request");
//pageContext 域对象存储键值对
pageContext.setAttribute("key1","page");
%>
<%--
原始方式 < % %> 取出数据
--%>
application:: <%= application.getAttribute("key") %> <br/>
session:: <%= session.getAttribute("key")%> <br/>
request:: <%= request.getAttribute("key")%> <br/>
pageContext:: <%= pageContext.getAttribute("key")%> <br/>
<hr>
<%--
EL 表达式取出数据
xxxxScope.key
${applicationScope.键名} //ServletContext 域对象存储键值对
${sessionScope.键名} //session 域对象存储键值对
${requestScope.键名} //request 域对象存储键值对
${pageScope.键名} //pageContext 域对象存储键值对
--%>
application:: ${applicationScope.key}<br/>
session:: ${sessionScope.key}<br>
request:: ${requestScope.key}<br>
pageContext::${pageScope.key}
<hr>
<%--
EL 表达式取出域对象的数据,简化方式,推荐使用
${键名} 将会从最小的域对象开始查找,一旦找到,就不会在查找
友好性: 客户端的友好性
< %=%> 取出域对象数据,没有此键,页面中显示 null
${} 取出域对象的数据,没有此键, 页面中显示 ""
--%>
${key}
A: EL 表达式取出域对象的数据, 存储的数据是自定义的对象
<%-- < %=%> 取出数据, 张三, 昌平区
pageContext.getAttribute("user") 取出来的对象是User
张三是User对象的成员变量 getUserName() 类型强制转换
--%>
<%= ((User)pageContext.getAttribute("user")).getUsername() %>
<%= ((User)pageContext.getAttribute("user")).getAddress().getArea() %>
<hr>
<%--
${ } 取出数据, 张三, 昌平区
$ {user} 最小域对象中,取出了 user对象
EL 无需强制转换
EL 不需要调用方法,直接写成员变量名即可
--%>
${user.username} 通过发射机制获取私有属性username,不用调用方法
${user.address.area}
B:EL获得List集合的值
<%--
< %= %> 取出数据 李四 武清
pageContext.getAttribute("list") 取出是域对象中的集合List,集合的索引1,才能取到李四
((List<User>) pageContext.getAttribute("list")) 强制转成List集合
--%>
<%= ((List<User>) pageContext.getAttribute("list")).get(1).getUsername() %>
<%= ((List<User>) pageContext.getAttribute("list")).get(1).getAddress().getArea() %>
<hr>
<%--
EL 取出数据 李四 武清
$ {list} 取出域对象pageContext中的集合List,集合的索引1
$ {list[1]} 集合的索引, 1索引就是User对象
--%>
${list[1].username}
${list[1].address.area}
C:EL获得Map的值
<%--
取出数据 李四 武清
是Map集合中的键 user2对应值
域对象键名.Map集合的键名
写法: 防君子不防小人
域对象键名['Map集合键名']
map.put("user.1",user1);
--%>
${map['user2'].username}
${map['user2'].address.area}
1.4 EL的内置对象
JSP有9个内置对象,而EL也有自己的内置对象。EL内置对象总共有11 个,内置对象可以直接拿来使用,如cookie对象。
1.4.1 域属性相关(4个)
- pageScope:从page范围域属性空间中查找指定的key
- requestScope:从request范围域属性空间中查找指定的key
- sessionScope:从session范围域属性空间中查找指定的key
- applicationScope:从application范围域属性空间中查找指定的key
举例子:
<%
//ServletContext 域对象存储键值对
application.setAttribute("key","app");
//session 域对象存储键值对
session.setAttribute("key","session");
//request 域对象存储键值对
request.setAttribute("key","request");
//pageContext 域对象存储键值对
pageContext.setAttribute("key","page");
%>
<%--
EL 表达式取出数据
xxxxScope.key
${applicationScope.键名} //ServletContext 域对象存储键值对
${sessionScope.键名} //session 域对象存储键值对
${requestScope.键名} //request 域对象存储键值对
${pageScope.键名} //pageContext 域对象存储键值对
--%>
application= ${applicationScope.key}<br/>
session= ${sessionScope.key}<br>
reques= ${requestScope.key}<br>
pageContext=${pageScope.key}<br>
1.4.2 其他重要内置对象(4个)
1、pageContext
该pageContext与JSP内置对象pageContext是同一个对象。通过该对象,可以获取到request、response、session、servletContext、servletConfig等对象注意:这些对象在EL里不是内置对象,这些对象只能通过pageContext获取
- 例如,在Servlet中,想获得web应用的名称:
request.getContextPath();
- 那么,在jsp页面上,想获得web应用的名称:
${pageContext.request.contextPath}
<form action="${pageContext.request.contextPath}/servlet" method="post">
<input type="submit">
</form>
2、param(获取请求中的指定参数)
其底层实际调用request.getParameter()
3、paramValues
获取请求中的指定参数的所以值,其底层实际调用request.getParameterValues()
4、initParam
获取初始化参数,其底层调用的是ServletContext.getInitParameter()
1.5 EL执行运算符
- 算数运算符 + , - , * , / , %
- 逻辑运算符 && , || , !
- 比较运算符 > , < , >= , <= , == , !=
- Null运算符 empty
- 三元运算符
<body>
<%--
EL 表达式支持运算符
三元运算符
(布尔表达式)?结果1:结果2
--%>
${ 3 > 2 ? "欢迎你":"拒绝你"}
<br>
<%--
EL 表达式 判空运算
元算符 empty
如果被判断的对象是空,结果是true
容器判断,判断的是长度 ==0 就是空
基本类型数组,不是判断长度,判断数组容器是否存在
基本类数组,判断是空 = null
--%>
<%
User user = new User();
pageContext.setAttribute("user",user);
String[] str = new String[1];
request.setAttribute("str",str);
List<String> list = new ArrayList<String>();
request.setAttribute("list",list);
Integer[] arr = {};
request.setAttribute("arr",arr);
%>
user对象是空吗:: ${ empty user} <%-- user对象 false--%> <br>
str数组是空吗:: ${empty str} <%-- 数组 false--%><br>
list集合是空吗:: ${empty list}<br>
arr数组是空吗:: ${empty arr}
</body>
2 JSTL的核心标签库使用
2.1 jstl标签概述
JSTL(JSP Standard Tag Library),JSP标准标签库,可以嵌入在jsp页面中使用标签的形式完成业务逻辑等功能。jstl出现的目的同el一样, 也是要放到jsp页面中的脚本代码。JSTL标准标准标签库有5个子库,但随着发展,目前常使用的是它的核心库Core
标签库 | 标签库的URI | 前缀 |
---|---|---|
Core | http://java.sun.com/jsp/jstl/core | c |
I18N | 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 |
2.2 jstl标签的安装
导入jar包
javax.servlet.jsp.jstl.jar
standard.jar
使用taglib指令在jsp页面导入要使用的jstl标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
实例:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
使用jstl标签库
引入核心库
指令: 文件的最开端
<%@ taglib %> 引入标签库
属性 uri ="标签库的地址"
属性 prefix="标签的前缀"
引入标签库后,在jsp页面中,请你写标签
<c:>
--%>
</body>
</html>
2.3 常用的jstl标签
jstl的核心标签内容有很多,现在目前还常用的标签只有if、foreach标签。
if标签:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
if标签: 判断使用
注意.没有else标签
JSTL标签配合EL使用
<c:if test=""> if语句判断主体 </c:if>
test的属性值结果是true,执行判断体
--%>
<%
pageContext.setAttribute("number",3);
%>
<c:if test="${number >= 5}">
<div style="color: red;font-size: 28px">div区域</div>
</c:if>
<c:if test="${number < 5}">
<div style="color: blue;font-size: 28px">div区域</div>
</c:if>
</body>
</html>
forEach标签 重点重点重点
普通遍历:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
foreach标签:循环,包含传统for 和 增强for
传统for for(int a = 0 ; a < 10 ;a++){ System.out.println(a) }
属性;
begin="" 相当于 a=0
end="" 相当于 a<10 (包含)
step="2" (步长) 相当于 a++ 相当于 a+=2
var="a" 属性var,定义属性值
含义: 将循环次数变量,存储到最小域对象 pageContext中
键名就是a
EL 表达式取出
<c:foreach>
循环体
</c:foreach>
--%>
<c:forEach begin="1" end="10" step="2" var="a">
<div style="color: red;font-size: 28px">div区域 ${a}</div>
</c:forEach>
</body>
</html>
遍历集合、数组
<%@ page import="java.util.List" %>
<%@ page import="com.itheima.pojo.User" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
foreach标签: 增强for循环
遍历数组,遍历集合
属性:
items: 遍历的容器
var: 遍历的容器中的每个元素,存储在pageContext域对象
var属性值,就是域对象的键名
varStatus(变量状态属性):
varStatus="vs": 会将对象,存在pageContext域中
域对象的键名就是 vs
对象,是定义了循环状态的对象
对象中.有个属性count,循环的次数
输出循环的次数
int count = 0;
for(String s: str){
count++;
System.out.println(s);
}
--%>
<%
String[] strs = {"i","love","java"};
pageContext.setAttribute("strs",strs);
%>
<c:forEach items="${strs}" var="s" varStatus="vs">
<%--EL 在pageContext域中取出键值对,键是s,值是数组元素--%>
${s} ${vs.count} <br>
</c:forEach>
<hr>
<%--
集合,存储User对象
--%>
<%
List<User> list = new ArrayList<User>();
User u1 = new User();
u1.setUsername("张三");
u1.setPassword("123");
User u2 = new User();
u2.setUsername("李四");
u2.setPassword("456");
list.add(u1);
list.add(u2);
pageContext.setAttribute("list",list);
%>
<%--
$ {list} 域对象中的键,取出的值,值是List集合
var: 集合中的元素,存储到pageContext域对象,
s: 域对象的键名
--%>
<c:forEach items="${list}" var="s">
${s.username} ${s.password}
</c:forEach>
</body>
</html>
3 过滤器Filter
3.1 过滤器概述
过滤器: 过筛子,符合条件的过去,不符合条件不能过去.
生活比喻: 安检,检查安全的人与物才可以通过放行
程序: 客户端需要访问服务器的目标资源,在客户端和服务器资源之间设置过滤器, 符合要求放行
3.2 过滤器的快速入门和过滤器生命周期
* 过滤器的快速入门
* 1: 定义类实现接口 Filter
* 2: 重写接口中的抽象方法
* 3: web.xml配置
*
* 过滤器对象的生命周期
* 1: 对象初始化创建,调用方法init
* 方法参数 FilterConfig
* Tomcat引擎创建过滤器对象,并调用方法init传递参数
* Tomcat启动的时候,创建过滤器对象
*
* 方法参数 FilterConfig,过滤器配置对象
* 可以获取到过滤器的 名字等等
* 方法 getServletContext()获取到最大域对象
*
* 2: 拦截方法doFilter
* 过滤器执行过滤的方法,过滤被访问资源的时候,必须是被过滤器过滤器的资源
*
* 3: 对象销毁方法 destroy()
* 关闭服务器的时候,过滤器对象销毁
* web项目,从服务器中移除
举个例子:
public class MyFilter1 implements Filter{
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("我是过滤器1");
//放行,没有继续执行doFilter就不能向后执行Servlet程序,如下图执行原理图所示
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器对象被创建");
ServletContext servletContext = filterConfig.getServletContext();
System.out.println(servletContext);
}
@Override
public void destroy() {
System.out.println("过滤器对象被销毁");
}
}
执行过程:
3.3 Filter的url-pattern配置
- 完全匹配(不使用)
<!--
过滤资源,只有hello
绝对匹配 <url-pattern>/hello</url-pattern>
只能过滤指定的资源
-->
<url-pattern>/hello</url-pattern>
- 目录匹配(常用)
<!--
目录匹配,过滤器中最常见
/abc/* 过滤abc目录下的所有资源
一次过滤一片资源
过滤后台资源 /admin/*
-->
<url-pattern>/admin/*</url-pattern>
- 后缀名匹配
<!--
后缀名匹配,一般不使用
*.jsp 访问所有jsp文件
-->
<url-pattern>*.jsp</url-pattern>
3.4 注解配置Filter
* 注解开发过滤器
*
* 注解 @WebFilter
* 属性 urlPatterns,配置的是拦截资源
*
* 注解过滤器之间的顺序:
* 注解开发没有配置文件的
* 按照类名的自然顺序决定 a b c d 1 2 3 4(不区分大小写)
* 如果存在配置文件,配置文件优先
*
@WebFilter(urlPatterns = "/*")
public class MyFilter3 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("注解过滤器 myFilter3");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
3.5 应用:Filter 处理中文乱码
在过滤器上设置编码集,这样调用Servlet程序必须经过过滤器,都经过了处理
@WebFilter("/*")
public class ChinaFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//在过滤器中,设置request对象的编码表
req.setCharacterEncoding("utf-8");
//设置response缓冲区的编码表,通知浏览器的解码
resp.setContentType("text/html;charset=utf-8");
System.out.println("ChinaFilter执行了, 过滤中文,设置编码utf-8");
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
3.6 过滤器链 FilterChain的执行过程
3.7 过滤器案例:登录验证(权限校验)
1. 访问项目的资源。验证其是否登录
2. 如果登录了,则直接放行。
3. 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。
4 监听器Listener
4.1 监听器概述
监听器Listener 是 监听某个组件变化的对象.
-
事件源是固定的,主要是request, session, servletcontext域对象
-
监听的是域对象变化
- 对象的创建和销毁, 域对象中存储的数据变化
-
第一个维度划分:
-
监听的域对象request, session, servletcontext
域对象 监听器 request ServletRequestListener session HttpSessionListener servletcontext ServletContextListener
-
-
第二个维度划分:
- 监听的域对象的状态
4.2 ServletContext监听器入门
用于监听 servletcontext域对象, 对象的创建和销毁, 域对象中存储的数据变化
步骤:
- 创建类实现监听器接口 ServletContextListener
- 重写抽象方法
- 注解方式 配置 Listener
/**
* 自定义监听器,监听对象(事件源) 域对象ServletContext
* 域对象的创建和销毁
*
* ServletContext域对象,Tomcat服务器启动的时候被创建
* ServletContext域对象,Tomcat服务器关闭的时候被销毁
*/
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
/**
* ServletContext对象,被创建,调用
*/
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext域对象创建");
}
@Override
/**
* ServletContext对象,被销毁前调用
*/
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext域对象销毁");
}
}
- web.xml配置方式 Listener
<listener>
<listener-class>com.itheima.listener.MyServletContextListener</listener-class>
</listener>
4.3 监听器事件对象 ServletContextEvent
ServletContextEvent: 是ServletContext域对象的事件对象, 此对象由tomcat引擎创建ServletContext
- 方法:
Object getSource()
获取到被监听的事件源ServletContext getServletContext()
获取到被监听的事件源
- 小结:
- 两个方法 除了返回值外,功能实现是一致的, 设计目的为了通用性
- 其他的监听器事件对象(HttpSessionEvent, ServletRequestEvent), 都有共同的方法 getSource()
@WebListener
public class MyServletContextListener implements ServletContextListener{
@Override
/**
* 当ServletContext对象被创建的时候,调用此方法
* 方法中的参数 ServletContextEvent (域对象的事件对象) 什么都是对象
* 时间对象是Tomcat引擎创建,调用方法contextInitialized传递的
* 方法:
* ServletContext getServletContext() 获取ServletContext域对象
* 域对象称为事件源
*
* 任意一个监听器,都可以使用方法参数中的事件对象,获取事件源
*
* Object source = servletContextEvent.getSource();获取事件源
* 返回的是Object,强制转换
* 任意一个监听器,都可以使用方法参数中的事件对象,共同的方法 getSource()
*/
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext对象被创建");
ServletContext servletContext = servletContextEvent.getServletContext();
System.out.println("事件源::"+servletContext);
// Object source = servletContextEvent.getSource();
}
@Override
/**
* 当ServletContext对象被销毁的时候,调用此方法
*/
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext对象被销毁");
}
}
5 Servlet抽取思想
每个功能对应一个Servlet?
这样会非常冗余,用一个Servlet执行多个功能
service方法中根据请求的不同,调用不同功能的方法
如果用多个if else去匹配请求参数,也是非常冗余的
使用反射来调用请求方法。
案例:
<body>
<%--
每个超链接上添加参数,告知服务器,我要做什么
--%>
<a href="${pageContext.request.contextPath}/user?operator=login">登录</a> <br>
<a href="${pageContext.request.contextPath}/user?operator=register">注册</a> <br>
<a href="${pageContext.request.contextPath}/user?operator=updatePassword">改密</a> <br>
<a href="${pageContext.request.contextPath}/user?operator=findPassword">找回密码</a> <br>
</body>
/**
* 所有的客户端的用户操作请求
* 我来实现,方法实现
*/
@WebServlet(urlPatterns = "/user")
public class UserServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取客户端超链接的参数
String operator = request.getParameter("operator");
//反射, 获取客户端参数 operator 是方法名
try {
Class clazz = this.getClass();
//获取方法,operator方法名
Method method =
clazz.getMethod(operator,HttpServletRequest.class,HttpServletResponse.class);
method.invoke(this,request,response);
}catch (Exception ex){
ex.printStackTrace();
}
}
//方法处理登录
public void login(HttpServletRequest request, HttpServletResponse response){
System.out.println("处理登录的方法");
}
//方法处理注册
public void register(HttpServletRequest request, HttpServletResponse response){
System.out.println("处理注册方法");
}
//方法处理改密
public void updatePassword(HttpServletRequest request, HttpServletResponse response){
System.out.println("处理修改密码方法");
}
public void findPassword(HttpServletRequest request, HttpServletResponse response){
System.out.println("处理找回密码方法");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
/**
* if("login".equals(operator)){
login(request, response);
}else if("register".equals(operator)){
register(request, response);
}else if("updatePassword".equals(operator)){
updatePassword(request, response);
}*/