NETCTOSS代码实现第八版:过滤器之登录检查功能与监听器

  • 前言
    • 模拟案例jsp6:演示过滤器的使用
      • 过滤器模拟案例需求分析与设计以及代码分析
        • 模拟资费增加和资费查询功能组件:AddCostServlet和DelCostServlet
        • 模拟过滤器实现统一记日志功能:LogFilter
        • 在配置文件web.xml中配置Filter
        • 开发工具卡住的解决方案
        • Filter组件为单例模式
        • Filter中的doFilter方法的作用
      • 过滤器模拟案例jsp6之代码实现
        • 1.jsp6/src/main/java/web/AddCostServlet.java
        • 2.jsp6/src/main/java/web/DelCostServlet.java
        • 3.jsp6/src/main/java/web/LogFilter.java
        • 4.jsp6/src/main/java/web/GuolvFilter.java
        • 5.jsp6/src/main/webapp/WEB-INF/web.xml
    • Filter在NetCTOSS中的应用:用户登录验证功能
      • 登录验证功能需求分析与设计
      • 登录验证功能代码分析
      • 编程思维之代码阅读经验
    • Filter在NetCTOSS中的应用之登录检查功能代码实现
      • 1.src/main/java/web/LoginFilter.java
      • 2.src/main/webapp/WEB-INF/web.xml
    • 面向对象接口设计理念之多态应用:JavaEE底层API设计HttpServletRequest接口
    • 监听器
      • 什么是监听器
      • 监听器的分类
      • 监听器的使用
      • 监听器模拟案例jsp6之代码实现
        • 1.jsp6/src/main/java/web/MyListener.java
        • 2.jsp6/src/main/webapp/WEB-INF/web.xml
        • 3.jsp6/src/main/java/web/AddCostServlet.java
  • 写在后面

前言

  • 返回 NETCTOSS

模拟案例jsp6:演示过滤器的使用

  • 那这个过滤器,我们怎么用它处理公共业务,怎么去用呢,解释一下,第一步啊,第一步我们需要,就是创建一个类,这个类啊,有要求,不是说我这随便写一个类就是过滤器,这个类像Servlet一样,也需要实现一个接口,它需要实现这个Filter接口,这是这个要求,然后呢,我们把这个类写好以后,这个类我们需要加以配置,不是说,在组件Servlet当中去调用,而是加以配置,通过配置就可以用了啊,那我们需要呢,在web.xml当中配置此类,这就行了,那你会发现呢,我们写了这个类,它和Servlet没有直接发生任何关系,对吧,没有直接发生关系,所以,哪怕是你有一千个请求,有一千个处理请求的方法,这一千个方法,一行代码不用加明白么,一行代码不用改,我们想处理一个公共业务,只需要额外写一个类,就能处理,多写一段配置就可以,这个耦合度是很低的。

过滤器模拟案例需求分析与设计以及代码分析

  • 那所写的这个过滤器的类,这个程序是怎么调用的啊,现在就说一下,说一下怎么调用的。就两步,那我说一下这个,我说一下这个小案例的逻辑,然后呢,用这个小案例来演示这个步骤。那这个小案例,咱们还是模拟啊,模拟这个,某个项目中的一些功能,比如说,我这个项目中啊,有这个增加的功能,比如说,AddCostServlet,增加资费,就这样一个功能,再比如说,我还有这个删除的功能,我们再写一个,比如说,还有这个DelCostServlet,等等吧,那我们这个项目中呢,有很多很多这样的功能,那我们一会写代码时,就模拟这两个,模拟个增加,模拟个删除,当然了,那这两个组件,它是运行在tomcat之内的,这是tomcat之内的组件,那最终呢,用户访问的时候,能够访问到它们,用户呢,可以访问到它们,比如说用户呢,发请求,要求访问某功能,那么,tomcat最终呢,就会调用这个组件,来处理这个请求,这是咱们这个正常的,就是处理请求的一个流程。
  • 然后现在呢,比如说,我想做一个处理,我想呢,想这样,我想在用户访问任何功能的时候,访问任何请求的时候,我希望呢,这个记日志,我希望呢,在请求的开始时记日志,在请求结束时,也记日志,记两个地方。那我们可以用filter实现,因为这个业务,属于公共的业务,任何请求都需要,那这个filter呢,它叫过滤器,它起到一个过滤的作用,起到一个或者说拦截的作用,它呢,能够在我们调用 Servlet整个过程当中,起到一个过滤拦截的作用,它能够在请求阶段,拦截 一次,在响应阶段拦截一次啊,就一个过滤器,它能对整个请求和响应,整个过程啊,拦截两次,请求阶段和响应阶段,这两个位置,那这两个位置呢,是两个点,咱们一会写代码时,能看出来,观察就知道啊,那我们可以图中1和2,这两个点记日志,那么这两个点怎么体现,写代码去看啊。
  • 那这个黄色的长条,它是叫,它是一个filter啊,记日志的filter,我起名叫LogFilter啊,我们就先写一个LogFilter,来体会一下filter的使用方式。那所以啊,下面我们就写这个案例,我们先建一个项目,在这个项目之内呢,我们模拟开发这个增加,删除两功能,然后呢,写一个LogFilter,给这两个功能,统一记日志啊,就这么一个案例啊。打开这个开发工具啊,打开eclipse,那咱们呢,再新建一个项目,这个项目呢,我们按照顺序叫jsp6。建完项目以后啊,你得把包导好,就依赖于tomcat的包,那么创建完项目之后啊,那你看刚才我说了,我们需要呢,先让这个项目中啊,有处理,有这个业务逻辑,增加,删除的业务,那这两个业务呢,需要这个呢,加以模拟,所以我们呢,先写两个Servlet,一个叫AddCostServlet ,一个DelCostServlet,我先写这个Add吧。

模拟资费增加和资费查询功能组件:AddCostServlet和DelCostServlet

  • 所以呢,我在这个jsp6啊,然后呢,src/main/java之下,创建一个包,叫web,然后呢,在包下创建一个Servlet,那我创建对这个Servlet,它的名字啊,叫AddCostServlet啊,在这(web包下),那么创建完这个Servlet以后啊,咱们打开这个类,然后重写service方法,在方法之内呢,我们需要处理啊,增加请求,当然是模拟实现啊。我们写了这个service,然后呢,我们模拟实现了这个增加的功能,怎么模拟呢,就输出一句话,用输出一句话代替,我输出一句话啊,说这个增加资费啊,System.out.println("增加资费");,那这个模拟的是增加资费的业务,我们再来一个模拟增加删除,反正也是模拟啊,这个代码也是比较简单的,那我们另外的那个类啊,我们直接复制这个,复制一下AddCostServlet,一粘贴,改个名就可以了,简单来,那我们选择AddCostServlet啊,把它复制一下,ctrl+c,然后呢,直接在原地,就在这个web包下粘贴啊,ctrl+v,然后呢,它提示重名对吧,改名,那我就改名吧,改成叫DelCostServlet。
  • 然后呢,这个类当中呢,我们把这个话改一下,这就不是增加了,对吧,改成删除啊,那么这两个Servlet啊,System.out.println("删除资费");,我们就写完了啊,简单模拟,然后呢,还得配置对吧,得配置,所以下面呢,我们打开这个项目下的配置文件web.xml啊,把这个两个类呢,配置好,那配置的话,有增加,还有删除啊,那配置的方式啊,没什么好说的,都一样啊,就是名字不同而已啊,

<servlet><servlet-name>addCost</servlet-name><servlet-class>web.AddCostServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>addCost</servlet-name><url-pattern>/addCost</url-pattern>
</servlet-mapping>
<servlet><servlet-name>delCost</servlet-name><servlet-class>web.DelCostServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>delCost</servlet-name><url-pattern>/delCost</url-pattern>
</servlet-mapping>

模拟过滤器实现统一记日志功能:LogFilter

  • 那这就配置完了啊,这个我就不测了,没什么好测的,就是说白了,就是hello world对吧,这个简单,就一句话,输出啊,那么这两个请求啊,代表了我们众多,就是一个项目众多请求的,就是一部分,它是个代表,那我们完整的项目里呢,将来的请求特别多,很多很多啊,这是代表,那么我们想对所有请求,那么统一记日志,我不需要呢,改这每一个Servlet,不需要,我只需要呢,单独写一个Filter,单独写个类,类得加以配置才行,那下面呢,我们来写这个filter,写这个类啊,那再回到我们这个开发工具当中来啊,我在web这个包下,再创建一个类,这个类的名字啊,叫LogFilter,实现一个接口,就叫Filter。类名叫LogFilter,实现接口啊,我们搜一下Filter,然后呢,有好多同名的接口。
  • 那你看啊,哪个像,哪个是我们需要用的呢,有人说第3个有人说第5个,到底哪一个呢,应该是哪个啊,这个javax.servlet这个,因为Filter这个内容,它属于我们当前阶段要讲的,它属于javaEE明白吧,那么javaEE的绝大部分的那部分接口,那个包都是叫javax.servlet,明白吧,它肯定不是java.utiljava.util你看它是jre自带的对吧,jdk自带的,那看这个javax这个,它是servet-api对吧,这属于javaEE啊,所以我们用的是javax.servlet下的Filter。
  • 那我们选择这个第5个啊,这个Filter啊,它的完整的包名,看啊,是javax.servlet.Filter,别搞错了,然后呢,完成,完成以后,这个类会自动出现3个方法对吧,3个方法。

public class LogFilter implements Filter {public void destroy() {    }//公共的业务在此处实现public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException, ServletException { }public void init(FilterConfig arg0) throws ServletException {  }
}

  • 然后呢,第3个方法叫init,这个方法啥意思呢,看得出来吧,初始化,对,这个方法和Servlet的init差不多对吧,如果你想在创建对象之后,给这个对象传值用init,明白吧,init是用来初始化对象,给它传入数据的,那如果你想给这个filter传数据,用什么传呢,这不有参数么对吧,FilterConfig,FilterConfig咱们后面再演示,先别管它啊,我们先在init方法里啊,输出一句话,我们好观察一下,这个init方法什么时候调用的的啊,初始化LogFilter,System.out.println("初始化LogFilter");,咱们把它写清楚。
  • 然后呢,还有一个方法,叫destroy,销毁对吧,销毁方法,那么,销毁时tomcat会自动的调这个Filter的destroy方法,那这个方法什么时候调呢,我们也输出一句话,观察啊,System.out.println("销毁LogFilter");。最后,还有一个方法doFilter,那么这个方法才是关键,这个方法才能够实现,我们要实现的那个公共的业务,这我写个注释啊,就是公共的业务,在此处实现,然后啊,这个方法有3个参数,你看看这3个参数ServletRequest,ServletResponse和FilterChain,异常不用看了,主要看参数,第一个参数,ServletRequest,那这个参数,咱们以前看到过么,它就是request,确实是这样,就是request,还有呢,ServletResponse,它就是response,但是你注意啊,和我们以前看的不太一样,以前呢,我们那个request和response的类型是Http对吧,什么什么request,Http的Response,那这个有什么联系呢,有联系啊,就这个ServletRequest,它是HttpServletRequest的父接口,它是父亲啊,ServletResponse这个也是父接口。
  • 这样啊,咱们先不探讨它为什么这样设计,也不探讨说它这搞一个父接口,这个在使用时会不会有什么问题啊,这个先不管这个事,我们先把这个案例呢,完成啊,然后呢,我们再讲下一个案例,讲这个登录检查的时候,我给你解释一下,这个它为什么这样设计,有什么原因啊,那除了这两个参数之外,还有一个参数叫FilterChain,那我简称为chain吧,那这个chain这个参数,至关重要啊,它有什么用,我们演示一下啊,通常呢,我们在这个doFilter方法里,都会调这个参数的一个方法,调哪个方法呢,是chain.doFilter(request, response),通常,我们都会调这个方法啊。那么这个方法是什么意思,我先不讲啊,咱们一会啊,代码一执行,去体会,去观察啊,那么这个方法之前之后呢,分别呢,写一句话,我们看一下,这个方法什么时候调用的,通过这个方法之前,之后的那句话,能够看出来,它是什么时候调用的。那我在它,在chain.doFilter(request, response)这个方法之前输出一句话,说在前面记日志,后面输出一句话,在后面记日志。

