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的实现。