组件必知必会|那些年我们使用过的轮子—Filter和Proxy

开发 前端
本文将带大家详细介绍Filter以及Proxy的具体原理以及使用。

[[352586]]

 前言

过滤器Filter是JavaWeb三大组件之一,它与Servlet很相似,过滤器是用来拦截请求的,而不是处理请求的。当用户请求某个Servlet时,会先执行部署在这个请求上的Filter,如果Filter“放行”,那么会继承执行用户请求的Servlet;如果Filter“不放行”,那么就不会执行用户请求的Servlet。可以这样理解,当用户请求某个Servlet时,Tomcat会去执行注册在这个请求上的Filter,然后是否“放行”由Filter来决定。可以理解为,Filter来决定是否调用Servlet!当执行完成Servlet的代码后,还会执行Filter后面的代码。

设计模式不是技术,也不是什么框架,只是前人的一个工作的总结,在实现某一个功能的时候,怎样来减少代码之间的耦合性,以及如何实现高内聚低耦合,设计模式说白了就是按照一定的步骤来完成相应的一个功能,这个就称为设计模式。代理Proxy是指给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗来讲,代理设计模式就是我们生活中常见的中介。主要用来解决的问题第一个:监控目录一个类里面的方法的执行,第二个:在类里面某一个方法执行的前后动态的植入代码。

本文将带大家详细介绍Filter以及Proxy的具体原理以及使用。

1、什么是过滤器

「定义:」

  • 过滤器按照我们的理解就是过滤东西的,那么到底这个过滤器能够来过滤啥呢?过滤的是请求
  • 请求:动态资源的请求(Servlet)就是程序在运行的过程中,代码会发生改变,那么这一类的资源请求,就称为动态的网络资源请求
  • 请求:静态资源的请求(css、html、js、img、png...)程序在运行的过程中,代码不会发生改变的那一类请求,就称为静态的网络资源请求
  • 总结:过滤器能够拦截所有的资源请求

2、过滤器的作用

「主要用途:」

  • 用户的身份认证
  • 字符串的和谐
  • 请求的Servlet的乱码问题处理

3、过滤器的使用

「使用案例:」

