• 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>
      
  • 相关阅读:
    A:hover,A:visited 和A:active的区别
    什么是UrlEncode
    ERP系统BOM详细解析(一)
    ERP术语 [转]
    ERP理论的形成
    MRP的計算步驟
    ERP系统模块完全解析──物料编码分章(一)
    Transact SQL 常用语句以及函数
    SQL 2000中的触发器使用
    修改docker0默认IP地址
  • 原文地址:https://www.cnblogs.com/zk2020/p/15164508.html
Copyright © 2020-2023  润新知