寫(xiě)在前面:本文面向的讀者為熟悉Java面向?qū)ο蟪绦蜷_(kāi)發(fā)陶珠、有過(guò)Spring開(kāi)源框架使用經(jīng)驗(yàn)的群體。
AOP通常被稱(chēng)為面向切面編程秦士,主要的作用是可以將那些分散在業(yè)務(wù)系統(tǒng)中相同的代碼抽取出來(lái)放到一個(gè)地方進(jìn)行管理拣凹。這么做的好處是減少了重復(fù)代碼的編寫(xiě),并且軟件的可維護(hù)性也強(qiáng)颅悉。為什么叫做面向切面編程呢?舉個(gè)例子:假如我們的代碼中迁匠,有許多以u(píng)pdate開(kāi)頭的函數(shù)的執(zhí)行都需要管理員權(quán)限剩瓶。如果不使用AOP,那么我們?cè)诿總€(gè)以u(píng)pdate開(kāi)頭的函數(shù)中都要進(jìn)行權(quán)限驗(yàn)證城丧,這樣導(dǎo)致了大量重復(fù)代碼的產(chǎn)生延曙。與此同時(shí),萬(wàn)一某天需求有變亡哄,不再限制只有管理員才能執(zhí)行這些函數(shù)枝缔,那么我們又要將原來(lái)代碼中和這個(gè)部分相關(guān)的代碼逐行移除,十分的麻煩蚊惯。引入了AOP之后愿卸,這項(xiàng)工作就變得簡(jiǎn)單了:我們可以將權(quán)限驗(yàn)證的代碼放在某個(gè)地方,然后通過(guò)某些特定的配置實(shí)現(xiàn)在執(zhí)行系統(tǒng)中以u(píng)pdate開(kāi)頭的函數(shù)之前截型,先執(zhí)行權(quán)限驗(yàn)證的代碼趴荸。如此,萬(wàn)一需求變了宦焦,我們也只要改一個(gè)地方的代碼发钝。那一個(gè)個(gè)以u(píng)pdate開(kāi)頭的函數(shù)就是切點(diǎn)顿涣,橫向地來(lái)看,可以把它們抽象成一個(gè)切面笼平,所以AOP被稱(chēng)為面向切面編程园骆。
AOP比較常見(jiàn)的應(yīng)用場(chǎng)景:日志記錄舔痪、性能統(tǒng)計(jì)寓调、安全認(rèn)證、事務(wù)處理锄码、異常處理等夺英。我們將這些代碼從業(yè)務(wù)邏輯代碼中分離出來(lái),通過(guò)對(duì)這些行為的分離滋捶,我們把它們獨(dú)立到非指導(dǎo)業(yè)務(wù)邏輯的代碼中痛悯,進(jìn)而改變這些代碼的時(shí)候不會(huì)影響到我們的業(yè)務(wù)邏輯代碼。并且業(yè)務(wù)邏輯代碼也感知不到它們的存在重窟,因?yàn)闃I(yè)務(wù)邏輯代碼“被代理了”载萌。
Spring AOP中代理機(jī)制的實(shí)現(xiàn)主要接觸了JDK動(dòng)態(tài)代理以及CGLIB動(dòng)態(tài)代理,如果有對(duì)這兩種動(dòng)態(tài)代理機(jī)制不熟悉的同學(xué)巡扇,可以參考我之前寫(xiě)過(guò)的博客:
深入理解JDK動(dòng)態(tài)代理機(jī)制
深入理解CGLIB動(dòng)態(tài)代理機(jī)制
下面再回顧一下Spring AOP中幾個(gè)基本的概念:
- 連接點(diǎn):目標(biāo)被增強(qiáng)的函數(shù)扭仁;
- Advice通知:定義在連接點(diǎn)做什么,為切面增強(qiáng)提供織入接口厅翔,有BeforeAdvice乖坠、AfterAdvice、ThrowsAdvice等刀闷;
- Pointcut切點(diǎn):Pointcut(切點(diǎn))決定Advice通知應(yīng)該作用于哪個(gè)連接點(diǎn)熊泵,也就是說(shuō)通過(guò)Pointcut來(lái)定義需要增強(qiáng)的方法的集合,這些集合可以按一定的規(guī)則來(lái)選取甸昏,如可以由特定的正則表達(dá)式或根據(jù)方法簽名進(jìn)行匹配顽分;
- Advisor通知器:將目標(biāo)方法的切面增強(qiáng)設(shè)計(jì)(Advice)和關(guān)注點(diǎn)的設(shè)計(jì)(Pointcut)結(jié)合起來(lái)。通過(guò)Advisor施蜜,可以定義該使用哪個(gè)通知并在哪個(gè)關(guān)注點(diǎn)使用它卒蘸。
1、Advice
Advice是AOP中的一個(gè)基本接口花墩,BeforeAdvice悬秉、AfterAdvice、ThrowsAdvice等都繼承了它冰蘑。
以前置通知(BeforeAdvice)為例和泌,我們來(lái)看看它的類(lèi)層次關(guān)系
在BeforeAdvice的繼承關(guān)系中,定義類(lèi)為待增強(qiáng)的目標(biāo)方法設(shè)置的前置增強(qiáng)接口MethodBeforeAdvice祠肥,使用這個(gè)前置接口需要實(shí)現(xiàn)一個(gè)回調(diào)函數(shù)before武氓,作為回調(diào)函數(shù),before方法的實(shí)現(xiàn)在Advice中被配置到目標(biāo)方法后,會(huì)在調(diào)用目標(biāo)方法時(shí)被回調(diào)县恕。
回調(diào)函數(shù)before的調(diào)用參數(shù)有:Method 對(duì)象东羹,這個(gè)參數(shù)是目標(biāo)方法的反射對(duì)象;Object [ ] 對(duì)象數(shù)組忠烛,這個(gè)對(duì)象數(shù)組包含目標(biāo)方法的輸入?yún)?shù)属提。
同樣的,在AfterAdvice繼承體系下的AfterReturningAdvice中也有相似的回調(diào)函數(shù)
2美尸、Pointcut切點(diǎn)
從Pointcut的基本接口定義中可以看到冤议,需要返回一個(gè)MethodMatcher。對(duì)于Point的匹配判斷功能师坎,具體是由這個(gè)MethodMatcher來(lái)完成的恕酸,也就是說(shuō),由這個(gè)MethodMatcher來(lái)判斷是否需要對(duì)當(dāng)前方法調(diào)用進(jìn)行增強(qiáng)胯陋,或者是否需要對(duì)當(dāng)前調(diào)用方法應(yīng)用配置好的Advice通知蕊温。
而在MethodMatcher接口中,有一個(gè)matcher方法遏乔,這個(gè)方法在匹配連接點(diǎn)的過(guò)程中起著至關(guān)重要的作用义矛。
在Pointcut的類(lèi)繼承體系中,MethodMatcher對(duì)象是可以配置成JdkRegexpMethodPointcut以及NameMatchMethodPointcut來(lái)完成方法的匹配判斷的按灶。在JdkRegexpMethodPointcut中症革,我們可以看到一個(gè)matches方法,這個(gè)matches方法是MethodMatcher定義的接口方法鸯旁。在JdkRegexpMethodPointcut實(shí)現(xiàn)中噪矛,這個(gè)matches方法就是使用正則表達(dá)式來(lái)對(duì)方法名進(jìn)行匹配判斷的。
在NameRegexpMethodPointcut中铺罢,給出了matches方法的另一個(gè)實(shí)現(xiàn)--根據(jù)方法的全限定名稱(chēng)進(jìn)行匹配
從圖2.4和圖2.5中我們可以看到艇挨,是在JdkDynamicAopProxy的invoke方法中出發(fā)了對(duì)matches方法的調(diào)用。這個(gè)invoke方法就是Proxy對(duì)象進(jìn)行代理回調(diào)的入口方法韭赘。
3. Advisor通知器
在Spring AOP中缩滨,我們以一個(gè)Advisor(DefaultPointcutAdvisor)的實(shí)現(xiàn)為例來(lái)了解Advisor的工作原理。
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
private Pointcut pointcut = Pointcut.TRUE;
/**
* Create an empty DefaultPointcutAdvisor.
* <p>Advice must be set before use using setter methods.
* Pointcut will normally be set also, but defaults to {@code Pointcut.TRUE}.
*/
public DefaultPointcutAdvisor() {
}
/**
* Create a DefaultPointcutAdvisor that matches all methods.
* <p>{@code Pointcut.TRUE} will be used as Pointcut.
* @param advice the Advice to use
*/
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
/**
* Create a DefaultPointcutAdvisor, specifying Pointcut and Advice.
* @param pointcut the Pointcut targeting the Advice
* @param advice the Advice to run when Pointcut matches
*/
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = pointcut;
setAdvice(advice);
}
/**
* Specify the pointcut targeting the advice.
* <p>Default is {@code Pointcut.TRUE}.
* @see #setAdvice
*/
public void setPointcut(Pointcut pointcut) {
this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public String toString() {
return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
}
}
在DefaultPointcutAdvisor中泉瞻,有兩個(gè)屬性脉漏,分別為Advice和Pointcut。通過(guò)這兩個(gè)屬性袖牙,我們可以分別配置Advice和Pointcut侧巨。在DefaultPointcutAdvisor中,pointcut默認(rèn)被設(shè)置為Pointcut.True鞭达,這在Pointcut接口中被定義為
Pointcut TRUE = TruePointcut.INSTANCE;
TruePointcut.INSTANCE是一個(gè)餓漢式的單例:
在TruePointcut的methodMatcher實(shí)現(xiàn)中司忱,使用TrueMethodMatcher作為方法匹配器皇忿。這個(gè)匹配器對(duì)任何的方法匹配都要求返回true的結(jié)果,也就是說(shuō)對(duì)任何方法名的匹配要求它都會(huì)返回匹配成功的結(jié)果坦仍。類(lèi)似地鳍烁,TrueMethodMatcher的實(shí)現(xiàn)也是一個(gè)單例模式。
4. Spring AOP的設(shè)計(jì)分析
我們前面提到過(guò)繁扎,在使用Spring AOP的過(guò)程中幔荒,我們可以通過(guò)配置達(dá)到在目標(biāo)對(duì)象執(zhí)行前或者執(zhí)行后進(jìn)行其他操作的目的。其實(shí)也就是AOP完成了一系列的過(guò)程锻离,為目標(biāo)對(duì)象建立了代理對(duì)象铺峭,然后啟動(dòng)代理對(duì)象的攔截器來(lái)完成各種橫切面的注入的過(guò)程。這個(gè)代理對(duì)象可以通過(guò)使用JDK動(dòng)態(tài)代理或者是CGLIB動(dòng)態(tài)代理來(lái)創(chuàng)建汽纠。同時(shí)震束,這一系列的織入設(shè)計(jì)是通過(guò)一系列的Adapter來(lái)實(shí)現(xiàn)的陶舞。通過(guò)一系列Adapter的設(shè)計(jì),可以把AOP的橫切面設(shè)計(jì)和Proxy模式有機(jī)地結(jié)合起來(lái)豌拙。
在Sprong的AOP模塊中钓账,代理對(duì)象的生成主要是通過(guò)配置和調(diào)用ProxyFactoryBean來(lái)完成的碴犬。在ProxyFactoryBean中封裝了主要代理對(duì)象的生成過(guò)程。
而在ProxyFactoryBean中梆暮,代理對(duì)象的生成是以getObject方法為入口的
public Object getObject() throws BeansException {
//初始化通知器鏈
initializeAdvisorChain();
//對(duì)singleton和prototype類(lèi)型加以區(qū)分服协,生成對(duì)應(yīng)的Proxy對(duì)象
if (isSingleton()) {
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}
為Proxy代理對(duì)象配置Advisor鏈?zhǔn)窃?strong>initializeAdvisorChain方法中完成的,這個(gè)初始化的過(guò)程中有一個(gè)標(biāo)志位advisorChainInitialized啦粹,這個(gè)標(biāo)志用來(lái)表示通知器是否已經(jīng)初始化偿荷。如果已經(jīng)初始化,那么這里就不會(huì)再進(jìn)行初始化唠椭,而是直接返回跳纳。由于ProxyFactoryBean實(shí)現(xiàn)了BeanFactoryAware接口,而在初始化Bean的過(guò)程中贪嫂,會(huì)對(duì)所有實(shí)現(xiàn)了該接口的Bean設(shè)置一個(gè)setBeanFactory的回調(diào)寺庄,即可以通過(guò)生成的Bean獲取對(duì)應(yīng)的BeanFactory,所以我們?cè)谶@里可以很方便的通過(guò)java this.beanFactory.getBean(name)
來(lái)獲得通知器
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (this.advisorChainInitialized) {
return;
}
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
}
// Globals can't be last unless we specified a targetSource using the property...
if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
throw new AopConfigException("Target required after globals");
}
// Materialize interceptor chain from bean names.
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
}
if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
else {
// If we get here, we need to add a named interceptor.
// We must check if it's a singleton or prototype.
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
// Add the real Advisor/Advice to the chain.
advice = this.beanFactory.getBean(name);
}
else {
// It's a prototype Advice or Advisor: replace with a prototype.
// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
}
this.advisorChainInitialized = true;
}
如getObject方法中所示力崇,如果是單例對(duì)象斗塘,則調(diào)用getSingletonInstance方法生成單例的代理對(duì)象,否則將調(diào)用newPrototypeInstance方法亮靴。
這里出現(xiàn)了AopProxy類(lèi)型的對(duì)象馍盟,Spring利用這個(gè)AopProxy接口類(lèi)把AOP代理對(duì)象的實(shí)現(xiàn)與框架的其他部分有效地分離開(kāi)來(lái)。AopProxy是一個(gè)接口台猴,它由兩個(gè)子類(lèi)實(shí)現(xiàn)朽合,一個(gè)是CglibAopProxy俱两,另一個(gè)是JdkDynamicProxy。即對(duì)這兩個(gè)AopProxy接口的子類(lèi)的實(shí)現(xiàn)曹步,Spring分別通過(guò)CGLIB和JDK來(lái)的AopProxy對(duì)象宪彩。
具體代理對(duì)象的生成是在ProxyFactoryBean的基類(lèi)AdvisedSupport的實(shí)現(xiàn)中借助AopProxyFactory完成的,這個(gè)代理對(duì)象要么從JDK中生成讲婚,要么借助CGLIB獲得尿孔。因?yàn)?strong>ProxyFactoryBean本身就是AdvisedSupport的子類(lèi),所以在ProxyFactoryBean中獲得AopFactory是比較方便的筹麸,可以在ProxyCreatorSupport中看到活合,具體的AopProxy是通過(guò)AopProxyFactory來(lái)生成的。至于需要生成什么樣的對(duì)象物赶,所有的信息都在AdvisedSupport里白指,這個(gè)對(duì)象也是生成AopProxy的方法的輸入?yún)?shù),這里設(shè)置為this本身酵紫,因?yàn)?strong>ProxyCreatorSupport本身就是AdvisedSupport的子類(lèi)告嘲。
在ProxyCreatorSupport中,我們通過(guò)createAopProxy生成AopProxy對(duì)象奖地。在上方代碼中我們可以看到是使用了一個(gè)AopProxyFactory橄唬。這個(gè)AopProxyFactory實(shí)際上使用的是DefaultAopProxyFactory。它作為AopProxyFactory的創(chuàng)建工廠對(duì)象参歹,是在ProxyFactoryBean的基類(lèi)ProxyCreatorSupport中被創(chuàng)建的仰楚。在創(chuàng)建AopProxyFactory時(shí),它被設(shè)置為DefaultAopProxyFactory犬庇。
在AopProxy代理對(duì)象生成的過(guò)程中僧界,需要考慮使用哪種生成方式。如果目標(biāo)對(duì)象是接口類(lèi)械筛,那么使用JDK動(dòng)態(tài)代理來(lái)生成代理對(duì)象捎泻;否則將使用CGLIB來(lái)生成目標(biāo)對(duì)象的代理對(duì)象。為了滿(mǎn)足不同類(lèi)型的代理對(duì)象的生成要求埋哟,DefaultAopProxyFactory作為AopProxy對(duì)象的生成工廠笆豁,可以根據(jù)不同的需求來(lái)生成這兩種AopProxy對(duì)象。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如果 targetClass 是接口類(lèi)赤赊,使用JDK來(lái)生成AopProxy
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 否則使用CGLIB來(lái)生成AopProxy對(duì)象
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
JDK動(dòng)態(tài)代理生成AopProxy代理對(duì)象
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 調(diào)用JDK生成Proxy的地方
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
CGLIB動(dòng)態(tài)代理生成AopProxy代理對(duì)象
@Override
public Object getProxy() {
return getProxy(null);
}
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
}
// 從Advised中獲得在IoC容器里配置的target對(duì)象
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// 驗(yàn)證代理對(duì)象的接口設(shè)置
// 創(chuàng)建并配置CGLIB的Enhancer闯狱,這個(gè)Enhancer對(duì)象是CGLIB的主要操作類(lèi)
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
通過(guò)使用AopProxy對(duì)象封裝目標(biāo)對(duì)象之后,ProxyFactoryBean的getObject方法得到的對(duì)象就不是一個(gè)普通的Java對(duì)象了抛计,而是一個(gè)AopProxy代理對(duì)象哄孤。這時(shí)已經(jīng)不會(huì)讓?xiě)?yīng)用調(diào)用在ProxyFactoryBean中配置的target目標(biāo)的方法實(shí)現(xiàn),而是作為AOP實(shí)現(xiàn)的一部分吹截。對(duì)target目標(biāo)對(duì)象的方法調(diào)用會(huì)首先被AopProxy代理對(duì)象攔截瘦陈,對(duì)于不同的AopProxy代理對(duì)象生成方式凝危,會(huì)使用不同的攔截回調(diào)入口。