编写一个类,这个类实现于Filter接口,并实现里面的方法

  1. public class HelloFilter implements Filter{ 
  2.  /** 
  3.   * 在Filter实例被创建的时候 初始化方法的时候执行的 这个一般用于Filter的初始化 
  4.   * filterConfig:获取的是Filter的配置信息以及初始化信息的 
  5.   */ 
  6.  @Override 
  7.  public void init(FilterConfig filterConfig) throws ServletException { 
  8.   System.out.println("init初始化执行了"); 
  9.  } 
  10.     /** 
  11.      * 过滤器拦截的方法 
  12.      * request:当前的请求对象的封装 
  13.      * response:当前的响应的封装 
  14.      * chain:判断是否拦截的方法 
  15.      *  
  16.      */ 
  17.  @Override 
  18.  public void doFilter(ServletRequest request, ServletResponse response, 
  19.    FilterChain chain) throws IOException, ServletException { 
  20.   System.out.println("doFilter执行了...."); 
  21.   //执行了这个方法那么就表示允许通过  没有执行那么就不允许通过 
  22.   chain.doFilter(request, response); 
  23.    
  24.  } 
  25.   
  26.     /** 
  27.      * 过滤器在死了之后 执行的这个方法 
  28.      */ 
  29.  @Override 
  30.  public void destroy() { 
  31.    
  32.   System.out.println("destory执行了...."); 
  33.  } 

在web.xml中配置Filter

  1. <filter> 
  2.     <filter-name>HelloFilter</filter-name
  3.     <filter-class>com.qy.filter.HelloFilter</filter-class> 
  4.  </filter>  
  5.  <filter-mapping> 
  6.     <filter-name>HelloFilter</filter-name
  7.     <url-pattern>/*</url-pattern> 
  8.  </filter-mapping> 
  • 测试
  • 总结:Filter的初始化方法是在程序启动的时候执行的

4、过滤器的生命周期

「生命周期:」

首先在Web容器被使用的时候那么Filter对象被创建

  1.   public HelloFilter() { 
  2.  System.out.println("类的对象被创建了...."); 

创建完成之后紧接着调用init方法进行Filter的初始化

  1. @Override 
  2. public void init(FilterConfig filterConfig) throws ServletException { 
  3.  System.out.println("init初始化执行了"); 

当前端有请求到来的时候,那么这个时候doFilter方法被执行

  1. @Override 
  2. public void doFilter(ServletRequest request, ServletResponse response, 
  3.   FilterChain chain) throws IOException, ServletException { 
  4.  System.out.println("doFilter执行了...."); 
  5.  // 执行了这个方法那么就表示允许通过 没有执行那么就不允许通过 
  6.  //放行 
  7.  // chain.doFilter(request, response); 

当 Web容器死亡的时候,那么destory方法被执行

  1. @Override 
  2.  public void destroy() { 
  3.   System.out.println("destory执行了...."); 
  4.  } 

init中filterConfig如何使用

  1. 在web.xml中给filter添加初始数据  
  2. <filter> 
  3.      <filter-name>HelloFilter</filter-name
  4.      <filter-class>com.qy.filter.HelloFilter</filter-class> 
  5.      <init-param> 
  6.         <param-name>userName</param-name
  7.         <param-value>qianyu</param-value> 
  8.      </init-param> 
  9.       
  10.      <init-param> 
  11.         <param-name>password</param-name
  12.         <param-value>123</param-value> 
  13.      </init-param> 
  14.   </filter> 
  15.    
  16. 使用filterConfig来获取这个数据  
  17.  public void init(FilterConfig filterConfig) throws ServletException { 
  18.   System.out.println("init初始化执行了"); 
  19.   //获取fileter的初始化数据 
  20.   String userName=filterConfig.getInitParameter("userName"); 
  21.   String password=filterConfig.getInitParameter("password"); 
  22.    
  23.   //获取的所有参数的名字 
  24.   filterConfig.getInitParameterNames(); 
  25.   //获取的是application 
  26.   filterConfig.getServletContext(); 
  27.   //获取的是Filter的名字 
  28.   filterConfig.getFilterName(); 
  29.    
  30.   System.out.println("获取到的数据是:"+userName+"----"+password); 
  31.  }  
  32.    
  33. 过滤中器放行的方法是什么? 
  34.                chain.doFilter(request, response); 

5、代理设计模式

「解决的问题:」

  • 问题:代理设计模式到底解决了一个什么问题?
  1. 什么是设计模式?  设计模式不是技术、也不是什么框架、设计模式只是前人的一个工作的总结、在实现某一个功能的时候 怎样来减少代码之间的耦合性、以及如何实现高内聚低耦合。设计模式说白了就是按照一定的步骤来完成相应的一个功能 这个就称为设计模式 
  2.        代理的设计模式主要用来解决一个什么问题呢?  第一个:监控目录一个类里面方法的执行 第二个:在类里面某一个方法执行的前后 动态的植入代码 

「静态代理:」

  • 静态代理的使用前提:被代理的这个类必须要实现接口
  • 接口的玩法

 

编写被代理类


编写代理类


测试

「动态代理(JDK代理):」

  • 使用前提:被代理的类也必须实现接口
  1. 动态代理的实现原理:实际上也是生成了接口的实现类对象、然后在接口的实现类对象里面传入了 被代理的对象、在方法执行的时候使用了被代理类的方法去执行、实际上就跟静态代理差不多 区别是静态代理自己写的代 理类、动态代理是自动生成的这个代理类 
  • 被代理的类以及接口的实现和上面类似
  • 代理类的生成和实现
  1. IUserDAO userDAO=(IUserDAO)Proxy.newProxyInstance( 
  2. UserDAO.class.getClassLoader(), 
  3.               UserDAO.class.getInterfaces(), 
  4. new InvocationHandler() { 
  5.            /** 
  6.             * 第一个参数:代理对象 
  7.             * 第二个参数:当前要执行方法的一个封装 
  8.             * 第三个参数:执行方法要传递的这个参数 
  9.             */ 
  10.            @Override 
  11.      public Object invoke(Object proxy, Method method, Object[] args) 
  12.        throws Throwable { 
  13.            System.out.println("执行前添加的功能...."); 
  14.            Object result=method.invoke(new UserDAO(), args); 
  15.            System.out.println("执行后添加的功能...."); 
  16.       return result; 
  17.      } 
  18.     }); 

