面试题:增强一个对象的方法的三种方式
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不在乱码。
原文地址:https://www.cnblogs.com/itzhouq/p/proxy1.html
时间: 2024-10-10 14:34:37