版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习!java
有这样一种场景,有一个实现类实现了某种功能,这个实现类咱们没法修改或者不容许被修改,可是除了实现类里的功能咱们还须要扩展其余的功能,这种状况时咱们该怎么办呢?咱们能够建立一个代理类,在代理类里调用实现类里的功能而且在代理类中扩展咱们须要的功能,客户端直接调用代理类而不须要关心实现类,这就是代理模式的思想。简单来讲,代理模式的本质就是建立一个代理,代理类在原有类的行为基础上再加上一个额外的行为,甚至是替换掉原有类的行为,返回一个代理供客户端调用。spring
举一个生活中常见的例子,咱们平时去房产中介公司租房,房产中介提供出租房子的功能,可是房产中介自己并无房子,房子是房东委托给中介公司,受权给他出租,租房者不须要关心房东的信息或者说没法得到房东的信息,只须要直接向房产中介获取租房信息。可能这个例子不是很恰当,可是它反映了代理模式的思想。房东具备出租房屋的功能,房东直接跟中介交互并把这种功能委托给中介,所以中介具备了出租房屋的功能,而且额外加入收取中介费的功能,租房者直接跟中介交互经过中介得到房东出租房屋的功能。数组
代理模式中有如下几个角色:框架
1)抽象角色:声明真实对象和代理对象的共同接口;学习
2)代理角色:代理角色内部有对真实角色的引用,所以能够操做真实对象的功能;代理对象和真实对象实现相同的接口,以便在任什么时候候均可以代替真实对象;代理对象能够在执行真实对象的操做时,加上一些额外的操做,至关于对真实对象进行封装;this
3)真实角色:代理角色所表明的对象,是咱们最终要操做的对象。spa
在上面的举例中,抽象角色就是出租房屋这一功能,房东就至关于真实角色,实现了抽象角色这一接口,具备了出租房屋的功能,房产中介就至关于代理角色,他引用了真实角色出租房屋的功能,而且额外加上了收取中介费的功能。代理
代理模式能够分为两种:静态代理模式和动态代理模式。这两种代理模式本质上是同样的,只是生成代理类的方式不同。code
一、静态代理模式对象
静态代理模式采用的方式是手动引入真实角色的引用,这种方式将被代理的对象定义死了,所以静态代理适合被代理对象很固定、只须要去代理一个类或者若干个固定的类而且数量不是不少的场景。代码示例:
定义抽象角色:
1 /** 2 * 抽象角色: 3 * 定义一个接口或者虚拟类 4 * 5 */ 6 public interface Subject { 7 8 public void rentHouse(); 9 }
定义真实角色:
1 /** 2 * 真实角色: 3 * 实现抽象角色接口 4 */ 5 public class RealSubject implements Subject { 6 7 public void rentHouse() { 8 9 System.out.println("房东:出租房屋"); 10 } 11 12 }
定义代理角色:
1 /** 2 * 代理角色: 3 * 一、实现抽象角色接口; 4 * 二、维护一个真实对象的引用,用来操做真实对象; 5 * 三、额外添加功能; 6 * 7 */ 8 9 public class ProxySubject implements Subject{ 10 11 private RealSubject realSubject; //代理角色内部引用了真实角色 12 13 public void rentHouse() { 14 15 if(null == realSubject){ 16 realSubject = new RealSubject(); 17 } 18 19 realSubject.rentHouse();//执行真实角色所完成的事情 20 21 this.getFee();//额外加入本身的功能 22 23 } 24 25 private void getFee(){ 26 System.out.println("代理角色:收取中介费"); 27 } 28 29 }
客户端调用:
1 public class Client { 2 3 public static void main(String[] args) { 4 5 Subject subject = new ProxySubject();//得到代理对象,由代理对象来执行所需的操做 6 7 subject.rentHouse();//输出结果:房东:出租房屋 8 // 代理角色:收取中介费 9 10 } 11 12 }
静态代理在使用场景上有必定的局限性,由于代理类中引入的对象是写死的(好比上面的RealSubject),所以它只能代理固定的一个类或若干个类。当须要代理一系列类中的某些方法,好比比较典型的应用就是springAOP,咱们须要建立出一批代理类来代理一系列类中的某些方法,这个时候静态代理就很难知足咱们的需求了,咱们须要用动态代理的方式。
二、动态代理
动态代理是JDK自带的功能,它涉及到一个接口InvocationHandler和一个类Proxy,这两个类都在java.lang.reflect包下。咱们须要实现InvocationHandler接口,而且调用Proxy类的静态方法newProxyInstance方法来得到一个代理类的实例。动态代理的实现过程与静态代理最大的区别就是代理实例是动态生成的。咱们先来看看实现过程:
定义抽象角色:
1 /** 2 * 抽象角色: 3 * 定义一个接口 4 * 5 */ 6 public interface Subject { 7 8 public void rentHouse(); 9 }
定义真实角色:
1 /** 2 * 真实角色: 3 * 实现抽象角色接口 4 */ 5 public class RealSubject implements Subject { 6 7 public void rentHouse() { 8 9 System.out.println("房东:出租房屋"); 10 } 11 12 }
定义动态代理角色:
1 /** 2 * 动态代理角色: 3 * 4 * 该代理类的内部属性是Object类型,实际使用的时候经过该类的构造方法传入一个对象, 5 * 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象 6 * 的将要被执行的方法,参数sub表示执行的方法从属于sub,在动态代理类中咱们还能够 7 * 在执行真实对象的方法以外再额外加入一些本身的方法 8 * 9 */ 10 public class DynamicProxySubject implements InvocationHandler { 11 12 //被代理类对象,声明为Object类型,这样就能够传入任何类型的对象 13 private Object sub; 14 15 public DynamicProxySubject(Object obj){ 16 this.sub = obj; 17 } 18 19 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 20 21 System.out.println("before calling :"+ method); //能够在执行真实对象的方法以外加入本身额外的方法 22 23 method.invoke(sub, args);//反射机制 24 25 System.out.println("after calling :"+ method); 26 return null; 27 } 28 29 }
客户端调用:
1 public class Client { 2 3 public static void main(String[] args) { 4 //建立被代理类的对象,根据传入的这个对象得到调用处理程序(handler) 5 RealSubject realSubject = new RealSubject(); 6 7 //建立一个调用处理程序(handler),因为DynamicProxySubject类实现了InvocationHandler接口, 8 //所以,handler能够指向DynamicProxySubject实例,在后面生成代理实例时须要传入这个handler 9 InvocationHandler handler = new DynamicProxySubject(realSubject); 10 11 Class<?> classType = handler.getClass(); 12 13 //一次性生成代理类的实例 14 Subject subject = (Subject)Proxy.newProxyInstance(classType.getClassLoader(), 15 realSubject.getClass().getInterfaces(), handler); 16 17 //代理实例调用方法时,将其指派到它的调用处理程序(handler)的invoke方法,流程转入invoke方法的调用 18 subject.rentHouse(); 19 20 //打印动态生成的代理实例的Class对象,能够看出生成的代理类的类型 21 System.out.println(subject.getClass()); 22 23 } 24 25 }
以上代码是动态代理模式的实现过程的示例,与静态代理的实现过程不一样,代理类须要实现InvocationHandler这个接口,此接口只有一个invoke方法,每个代理实例都具备一个关联的调用处理程序(handler),对代理实例调用方法时,将会被与之关联的handler接管,转而调用handler的invoke方法。invoke方法的参数中,method参数表示代理实例调用的那个方法对应的Method对象,好比上面的程序里就表示rentHouse()方法的Method对象;args参数表示被调用方法的参数列表。
Proxy类的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)会动态返回一个代理类的实例,第一个参数表示代理类的类加载器;咱们须要注意第二个参数,它是一个接口数组,表示代理类要实现的接口列表,也就是说动态生成的这个代理实例实现了这些接口;第三个参数表示与代理实例关联的调用处理程序(handler)。
运行程序,咱们能够获得如下结果输出:
从运行结果能够看出,第一行和第三行信息是代理类中咱们加入的额外的方法,第二行是代理类代理执行RealSubject类中的方法。咱们来看看第四行信息,它就是咱们打印的动态生成的代理类,这个类是运行期间动态生成的一个类,它既不是RealSubject类型也不是DynamicProxySubject类型,它是$Proxy0类型的。在代码中,因为咱们传入的接口数组是realSubject对象所实现的接口,即Subject接口,所以生成的这个代理类也实现了Subject接口,根据多态,咱们能够将生成的代理类强制转换成Subject类型。
动态代理模式适合被代理对象类型不肯定的场景,咱们在代理类DynamicProxySubject中把被代理对象定义为Object类型,这样就能够代理全部类型的对象,在上面的例子中,咱们传入的被代理对象是RealSubject类型。代理类是在运行时根据咱们传入的被代理对象以及须要实现的接口等信息动态生成的,它的类型是不固定的(可是都是以$Proxy开头)。
以上内容给你们介绍了代理模式的静态代理和动态代理原理,咱们要根据不一样的应用场景来选择用哪种,其实最重要的是要理解这种思想,在Spring框架底层源码中不少地方也用到了代理模式,理解了代理模式对咱们研究框架颇有帮助。以上只是LZ我的的看法,仅供参考。