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


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

    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. 动态代理

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

      代理的概念.png

    • 动态代理的条件:必须知道要被代理的类/对象是谁,这里要被代理的类是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不在乱码。

  • 相关阅读:
    7. 输油管道问题
    6. 循环赛日程表
    4.JSP内置对象
    3.JSP
    2.CSS
    1.HTML
    5. 逆序对数
    [转]Android View.onMeasure方法的理解
    [转]android:clipToPadding和android:clipChildren
    [转]倍数提高工作效率的 Android Studio 奇技
  • 原文地址:https://www.cnblogs.com/itzhouq/p/proxy1.html
Copyright © 2020-2023  润新知