代理模式是Java模式中很重要的一个。特别是动态代理模式是Spring AOP实现的基石,也是我们理解AOP原理的基础。网上关于静态代理和动态代理的介绍有很多,详细地也可以查看本文下方引用。时间关系,这里只是简单地记录一些要点。
什么是代理
定义:为其他对象提供一种代理以控制对这个对象的访问
静态代理
静态代理是指预先确定了代理与被代理者的关系。
静态代理的实现
通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。
静态代理可以通过聚合来实现,让代理类持有一个委托类的引用即可。
(顺便复习下类和类之间的关系,请参考UML 学习笔记 - 类图)
动态代理
动态代理本质上仍然是代理,情况与上面介绍的完全一样,只是代理与被代理的关系是动态确定的。
相比静态代理,有何优势
那既然动态代理没有为我们增强代理方面的任何功能,那我们为什么还要用动态代理呢,静态代理不是挺好的吗?凡是动态确定的东西大概都具有灵活性,强扩展的优势。
动态代理有两种实现,一个是JDK实现,一个是cgLib实现。
Jdk动态代理实现
在java
的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler
接口、另一个则是 Proxy
类,这个类和接口是实现我们动态代理所必须用到的。
InvocationHandler
接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy
是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。
一个值得研究的问题是,为什么每次对动态代理类的调用,会去自动调用InvocationHandler.invoke()
方法,这个需要反编译动态生成的代理类,可以看到这个代理类其实是Proxy
类的一个子类,并且实现了与被代理类一样的接口,创建的InvocationHandler
实现是这个代理类的一个属性。关键是,对这些接口方法的调用实现,其实是调用了InvocationHandler
的invoke()
方法的。看下面这段反编译的代码就很清楚了:
public final class $Proxy0 extends Proxy implements Manager {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
static {
try {
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]);
m3 = Class.forName("com.ml.test.Manager").getMethod("modify",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
} //static
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void modify() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
CGLib的动态代理实现
CGLib(Code Generation Library)
是一个强大、高性能的Code
生成类库,它可以在程序运行期间动态扩展类或接口,它的底层是使用java
字节码操作框架ASM
实现。
CGLIB
原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final
的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java
反射的JDK
动态代理要快。
CGLIB
底层:使用字节码处理框架ASM
,来转换字节码并生成新的类。不鼓励直接使用ASM
,因为它要求你必须对JVM内部结构包括class
文件的格式和指令集都很熟悉。
CGLIB
缺点:对于final
方法,无法进行代理。
应用
秒懂Java代理与动态代理模式
Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)
动态代理 InvocationHandler中的invoke()方法是在哪被调用的
网友评论