1. 程式人生 > >AOP的底層實現-CGLIB動態代理和JDK動態代理

AOP的底層實現-CGLIB動態代理和JDK動態代理

        AOP是目前Spring框架中的核心之一,在應用中具有非常重要的作用,也是Spring其他元件的基礎。它是一種面向切面程式設計的思想。關於AOP的基礎知識,相信多數童鞋都已經瞭如指掌,我們就略過這部分,來講解下AOP的核心功能的底層實現機制:如何用動態代理來實現切面攔截。         AOP的攔截功能是由java中的動態代理來實現的。說白了,就是在目標類的基礎上增加切面邏輯,生成增強的目標類(該切面邏輯或者在目標類函式執行之前,或者目標類函式執行之後,或者在目標類函式丟擲異常時候執行。不同的切入時機對應不同的Interceptor的種類,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。         那麼動態代理是如何實現將切面邏輯(advise)織入到目標類方法中去的呢?下面我們就來詳細介紹並實現AOP中用到的兩種動態代理。         AOP的原始碼中用到了兩種動態代理來實現攔截切入功能:jdk動態代理和cglib動態代理。兩種方法同時存在,各有優劣。jdk動態代理是由java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行快取,這樣解決asm生成類過程低效問題)。還有一點必須注意:jdk動態代理的應用前提,必須是目標類基於統一的介面。如果沒有上述前提,jdk動態代理不能應用。由此可以看出,jdk動態代理有一定的侷限性,cglib這種第三方類庫實現的動態代理應用更加廣泛,且在效率上更有優勢。 一、jdk動態代理實現AOP攔截(程式碼中的關鍵地方都添加了註釋) 1、為目標類(target)定義統一的介面類Service,這個是jdk動態代理必須的前提。
package jdkproxy;

/**
 * 該類是所有被代理類的介面類,jdk實現的代理要求被代理類基於統一的介面
 * 
 * @author typ
 * 
 */
public interface Service {
	/**
	 * add方法
	 */
	public void add();

	/**
	 * update方法
	 */
	public void update();
}
2、目標類AService,我們的實驗目標就是在AService中add和update方法的前後實現攔截,加入自定義切面邏輯advise
package jdkproxy;

/**
 * 被代理類,即目標類target
 * 
 * @author typ
 * 
 */
public class AService implements Service {
	/*
	 * (non-Javadoc)
	 * 
	 * @see jdkproxy.Service#add()
	 */
	public void add() {
		System.out.println("AService add>>>>>>>>>>>>>>>>>>");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see jdkproxy.Service#update()
	 */
	public void update() {
		System.out.println("AService update>>>>>>>>>>>>>>>");
	}
}
3、實現動態代理類MyInvocationHandler,實現InvocationHandler介面,並且實現介面中的invoke方法。仔細看invoke方法,就是在該方法中加入切面邏輯的。目標類方法的執行是由mehod.invoke(target,args)這條語句完成。
package jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author typ
 *
 */
public class MyInvocationHandler implements InvocationHandler {
	private Object target;

	MyInvocationHandler() {
		super();
	}

