zoukankan      html  css  js  c++  java
  • Filter&Listener

    Filter:过滤器

    概念:

    • 生活中的过滤器:净水器,空气净化器,土匪
    • web 中的过滤器,当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能.
    • 过滤器的作用:
      一般用于完成通用的操作.如:登陆验证,统一编码处理,敏感字符过滤...

    快速入门

    步骤

    1. 定义一个类,实现接口 filter
    2. 复写方法
    3. 配置拦截路径
      • web.xml
      • 注解
    @WebFilter("/*") //访问所有资源之前,都会执行该过滤器
    public class FilterDemo1 implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            System.out.println("doFilter");
            //是否放行 --放行
            chain.doFilter(request, response);
        }
        @Override
        public void destroy() {
        }
    }
    

    过滤器细节

    web.xml 配置

        <filter>
            <filter-name>demo2</filter-name>
            <filter-class>com.zhiyou100.filter.FilterDemo2</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>demo2</filter-name>
            <!--访问所有的资源都会执行 demo2的 filter-->
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    

    过滤器的执行流程

    1. 执行过滤器
    2. 执行放行后的资源
    3. 回来执行过滤器放行代码下边的代码

    过滤器声明周期方法

    1. init(FilterConfig); 在服务器启动后,会创建 filter对象,然后调用 init方法。只执行一次。用于加载资源

    2. doFilter(SerlvetRequest, ServletResponse, FilterChain); 每个请求进来的时候这个方法都会被调用,并在Servlet的service方法执行之前。而FilterChain就代表当前的整个请求链,所以通过调用FilterChain的doFilter方法可以继续将请求传递下去。如果想拦截这个请求,可以不调用FilterChain的doFilter,那么这个请求就返回了,所以Filter是一种责任链模式。执行多次、

    3. destroy(); 在服务器关闭后,filter对象被销毁,如果服务器时正常关闭,则会执行 destroy方法。只执行一次。用于释放资源

    • Filter类的核心还是doFilter中传递的FilterChain对象,这个对象保存了到最终的Servlet对象的所有Filter对象,这些对象都保存在ApplicationFilterChain对象的filter数组中。在FilterChain链上每执行一个Filter对象,数组的当前计数都会加1,直到计数等于数组的长度,当FilterChain上的所有Filter对象都执行完之后,就会执行最终的Servlet。所以在ApplicationFilterChain对象中会持有Servlet对象的引用。

    过滤器配置详解

    • 拦截路径配置:
      1. 具体资源路径:/index.jsp 只有访问 index.jsp资源时,过滤器才会被执行
      2. 拦截目录:/user/* 访问 /user下的所有资源时,过滤器都会被执行
      3. 后缀名拦截器: *.jsp 访问所有后缀名为 jsp资源时,过滤器都会被执行
      4. 拦截所有资源:/* 访问所有资源时,过滤器都会被执行
    • 拦截方式配置:资源被访问的方式
      • 注解配置:
        • 设置 dispatcherTypes 属性 (他是个数组)
          request:默认值.浏览器直接请去资源
          forward:转发访问资源
          include:包含访问资源
          error:错误跳转资源
          async:异步访问资源
      • web.xml 配置:
        在 file-mapping标签中配置
        <dispatcher></dispatcher>属性还是上面那五个

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

    • 执行顺序:如果有两个过滤器:过滤器1,和过滤器2
      过滤器1先执行
      过滤器2
      资源执行
      过滤器2
      过滤器1
    • 过滤器先后顺序问题
      1. 注解配置:按照类名的字符串比较规则比较,较小的先执行
        如:AFilter 和 BFilter,AFilter 先执行
      2. web.xml配置的:谁定义在上面,谁先执行

    案例

    登陆验证

    需求

    1. 访问 该项目的资源时,验证是否登陆
    2. 如果登陆了,则直接放行
    3. 如果没有登陆,则跳转到登陆页面,提示"你尚未登陆,请先登录"

    步骤

    1. 判断是否是登陆相关的资源
      是:直接放行
      不是:判断是否登陆
    2. 判断当前用户是否登陆,判断 Session中是否有 User
      有:已经登陆,放行
      没有,没有登陆,跳转到登陆页面
    @WebFilter("/*")
    public class FilterLogin implements Filter {
    
        @Override
        public void destroy() {
        }
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            //强制转换
            HttpServletRequest request = (HttpServletRequest) req;
            //1. 获取资源请求路径
            String uri = request.getRequestURI();
            //2. 判断是否包含登陆相关资源路径,要注意排除掉 css、js、图片、验证码等资源
            if (uri.contains("/login.jsp") || uri.contains("/loginServlet")
                    || uri.contains("/css/") || uri.contains("/js/")
                    || uri.contains("/fonts") || uri.contains("/checkServlet")) {
                //包含,用户就是先登录,放行
                chain.doFilter(req, resp);
            } else {
                // 不包含,需要验证用户是否登陆
                //3. 从session中获取 user
                Object user = request.getSession().getAttribute("user");
                if (user != null) {
                    //登陆了 放行
                    chain.doFilter(req, resp);
                } else {
                    //没有登陆,跳转登陆页面
                    request.setAttribute("login_msg", "你尚未登陆,请登录");
                    request.getRequestDispatcher("/login.jsp").forward(request, resp);
                }
            }
        }
    
        @Override
        public void init(FilterConfig config) throws ServletException {
        }
    }
    

    敏感词汇过滤

    需求

    1. 对项目录入的数据进行敏感词汇过滤
    2. 敏感词汇:笨蛋 坏蛋
    3. 如果是敏感词汇,替换 **

    步骤:

    1. filter 和 servlet 中的 request和response对象都是一样的
    2. 对 request对象的 getParameter方法进行增强.产生一个新的 request对象.
    3. 放行.将新的 request对象传入
      chain.doFilter(req,resp)
    4. 增强对象的功能:
      设计模式:一些通用的解决问题的方式
      • 装饰模式
      • 代理模式
        • 概念:
          真实对象:被代理的对象
          代理对象:
          代理模式:代理对象代理真实对象,达到增强真实对象功能的目的
        • 实现方式
          • 静态代理:

          • 动态代理
            实现步骤:

            1. 代理对象和真实对象实现相同的接口
            2. 代理对象 = newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h);
            3. 使用代理对象调用方法.
            4. 增强方法

            增强方法

            1. 增强参数列表
            2. 增强返回值类型
            3. 增强方法体执行逻辑
          public interface SaleComputer {
          	public String sale(double money);
          	public  void  show();
          }
          
          public class Lenovo implements SaleComputer {
          	@Override
          	public String sale(double money) {
          		System.out.println("花了" + money + "元,买了一台联想电脑");
          		return "联想电脑";
          	}
          
          	@Override
          	public void show() {
          		System.out.println("展示电脑");
          	}
          }
          
          public class ProxyTest {
          public static void main(String[] args) {
          	SaleComputer lenovo = new Lenovo();
          	System.out.println(lenovo.sale(800));
          	System.out.println("~~~~~~~~~~~~~~~~~~~~");
          	/*
          	 * Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
          	 * 三个参数:
          	 *  1. 类加载器,真实对象.getClass.getClassLoader()
          	 *  2. 接口数组:真实对象.getClass.getInterfaces()
          	 *  3. 处理器:new InvocationHandler() ==>重写 invoke(Object proxy,Method method,Object[] args)
          	 * */
          	SaleComputer proxyLenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader()
          			, lenovo.getClass().getInterfaces(), (proxy, method, arguments) -> {
          				/*
          				 * 代理逻辑编写的方法:代理对象调用的所有都会触发该方法执行
          				 * invoke(Object proxy,Method method,Object[] args)
          				 * 参数
          				 *  1. proxy 代理对象
          				 *  2. method 代理对象调用的方法,被封装为的对象
          				 *  3. args 代理对象调用方法时,传递的实际参数
          				 * */
          				//判断是否是 sale方法
          				if (method.getName().equals("sale")) {
          					//1. 增强参数
          					double money = (double) arguments[0];
          					money = money * 0.85;
          					System.out.println("专车接你");
          					//使用真实对象调用该方法
          					//增强返回值类型 obj==字符串
          					Object obj = method.invoke(lenovo, money);
          					System.out.println("免费送货");
          					return obj + "_鼠标垫";
          				}
          				//使用真实对象调用该方法
          				return method.invoke(lenovo, arguments);
          			});
          	System.out.println(proxyLenovo.sale(800));
          }
          }
          

    过滤器

    @WebFilter("/*")
    public class SensitiveWordsFilter implements Filter {
        //敏感词汇集合
        private List<String> list = new ArrayList<>();
    
        @Override
        public void init(FilterConfig config) throws ServletException {
            //1. 加载文件,->获取文件真实路径
            ServletContext servletContext = config.getServletContext();
            //敏感词汇.txt 在 idea中的是 src下的,但在 idea部署的是这个路径下
            String path = servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt");
            //2. 读取文件
            try (
                    BufferedReader bfr = new BufferedReader(new FileReader(path));
            ) {
                //3. 将文件的每一行添加到list中
    
                String line = null;
                while ((line = bfr.readLine()) != null) {
                    list.add(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            System.out.println(list);
    
        }
    
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            //1. 创建代理对象,增强 getParameter 方法
            ServletRequest proxyRequest = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(),
                    (proxy, method, args) -> {
                        //增强 getParameter方法
                        //判断是否是 getParameter方法
                        if (method.getName().equals("getParameter")) {
                            //增强返回值
                            //获取返回值
                            String value = (String) method.invoke(req, args);
                            //替换敏感词汇
                            if (value != null) {
                                for (String str : list) {
                                    if (str.contains(value)) {
                                        value = value.replaceAll(str, "**");
                                    }
                                }
                            }
    
                            return value;
                        }
                        return method.invoke(req, args);
                    });
            // 2. 放行
            chain.doFilter(proxyRequest, resp);
        }
    
        @Override
        public void destroy() {
        }
    }
    

    控制器

    @WebServlet("/TestServlet")
    public class TestServlet extends HttpServlet {
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String text = request.getParameter("text");
            System.out.println(text);
        }
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        }
    }
    

    Listener:监听器

    概念

    • web的三大组件之一,
      • 事件监听机制
        事件:一件事情
        事件源:事件发生的地方
        监听器:一个对象
        注册监听器:将事件,事件源,监听器绑定在一起,当事件源上发生某个事件后,执行监听器代码

    Listener的分类与使用

    1. ServletContext监听器

    • ServletContextListener:用于对Servlet整个上下文进行监听(创建,销毁)
      public interface ServletContextListener extends EventListener {
      	//上下文初始化
      	public default void contextInitialized(ServletContextEvent sce) {
      	}
      	//上下文销毁
      	public default void contextDestroyed(ServletContextEvent sce) {
      	}
      }
      
      • ServletContextEvent事件主要 api
        public class ServletRequestEvent extends java.util.EventObject
        public ServletRequest getServletRequest()
        public ServletContext getServletContext()
        
    • ServletContextAttributeListener,对Servlet上下文属性的监听(增删改查)
      public interface ServletContextAttributeListener extends EventListener {
      	//增加属性
      	public default void attributeAdded(ServletContextAttributeEvent scae) {
      	}
      	//属性删除
      	public default void attributeRemoved(ServletContextAttributeEvent scae) {
      	}
      	//属性替换(第二次设置同一个属性)
      	public default void attributeReplaced(ServletContextAttributeEvent scae) {
      	}
      }
      
      • ServletContextAttributeEvent 常用方法
        public String getName() 得到属性名称
        public Object getValue() 取得属性的值
        

    2. Session监听

    1. Session属于 http协议的内容,接口位于 javax.sevlet.http.*包下。
    2. HttpSessionListener 接口,对 Session的整体状态的监听。
      public interface HttpSessionListener extends EventListener {
      	//session创建
      	public default void sessionCreated(HttpSessionEvent se) {
      	}
      	//session 销毁
      	public default void sessionDestroyed(HttpSessionEvent se) {
      	}
      }
      
      • HttpSessionEvent
        public HttpSession getSession() 取得当前操作的 session
        
    3. HttpSessionAttributeListener 接口,对 session的属性监听
      public interface HttpSessionAttributeListener extends EventListener {
      	//增加属性
      	public default void attributeAdded(HttpSessionBindingEvent se) {
      	}
      	//删除属性
      	public default void attributeRemoved(HttpSessionBindingEvent se) {
      	}
      	//替换属性
      	public default void attributeReplaced(HttpSessionBindingEvent se) {
      	}
      }
      
      • HttpSessionBindingEvent
        public String getName();//取得属性的名称
        public Object getValue();//取得属性的值
        public HttpSession getSession();//取得当前的session
        
    4. session 的销毁有两种情况
      1. session超时,web.xml 配置
        <session-config>
        	<session-timeout>120</session-timeout><!--session120分钟后超时销毁-->
        </session-config>
        
      2. 手工使 session失效
        public void invalidate();//使session失效方法。session.invalidate();
        

    3. Request 监听

    1. ServletRequestListener,用于对 Request请求进行监听(创建、销毁)
      public interface ServletRequestListener extends EventListener {
      	// request 初始化
      	public default void requestDestroyed (ServletRequestEvent sre) {
      	}
      	// request 销毁 
      	public default void requestInitialized (ServletRequestEvent sre) {
      	}
      }
      
      • ServletRequestEvent事件
        public ServletRequest getServletRequest();//取得一个ServletRequest对象
        public ServletContext getServletContext();//取得一个ServletContext(application)对象
        
    2. ServletRequestAttributeListener:对 request属性的监听(增删改查)
      public interface ServletRequestAttributeListener extends EventListener {
      	//增加属性
      	public default void attributeAdded(ServletRequestAttributeEvent srae) {
      	}
      	//属性删除
      	public default void attributeRemoved(ServletRequestAttributeEvent srae) {
      	}
      	属性替换(第二次设置统一属性)
      	public default void attributeReplaced(ServletRequestAttributeEvent srae) {
      	}
      }
      
      • ServletRequestAttributeEvent事件:能取得设置属性的名称与内容
        public String getName();//得到属性名称
        public Object getValue();//取得属性的值
        

    4. 配置 监听器

    • 在 web.xml 中配置
    • Listener配置信息必须在 filter和 servlet 配置之前,listener的初始化(ServletContentListener初始化)比servlet和 filter都有限,而销毁比 servlet和 filter都慢
      <listener>
      	<listener-class>路径</listener-class>
      </listener>
      
    • 注解
      @WebListener()

    案例HttpSessionAttributeListener 实现统计会员在线人数

    @WebListener
    public class Demo01AttributeListener implements HttpSessionAttributeListener {
        @Override
        //HttpSessionAttributeListener 实现 统计在线会员人数
        public void attributeAdded(HttpSessionBindingEvent se) {
            System.out.println("Demo01SessionAttributeListener:::attributeAdded");
            System.out.println("session对象:" + se.getSession().getId() + ":::添加属性:" + se.getName() + "=" + se.getValue());
    
            //统计人数:需要被整个项目共享  装在servletcontext域中:::要求会员登录  在seesion域中添加属性user
            if (se.getName().equals("user")) {//会员+1
                Object count = se.getSession().getServletContext().getAttribute("count");//获取servletcontext域中的count属性
                if (count == null) {//你是第一个登录者
                    se.getSession().getServletContext().setAttribute("count", 1);
                } else {//原来的值+1
                    se.getSession().getServletContext().setAttribute("count", ((Integer) count) + 1);
                }
            }
        }
    
        @Override
        public void attributeRemoved(HttpSessionBindingEvent se) {
            System.out.println("Demo01SessionAttributeListener:::attributeRemoved");
            System.out.println("session对象:" + se.getSession().getId() + ":::删除属性:" + se.getName() + "=" + se.getValue());
            //统计人数:需要被整个项目共享  装在servletcontext域中:::要求会员登出:删除session中的域属性user
            if (se.getName().equals("user")) {//会员+1
                Object count = se.getSession().getServletContext().getAttribute("count");//获取servletcontext域中的count属性
                //原来的值-1
                se.getSession().getServletContext().setAttribute("count", ((Integer) count) - 1);
            }
        }
    
        @Override
        public void attributeReplaced(HttpSessionBindingEvent se) {
            System.out.println("Demo01SessionAttributeListener:::attributeReplaced");
            System.out.println("session对象:" + se.getSession().getId() + ":::修改属性:" + se.getName());
            System.out.println("删除属性" + se.getName() + "旧值=" + se.getValue() + ",新值=" + se.getSession().getAttribute(se.getName()));
        }
    }
    

    案例 ServletContextListerner 初始化指定文件

    1. 定义一个类,实现 ServletContextListener 接口

    2. 复写方法

    3. 配置

      public class ListenerDemo2 implements ServletContextListener {
      	/**
      	 * 监听 ServletContextListener 对象创建的。
      	 * ServletContextListener 对象服务器启动后自动创建
      	 * 在服务器启动后自动调用
      	 *
      	 * @param sce
      	 */
      	@Override
      	public void contextInitialized(ServletContextEvent sce) {
      		//加载资源文件
      		//
      		//1. 获取 ServletContext对象
      		ServletContext servletContext = sce.getServletContext();
      		//2. 加载资源文件
      		String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
      		//3. 获取真实路径
      		String realPath = servletContext.getRealPath(contextConfigLocation);
      		//4. 加载进内存
      		try {
      			FileInputStream fis = new FileInputStream(realPath);
      			System.out.println("fis = " + fis);
      		} catch (IOException e) {
      			e.printStackTrace();
      		}
      
      		System.out.println("ServletContextListener 对象被创建了");
      
      	}
      
      	/**
      	 * 在服务器关闭后,ServletContextListener 对象被销毁。当服务器正常关闭后该方法被调用
      	 *
      	 * @param sce
      	 */
      	@Override
      	public void contextDestroyed(ServletContextEvent sce) {
      		//
      		System.out.println("ServletContextListener 被销毁了");
      	}
      }
      
      web.xml 下
      <listener>
      	<listener-class>com.cainiao.Listener.ListenerDemo2</listener-class>
      </listener>
      <!--指定初始化参数-->
      <context-param>
      	<param-name>contextConfigLocation</param-name>
      	<!--因为创建的是普通的 web项目,在idea中的位置是 src下的,
      	其实看的是idea的out下的目录 -->
      	<param-value>WEB-INF/classes/applicationContext.xml</param-value>
      </context-param>
      
  • 相关阅读:
    leetcode33. Search in Rotated Sorted Array
    pycharm 设置sublime text3 monokai主题
    django class Meta
    leetcode30, Substring With Concatenation Of All Words
    Sublime text3修改tab键为缩进为四个空格,
    sublime text3 python打开图像的问题
    安装上imesupport输入法依然不跟随的解决办法,
    sublime text3 的插件冲突弃用问题,
    sublime text3 BracketHighlighter括号匹配的设置
    windows 下wget的使用
  • 原文地址:https://www.cnblogs.com/zk2020/p/15164508.html
Copyright © 2011-2022 走看看