java动态代理原理详解源码分析

java动态代理原理详解

什么是代理?

简单的来说就是厂家不直接卖商品而是通过销售来卖,这里的销售商就是代理,厂家就是委托。也就是在厂家和商品之间增加了一定的间接性!那么这样的好处是什么?

  1. 隐藏委托类的实现。
  2. 可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。(当其中一位客户还需要更多的要求,在不违背开闭原则的情况下,代理类可以增加一些额外的处理。)

静态代理

由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

简单实现:

老板让秘书通知各个部门的部长来开会, 定义一个接口Move(开会)是代理类(秘书)和被代理类(部门)的公共接口。

public interface Move{
    void play();
}
/*被代理类实现Move接口*/
class Department implements Move{
    private String name;
    public Department(String name){
        this.name=name;
    }
    public  void play(){
       System.out.println(name+"开会")}
}
/*代理类也要实现Move接口*/
class Secretary implements Move{
    private Departmnt de;
    public Secretary(Department de){
        this.de=de;
    }
    public void play(){
        de.play();
    }
}
/*测试一下*/
public class Demo{
    public static void main(String args[]){
       	Move de=new Department("人事");
        /*通过持有被代理对象,来执行开会的行为*/
        Move se=new Secretary(de);
        se.play();
    }
}

从上面的例子使用代理似乎没有什么好处。反而增加了代码长度。但当开会这一行为无法满足的时候,该如何设计被代理类?

  1. 直接在被代理类的方法中加上额外的功能,弊端很明显随着部门的增多,代码冗余越来越大。
  2. 直接在代理类中的方法找中增加额外的方法,避免了冗余。
public void play(){
    System.out.println("部门情况");
    de.play();
}

这样当我们通过代理类调用方法的时候,会在调用方法之前加入一些额外的操作。

但同时我们又会想到这样一种情况:当我们的被代理类有很多,而且方法是不同的这是我们又该如何解决?有下面两种情况:

  1. 我们为每一个被代理对象创建一个代理类,来持有这个对象。

    se1.play1();
    se2.play2();
    se3.play3();
    se4.play4();
    
  2. 我们创建一个代理类来实现这些被代理类的接口Move1,Move2,Move3…但是这样就会使得这一个代理类显得臃肿。

动态代理

通过上一个情况,引出了动态代理设计模式。就是解决静态代理上面这一缺点的。他和第一种解决方法类似,只不过这些se是在运行期间自动生成的代理类。

创建一个接口:

//接口
interface Subject{
    public void rent();
    public void hello(String str);
}

被代理对象:

//被代理对象
class RealSubject implements Subject{
    public void rent()
    {
        System.out.println("I want to rent my house");
    }
    public void hello(String str)
    {
        System.out.println("hello: " + str);
    }
}

创建一个类实现InvocationHandler接口,所有的被代理执行的方法都是通过执行该类中的invoke方法。

class DynamicProxy implements InvocationHandler{
    // 这个就是我们要代理的真实对象
    private Object proxy;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象  
    //    构造方法,给我们要代理的真实对象赋初值
    public DynamicProxy(Object proxy)
    {
        this.proxy = proxy;
    }
    /*Object invoke(Object proxy, Method method, Object[] args) throws Throwable

      proxy:     在其上调用方法的代理实例.
      method:  对应于在代理实例上调用的接口方法的 Method 
      实例: 指代的是我们所要调用真实对象的某个方法的Method对象
      args:     包含:传入代理实例上/方法调用/的参数值/的对象数组,如果接口方法不使用参数,则为 null。*/
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        //  在代理真实对象前我们可以添加一些自己的操作
        System.out.println("before rent house");
        System.out.println("Method:" + method);
        //    当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用,
        //    而handler对象又传入了realSubject对象为参数,所以实际调用的是RealSubject中的方法。
        method.invoke(proxy,args);
        //  在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("after rent house");   
        return null;
    }
}

在测试类中动态创建代理

