java 静态代理与动态代理(代理模式)

来源:互联网 发布:淘宝开店诈骗 编辑:程序博客网 时间:2024/06/10 09:04
 

因为对java的动态代理与静态代理整得不是特清楚,所以在这里以一个专题的形式对其进行深究:

一、代理模式是干啥的?
    代理模式是为其他对象提供一种代理以控制这个对象的访问。
    在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
二、代理模式涉及到哪些具体角色呢?
    代理模式一般涉及的角色有
    抽象角色(租房子):声明真实对象和代理对象的共同接口
    代理角色(中介):代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真

实对象操作时,附加其他的操作,相当于对真实对象进行封装
    真实角色(房主):代理角色所代表的真实对象,是我们要最终要引用的对象。
三、静态代理示例  
package com.javase.proxy;
public abstract class Subject {
   public abstract void request();
}

package com.javase.proxy;
public class RealSubject extends Subject {
   @Override
   public void request() {
      System.out.println("From real subject!");
   }
}

package com.javase.proxy;
public class ProxySubject extends Subject {
   private RealSubject realSubject;//代理角色内部引用了真实角色
   @Override
   public void request() {
      this.preRequest();//在真实角色操作之前所附加的操作   
      if(null == realSubject){
        realSubject = new RealSubject();
      }
      realSubject.request();//执行真实角色所完成的事情
      this.postRequest();//在真实角色操作之后所附加的操作。
   }
   //增加代理角色自己可以完成的一些事情
   private void preRequest(){
      System.out.println("pre request");
   }
   private void postRequest(){
      System.out.println("post request");
   }
}

package com.javase.proxy;
public class Client {
   public static void main(String[] args) {
       Subject subject = new ProxySubject();
       subject.request();
   }
}
    由以上代码可以看出,客户实际需要调用的是RealSubject类的request()方法,现在用ProxySubject来代理RealSubject类,同样达到目的,同时还封装了其他方法(preRequest(),postRequest())可

以处理一些其他问题。
    另外,如果要使用上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致

类的急剧膨胀;如果事先不知道真实角色,该如何来使用代理呢?这个问题可以使用Java动态代理类来解决。

四、动态代理
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类
   (1)Interface InvocationHandler:该接口中定义了一个方法
public Object invoke(Object obj,Method method,Object[] args)
在实际使用时,第一个参数obj一般指代理类,method是被代理的方法,如上例中的request()方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。

    解说:(每一个代理实例都会有一个与之关联的调用处理器,当我们调用某一个代理实例的某一个方法的时候,这个方法调用就会被编码,并且被派发到它的调用处理器的invoke方法上。)

    参数1:obj表示invoke方法是调用哪一个代理实例的方法。

    参数2: method对应于代理实例上的接口方法。

    参数3:args表示接口方法所需要接收的参数数组。

    (2)Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容。

解说:用于创建动态代理类,它是动态代理类的父类。也就是说我们自己创建的动态代理类是Proxy类的一个子类。

每一个代理实例都会有一个与之关联的InvocationHandler实例,这个实例是存在代理这个对象内部的,这样的话才能实现关联。

protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。

static Class getProxyClass(ClassLoader  loader,Class[] interfaces),获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。  

static Object newProxyInstance(ClassLoader  loader,Class[] interfaces,InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的Subject接口中

声明的方法).

即返回一个真实对象。newProxyInstance()方法生成的是一个代理对象,这个代理对象可以模拟真实对象的操作。还可以增加自己的一些额外的一些操作。

所谓Dynamic Proxy是这样一种class:它是运行时生成的class,在生成时必须提供一个interface,(也就是说这些class是在运行时创建出来的,而并不是事先定义好的),然后该class就宣称它实现

了这些interface。你当然可以把该class的实例当作这些interface中的任何一个使用。

    当然,Dynamic Proxy其实就是一个Proxy,它不会为你做任何实值性的工作。在生成它的实例时,你必须提供一个handler,,由它接管实际工作。

    在使用动态代理类的时,必须实现InvocationHandler接口

示例代码:

package com.javase.dynamicproxy;
public interface Subject {

   public void request();

}

package com.javase.dynamicproxy;
public class RealSubject implements Subject{
   @Override
   public void request() {
      System.out.println("From real subject");
   }
}

package com.javase.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicSubject implements InvocationHandler {
         private Object sub;
         public DynamicSubject(Object obj){
                   this.sub = obj;
         }
         @Override
         public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                   System.out.println("before calling: " + method);
                   method.invoke(sub, args);
                   System.out.println("after calling: " + method );
                   return null;
         }
}

package com.javase.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
         public static void main(String[] args) {
                   RealSubject realSubject = new RealSubject();
                   InvocationHandler invocationHandler = new DynamicSubject(realSubject);
                   Class<?> clasType = invocationHandler.getClass();
                   Subject subject = (Subject)Proxy.newProxyInstance(clasType.getClassLoader(),realSubject.getClass().getInterfaces() , invocationHandler);
                   subject.request();
                   System.out.println(subject.getClass());
         }

}

注意:要想使用动态代理,两个必须要注意的地方:
    1、一定要实现InvocationHandler接口,这个干嘛用的呢,是用于传给Proxy,作为Proxy.newInstance()这个方法的最后一个参数。当我通过它在运行时生成一个实例的时候,那么实例调request方

法,就转移给invocationHandler去接管了,就转而去执行代理类中的invoke方法,由invoke方法真正的去完成实际的事情。

    2、(Subject)Proxy.newProxyInstance(clasType.getClassLoader(),realSubject.getClass().getInterfaces() , invocationHandler);
    方法的说明是,生成一个代理的实例,代理实例是什么,就是代理对象,有对象,什么可以称为对象,类可以称作对象,没有类哪来对象呢,我们所生成的这个对象,就是最终强制转化的这个对象

(Subject),这个对象它既不是RealSubject实例,也不是DynamicSubject实例。    是哪个类呢,是$Proxy0, 这个类是在运行期间动态生成的一个类,也就是说我们生成的实例是$Proxy0这个类的一个实

例,那么生成的这个类,它有哪些特点呢,它实现了realSubject.getClass().getInterfaces() ,这些接口,即realSubject所实现的那些接口。realSubject实现了哪些接口呢,实现了Subject接口,生

成的那个类($Proxy0)就实现了realSubject实现的接口,因此就可以强制转换为接口类型。因此语句中的强制转换是没问题的。

原创粉丝点击