目录

8. JSP

8.1 概念

8.2 原理

8.3 JSP脚本

8.4 JSP的内置对象

8.5 JSP指令

8.5.1 概述

8.5.2 page指令

8.5.3 include命令

8.5.4 taglib指令

8.6 JSP注释

9. EL

9.1 概念

9.2 作用

9.3 语法

9.4 注意

9.5 使用

9.5.1 运算

9.5.2 获取值

9.5.3 获取对象、List集合、Map集合的值

10. JSTL

10.1 概念

10.2 使用步骤

10.3 常用的JSTL标签

10.3.1 if标签

10.3.2 choose标签

10.3.3 foreach标签

10.4 综合示例

11. Filter:过滤器

11.1 概念

11.2 快速入门

11.3 过滤器细节

11.3.1 web.xml配置与注解配置

11.3.2 过滤器的执行流程

11.3.3 过滤器生命周期的方法

11.3.4 过滤器配置详解

11.3.5 过滤器链(即配置多个过滤器)

11.4 案例之登录验证

11.5 案例之敏感词过滤

12. Listener:监听器

12.1 概念

12.2 ServletContextListener例子


注:JSP&Servlet学习笔记(上)

8. JSP

8.1 概念

JSP全称是 Java Server Pages: java服务器端页面。

可以理解为:一个特殊的页面,其中既可以指定定义html标签,又可以定义java代码,用于简化书写。

8.2 原理

JSP本质上就是一个Servlet。

运行tomcat,复制如下图的路径,到资源管理器打开

然后到\work\Catalina\localhost\ROOT\org\apache\jsp目录下

这就是tomcat运行index.jsp生成的文件,打开查看

发现该类继承了HttpJspBase,该类需要去tomcat的源码中查找

打开查看文件源码

发现该类继承了HttpServlet,所以本质上JSP就是Servlet,实现了service()方法

8.3 JSP脚本

JSP的脚本:JSP定义Java代码的方式

  • <%  代码 %>:定义的java代码,在service方法中。service方法中可以定义什么,该脚本中就可以定义什么。
  • <%! 代码 %>:定义的java代码,在jsp转换后的java类的成员位置。
  • <%= 代码 %>:定义的java代码,会输出到页面上。输出语句中可以定义什么,该脚本中就可以定义什么。

8.4 JSP的内置对象

内置对象:就是在jsp页面中不需要获取和创建,可以直接使用的对象。

jsp一共有9个内置对象。

内置对象名真实类作用
pageContextPageContext也是域对象,当前JSP页面共享数据,还可以获取其他八个内置对象
requestHttpServletRequest也是域对象,一次请求中共享数据,在转发中可以使用request域共享数据
sessionHttpSession也是域对象,一次会话中多个请求间都能共享数据
applicationServletContext也是域对象,在所有用户间共享数据
responseHttpServletResponse响应对象
pageObject当前页面(Servlet)的对象
outJspWriter输出对象,数据输出到页面上。字符输出流对象。可以将数据输出到页面上。和response.getWriter()类似
configServletConfigServlet的配置对象
exceptionThrowable异常对象
  • response.getWriter()和out.write()的区别
    • 在tomcat服务器真正给客户端做出响应之前,会先找response缓冲区数据,再找out缓冲区数据。
    • response.getWriter()数据输出永远在out.write()之前。建议使用out对象输出以免打乱布局。

8.5 JSP指令

8.5.1 概述

JSP指令就是用来配置JSP页面,导入资源文件的。

使用格式如下:

<%@ 指令名称 属性名1=属性值1 属性名2=属性值2 ... %>

JSP指令分为三类:

  • page指令:用来配置JSP页面
  • include指令:页面包含,可以导入其他的JSP页面
  • taglib:导入资源,如导入jstl标签库

8.5.2 page指令

page命令主要用于配置JSP页面的。

常见的属性有contentType、import、errorPage、isErrorPage等。

  • contentType属性

