• Filter学习


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

    2. 快速入门:
      1. 步骤:
        1. 定义一个类,实现接口Filter
        2. 复写方法
        3. 配置拦截路径

          *两种配置方式
            1. web.xml
            2. 注解

      2.代码

        1. web.xml配置filter

    <filter>
      <filter-name>demo1</filter-name>
      <filter-class>cn.itcast.web.filter.FilterDemo1</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>demo1</filter-name>
      <!-- 拦截路径 -->
      <url-pattern>/*</url-pattern>
    </filter-mapping>

        2. 注解方式配置filter

    @WebFilter("/*")//访问所有资源之前,都会执行该过滤器
    public class FilterDemo1 implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException { }            
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            
         System.out.println(
    "filterDemo1被执行了...."); //放行,该句确保请求能正常访问 filterChain.doFilter(servletRequest,servletResponse); System.out.println("filterDemo1回来了....") } @Override public void destroy() { } }

      3. 过滤器细节:

        1. 过滤器执行流程   

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

                     

        2. 过滤器生命周期方法
          1. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源
          2. doFilter:每一次请求被拦截资源时,会执行。执行多次
          3. destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源

          3. 过滤器配置详解(结合下方代码)

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

          * 拦截方式配置:资源被访问的方式
            * 注解配置:
              * 设置dispatcherTypes属性
                1. REQUEST:默认值。浏览器直接请求资源
                2. FORWARD:转发访问资源
                3. INCLUDE:包含访问资源
                4. ERROR:错误跳转资源
                5. ASYNC:异步访问资源
            * web.xml配置
              * 设置<dispatcher></dispatcher>标签即可

     1 import javax.servlet.*;
     2 import java.io.IOException;
     3 
     4 //浏览器直接请求index.jsp资源时,该过滤器会被执行
     5 //@WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.REQUEST)
     6 //只有转发访问index.jsp时,该过滤器才会被执行
     7 //@WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.FORWARD)
     8 
     9 //浏览器直接请求index.jsp或者转发访问index.jsp。该过滤器才会被执行
    10 //@WebFilter(value="/*",dispatcherTypes ={ DispatcherType.FORWARD,DispatcherType.REQUEST})
    11 public class FilterDemo5 implements Filter {
    12 
    13     public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
    14         System.out.println("filterDemo5....");
    15         chain.doFilter(req, resp);
    16     }
    17 
    18     public void init(FilterConfig config) throws ServletException {
    19 
    20     }
    21 
    22     public void destroy() {
    23     }
    24 
    25 }

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

          * 执行顺序:如果有两个过滤器:过滤器1和过滤器2
            1. 过滤器1
            2. 过滤器2
            3. 资源执行
            4. 过滤器2
            5. 过滤器1

          * 过滤器先后顺序问题:
            1. 注解配置:按照类名的字符串比较规则比较,值小的先执行
              * 如: AFilter 和 BFilter,AFilter就先执行了。
            2. web.xml配置: <filter-mapping>谁定义在上边,谁先执行

      4. 案例:
        1. 案例1_登录验证
          * 需求:
            1. 验证其是否登录
            2. 如果登录了,则直接放行。
            3. 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。

                                     

          *代码:(直接贴的登录验证的过滤器代码,主要为展现其流程思想)

    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * 登录验证的过滤器
     */
    @WebFilter("/*")
    public class LoginFilter implements Filter {
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println(req);
            //0.强制转换
            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("/checkCodeServlet")  ){
                //包含,用户就是想登录。放行
                chain.doFilter(req, resp);
            }else{
                //不包含,需要验证用户是否登录
                //3.从获取session中获取user
                Object user = req uest.getSession().getAttribute("user");
                if(user != null){
                    //登录了。放行
                    chain.doFilter(req, resp);
                }else{
                    //没有登录。跳转登录页面
                    request.setAttribute("login_msg","您尚未登录,请登录");
                    request.getRequestDispatcher("/login.jsp").forward(request,resp);
                }
            }
        }
    
        public void init(FilterConfig config) throws ServletException {}
    
        public void destroy() {}
    
    }

        2. 案例2_敏感词汇过滤
          * 需求:
            1. 对录入的数据进行敏感词汇过滤
            2. 敏感词汇参考《敏感词汇.txt》

                                 

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

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

                         

          * 代码:

    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.BufferedReader;
    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;
    
    /**
     * 敏感词汇过滤器
     */
    @WebFilter("/*")
    public class SensitiveWordsFilter implements Filter {
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            //1.创建代理对象,增强getParameter方法
            ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.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(req,args);
                        if(value != null){
                            for (String str : list) {
                                if(value.contains(str)){
                       //将敏感词替换 value
    = value.replaceAll(str,"***"); } } } return value; } //判断方法名是否是 getParameterMap //判断方法名是否是 getParameterValue return method.invoke(req,args); } }); //2.放行 chain.doFilter(proxy_req, resp); }
    private List<String> list = new ArrayList<String>();//敏感词汇集合
    public void init(FilterConfig config) throws ServletException { try{ //1.获取文件真实路径 ServletContext servletContext = config.getServletContext();
           //《敏感词汇.txt》是放在src文件夹下的,最后会被加载到classes文件夹中  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 (Exception e){ e.printStackTrace(); } } public void destroy() {} }

    =======================================================================      

    在上文中有提到增强对象的功能,这涉及到代理模式的知识,下面就着重讲解一下代理模式中的动态代理:
          * 设计模式:一些通用的解决固定问题的方式
            1. 装饰模式
            2. 代理模式
              * 概念: 
                1. 真实对象:被代理的对象
                2. 代理对象
                3. 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的
              * 实现方式:
                1. 静态代理:有一个类文件描述代理模式
                2. 动态代理:在内存中形成代理类
              * 实现步骤:
                1. 代理对象和真实对象实现相同的接口
                2. 代理对象 = Proxy.newProxyInstance();
                3. 使用代理对象调用方法。
                4. 增强方法

            * 增强方式:
             1. 增强参数列表
             2. 增强返回值类型
             3. 增强方法体执行逻辑

        动态代理学习案例:

                               

       上图中,西安联想代理商就是联想公司的一个代理对象,用户可以调用该代理对象,而该代理对象再去调用联想对象。在用户调用代理对象时,该对象可以增强联想对象的功能。

    创建一个SaleComputer接口:

    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("展示电脑。。。");
        }
    }

    创建一个代理测试类ProxyTest,用以展示动态代理的基本实现步骤:

    public class ProxyTest {
        public static void main(String[] args) {
            //1.创建真实对象
            Lenovo lenovo = new Lenovo();
    
            //2.动态代理增强lenovo对象
            /*
                三个参数:
                    1.类加载器:真实对象.getClass().getClassLoader()
                    2.接口数组;真实对象.getClass().getInterfaces()
                    3.处理器:new InvocationHandler()
             */
            SaleComputer  proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
                /*
                    代理逻辑编写的方法:代理对象调用的所有方法都会触发该方法的执行
                        参数: 1.proxy:代理对象
                              2.method: 代理对象调用方法,被封装为的对象
                              3.args: 代理对象调用方法时,传递的实际参数
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("该方法执行了。。。");
                    System.out.println(method.getName());
                    System.out.println(args[0]);
                    return null;
                }
            });
    
            //3.调用方法
            String computer = proxy_lenovo.sale(8000);
            System.out.println(computer);
    
            //proxy_lenovo.show();
        }
    }

    测试的结果为:

    可以发现,代理对象调用的sale()方法什么事也没有干:

    String computer = proxy_lenovo.sale(8000);
    System.out.println(computer);

    这就意味着,西安联想代理商并不具有卖电脑的功能,他只是增强了联想公司卖电脑的功能,所以他将来需要调用真实的联想公司卖电脑的功能。

    此时,我们对代理测试类ProxyTest进行一下修改:

    public class ProxyTest {
        public static void main(String[] args) {
            //1.创建真实对象
            Lenovo lenovo = new Lenovo();
    
            //2.动态代理增强lenovo对象
            SaleComputer  proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    /*System.out.println("该方法执行了。。。");
                    System.out.println(method.getName());
                    System.out.println(args[0]);*/
    
                    //使用真实对象调用该方法
                    Object obj = method.invoke(lenovo,args);
    return obj; } }); //3.调用方法 String computer = proxy_lenovo.sale(8000); System.out.println(computer); proxy_lenovo.show(); } }

    返回的obj对象就是输出的computer变量。

    测试结果为:

    可以发现sale()方法和show()方法都得到了正常的调用。

    下面介绍动态代理增强功能的三种方式,代码修改为:

    public class ProxyTest {
        public static void main(String[] args) {
            //1.创建真实对象
            Lenovo lenovo = new Lenovo();
    
            //2.动态代理增强lenovo对象
            SaleComputer  proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               
                    //判断是否是sale方法,因为这样就可以做到指定某个方法进行增强
                    if(method.getName().equals("sale")){
                        //1.增强参数
                        double money = (double)args[0];
                        money = money*0.85;
                        System.out.println("专车接你。。。");//3.增强方法体执行逻辑
                        //使用真实对象调用该方法
                        Object obj = method.invoke(lenovo,money);
                        System.out.println("免费送货。。。");//3.增强方法体执行逻辑
                        //2.增强返回值,例如可以在返回值后加上一些字符串
                        return obj+"+鼠标垫";
                    }else{
                        //若不是sale方法,则不用修改,原样执行
                        Object obj = method.invoke(lenovo,args);
                        return obj;
                    }
    
                }
            });
    
            //3.调用方法
            String computer = proxy_lenovo.sale(8000);
            System.out.println(computer);
    
            proxy_lenovo.show();
        }
    }

    1.增强参数:

    //1.增强参数
    double money = (double)args[0];
    money = money*0.85;
    Object obj = method.invoke(lenovo,money);

    可以发现,代理可对调用的方法的参数进行修改。

    2.增强返回值

    Object obj = method.invoke(lenovo,money);
    //2.增强返回值,例如可以在返回值后加上一些字符串
    return obj+"+鼠标垫";

    3.增强方法体执行逻辑

    System.out.println("专车接你。。。");//3.增强方法体执行逻辑
    Object obj = method.invoke(lenovo,money);//方法体
    System.out.println("免费送货。。。");//3.增强方法体执行逻辑

    最后的运行结果为:

  • 相关阅读:
    【IoT】IDEA 编译出错 Error running xxx : Command line is too long.Shorten command line
    【可视化】地震剖面道数据可视化绘制思路
    【IoT】使用MQTTBox.exe发送消息到Thingsboard3.3.3上
    【IoT】thingsboard3.3.3测试,使用nodemqtt发送消息
    【IoT】thingsboard3.3.3编译问题解决
    【Postgres】Postgres12帮助手册
    【IoT】发布启动thingsboard3.3.3
    【前端开发】Webpack mars3dvue2electron打包问题
    【可视化】地震数据体Segy文件inline、xline道数据计算获取
    【IoT】thingsboard3.3.3运行启动
  • 原文地址:https://www.cnblogs.com/churujianghudezai/p/11817306.html
Copyright © 2020-2023  润新知