03_16_JavaWeb||day19_Filter&Listener||day19_Filter&代理模式(23种设计模式之一:用来解决对象增强问题,框架底层原理也有涉及)&Listener

今日内容

* Servlet,Filter,Listener被称为JavaWeb三大组件
1. Filter:过滤器
2. Listener:监听器

1. Filter:过滤器

  1. 概念:
    1. 生活中的过滤器:净水器,空气净化器,土匪、
    2. web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。
    3. 过滤器的作用
      • ==一般用于完成通用的操作。==如:登录验证、统一编码处理、敏感字符过滤…
  2. 快速入门:
    1. 步骤:
      1. 定义一个类,实现接口Filter(javax.servlet包下的)
      2. 复写方法
      3. 配置拦截路径
        1. web.xml
        2. 注解(多用)
    2. 代码:
          @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);
              }
          
              @Override
              public void destroy() {
          
              }
          }
      
  3. 过滤器细节:
    1. web.xml配置(一般都用注解,不用web.xml)
          <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. 过滤器执行流程
      1. 执行过滤器
      2. 执行放行后的资源
      3. 回来执行过滤器放行代码下边的代码
    3. 过滤器生命周期方法
      1. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源
      2. doFilter:每一次请求被拦截资源时,会执行。执行多次
      3. destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源
    4. 过滤器配置详解
      1. 拦截路径配置:
        1. 具体资源路径(用的少): /index.jsp
          • 只有访问index.jsp资源时,过滤器才会被执行
        2. 拦截目录: /user/*
          • 访问/user下的所有资源时,过滤器都会被执行
        3. 后缀名拦截(注意无/): *.jsp
          • 访问所有后缀名为jsp资源时,过滤器都会被执行
        4. 拦截所有资源:/*
          • 访问所有资源时,过滤器都会被执行
      2. 拦截方式配置:资源被访问的方式
        • 注解配置:
          • 设置dispatcherTypes属性
            1. REQUEST:默认值。浏览器直接请求资源
            2. FORWARD:转发访问资源
            3. INCLUDE:++包含访问资源++
            4. ERROR:++错误跳转资源++
            5. ASYNC:++异步访问资源++
            • 案例:做一个浏览器请求资源的时候,执行过滤器,转发访问的时候不执行过滤器。及其他两种情况(相反和都执行)
                  //FilterDemo2.java中(过滤器)
                  //浏览器直接请求index.jsp资源的时候,该过滤器才会被执行,转发请求的index.jsp过滤器就不会被执行
                  //@WebFilter(value = "/index.jsp", dispatcherTypes = DispatcherType.REQUEST)
                  //只有转发访问index.jsp资源的时候,该过滤器才会被执行
                  //@WebFilter(value = "/index.jsp", dispatcherTypes = DispatcherType.FORWARD)
                  
                  //浏览器直接请求index.jsp或者转发访问index.jsp资源的时候,该过滤器都会被执行
                  @WebFilter(value = "/index.jsp", dispatcherTypes = {DispatcherType.FORWARD, DispatcherType.REQUEST})
                  public class FilterDemo2 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("filterDemo5");
                          filterChain.doFilter(servletRequest, servletResponse);
                      }
                  
                      @Override
                      public void destroy() {
                  
                      }
                  }
                  
                  
                  //ServletDemo1.java中(演示转发访问)
                  @WebServlet("/user/updateServlet")
                  public class ServletDemo1 extends HttpServlet {
                      @Override
                      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                          System.out.println("updateServlet");
                  
                          //转发到index.jsp
                          req.getRequestDispatcher("/index.jsp").forward(req, resp);
                      }
                  
                      @Override
                      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                          this.doPost(req, resp);
                      }
                  }
                  
                  //index.jsp页面
                  <%@ page contentType="text/html;charset=UTF-8" language="java" %>
                  <html>
                    <head>
                      <title>$Title$</title>
                    </head>
                    <body>
                      index.jsp
                    </body>
                  </html>
                  
                  
                  //浏览器直接请求和转发访问,过滤器都执行的情况,访问http://localhost/day19/user/updateServlet时:
                  updateServlet
                  filterDemo5
              
              • 注:都可以让过滤器执行的情况中,访问转发访问的路径时,过滤器只被执行一次
        • web.xml配置
          • 设置标签即可
            • 标签的取值也是上面五个
          <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>
              <dispatcher>FORWARD</dispatcher>
          </filter-mapping>
          
    5. 过滤器链(配置多个过滤器)
      • 执行顺序:如果有两个过滤器:过滤器1和过滤器2
        1. 过滤器1
        2. 过滤器2
        3. 资源执行
        4. 过滤器2
        5. 过滤器1
      • 过滤器先后顺序问题
        1. 注解配置:按照++类名的++字符串比较规则比较,值小的先执行
          • 如: AFilter 和 BFilter,AFilter就先执行了。
        2. web.xml配置: 谁定义在上边,谁先执行
  4. 案例:
    1. 案例1_登录验证(权限控制基础)
      1. 需求:
        1. 访问day17_case案例的资源。验证其是否登录
        2. 如果登录了,则直接放行。
        3. 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。
      2. 代码逻辑:
      3. 代码实现:
            //LoginFilter.java
            @WebFilter("/*")
            public class LoginFilter implements Filter {
                @Override
                public void init(FilterConfig filterConfig) throws ServletException {
            
                }
            
                @Override
                public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                    //1. ***强制转换(因为获取请求路径在HttpServletRequest中)
                    HttpServletRequest request = (HttpServletRequest)servletRequest;
            
                    //2. 获取资源请求路径
                    String uri = request.getRequestURI();
            
                    //3. 判断是否包含登录相关资源路径,注意要排除掉 css/js/图片/验证码等资源
                    if(uri.contains("/login.jsp") || uri.contains("/loginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/fonts/") || uri.contains("/checkCodeServlet")){
                        //包含,用户就是想登录,放行
                        filterChain.doFilter(servletRequest, servletResponse);
                    }else {
                        //不包含,需要验证用户是否登录
                        //4. 从session中获取user
                        Object user = request.getSession().getAttribute("user");
                        if(user != null){
                            //说明登录了,放行
                            filterChain.doFilter(servletRequest, servletResponse);
                        }else {
                            //没有登录,跳转登录页面
                            request.setAttribute("login_msg", "你尚未登录,请登录");
                            request.getRequestDispatcher("/login.jsp").forward(request, servletResponse);
                        }
                    }
            
                }
            
                @Override
                public void destroy() {
            
                }
            }
        
    2. 案例2_敏感词汇过滤
      1. 需求:
        1. 对day17_case案例录入的数据进行敏感词汇过滤
        2. 敏感词汇参考《敏感词汇.txt》
        3. 如果是敏感词汇,替换为 ***
      2. 代码逻辑:
      3. 分析:
        1. 对request对象进行增强。增强获取参数相关方法
        2. 放行。传递代理对象
      • 增强对象的功能:(前人已经设计好了,学会使用)
        • 设计模式一些通用的解决固定问题的方式
          • 一共23钟,今天用两种都可以解决对象增强问题
        1. 装饰模式(相比代理模式会笨重一点)
        2. 代理模式【理解,会用
          • 概念:
            1. 真实对象:被代理的对象
            2. 代理对象:
            3. 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的
          • 实现方式
            1. 静态代理:有一个类文件(.java)描述代理模式
            2. 动态代理[使用更多]:在内存中形成代理类
              • 实现步骤
                1. 代理对象和真实对象实现相同的接口
                2. 代理对象 = Proxy.newProxyInstance();
                3. 使用代理对象调用方法。
                4. 增强方法
              • 案例:卖电脑
                  //SaleComputer.java(接口)
                  public interface SaleComputer {
                      public String sale(double money);
                  
                      public void show();
                  }
                  
                  
                  //Lenove.java(真实类)
                  public class Lenove implements SaleComputer{
                      @Override
                      public String sale(double money) {
                          System.out.println("花了" + money +"元买了一台电脑");
                          return "联想电脑";
                      }
                  
                      @Override
                      public void show() {
                          System.out.println("展示电脑");
                      }
                  }
                  
                  
                  //ProxyTest.java
                  public class ProxyTest {
                      public static void main(String[] args) {
                          //1. 创建真实对象
                          Lenove lenove = new Lenove();
                  
                          //2. 动态代理增强lenove对象
                          /*
                          三个参数:
                              1. 类加载器:真实对象.getClass().getClassLoader()
                              2. 接口数组:真实对象.getClass().getInterfaces()
                              3. 处理器:new InvocationHandler()
                           */
                          //强转成对象
                          SaleComputer proxy_lenove = (SaleComputer)Proxy.newProxyInstance(lenove.getClass().getClassLoader(), lenove.getClass().getInterfaces(), new InvocationHandler() {
                              /*
                                  代理逻辑编写的方法:代理对象调用的所有方法都会触发该方法执行
                                      * 也及时不管调用proxy_lenove什么方法,都会调用invoke方法
                                  * 参数:
                                      1. proxy:代理对象
                                      2.method:代理对象调用的方法,被封装为的对象
                                      3. args:代理对象调用的方法时,传递的实际对象
                               */
                              @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 *= 0.85;
                                      //使用真实对象调用该方法
                                      String obj = (String)method.invoke(lenove, money);
                  
                                      //2. 增强返回值
                                      return obj + "_鼠标垫";
                                  }else {
                                      //使用真实对象调用该方法
                                      Object obj = method.invoke(lenove, args);
                  
                                      return obj;
                                  }
                  
                              }
                          });
                  
                          //2. 调用方法
                          String computer = proxy_lenove.sale(8000);
                          System.out.println(computer);
                      }
                  }
                  
                  //结果:
                  花了6800.0元买了一台电脑
                  联想电脑_鼠标垫
                  
              
              • 增强方式
                1. 增强参数列表
                2. 增强返回值类型
                3. 增强方法体执行逻辑
      1. 案例2_敏感词汇过滤的代码实现:
            //src下的敏感词汇.txt文件
            笨蛋
            坏蛋
        
            //SensitiveWordsFilter.java中
            /**
             * 敏感词汇过滤器
             */
            @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();
                        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();
            
                        System.out.println(list);
            
                    }catch (Exception e){
                        e.printStackTrace();
                    }
            
                }
            
                public void destroy() {
                }
            
            }
        

