76-filter过滤器--重点

# Filter:过滤器
    1. 概念:
        * 生活中的过滤器:净水器,空气净化器,土匪、
        * web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。
        * 过滤器的作用:拦截请求增强其功能
            * 一般用于完成通用的操作。如:

    登录验证(登陆过才能访问资源,直接访问list.jsp地址如果不登陆的话会拦下来不显示界面而会直接要求登录)、 

    统一编码处理(post很多地方需要设置编码  把设置编码的任务放在过滤器中来写简化代码)

    敏感字符过滤(敏感字符用***表示)

过滤器形象举例:我去外婆家 有一条山路 山路上有好土匪 去的时候说前面要下雨给一把伞增强了功能  回来之后还拦我又增强了功能     

注意来回都栏 而且增强功能的含义是什么

注意下面图片中的浏览器  服务器  请求   资源的标注

    2. 快速入门:
        1. 步骤:
            1. 定义一个类,实现接口Filter(类似于实现servlet)
            2. 复写方法
            3. 配置拦截路径(与servlet不同 servlet配置的是访问路径 filter配置的是拦截路径)
                1. web.xml
                2. 注解
        2. 代码:
        

package cn.itcat.web.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * 过滤器快速入门
 */
@WebFilter("/*")//访问所有资源之前都会执行该过滤器
public class FilterDemo01 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("filter被执行了");
        //放行
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

index.jsp


    3. 过滤器细节:
        1. web.xml配置     这里也可以直接注解配置<url-pattern>
            <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>

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
<filter>
    <filter-name>demo01</filter-name>  <!--声明一个名字-->
    <filter-class>cn.itcat.web.filter.FilterDemo01</filter-class>  <!--对应的类写进来-->
