写点什么

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

作者:浅羽技术
  • 2023-04-21
    四川
  • 本文字数:5829 字

    阅读完需:约 19 分钟

前言

过滤器 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 接口,并实现里面的方法

public class HelloFilter implements Filter{ /**  * 在Filter实例被创建的时候 初始化方法的时候执行的 这个一般用于Filter的初始化  * filterConfig:获取的是Filter的配置信息以及初始化信息的  */ @Override public void init(FilterConfig filterConfig) throws ServletException {  System.out.println("init初始化执行了"); }    /**     * 过滤器拦截的方法     * request:当前的请求对象的封装     * response:当前的响应的封装     * chain:判断是否拦截的方法     *      */ @Override public void doFilter(ServletRequest request, ServletResponse response,   FilterChain chain) throws IOException, ServletException {  System.out.println("doFilter执行了....");  //执行了这个方法那么就表示允许通过  没有执行那么就不允许通过  chain.doFilter(request, response);   }     /**     * 过滤器在死了之后 执行的这个方法     */ @Override public void destroy() {    System.out.println("destory执行了...."); }}
复制代码


点击并拖拽以移动
  • 在 web.xml 中配置 Filter

 <filter>     <filter-name>HelloFilter</filter-name>     <filter-class>com.qy.filter.HelloFilter</filter-class>  </filter>   <filter-mapping>     <filter-name>HelloFilter</filter-name>     <url-pattern>/*</url-pattern>  </filter-mapping>
复制代码


点击并拖拽以移动
  • 测试

  • 总结:Filter 的初始化方法是在程序启动的时候执行的

4、过滤器的生命周期

「生命周期:」

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

    public HelloFilter() {  System.out.println("类的对象被创建了...."); }
复制代码


点击并拖拽以移动
  • 创建完成之后紧接着调用 init 方法进行 Filter 的初始化

  @Override public void init(FilterConfig filterConfig) throws ServletException {  System.out.println("init初始化执行了"); }
复制代码


点击并拖拽以移动
  • 当前端有请求到来的时候,那么这个时候 doFilter 方法被执行

 @Override public void doFilter(ServletRequest request, ServletResponse response,   FilterChain chain) throws IOException, ServletException {  System.out.println("doFilter执行了....");  // 执行了这个方法那么就表示允许通过 没有执行那么就不允许通过  //放行  // chain.doFilter(request, response); }
复制代码


点击并拖拽以移动
  • 当 Web 容器死亡的时候,那么 destory 方法被执行

@Override public void destroy() {  System.out.println("destory执行了...."); }
复制代码


点击并拖拽以移动
  • init 中 filterConfig 如何使用

在web.xml中给filter添加初始数据 <filter>     <filter-name>HelloFilter</filter-name>     <filter-class>com.qy.filter.HelloFilter</filter-class>     <init-param>        <param-name>userName</param-name>        <param-value>qianyu</param-value>     </init-param>          <init-param>        <param-name>password</param-name>        <param-value>123</param-value>     </init-param>  </filter>  使用filterConfig来获取这个数据  public void init(FilterConfig filterConfig) throws ServletException {  System.out.println("init初始化执行了");  //获取fileter的初始化数据  String userName=filterConfig.getInitParameter("userName");  String password=filterConfig.getInitParameter("password");    //获取的所有参数的名字  filterConfig.getInitParameterNames();  //获取的是application  filterConfig.getServletContext();  //获取的是Filter的名字  filterConfig.getFilterName();    System.out.println("获取到的数据是:"+userName+"----"+password); }   过滤中器放行的方法是什么?               chain.doFilter(request, response);
复制代码


点击并拖拽以移动

5、代理设计模式

「解决的问题:」

  • 问题:代理设计模式到底解决了一个什么问题?

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


点击并拖拽以移动

「静态代理:」

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

  • 接口的玩法



点击并拖拽以移动

​编辑

  • 编写被代理类


点击并拖拽以移动

​编辑

  • 编写代理类


点击并拖拽以移动

​编辑

  • 测试

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

  • 使用前提:被代理的类也必须实现接口

动态代理的实现原理:实际上也是生成了接口的实现类对象、然后在接口的实现类对象里面传入了 被代理的对象、在方法执行的时候使用了被代理类的方法去执行、实际上就跟静态代理差不多 区别是静态代理自己写的代 理类、动态代理是自动生成的这个代理类
复制代码


点击并拖拽以移动
  • 被代理的类以及接口的实现和上面类似

  • 代理类的生成和实现

IUserDAO userDAO=(IUserDAO)Proxy.newProxyInstance(UserDAO.class.getClassLoader(),              UserDAO.class.getInterfaces(),new InvocationHandler() {           /**            * 第一个参数:代理对象            * 第二个参数:当前要执行方法的一个封装            * 第三个参数:执行方法要传递的这个参数            */           @Override     public Object invoke(Object proxy, Method method, Object[] args)       throws Throwable {           System.out.println("执行前添加的功能....");           Object result=method.invoke(new UserDAO(), args);           System.out.println("执行后添加的功能....");      return result;     }    });
复制代码