contentType属性的配置等同于response.setContentType()。

可以设置响应体的MIME类型以及字符集,同时可以设置当前jsp页面的编码(只有是高级的IDE如IDEA才能生效,如果是低级工具如记事本需要设置pageEncoding属性设置当前页面的字符集)

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<html>
  <head>
    <title>Index</title>
  </head>
  <body>
  
  </body>
</html>
  • import属性

可以导入java的各种类,如List等。

<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<html>
<head>
    <title>Index</title>
</head>
<body>
<%
    List<Integer> list = new ArrayList<>();
%>
</body>
</html>
  • errorPage属性

设置后,指如果当前页面发生异常后,会自动跳转到指定的错误页面。

  • isErrorPage属性

isErrorPage可以标识当前JSP页面是否是错误页面,属性值是一个布尔值,如果是true表示可以使用内置对象exception,如果是false表示不可以使用内置对象exception。

8.5.3 include命令

使用include命令可以导入其他的JSP页面。

8.5.4 taglib指令

比如说导入jstl标签库。

不过要使用jstl标签库,需要导入相关的依赖包

然后再使用taglib命令引用

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Index</title>
</head>
</body>
</html>

其中prefix是自定义的前缀,约定成俗写"c",不过也可以写其他任意名称,而uri是路径地址。

8.6 JSP注释

在JSP页面中可以使用的注释有两种:

<!-- -->     只能注释html代码片段
<%-- --%>    推荐使用,既可以注释html代码片段,又可以注释jsp代码,并且注释后查看不到注释后的源码

如:

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<html>
<head>
    <title>Index</title>
    <%--
        <h1>标题</h1>
    --%>
    <%--
        <%
        System.out.println("输出");
        %>
    --%>
</head>
</body>
</html>

 

9. EL

9.1 概念

EL全称是Expression Language,是一种表达式语言。

9.2 作用

替换和简化jsp页面中java代码的编写。

9.3 语法

${表达式}

9.4 注意

jsp默认支持el表达式的。如果要忽略el表达式

  • 设置jsp中page指令中:isELIgnored="true" 忽略当前jsp页面中所有的el表达式
  • \${表达式} :忽略当前这个el表达式

9.5 使用

9.5.1 运算

EL表达式支持的运算符如下:

  • 算数运算符: + - * /(div) %(mod)
  • 比较运算符: > < >= <= == !=
  • 逻辑运算符: &&(and) ||(or) !(not)
  • 空运算符: empty

特别说明下空运算符的使用:功能是用于判断字符串、集合、数组对象是否为null或者长度是否为0。

  • ${empty list}:判断字符串、集合、数组对象是否为null或者长度为0
  • ${not empty str}:表示判断字符串、集合、数组对象是否不为null 并且 长度>0

使用举例:

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<html>
<head>
</head>
<body>
算数运算符: + - * /(div) %(mod) <br/>
+:${3+5}<br/>
-:${3*5}<br/>
*:${3*5}<br/>
/:${3/5}<br/>
%:${3%5}<br/>
比较运算符: > < >= <= == != <br/>
>:${3>5}<br/>
<:${3<5}<br/>
>=:${3>=5}<br/>
<=:${3<=5}<br/>
==:${3==5}<br/>
!=:${3!=5}<br/>
逻辑运算符: &&(and) ||(or) !(not) <br/>
&&:${true&&false}<br/>
||${true||false}<br/>
!:${!false}<br/>
空运算符: empty和not empty <br/>
${empty "abc"}<br/>
${not empty "abc"}<br/>
</body>
</html>

9.5.2 获取值

el表达式只能从域对象(pageContext、request、session、application)中获取值。

而实际取值的域名称和域对象的对应关系如下:

域名称域对象
pageScopepageContext
requestScoperequest
sessionScopesession
applicationScopeapplicion(ServletContext)

获取值的语法如下:

// 语法:从指定域中获取指定键的值(没有取到返回空字符串)    
${域名称.键名}
// 示例
${requestScope.name}// 在request域中找到名为name所对应的值

注意,也可以直接通过${键名}语法去查找,不过是依次从最小的域中查找是否有该键对应的值,直到找到为止。

示例如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<html>
<head>
</head>
<body>
    <%
        pageContext.setAttribute("page","index.jsp");// pageContext域对象
        request.setAttribute("username","张三");// request域对象
        session.setAttribute("session","session");// session域对象
        application.setAttribute("application","application");// application域对象
    %>
    使用EL表达式中的获取域对象中的值<br/>
    ${pageScope.page}<br>
    ${requestScope.username}<br>
    ${sessionScope.session}<br>
    ${applicationScope.application}<br>
    也可以用下面的语法<br/>
    ${page}<br>
    ${username}<br>
    ${session}<br>
    ${application}<br>
    如果要查找的键不在域对象中,会显示空字符串,不会打乱排布
    ${requestScope.hello}
</body>
</html>

效果如下:

9.5.3 获取对象、List集合、Map集合的值

9.5.3.1 获取对象

获取对象本质上会去调用对象的getter方法,基本语法如下:

// 语法
${域名称.键名.属性名}
// 示例:获取保存在session域中的用户的用户名
${sessionScope.user.username}

示例

<%@ page import="java.util.Date" %>
<%@ page import="bean.User" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<html>
<head>
</head>
<body>
    <%
        // 创建对象
        User user=new User();
        user.setUsername("张三");
        user.setAge(18);
        user.setBirth(new Date());
        // 保存到session域中
        session.setAttribute("user",user);
    %>
    输出对象:${sessionScope.user.username}
</body>
</html>

效果如下:


9.5.3.2 获取List集合

List集合中可能是基本数据类型,也可能是引用数据类型,安装下面的语法来获取,都能成功。

// 语法:如果List集合中的是基本数据类型或字符串
${域名称.键名[索引]}
// 示例:获取保存在session域中的List集合中的第一个元素
${sessionScope.lists[0]}

// 语法:如果List集合中的是对象
${域名称.键名[索引].属性名}
// 示例:获取保存在session域中的List集合中的第一个对象的username
${sessionScope.lists[0].username}

例如:

<%@ page import="java.util.Date" %>
<%@ page import="bean.User" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<html>
<head>
</head>
<body>
    <%
        // 创建对象
        User user=new User();
        user.setUsername("张三");
        user.setAge(18);
        user.setBirth(new Date());
        // 将保存有基本数据类型或字符串的List集合保存到session域中
        List<String> list1=new ArrayList<>();
        list1.add("唐僧");
        list1.add("孙悟空");
        list1.add("猪八戒");
        session.setAttribute("list1",list1);
        // 将保存有对象的List集合保存到session域中
        List<User> list2=new ArrayList<>();
        list2.add(user);
        list2.add(user);
        list2.add(user);
        session.setAttribute("list2",list2);
    %>
    输出字符串:${sessionScope.list1[1]}<br/>
    输出对象:${sessionScope.list2[0].username}
</body>
</html>

9.5.3.3 获取Map集合

基本语法如下:

${域名称.键名.key名称}
// 或者
${域名称.键名["key名称"]}

示例:

<%@ page import="bean.User" %>
<%@ page import="java.util.*" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<html>
<head>
</head>
<body>
    <%
        // 创建对象
        User user=new User();
        user.setUsername("张三");
        user.setAge(18);
        user.setBirth(new Date());
        // 保存字符串到Map集合中
        Map<String,String> map1=new HashMap<>();
        map1.put("book","《西游记》");
        session.setAttribute("map1",map1);
        // 保存对象到Map集合中
        Map<String,User> map2=new HashMap<>();
        map2.put("user",user);
        session.setAttribute("map2",map2);
    %>
    输出字符串:${sessionScope.map1.book} 或 ${sessionScope.map1["book"]}<br/>
    输出对象:${sessionScope.map2.user.username} 或 ${sessionScope.map2["user"].username}