2. Listener:监听器(后期框架写好了,只需要学会配置即可)

  1. 概念:web的三大组件之一。(三个组件都需要配置)
    • 事件监听机制
      • 事件 :一件事情
      • 事件源 :事件发生的地方
      • 监听器 :一个对象
      • 注册监听:将事件、事件源、监听器绑定在一起。
        • 当事件源上发生某个事件后,执行监听器代码
  2. ServletContextListener接口:++监听ServletContext对象的创建和销毁++
    1. 方法
      • void contextDestroyed(ServletContextEvent sce) :ServletContext对象被销毁之前会调用该方法
      • void contextInitialized(ServletContextEvent sce) :ServletContext对象创建后会调用该方法
    2. 步骤【掌握步骤即可】
      1. 定义一个类,实现ServletContextListener接口
      2. 复写方法
      3. 配置
        1. web.xml
          • 中间是类的路径
              <listener> <listener-class>cn.itcast.web.listener.ContextLoaderListener</listener-class>
          	</listener>
          
          • 指定初始化参数
              <context-param>
                  <param-name>contextConfigLocation</param-name>
                  <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
              </<context-param>
          
        2. 注解:
          • @WebListener
      4. 代码样例:
            @WebListener("")
            public class ContextLoaderListener implements ServletContextListener {
                /**
                 * 监听ServletContext对象创建的。ServletContext对象,服务器启动后自动创建
                 *
                 * 在服务器启动后自动调用
                 * @param servletContextEvent
                 */
                @Override
                public void contextInitialized(ServletContextEvent servletContextEvent) {
                    //加载资源文件
                    //1. 获取ServletContext对象
                    ServletContext servletContext = servletContextEvent.getServletContext();
            
                    //2. 加载资源文件
                    String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
            
                    //3. 获取真实路径
                    String realPath = servletContext.getRealPath(contextConfigLocation);
            
                    //4. 加载进内存
                    try{
            
                        FileInputStream fis = new FileInputStream(realPath);
                        System.out.println(fis);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
            
            
                    System.out.println("ServletContext对象被创建了");
                }
            
                /**
                 * 在服务器关闭后,ServletContext对象被销毁。当服务器正常关闭后该方法被调用
                 * @param servletContextEvent
                 */
                @Override
                public void contextDestroyed(ServletContextEvent servletContextEvent) {
                    System.out.println("ServletContext对象被销毁了");
                }
            }
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂野小白兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值