Spring AOP其實是基於動態代理實現的,只不過Spring 同時支持JDK Proxy和cglib,下面咱們來介紹一下這兩種實現動態代理的方式html
注:本示例中使用JDK1.8java
/** * 在代理的接口調用時的處理器類 */
class MyHandler implements InvocationHandler {
private final Object target;
public MyHandler(final Object target) {
this.target = target;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("-----before----");
final Object o = method.invoke(this.target, args);
System.out.println("-----after----");
return o;
}
}
public static void main(final String[] args) {
final BizAImpl bizAImpl = new BizAImpl();
final IBizA newBizA = (IBizA) Proxy.newProxyInstance(MyHandler.class.getClassLoader(),
bizAImpl.getClass().getInterfaces(),
new MyHandler(bizAImpl));
newBizA.doSomething();
}
複製代碼
class MyHandler implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("----before----");
proxy.invokeSuper(obj, args);
System.out.println("----after----");
return obj;
}
}
public static void main(String[] args) {
MyHandler myHandler = new MyHandler();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(BizA.class);
enhancer.setCallback(myHandler);
BizA bizA = (BizA) enhancer.create();
bizA.doSomething();
}
複製代碼
從示例代碼中,咱們能夠得出如下結論git
JDK Proxy | Cglib |
---|---|
要求代理類必須實現接口,參見IBizA和BizImpl | 支持類的代理,對接口實現沒有要求 |
須要將代理目標對象(bizImpl)傳遞給InvocationHandler,在寫法上不夠靈活 | 能夠直接根據類(BizA.class)生成目標代理對象 |
實際上,這兩種代理的實現方式也大不同github
JDK Proxy是經過複製原有代理類,而後生成一個新類,在生成新類的同時,將方法的調用轉給了InvocationHandler,在代理類執行方法時,其實是調用了InvocationHandler的invoke方法。spring
咱們看下JDK源碼,從newProxyInstance方法開始設計模式
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
/* * Look up or generate the designated proxy class. */
Class<?> cl = getProxyClass0(loader, intfs);
/* * Invoke its constructor with the designated invocation handler. */
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
}
複製代碼
newProxyInstance方法只幹了兩件事緩存
那麼這裏的重點是若是獲取代理類呢,咱們接着往下看安全
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 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);
}
複製代碼
優先從緩存中獲取,若是緩存中沒有,會經過工廠(ProxyClassFactory)生成,緩存部分代碼略過,直接看生成代理類的流程bash
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
//校驗接口,訪問權限,肯定package等,省略...
/*
* Choose a name for the proxy class to generate.
*/
//生成代理類名字
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
複製代碼
將核心方法提取出來app
//生成字節碼數據
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
//這是一個native方法
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
複製代碼
進入方法ProxyGenerator.generateProxyClass()
public static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();
//這個標誌是系統的一個配置,是否保存生成的文件,能夠經過配置項
//sun.misc.ProxyGenerator.saveGeneratedFiles拿到
if (saveGeneratedFiles) {
//...保存文件,非討論重點
}
return classFile;
}
複製代碼
生成類文件的核心方法
private byte[] generateClassFile() {
/* ============================================================ * Step 1: Assemble ProxyMethod objects for all methods to * generate proxy dispatching code for. * 第一步:添加代理方法(ProxyMethod) */
//object公用方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//代理實現的接口方法
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
}
/* ============================================================ * Step 2: Assemble FieldInfo and MethodInfo structs for all of * fields and methods in the class we are generating. * 步驟2:將代理方法(MethodProxy)轉換爲方法(MethodInfo),MethodInfo實現了寫入字節碼 */
try {
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for method's Method object
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
//代理方法(MethodProxy)轉換爲方法(MethodInfo)
// generate code for proxy method and add it
methods.add(pm.generateMethod());
}
}
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
/* ============================================================ * Step 3: Write the final class file. * 步驟3:寫入類文件 */
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
/* * Write all the items of the "ClassFile" structure. * See JVMS section 4.1. */
//...依照JVM規範生成字節碼,省略
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
return bout.toByteArray();
}
複製代碼
再次進入ProxyMethod轉換爲MethodInfo的方法generateMethod(),這裏的內容較多,咱們只貼出來關鍵的一處,將InvocationHandler的調用加入了新方法中
out.writeByte(opc_invokeinterface);
out.writeShort(cp.getInterfaceMethodRef(
"java/lang/reflect/InvocationHandler",
"invoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
"[Ljava/lang/Object;)Ljava/lang/Object;"));
out.writeByte(4);
out.writeByte(0);
複製代碼
由此咱們瞭解了整個JDK Proxy的執行過程,最終能夠從生成的class文件($Proxy0.class)中反編譯,反編譯結果以下:
package com.sun.proxy;
import com.proxy.IBizA;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class Proxy0 extends Proxy implements IBizA {
public Proxy0() throws {
super(paramInvocationHandler);
}
public final void doSomething() throws {
try
{
this.h.invoke(this, m3, null);
return;
}
}
public final String toString() throws {
try
{
return ((String)this.h.invoke(this, m2, null));
}
}
}
複製代碼
咱們從反編譯的文件中能夠看出來,實際上代理類調用了InvocationHandler的invoke方法
注:系統默認不輸出代理class文件,若是要實現新增代理class文件的輸出,須要在main方法中加上
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
複製代碼
注:Proxy相關的代碼能夠直接經過jdk源碼看到,ProxyGenerator的代碼請參考:
cglib是經過asm庫實現了代理class的生成,cglib會生成兩個類,一個類是做爲代理類使用,一個類是做爲方法調用時使用。在調用方法的時候,cglib作了一些優化,沒有采用反射調用方法,而是給每一個代理方法增長一個索引,而後根據索引找到方法,並直接調用。
注:asm是一個操做Java字節碼的庫
參考資料:
https://www.ibm.com/developerworks/cn/java/j-lo-asm30/index.html
https://asm.ow2.io/
複製代碼
咱們首先看下cglib是如何生成代理類的,因爲cglib的代碼較爲複雜,咱們只貼出來關鍵部分.
cglib和JDK proxy有一點比較相似,他們都是優先從緩存中獲取,若是取不到纔會調用生成class的方法,咱們直接看AbstractClassGenerator類的generate方法
protected Class generate(ClassLoaderData data) {
Class gen;
Object save = CURRENT.get();
CURRENT.set(this);
try {
ClassLoader classLoader = data.getClassLoader();
synchronized (classLoader) {
String name = generateClassName(data.getUniqueNamePredicate());
data.reserveName(name);
this.setClassName(name);
}
if (attemptLoad) {
try {
gen = classLoader.loadClass(getClassName());
return gen;
} catch (ClassNotFoundException e) {
// ignore
}
}
//關鍵代碼,調用了strategy的generate方法
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = getProtectionDomain();
synchronized (classLoader) { // just in case
if (protectionDomain == null) {
gen = ReflectUtils.defineClass(className, b, classLoader);
} else {
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
}
}
return gen;
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw new CodeGenerationException(e);
} finally {
CURRENT.set(save);
}
}
複製代碼
調用了GeneratorStrategy的generate方法,默認策略是DefaultGeneratorStrategy,咱們繼續跟蹤
public byte[] generate(ClassGenerator cg) throws Exception {
DebuggingClassWriter cw = getClassVisitor();
transform(cg).generateClass(cw);
return transform(cw.toByteArray());
}
protected ClassGenerator transform(ClassGenerator cg) throws Exception {
return cg;
}
複製代碼
默認策略並無作什麼實際的工做,直接調用了ClassGenerator接口的generateClass方法。
咱們回到AbstractClassGenerator類,該類並無定義generateClass方法,但在他的實現類Enhancer中定義了generateClass方法,而實際上也是調用了Enhancer的方法。
public void generateClass(ClassVisitor v) throws Exception {
Class sc = (superclass == null) ? Object.class : superclass;
// Order is very important: must add superclass, then
// its superclass chain, then each interface and
// its superinterfaces.
List actualMethods = new ArrayList();
List interfaceMethods = new ArrayList();
final Set forcePublic = new HashSet();
getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic);
List methods = CollectionUtils.transform(actualMethods, new Transformer() {
public Object transform(Object value) {
Method method = (Method)value;
//...
return ReflectUtils.getMethodInfo(method, modifiers);
}
});
//類的開始
ClassEmitter e = new ClassEmitter(v);
e.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),
Type.getType(sc),
(useFactory ?
TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :
TypeUtils.getTypes(interfaces)),
Constants.SOURCE_FILE);
//聲明屬性
e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null);
e.declare_field(Constants.ACC_PUBLIC | Constants.ACC_STATIC, FACTORY_DATA_FIELD, OBJECT_TYPE, null);
e.declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null);
e.declare_field(Constants.PRIVATE_FINAL_STATIC, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null);
// This is declared private to avoid "public field" pollution
e.declare_field(Constants.ACC_PRIVATE | Constants.ACC_STATIC, CALLBACK_FILTER_FIELD, OBJECT_TYPE, null);
//構造函數
emitDefaultConstructor(e);
//實現回調
emitSetThreadCallbacks(e);
emitSetStaticCallbacks(e);
emitBindCallbacks(e);
//...
//類的結束
e.end_class();
}
複製代碼
本段代碼的核心是使用ClassEmitter(對asm ClassVisitor的封裝)構造出來一個類,這個類實現了代理目標類(BizA)的方法,而後也增長了方法攔截器的調用,從反編譯代碼中咱們能夠看出來
public final String doSomething() {
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null)
return ((String)tmp17_14.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy));
return super.doSomething();
}
複製代碼
注意這一段代碼
tmp17_14.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy));
複製代碼
實現了方法攔截器(MethodInterceptor)的調用。
在生成代理類的同時,cglib同時生成了另一個類FastClass,這個會在下面詳細介紹
從cglib方式的代碼示例中,咱們看到,實際上在intercept方法中,咱們是這麼調用的
proxy.invokeSuper(obj, args);
複製代碼
咱們進一步看看這個方法作了什麼
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
複製代碼
忽略掉init,其實是調用了FastClass的invoke方法
/** * Invoke the method with the specified index. * @see getIndex(name, Class[]) * @param index the method index * @param obj the object the underlying method is invoked from * @param args the arguments used for the method call * @throws java.lang.reflect.InvocationTargetException if the underlying method throws an exception */
abstract public Object invoke(int index, Object obj, Object[] args) throws InvocationTargetException;
複製代碼
這是一個抽象方法,方法的實現是在動態生成的FastClass子類中(稍後會貼出反編譯字節碼)。從方法描述中,咱們能看出來,FastClass根據不一樣的索引調用不一樣的方法(注意,這裏是直接調用而不是反射)。咱們看下反編譯代碼
public Object invoke(, Object paramObject, Object[] paramArrayOfObject) throws InvocationTargetException {
// Byte code:
// 0: aload_2
// 1: checkcast 133 com/cglib/BizA$$EnhancerByCGLIB$$eca2fdc
// 4: iload_1
// 5: tableswitch default:+304 -> 309, 0:+99->104, 1:+114->119....
//....
// 198: invokevirtual 177 com/cglib/BizA$$EnhancerByCGLIB$$eca2fdc:doSomething ()Ljava/lang/String;
//...
// 272: aconst_null
// 273: areturn
// 274: invokevirtual 199 com/cglib/BizA$$EnhancerByCGLIB$$eca2fdc:CGLIB$toString$2 ()Ljava/lang/String;
// 277: areturn
// 278: invokevirtual 201 com/cglib/BizA$$EnhancerByCGLIB$$eca2fdc:CGLIB$clone$4 ()Ljava/lang/Object;
// 281: areturn
// 330: athrow
//
// Exception table:
// from to target type
// 5 312 312 java/lang/Throwable
}
複製代碼
咱們看到,這裏使用了tableswitch,能夠認爲是switch語句。根據不一樣的值調用不一樣的方法。從198行中咱們能夠看到調用了doSomething方法。
這是cglib的一大優點,經過建立索引,減小了反射的過程,也提高了動態代理的性能。另外cglib代碼中大量使用了緩存,懶加載等機制,這對提高性能也有很多幫助。
cglib的代碼比較複雜,因爲文章篇幅緣由,咱們只是從動態代理實現的角度簡單說明。cglib代碼質量很是高,其中運用了大量的設計模式,對於線程安全,緩存,懶加載等都有涉及,有興趣的同窗能夠直接閱讀下源碼,我在附錄中貼出了源碼的連接。
注:經過設置類輸出目錄,能夠將cglib生成的類輸出
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\classes");
複製代碼
再研究了各自實現原理以後,咱們再次作個對比
JDK Proxy | cglib |
---|---|
經過操做字節碼實現,在生成的class中插入InvocationHandler的調用 | 在asm庫的基礎上進一步抽象封裝,進而生成代理類(Enhancer代理)和FastClass。固然cglib本質上也是操做字節碼。 |
代理接口方法調用時採用反射調用,反射性能較低 | 根據自建的索引直接調用方法,性能高 |
注:思考一下,爲何反射影響性能呢?
cglib:github.com/cglib/cglib
Spring AOP:docs.spring.io/spring/docs…
Spring Source Code:github.com/spring-proj…