</body>
</html>

9.5.3.4 获取隐式对象

EL表示也内置了11个隐式对象,即不需要创建就可以使用的对象。

注意下pageContext对象即可,可以通过它获取JSP的其他八个内置对象。

如:${pageContext.request.contextPath}:动态获取虚拟目录

10. JSTL

10.1 概念

JSTL的全称是JavaServer Pages Tag Library  JSP标准标签库。

是由Apache组织提供的开源的免费的jsp标签。

主要用于简化和替换JSP页面上的Java代码。

10.2 使用步骤

第一步:导入JSTL相关的jar包。

第二步:在JSP页面使用taglib指令引入标签库

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

第三步:使用标签

10.3 常用的JSTL标签

10.3.1 if标签

if标签相当于Java代码中的if语句。

语法:

<c:if test="条件表达式">
    条件成立执行的内容
</c:if>

条件表达式可以是EL表达式也可以是JSP表达式,test 必须属性,接受boolean表达式

如果表达式的值为true,则会执行主体内容,没有对应的<c:else>标签,因此如果如果条件不成立则需要使用<c:choose>、<c:when>及<c:other>标签。

示例1:

    <c:if test="${3>2}">
        ${true}
    </c:if>

示例2:

<%@ page import="java.util.List" %>
<%@ 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>if标签</title>
</head>
<body>
    <%
        //判断request域中的一个list集合是否为空,如果不为null则显示遍历集合

        List list = new ArrayList();
        list.add("aaaa");
        request.setAttribute("list",list);

        request.setAttribute("number",4);

    %>

    <c:if test="${not empty list}">
        遍历集合...

    </c:if>
    <br>

    <c:if test="${number % 2 != 0}">

            ${number}为奇数

    </c:if>

    <c:if test="${number % 2 == 0}">

        ${number}为偶数

    </c:if>

</body>
</html>

10.3.2 choose标签

choose标签相当于Java代码中的switch语句。

功能:<c:choose>、<c:when>及<c:otherwise>标签实现if/elseif/else语句的作用

语法:

    <c:choose>
        <c:when test="条件表达式1">
            主体内容1
        </c:when>
        <c:when test="条件表达式2">
            主体内容2
        </c:when>
        <c:otherwise>
            表达式都不成立时执行的主体内容
        </c:otherwise>
    </c:choose>

示例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
    <title>choose标签</title>
</head>
<body>

    <%
        request.setAttribute("number",51);
    %>

    <c:choose>
        <c:when test="${number == 1}">星期一</c:when>
        <c:when test="${number == 2}">星期二</c:when>
        <c:when test="${number == 3}">星期三</c:when>
        <c:when test="${number == 4}">星期四</c:when>
        <c:when test="${number == 5}">星期五</c:when>
        <c:when test="${number == 6}">星期六</c:when>
        <c:when test="${number == 7}">星期天</c:when>

        <c:otherwise>数字输入有误</c:otherwise>
    </c:choose>

</body>
</html>

10.3.3 foreach标签

功能:实现for循环。

语法:

1.完成简单的重复操作
    等价于
        <%
            for(int i=0; i<10; i++){
                System.out.println(i);
            }
        %>
    语法示例
        <c:forEach begin="1" end="10" var="i" step="2" varStatus="s">
            ${i} <h3>${s.index}<h3> <h4> ${s.count} </h4><br>
        </c:forEach>
    属性
        begin:开始值
        end:结束值
        var:临时变量,是当前正在遍历的值
        step:步长,如果step是1,则表示begin加1到2,2加1到3;如果step是2,则表示begin加2到3,3加2到5
        varStatus:常用的两个调用对象,varStatus表示循环状态对象,如果是调用的是index(即varStatus.index)则显示容器中的元素索引从0开始,如果是调用的是count(即varStatus.count)则显示循环次数从1开始