</filter>
<filter-mapping>
    <filter-name>demo01</filter-name>
    <url-pattern>/*</url-pattern>  <!--这里的<url-pattern>是拦截路径-->
</filter-mapping>

    
</web-app>


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

注意颜色对应

 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

       //chain.doFilter();之前对request对象的请求消息进行增强
        System.out.println("filterDemo02执行了....");


        //放行
        chain.doFilter(req, resp);     可以是index.jsp中的资源


        //chain.doFilter();之后对response对象的响应消息进行增强
        System.out.println("filterDemo02回来了....");

};

@WebFilter("/*")
public class FilterDemo02 implements Filter {

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //chain.doFilter();之前对request对象的请求消息进行增强
        System.out.println("filterDemo02执行了....");
        //放行
        chain.doFilter(req, resp);
        //chain.doFilter();之后对response对象的响应消息进行增强
        System.out.println("filterDemo02回来了....");
    }

    public void init(FilterConfig config) throws ServletException {

    }

    public void destroy() {
    }

}

-------------------------------------------------------------------
index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>

  </head>
  <body>
  <h1>index.jsp</h1>
  </body>
<%
  System.out.println("index.jsp....");
%>
</html>

控制台输出:

先打印FilterDemo02中的放行前的filterDemo02执行了....

再放行

再请求资源打印index.jsp中的代码index.jsp...

再回来直接执行FilterDemo02中过滤器放行下面的代码   打印过滤器后面的filterDemo02....回来了

注意:

        //chain.doFilter();之前对request对象的请求消息进行增强
        //放行
        chain.doFilter(req, resp);
        //chain.doFilter();之后对response对象的响应消息进行增强
       


        3. 过滤器生命周期方法
            1. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源           注意是启动服务器就执行
            2. doFilter:每一次请求被拦截资源时,会执行。执行多次

                     注意这里可以理解为访问index.jsp就执行
            3. destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源

                      注意这里是停掉服务器执行


        4. 过滤器配置详解


            * 拦截路径配置:4种
                1. 具体资源路径: /index.jsp  /servletDemo01  只有访问index.jsp或者某个特定资源资源时,过滤器才会被执行


                2. 拦截目录: /user/*    访问/user下的所有资源时,过滤器都会被执行

@WebServlet("/user/servletDemo01")    //加一个目录统一管理  访问时也要加整个目录才能找到


                3. 后缀名拦截: *.jsp /*.do/*.action        访问所有后缀名为jsp资源时,过滤器都会被执行    注意后缀名拦截没有/
                4. 拦截所有资源:/*        访问所有资源时,过滤器都会被执行


            * 拦截方式配置:资源被访问的方式 2种
                * 注解配置:(是在注解中写的)
                    * 设置dispatcherTypes属性 有五种取值可以设置
                        1. REQUEST:默认值。浏览器直接请求资源
@WebFilter(value="/*",dispatcherTypes = DispatcherTypeREQUEST)

//注意设置两个值就要加名字了  浏览器直接请求index.jsp资源时,过滤器才会被执行
                        2. FORWARD:转发访问资源

@WebFilter(value = "/index.jsp",dispatcherTypes = DispatcherType.FORWARD)

还可以同时request和forward   用大括号

@WebFilter(value = "/index.jsp",dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})


                        3. INCLUDE:包含访问资源---了解
                        4. ERROR:错误跳转资源---了解
                        5. ASYNC:异步访问资源---了解


                * web.xml配置-----感觉用的不多
                    * 在<urlpattern>下面直接设置<dispatcher></dispatcher>标签即可


                
        5. 过滤器链(配置多个过滤器)
            * 执行顺序:如果有两个过滤器:过滤器1和过滤器2 

比如一条路两个土匪你过去拿资源再回来 也是这么个顺序
                1. 过滤器1
                2. 过滤器2
                3. 资源执行
                4. 过滤器2
                5. 过滤器1 

            * 过滤器先后顺序问题:
                1. 注解配置:按照类名的字符串比较规则比较,值小的先执行

字符串的比较规则:每个字符逐字比较大小
                    * 如: AFilter 和 BFilter,AFilter就先执行了。
                2. web.xml配置: <filter-mapping>谁定义在上边,谁先执行

浏览器访问index.jsp

控制台打印:

filterDemo06被执行
filterDemo17被执行
index.jsp....
filterDemo17回来了
filterDemo06回来了


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

         注意:

         1.过滤器配置的拦截路径/*

         2.哪些资源是登陆相关的:login.jsp  显示index.jsp里面的验证码 css  js样式  点击登录按钮访问的loginservlet等
    

代码:

package cn.itcast.web.filter;

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 {
       //1.强转ServletRequest req为HttpServletRequest
        HttpServletRequest request= (HttpServletRequest) req;
        //2.获取资源的请求路径
        String uri = request.getRequestURI();
        //3.判断是否包含登录相关的资源路径 要注意排除css /js /图片 /验证码等资源
        if (uri.contains("/login.jsp")||uri.contains("/loginServlet")||uri.contains("/checkCode")||uri.contains("/css/")||uri.contains("/js/")||uri.contains("/fonts/")){
            //包含证明用户就是想登录直接放行
            chain.doFilter(req, resp);
        }else{
            //不包含的话需要验证用户是否登录
            //从session里面获取user
            Object user = request.getSession().getAttribute("user");
            if (user!=null){
                //user不等于null说明登陆了可以放行
                chain.doFilter(req, resp);
            }else{
                //没有登陆跳转登录页面 跳转之前存点提示信息
                request.setAttribute("login_msg","您尚未登录,请登录!");
                request.getRequestDispatcher("/login.jsp").forward(request,resp);
            }
        }

        /*chain.doFilter(request, response);*/
    }

    public void init(FilterConfig config) throws ServletException {

    }

    public void destroy() {
    }

}

      

   2. 案例2_敏感词汇过滤
            * 需求:
                1. 对day17_case案例录入的数据进行敏感词汇过滤
                2. 敏感词汇参考《敏感词汇.txt》
                3. 如果是敏感词汇,替换为 *** 

       注意这里想一个问题 doFilter()的参数req和resp其实就是浏览器的请求 dofilter放行之后再传下去 到资源  

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

代码:

 

package cn.itcast.web.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
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;

@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方法
                if (method.getName().equals("getParameter")){
                    //是getParameter方法需要增强
                    //增强返回值
                    //获取返回值
                    String value = (String)method.invoke(req, args);
                    if (value!=null){
                        for (String str : list) {
                            if (value.contains(str)){
                                //对返回值进行替换
                                value=value.replaceAll(str,"***");
                            }
                        }
                    }
                }