	MyInvocationHandler(Object target) {
		super();
		this.target = target;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// 程式執行前加入邏輯,MethodBeforeAdviceInterceptor
		System.out.println("before-----------------------------");
		// 程式執行
		Object result = method.invoke(target, args);
		// 程式執行後加入邏輯,MethodAfterAdviceInterceptor
		System.out.println("after------------------------------");
		return result;
	}

}
4、測試類,其中增強的目標物件是由Proxy.newProxyInstance(aService.getClass().getClassLoader(), aService.getClass().getInterfaces(), handler);來生成的。
package jdkproxy;

import java.lang.reflect.Proxy;

/**
 * @author typ
 *
 */
public class Test {
	public static void main(String[] args) {
		Service aService = new AService();
		MyInvocationHandler handler = new MyInvocationHandler(aService);
		// Proxy為InvocationHandler實現類動態建立一個符合某一介面的代理例項
		Service aServiceProxy = (Service) Proxy.newProxyInstance(aService
				.getClass().getClassLoader(), aService.getClass()
				.getInterfaces(), handler);
		// 由動態生成的代理物件來aServiceProxy 代理執行程式,其中aServiceProxy 符合Service介面
		aServiceProxy.add();
		System.out.println();
		aServiceProxy.update();
		// 以下是對B的代理
		// Service bService = new BService();
		// MyInvocationHandler handler = new MyInvocationHandler(bService);
		// Service bServiceProxy = (Service) Proxy.newProxyInstance(bService
		// .getClass().getClassLoader(), bService.getClass()
		// .getInterfaces(), handler);
		// bServiceProxy.add();
		// System.out.println();
		// bServiceProxy.update();
	}
}
自此,jdk動態代理來實現AOP攔截機制的程式碼已經實現,下面我們看一下攔截的結果,程式輸出結果如下: before----------------------------- AService add>>>>>>>>>>>>>>>>>> after------------------------------ before----------------------------- AService update>>>>>>>>>>>>>>> after------------------------------
可以看到,在目標類AService的add和update方法前後已經加入了自定義的切面邏輯,AOP攔截機制生效了。
二、cglib動態代理實現AOP攔截(程式碼中的關鍵地方都添加了註釋) 1、目標類,cglib不需要定義目標類的統一介面
package cglibproxy;

/**
 * 被代理類,即目標物件target
 * 
 * @author typ
 * 
 */
public class Base {
	/**
	 * 一個模擬的add方法
	 */
	public void add() {
		System.out.println("add ------------");
	}
}
2、實現動態代理類CglibProxy,需要實現MethodInterceptor介面,實現intercept方法。該代理中在add方法前後加入了自定義的切面邏輯,目標類add方法執行語句為proxy.invokeSuper(object, args);
package cglibproxy;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 此為代理類,用於在pointcut處新增advise
 * 
 * @author typ
 * 
 */
public class CglibProxy implements MethodInterceptor {

	public Object intercept(Object object, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		// 新增切面邏輯(advise),此處是在目標類程式碼執行之前,即為MethodBeforeAdviceInterceptor。
		System.out.println("before-------------");
		// 執行目標類add方法
		proxy.invokeSuper(object, args);
		// 新增切面邏輯(advise),此處是在目標類程式碼執行之後,即為MethodAfterAdviceInterceptor。
		System.out.println("after--------------");
		return null;
	}

}
3、獲取增強的目標類的工廠Factory,其中增強的方法類物件是有Enhancer來實現的,程式碼如下所示:
package cglibproxy;

import net.sf.cglib.proxy.Enhancer;

/**
 * 工廠類,生成增強過的目標類(已加入切入邏輯)
 * 
 * @author typ
 * 
 */
public class Factory {
	/**
	 * 獲得增強之後的目標類,即添加了切入邏輯advice之後的目標類
	 * 
	 * @param proxy
	 * @return
	 */
	public static Base getInstance(CglibProxy proxy) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(Base.class);
		//回撥方法的引數為代理類物件CglibProxy,最後增強目標類呼叫的是代理類物件CglibProxy中的intercept方法
		enhancer.setCallback(proxy);
		// 此刻,base不是單純的目標類,而是增強過的目標類
		Base base = (Base) enhancer.create();
		return base;
	}
}
4、測試類
package cglibproxy;

/**
 * @author typ
 *
 */
public class Test {
	public static void main(String[] args) {
		CglibProxy proxy = new CglibProxy();
		// base為生成的增強過的目標類
		Base base = Factory.getInstance(proxy);
		base.add();
	}
}
自此,cglib動態代理實現的AOP攔截機制已經基本實現,下面我們來看一下攔截的效果如何,程式執行結果如下:
before------------- add ------------ after-------------- 可以看到,在目標類Base的add方法前後已經加入了自定義的切面邏輯,AOP攔截機制生效了。
此外,需要說明一下的是,cglib動態代理用到了第三方類庫,需要在專案中引入兩個jar包:cglib.jar和asm.jar。稍後會在csdn資源中上傳這兩個jar包。免積分的啊。 總之,AOP的核心機制和基本功能已經能夠通過動態代理來實現了,至於AOP中,如何從配置文件中得到目標類target、advisor的bean,如何判斷攔截器型別等問題,就藉助於Spring中另一個核心IOC來解決了,後續會有IOC的核心實現機制講解。