public class DynamicProxyDemo
{
    public static void main(String[] args)
    {
        //    我们要代理的真实对象
        Subject realSubject = new RealSubject();

        //    我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
//      DynamicProxy实现InvocationHandler接口,通过有参构造方法将被代理对象传入,表明invoke调用的是realSubject对象的方法
        InvocationHandler handler = new DynamicProxy(realSubject);

        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler的handler对象。
         */
        Subject subject = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);
        //这里会有一个疑问我们是如何调用rent方法的?
        subject.rent();
        
        subject.hello("world");
    }
}

我只将realSubject关联到了DynamicProxy类中的subject对象上,知道了如果我调用了DynamicProxy的invoke方法其实就是调用realsubject中的方法,但是测试类中似乎我没有明显指出我哪里调用了DynamicProxy中的invok方法。

其实这里的newProxyInstance方法隐式地创建了一个代理类proxy0,通过反编译我们可以查看其中的源码。

public final class $Proxy0 extends Proxy implements Subject
{
  private static Method m3;
  /**
  *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
  *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
  *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
  *
  *super(paramInvocationHandler),是调用父类Proxy的构造方法。
  *父类持有:protected InvocationHandler h;
  *Proxy构造方法:
  *    protected Proxy(InvocationHandler h) {
  *         Objects.requireNonNull(h);
  *         this.h = h;
  *     }
  *
  */
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  
   static
  {
      //看看这儿静态块儿里面有什么,是不是找到了rent方法。请记住rent通过反射得到的名字m3,其他的先不管
      m3 = Class.forName("proxy.Subject").getMethod("rent", new Class[0]);
 }
  /**
  * 
  *这里调用代理对象的rent方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
  *this.h.invoke(this, m3, null);
  这里的this.h代表的是父类proxy中的 protected InvocationHandler h。 this代表的是 当前的代理对象。
  */
  public final void rent()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
}

分析subject.rent():

首先这里的

Subject subject = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);

//其实就等同于

Subject subject =new proxy0(handler);

那么:subject.rent()其实就是调用Proxy0中的rent()

 public final void rent(){  
      this.h.invoke(this, m3, null);
      return;
    }

这里的this.h是什么?


 public $Proxy0(InvocationHandler paramInvocationHandler){
    super(paramInvocationHandler);
  }

Proxy构造方法:
	protected InvocationHandler h;
    protected Proxy(InvocationHandler h) {
          this.h = h;
 }
  
 

Proxy0继承Proxy,所以this.h代表的是Proxy中的h,而Proxy0中的构造方法super(paramInvocationHandler);但是我们又前面说过了new Proxy0(handler);

:this.h=h=paramInvocationHandler=handler;

handler分析

handler = new DynamicProxy(realSubject);
Subject realSubject = new RealSubject();

handler是DynamicProxy类的一个实例,我们初始化这个实例的时候我们将realSubject对象传递进去了。

    private Object proxy;
    public DynamicProxy(Object proxy)
    {
        this.proxy = proxy;
    }
//所以这里的proxy就是realSubject

所以:this.h.invoke()就是调用的DynamicProxy中的invoke()方法

我们先看invoke(this,m3,null) 中的三个参数

//this不用说代表的是当前的代理对象
 m3 = Class.forName("Subject").getMethod("rent", new Class[0]);
//class.forname是加载初始化Subject接口  getMethod是返回一个method对象 new Class[0]表示rent方法没有参数   一句话m3表示Subject中的rent()

我们再来看看DynamicProxy中的invoke()

	public Object invoke(Object proxy, Method method, Object[] args){   
        method.invoke(proxy,args)}
proxy=this; method=m3; args=null

分析method.invoke(proxy,null):上面说到初始化DynamicProxy类时传递了一个realSubject对象,proxy=realSubject。realSubject是RealSubject类的实例对象。

总体:subject.rent()结果就是: System.out.println(“I want to rent my house”);

最后:https://www.cnblogs.com/gonjan-blog/p/6685611.html 部分内容借鉴这篇文章,博主写的很详细!可以去学习学习!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值