1、概念
代理模式将调用类方法的操作转换为调用代理类的方法,这样做的目的是:
- 实现了”按需调用”,它和延迟加载类的思想是一致的。以此达到性能的提升。
- 调用代理类方法时,可以在原功能之前添加一些功能,之后加入一些功能。例如记录类方法的执行时间。
第一种情况下,代理模式的本质是延迟原方法的调用时间,实现”按需调用”。此时原方法具备的特点是比较耗时,代理模式的目的是提升性能。
第二种情况下,代理模式的本质是拦截原方法,在其之前添加一些功能性代码,在其之后添加一些功能性代码。它与面向切面的思想是一致的,spring的AOP功能就是通过代理模式实现的。
它的解决方案是:
提供额外的代理类,第一种情况时,代理类的职责是执行耗时的功能。第二种情况时,代理类的职责是抽象公共,通用,而且较为重要的功能,这些功能包括日志,安全监测等等。
它的实现方式有两种,静态代理和动态代理,区别在于代理类的创建时机,类之间的关系,代理类方法触发原类方法的方式。
2、UML图
2.1 静态代理
2.2 动态代理(JDK方式)
3、代码
3.1 静态代理
接口,它扮演着被代理的角色
/** * @File Name: RealInterface.java * @Description: 接口类 * @version 1.0 * @since JDK 1.8 */ public interface RealInterface { /** * * @Title: work * @Description:实现某个特定的功能 */ void work(); }
接口实现类
/** * @File Name: RealInterfaceImpl.java * @Description: 接口实现类 * @version 1.0 * @since JDK 1.8 */ public class RealInterfaceImpl implements RealInterface { @Override public void work() { } }
代理类,扮演着代理的角色。它与被代理的接口是实现,组合的关系。
/** * @File Name: ProxyObject.java * @Description: 代理类 * @version 1.0 * @since JDK 1.8 */ public class ProxyObject implements RealInterface { // 被代理的接口类 private RealInterface realInterface; @Override public void work() { realInterface.work(); } }
3.2 动态代理
MyInvocationHandler,它实现InvocationHandler接口,只有一个方法invoke,它的核心作用是触发原方法的调用。它相当于静态代理方式中ProxyObject中的work方法。
/** * * @File Name: MyInvocationHandler.java * @Description: InvocationHandler接口实现类 * @version 1.0 * @since JDK 1.8 */ public class MyInvocationHandler implements InvocationHandler{ private RealInterface real = new RealInterfaceImpl(); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(real, args); } }
原接口RealInterface,与静态代理方式的代码相同,此处略。
动态生成的代理类Proxy$0,如果想查看该类,需要设置sun.misc.ProxyGenerator.saveGeneratedFiles变量为true。它是一个JVM系统变量。
public final class $Proxy0 extends Proxy implements RealInterface { private static Method m1; // m3代表被代理接口中的方法,其他变量代表equals,hashCode等 private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } // 略 public final boolean equals(Object var1); /** * 创建与原接口的同名方法, * 它与InvocationHandler的关系是构造器依赖关系 * 它与代理Interface产生相同的方法 * InvocationHandler与被代理的Interface之间关系为set依赖关系,与被代理方法Method,Proxy对象是方法参数依赖关系 */ public final void work() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } // 略 public final String toString(); // 略 public final int hashCode(); static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("designpattern.proxy.RealInterface").getMethod("work"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
4、讨论
1. 问题1:代理模式的主要功能?
答:代理模式的主要功能是将原方法的调用转换为代理方法的调用。代理方法与原方法的关系在第一种情况下是”按需调用”,第二种情况下是增强原方法的功能。这些功能通常都是公共的,并且是较为重要的。
2. 问题2:代理模式的实现方式有哪些,以及它们的区别?
答:代理模式有两种,静态代理和动态代理,动态代理目前有两种技术,一种是JDK,另一种是CGLib。
静态代理和动态代理在本质上没有区别,都是代理方法替换原方法,并在代理方法中触发原方法的调用。
二者的区别在于代理类的创建方式和代理类之间的关系上。
- 静态代理的代理类是由我们手动创建的,而动态代理是由JVM动态产生的。
- 静态代理的代理类和被代理接口之间的关系是实现,依赖的关系。而在动态代理时,代理类与被代理接口之间的关系还是实现关系,代理类依赖原方法对应的Metthod对象。触发原方法的方式也变为super.h.invoke方法,将Method对象作为该方法的参数,在其内部调用Method对象的invoke方法。
3. 问题3:代理模式类之间的关系?
答:静态代理,ProxyObject与RealObject实现相同的接口,并且依赖RealObject。代理类触发原方法的方式是通过RealObject对象的实例去调用。
动态代理,Proxy与XXInterface之间的关系为实现关系,静态代理的依赖关系转变为Proxy与Method之间的依赖关系。Proxy与InvocationHandler是依赖关系,是通过构造器的方式注入的。InvocationHandler与XXInterface之间的关系为依赖关系,是通过Set的方式注入的。
抛开复杂的类关系,掌握代理方法与原方法之间的关系会更容易理解代理模式。
代理类是提供代理方法的,总是有触发原发法的实现机制,不论是通过组合被代理接口的实现类,还是组合原方法的Method对象。
5、示例
- Spring框架中作用域为singleton的bean依赖作用域为prototype的bean时。想达到每次获取依赖bean都是不同实例时。
- Spring框架中AOP的实现。