面试题增强一个对象的方法的三种方式

9次阅读

共计 6534 个字符,预计需要花费 17 分钟才能阅读完成。

面试题:增强一个对象的方法的三种方式

1. 继承

  • 使用这种方式必须满足的条件是:被增强的方法的所在类能被继承,并且这个对象已经明确知道。
  • 举例:
  • 有一个接口 Person,里面有一个方法 run()

    package com.itzhouq.demo1;
    
    public interface Person {public void run();
    }
  • 类 NormalPerson 实现了这个接口 Person

    package com.itzhouq.demo1;
    
    public class NormalPerson implements Person {
    
        @Override
        public void run() {System.out.println("走.......");
        }
    }
  • 现在的需求是,使用继承方式增强 NomalPerson 中的方法 run()
  • 这里需要被增强的方法是 run(),所在的类 NomalPerson 可以被继承并且已经明确。
  • 所以创建一个类 Superson 继承 NormalPerson

    package com.itzhouq.demo1;
    
    public class Superson extends NormalPerson {
        // 重写了父类 NormalPerson 的方法
        @Override
        public void run() {super.run();
            System.out.println("增强了,变成飞了。。。");
        }
    }
  • 类 Superson 通过对父类 NormalPerson 的 run() 方法进行重写,实现对 run() 方法的增强。
  • 测试
