listener

简述

最初EE规范里面其实只有Servlet,后面发展到某一版本时,又引入了两个组件:一个叫做Listener(监听器);一个叫做Filter(过滤器、拦截器,黑名单)。
监听器。即监听某个对象的创建和销毁等。

现实生活
监听者:朝阳人民群众
被监听者:明星艺人
触发事件:吸毒嫖娼
回调函数:报警

web中的监听器
监听者:自己去编写一个监听器
被监听者:ServletContext对象
触发事件:创建、销毁
回调函数:运行我们监听器里面对应的方法

简单使用

1.编写一个监听器(类实现ServletContextListener接口)
2.在服务器里面注册该监听器(注解、web.xml)

  1. package com.simon;
  2. import javax.servlet.ServletContextEvent;
  3. import javax.servlet.ServletContextListener;
  4. import javax.servlet.annotation.WebListener;
  5. @WebListener
  6. public class MyServletContextListener implements ServletContextListener {
  7. //当context对象被创建的时候,那么会调用我们这个方法
  8. @Override
  9. public void contextInitialized(ServletContextEvent servletContextEvent) {
  10. System.out.println("servlet init...");
  11. }
  12. //当context对象被销毁的时候,会调用这个方法
  13. @Override
  14. public void contextDestroyed(ServletContextEvent servletContextEvent) {
  15. System.out.println("servlet destory...");
  16. }
  17. }

使用场景

比如之前我们写在某个servlet中,init设置为自启动里面的代码其实可以放置在listener中。JavaWeb发展过程中是先有servlet————>listner、filter。Servlet之前在初始化逻辑写在某个servlet中,终究是不恰当的,为什么不写在其他servlet中?别人去找这个逻辑,他也不一定知道要在哪个servlet去找。如果把这个初始化的逻辑写在listener里面,比较恰当的,比如说应用的启动的时候获取配置文件
JDBC,listener来读取usename、password、url、driverClassName。

原理(监听者模式)

假定宝宝哭了,爸爸妈妈和爷爷奶奶分别在周末和工作日照顾宝宝。实现如下
image.png

  1. package com.simon.listenermodel;
  2. public interface Family {
  3. public void action();
  4. }
  1. package com.simon.listenermodel;
  2. public class Dad implements Family {
  3. @Override
  4. public void action() {
  5. System.out.println("Dad take care...");
  6. }
  7. }
  1. package com.simon.listenermodel;
  2. public class Mon implements Family {
  3. @Override
  4. public void action() {
  5. System.out.println("Mon take care...");
  6. }
  7. }
  1. package com.simon.listenermodel;
  2. public class Grandma implements Family {
  3. @Override
  4. public void action() {
  5. System.out.println("Grandma take care...");
  6. }
  7. }
  1. package com.simon.listenermodel;
  2. public class GrandPa implements Family {
  3. @Override
  4. public void action() {
  5. System.out.println("Grandpa take care...");
  6. }
  7. }
  1. package com.simon.listenermodel;
  2. public class Baby {
  3. public void babyCry(Family family){
  4. family.action();
  5. }
  6. }
  1. package com.simon.listenermodel;
  2. import org.junit.Test;
  3. //事实上,这就是监听者模式
  4. //几个对应:
  5. //Family对应ServletContextListener
  6. //Dad对应MyServletContextListener
  7. //Baby对应ServletContext
  8. public class BabyTest {
  9. @Test
  10. public void testCry(){
  11. Baby baby = new Baby();
  12. baby.babyCry(new Dad());
  13. baby.babyCry(new Mon());
  14. baby.babyCry(new Grandma());
  15. }
  16. }
  17. //Dad take care...
  18. //Mon take care...
  19. //Grandma take care...

Filter

简述

Filter就是请求进入Servlet之前必须经过的一层过滤器,也称拦截器。Filter位于服务器内,是一个web组件,位于Servlet之前;请求在到达servlet之前,如果能够经过filter,那么肯定会先经过filter,再进入servlet。Filter定为和城门一样的:请求到达Servlet之前,会先经过filter处理一步,处理一下request
请求返回给客户端之前,会再次经过filer一次,filter进行进一步处理response。

使用