//公共的业务在此处实现
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("在前面记日志");chain.doFilter(request, response);System.out.println("在后面记日志");
}

在配置文件web.xml中配置Filter

  • 那我们先这样写的啊,然后呢,代码一会一执行,你就能看明白,就能有所领悟了啊,现在先这样写着啊,当然了,刚才我们写的这个chain.doFilter,我们是把这个request和response传给它了啊,它要有用,那为什么它需要request和response呢,一会也会讲啊,先这样写着,那到这啊,这个类就写完了,当然我们这里没有业务,只是模拟啊,那写完以后,我们需要呢,对它进行配置,也是在web.xml里配,那filter的配置方式和servlet一模一样,怎么配呢,这样写啊。

<filter><filter-name>log</filter-name><filter-class>web.LogFilter</filter-class>
</filter>
<filter-mapping><filter-name>log</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

  • 有人说这不一样啊,仔细看看它一不一样呢,它的结构是不是一模一样,它的结构啊,和配置servlet一模一样。区别就是那个单词换了,是吧,仅此而已。那很显然啊,filter-class里面写什么呢,类名,是不是,就写类名么,web.LogFilter,那么filter-name写什么呢,写它的别名,这别名,我就叫log,那下面这一段,filter-name还是写别名log,表示我要引用这个类,然后url-pattern,这里写什么呢,那你注意,这里不是写filter的访问路径,不是写filter的网名,写什么呢,写这个filter,它能够过滤,它能够拦截哪些请求,说白了,filter也是为servlet服务的,对吧,也是一个服务者,它能够对哪些servlet请求做出处理呢,你比如说,我们这个案例,案例要求是它能处理一切请求,对吧,addCostServlet也可以处理,delCostServlet也可以处理,那这块怎么写呢,是这样啊。
  • 如果说这个filter只能处理这个增加请求,url-pattern这应该写,/addCost<url-pattern>/addCost</url-pattern>,那如果它只能处理删除请求,应该写/delCost<url-pattern>/delCost</url-pattern>,那如果是都能处理,怎么写呢,/*<url-pattern>/*</url-pattern>啊,所以我们这写/*啊。你这样配置以后啊,这个filter就能够处理一切请求,在我们访问所有请求时,这个filter都会插一手,都会对这个请求呢,做一些处理,当然,这个处理的背后,是tomcat调用的结果,tomcat按照一定的顺序,去调这个程序,保障了这一点啊,咱们试一下,把这个项目呢,部署一遍,部署以后啊,启动tomcat,完了,我这没反应呢,没死机啊,完了,我tomcat,完了,难道是我这部属的项目太多了,有这么脆弱么,我把它删掉一点啊,还是不行,那完蛋了啊,我把这个eclipse关一下试试啊,没反应了,不知道为什么,啊对,出错了,我们重启一下是吧,实在不行重启电脑是吧,实在不行重装系统,来一遍啊,就是3大招啊,3个绝招啊。

开发工具卡住的解决方案

  • 那刚才我这电脑,就是eclipse,有点卡住了啊,有点问题,它这卡住啊,有的时候呢,可能是因为这个内存的问题,有的时候呢,可能是因为是这个网络的问题,然后这个电脑有的时候啊,它会联网去,去搜一些东西,可能是刚好在搜的过程中卡住了啊,然后呢,我把它强制关掉了,杀死了这个进程,然后的话,再打开又好了,但是一般呢,咱们能多等一会,尽量多等一会,要不然的话,强制杀死这个,eclipse进程啊,它有可能,会产生一些垃圾文件,产生垃圾文件以后呢,有的时候,你再怎么启动,再怎么访问,它都老是报错啊,不太好,但是没办法啊,那如果说,出现这种情况啊,比如说你强制把它杀死了,只要一启动,它老报错,你不行的话,就切换一个workspace,你换个workspace,那么那些,因为它换了工作空间,它的那些垃圾文件就丢弃了啊,重置了,就好了。

Filter组件为单例模式

  • 那刚才呢,咱们没测啊,再测一下,我们把这个项目部署,然后呢,启动tomcat,那一启动啊,你就发现,直接就输出一些东西了是吧,哎,直接输出一句话,初始化LogFilter。
  • 所以这个filter默认,就是一启动tomcat就初始化,那你看tomcat只启动一遍,filter只初始化一遍,对吧,所以,它是不是一个实例啊,对吧,就是LogFilter只有一个实例,单例的啊,那你想啊,启动时初始化,什么时候销毁呢,肯定是关闭时对吧,shutdown时,一会试一下,先不着急试啊。

Filter中的doFilter方法的作用

  • 那现在呢,我要访问,我们不是呢,要访问filter,因为我们要访问是谁呢,访问的是增加,删除对吧,在访问增加删除的时候,被filter给处理了对吧,被filter给拦截了,我们要做这样一件事情,那下面呢,我们打开浏览器,我们去访问这个AddCostServlet,DelCostServlet,看一下,我们在filter里写的代码,会不会被自动调用啊,看一下,这个项目名啊,叫jsp6,然后呢,叫addCost,即localhost:8080/jsp6/addCost。那访问以后啊,页面上一片空白,因为我们没有写响应内容,对吧,就是这样,那我们看以下控制台,有没有什么输出啊,你看,这个神奇的事情呢,发生了啊。
  • 最终呢,它输出了增加资费,它调用了我增加资费这个逻辑对吧,这个业务,但是呢,增加资费之前,记日志啊,之后也记日志了,是吧,它实现了记日志,那你看,我们使用filter,能够实现这件事,在业务逻辑之前前后记日志,但实现的时候,我们每一个Servlet,每一方法都不需要改的对吧,只需要额外写一个类,进行配置就能实现,这很神奇,那之所以呢,能够实现这个效果啊,那最关键的是谁呢, 最关键的是,是这句话,chain.doFilter(request, response);,那chain.doFilter是什么意思呢,这句话的意思是,让请求继续,就放行。
  • 这个filter就好像什么呢,好像是一个,怎么说呢,一个管家一样,我去一个豪门大户,我要去拜访谁谁谁,这管家不让,拦着啊,看看你这个怎么表现了是吧,表现好你进去,表现不好,滚蛋啊,就这样。所以说,当然了,还有可能还几个管家,层层的,一层一层的啊,是这样的,这个filter,就好像一个管家一样,那么一旦有了filter,一旦我们在配置文件里面声明了,这个filter,可以过滤谁,拦截谁,那么,我们再访问服务器的这个组件时,得先过它着一关,明白么,或者说tomcat会先调filter,tomcat呢,把这调用的,就程序调用的权力,全权交给filter,那你看tomcat把谁,把什么都给它们了呢,把request和response,都给它了对吧,就本来啊,tomcat应该去调那个Servlet处理请求,但是呢,tomcat一看啊,有filter,那你管吧,把request和response给它了,你去调,明白吧,这个意思。
  • 所以呢,当程序中有filter以后,tomcat不是直接去调Servlet,而是调filter,由filter决定啊,是否可以调用这个Servlet,那决定与否的关键是什么呢,是我们写的那句话,就是那个chain.doFilter(request, response),那句话,如果呢,我们在程序中写下了doFilter,那这句话的意思呢,是让程序放行,往下执行,调用组件,明白吧,继续,如果你没写doFilter,这个程序,这个请求,就中断了,不会往下执行,从filter的这个拦截器(过滤器),比如LogFilter这里就结束了,不会调Servlet组件了,明白吧,就直接就结束了,但我们一般会调,特殊情况不会调啊,那么哪些特殊情况不会调,我们后面的案例会演示啊,总之,这个doFilter的意思,这个意思,标一下吧,就是这个箭头啊,代表让程序去执行Servlet组件的这个方向的箭头,它就是doFilter(),它的作用,是让程序继续向下执行,或者说,让请求继续啊,这个意思,doFilter啊,是让请求继续。
  • 然后呢,如果不去doFilter,那程序将会中断啊,到此结束(在这个没有执行doFilter方法的LogFilter这里,程序就被拦截了)啊,就这样。两种情况,那我们写的这个是LogFilter,它能够记日志,那很有可能,我们在项目中呢,不只是一个公共业务,可能是我既要记日志,又要过滤敏感词,那这种情况,我们写一个filter可以,这个filter既记日志,又过滤敏感词,可以,明白吧,但这样不好,这样的话,两件事揉在一起写,这个耦合度高了,我们最好是什么呢,一个Filter做一件事,处理一个业务,明白么,如果你还有一个业务,过滤敏感词,再写一个filter,那我们再写一个,再体会一下啊,因为刚才体会的,这个还不够啊,再接着,再体会一下。那我们再写一个filter啊,另外一个filter啊,也会,对这个请求进行一个过滤和拦截啊,也会在请求阶段和响应阶段,各拦一次啊,图中,标记3这拦一次,标记4这栏一次,也会这样。
  • 那我们再写一个filter啊,我们给它取个名字啊,叫什么名字呢,这是过滤敏感词啊,那这个类啊,我给它取名叫,叫GuolvFilter吧,没办法,不好取名字,为啥呢,因为filter的意思,本来就是过滤器,过滤的意思明白吧,我总不能叫FilterFilter,太难听了,或者叫doubleFilter,不好,所以就GuolvFilter啊,这样,毕竟能感觉能舒服一点啊,GuolvFilter。那这个Filter也一样啊,就是说,如果呢,你在这个filter之内,你调了doFilter方法,那么请求怎么样呢,继续,放行,如果你没调doFilter,怎么样呢,中断,还是这个套路,明白吧,那任何的filter,都是这个一样的套路啊,所以,调了doFilter,请求继续,没调doFilter,中断啊,就这样。
  • 那我们写了两个filter的话,这俩filter,那都会调用,有没有先后顺序呢,那肯定得有对吧,那谁先谁后呢,难道是LogFilter,我先写就先调么,肯定不是,那这个以什么顺序为准呢,有人说以配置文件,哎,说的对了,但是你看啊,配置文件里头啊,我们配一个filter,有两段,一段是filter,一段是filter-mapping对吧,那这个到底是以谁为准呢,那有人可能会想,哎,这有区别么,咱们写两个filter,这不两段,那filter在前,mapping也在前对吧,还能不一样么,其实不是 啊,有可能是这样写,我们配filter时啊,有可能这样写,就是说,<filter></filter>,写了一个类啊,再写个filter,这filter啊,配了一个filter,那再来一个,<filter></filter>,又声明一个filter,然后呢filter-mapping<filter-mapping></filter-mapping>,然后呢,再来一个filter-mapping<filter-mapping></filter-mapping>,这是有可能的,有可能会这样配。

<filter></filter>
<filter></filter><filter-mapping></filter-mapping>
<filter-mapping></filter-mapping>

  • 那filter之间的顺序到底由谁决定呢,由这个mapping决定。看mapping的顺序啊,这标一下啊,那么多个Filter,这个之间的顺序,由filter-mapping决定啊,那哪个filter-mapping在前,顺序就在前,这样。
  • 那下面呢,我们再写第2个Filter,咱们试一下啊,进一步加深对filter的理解。那我们再打开我们这个开发工具啊,eclipse。打开以后啊,jsp6这个项目啊,我们在web这个包下,再去创建一个新的类啊,这个类,我取名叫GuolvFilter,也是要实现Filter(javax.servlet.Filter)接口。
  • 那么,创建了这个类以后啊,这个类当中呢,咱们也是啊,写出一些代码来,比如说,销毁的时候,那我也写一句话,销毁GuolvFilter,System.out.println("销毁GuolvFilter");,那么,另外的方法init,也是输出一句话啊,初始化GuolvFilter,System.out.println("初始化GuolvFilter");,最后啊,这个doFilter方法,咱们也处理一下,咱们先把这个方法的参数呢,先整理一下,3个参数,当然这个参数,前两个还没用上啊,我们在后面的案例中呢,会用上,现在的话先,先放在这吧。
  • 那这个方法呢,我们最终也要调用chain.doFilter,一般都得调啊,我也调chain.doFilter,调用时传入request,以及response,在chain.doFilter(request, response);,这句话之前之后,分别输出一点内容。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("在前面过滤敏感词");chain.doFilter(request, response);System.out.println("在后面过滤敏感词");
}

  • 总之啊,这个类,咱们的代码结构呢,和LogFilter一样,所以再巩固一下这个语法,那么,主要呢,是看一下,写了两个filter,这个调用的顺序,那么写完以后呢,它也要进行配置,我们打开web.xml,把它也配置好。

<filter><filter-name>guolv</filter-name><filter-class>web.GuolvFilter</filter-class>
</filter>
<filter-mapping><filter-name>guolv</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

  • 那配置完以后啊,咱们也要进行测试啊,把这个项目啊,再重新部署一下,部属以后呢,重启tomcat,启动时呢,看一下控制台,启动时看一下控制台,这个初始化俩对吧,因为这两个不是都声明了么,都配了吧,对吧,两个都初始化了啊。
  • 然后,初始化以后啊,那我们打开这个浏览器,我们再访问一下,增加,或者是删除,都行啊,访问谁都行,我就去访问删除吧,我上次访问的是增加对吧,这回访问删除啊,看一下啊,delCost,即localhost:8080/jsp6/delCost,那访问完以后呢,看一下控制台,你看,
  • 它最终啊,输出了删除资费,调了我最终的这个业务,那么,在此之前,它先记日志对吧,然后过滤敏感词,在此之后呢,它先过滤敏感词,再记日志,哎,为什么在删除资费,在此之前之后,这个顺序是相反的呢,那你看下我这个图。
  • 你看符不符合我画的这个图的顺序呢,在调用Servlet之前,即执行doFilter方法之前,先这个LogFilter,再GuolvFilter对吧,在Servlet之后,即执行doFilter方法之后,先GuolvFilter,再LogFilter对吧,是按照这个顺序来的,所以这个顺序啊,你把它记一记,记这个图就好了(多个Filter之前的顺序由filter-mapping决定),所以呢,我们发现呢,我们确实能够利用filter,统一处理业务,而且呢,这个用它来处理业务,这个Servlet,不用写任何代码啊,那么这个Filter和Servlet,基本上是没有耦合度对吧,基本上是没有耦合度的,很容易管理的,这是它的优点。
  • 然后呢,你看啊,刚才也讲了,这个当我们程序中,我们项目中有了,有了这个filter以后,那么客户访问这个服务器,那服务器呢,不会自己去调这个Servlet,它会把这个请求处理的权力,全权交给Filter,所以呢,tomcat直接调了,第一个filter,LogFilter,调用时,它把这个request和response,传给它,如果第一个Filter,感觉有问题,那么,不调用doFilter,请求中断,那第二个filter也不会执行,是这样吧,所以,如果呢,我们在调LogFilter时,这个它里面没有调doFilter方法,请求会中断,中断时,后面的Filter还会执行么,不会,到此为止。但是呢,如果我们调了doFilter请求继续,对吧,继续执行,会执行到,请求继续执行,因为后面还有filter,所以呢,它会把处理请求的权力,交给第2个Filter,它接着处理,也是啊,那么,请求继续doFilter,请求中断,不doFilter,就这样。
  • 总之呢,如果你有多少个filter,10个filter,20个都是一样,按顺序,逐个执行,filter就是一个管理者,它拥有对请求的绝对处理权,那么它处理完以后,会把请求交给后面的,后面的filter,就再继续处理,直到所有filter都处理完以后,调用组件,明白吧,而每一个filter,处理一项业务,解决一个问题啊,所以你看,咱们这个doFilter方法,chain.doFilter(request, response);这个方法啊,它两个参数,这俩参数谁给它的呢,是之前的处理者给它的,它之前的处理者,可能是另外的filter,也可能是tomcat啊,这个参数,是别人给它的,然后呢,这个你看啊,第3个参数,叫FilterChain,chain是链的意思,对吧,链。为啥叫链呢,因为这个filter在处理请求时,它是环环相套的,是一环一环的,那只有这一环通过了,才能进入下一环,对吧,它是一个环节,一个环节,这一环节通过了,进入下一个环节,这一环通过了,再进入下一个环节,是这样的,那在任何一环上中断,就脱落了,后面就不会执行了。
  • 所以呢,这个形象来讲,就这件事,就好像一个,这个filter之间啊,它的关系就好像是一个链条一样,是环环相扣的啊,那么,一环套一环,如果中间某一环脱落,后面的就中断了,明白吧,就没了啊,它这样的。所以呢,那它取名的话,也是来源于生活啊,它认为这是一个链条啊,所以取名叫FilterChain,过滤链啊,这样,那关于这个filter啊,大家尽量去体会,总之呢,它是这样设计的,然后呢,tomcat是这样调用的,是逐个filter挨个调用的,尽量体会,然后呢,这个套路会用就行了,反正就是这么用啊。那么,咱们刚才呢,看了这个init,还没看到destroy对吧。我们把这个tomcat呀,这个关一下,看看这个销毁的时刻啊。
  • 那我在Servers之下,点这个红色按钮,把它stop,stop以后啊,再看这个控制台,是吧,销毁了啊,销毁LogFilter,销毁GuolvFilter。
  • 那我们刚才呢,在写这个filter的时候啊,还有一个内容没有说啊,这个filter的方法里边,它这个init方法里边,还有个参数叫FilterConfig,是吧,我要讲下这个参数怎么用,那你看到这个参数,你会想到什么呢,你会想到ServletConfig对吧,那么ServletConfig,它也是在init方法里传入的是吧,跟那个一样,其实道理都一样,那么ServletConfig和Servlet之间,一对一,对吧,ServletConfig可以给这个Servlet提供数据,初始化数据,那FilterConfig和这个Filter也是一对一,明白吧,它能够给呢,Filter呢,初始化数据,那这个FilterConfig的使用方式和ServletConfig一模一样啊,那比如说,现在,我想给这个GuolvFilter初始化点数据,随便初始化点什么啊,我们看,演示一下,了解一下。
  • 那你想给它,给Filter初始化数据呢,那我们需要在web.xml中配啊,所以我们再回到这个配置文件,那么,我们在这个filter-class之后,在这个filter之内加以配置,配置方式,咱们以前ServletConfig也写过啊,和以前的那个写法是一模一样的,那么,传个什么参数呢,还是来个城市吧,我不知道写啥好了啊,city,beijing啊,好了。

<filter><filter-name>guolv</filter-name><filter-class>web.GuolvFilter</filter-class><init-param><param-name>city</param-name><param-value>北京</param-value></init-param>
</filter>

  • 这个配置文件写完以后,那我们回到那个GuolvFilter,我们用Config接收一下这个参数啊,那我把这个参数名改一下啊,这个叫arg0,有点不舒服啊,我改成叫config缩写cfg,然后呢,在这个init方法之内,我调用config,获取这个数据啊。

public void init(FilterConfig cfg) throws ServletException {System.out.println("初始化GuolvFilter");String city = cfg.getInitParameter("city");System.out.println(city);
}

  • 那下面呢,写完以后呢,试一下啊,我们把这个项目重新部署,启动的时候就能看到,因为启动时就调了init,那我重新呢,部署这个项目啊,重启tomcat,启动完以后呢,看一下控制台,看一下,北京啊,输出了,

过滤器模拟案例jsp6之代码实现

1.jsp6/src/main/java/web/AddCostServlet.java

package web;import java.io.IOException;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class AddCostServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {System.out.println("增加资费");}
}

2.jsp6/src/main/java/web/DelCostServlet.java

package web;import java.io.IOException;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class DelCostServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {System.out.println("删除资费");}
}

3.jsp6/src/main/java/web/LogFilter.java

package web;import java.io.IOException;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;public class LogFilter implements Filter {public void destroy() {System.out.println("销毁LogFilter");}//公共的业务在此处实现public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("在前面记日志");chain.doFilter(request, response);System.out.println("在后面记日志");}public void init(FilterConfig arg0) throws ServletException {System.out.println("初始化LogFilter");}
}

4.jsp6/src/main/java/web/GuolvFilter.java

package web;import java.io.IOException;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;public class GuolvFilter implements Filter {public void destroy() {System.out.println("销毁GuolvFilter");}public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("在前面过滤敏感词");chain.doFilter(request, response);System.out.println("在后面过滤敏感词");}public void init(FilterConfig cfg) throws ServletException {System.out.println("初始化GuolvFilter");String city = cfg.getInitParameter("city");System.out.println(city);}
}

5.jsp6/src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"><display-name>jsp6</display-name><welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list><servlet><servlet-name>addCost</servlet-name><servlet-class>web.AddCostServlet</servlet-class></servlet><servlet-mapping><servlet-name>addCost</servlet-name><url-pattern>/addCost</url-pattern></servlet-mapping><servlet><servlet-name>delCost</servlet-name><servlet-class>web.DelCostServlet</servlet-class></servlet>  <servlet-mapping><servlet-name>delCost</servlet-name><url-pattern>/delCost</url-pattern></servlet-mapping><filter><filter-name>log</filter-name><filter-class>web.LogFilter</filter-class></filter><filter-mapping><filter-name>log</filter-name><url-pattern>/*</url-pattern></filter-mapping><filter><filter-name>guolv</filter-name><filter-class>web.GuolvFilter</filter-class><init-param><param-name>city</param-name><param-value>北京</param-value></init-param></filter><filter-mapping><filter-name>guolv</filter-name><url-pattern>/*</url-pattern></filter-mapping>
</web-app>

Filter在NetCTOSS中的应用:用户登录验证功能

登录验证功能需求分析与设计

  • 那通过这个jsp6啊,这个小例子啊,咱们就把这个过滤器啊,它的语法,有了一个演示啊,那么具体说,我们怎么去利用它,怎么在项目中解决问题,这个还需要在项目中再练啊,所以啊,下面我们就去完善这个电信计费项目,那么,我们就是用这个filter,解决这个项目中的一些问题,解决它的一些问题,什么问题呢,看一下。那么对于电信计费项目啊,如果说我们用的话,正常来说是先要去登录,先得登陆对吧,然后才能访问里面的东西,但现在没登录,也能访问,我可以直接敲路径啊,localhost:8080/netctoss/findCost.do,可以访问对吧,比如说,我直接敲路径啊,localhost:8080/netctoss/toAddCost.do,也可以对吧,这就是bug了啊,这就好比什么呢,说你家呢,辛辛苦苦盖了个房子,房子盖的很不错,很漂亮,然后呢,装了一个非常豪华的大门,没有墙是吧,这门白扯。
  • 所以呢,我们需要呢,给这个软件呢,做一套围墙,让它这个安全对吧,让它靠谱啊,那这个墙怎么做呢,我们用软件的手段来做,我们应该加以判断,怎么判断呢,应该是啊,在访问这个软件的任何请求之前,在请求之前,我们判断一下,用户有没有登录,如果它已经登录了,ok,你继续访问,没问题,如果你没有登录,我们要拒绝对吧,应该将它转,将它跳转到什么呢,登录页面,应该这样做啊,大概是这个思路,那你看,咱们这个访问资费查询,资费增加,资费修改,资费删除,任何功能,是不是都得判断啊,都要判断,很显然,这是一个公共的业务,那既然是公共业务呢,我们最好,就由filter处理啊。
  • 那下面我们说一下啊,这个具体,我们怎么去处理啊,说的更精确,更这个准确一点啊,它到底怎么写代码,说一下这个思路,那你注意啊,咱们判断的时候啊,其实不是说,所有请求都要判断,其实有个别几个请求,是不需要的,你比如说,我们打开登录页面,toLogin.do,这个需要判断么,这就不需要,因为它正要登录对吧,你就不要判断,所以呢,我们得搞清楚,那到底呢,具体说,哪些需要判断呢,哪些不用呢,我把它说的明确一点啊,所以呢,我把它这个,我们现在呢,写的请求啊,罗列一下啊,这个思路上呢,让它更 清楚一点啊。
  • 我画的这个方块,代表服务器啊,因为呢,我们做的功能,它都是服务器端提供的功能,所以这个方块代表服务器,那目前呢,服务器端,我们提供了如下的功能啊,提供了什么呢,这个我就写路径了,这个提供了/toLogin.do,是吧,有这个,这是一个功能,还有什么呢,/login.do对吧,还有什么呢,和登录相关的,还有一个/createImg.do,刚刚做的对吧,这不也是么。还有啊,还有什么呢,/toIndex.do,还有这个/findCost.do,还有/toAddCost.do,还有/addCost.do,如果你做修改,还有/toUpdate.do,做删除,还有什么delete等等啊,那么这个项目如果做完的话,这个请求就多了去了啊,但是呢,我们就不写那么多,我们没做完啊,我就是做几个我就写几个啊。
  • 那看一下啊,这么多请求当中呢,哪几个请求是我不需要去判断的,不能去判断的,toLogin.do能判断么,不能,因为我打开登录页面,这肯定不能登录对吧,要登录的话,我要打开登录页面干什么呢,一定是没登录,你就别判断了,如果你判断会怎么样呢,我一打开登录页面,就判断啊,我登没登录啊,没登录,一脚把我踢回到登录页面,然后又判断,登没登录啊,没登录,又踢回去,循环了,明白吧,死循环了。所以一会啊,我们在写程序的时候,如果说,咱们一运行,有同学发现呢,出现一个问题,提示你什么呢,有个提示叫,叫什么循环重定向,要不重定向次数太多之类这样的提示,意味着什么呢,你一定是有些路径,不该检查你检查了,明白吧,是这样的。
  • 所以,toLogin.do这个是不能检查的啊,我给它表一个蓝色吧,不能检查,还有别的不能检查的么,/createImg.do,这个能检查么,因为这也是正在登录的路上对吧,还没开始登录呢,它也不能检查,还有么,那我问问你啊,/login.do需不需要检查呢,login.do是我正在登录对吧,登不登录还不一定呢,是吧,所以,你在调这个方法时,还没有确定它成功了是吧,所以login.do这个方法是不是也不应该检查啊,这也不应该检查,是正在登录,成不成功,还未知呢,而其他的,打开主页,查询,增加,一定得是登录成功以后,才能做,是这样么,就这些是得检查,我把它们隔开点啊,就是说,做一个区别啊。/toIndex.do/findCost.do/toAddCost.do,还有/addCost.do,这些是需要检查的,这3个是不需要检查的,/toLogin.do/login.do/createImg.do,这3个不要检查,剩下这些需要检查,那检不检查,由谁检查呢,刚才不说了么,我们写个filter对吧,由filter检查,那你看这个filter,它所做的事情,是检查是否登录对吧,登录检查,我取名叫什么呢,LoginFilter行吧,我给它取名叫LoginFilter。
  • 那么,有了filter以后啊,用户,它在访问服务器的时候,服务器会怎么处理呢,服务器不会直接去调Servlet对吧,不会,它会把请求,直接给了filter对吧,由filter全权处理,它把处理请求的工具,request和response,也给了LoginFilter是吧,全权处理,好了,那Filter就处理吧,这得怎么处理呢,主要就是判断啊,我就判断一下用户是否登录是吧,如果用户已经登录,那怎么办呢,继续对吧,你继续,那如果用户没登录呢,跳转到登录功能对吧,跳转,跳转到登录,/toLogin.do,那怎么跳转呢,是怎么过来,显然是重定向对吧,因为你看,我们当前,比如说当前访问的时查询,一看没登录,到toLogin.do,这是两件事对吧,查询,登录,两件事啊,重定向,所以要重定向过去,重定向过去,要这样过去,这样能看明白吧,有点奇葩啊,颜色重复了,那就这样啊,就这样。
  • 我把这个顺序啊,标一下啊, 就是说,正常来说,用户正常的,访问这个服务器,发出了请求,那服务器得到请求一看,哎,有filter,它把这个请求交给了filter,对吧,哎,LoginFilter,然后呢,filter,对用户是否登录加以判断,登录了,ok你继续,没登录,重定向,对吧,是这样的,就这样一个步骤啊,基本上是4步,那么其中呢,第3步是什么情况,是这样呢,是已经登录了,对吧,成功了,那我写个y,ok了啊,第4步,那这种情况是什么呢,没有登录,no,n啊,标一下,这样。这是我们这个,大致的这个步骤和原则,但是有个小问题,我在写这个filter时,我要判断用户是否登录,我以什么为依据来判断,我得判断什么呢,有人说对了,我是不是可以看session啊,如果说session中有用户名,它就登录了对吧,因为只有登录时,我才往里写了用户名,是这样吧,对吧,那你想一想啊,我能不能判断cookie呢,不是还有cookie么,可不可以呢,从功能上讲,可以,但使用cookie的话,它安全么,不安全,我们登录是为了安全对吧,是吧,检查登录是为了安全,所以这个不要用cookie了,就得用session。
  • 那么session是什么时候产生的啊,是这样的,就是说,我们/login.do时,如果说,登录成功了,我们就会把账号,adminCode存入session是这样吧,我们会把这个adminCode存入session啊,就总之啊,在登录的这个请求里,如果说用户登录成功了,我们会将adminCode写入session,那么LoginFilter想知道,用户是否登录,我们只需要去判断session就可以,是这样吧,对吧,所以这个session是关键,所以,虽然说当前的案例啊,我们主要想演示的是LoginFilter,但是呢,也会结合着以前的内容,也会把这个session再用一下啊,session还是挺有用的啊。这个就是咱们这个登录检查,大概的这个思路,那么,整个过程说起来比较啰嗦,比较麻烦,但写起来的话,还是比较容易的,我们这些/***.do,这些请求需要写么,不需要了,这些地方都已经写好了对吧,不用动,我们唯一要写的就是,写这个LoginFilter对吧,新写个filter,一配就ok了,很容易啊。

登录验证功能代码分析

  • 下面我们就来写这个代码,我们写这个LoginFilter啊,那么打开电信计费项目netctoss啊,打开以后,我在web这个包下,创建一个filter,在web之下创建这个LoginFilter这个类啊,创建好这个类。
  • 当然这个类,你要实现接口对吧,实现Filter接口,那么这个类呢,有3个方法,其中init和destroy,一般是不用的,所以我们这个案例中,这俩方法,没有用,所以我就把它放在这,不动了,这个注释啊,看的烦啊,删了啊。然后呢,我们呢,主要是写这个doFilter方法,那么,这个方法,我把它的参数呢,稍微整理一下,要不然这个名字呢,不太好用啊。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {... ...
}

  • 那下面呢,我们在这个doFilter方法里啊,处理这个业务逻辑啊,那这个逻辑很简单啊,刚才说了,我们就是呢,通过session去判断用户当前是否已经登录对吧,那首先呢,我们得获得session,那获得session,session通过什么获得呢,什么啊,request,那就得吧,request,然后呢,request.getSess...,哎,我发现我没Sess..出来啊,为啥呢,这个类当中,没有那个方法,对吧,这太缺德了,没有那个方法,怎么办呢,你说怎么办,自己new一个request,这个不靠谱,为啥呢,你自己new的request,你知道这个请求的来源么,你知道里面它有什么数据么对吧,啥也不知道,没法new啊,这个request必须由服务器给我们,那其实呢,这个request,就是我们想要的那个,只不过类型不符合我们的要求对吧,我们以前用的是那什么http的那个,对吧,那怎么办呢,可以强转啊。那有人说,哎,这为啥可以强转呢,咱们先转着,后面我再讲啊,转完以后再说啊,先转一下。

    //将参数转型
    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse res = (HttpServletResponse) response;

  • 不但把request转了,把response顺手也转了,都可以转,反正呢,是可以啊,把这参数强转成我们所熟悉的这两个参数啊,我先转了啊,那凭什么能转,我这个案例写完之后,再说,别着急啊。那第一步啊,咱们补充个注释吧,这一步啥意思呢,是对参数加以处理,将参数呢,转型。那得到这个参数就好办了,我们有了这个request,我们就可以得到session了对吧,那下面就获取session啊,然后呢,从session中尝试获取那个账号,所以下一步是从session当中获取账号。

    //从session中获取账号
    HttpSession session = req.getSession();
    Object user = session.getAttribute(“user”);

  • 那么,我通过request也得到了session,然后呢,我从session中呢,获取了名为user的数据,咱们登录成功以后,存的名叫user对吧,然后呢,得到的数据啊,我没有转型,我就是Object,因为我并不关心它的值是什么,我只关心它有没有,明白吧,它是不是空的,所以没转型,这个对象Object就可以啊。那么,如果你登录了,这个user就是非空的,如果没登录就是空值对吧,判断它user,就知道你等没登录啊,所以呢,下一步是判断用户是否登录,那就判断它,user是否为空就可以啊,user等于null么,那么,如果user等于null,说明什么问题呢,没登录,没登录怎么办,把它跳转到登录页面对吧,重定向过去啊,重定向到登录页面,然后呢,else否则,否则就已登录对吧,已登录,已登录怎么办呢,就请求继续向下执行对吧,你别管,就是请求继续执行。


//判断用户是否登录
if(user == null) {//没登录,重定向到登录页//res.sendRedirect("/netctoss/toLogin.do");res.sendRedirect(req.getContextPath()+ "/toLogin.do");
} else {//已登录,请求继续执行chain.doFilter(request, response);
}

  • 那具体实现的话,怎么让请求继续执行呢, 怎么让请求继续执行啊,chain.doFilter对吧,你调这句话就继续,对吧,不调这句话就中断,所以这里得调啊,chain.doFilter(request, response);,然后你把这个request和response传入。那没登录要重定向,重定向得用response对吧,那res.sendRedirect("/netctoss/toLogin.do");,然后呢,写上路径,那你看这路径啊,我怎么写啊,绝对还是相对啊,绝对,因为你不知道它访问谁对吧,不知道,所以绝对路径,绝对路径呢,我们要以斜线开头,项目名开始写对吧,它得这样写啊,"/netctoss/toLogin.do",这样写吧,还有一点不好的地方,这个项目名,在路径里写死了,那万一说将来项目名要改的话,是不是你还得把这个改一下,这样可能会有一些问题,不过问题不大啊,要想把它更完善一下,避免这个问题怎么办呢,我们可以这样 res.sendRedirect(req.getContextPath()+ "/toLogin.do");,因为你看 ,getContextPath()不就是得到这个项目名么,对吧,你把它拼一下,如果项目名改了,这个值也会变啊,这块就不用改啊,那这个Filter啊,基本的逻辑,基本就完成了。
  • 那么它的逻辑很简单,比这个,比这个登录验证还简单,对吧 ,比较简单啊,反正就是呢,看session里有没有user啊,如果呢,没有,就没登录,就得重定向,如果有了,就等录,就继续,就可以了啊。那这个登录检查功能啊,还没完成啊,那我们写了LoginFilter这个类,这个还没写完呢,还有一点需要补充的地方啊,当然,补充什么呢,主要是对它进行配置,配置时呢,我们会看到一些问题,我们再补充啊,那我们下面呢,把这个类呢,配置一下啊,那么,打开这个netctoss,这个项目里的配置文件web.xml,打开这个文件以后,往后看,我们在这个文件的最后啊,对它进行配置,这是配置的内容,是登录检查这个过滤器啊。
  • 那配到了,它能够过滤的路径的时候,有点纠结了,如果说呢,它能过滤一切请求,我毫不犹豫写成*.do对吧,因为我们这个项目中都是点do么,如果说呢,它能只过滤一个请求,我就把那个请求路径写上就好了,对吧,但这都不是,它既不是一个,也不是全部,而是一部分,对吧,是多个,那这怎么写呢,有人说,你能不能写路径点,逗号,路径,逗号分开呢,这还不行啊。
  • 那怎么办呢,我们这块啊,没办法,还得写什么呢,*.do,先写上全部的请求,都由它来过滤,那么,你这样配的话,它肯定过滤的 就多了对吧,多了,那么,那几个不需要过滤的怎么办,我们在那个代码中,把它再人为的排除掉,明白吧,就这我是全过滤,配置文件配置Filter时,我是全过滤,但我们在写那个Filter时,可以人为的把不需要过滤的,把它排除掉,明白了吧,想办法写一下逻辑就就可以了。

<filter><filter-name>login</filter-name><filter-class>web.LoginFilter</filter-class>
</filter>
<filter-mapping><filter-name>login</filter-name><url-pattern>*.do</url-pattern>
</filter-mapping>

  • 那我们再回到LoginFilter啊,需要对它呢,进行补充啊。这是我们写到这块的时候啊,发现了问题啊,对它做了一个补充,那我回到 LoginFilter,那么我们在最开始的地方啊,比如说就在,就这吧,我们参数也转完型了,我马上就要判断了,从session中获取账号之前这,但是我得看一下,我当前这个请求,需不需要判断,对吧,得排除一些啊,那写一下啊,就是说,有几个请求,不需要这个过滤啊,需将它们排除,那哪几个请求不用过滤呢,看一下,之前也列举了啊,有3个对吧,/toLogin.do/login.do/createImg.do,那我下面把这些啊,不需要过滤的请求啊,把它归纳到一起,我们想办法一起过滤,我把它们呢,组合成了一个数组,写个数组啊。

//有几个请求不需要过滤,将它们排除
String[] paths = new String[] {"/toLogin.do","/login.do","/createImg.do"
};
String current = req.getServletPath();
for(String p : paths) {if(p.equals(current)) {chain.doFilter(req, res);return;}
}

  • 那么这个数组当中呢,是所有的,我们不需要过滤的对吧,不用管的路径啊,那你注意啊,这个路径,你要写对了,如果说呢,某一个 路径你没写对,那这个该排除没排除,我们后面对它进行了过滤,就会出问题,那你可以判断,就我们在访问/toLogin.do的时候,就判断,它登没登录,没登录,又进入了登录页面,就会死循环,明白吧,那个错误叫循环重定向啊,所以这个路径一定要写对了啊,反过来讲,如果说啊,一会你发现报错,循环重定向,就估计是路径,你写错了,回来检查啊,那么对于这些路径,如果说,当前的访问路径是这/toLogin.do/login.do/createImg.do,这3者之一,我们就继续对吧,让请求继续,别去过滤,那当前路径是谁呢,当前路径是谁啊,有人说不知道,对,用户访问谁,我们不知道,但如果程序走到这了,我们能知道么,那用户已经输入了路径,已经走到这了,能知道么,能知道吧,那怎么得到那个路径呢,怎么办,request怎么呀,req.getServletPath();,是这样吧。
  • 就是说,确实,我们事先呢,没法估计用户到底会访问谁,但是程序一旦走到这的时候,说明用户已经把路径敲出来了,对吧,他已经访问了,这个时候就能确定了,就能得到了啊,那我们得到的是当前的路径啊,当前路径我叫current,当前的意思,然后呢,我要看一下,这个当前的路径,它是不是这3者之一,如果是其中的任何一个,就不用执行后面的代码了,对吧,到此为止啊,那你要判断的话,就得循环了对吧,这是不是数组3个值啊,你得循环了,我循环一下啊,那我循环遍历呢,那3个路径,每次循环得到一个p,那么如果呢,当前路径等于p的话,那说明它是这3者之一,对吧,此时,后面代码不用执行,我先return;,到此为止。但是呢,我们不能让程序就中断在这,就什么也不管了,对吧,你得怎么样呢,让请求继续对吧,你可以去访问这个请求对吧,怎么让请求继续呢,还是那句话,chain.doFilter(req,res);

编程思维之代码阅读经验

  • 所以你看啊,咱们这个地方,这段代码,是非常典型的,我们在书写代码时是,这个代码是具有跳跃性的,我们书写代码时呢,不是由上向下来写的,是可以跳跃的,是按照呢,我们的思维方式,是按照我们的思维惯性来写,那我在写的时候,我的思维想到哪,我就写到哪,这个是顺的,即便是跳了,我思维没乱也是顺的,对吧,没问题,但是什么时候有问题呢,你去工作了,刚入职,然后呢,你们这个项目刚做到一半,还在进行当中,你加入了,那项目经理肯定让你看看,熟悉以前的代码,好,你熟悉吧,那当你去看别人的代码的时候,他的思维逻辑的惯性你是看不到的,你只看到的是有序的代码对吧,你一定会从上往下去看,而当你突然看到这样一段代码的时候,有点愣,哎,这什么意思,为啥要这样一下对吧,感觉有点别扭,但是呢,实际他开发时,不是先写这段代码,是先把这段逻辑写完,后补充的对吧。所以中间的这个过程,你是读不到的,尤其是在没有经验的时候。
  • 所以一开始,你去看别人的代码,尤其是比较复杂的时候,确实会有很多的这个困扰的地方,很多地方你看不懂,然后呢,尤其是你去看,比如说,你去看Spring框架源码,那就更看不懂了,就明明是,就一件简单的事,你会发现那个框架的底层的处理,是极其的繁琐,极其的啰嗦,为啥呢,它想让这段代码,能够适用全世界所有的项目的逻辑,能够给所有的程序员用,不是那么容易的,它需要考虑的过于全面,所以说,现在啊,咱们这个编程经验太少啊,读的代码太少,所以你在看代码时,会有一定的障碍啊,那么,你要想跨过这个障碍,就得有大量的编程经验,就得多写啊,你得多练,只有呢,什么时候,你已经非常习惯于这样写代码了,你已经适应的时候,你才能够去读别人代码时呢,用另外一个角度去审视这个代码,哎,你可以用跳跃啊,这个逻辑是这样的,我跳跃的看,你才会有这种,这样的想法,不然的话,这个习惯是改不过来的啊。
  • 总之啊,就是说,你得多练啊,那这段逻辑,这就写完了啊。写完以后呢,这回就可以测了,那测试一下啊,那么,我把这个项目,重写部署,然后启动tomcat,启动以后啊,我打开浏览器,打开浏览器之后呢,我不去登录啊,我想直接增加,你看,我当前的地址栏,就是增加的路径,localhost:8080/netctoss/toAddCost.do,我就刷新一下,我看能不能访问这个增加的功能啊,就/toAddCost.do,我刷新了啊,一刷新,哎,直接登录了对吧,强制我去登录了,我再试一下,比如说我敲那个路径啊,/findCost.do,看能查询么,也不可以对吧,都不行,被限制了。那我没办法,我就登录吧,然后呢,我看一下,我登录以后,能不能访问这些功能,登录以后,这就可以了。那这个话题呢,还要再补充一下,刚才在做登录检查的时候,我们在这个方法的一开头啊,对参数做了转型对吧,凭什么能转,介绍一下。

Filter在NetCTOSS中的应用之登录检查功能代码实现

1.src/main/java/web/LoginFilter.java

package web;import java.io.IOException;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;public class LoginFilter implements Filter {public void destroy() {}public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {//将参数转型HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse res = (HttpServletResponse) response;//有几个请求不需要过滤,将它们排除String[] paths = new String[] {"/toLogin.do","/login.do","/createImg.do"};String current = req.getServletPath();for(String p : paths) {if(p.equals(current)) {chain.doFilter(req, res);return;}}//从session中获取账号HttpSession session = req.getSession();Object user = session.getAttribute("user");//判断用户是否登录if(user == null) {//没登录,重定向到登录页//res.sendRedirect("/netctoss/toLogin.do");res.sendRedirect(req.getContextPath()+ "/toLogin.do");} else {//已登录,请求继续执行chain.doFilter(request, response);}}public void init(FilterConfig arg0) throws ServletException {}
}

2.src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"><display-name>netctoss</display-name><welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list><servlet><servlet-name>main</servlet-name><servlet-class>web.MainServlet</servlet-class></servlet><servlet-mapping><servlet-name>main</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping><!-- 配置错误页面:当tomcat捕获到这种错误时会自动转发到对面。转发时若写绝对路径,需省略项目名,因为tomcat会自动增加项目名。 --><error-page><error-code>404</error-code><!-- <location>/netctoss/WEB-INF/error.jsp</location>写绝对路径时,只有此处特别,不能写项目名 --><location>/WEB-INF/error.jsp</location></error-page><error-page><error-code>405</error-code><location>/WEB-INF/error.jsp</location></error-page><error-page><error-code>500</error-code><location>/WEB-INF/error.jsp</location></error-page><!-- 登录检查过滤器 --><filter><filter-name>login</filter-name><filter-class>web.LoginFilter</filter-class></filter><filter-mapping><filter-name>login</filter-name><url-pattern>*.do</url-pattern></filter-mapping>
</web-app>

面向对象接口设计理念之多态应用:JavaEE底层API设计HttpServletRequest接口

  • 关于重写父类Filter当中doFilter方法中的ServletRequest和ServletResponse,这个两个参数,为什么可以直接转型为HttpServletRequest和HttpServletResponse呢,那么你要想了解这个话题呢,你得知道一下,这个怎么说呢,javaEE,它底层的一些API的这个设计,但这个设计啊,我不会说的这个太多,太这个,太全啊,否则不好理解,然后呢,我们就说一小部分,你把这部分理解了,这个问题就能解决啊,那当前我们所学的是java的javaEE这部分,javaEE啊,是java的企业级开发的这部分,我画的这个框,它代表了我这个所学的javaEE这部分内容。
  • 那么,javaEE啊,这部分内容,当然是由Sun这个设计的,Sun提供的,但是呢,Sun,它很多地方,都只设计了接口,并没有实现,为啥没有实现呢,因为它实现不起,因为你想啊,市场上,有很多服务器,有tomcat,有JBoss,有WebLogic,WebSphere,对吧 ,等等,是这样吧,有那多服务器厂商,Sun怎么去实现,没法提供实现类,这个逻辑呢,和这个jdbc一样,其实Sun呢,在jdbc的时候,也是这么玩的,那它想访问数据库,它只提供接口对吧,谁实现呢,数据库厂商啊。
  • 那么Sun呢,想提供一项技术,想这个让浏览器访问服务器,想支持这个http协议,那么它怎么办呢,它也是,只提供接口,实现类由服务器提供明白吧,也是这个套路啊,那么在这个javaEE当中呢,Sun提供了很多接口,比如说,我们平时所学的那个HttpServletRequest,就是个接口,其实它是接口,它不是一个类,你不信,你可以进到那个,你可以搜一下那个,进去看一下,它是个接口啊,叫HttpServletRequest,当然了,它的兄弟,Response也是接口明白吧,那我说这个request,response同样道理啊,我就那个也不说了,说这个,整明白就可以了,这是个接口,这样,我把这个 接口啊,就是标一下,我画个方块吧,这个是接口啊,接口是Interface啊,I,就i吧,大写的I,看不清楚啊,小写的i,表示说呢,这HttpServletRequest是个接口。
  • 而且呢,这个接口HttpServletRequest,还有父接口啊,它的父接口是,就叫ServletRequest,这也是接口,这两个接口呢,是一个继承关系,继承。这个接口可以继承于接口吧,行不行,可以啊,继承于接口。那有人可能体会不了,这为什么这么麻烦呢,为什么还要继承呢,主要是Sun是这样想的,咱们当前呢,这个http协议比较流行,大家都用这个,那将来有一天,有没有可能这个协议,不好用了,不用了呢,有个更好的协议呢,有没有可能呢,有可能,那如果说呢,它把这个代码都放到一个接口里,如果说将来要换的话,这接口就废了对吧,重写一个,那它呢,是怎么招呢,是把和这个协议http相关的代码,放到HttpServletRequest这个接口里,无关的放到ServletRequest里来,明白吧。
  • 那将来的话,万一说,http这个协议不好用了,有了一个新协议,那么把这个子接口HttpServletRequest去掉,再加一个子接口,而父接口还能用明白吧,不至于说我所有的代码,都得推翻重写,明白这意思吧,它是为了这样的考虑,说白了,还是为了解偶啊。总之,这是它的设计的一个理念,这个理念,经证明是非常的合理的。那么我们在没有做出设计的时候,我们可能对这个理念呢,有所排斥,或者说,有些地方,想的不那么透彻,这也很正常,你也不用纠结啊。等你工作个3年以上,你也作为高级程序员,或者是设计,你做出一些个设计的时候,你自然而言会去想借鉴人家的这个想法,你自然而然会接受这个思想,你那个时候,你不想接受,都会接受了啊,一定会接受的,所以说,这个需要时间的一个打磨,你慢慢就会,接受度会越来越高的。
  • 总之呢,现在的话,就尽量去体会,尽量去理解吧啊,总之啊,这个Sun,它声明了,它规定了这两种接口,互相是一个继承关系,然后呢,它没有提供实现类,这个实现类,由谁提供呢,由tomcat提供啊,这实现类呢,由tomcat提供,我在下面呢,把这个tomcat啊,也给它画出来,再画一个方块啊,代表tomcat,那你注意,javaEE是一项技术,tomcat是个服务器,这两者不是等价关系,明白吧,是一个相互依赖的关系,那javaEE需要运行在服务器里对吧,服务器它能运行javaEE,这样的代码啊,是一个互相依赖的关系啊,我后面写的,这个是tomcat啊,服务器。
  • 那么服务器呢,想运行这个程序,它需要提供这个实现类,它也提供了啊,那它提供的这个接口的实现类叫什么呢,它就叫做,RequestFacade,那么tomcat给我们提供的这个实现类,这是实现类啊,叫RequestFacade,这个类它实现了这个HttpServletRequest,这个接口啊,实现了,这是一个类啊,类呢,我标上c,行吧,c是class啊,然后呢,这是一个实现啊。好了,就这样一个结构,那么你想一想啊,那这个request也好,response也好,最终由谁去调用呢,是Servlet是吧,Servlet需要它们,所以这个对象RequestFacade,最终要传给Servlet,最终要传过去,那传的话,你想一想,tomcat会传谁呢,就javaEE,它当然了,它也规定了什么呢,它也规定了,它也有这个,规定了什么呢,Servlet,就是我们所写的Servlet,是我们自己写的,我们写的Servlet,运行在tomcat之内啊。
  • 比如说,咱们之前写的这个,MainServlet,别写Main吧,就写Filter吧,咱们刚才说的是Filter啊,说串了,我们写的是LoginServlet,那这个LoginFilter,它是由tomcat调用的是吧,由它来执行,它来调用的, 然后我们调的是doFilter()这个方法,LoginFilter.doFilter()这个方法,那么这个方法,它声明的参数是什么参数呢,它声明的是不是这个ServletRequest啊,对吧,它声明的参数是ServletRequest,LoginFilter.doFilter(ServletRequest req),这样,说白了,它声明的是这个类型ServletReequest,它声明的是这个类型,那你想啊,tomcat传参的时候,它能不能直接传,它提供的这个RequestFacade实现类进去呢,这样传可不可以呢,可以吧,行不行,很多人就含糊啊,不是很确定啊,说可以的话,也不是很那个确信啊,就是什么呢,关于多态这件事,关于继承啊,实现呢,多态啊,这些事,大家理解的还是不够透彻啊,这就叫多态啊。
  • 那么,我这个方法LoginFilter.doFilter(ServletRequest req),声明的是接口,或者声明的是父类,你传入实现类,传入子类是允许的对吧,是可以的,这是多态么,我们这样说啊,说这个request这个关系,大家感觉就是有点晕了啊,如果我们换个角度去说的话,你很快就明白了,咱们做个,还是做个类比吧,有同学你不给它类比啊,他想不明白啊,你比如说,咱们现实中啊,说个现实中的事,有同学老说啊,说老师这个多态我懂,其实你不懂,为啥呢,我们讲多态的时候一说啊,人,老师,苍老师,这你就懂了,当我们到这个真实的业务当中说request,你就不懂了,那你是懂还是不懂,还是不懂,你的懂只局限在,那个实际的生活场景当中。
  • 那我们做个对比啊,想办法让你懂吧。你看啊,这个人,我可以理解为它是个接口么,可以吧,那这个ServletRequest,就好像是人这个接口一样啊,我可以把它类比成人这个接口,然后,这个老师,我可以把它理解为一个接口么,可以啊,我们就做个类比说,HttpServletRequest它是老师这个接口,假设啊,ServletRequest,它是人这个接口,HttpServletRequest,它是老师这个接口,它俩之间继承,可以么,老师继承于人,没问题吧,这你就好理解了,这个实现类RequestFacade,做个类比,它是具体的一个实例,具体的,它是一个实现啊,就不是抽象啊,javaEE的ServletRequest和HttpServletRequest,这是抽象啊,Tomcat中的RequestFacade,它就具体了,比如说它是谁呢,苍老师,非常具体了,好了,你看,人这个接口是顶层接口,老师是第2层接口,苍老师是实现了老师,间接实现人,能理解这意思吧,能理解吧。
  • 那这3个,ServletRequest,HttpServletRequest和RequestFacade,这3个之间的关系,接口和类,它们的关系就好像这个人,老师,苍老师,这个关系类似,那你想啊,我这LoginFilter.doFilter(ServletRequest req),这声明的参数是什么呢,是人,tomcat实际传入的是苍老师,有问题没有,苍老师是不是个人,符不符合这个规则,符合,那我把苍老师,传进来的是苍老师,苍老师在这站着,我去引用苍老师,我说这个人,太缺德了,这个人说的有问题么,我用这个人指代他,可以吧,或者我说,这个老师,怎么怎么样,可以吧,我说苍老师怎么怎么样,可以吧,他就站在这,LoginFilter.doFilter(ServletRequest req...),我用人,老师,苍老师,去指代他,都行,能理解么,因为你传入的是一个具体,我用什么去代替他,都是可以的,能理解么,多态啊。
  • 有人又不耐烦了,说能,你看,刚才我们说这个,不说这个人,老师,苍老师的话,你为什么不说能呢,是吧,一说这个你就说能,说那个就不能啊,这就是问题啊,就是,就怕你不理解,好了,同理啊,就是生活中,我们到处都有这样的例子啊,比如我说,这个东西,东西是个接口,我说这个设备,设备是个接口,我说这个鼠标,鼠标是个具体,鼠标实现了设备接口,设备继承于东西接口,能这么理解么,可以这么理解吧,东西是顶层接口,设备是第2层接口,鼠标实现了设备这个接口,我声明了方法,要求传入东西,我传入的是鼠标,可以吧,当我提这个东西的时候,当我说这个这个玩意的时候啊,我说这个东西,这个设备,这个鼠标,都没有问题对吧,所以你看啊,咱们在这个程序当中,传入的是一个具体的实例,那么,我把它往上转,我说它是这个,我把它ServletRequest,LoginFilter.doFilter(ServletRequest req...)中的这个ServletRequest这个接口,转为这个接口HttpServletRequest,也可以对吧,因为你传入的是那个具体的那个实现类么(传入的是RequestFacade实现类),我往上转没有问题。
  • 再一个啊,你看我后面调chain.doFilter(req, res),咱们之前的案例啊,我们传的,都是传谁呢,传ServletRequest这种类型的参数,而我这回写的,传的都是这种HttpServletRequest,这个类型的参数对吧。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {//将参数转型HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse res = (HttpServletResponse) response;//有几个请求不需要过滤,将它们排除String[] paths = new String[] {"/toLogin.do","/login.do","/createImg.do"};String current = req.getServletPath();for(String p : paths) {if(p.equals(current)) {chain.doFilter(req, res);return;}}//从session中获取账号HttpSession session = req.getSession();Object user = session.getAttribute("user");//判断用户是否登录if(user == null) {//没登录,重定向到登录页res.sendRedirect(req.getContextPath()+ "/toLogin.do");} else {//已登录,请求继续执行chain.doFilter(request, response);}
}

  • 有问题么,其实还是没有问题,为啥呢,因为那个方法chain.doFilter声明的为,即LoginFilter.doFilter(ServletRequest req...),这个doFilter方法的参数,还是这个人,那我传的话,传的其实还是苍老师,也没问题对吧,但是,我把苍老师当作老师传进去,也没问题啊,就是,你说这个苍老师是老师,是人,怎么指代都可以,是这样的。尽量去体会吧,总之啊,这就是多态啊,那多态的意义是为了省事,为什么会省事呢,因为我这个方法声明的是顶层接口,你这个tomcat,給我传了一个RequestFacade,能满足要求,那如果我换了个JBoss服务器,它不叫RequestFacade,它传的是Request2,行不行,也可以,那我这个LoginFilter.doFilter(ServletRequest req...),这个段代码是通用的对吧,那你服务器给我传什么都行,是这样吧。
  • 然后,那为什么这个参数不声明这个HttpServletRequest这个参数呢,它为啥声明是"人"呢,就最顶层这个人呢,它咋不声明是老师呢,因为万一将来有一天,http这个协议,不行了,换了个协议,你这要写这个HttpServletRequest的话,LoginFilter.doFilter(HttpServletRequest req...)这个方法的参数,要写的是HttpServletRequest的话,是不是doFilter这块这个方法,这个代码就得改啊,如我写的是顶层接口的话,LoginFilter.doFilter(ServletRequest req...),这个代码就不用改,是这意思么,能理解么,这是为了解耦,这是为了这个多态,其实很多时候,是为了解耦啊。
  • 所以,其实啊,我们很多内容,最终万变不离其宗,是为了干什么呢,是为了程序的设计,是为了让程序的耦合度更低明白吧,是为了更好的扩展性,其实都是这么一个话题,都是这么一个主题。那么,我们学到任何一个地方,这个东西越抽象,越复杂,越不好理解,其实它,为什么它会这样呢,它这么设计的目的,就是为了解耦明白吧,那我们用这项技术用的爽了,那这项技术背后的开发者,设计者,他们很麻烦,而我们在学习它们的这个设计时很麻烦,明白么,学习时麻烦,但你学会了,用的时候就方便了,所以是这样的。
  • 那作为一个程序员吧,那你就得学,那也没办法啊,你要是不懂原理的话,那这个学的太片面,这也不行啊,所以就得这么学啊。好了,这个呢,就是再去想一想,再去复习一下多态,就这样。我把这个图呢,写到这个笔记里啊,那个图是什么来着,是这参数啊,就是参数说明。那目前呢,咱们这个filter啊,也就讲完了,讲完以后,还有最后一个话题啊,最后一个话题是什么呢,叫监听器啊,这个监听器啊,我们工作时用的比较少啊,很少用,然后的话呢,大家了解一下就行了,那万一说,你工作时有一个特殊情况,可能需要用到的话,咱们也讲过,你翻翻笔记,看到的话,哎,好像这能解决问题啊,你用一下试试,能解决就ok了,就总之了解一下啊。

监听器

  • 然后呢,监听器呢,它怎么说呢,这个概念听起来比较抽象,但事实上呢,它用起来倒是比较容易啊,所以我们还是得花很多功夫呢,解释这件事,什么叫监听器啊,那我们从下面几个角度来阐述这个监听器啊,这是我们的第5个话题,监听器,咱们上面那个叫过滤器对吧,这名字不一样啊,就是咱们这个编程领域啊,有一些,其实说白了,就是个对象,就是个接口,但是呢,它命名时搞的很高大上,很专业的感觉,明白吧,取名比较这个高大上啊,我们要理解它这个名字背后的意义,意思啊。那第一个话题,我们解释一下,什么是监听器,然后呢,那第2个话题,我们说一下,那监听器呢,有几种,几类啊,分类,那这些都了解了以后,我们再演示一下监听器怎么用啊,说一下,它的这个开发步骤,从这个3方面来阐述。

什么是监听器

  • 那第一个啊,什么是监听器,监听器英文叫Listener,就监视,监视器,监视你啊,这个东西啊,其实不止是这有,咱们那个java的Spring里也有监听器,其实JS里也有监听器,只不过呢,这东西平时不用去,不用去管它,是自动的,所以之前呢,一些内容里没讲,这我就提到,那什么是监听器呢,就是它用,它能够监视对象的变化,监听器用来监听对象的变化,或者说,它用来呢,监听某种事件,写上,监听器用来监听事件,当某事件发生时,它会通知服务器,服务器可以做进一步处理,这个意思,那它监听或者监视的目标是谁呢,是一些对象啊,它监视的目标是一些对象,监视这个对象的变化,是这样的。
  • 其实,你像我们js,不是学过事件么,对吧,是吧,什么onclick啊,什么onblur啊,等等,那为什么我一点按钮,这个就会触发单击事件呢,其实浏览器在加载网页时,给每一个元素都创建了一个监听器,这个监听器就监视这个元素,哎,一旦有人动了这个元素,它立刻告诉浏览器,浏览器就调那个方法,明白吧,就触发了那个事件,所以,凭什么我们点按钮,能够触发事件呢,是监听器在监视这个网页,那我们现在所说的监听器呢,不是监视网页,它是监视对象,那监视哪个对象呢,我们看监听器的分类,这个javaEE中的监听器啊,它只能监视3个对象,第一个对象是request,第2个对象是,不是response啊,第2个对象是session,第3个对象是context啊,因为呢,这3个对象,它都能存变量,config不能存变量对吧,是吧,它不能存变量,它的数据没什么变化,request,session和context,都能存变量,它的数据会有所变化,所以需要监视。
  • 那为啥需要监视它们呢,因为啊,这3个对象,request,session和context,那个类是我们写的么,不是,它是我们new的么,不是,是我们调的么,或者说它是我们传给Servlet的么,不是,就基本上这个对象,它的管理都不是由我们做的对吧,它什么时候出生的,什么时候死的,对吧,什么时候数据变化的,我们一概不知道对吧,不知道,那万一我们工作时有这样一个要求说,请求开始,或者说请求一开始,我就要怎么怎么样,那什么叫请求开始呢,就是那你创建request时,请求开始对吧,可以这么理解,那我们想监视request被创建,怎么监视,得用监听器。比如说,咱们讲这个session,是我们一访问服务器,服务器自动创建,对吧,那比如说我有一个要求,只要你一创建session,我们就怎么怎么样,怎么办,我怎么知道这个session什么时候创建,对吧,得用监听器监视明白吧,就总之啊,这些对象,不是由我们管理,是由tomcat自动管理,但如果我们真的想知道它这个,什么时候发生了变化,发生什么变化,我们需要在这些变化发生时,做一些处理怎么办,用监听器。

监听器的分类

  • 总之,监听器可以帮我们监视它,是这个意思,那监听器啊,可以监视这3个对象,request,session和context,然后呢,每一个对象,有两种监听器,监听它的不同的方面啊,你像这个request,有个监听器叫,HttpServletRequestListener,这单词比较长啊,我看一下我写没写错啊,单词太长了啊,啊,写错了,是ServletRequestListener呢,把它写对一下啊,这前面没有前缀啊。还有一个Listener是,ServletRequestAttributeListener,巨长啊,那通过名字,能看出它的意思啊,前者是监视什么呢,监视request创建与销毁,后者是监视request的这个数据的变化。
  • 就总之啊,你想知道,request什么时候创建,什么时候销毁,用这个ServletRequestListener来监视明白吧,你想知道request数据,什么时候增加,什么时候删除,用这个ServletRequestAttributeListener来监视啊,就是说,如果啊,我们把request比做一个人的话,这个Listener,是用来专门监视这个人的,明白吧,ServletRequestListener,它监视这个人的起居,ServletRequestAttributeListener,它监视这个人的外出,是这样的,反正这两个,这两个刺客啊,专门监视这个人啊,监视它的不同的方面,你可以这么理解。写一下啊,前者是监视这个request的创建与销毁,后者是视request的数据的变化。那同样道理,也有两个监听器,监视session,创建与销毁,数据的变化啊,那么,session的创建与销毁的监听器叫,HttpSessionListener,然后呢,监听它数据变化的监听器是,HttpSessionAttributeListener。我看看我写没写错啊,这我有可能记串单词,没错啊,再来,那同理,这个context,也有俩监听器监视它,也是两个方面,创建与销毁,那么它是叫ServletContextListener,然后监视它数据的变化,那个监听器叫,ServletContextAttributeListener,哎呀,反正挺长。
  • 那这个,你大概这个逻辑,记住就可以了,你也不用去,不用去死记硬背,这个比较长,你要想知道这个接口的名字,这个类型的话,你去搜对吧,去看API,一看就知道了,总之呢,就是说,javaEE,它有怎么说呢,有监听器,一共有6个,能监视3个对象,明白吧 ,分别监视呢,对象的创建与销毁,数据的变化,监视这两个方面。那你想啊,比如说我,我以这个request为例给你演示一下,怎么去监视它创建与销毁,怎么去监视它数据的变化,如果这个你会了的话,这俩监听器会的话,其他的那两个session和context,是不是和那个request一样啊,这是不是跟它一样啊,是这样吧,雷同的,这就好像事件一样,我们学了单击事件对吧,再学一个事件,一样的啊,所以,我只是给你演示,request的这两个监听器,我们去体验一下,然后呢,其他的,我们就不演示了,大同小异啊。

监听器的使用

  • 那么,监听器啊,咱们只是一个基本的语法的演示,我们在项目中没有地方用,我也没有去杜撰一个功能,这杜撰不出来,没有,那就算了,反正你就了解一下,工作时万一说啊,这个request要创建时怎么怎么样,你就考虑监听器就可以了,一般可能会没有,比较少这样的需求。那下面呢,我们说一下,这个监听器怎么开发啊,它的开发步骤,那么监听器开发的第一步是这个,也是写一个类啊,创建一个类,这个类啊,要实现什么呢,监听器接口,那你注意,我们这里所写的这6种监听器,什么什么Listener,都是接口明白吧,我们要实现这个接口,你想监视什么,就实现对应接口,能理解吧,什么什么Listener,这都是接口啊,我在这写清楚啊,是接口,创建一个类,实现对应的监听器接口,然后呢,这个监听器要想工作啊,监听器呢,是把这个事告诉tomcat,由tomcat调用程序,那么,要想工作,我们需要配置啊,需要在,需要加以配置啊,所以我们需要在web.xml中,这个声明此类,就两步。
  • 那下面我们写一下。我们就在这个jsp6当中去演示啊,那打开你的开发工具,eclipse,我们打开呢,jsp6这个项目啊,然后呢,我在web这个包下创建一个类,一般呢,什么监听器,就叫什么什么Listener,我这里没有业务,那我取名叫什么呢,我取名叫MyListener,我的监听器啊,随便写点啥。然后呢,我现在呢,想先演示哪个呢,这个,先演示ServletRequestListener,我想呢,监视request的创建与销毁啊,所以呢,我们就实现这个接口,ServletRequestListener,搜一下,看一看,javax.servlet包下的,对,实现这个接口啊。
  • 类名叫MyListener,实现的接口叫ServletRequestListener,我搜了ServletRequest,直接出来了,对吧。选中这个接口ServletRequestListener,别选错了啊,选中它,然后呢,完成。
  • 完成以后你看,我就有了一个类啊,MyListener,实现了这个接口ServletRequestListener,这个接口规定了两个方法,那这个方法叫requestDestroyed,那个方法叫requestInitialized,那你能看明白它的意思么,那很显然,requestDestroyed这个方法是在request销毁时调的对吧,是tomcat自动调的,requestInitialized的这个方法呢,是初始化时调的明白么,那我们呢,分别在这两句话之间,在这个两个方法里啊,分别输出一句话,来看一下,它的调用的时机啊,request销毁和request创建,然后呢,你仔细观察会发现呢,这个方法是有参数的啊,参数还都一样,你看这个参数叫什么呢,叫event对吧,事件,ServletRequestEvent,那么监听器,监听到的是一件事的发生,监听到的是个事件,它会创建一个事件对象传进来,那么,通过事件对象,我们得到呢,一些事件相关的内容。

public void requestDestroyed(ServletRequestEvent arg0) {System.out.println("request销毁");
}
public void requestInitialized(ServletRequestEvent e) {System.out.println("request创建");System.out.println(e.getServletRequest());
}

  • 那这个对象,ServletRequestEvent,我们也演示一下吧,我在创建时演示啊,我把变量名呢,改一下,改成叫e吧,简单点,然后呢,在这个public void requestInitialized(ServletRequestEvent e) {...},方法内部,我想呢,输出e的一些内容啊,看一下,通过e啊,我们能得到什么呢,能得到ServletContext,能得到当前监听的那个request,那个目标ServletRequest,明白吧,还能得到那个Source,事件源啊。
  • 那这样,我们输出这个request,很多时候我们要有业务的话,估计也是要对request做处理,因为你监视的就是它么,那我就输出这个request,看一下啊,System.out.println(e.getServletRequest());,好,这个就这样了。那这个类写完以后啊,我们需要呢,进行配置啊,那下面呢,我们再打开这个配置文件,在web.xml当中,我们来配置监听器,监听器的配置方式,要比filter,要比Servlet都要简点一些啊,它是这样写,listener,listener-classs,然后里面写上类名,就可以了。

<listener><listener-class>web.MyListener</listener-class>
</listener>

  • 这就完了啊,那么,那个监听创建与销毁的那个,那个方法里啊,如果你有业务的话,先写那业务就可以了啊,我这里没有业务,就一句话代替,那咱们试一下,我们把这个项目呢,重新部署一下。然后呢,启动tomcat,启动以后啊,我们去访问这个项目中的功能啊,增加资费,删除资费,那在增加资费,删除资费的时候,你想一想,每当我发请求时,request就创建对吧,请求结束时就销毁,对吧,就你访问的时候,就应该有创建与销毁的行为,咱们试一下。那这个项目名,叫jsp6,我访问addCost,增加资费,localhost:8080/jsp6/addCost,然后呢,回车啊,然后呢,看控制台,看一下,你看啊。
  • 你看,是不是在一开始request就创建,最后销毁啊,那每次请求都这样,所以以前呢,我讲这个Servlet原理,jsp原理时也说过啊,咱们请求一开始,request创建对吧,然后结束时销毁,然后呢,可能你体会的不深,现在通过监听器,很直观能看到,对吧,确实是这样啊。那再来,我们再访问删除,你看删除的话,也会这样么,localhost:8080/jsp6/delCost,看一下啊,是不是也一样啊,一开始创建,最后销毁,都是这样啊,然后呢,你看输出的这个request是什么呢,是org.apache.catalina.connector.Request@707fc7a9,对吧,是吧,这个玩应就是tomcat提供的那个东西,Request对象,就这个org.apache.catalina.connector.Request@707fc7a9。这就是,这个监视request的创建与销毁的监听器。
  • 那下面呢,我们再演示另外一个监听器啊,也是监视request啊,我想呢,监视request中数据的变化,什么时候增加数据啊,什么时候移除数据啊,监视一下,那我们需要呢,实现那个接口,那按理来说,我也需要再写一个类,实现这个接口,但是你想啊,我不想写一个新的类了,我就让MyListener,再实现这个接口ServletRequestAttributeListener,可不可以呢,它可以实现多个接口,对吧,所以,我打开MyListener啊,给它再加一个接口,这有点长啊,你照着我抄吧,叫ServletRequestAttributeListener,那我们要实现这个接口的话,还得添加它的方法对吧,你点前面这个叉,把这个方法呢,加进来,add unimplemented methods,加进来以后,有几个方法呢,3个啊,这个方法一看就知道了,attributeAdded,就添加数据对吧,还有Removed,移除数据对吧,还有呢,Replaced这个是修改数据啊,我不都演示了,我们就演示一个add吧,增加数据,可以吧,我们监视一下,它数据什么时候增加的,这里呢,输出一句话,输出什么呢,向request中添加数据,

public void attributeAdded(ServletRequestAttributeEvent arg0) {System.out.println("向request中添加数据");
}

  • 这句话写完以后,还不够,那你想啊,我们之前的那个,增加资费,删除资费,咱们往那个request中加数据了么,没有,所以监视不到对吧,咱们还得加点数据啊,所以我们再打开那个,AddCostServlet,你打开AddCostServlet.java,那在这里啊,我就往这个request里面呢,加点数据啊,随便加点啥,都行啊,request点setAttribute,加点什么呢,user啊,行吧,那你看,一个说user吧,一个说还user,那我到底写啥呢,那就zhangsan吧,什么都行啊,req.setAttribute("user", "zhangsan");

@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {req.setAttribute("user", "zhangsan");System.out.println("增加资费");
}

  • 那么这句话写完,咱们可以测试啊,把这个项目呢,再重新部署一下,然后呢,启动tomcat,然后呢,我们去访问资费增加的功能,addCost,即localhost:8080/jsp6/addCost,然后呢,你看一下,控制台,你看是不是在增加资费之前,输出了这句话来啊,向request中添加了数据,监听到了。
  • 就总而言之吧,监听器能够监视对象,3个对象,Request,Session,Context啊,监视呢,对象的两种情况,分别是创建与销毁数据的变化。如果工作时,有需求说,对象怎么怎么样时,你要做出什么处理,你可以用监听器处理啊。那这监听器啊,我们就说完了,它总之呢,比较简单,比较容易啊,那么到这啊,咱们这个javaEE啊,这段内容就说完了啊,那么这段内容当中的,主要的重点是什么呢,是Servlet,jsp,以及呢,在MVC模式下,这两者怎么相互配合使用,还有呢,jsp上我们所写的el表达式和jstl标签,再者就是我们所讲的netctoss项目,在项目里,我们怎么去利用这些,这些技术解决问题。
  • 那么,后面讲的cookie啊,session啊,filtrer,listener,是对这个javaEE的一个扩充,往往呢,处理的是项目中一些啊,这个高级的这个部分,那么,这些内容,可能作为初学者一开始不会让你写,但是你要知道,怕面试时问到,然后,将来你这个能力提高了,你要用的时候,也有一个参考。总之啊,我们这段课程就讲完了,那么,后续的内容,还有框架,Spring,Mybatis,还有ajax,等等,然后呢,框架的话,你会发现,这框架肯定比我们这个内容还是要难的。

监听器模拟案例jsp6之代码实现

1.jsp6/src/main/java/web/MyListener.java

package web;import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;public class MyListener implements ServletRequestListener,ServletRequestAttributeListener{public void requestDestroyed(ServletRequestEvent arg0) {System.out.println("request销毁");}public void requestInitialized(ServletRequestEvent e) {System.out.println("request创建");System.out.println(e.getServletRequest());}public void attributeAdded(ServletRequestAttributeEvent arg0) {System.out.println("向request中添加数据");}public void attributeRemoved(ServletRequestAttributeEvent arg0) {// TODO Auto-generated method stub}public void attributeReplaced(ServletRequestAttributeEvent arg0) {// TODO Auto-generated method stub }
}

2.jsp6/src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"><display-name>jsp6</display-name><welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list><servlet><servlet-name>addCost</servlet-name><servlet-class>web.AddCostServlet</servlet-class></servlet><servlet-mapping><servlet-name>addCost</servlet-name><url-pattern>/addCost</url-pattern></servlet-mapping><servlet><servlet-name>delCost</servlet-name><servlet-class>web.DelCostServlet</servlet-class></servlet>  <servlet-mapping><servlet-name>delCost</servlet-name><url-pattern>/delCost</url-pattern></servlet-mapping><filter><filter-name>log</filter-name><filter-class>web.LogFilter</filter-class></filter><filter-mapping><filter-name>log</filter-name><url-pattern>/*</url-pattern></filter-mapping><filter><filter-name>guolv</filter-name><filter-class>web.GuolvFilter</filter-class><init-param><param-name>city</param-name><param-value>北京</param-value></init-param></filter><filter-mapping><filter-name>guolv</filter-name><url-pattern>/*</url-pattern></filter-mapping><listener><listener-class>web.MyListener</listener-class></listener>
</web-app>

3.jsp6/src/main/java/web/AddCostServlet.java

package web;import java.io.IOException;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class AddCostServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {req.setAttribute("user", "zhangsan");System.out.println("增加资费");}
}

写在后面

NETCTOSS代码实现第八版相关推荐

  1. NETCTOSS代码实现第五版

    NETCTOSS代码实现第五版:Cookie的使用场景与使用方式 前言 Cookie的使用场景与使用方式 模拟Cookie的使用场景 Cookie的创建,存储,获取与修改 Cookie的生存时间 小总 ...

  2. NETCTOSS代码实现第六版

    NETCTOSS代码实现第六版:Session的原理与应用 前言 Session的原理与应用 模拟Session应用案例需求分析:Session的运行原理 模拟Session应用案例代码分析:Sess ...

  3. OpenGL编程指南(第八版)第一个渲染三角形案例代码在win8双显卡电脑VS2015中运行方法总结

    弄了好几天才把第一个程序运行出来,用该博客记录一下参考的博客资料及问题: 写得很好的博客参考资料为: http://blog.csdn.net/IceTeaSet/article/details/50 ...

  4. NETCTOSS代码实现第一版

    NETCTOSS代码实现第一版:资费查询功能 前言 资费查询功能需求分析与程序设计 根据需求拆分请求 根据请求推导程序内部执行过程 根据执行过程进行代码设计与实现 项目公共业务层之JDBC封装工具类: ...

  5. 计算机网络(谢希仁第八版)第一章:概述

    1.计算机网络在信息时代的作用 三网:电信网络,有线电视网络,计算机网络. 三网融合:由于涉及多方面的利益和行政管辖权的问题,目前没有实现. Internet(译名:因特网,目前使用最广泛的译名为:互 ...

  6. 大学计算机实践教程中国工信出版社答案,中国石油大学出版社第八版计算机文化基础实验教程习题答案...

    中国石油大学出版社 第八版 计算机文化基础实验教程答案 第一章 一.单项选择题 1.A 2.A 3.A 4.A 5.C 6.D 7.C 8.B 9.C 10.B 11.B 12.D 13.A 14.C ...

  7. 关于在vs2013中配置opengl红宝书第八版环境

    转自 http://blog.csdn.net/qq821869798/article/details/45247241 本人刚开始学习opengl,买了一本opengl红宝书第八版, 第一个例子研究 ...

  8. 数据与计算机通信第八版pdf,数据与计算机通信(第八版)(英文版)

    <数据与计算机通信(第八版)(英文版)> PART ONE OVERVIEW 1 Chapter 1 Data Communications, Data Networking, and t ...

  9. 编写代码的「八荣八耻」- 以用户易用为荣,以复杂歧义为耻

    概述 本文是继<编写代码的「八荣八耻」(上篇)>和<编写代码的「八荣八耻」-以开关上线为荣,以自信编码为耻 >之后,编写代码的「八荣八耻」系列的第三篇. 本篇整体框架还是采用经 ...

最新文章

  1. java synchronized关键字
  2. 16、ASP.NET MVC入门到精通——MVC过滤器
  3. MyBatisPlus3.x代码生成器自定义模板配置
  4. 使用Xcode 7 beta免费真机调试iOS应用程序
  5. 盘点那些让程序员目瞪口呆的Bug都有什么?
  6. mysql 非空语法_mysql从入门到优化(1)基本操作上
  7. 3650m5设置u盘启动_系统重装必备神器,U盘如何制作启动盘?
  8. Java ArrayList contains()方法及示例
  9. pycharm导入自己写的模块时,模块下方出现红色波浪线的解决方案
  10. Lazyload 延迟加载效果
  11. Android开发技术周报 Issue#9
  12. 会看源码,你将比98.3%的人更容易拿到百度、阿里、腾讯、字节跳动等大公司的高薪Offer!请看这几个公众号!...
  13. kell Vision5有那些使用技巧呢
  14. Excel函数实战技巧精粹(二)常用函数之VLOOKUP全解
  15. 【ROM制作工具】线刷包转卡刷包制作教程
  16. JAVA GUI创作简易记牌器
  17. 推荐一款Android高清平板应用--豆果美食
  18. 数据库中存储的是什么?数据库存取的是地址
  19. 鸿蒙宴中的人物故事,鸿门宴简介_鸿门宴的主要人物和背景_鸿门宴的目的和结局是什么? - 趣历史...
  20. zabbix邮箱告警

热门文章

  1. 计算机除尘 注意事项,数码相机除尘注意事项
  2. 首饰logo创意欣赏 - 设计商标 - logo公司
  3. 【智能路由器】openwrt创建用户软件包
  4. 软件学院研究方向,对研究方向迷茫的同学一定要看(据说是一位北大软院前辈写的)
  5. 核心能力:产品认知能力
  6. Android 引入高德3D地图 显示白屏或黑屏解决办法
  7. 围观新版香港身份证与OCR识别完美搭配
  8. 反编译华为U8825Dldquo;framework-res.apkrdquo;出现的错误提示(2)
  9. 利用Python输出九九乘法表
  10. 期货反向跟单--“盘手”转化为“散户”的战斗