2.遍历集合容器
    等价于
        <%
            List<User> list=new ArrayList<>();
            for(User user : list){
                System.out.println(user);
            }
        %>
    语法示例
        <c:forEach items="${list}" var="str" varStatus="s">
            ${s.index} ${s.count} ${str}<br>
        </c:forEach>
    属性
        items:容器对象,一般是集合如List的对象
        var:容器中元素的临时变量,表示当前正在遍历的集合中的元素
    varStatus:只能调用两个对象,表示循环状态对象,如果是调用的是index则显示容器中的元素索引从0开始,如果是调用的是count则显示循环次数从1开始

示例:

    <%
        String[] users={"张三","李四","王五"};
    %>
    <c:forEach var="user" items="<%=users%>">
        ${user}
    </c:forEach>

10.4 综合示例

<%@ page import="bean.User" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.Date" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
    <title>test</title>
</head>
<body>

<%
    List list = new ArrayList();
    list.add(new User("张三", 23, new Date()));
    list.add(new User("李四", 24, new Date()));
    list.add(new User("王五", 25, new Date()));

    request.setAttribute("list", list);
%>

<table border="1" width="500" align="center">
    <tr>
        <th>编号</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>生日</th>
    </tr>
    <%--数据行--%>
    <c:forEach items="${list}" var="user" varStatus="s">

        <c:if test="${s.count % 2 != 0}">

            <tr bgcolor="red">
                <td>${s.count}</td>
                <td>${user.username}</td>
                <td>${user.age}</td>
                <td>${user.birth}</td>
            </tr>
        </c:if>

        <c:if test="${s.count % 2 == 0}">

            <tr bgcolor="green">
                <td>${s.count}</td>
                <td>${user.username}</td>
                <td>${user.age}</td>
                <td>${user.birth}</td>
            </tr>
        </c:if>
    </c:forEach>

</table>

</body>
</html>

11. Filter:过滤器

11.1 概念

web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。

过滤器的作用:一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤...

11.2 快速入门

快速入门步骤:

  • 1. 定义一个类,实现接口Filter
  • 2. 复写init、doFilter、destroy方法
  • 3. 配置拦截路径,有web.xml配置文件和注解两种方式实现。

代码实现:

/**
 * 过滤器快速入门程序
 */
@WebFilter("/*")//访问所有资源之前,都会执行该过滤器
public class DemoFilter1 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("filterDemo1被执行了....");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

11.3 过滤器细节

11.3.1 web.xml配置与注解配置

web.xml配置(同对servlet类配置一样,不过是filter标签):

    <filter>
        <filter-name>demoFilter1</filter-name>
        <filter-class>com.demo.filter.DemoFilter1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>demoFilter1</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

注解配置(在过滤器类上配置):

@WebFilter("/*")

两种方式都可以使用,不过使用注解配置代码写得少。

11.3.2 过滤器的执行流程

执行流程:

  • 1. 执行过滤器
  • 2. 执行放行后的资源
  • 3. 回来执行过滤器放行代码下边的代码
@WebFilter("/*")
public class DemoFilter2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        //对request对象请求消息增强
        System.out.println("DemoFilter2执行了....");
        //放行
        chain.doFilter(req, resp);
        //对response对象的响应消息增强
        System.out.println("DemoFilter2回来了...");
    }

    @Override
    public void destroy() {

    }
}

11.3.3 过滤器生命周期的方法

Filter的生命周期方法:

  • 1. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源。
  • 2. doFilter:每一次请求被拦截资源时,会执行。执行多次。
  • 3. destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源。
@WebFilter("/*")
public class DemoFilter3 implements Filter {

    /**
     * 在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源
     */
    public void init(FilterConfig config) throws ServletException {
        System.out.println("init....");
    }

