Java动态代理原理分析

先简单说下静态代理

Java动态代理原理分析

       其实这静态代理非常类似委派模式(Delegate),只不过可以认为代理类跟实现类还是有关联嘛,起码他们都实现了相同的接口,而委派模式两个类完全不相关。

       静态代理比较简单,一般就是我们的代理类与我们的实现类实现了同样的接口,在代理类的方法(实现接口的方法)中引用我们的实现类的方法,从而能够起到一个简单的AOP功能了。  【因比较简单,暂不列出代码了。。】

       但是这种很有局限性,这种代理更像是在代理方法,试想一下,假设我们这个接口有很多的方法。比如100个,原来的实现类SubjectImpl已经写好了,这时我们发现这些方法还有不足,比如每个方法执行之后应该有输出日志信息,那该怎么办?继续这样的静态代理,实现这100个方法之后,继续引用实现类的方法,再打印日志?这工作量简直不要太大…..

jdk动态代理:

简单实现:

       jdk中自带的Proxy类,其创建代理对象核心方法:

public static Object newProxyInstance(

ClassLoader loader,  Class<?>[] interfaces, InvocationHandler h )

参数分别为类加载器,代理接口,InvocationHandler,前两个没什么好说的。InvocationHandler是一个函数式的接口,其唯一的方法:

public Object invoke(Object proxy, Method method, Object[] args)

Proxy为代理的对象,method为对象的方法,args是方法参数,InvocationHandler可以看作处理我们需要被AOP的方法的一个类吧,当然,必须要指定实现类了。

使用jdk动态代理创建代理对象的代码如下:

先定义一个类实现InvocationHandler接口,

class MyInvocation implements InvocationHandler{

    private Object ref;

    public MyInvocation(Object ref){

        this.ref = ref;

    }

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("方法代理之前"); //这两行是为了验证而随便写的嘛

        Object invoke = method.invoke(ref, args);

        System.out.println("方法代理之后");//写什么都行

        return invoke;

    }

}

而后直接调用Proxy的静态方法newProxyInstance

public static Object getSubject(Object o){

        return Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]

{Subject.class}, new MyInvocation(o));

    }

从而便生成我们的代理对象了。。

 

深入分析:

在Proxy.newProxyInstace源码中:一处核心代码

/*

* Look up or generate the designated proxy class.

*/

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

Loader是类加载器,infs为clone代理接口数组后的克隆接口数组。这个方法就是生成接口代理类的核心方法。。进入该方法,核心处:

// If the proxy class defined by the given loader implementing

// the given interfaces exists, this will simply return the cached copy;

// otherwise, it will create the proxy class via the ProxyClassFactory

return proxyClassCache.get(loader, interfaces);

proxyClassCache是WeakCache <ClassLoader, Class<?>[], Class<?>>的实例,其泛型参数分别为:Key:类加载器、Parameters:参数(这里就是我们所需要被代理的接口的Class类)、Value:代理类(实现好的)。也根据官方注释可见:该方法作用是:首先在缓存中通过Key(classLoader)来找,如果给定的接口已在缓存中已有创建好的代理类,那么直接返回,否则就创建一个。

根据这句话,可以看出:代理类创建后是需要放在缓存中的,保存的key为其类加载器,就不用频繁的创建,这种缓存根据名字(Weak)可知,在内存中为弱引用。

进入WeakCache.get方法里面,核心代码:(…表示前面/后面省略了很多代码,下同)

if (supplier != null) {

                // supplier might be a Factory or a CacheValue<V> instance

                V value = supplier.get();

                if (value != null) {

                    return value;

                }

            }

            if (factory == null) {

                factory = new Factory(key, parameter, subKey, valuesMap);

            }

 

            if (supplier == null) {

                supplier = valuesMap.putIfAbsent(subKey, factory);

                if (supplier == null) {

                }            }

如官方注释,supplier如果一开始不为null,也就是表示缓存中已经存在该代理类,直接通过supplier.get()拿到valueà代理类返回。如果为Null,那么想将key(类加载器)、parameter(要被代理的接口数组)、subKey(可以看作前面parameter的存根吧)、valueMap(表示缓存中已有代理对象),封装进Factory(Factory实现了Supplier接口),而后调用其get()方法。在Factory.get方法中,一处核心代码:

value = Objects.requireNonNull(valueFactory.apply(key, parameter));

valueFactory是ProxyClassFactory的对象,这个就是专门来生成代理对象的。

进入apply方法,几处核心代码:

interfaceClass = Class.forName(intf.getName(), false, loader);

/*

                 * Verify that the Class object actually represents an

                 * interface.

                 */

                if (!interfaceClass.isInterface()) {

                    throw new IllegalArgumentException(

                        interfaceClass.getName() + " is not an interface");

                }

                /*

                 * Verify that this interface is not a duplicate.

                 */

                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {

                    throw new IllegalArgumentException(

                        "repeated interface: " + interfaceClass.getName());

                }…

             /*

             * Generate the specified proxy class.

             */

            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(

                proxyName, interfaces);

return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);

 

可以看出,首先,代理的只能是接口,并且接口还不能是@Deprecate 注解的。

根据前面组装出来的代理名:com.sun.proxy.$Proxy0。【这个是默认指定的】,以及你要代理的接口,从而生成代理类,会保存在本地(.class文件),而后通过defineClass0方法,这个方法是native标识的,本地方法,生成代理类。

 

到这里一个代理类的生成流程就算是走完了。。

 

 

总结

  1. jdk动态代理只能代理接口
  2. 接口不能是@Deprecate标识的
  3. InvocationHandler中的方法就是你需要对被代理的方法做什么事情(AOP机制)
  4. 生成的代理类首先会被放在缓存中,下次只要K:类加载器,P:接口(忘记说了,是接口的Class对象)数组 一样,直接能从缓存中拿到V
  5. 动态代理能代理你的所有方法,其原理是通过反射,在类加载中动态生成代理类,所以叫动态代理嘛。。

后记:

Q:为什么jdk动态代理只能代理接口?

A:同上的代码,

public static Object getSubject(Object o){

        return Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]

{Subject.class}, new MyInvocation(o));

    }

在测试方法中:Object proxyObj = SubjectProxy.getSubject(new SubjectImpl());传入参数为Subject的实现类,再分别proxyObj instanceof Subject、Proxy、SubjectImpl,结果发现,此时生成的代理对象proxyObj是Subject接口类型,也是Proxy类型,但不是SubjectImpl类型了。本来java就是单继承模式,proxyObj就是extends Proxy类,并且implements Subject接口。但既然是我们生成的对象,里面肯定就要有我们所需要的方法,那么这方法就是接口中的方法了,在proxyObj实现接口Subject的方法中,就会引用我们所指定的InvocationHandler,调用其invoker方法,那么这个invoker里就是实现类的方法了。