十、JavaWeb之EL表达式、JSTL使用、过滤器Filter和监听器Listener

1 EL表达式

1.1 EL表达式概述

EL(Express Lanuage)表达式可以嵌入在jsp页面内部,减少jsp脚本的编写,EL 出现的目的是要替代jsp页面中输出脚本<%= 数据 %>的编写。

1.2 EL表达式的格式和作用

EL表达式的格式:

${EL表达式内容}

EL表达式的作用:

  1. 从域对象中查找指定的数据。
  2. EL内置对象的使用
  3. 执行运算符

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个)
  1. pageScope:从page范围域属性空间中查找指定的key
  2. requestScope:从request范围域属性空间中查找指定的key
  3. sessionScope:从session范围域属性空间中查找指定的key
  4. 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执行运算符

  1. 算数运算符 + , - , * , / , %
  2. 逻辑运算符 && , || , !
  3. 比较运算符 > , < , >= , <= , == , !=
  4. Null运算符 empty
  5. 三元运算符
<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前缀
Corehttp://java.sun.com/jsp/jstl/corec
I18Nhttp://java.sun.com/jsp/jstl/fmtfmt
SQLhttp://java.sun.com/jsp/jstl/sqlsql
XMLhttp://java.sun.com/jsp/jstl/xmlx
Functionshttp://java.sun.com/jsp/jstl/functionsfn

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

      域对象监听器
      requestServletRequestListener
      sessionHttpSessionListener
      servletcontextServletContextListener
  • 第二个维度划分:

    • 监听的域对象的状态

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);
 }*/
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值