导读:
海霞是一个希望不仅自己会用,也知道为什么的程序媛,或者说,是走在理想道路上的开发者。思考是她的习惯,今天她为我们带来关于jdk的一些学习过程,适合想了解动态代理的同学阅读~。
23个设计模式中,自己觉得动态代理是最有意思的一个,开始只会用,发现总是印象不够深刻,原来是自己没有真正理解JDK到底是怎么实现的,因此深入学习下,代码就是最好的老师!
直接上实例比较直观,举个最简单的例子,比如,周杰伦开演唱会, 前前后后应该有很多事情要做,这些事情他本人可以不用关注,因为有经纪人么。
经纪人呢在开演唱会前做策划活动,演唱会的时候,找周杰伦本人唱歌就行,演唱会结束了,经纪人根据协议去收钱哇,当然。这个也可以在演唱会前做。
(一)例子
JDK动态代理的关键就是代理者与被代理者继承同一个接口:
public interface Subject {
void sing(String songName);
}
被代理者,周杰伦本人:
public class RealSubject implements Subject {
@Override
public void sing(String songName){
System.out.println("周杰伦本人" + "sing:" + songName);
}
}
动态代理的关键代码就是实现一个InvocationHandler的invoke接口:
public class DynamicProxy implements InvocationHandler {
private Subject singer;
DynamicProxy(Subject singer){
this.singer = singer;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("演唱会前,周杰伦的经纪人准备活动策划并签署协议。。。");
method.invoke(singer, args);
System.out.println("演唱会后,周杰伦的经纪人收演出费!");
return null;
}
}
客户端调用代码:
public class Client {
public static void main(String[] args) {
Subject zhoujielun = new RealSubject();
InvocationHandler handler = new DynamicProxy(zhoujielun);
Subject proxy = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), zhoujielun
.getClass().getInterfaces(), handler);
proxy.sing("菊花台");
Class c1 = proxy.getClass();
byte[] proxyClassFile = ProxyGenerator.generateProxyClass("zhoujielunProxy", zhoujielun
.getClass().getInterfaces());
exportClazzToFile("D:\\zhoujielunProxy.class", proxyClassFile);
try {
Method m = c1.getDeclaredMethod("sing", String.class);
System.out.println(m.toString());
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
} }
看看运行结果:
最后一行,我打印了一下这个代理的方法名,$Proxy这个东东就是生成的动态代理了,他在内存里,长什么样子呢?
我们把Class打印到文件里,然后再反编译一看一下就知道了,其实也没那么神秘:
先继承了Proxy,Proxy拥有传入的handler对象,通过反射生成了所有方法,包括父类Objcet的equals,hashCode,toString方法,看具体的sing方法,就是调用了handler的invoke方法。
看到invoke方法的参数,第一个就是这个动态代理类自己,以及通过接口反射出来的具体方法对象,最后就是参数类型列表了。
public final class ZhoujielunProxy extends Proxy
implements Subject {
private static Method m3;
private static Method m1;
private static Method m0;
private static Method m2;
public ZhoujielunProxy(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final void sing(String paramString) {
try {
this.h.invoke(this, m3, new Object[] { paramString });
return;
}
catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
}
catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final boolean equals(Object paramObject){... }
public final int hashCode(){... }
public final String toString(){ ...}
static
{
try
{
m3 = Class.forName("com.test.proxy.dinamic1.Subject").getMethod("sing",
new Class[] { Class.forName("java.lang.String") });
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
以上就是动态代理的的真实面目了,具体怎么创建出来的,就需要好好分析Proxy这个类了。
让我们继续看,首先就是在DynamicProxy里调用的Proxy.newProxyInstance了:
Class cl = getProxyClass(loader, interfaces);
Constructor cons = cl.getConstructor();
(Object) cons.newInstance(Object[] { h });
下面得看看Proxy里有什么重要属性
(直接看英文定义吧,比我解释的好):
/** maps a class loader to the proxy class cache for that loader */
private static Map loaderToCache = new WeakHashMap();
/** marks that a particular proxy class is currently being generated */
private static Object pendingGenerationMarker = new Object();
/** next number to use for generation of unique proxy class names */
private static long nextUniqueNumber = 0;
private static Object nextUniqueNumberLock = new Object();
/** set of all generated proxy classes, for isProxyClass implementation */
private static Map proxyClasses =
Collections.synchronizedMap(new WeakHashMap());
getProxyClass这个方法有点长,大概做了一下几件事:
1. 对这组接口进行一定程度的安全检查,包括检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的,还会检查确保是 interface 类型而不是 class 类型。
2. 从 loaderToCache 映射表中获取以类装载器对象为关键字所对应的缓存表,如果不存在就创建一个新的缓存表并更新到 loaderToCache。
缓存表是一个 HashMap 实例,正常情况下它将存放键值对(接口名字列表,动态生成的代理类的类对象引用)。
当代理类正在被创建时它会临时保存(接口名字列表,pendingGenerationMarker)。
标记 pendingGenerationMarke 的作用是通知后续的同类请求(接口数组相同且组内接口排列顺序也相同)代理类正在被创建,请保持等待直至创建完成。
loaderToCache.put(loader, cache);
cache.put(key, new WeakReference(proxyClass));
或者:cache.put(key, pendingGenerationMarker);
3. 动态创建代理类的类对象。首先是确定代理类所在的包,其原则如前所述,如果都为 public 接口,则包名为空字符串表示顶层包;如果所有非 public 接口都在同一个包,则包名与这些接口的包名相同;如果有多个非 public 接口且不同包,则抛异常终止代理类的生成。
确定了包后,就开始生成代理类的类名,同样如前所述按格式“$ProxyN”生成。(生成代理的关键代码)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
JDK动态代理的缺点很明显,就是代理类和被代理类必须都实现同一个接口,那么一个没有实现接口的类,该如何代理呢?
另外就是JAVA反射其实效率不是那么高,所以需要进一步研究研究CGLIB的动态代理O(∩_∩)O!学习是永无止境的~