    /**
     * 每一次请求被拦截资源时,会执行。执行多次
     */
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("doFilter....");
        chain.doFilter(req, resp);
    }

    /**
     * 在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源
     */
    public void destroy() {
        System.out.println("destroy....");
    }
}

11.3.4 过滤器配置详解

拦截路径配置,即@WebFilter中的值或<url-pattern>中的路径,即配置要拦截的访问路径。

  • 1. 具体资源路径: /index.jsp   只有访问index.jsp资源时,过滤器才会被执行
  • 2. 拦截目录: /user/*    访问/user下的所有资源时,过滤器都会被执行
  • 3. 后缀名拦截: *.jsp        访问所有后缀名为jsp资源时,过滤器都会被执行
  • 4. 拦截所有资源:/*        访问所有资源时,过滤器都会被执行
@WebFilter("/index.jsp")  //1. 具体资源路径: /index.jsp   只有访问index.jsp资源时,过滤器才会被执行
//@WebFilter("/user/*")     //2. 拦截目录: /user/*	访问/user下的所有资源时,过滤器都会被执行
//@WebFilter("*.jsp")       //3. 后缀名拦截: *.jsp		访问所有后缀名为jsp资源时,过滤器都会被执行
//@WebFilter("/*")          //4. 拦截所有资源
public class DemoFilter4 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        System.out.println("DemoFilter4....");
        chain.doFilter(req, resp);
    }

    @Override
    public void destroy() {

    }
}

拦截方式配置,即配置资源被访问的方式。

可以在注解中使用dispatcherTypes属性配置或在web.xml中使用<dispatcher></dispatcher>标签配置。

其中可以选择的值如下:

  • 1. REQUEST:默认值。浏览器直接请求资源
  • 2. FORWARD:转发访问资源
  • 3. INCLUDE:包含访问资源
  • 4. ERROR:错误跳转资源
  • 5. ASYNC:异步访问资源
//浏览器直接请求index.jsp资源时,该过滤器会被执行
//@WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.REQUEST)

//只有转发访问index.jsp时,该过滤器才会被执行
//@WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.FORWARD)

//浏览器直接请求index.jsp或者转发访问index.jsp。该过滤器才会被执行
//@WebFilter(value="/*",dispatcherTypes ={ DispatcherType.FORWARD,DispatcherType.REQUEST})
public class DemoFilter5 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("DemoFilter5....");
        chain.doFilter(req, resp);
    }

    @Override
    public void destroy() {

    }
}

其中web.xml配置如下:

11.3.5 过滤器链(即配置多个过滤器)

过滤器链即多个过滤器一同过滤,那么就需要考虑多个过滤器之间的执行顺序了。

  • 执行顺序:如果有两个过滤器:过滤器1和过滤器2

  • 过滤器先后顺序问题:

它的执行先后顺序跟配置方式有关,注解配置和web.xml配置导致的执行顺序不一致:

(1)注解配置:按照类名的字符串比较规则比较,值小的先执行。如: DemoFilter6_A.java 和 DemoFilter6_B.java,DemoFilter6_A就先执行了。(具体查看DemoFilter6_A.java与DemoFilter6_B.java实例

(2)web.xml配置: <filter-mapping>谁定义在上边,谁先执行。(具体查看DemoFilter7_A.java与DemoFilter7_B.java实例,如下图

11.4 案例之登录验证

需求:

  • 访问资源。验证其是否登录。
  • 如果登录了,则直接放行。
  • 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。

核心过滤器代码如下:

@WebFilter("/*")
public class LoginFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //0.强制转换
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        //1.获取资源请求路径
        String uri = request.getRequestURI();
        //2.判断是否包含登录相关资源路径,要注意排除掉 css/js/图片/验证码等资源
        if (uri.contains("/index.jsp") || uri.contains("/loginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/fonts/")) {
            //包含,用户就是想登录。放行
            chain.doFilter(req, resp);
        } else {
            //不包含,需要验证用户是否登录
            //3.从获取session中获取user
            Object user = request.getSession().getAttribute("username");
            if (user != null) {
                //登录了。放行
                chain.doFilter(req, resp);
            } else {
                //没有登录。跳转登录页面
                response.sendRedirect("index.jsp");
            }
        }
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

其他更多代码看源码。

11.5 案例之敏感词过滤

需求:

  • 1. 对录入的数据进行敏感词汇过滤
  • 2. 敏感词汇参考《敏感词汇.txt》
  • 3. 如果是敏感词汇,替换为 ***

分析:

  • 1. 对request对象进行增强。增强获取参数相关方法
  • 2. 放行。传递代理对象
  • 3. 该实例涉及JDK代理

代码实现:

@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {
    private List<String> list = new ArrayList<String>();//敏感词汇集合

    public void init(FilterConfig config) throws ServletException {
        try {
            //1.获取文件真实路径
            ServletContext servletContext = config.getServletContext();
            String realPath = servletContext.getRealPath("/WEB-INF/classes/words.txt");
            //2.读取文件
            BufferedReader br = new BufferedReader(new FileReader(realPath));
            //3.将文件的每一行数据添加到list中
            String line = null;
            while ((line = br.readLine()) != null) {
                list.add(line);
            }

            br.close();

            System.out.println(list);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        // 1.创建代理对象
        ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //增强getParameter方法
                //判断是否是getParameter方法
                if (method.getName().equals("getParameter")) {
                    //增强返回值
                    //获取返回值
                    String returnVal = (String) method.invoke(req, args);
                    if (returnVal != null) {
                        for (String s : list) {
                            if (returnVal.contains(s)) {
                                System.out.println(returnVal);
                                returnVal = returnVal.replaceAll(s, "***");
                                System.out.println(returnVal);
                            }
                        }
                    }
                    return returnVal;
                }
                //判断方法名是否是 getParameterMap
                //判断方法6名是否是 getParameterValue
                return method.invoke(req, args);
            }
        });
        // 2.放行
        chain.doFilter(proxy_req, resp);
    }

    public void destroy() {
    }
}

12. Listener:监听器

12.1 概念

web的三大组件之一。

事件监听机制:

  • 事件    :一件事情
  • 事件源 :事件发生的地方
  • 监听器 :一个对象
  • 注册监听:将事件、事件源、监听器绑定在一起。 当事件源上发生某个事件后,执行监听器代码

12.2 ServletContextListener例子

ServletContextListener:监听ServletContext对象的创建和销毁。它的方法:

  • void contextDestroyed(ServletContextEvent sce) :ServletContext对象被销毁之前会调用该方法
  • void contextInitialized(ServletContextEvent sce) :ServletContext对象创建后会调用该方法

步骤:
        1. 定义一个类,实现ServletContextListener接口
        2. 复写方法
        3. 配置,可以使用web.xml配置和注解配置

web.xml配置:

注解配置:

代码实例:

@WebListener
public class ContextLoaderListener implements ServletContextListener {

    /**
     * 监听ServletContext对象创建的。ServletContext对象服务器启动后自动创建。
     * <p>
     * 在服务器启动后自动调用
     *
     * @param servletContextEvent
     */
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //加载资源文件
        //1.获取ServletContext对象
        ServletContext servletContext = servletContextEvent.getServletContext();
        //2.加载资源文件
        String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
        //3.获取真实路径
        String realPath = servletContext.getRealPath(contextConfigLocation);
        //4.加载进内存
        try {
            FileInputStream fis = new FileInputStream(realPath);
            System.out.println(fis);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("ServletContext对象被创建了。。。");
    }

    /**
     * 在服务器关闭后,ServletContext对象被销毁。当服务器正常关闭后该方法被调用
     *
     * @param servletContextEvent
     */
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext对象被销毁了。。。");
    }
}

 

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