JavaWeb笔记Day9------过滤器和监听器

增强对象的功能

  1. 装饰模式
  2. 代理模式
  • 设计模式:一些通用的解决固定问题的方式

代理模式

概念

  1. 真实对象:被代理的对象
  2. 代理对象:
  3. 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的

image-20220325004915122

实现方式

  1. 静态代理:有一个类文件描述代理模式

  2. 动态代理:在内存中形成代理类

动态代理

实现步骤

  1. 代理对象和真实对象实现相同的接口
  2. 代理对象=Proxy.newInstance();
  3. 使用代理对象调用方法
  4. 增强方法

增强方式

  1. 增强参数列表

  2. 增强返回值类型

  3. 增强方法体执行逻辑

示例

SaleComputer
package proxy;

public interface SaleComputer {
    public String sale(double money);
    public void Show();
}
Lenovo
package proxy;

/**
 * 真实类
 */
public class Lenovo implements SaleComputer {
    @Override
    public String sale(double money) {
        System.out.println("联想电脑,卖出" + money + "元");
        return "联想电脑";
    }

    @Override
    public void Show() {
        System.out.println("展示电脑");
    }
}
ProxyTest
package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(String[] args) {
        //1. 创建真实对象
        Lenovo lenovo=new Lenovo();

        /**
         * 三个参数:
         *        1.类加载器:真实对象.getClass().getClassLoader()
         *        2.接口数组:真实对象.getClass().getInterfaces()
         *        3.处理器:new InvocationHandler()
         */
        //2. 动态代理增强lenovo对象(固定套路)
        SaleComputer proxy_Lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 代理逻辑编写的方法:代理对象调用的所有方法都会触发该方法执行
             * @param proxy 代理对象
             * @param method 代理对象调用的方法,被封装为的对象
             * @param args 代理对象调用的方法时,传递的实际参数
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("sale")) {
                    //1. 增强参数
                    double money= (double) args[0];
                    money=money*0.8;
                    System.out.println("专车接你.....");
                    //使用真实对象调用该方法
                    Object obj=method.invoke(lenovo,money);
                    System.out.println("免费送货.....");
                    //2. 增强返回值
                    return obj+"_鼠标垫";
                }else {
                    Object obj=method.invoke(lenovo,args);
                    return obj;
                }
            }
        });

        //调用方法
        String computer=proxy_Lenovo.sale(8000);
        System.out.println(computer);

        proxy_Lenovo.Show();
    }
}

Filter:过滤器

image-20220323223108787

Web中的过滤器

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

作用

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

编写步骤

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

  2. 复写方法

  3. 配置拦截路径

    1. web.xml

    2. 注解

示例代码

package Filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.*;

import java.io.IOException;

@WebFilter(filterName = "FilterDemo1", urlPatterns = "/*")
public class FilterDemo1 implements Filter {
    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        System.out.println("FilterDemo1.doFilter().....");

        //放行
        chain.doFilter(request, response);
    }
}