点击并拖拽以移动
  • 测试

userDAO.save();
复制代码


点击并拖拽以移动

「cglib 代理:」

  • 需求:假设一个类没实现接口?假设我们也需要监听这个类里面的方法的执行呢?

cglib的使用场景:就是一个类如果没有实现接口  而且我们想在这个类的方法里面动态植入代码 那么这种情况下就可以使用cglib代理
复制代码


点击并拖拽以移动
  • 首先导入 cglib 的包

  • 编写代理类

public class UserDAOProxy { private UserDAO userDAO=null; public UserDAOProxy(UserDAO userDAO) {  this.userDAO=userDAO; }
 /**  * 编写一个方法 用来返回代理类的对象  * @Title: getObjProxy     * @Description: TODO  * @param: @return        * @return: Object        * @throws  */ public Object getObjProxy(){  //这个对象就是用来返回代理类对象的方法  Enhancer enhancer=new Enhancer();  //给代理了设置父亲 enhancer.setSuperclass(userDAO.getClass());  //设置调用的回调  enhancer.setCallback(new MethodInterceptor() {   @Override   public Object intercept(Object proxy, Method method, Object[] arg2,     MethodProxy arg3) throws Throwable {    System.out.println("植入了功能....");    Object objResult=method.invoke(userDAO, arg2);    System.out.println("植入了功能....1111");    return objResult;   }  });  //生成代理类  return enhancer.create(); }}
复制代码


点击并拖拽以移动
  • 编写测试类

@Test   public void testCglibProxy() throws Exception {    //获取代理类的对象  UserDAOProxy2 userDAOProxy=new UserDAOProxy2(new UserDAO());  //第二步:调用  UserDAO userDAO=(UserDAO) userDAOProxy.getObjProxy();  //第三步  userDAO.save();   }
复制代码


点击并拖拽以移动

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

「编码处理的问题:」

  • 原理

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


点击并拖拽以移动
  • 字符编码处理的实现

 public class CharacterFilter implements Filter{ @Override public void init(FilterConfig arg0) throws ServletException {   }      /**     * 拦截的这个方法     */ @Override public void doFilter(ServletRequest request, ServletResponse response,   final FilterChain chain) throws IOException, ServletException {            final HttpServletRequest req=(HttpServletRequest) request;      final HttpServletResponse resp=(HttpServletResponse) response;            //第一步:将返回数据的编码问题给处理了      resp.setContentType("text/html;charset=utf-8");      //POST的解决方案       req.setCharacterEncoding("UTF-8");      //第二步:监听httpServletRequest中 getParameter方法的执行      HttpServletRequest req1= (HttpServletRequest) Proxy.newProxyInstance(HttpServletRequest.class.getClassLoader(),        new Class[]{HttpServletRequest.class},        new InvocationHandler() {      @Override      public Object invoke(Object proxy, Method method, Object[] args)        throws Throwable {          //监听当前执行的方法是不是 getParameter          String methodName=method.getName();          if("getParameter".equals(methodName)){ //说明执行的是getParameter          //判断当前执行的是POST呢?还是GET呢?          String reqName=req.getMethod();                    //通过key获取这个值          String val= (String) method.invoke(req, args);          if("GET".equalsIgnoreCase(reqName)){ //说明是GET方法           //执行这个方法获取这个值           val=new String(val.getBytes("ISO-8859-1"),"UTF-8");                     }else if("POST".equalsIgnoreCase(reqName)){ //说明是POST方法                     }          //返回这个方法执行的结果          return val;           }else{          return  method.invoke(req, args);          }         }     });        //最终要进行放行     chain.doFilter(req1, resp); } @Override public void destroy() {    }}
复制代码


点击并拖拽以移动

「字符和谐的问题:」

  • 明白一个问题:什么是字符和谐:类似于博客网站上,比如你发表不文明语句不会直接显示出来,而是显示成***等这种现象就称为字符的和谐

  • 要实现字符和谐首先要解决编码问题(上面已经解决了)

  • 在过滤器中设置脏数据

//需要和谐的脏数据 private String[] dirtyData={"MMD","NND","GD","CTM"};
复制代码


点击并拖拽以移动
  • 在处理完字符编码问题的时候进行和谐(在处理完编码之后进行调用)

protected String handleDirtyData(String val) {  for (int i = 0; i < dirtyData.length; i++) {   if(val.contains(dirtyData[i])){    val=val.replaceAll(dirtyData[i],"***");   }  }  return val; }
复制代码


点击并拖拽以移动
  • 测试

结语

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


发布于: 刚刚阅读数: 6
用户头像

浅羽技术

关注

才疏学浅,习习而为,编程羽录,与你同行。 2019-02-26 加入

分享一些计算机信息知识、理论技术、工具资源、软件介绍、后端开发、面试、工作感想以及生活随想等一系列文章。

评论

发布
暂无评论
组件必知必会|那些年我们使用过的轮子—Filter和Proxy_javaWeb_浅羽技术_InfoQ写作社区