• Java设计模式之代理模式(一)


    什么是代理?可以理解为其他对象提供一种代理以控制对这个对象的访问。

    举个例子,生活中的外卖平台,店铺是制作外卖的,然后放到平台上售卖。这里的店铺就是真实角色,为了能够让店铺不用担心销售等问题,从而能够更加专注做外卖,所以店铺的外卖都会放到平台上面。这里平台就是代理。平台代店家出售,而店家只需要做外卖就好了。

    代理模式和装饰器模式的区别:装饰器模式为了增强目标对象的功能,而代理模式是为了代理或增强目标对象的功能,本质上两者都可以为目标对象添加一些功能。

    代理模式可以分为静态代理和动态代理。

    一、静态代理

    静态代理:程序在运行之前需要手动创建代理类,代理类与目标类实现同一个接口。代理类为目标类增加一些额外的功能。

    1、创建接口:

    1. package com.statics.proxy.interfaces;
    2. /**
    3. * @Author: 倚天照海
    4. * @Description:
    5. */
    6. public interface HotelInterface {
    7. public void cook();
    8. }

    2、创建实现类

    1. package com.statics.proxy.impl;
    2. import com.statics.proxy.interfaces.HotelInterface;
    3. /**
    4. * @Author: 倚天照海
    5. * @Description:
    6. */
    7. public class HuangMenJi implements HotelInterface {
    8. @Override
    9. public void cook() {
    10. System.out.println("黄焖鸡米饭很好吃!");
    11. }
    12. }

    3、创建代理类

    1. package com.statics.proxy.impl;
    2. import com.statics.proxy.interfaces.HotelInterface;
    3. /**
    4. * @Author: 倚天照海
    5. * @Description:
    6. */
    7. public class MeiTuan implements HotelInterface {
    8. private HuangMenJi huangMenJi;
    9. public MeiTuan(HuangMenJi huangMenJi) {
    10. this.huangMenJi = huangMenJi;
    11. }
    12. @Override
    13. public void cook() {
    14. discount();
    15. huangMenJi.cook();
    16. pay();
    17. }
    18. private void discount(){
    19. System.out.println("5折优惠,量大实惠,欢迎来购");
    20. }
    21. private void pay(){
    22. System.out.println("快捷付款,一键支付");
    23. }
    24. }

    4、创建测试类

    1. package com.statics.proxy;
    2. import com.statics.proxy.impl.HuangMenJi;
    3. import com.statics.proxy.impl.MeiTuan;
    4. /**
    5. * @Author: 倚天照海
    6. * @Description:
    7. */
    8. public class ProxyTest {
    9. public static void main(String[] args) {
    10. HuangMenJi huangMenJi = new HuangMenJi();
    11. MeiTuan meiTuan = new MeiTuan(huangMenJi);
    12. meiTuan.cook();
    13. }
    14. }

    输出结果:

    二、JDK动态代理

    动态代理是一种在运行时动态地创建代理对象并调用代理方法的机制。动态代理不需要定义代理类的.java源文件,在程序运行时由JVM根据反射机制动态的创建代理类对象。动态代理其实就是jdk运行期间,动态创建class字节码并加载到JVM。

    动态代理的优点是提高了代码的灵活性和扩展性,具有解耦的意义。缺点是生成代理对象和调用代理方法需要耗费时间。

    动态代理主要有两种实现方式:JDK动态代理和CGLIB动态代理

    JDK动态代理其实是通过反射机制实现的。

    使用JDK动态代理主要有以下几个步骤:

    1.编写一个被代理的接口和接口实现类;

    2.自定义一个调用处理器,并实现InvocationHandler接口,重写invoke方法;

    3.在invoke方法中通过反射调用被代理方法(即上面接口实现类中的方法);

    4.创建接口实现类的实例,并将该实例作为参数来创建自定义调用处理器的实例;

    5.通过Proxy类的静态方法newProxyInstance获取代理对象(实现接口),并通过代理对象调用接口中的目标方法(实际是代理对象中重写后的目标方法)。

    InvocationHandler是proxy代理实例调用处理程序实现的一个接口,每一个proxy代理实例都与一个调用处理器进行关联,在代理实例调用接口方法时,方法调用会被转发到调用处理器的invoke方法,即代理对象调用接口方法时,在内部会调用InvocationHandler的invoke方法。

    Proxy类就是用来创建一个代理对象的类,其中最常用的方法是newProxyInstance静态方法。这个方法的作用就是创建一个代理类对象,它接收三个参数,三个参数的含义如下所示:

    loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载。

    interfaces:一个interface对象数组,表示给代理对象提供一组接口对象,也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。

    h:一个InvocationHandler对象,表示的是当动态代理对象调用接口方法时,会关联到指定的InvocationHandler对象上,并调用指定的InvocationHandler的invoke方法。

    示例代码:

    被代理的接口及实现类:

    1. package com.tx.study.others.proxy.jdkProxy;
    2. /**
    3. * @Author: 倚天照海
    4. */
    5. public interface Manager {
    6. public void modify();
    7. }
    8. package com.tx.study.others.proxy.jdkProxy;
    9. /**
    10. * @Author: 倚天照海
    11. */
    12. public class ManagerImpl implements Manager {
    13. @Override
    14. public void modify() {
    15. System.out.println("*******实现类的modify()方法被调用*********");
    16. }
    17. }

    自定义的调用处理器

    1. package com.tx.study.others.proxy.jdkProxy;
    2. import java.lang.reflect.InvocationHandler;
    3. import java.lang.reflect.Method;
    4. /**
    5. * @Author: 倚天照海
    6. */
    7. public class ManagerInvocationHandler implements InvocationHandler {
    8. private Object object = null;
    9. public ManagerInvocationHandler(Object object) {
    10. this.object = object;
    11. }
    12. /**
    13. * 重写了InvocationHandler接口中的invoke方法(该方法在何处被调用?)
    14. * @param proxy 代理对象
    15. * @param method 被代理类中的被代理方法的Method对象
    16. * @param args 被代理方法的参数
    17. * @return 被代理方法的返回值,如果被代理方法是被void修饰的,则返回null
    18. * @throws Throwable 可能会抛出的异常
    19. */
    20. @Override
    21. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    22. //在真实的对象执行之前可以添加自己的操作
    23. System.out.println("do something before method");
    24. //通过反射调用被代理方法,即接口实现类中的方法
    25. Object ret = method.invoke(this.object, args);
    26. //在真实的对象执行之后可以添加自己的操作
    27. System.out.println("do something after method");
    28. return ret;
    29. }
    30. }

    获取代理对象并调用方法

    1. package com.tx.study.others.proxy.jdkProxy;
    2. import java.lang.reflect.Proxy;
    3. /**
    4. * @Author: 倚天照海
    5. */
    6. public class JdkProxyTest {
    7. public static void main(String[] args) {
    8. //原对象(被代理对象)
    9. Manager managerImpl = new ManagerImpl();
    10. //业务代理类
    11. ManagerInvocationHandler businessHandler = new ManagerInvocationHandler(managerImpl);
    12. //获得代理类($Proxy0 extends Proxy implements Manager)的实例
    13. Manager managerProxy = (Manager) Proxy.newProxyInstance(
    14. managerImpl.getClass().getClassLoader(),
    15. managerImpl.getClass().getInterfaces(),
    16. businessHandler);
    17. //通过代理对象调用代理类中重写的接口方法(重写的invoke方法在此处被调用)
    18. managerProxy.modify();
    19. }
    20. }

    执行结果:

    1. do something before method
    2. *******实现类的modify()方法被调用*********
    3. do something after method

    拓展:生成jdk动态代理类的源码:

    1. package com.tx.study.others.proxy.jdkProxy;
    2. import sun.misc.ProxyGenerator;
    3. import java.io.*;
    4. /**
    5. * @Author: 倚天照海
    6. * @Description: 生成jdk动态代理类的源码
    7. */
    8. public class JdkProxySourceCodeUtil {
    9. public static void main(String[] args) {
    10. //指定生成的代理类名称
    11. String proxyClassName = "$proxy0";
    12. Class interfaceClass = Manager.class;
    13. //指定保存代理类的路径
    14. String path = "D:\\ProgramFiles\\workspace\\myself\\data-query\\tx-study\\src\\main\\java\\com\\tx\\study\\others\\proxy\\jdkProxy\\";
    15. path = path.concat(proxyClassName).concat(".class");
    16. writeClassToFile(proxyClassName,interfaceClass,path);
    17. }
    18. private static void writeClassToFile(String proxyClassName, Class interfaceClass, String path){
    19. byte[] proxyClass = ProxyGenerator.generateProxyClass(proxyClassName, new Class[]{interfaceClass});
    20. OutputStream os = null;
    21. try {
    22. os = new FileOutputStream(new File(path));
    23. os.write(proxyClass);
    24. os.flush();
    25. } catch (IOException e) {
    26. e.printStackTrace();
    27. } finally {
    28. try {
    29. if (os != null){
    30. os.close();
    31. }
    32. } catch (IOException e) {
    33. e.printStackTrace();
    34. }
    35. }
    36. }
    37. }

    由上可知,Proxy类中的newProxyInstance静态方法被调用,该方法返回一个代理对象,然后向上转型为其对应的接口。接下来简单看一下Proxy类中的部分源码(如下),主要看一下静态方法newProxyInstance。在该方法中调用getProxyClass0(loader,intfs)方法,根据类加载器和接口数组获取动态代理类的字节码文件对象,进而获取构造器对象,最后执行cons.newInstance(new Object[]{h})(根据指定的调用处理器对InvocationHandler进行初始化),通过反射调用有参构造器创建代理对象,并返回该代理对象。

    1. public class Proxy implements java.io.Serializable {
    2. private static final long serialVersionUID = -2222568056686623797L;
    3. /** parameter types of a proxy class constructor */
    4. private static final Class[] constructorParams =
    5. { InvocationHandler.class };
    6. /** a cache of proxy classes */
    7. private static final WeakCache[], Class>
    8. proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    9. /** the invocation handler for this proxy instance. */
    10. protected InvocationHandler h;
    11. private Proxy() {
    12. }
    13. //有参构造器创建代理对象(为InvocationHandler初始化)
    14. protected Proxy(InvocationHandler h) {
    15. Objects.requireNonNull(h);
    16. this.h = h;
    17. }
    18. @CallerSensitive
    19. public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException {
    20. Objects.requireNonNull(h);
    21. final Class[] intfs = interfaces.clone();
    22. final SecurityManager sm = System.getSecurityManager();
    23. if (sm != null) {
    24. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    25. }
    26. /* Look up or generate the designated proxy class. */
    27. //根据类加载器和接口数组获取动态代理类的字节码文件对象
    28. Class cl = getProxyClass0(loader, intfs);
    29. /* Invoke its constructor with the designated invocation handler. */
    30. try {
    31. if (sm != null) {
    32. checkNewProxyPermission(Reflection.getCallerClass(), cl);
    33. }
    34. //通过字节码文件对象获取构造器对象
    35. final Constructor cons = cl.getConstructor(constructorParams);
    36. final InvocationHandler ih = h;
    37. if (!Modifier.isPublic(cl.getModifiers())) {
    38. AccessController.doPrivileged(new PrivilegedAction() {
    39. public Void run() {
    40. cons.setAccessible(true);
    41. return null;
    42. }
    43. });
    44. }
    45. //获取代理对象(通过反射调用有参构造器创建对象)
    46. return cons.newInstance(new Object[]{h});
    47. } catch (IllegalAccessException|InstantiationException e) {
    48. throw new InternalError(e.toString(), e);
    49. } catch (InvocationTargetException e) {
    50. Throwable t = e.getCause();
    51. if (t instanceof RuntimeException) {
    52. throw (RuntimeException) t;
    53. } else {
    54. throw new InternalError(t.toString(), t);
    55. }
    56. } catch (NoSuchMethodException e) {
    57. throw new InternalError(e.toString(), e);
    58. }
    59. }
    60. //根据类加载器和接口数组获取动态代理类的字节码文件对象
    61. private static Class getProxyClass0(ClassLoader loader, Class... interfaces) {
    62. if (interfaces.length > 65535) {
    63. throw new IllegalArgumentException("interface limit exceeded");
    64. }
    65. // If the proxy class defined by the given loader implementing the given interfaces exists,
    66. // this will simply return the cached copy; otherwise, it will create the proxy class
    67. // via the ProxyClassFactory
    68. return proxyClassCache.get(loader, interfaces);
    69. }
    70. //省略了Proxy类中的其他内部类和方法
    71. }

    在自定义调用处理器中重写了InvocationHandler接口中的invoke方法,但是,该方法在何处被调用?接下来看一下本例的$Proxy0类的源码。

    下面是本例的代理类$Proxy0的源码:

    1. import java.lang.reflect.InvocationHandler;
    2. import java.lang.reflect.Method;
    3. import java.lang.reflect.Proxy;
    4. import java.lang.reflect.UndeclaredThrowableException;
    5. public final class $Proxy0 extends Proxy implements Manager {
    6. private static Method m1;
    7. private static Method m0;
    8. private static Method m3;
    9. private static Method m2;
    10. static {
    11. try {
    12. m1 = Class.forName("java.lang.Object").getMethod("equals",new Class[] { Class.forName("java.lang.Object") });
    13. m0 = Class.forName("java.lang.Object").getMethod("hashCode",new Class[0]);
    14. m3 = Class.forName("com.tx.study.others.proxy.jdkProxy.Manager").getMethod("modify",new Class[0]);
    15. m2 = Class.forName("java.lang.Object").getMethod("toString",new Class[0]);
    16. } catch (NoSuchMethodException nosuchmethodexception) {
    17. throw new NoSuchMethodError(nosuchmethodexception.getMessage());
    18. } catch (ClassNotFoundException classnotfoundexception) {
    19. throw new NoClassDefFoundError(classnotfoundexception.getMessage());
    20. }
    21. } //静态代码块结束
    22. //在代理类$Proxy0的构造方法中调用父类Proxy的构造方法,初始化InvocationHandler
    23. public $Proxy0(InvocationHandler invocationhandler) {
    24. super(invocationhandler);
    25. }
    26. @Override
    27. public final boolean equals(Object obj) {
    28. try {
    29. return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
    30. } catch (Throwable throwable) {
    31. throw new UndeclaredThrowableException(throwable);
    32. }
    33. }
    34. @Override
    35. public final int hashCode() {
    36. try {
    37. return ((Integer) super.h.invoke(this, m0, null)).intValue();
    38. } catch (Throwable throwable) {
    39. throw new UndeclaredThrowableException(throwable);
    40. }
    41. }
    42. //自定义的InvocationHandler中重写的invoke方法就是在此处被调用的
    43. public final void modify() {
    44. try {
    45. super.h.invoke(this, m3, null);
    46. return;
    47. } catch (Error e) {
    48. } catch (Throwable throwable) {
    49. throw new UndeclaredThrowableException(throwable);
    50. }
    51. }
    52. @Override
    53. public final String toString() {
    54. try {
    55. return (String) super.h.invoke(this, m2, null);
    56. } catch (Throwable throwable) {
    57. throw new UndeclaredThrowableException(throwable);
    58. }
    59. }
    60. }

    本例的代理类$Proxy0继承了Proxy类,并实现了Manager接口(上面编写的需要被代理的接口),所以,在$Proxy0类中也实现了Manager接口中的modify()方法。在$Proxy0类中通过反射获取到了实现的modify()方法(由于此例中Manager接口中只有modify一个方法,所以就获取到了modify这一个方法,如果Manager接口中有多个方法,那么在$Proxy0类中通过反射将获取所有的实现方法),即Method m3方法。在$Proxy0类的modify方法中调用父类Proxy中h的invoke()方法,即InvocationHandler.invoke()方法。由于自定义调用处理器实现了InvocationHandler接口,并重写了invoke方法,由多态性可知,实际上调用的是重写后的invoke方法。所以,在上面的例子中,当创建了代理对象managerProxy,并通过代理对象调用managerProxy.modify()方法时,实际上是调用代理类$Proxy0中重写的modify方法,并在modify方法中调用了自定义调用处理器中重写的invoke方法。因此,自定义调用处理器中重写的invoke方法是在执行managerProxy.modify()方法时被调用的

  • 相关阅读:
    docker容器时间和宿主机时间不一致
    网站框架识别方法
    分享一个基于Python和Django的产品销售收入数据分析系统源码
    SpringBean生命周期&扩展接口&简化配置
    【C++系列】STL容器——vector类的例题应用(12)
    C#鼠标穿透功能(WinForm)
    Qt中QPropertyAnimation动画效果展示
    线性表--栈-1
    基于球向量的粒子群优化(SPSO)算法在无人机路径规划中的实现(Matlab代码实现)
    uni-app小程序使用DCloud(插件市场)流程
  • 原文地址:https://blog.csdn.net/cxbtdd/article/details/143275067