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标识的,本地方法,生成代理类。
到这里一个代理类的生成流程就算是走完了。。
总结
- jdk动态代理只能代理接口
- 接口不能是@Deprecate标识的
- InvocationHandler中的方法就是你需要对被代理的方法做什么事情(AOP机制)
- 生成的代理类首先会被放在缓存中,下次只要K:类加载器,P:接口(忘记说了,是接口的Class对象)数组 一样,直接能从缓存中拿到V
- 动态代理能代理你的所有方法,其原理是通过反射,在类加载中动态生成代理类,所以叫动态代理嘛。。
后记:
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里就是实现类的方法了。