过滤器细节

  1. web.xml配置

    1. 示例:

          <filter>
              <filter-name>demo1</filter-name>
              <filter-class>Filter.FilterDemo1</filter-class>
          </filter>
          <filter-mapping>
              <filter-name>demo1</filter-name>
              <!--拦截路径-->
              <url-pattern>/*</url-pattern>
          </filter-mapping>
      
  2. 过滤器执行流程

  3. 过滤器生命周期方法

    package Filter;
    
    import jakarta.servlet.*;
    import jakarta.servlet.annotation.*;
    
    import java.io.IOException;
    
    //@WebFilter(filterName = "FilterDemo1", urlPatterns = "/*")
    public class FilterDemo1 implements Filter {
        /**
         * 在服务器启动时,会创建Filter对象,然后调用init方法,只执行一次,用于加载资源
         * @param config
         * @throws ServletException
         */
        public void init(FilterConfig config) throws ServletException {
        }
    
        /**
         * 在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会调用destroy方法。只执行一次,用于释放资源
         */
        public void destroy() {
        }
    
        /**
         * 每一次请求被拦截资源时,都会调用doFilter方法。执行多次
         * @param request
         * @param response
         * @param chain
         * @throws ServletException
         * @throws IOException
         */
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        }
    }
    
    
  4. 过滤器配置详解

    • 拦截路径配置:

      1. 具体资源路径:/index.jsp 只有访问index.jsp资源时,过滤器才会被执行
      2. 拦截目录:/user/* 访问/user下的所有资源时,过滤器都会被执行
      3. 后缀名拦截:*.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行
      4. 拦截所有资源:/* 访问所有资源时,过滤器都会被执行
    • 拦截方式配置:资源被访问的方式

      1. 注解配置:

        • 设置dispatcherTypes属性

          1. REQUEST:默认值,浏览器直接请求资源。
          2. FORWARD:转发访问资源。
          3. INCLUDE:包含访问资源。
          4. ERROR:错误跳转资源。
          5. ASYNC:异步访问资源。
        • 示例:

          image-20220324234836215

      2. web.xml配置

        • 设置<dispatcher><dispatcher>标签即可
  5. 过滤器链(配置多个过滤器)

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

案例

登录验证

需求
  1. 访问JavaWeb笔记Day8案例的资源。验证其是否登录
  2. 如果登录了,则直接放行。
  3. 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。
分析

image-20220325001708811

代码
package web.filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.*;
import jakarta.servlet.http.HttpServletRequest;

import java.io.IOException;

@WebFilter(filterName = "LoginFilter" , urlPatterns = "/*")
public class LoginFilter implements Filter {
    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        //0. 强制类型转换
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        //1. 获取资源请求路径
        String requestURI = httpRequest.getRequestURI();
        //2.判断是否包含登录相关资源路径,要注意排除掉css/js/图片/验证码等资源
        if (requestURI.contains("/login.jsp") || requestURI.contains("/loginServlet") ||requestURI.contains("/css/")||requestURI.contains("/js/")||requestURI.contains("/fonts/")||requestURI.contains("/CheckCodeServlet")) {
            //包含,用户就是想登录,放行
            chain.doFilter(request, response);
        }else {
            //不包含,需要验证用户是否登录
            //3. 从获取session中获取login_user
            Object user = httpRequest.getSession().getAttribute("login_user");
            if (user != null) {
                //用户已经登录,放行
                chain.doFilter(request, response);
            }else {
                //用户未登录,跳转到登录页面
                httpRequest.setAttribute("login_msg", "您尚未登录,请先登录");
                httpRequest.getRequestDispatcher("/login.jsp").forward(request, response);
            }
        }
    }
}

敏感词汇过滤

需求
  1. JavaWeb笔记Day8案例录入的数据进行敏感词汇过滤

  2. 敏感词汇参考《敏感词汇.txt》

  3. 如果是敏感词汇,替换为 ***

分析
  1. 对request对象进行增强。增强获取参数相关方法
  2. 放行。传递代理对象

image-20220325004426428

代码
package web.filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.*;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@WebFilter(filterName = "SensitiveWordsFilter",urlPatterns = "/*")
public class SensitiveWordsFilter implements Filter {
    /**
     * 固定套路
     */
    private List<String> list=new ArrayList<String>();//敏感词汇集合
    public void init(FilterConfig config) throws ServletException {
        try {
            //1. 获取文件真实路径
            ServletContext servletContext=config.getServletContext();
            String realPath=servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt");
            //2.读取文件
            BufferedReader br=new BufferedReader(new FileReader(realPath));
            //3. 将文件的每一行数据添加到list中
            String line=null;
            while ((line=br.readLine())!=null){
                list.add(line);
            }

            br.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        //1.创建代理对象,增强getParameter方法
        ServletRequest pro_req= (ServletRequest) Proxy.newProxyInstance(request.getClass().getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //增强getParameter方法
                //判断是否是getParameter方法
                if(method.getName().equals("getParameter")){
                    //增强返回值
                    //获取返回值
                    String value= (String) method.invoke(request,args);
                    if (value!=null){
                        for (String str:list)
                            if (value.contains(str))
                                value=value.replaceAll(str,"***");
                    }
                    return value;
                }

                //判断方法名是否是getParameterMap
                if (method.getName().equals("getParameterMap")){
                    //增强返回值
                    //获取返回值
                    Map<String, String[]> values= (Map<String, String[]>) method.invoke(request,args);
                    for (Map.Entry<String, String[]> StringEntry:values.entrySet()){
                        for (String[] value:values.values())
                            for (int i=0;i<value.length;i++){
                                for (String str:list){
                                    if (value[i].contains(str)){
                                        value[i]=value[i].replaceAll(str,"***");
                                    }
                                }
                            }
                    }
                    return values;
                }

                //判断方法名是否是getParameterValues
                if (method.getName().equals("getParameterValues")){
                    //增强返回值
                    //获取返回值
                    String[] values= (String[]) method.invoke(request,args);
                    if (values!=null){
                        for (int i=0;i<values.length;i++){
                            for (String str:list){
                                if (values[i].contains(str)){
                                    values[i]=values[i].replaceAll(str,"***");
                                }
                            }
                        }
                    }
                    return values;
                }

                return method.invoke(request,args);
            }

        });



        //2.放行
        chain.doFilter(pro_req, response);
    }
}

Listener:监听器

概念

web的三大组件之一

事件的监听机制

  1. 事件:一件事情

  2. 事件源:事件发生的地方

  3. 监听器:一个对象

  4. 注册监听:将事件,事件源,监听器绑定在一起。当事件源上发生某个事件后,执行监听器代码

ServletContextListener

方法

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

步骤

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

  2. 复写方法

  3. 配置

    1. web.xml

          <!--配置监听器-->
          <listener>
              <listener-class>Listener.ContextLoaderListener</listener-class>
          </listener>
      
          <!--指定初始化参数-->
          <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>/WEB-INF/classes/context.xml</param-value>
          </context-param>
      
    2. 注解

      @WebListener
      

示例

package Listener;

import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

@WebListener
public class ContextLoaderListener  implements ServletContextListener {
    /**
     * 监听ServletContext对象创建的。ServletContext对象服务器启动后自动创建。
     *
     * 在服务器启动后自动调用
     * @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 {
            InputStream is = new FileInputStream(realPath);
            System.out.println(is);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }


        System.out.println("ContextLoaderListener.contextInitialized");
    }

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

posted @ 2022-03-25 03:22  灰之魔女伊蕾娜  阅读(69)  评论(0)    收藏  举报