测试

  1. userDAO.save(); 

「cglib代理:」

  • 需求:假设一个类没实现接口?假设我们也需要监听这个类里面的方法的执行呢?
  1. cglib的使用场景:就是一个类如果没有实现接口  而且我们想在这个类的方法里面动态植入代码 那么这种情况下就可以使用cglib代理 
  • 首先导入cglib的包
  • 编写代理类
  1. public class UserDAOProxy { 
  2.  private UserDAO userDAO=null
  3.  public UserDAOProxy(UserDAO userDAO) { 
  4.   this.userDAO=userDAO; 
  5.  } 
  6.  
  7.  /** 
  8.   * 编写一个方法 用来返回代理类的对象 
  9.   * @Title: getObjProxy    
  10.   * @Description: TODO 
  11.   * @param: @return       
  12.   * @return: Object       
  13.   * @throws 
  14.   */ 
  15.  public Object getObjProxy(){ 
  16.   //这个对象就是用来返回代理类对象的方法 
  17.   Enhancer enhancer=new Enhancer(); 
  18.   //给代理了设置父亲 
  19.  enhancer.setSuperclass(userDAO.getClass()); 
  20.   //设置调用的回调 
  21.   enhancer.setCallback(new MethodInterceptor() { 
  22.    @Override 
  23.    public Object intercept(Object proxy, Method method, Object[] arg2, 
  24.      MethodProxy arg3) throws Throwable { 
  25.     System.out.println("植入了功能...."); 
  26.     Object objResult=method.invoke(userDAO, arg2); 
  27.     System.out.println("植入了功能....1111"); 
  28.     return objResult; 
  29.    } 
  30.   }); 
  31.   //生成代理类 
  32.   return enhancer.create(); 
  33.  } 

编写测试类

  1. @Test 
  2.    public void testCglibProxy() throws Exception { 
  3.    
  4.   //获取代理类的对象 
  5.   UserDAOProxy2 userDAOProxy=new UserDAOProxy2(new UserDAO()); 
  6.   //第二步:调用 
  7.   UserDAO userDAO=(UserDAO) userDAOProxy.getObjProxy(); 
  8.   //第三步 
  9.   userDAO.save(); 
  10.    } 

6、基于代理和Filter的综合案例

「编码处理的问题:」

原理

  1. 原理:过滤器技术拦截所有的controll的请求、在controll请求中使用了动态代理的设计模式监听了HttpServletRequest这个接口中getParameter方法的执行、在getParameter执行的时候、我们首先去获取这个数据、再通过判断当前的请求是GET还是POST、如果是GET那么先使用IOS-8859-1进行转码 然后使用UTF-8从新进行编码、如果是POST那么直接使用request.setCharacterEncoding(“UTF-8”)来进行处理 

字符编码处理的实现

  1.  public class CharacterFilter implements Filter{ 
  2.  @Override 
  3.  public void init(FilterConfig arg0) throws ServletException { 
  4.    
  5.  }   
  6.     /** 
  7.      * 拦截的这个方法 
  8.      */ 
  9.  @Override 
  10.  public void doFilter(ServletRequest request, ServletResponse response, 
  11.    final FilterChain chain) throws IOException, ServletException { 
  12.        
  13.       final HttpServletRequest req=(HttpServletRequest) request; 
  14.       final HttpServletResponse resp=(HttpServletResponse) response; 
  15.        
  16.       //第一步:将返回数据的编码问题给处理了 
  17.       resp.setContentType("text/html;charset=utf-8"); 
  18.       //POST的解决方案  
  19.       req.setCharacterEncoding("UTF-8"); 
  20.       //第二步:监听httpServletRequest中 getParameter方法的执行 
  21.       HttpServletRequest req1= (HttpServletRequest) Proxy.newProxyInstance(HttpServletRequest.class.getClassLoader(), 
  22.         new Class[]{HttpServletRequest.class}, 
  23.         new InvocationHandler() { 
  24.       @Override 
  25.       public Object invoke(Object proxy, Method method, Object[] args) 
  26.         throws Throwable { 
  27.           //监听当前执行的方法是不是 getParameter 
  28.           String methodName=method.getName(); 
  29.           if("getParameter".equals(methodName)){ //说明执行的是getParameter 
  30.           //判断当前执行的是POST呢?还是GET呢? 
  31.           String reqName=req.getMethod(); 
  32.            
  33.           //通过key获取这个值 
  34.           String val= (String) method.invoke(req, args); 
  35.           if("GET".equalsIgnoreCase(reqName)){ //说明是GET方法 
  36.            //执行这个方法获取这个值 
  37.            val=new String(val.getBytes("ISO-8859-1"),"UTF-8"); 
  38.             
  39.           }else if("POST".equalsIgnoreCase(reqName)){ //说明是POST方法 
  40.             
  41.           } 
  42.           //返回这个方法执行的结果 
  43.           return val;  
  44.           }else
  45.           return  method.invoke(req, args); 
  46.           } 
  47.          } 
  48.      }); 
  49.     
  50.      //最终要进行放行 
  51.      chain.doFilter(req1, resp); 
  52.  } 
  53.  @Override 
  54.  public void destroy() {  
  55.    
  56.  } 

「字符和谐的问题:」

  • 明白一个问题:什么是字符和谐:类似于博客网站上,比如你发表不文明语句不会直接显示出来,而是显示成***等这种现象就称为字符的和谐
  • 要实现字符和谐首先要解决编码问题(上面已经解决了)
  • 在过滤器中设置脏数据
  1. //需要和谐的脏数据 
  2.  private String[] dirtyData={"MMD","NND","GD","CTM"}; 

在处理完字符编码问题的时候进行和谐(在处理完编码之后进行调用)

  1. protected String handleDirtyData(String val) { 
  2.   for (int i = 0; i < dirtyData.length; i++) { 
  3.    if(val.contains(dirtyData[i])){ 
  4.     val=val.replaceAll(dirtyData[i],"***"); 
  5.    } 
  6.   } 
  7.   return val; 
  8.  } 

测试

结语

本篇关于过滤器Filter及代理Proxy的介绍就先到这里结束了,后续会出更多关于Filter和代理Proxy系列更多文章,谢谢大家支持!

 

责任编辑:姜华 来源: 浅羽的IT小屋
相关推荐

2020-07-10 07:58:14

Linux

2024-11-15 11:11:48

2024-06-13 09:10:22

2024-01-03 07:56:50

2022-05-18 09:01:19

JSONJavaScript

2023-05-10 07:15:22

Nacos管理工具配置

2022-12-01 08:15:41

CGOswitcherror

2022-12-07 09:00:18

错误异常CGO

2018-10-26 14:10:21

2012-07-27 13:36:00

Office操作系统

2023-05-08 15:25:19

Python编程语言编码技巧

2023-04-20 14:31:20

Python开发教程

2023-12-26 12:10:13

2022-08-19 10:31:32

Kafka大数据

2020-05-13 11:20:57

MySQL规范数据库

2024-07-26 08:32:44

panic​Go语言

2021-06-09 11:06:00

数据分析Excel

2024-06-19 10:08:34

GoChannel工具

2020-08-23 18:18:27

Python列表数据结构

2015-10-20 09:46:33

HTTP网络协议
点赞
收藏

51CTO技术栈公众号