1.编写一个类实现Filter接口
2.声明该Filter

  1. package com.simon;
  2. import javax.servlet.*;
  3. import javax.servlet.annotation.WebFilter;
  4. import java.io.IOException;
  5. @WebFilter("/servlet1")
  6. public class FirstFilter implements Filter {
  7. //会随着应用的启动而执行(和servlet是不一样,默认情况下第一次访问之前)
  8. @Override
  9. public void init(FilterConfig filterConfig) throws ServletException {
  10. System.out.println("init ...");
  11. }
  12. //类似于servlet的service方法
  13. @Override
  14. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
  15. FilterChain filterChain) throws IOException, ServletException {
  16. System.out.println("doFilter...");
  17. //通行令牌
  18. filterChain.doFilter(servletRequest,servletResponse);
  19. }
  20. //应用被卸载、服务器关闭
  21. @Override
  22. public void destroy() {
  23. System.out.println("destory...");
  24. }
  25. }
  1. package com.simon;
  2. import javax.servlet.*;
  3. import javax.servlet.annotation.WebFilter;
  4. import java.io.IOException;
  5. @WebFilter("/servlet1")
  6. public class SecondFilter implements Filter {
  7. @Override
  8. public void init(FilterConfig filterConfig) throws ServletException {
  9. System.out.println("second init ...");
  10. }
  11. @Override
  12. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
  13. FilterChain filterChain) throws IOException, ServletException {
  14. System.out.println("second doFilter...");
  15. }
  16. @Override
  17. public void destroy() {
  18. System.out.println("second destory...");
  19. }
  20. }
  1. package com.simon;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.annotation.WebServlet;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. @WebServlet("/servlet1")
  9. public class MyServlet extends HttpServlet {
  10. @Override
  11. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  12. throws ServletException, IOException {
  13. System.out.println("servlet...");
  14. }
  15. }

image.png

几点注意

  • 如何将filter和servlet产生关联呢?

如果filter和servlet产生的关联,那么应该是调用servlet之前会先调用filter
其实是通过url-patterne来关联的,最简单的关联方式其实就是将servlet的url-patteren直接赋值给filter即可。注意一点:
①filter设置和servlet相同的url-patteren是没有任何问题的。
②servlet和servlet之间不可以设置相同的url-pattern。

  • filter url-pattern的特殊之处

①filter和filter之间可以设置相同的url-pattern
②filter可以设置/
③filter还可以设置和serlvet相同的url-patternfilter url-pattern的写法和servlet是相同的, /xxxx 和
.xxxx两种

  • 为什么会有这种filter url-pattern行为呢?
    filter主要的功能用途是用来当做拦截器、过滤器;servlet主要功能出发 开发动态web资源—— 主要是做出响应的

  • 多个filter同时执行时先后顺序

1.如果是web.xml,调用的先后顺序和filter-mapping的先后顺序有关
2.如果是注解,那么调用的先后顺序和类名的首字母ASCII先后顺序有关

  • 为什么doFilter方法里面的日志只打印了一遍?Web组件 - 图3

    应用场景

    1.filter 设置 / * ,filter和servlet的一对多:编码格式,request、response
    2.过滤器、拦截器。 没有登录的情况下,访问我的订单页面,直接将你跳转到登录页面

    引入Filter之后完整的请求处理流程

    以访问http://localhost/app/servlet1http://localhost/app/form.html为例:
    image-20210414141924371.png
    1、浏览器生成一个HTTP请求报文,传到目的机器之后
    2、被监听着80端口号的Connector接收到,将请求报文解析成为Request对象,同时还提供一个response对象
    3、这两个对象被传给engine,engine挑选合适的host将对象进行进一步传递
    4、host去挑选一个叫做app的 Context,将这两个对象传给Context
    5、首先根据请求的有效路径 /servlet1,找有没有对应的filter可以处理该请求,如果有多个,那么按照他们的先后顺序形成一个链表,接下来看有没有servlet可以处理该请求,(如果没有,则会交给缺省servlet),将这个servlet也加入到链表中,在filter后面。
    6、依次去调用链表中的每一个组件,调用对应的方法,如果调用的是filter,则调用doFilter(request,response),如果调用的是servlet,调用的是service(request,response),全程传递的就是我们之前提供的request、response对象
    7、整个过程全部执行完毕,response里面写入了数据,Connector读取response里面的数据,然后生成响应报文
    8、响应报文再传回给客户端,客户端进行解析,渲染