对Java中静态代理和动态代理的简单理解

1、为什么使用代理

代理模式可以有效的将具体的实现与调用方进行解耦,通过面向接口进行编码完全将具体的实现隐藏在内部。

2、静态代理

静态代理是在编译时就将接口、实现类、代理类和客户需求全部手动完成,但如果我们每一个实现类都需要代理,那么全部需要手动的去创建,不仅浪费时间,而且会有大量的重复代码。具体实现是:首先创建一个接口(JDK代理都是面向接口的),然后创建具体实现类来实现这个接口,在创建一个代理类同样实现这个接口,不同之处在于,具体实现类的方法中需要将接口中定义的方法的业务逻辑功能实现,而代理类中的方法只要调用具体类中的对应方法即可,这样我们在需要使用接口中的某个方法的功能时直接调用代理类的方法即可,将具体的实现类隐藏在底层。
第1步:创建接口Renting.java

public interface Renting {
   public void rent();
}

第2步:实现类:Master.java

public class Master implements Renting {
   public void rent() {
       System.out.println("有房屋出租");
  }
}

第3步:代理类:Proxy.java

//代理角色:中介
public class Proxy implements Renting {

   private Master master;
   public Proxy() { }
   public Proxy(Master master) {
       this.master = master;
  }

   //租房
   public void rent(){
       seeHouse();
       master.rent();
       fare();
  }
   //看房
   public void seeHouse(){
       System.out.println("带房客看房");
  }
   //收中介费
   public void fare(){
       System.out.println("收中介费");
  }
}

第4步:测试

//客户类,一般客户都会去找代理!
public class Client {
   public static void main(String[] args) {
       //房东要租房
       Master master = new Master ();
       //中介帮助房东
       Proxy proxy = new Proxy(master);

       //你去找中介!
       proxy.rent();
  }
}

3、动态代理

动态代理的思维模式与静态代理的模式是一样的,也是面向接口进行编码,创建代理类将具体类隐藏解耦,不同之处在于代理类的创建时机不同,动态代理需要在运行时因需实时创建。Java中的动态代理主要要记住两个类(InvocationHandler 和 Proxy)和一个方法(Object invoke(Object proxy, Method method, Object[] args);)

  • InvocationHandler:调用处理程序
    InvocationHandler 是由代理实例的调用处理程序实现的接口,每一个代理实例都有一个关联的调用处理程序,当在代理实例上调用方法时,方法调用将被编码并分配到其调用处理程序的invoke方法。
  • Proxy: 代理
    Proxy 提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理的超类。
  • Object invoke(Object proxy, Method method, Object[] args):
    该方法是InvocationHandler接口中定义的唯一方法,该方法在调用指定的具体方法时会自动调用
    参数含义:proxy - 调用该方法的代理实例;method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口;args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。

具体实现:

第1步:创建租房接口Renting.java

public interface Renting {
   public void rent();
}

第2步:房主实现类:Master.java

public class Master implements Renting {
   public void rent() {
       System.out.println("有房屋出租");
  }
}

第3步:中介代理类:ProxyInvocationHandler. java

public class ProxyInvocationHandler implements InvocationHandler {
   private Object obj;

   public void setObj (Object obj) {
       this.obj= obj;
  }

   //生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
   public Object getProxy(){
       return Proxy.newProxyInstance(this.getClass().getClassLoader(),
               this.getClass().getInterfaces(),this);
  }

   // proxy : 代理类 method : 代理类的调用处理程序的方法对象.
   // 处理代理实例上的方法调用并返回结果
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       seeHouse();
       // 核心:本质利用反射实现!
       Object result = method.invoke(proxy, args);
       fare();
       return result;
  }

   //看房
   public void seeHouse(){
       System.out.println("带房客看房");
  }
   //收中介费
   public void fare(){
       System.out.println("收中介费");
  }

}

第4步:测试

//租客
public class Client {

   public static void main(String[] args) {
       //真实角色
       Master master = new Master();
       //代理实例的调用处理程序
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
       pih.setObj(master); //将真实角色放置进去!
       Renting proxy = (Renting)pih.getProxy(); //动态生成对应的代理类!
       proxy.rent();
  }

}

4、通过以上两个实例,简单聊聊动态代理的实现过程

(1)为什么JDK的动态代理是基本接口实现
通过使用接口指向实现类的实例的多态实现方式,可以有效的将具体的实现与调用之间解耦,便于后期修改与维护。再具体的说就是我们在代理类中创建一个私有成员变量(private修饰),使用接口来指向实现类的对象(纯种的多态体现,向上转型的体现),然后在该代理类中的方法中使用这个创建的实例来调用实现类中的相应方法来完成业务逻辑功能。这就是面向接口编程,利用java的多态特性,实现程序代码的解耦。
(2)代理类的创建过程
与静态代理不同,我们不知道它什么时候创建,也不知道要创建针对哪个接口、实现类的代理类(因为它是在运行时因需实时创建的)。静态代理需要实现与实现类相同的接口,而动态代理需要实现的是固定的Java提供的内置接口(一种专门提供来创建动态代理的接口)InvocationHandler接口,因为java在接口中提供了一个可以被自动调用的方法invoke,该方法是InvocationHandler接口中定义的唯一方法,该方法在调用指定的具体方法时会自动调用。在这个方法中定义了几乎和静态代理相同的内容,仅仅是在方法的调用上不同,不同的原因与之前分析的一样(创建时机的不同,创建的方式的不同,即反射),Method类是反射机制中一个重要的类,用于封装方法,该类中有一个方法那就是invoke(Object object,Object...args)方法,其参数分别表示:所调用方法所属的类的对象和方法的参数列表,这里的参数列表正是从测试类中传递到代理类中的invoke方法三个参数中最后一个参数(调用方法的参数列表)中,在传递到method的invoke方法中的第二个参数中。在实例化具体实现类时,private Object obj; public void setObj (Object obj) {this.obj= obj;} 这几行代码通过构造方法创建代理类的实例并将具体实现类的实例与之绑定。实现了静态代理类中 private Master master; public Proxy(Master master) {this.master = master;}几行代码相似的作用。
(3)InvocationHandler
InvocationHandler是JDK中提供的专门用于实现基于接口的动态代理的接口,主要用于进行方法调用模块,而代理类和实例的生成需要借助Proxy类完成。每个代理类的实例的调用处理器都是实现该接口实现的,而且是必备的,即每个动态代理实例的实现都必须拥有实现该接口的调用处理器,也可以这么说,每个动态代理实例都对应一个调用处理器。
(4)Proxy
Proxy类是JDK提供的用于生成动态代理类和其实例的类。通过Proxy中的静态方法newProxyInstance方法来直接生产代理实例,需要提供参数为上面的三个参数,即类加载器,接口数组,InvocationHandler。
总结:
实现JDK动态代理的步骤

  1. 创建接口,JDK动态代理基于接口实现;
  2. 实现InvocationHandler接口,重写invoke方法;
  3. 调用Proxy的静态方法newProxyInstance方法生成代理实例;
  4. 使用新生成的代理实例调用某个方法实现功能。
原文地址:https://www.cnblogs.com/chaozhengtx/p/14265009.html