//还要判断方法名是否是getParameterMap      getParameterValue
                return method.invoke(req,args);  //其实返回的就是一个parameter字符串  如果value=mull直接执行 如果不为null先替换再执行
            }
        });
        chain.doFilter( proxy_req, resp);  //这样就会走增强的逻辑   整个就是代理对象调用要增强的方法

        // 注意这里增强的是request对象的getParameter(); 方法
    }




      private List<String> list = new ArrayList<>();  //定义一个集合放敏感的字符串
    public void init(FilterConfig config) throws ServletException {
      //在init中加载资源
        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() {
    }

}

servlet代码:

 


            * 增强对象的功能:   注意代理的是对象 增强的也是对象


                * 设计模式:一些通用的解决固定问题的方式 --------用设计模式来实现增强对象的功能
                1. 装饰模式
                2. 代理模式
                    * 概念:

概念举例:好比说我要去买联想电脑,联想的总部在北京厂在北京,联想公司的功能可以生产电脑卖电脑,我作为一个购买者,一种方式去北京买;现在代理商出现了,西安的联想代理商功能是卖电脑,我可以直接在西安的联想代理商用他的卖电脑的功能,但是西安代理是能卖电脑吗其实是北京联想的一个代理对象,我调用的是代理对象,代理对象要调用联想公司的卖电脑功能;我省了事省力


                        1. 真实对象:被代理的对象------------------------联想北京
                        2. 代理对象:--------------------------------- --------西安联想代理商
                        3. 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的
                     * 实现方式:两种 静态和动态代理
                         1. 静态代理:有一个类文件描述代理模式
                         2. 动态代理:在内存中形成代理类
                            * 实现步骤:
                                1. 代理对象和真实对象实现相同的接口
                                2. 代理对象 = Proxy.newProxyInstance();
                                3. 使用代理对象调用方法。
                                4. 增强方法

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

代码:

注意思考为什么直接代理的测试类 中就有 proxy_lenovo 而没有代理类 

         因为是在内存中形成代理类而不展示出来

接口:SellComputer
package cn.itcat.proxy;

public interface SellComputer {
    public String sell(double money);
    public void show();
}






真实类
package cn.itcat.proxy;

/**
 * 真实类  对应一个真实对象   联想的两个功能  sell   show
 */
public class Lenovo implements SellComputer {
    @Override
    public String sell(double money) {
        System.out.println("花"+money+"买了一台联想电脑");
        return "联想电脑";
    }

    @Override
    public void show() {
        System.out.println("展示电脑");
    }
}




----------------------------------------------------------------------------------
代理测试类
package cn.itcat.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) {
        //创建真实对象
        Lenovo lenovo = new Lenovo();

        //2.动态代理来增强lenovo对象
        /*
        三个参数:
              1.类加载器:真实对象.getClass().getClassLoader()
              2.接口数组:真实对象.getClass().getInterfaces()
              3.处理器  : new InvocationHandler()
         */
      /*  强转*/ /*  Objext-->sellcomputer  这样就可以使用sellcomputer的方法了*/
       SellComputer proxy_lenovo = (SellComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
            //proxy_lenovo就是代理对象  注意但是没有代理类 代理类是直接生成在内存中

            /*下面重写的是代理逻辑编写的方法  代理对象调用的所有方法都会触发该方法执行
               invoke();方法的三个参数:
               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]);*/

               //增强参数:
               //判断是否是sell方法
               if (method.getName().equals("sell")) {
                   //1.增强参数
                   double money = (double) args[0];
                   money = money * 0.85;
                   System.out.println("专车接你");
                   String obj = (String) method.invoke(lenovo, money);
                   System.out.println("免费送货");
                   return obj+"送一个鼠标垫";  //这里返回的就是lenovo的代理对象proxy_lenovo中的sell();方法的输出
               } else {
                   //使用真实对象调用该方法
                   Object obj = method.invoke(lenovo, args);  //这里的前一个参数是真实对象  后一个参数是参数 就是后面.sell中的8000
                   return obj;  /* 返回下面调用  obj-->computer*/
               }
           }
        });

        //2.调用方法
        /*上面的obj就是这里的computer*/
        String computer = proxy_lenovo.sell(8000);
        // proxy_lenovo是代理对象代理了对象真实lenono  这里用代理对象调用的方法跟由Lenovo类创建的lenovo真实对象中有的方法一致

        System.out.println(computer);
        proxy_lenovo.show();  //show就是原样调用
    }
}

控制台输出:

专车接你
花6800.0买了一台联想电脑
免费送货
联想电脑送一个鼠标垫
展示电脑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值