package com.itzhouq.demo1;
  
  import org.junit.Test;
  
  /*
   * 增强一个对象的方法之一:继承方式
   */
  public class Demo {
      
      @Test
      public void test() {NormalPerson p = new NormalPerson();
          p.run();// 走.......}
      
      // 需求:对普通人的 run 方法进行增强,由走变成飞 ---- 增强一个对象的方法
      // 用继承来实现需求:创建一个类继承 NormalPerson
      @Test
      public void test2() {Superson superson = new Superson();
          superson.run();
  //        走.......
  //        增强了,变成飞了。。。}
      
  }

2. 装饰者模式

  • 装饰者模式实现对方法的增强,不需要知道被增强的方法 run() 所在的类是哪个类,只需要知道这个类实现了哪个接口即可。
  • 条件:

    • 装饰者和被装饰者需要实现同一个类
    • 装饰者有被装饰者的引用
  • 接口:

    package com.itzhouq.demo2;
    
    public interface Person {public void run();
    }
  • 需要被增强的方法 run()

    package com.itzhouq.demo2;
    
    public class NormalPerson implements Person {
    
        @Override
        public void run() {System.out.println("走.......");
        }
    }
  • 这里被装饰者就是 run() 方法所在的类
  • 创建一个装饰者类,实现 run() 所在类,实现的接口 Person

    package com.itzhouq.demo2;
    
    public class Superson implements Person {
        // 被装饰者的引用
        private NormalPerson p;
        public Superson(NormalPerson p) {this.p = p;}
        
        @Override
        public void run() {
            // 这个是被装饰者以前的方法
            p.run();
            // 增强
            System.out.println("增强了,变成飞。。。。");
        }
    }
  • 测试
package com.itzhouq.demo2;
  
  import org.junit.Test;
  /*
   * 增强一个对象的方法之二:装饰者方式
   */
  public class Demo {
      
      @Test
      public void test() {NormalPerson p = new NormalPerson();
          p.run();// 走.......}
      
      // 需求:对普通人的 run 方法进行增强,由走变成飞
      // 假装不知道接口的实现类 NormalPerson,但是要对普通人的 run 方法进行增强
      // 不知道实现类就无法使用继承的方式进行增强
      // 使用装饰者解决这样的问题:// 条件 1:装饰者()和被装饰者()实现同一个接口 Person
          // 条件 2:装饰者里面有被装饰者的引用         在我出生的时候,你把你给我,我对你进行增强
      
      @Test
      public void test2() {NormalPerson p = new NormalPerson();
          Superson superson = new Superson(p);
          superson.run();
  //        走.......
  //        增强了,变成飞。。。。}
  }

3. 动态代理

  • 通过一张图回顾动态代理

  • 动态代理的条件:必须知道要被代理的类 / 对象是谁,这里要被代理的类是 NoemalPerson

    Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interface, InvocationHander h);
    // 返回一个指定接口的代理类实现 
  • 接口 person,这里再加一个方法 sleep

    package com.itzhouq.demo3;
    
    public interface Person {public void run();
        public String sleep();}
  • 实现类 NomalPerson

    package com.itzhouq.demo3;
    
    public class NormalPerson implements Person {
    
        @Override
        public void run() {System.out.println("走.......");
        }
    
        @Override
        public String sleep() {System.out.println("睡觉了。。。");
            return "sleep";
        }
    }
  • 使用动态代理增强
package com.itzhouq.demo3;
  
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  import java.lang.reflect.Proxy;
  
  import org.junit.Test;
  
  public class Demo {
      @Test
      public void test() {NormalPerson p = new NormalPerson();
          p.run();// 走.......}
      
      // 需求:使用动态代理的方式对普通人进行增强
      //JDK 提供的类和方法可以给咱们动态的生成代理对象 / 增强对象
      
      /*
       * 参数概述:固定的
       *     参数 1:和要被增强的对象,一样的,类加载器
       *     参数 2:和要被增强的对象一样的接口
       *             1 根据指定的传递接口返回一个该接口下的实例
       *             2 传递的接口里面的方法就是可以被增强的所有方法
       *     参数 3:所有的增强业务的逻辑实现(方法)*/
      @Test
      public void test1() {NormalPerson p = new NormalPerson();
          Person proxyPerson = (Person) Proxy.newProxyInstance(p.getClass().getClassLoader(), 
                  p.getClass().getInterfaces(),
                  new InvocationHandler() {
                      
                      /*
                       *     参数概述:固定的
                       *     参数 1:不用管,永远是固定值     代理对象的类型
                       *     参数 2:要被增强的方法
                       *     参数 3:要被增强的方法运行过程中需要的参数
                       */
                      @Override    //invoke 里面是所有的增强业务的逻辑代码
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          // 让以前的方法执行
                              // 参数 1:本身应该执行这个方法的对象
                              // 参数 2:执行这个方法需要的参数
                          Object value = method.invoke(p, args);
                          // 原来方法的返回值
                          System.out.println(value);
                          // 写增强业务逻辑
                          System.out.println("增强了,变成飞了。。。");
                          // 最终的返回值,谁调用返回给谁
                          return "abcd";
                      }
                  });
          proxyPerson.run();// 执行接口中的每一个需要增强的方法,invoke 都会执行一遍,执行的内容就是针对该方法的增强
  //        走.......
  //        增强了,变成飞了。。。String value = proxyPerson.sleep();
          System.out.println(value);
  //        睡觉了。。。//        sleep
  //        增强了,变成飞了。。。//        abcd
      }
  }

4. 扩展:使用动态代理方式统一字符集编码

  • 新建 Web 项目,新建一个 index.jsp。在 JSP 中写两个表单,分别为 post 和 get 方式提交。后台通过 Servlet 接收到前台输入的 username,打印在控制台会乱码。
  • JSP

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        <form action="${pageContext.request.contextPath}/sd1" method="get">
            用户名:<input type="text" name="username">
            <input type="submit" value="提交">
        </form>
        
        <hr>
        
        <form action="${pageContext.request.contextPath}/sd1" method="post">
            用户名:<input type="text" name="username">
            <input type="submit" value="提交">
        </form>
    </body>
    </html>
  • Servlet

    package com.itzhouq.web;
    
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class ServletDemo1 extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String username = request.getParameter("username");
            System.out.println(username);
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);
        }
    }
  • 为解决这个问题使用过滤器,在过滤器中使用动态代理方式解决
  • 新建一个过滤器 MyFilter.java,并在 xml 文件中配置过滤的资源为全部资源
  • web.xml

    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.itzhouq.filter.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  • MyFilter

    package com.itzhouq.filter;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    
    public class MyFilter implements Filter {public MyFilter() { }
        public void destroy() {}
    
        public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            // 要增强的方法:request.getparameter
            // 被代理的对象:request
            HttpServletRequest request = (HttpServletRequest)req;
            
            // 动态的生成代理对象
            HttpServletRequest hsr = (HttpServletRequest) Proxy.newProxyInstance(request.getClass().getClassLoader(), 
                    request.getClass().getInterfaces(), 
                    new InvocationHandler() {
                        
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //1. 判断是否是要增强的方法 getParameter
                            if("getParameter".equals(method.getName())) {
                                // 知道 getParameter 使用的是哪个提交方式
                                String m = request.getMethod();
                                // 判断是 get 还是 post
                                if("get".equalsIgnoreCase(m)) {
                                    //    以前方法调用后的乱码
                                    String s = (String)method.invoke(request, args);
                                    // 增强 --- 解决乱码
                                    s = new String(s.getBytes("iso8859-1"),"utf-8");
                                    return s;
                                }
                                if("post".equalsIgnoreCase(m)) {request.setCharacterEncoding("utf-8");
                                    return method.invoke(request, args);
                                }
                            }
                            
                            //    如果是别的方法
                            return method.invoke(request, args);
                        }
                    });
            
            chain.doFilter(hsr, response);
        }
    
        public void init(FilterConfig fConfig) throws ServletException {}}
  • 后台 Servlet 接收到的 username 不在乱码。
正文完
 0