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

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不在乱码。