JDK动态代理详解

前言:

最近在看Spring aop然后了解到了动态代理=。=,发现自己以前的认识还是停留在应用之上,这里跟着网上的大佬看了看源码,豁然开朗,特别是对于《head first 设计模式》里面的那一副动态代理的uml图。

动态代理

动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。 比如说,想要在每个代理的方法前都加上一个处理方法:
开撸

定义HelloInterface及其实现类
public interface HelloInterface {
    void sayHello();
    String getNameById(String id);
}
public class Hello implements HelloInterface{
    @Override
    public void sayHello() {
        System.out.println("Hello lqhao!");
    }

    @Override
    public String getNameById(String id) {
        System.out.println("argument id: " + id);
        return "lqhao";
    }
}
定义实现了InvocationHandler的实现类
public class ProxyHandler implements InvocationHandler {
    private Object object;
    public ProxyHandler(Object object)
    {
        System.out.println("Proxy Handler constuctor:"+object.getClass());
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Proxy Handler proxy name:"+proxy.getClass());
        System.out.println("method:"+method.getName());
        System.out.println("args:"+ Arrays.toString(args));
        Object invokeObject=method.invoke(object, args);
        if (invokeObject!=null){
            System.out.println("invoke object:"+invokeObject.getClass());
        }else {
            System.out.println("invoke object is null ");
        }
        return invokeObject;
    }
}
运行函数
public class ProxyTest {
    public static void main(String[] args) {
        HelloInterface hello = new Hello();
        hello.sayHello();
        System.out.println("====================================");
        String name1=hello.getNameById("111");
        System.out.println("name:"+name1);
        System.out.println("--------------------------------");

        InvocationHandler handler = new ProxyHandler(hello);
        ClassLoader classLoader=HelloInterface.class.getClassLoader();
        Class<?>[] interfaces=new Class[]{HelloInterface.class};


        HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(
                classLoader,
                interfaces,
                handler);
        proxyHello.sayHello();
        System.out.println("====================================");
        String name2=proxyHello.getNameById("111");
        System.out.println("name:"+name2);
    }
}
运行结果

在这里插入图片描述

分析

从上面的分析结果看来,我们利用ProxyHandler包含一个Hello对象,从而实现委托给hello对象来进行真正的操作,让ProxyHandler实现增强的逻辑代码。那么动态,动态在哪里呢?
只需要一个Handler就可以对各种接口进行代理。即我hello接口中有sayhello(),bye接口中有saybye()要对这两个方法进行相同的逻辑增强,动态代理只需要一个handler即可实现对着两个接口中所有方法的增强。而静态代理就需要手动写两个代理类了,一个实现hello接口,包含hello对象,一个实现bye接口包含bye对象,这就是他们之间最大的区别了。
那么jdk动态代理是如何实现的呢?
关键在于

Proxy.newProxyInstance(classLoader,interfaces,handler);

返回一个继承Proxy类实现了被代理接口的动态代理类。
观察一下他的源码实现:注释我都写好了

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);
    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
  	//这个方法用于获取代理类的class 继承proxy以及实现传入的接口
    Class<?> cl = getProxyClass0(loader, intfs);
    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
				//根据参数获取代理类中的构造函数 constructorParams是InvocationHandler.class
        final Constructor<?> cons = cl.getConstructor(constructorParams);
     		//记录传入的hanler
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
      //调用新建代理类的构造函数 传入handler 其实归根到底是调用了Proxy的构造函数。
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}
关键是下面这三行代码

这个方法用于获取动态生成的代理类的class,继承proxy以及实现传入的接口

Class<?> cl = getProxyClass0(loader, intfs);

根据参数来获取代理类中的构造函数 constructorParams是InvocationHandler.class

final Constructor<?> cons = cl.getConstructor(constructorParams);

利用获取的构造函数(调用了父类proxy的构造函数),来返回动态生成的代理类对象

return cons.newInstance(new Object[]{h});

而在Proxy中的构造函数,即为h初始化

 protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

所以呢他们的关系显而易见了就是 动态代理类中包含handler对象,handler对象包含被代理对象。 层层委托。那么是如何动态代理类是如何委托handler对象方法的呢?,别忘了动态代理类实现了被代理的接口,所以其实就是在实现的方法中调用h.invoke (反射技术)。

附上别人测试的反编译动态代理类的代码:这样子就比较清晰了吧。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;

public final class $Proxy0 extends Proxy implements Person
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;

/**
*注意这里是生成代理类的构造方法,方法参数为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
{
  try
  {
    //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
    m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
    m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
    m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
    m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    return;
  }
  catch (NoSuchMethodException localNoSuchMethodException)
  {
    throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  }
  catch (ClassNotFoundException localClassNotFoundException)
  {
    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
}

/**
* 
*这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
*this.h.invoke(this, m3, null);这里简单,明了。
*来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
*再联系到InvacationHandler中的invoke方法。嗯,就是这样。
*/
public final void giveMoney()
  throws 
{
  try
  {
    this.h.invoke(this, m3, null);
    return;
  }
  catch (Error|RuntimeException localError)
  {
    throw localError;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
}

//注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。
}
附上改良后的uml图

在这里插入图片描述

总结

  • jdk静态代理实现比较简单,一般是直接代理对象直接包装了被代理对象。

  • jdk动态代理是接口代理,被代理类A需要实现业务接口,业务代理类B需要实现InvocationHandler接口。

  • jdk动态代理会根据被代理对象生成一个继承了Proxy类,并实现了该业务接口的jdk代理类,该类的字节码会被传进去的ClassLoader加载,创建了jdk代理对象实例,

  • jdk代理对象实例在创建时,业务代理对象实例会被赋值给jdk代理对象,jdk代理对象实例也就有了业务代理对象实例,同时jdk代理对象实例通过反射根据被代理类的业务方法创建了相应的Method对象m(可能有多个)。当jdk代理对象实例调用业务方法,如proxy.sayhello();这个会先把对应的m对象作为参数传给invoke()方法(就是invoke方法的第二个参数),调用了jdk代理对象实例的invoke()回调方法,在invoke方法里面再通过反射来调用被代理对象的因为方法,即result = method.invoke(target, args);。

  • 动态代理比静态代理好的地方在于动态代理一个业务代理对象可以对很多个被代理接口进行代理,而静态代理则需要针对每一个代理接口创建对应业务代理类。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值