Spring基礎(chǔ)之AOP的概念介紹
Spring容器包含兩個重要的特性:面向切面編程(AOP)和控制反轉(zhuǎn)(IOC)。面向切面編程是面向?qū)ο螅∣OP)的一種補充,在面向?qū)ο缶幊痰倪^程中編程針對的目標(biāo)是一個個對象,而面向切面編程中編程針對的目標(biāo)是一個個切面。切面支持跨類型跨對象(如事務(wù)的切面可以加在任何地方)進(jìn)行模塊化。
前言
AOP是Spring的關(guān)鍵特性之一,雖然Spring的IOC特性并不依賴于AOP(意味著你可以只使用Spring的IOC特性而不使用AOP特性),但是二者結(jié)合起來可以靈活的實現(xiàn)很多中間件解決方案。比如我們經(jīng)常使用的事務(wù)(@Transaction),就是通過AOP方案實現(xiàn)的。本文重點介紹AOP編程中的一些術(shù)語,這些術(shù)語不僅僅局限于Spring,它適用于所有的AOP編程。
- 切面(Aspect):面向切面編程可以跨類跨對象進(jìn)行切面編程,一個切面就是對一類橫切關(guān)注點的模塊化。
- 切入點(JoinPoint):程序執(zhí)行過程中的一個點,如方法調(diào)用、字段訪問和異常拋出等。
- 增強(Advice):用于對切面增強,包含前增強、后增強和環(huán)繞增強。大多數(shù)AOP框架會對切入點進(jìn)行攔截,并在切入點維護(hù)一個攔截器鏈。
- 目標(biāo)對象(TargetObject):包含一個或者多個切面的對象。
- AOP代理(AOPProxy):通過Java動態(tài)代理或者CGLib增強得到的代理對象。
- 織入(Weaving):將切面整合到完整的流執(zhí)行流程。
Spring的AOP的功能和目標(biāo)
Spring的AOP使用純Java語言實現(xiàn)(如AspectJ就不是Java語言),不需要任何額外的編譯流程,不需要修改類加載器,適用于任何Servlet容器和應(yīng)用服務(wù)。Spring的AOP只支持方法攔截,不支持字段攔截,如果用戶需要使用字段攔截,可以考慮引入AspectJ等類似的框架。
Spring的AOP框架和其它的框架有些不同,Spring的Aop框架不僅僅是為了提供一個AOP功能,它更重要的功能是和Spring的IOC容器結(jié)合,提供一些企業(yè)應(yīng)用服務(wù)的解決方案(如事務(wù)等),我們可以和定義一個普通Bean一樣的方式去定以一個切面。Spring的AOP不支持非常細(xì)粒度的AOP,只支持對容器中的Bean進(jìn)行AOP,如果需要更細(xì)粒度的AOP,可以考慮使用AspectJ。Spring容器的一個優(yōu)秀的特性就是非侵入性,所以你可以靈活的選擇自己的AOP方案,不一定非要使用Spring的方案。
代理方式
Spring對實現(xiàn)接口的方法默認(rèn)使用Java動態(tài)代理實現(xiàn)AOP攔截,對于非接口方法則會使用CGLIB字節(jié)碼工具實現(xiàn)代理。
@AspectJ的支持
@AspectJ注解可以把一個普通的Java類聲明為切面。@AspectJ注解是AspectJ5引入的注解,Spring雖然可以讀取AspectJ5的注解用于切面元數(shù)據(jù)的配置,但是在運行的時候使用的仍然是Spring AOP進(jìn)行代理,而沒有使用AspectJ的編譯器或者織入邏輯。我們會在后續(xù)討論如何在Spring中使用AspectJ的編譯器和織入邏輯。
啟用@AspectJ
Spring默認(rèn)沒有啟用AspectJ,如果你需要Spring支持@AspectJ注解對應(yīng)的切面,可以通過在配置類上添加注解或者使用XML啟用配置(AspectJ所在的包是:aspectjweaver.jar)。
通過Java注解啟用AspectJ注解支持:
@Configuration @EnableAspectJAutoProxy public class AppConfig { }
通過XML配置啟用AspectJ注解
<aop:aspectj-autoproxy/>
定義一個切面
當(dāng)啟用AspectJ支持之后,開發(fā)者定義的任何Aspect切面會自動地被檢測到,然后Spring AOP會對切面進(jìn)行攔截。下面兩個例子展示了如何配置AspectJ切面,你可以通過添加@Component注解把切面Bean注冊到Spring容器中。
<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect"> <!-- configure properties of the aspect here --> </bean>
package org.xyz; import org.aspectj.lang.annotation.Aspect; @Aspect public class NotVeryUsefulAspect { }
聲明一個切入點
切入點程序運行過程中我們感興趣的一個點,Spring的AOP框架只支持發(fā)現(xiàn)對Spring Bean方法上的切入點,因此你可以簡單的把切入點理解為SpringBean的方法。
切入點確定感興趣的連接點,從而使我們能夠控制何時運行通知。springaop只支持springbean的方法執(zhí)行連接點,因此可以將切入點看作與springbean上方法的執(zhí)行相匹配。切入點聲明由兩部分組成:一部分是由名稱和任何參數(shù)組成的簽名,另一部分是確定我們感興趣的方法執(zhí)行的切入點表達(dá)式。在AOP的@AspectJ注釋樣式中,切入點簽名由常規(guī)方法定義提供,切入點表達(dá)式由@pointcut注釋指示(用作切入點簽名的方法必須具有void返回類型)。切入點由兩部分組成,一部分是用于區(qū)別不同切入點的標(biāo)識(下面例子中的 private void anyOldTransfer() {}
)),另外一部分是確定我們感興趣的Bean方法的表達(dá)式(下面例子中的 @Pointcut("execution(* transfer(..))")
), 下面的例子展示了一個切入點的定義:
@Pointcut("execution(* transfer(..))") // the pointcut expression private void anyOldTransfer() {} // the pointcut signature
Spring匹配切入點的語法使用了AspectJ5中的表達(dá)式語法,我們可以參考AspectJ文檔相關(guān)的語法.
常見的切入點匹配表達(dá)
Spring支持下面常見的AspectJ切面定義語法:
- execution:用于匹配方法的連接點。
@Pointcut("execution(public * *(..))") private void anyPublicOperation() {}
- within:用于匹配類型內(nèi)的方法。
@Pointcut("within(com.xyz.myapp.trading..*)") private void inTrading() {}
- this:匹配當(dāng)前AOP代理對象的執(zhí)行方法
@target(org.springframework.transaction.annotation.Transactional)
- target:target匹配目標(biāo)對象的類型,即被代理對象的類型,例如A繼承了B接口,則使用target("B"),target("A")均可以匹配到A
// 當(dāng)前AOP對象實現(xiàn)了 IPointcutService接口的任何方法 @Pointcut("target(cn.javass.spring.chapter6.service.IPointcutService)") private void anyPublicOperation() {}
- args:用于限定切點方法的參數(shù)類型。
args(java.io.Serializable)
- @target:被代理對象應(yīng)該包含指定的注解。
@target(org.springframework.transaction.annotation.Transactional)
- @args: 被代理對象的參數(shù)包含指定的注解。
@args(com.xyz.security.Classified)
- @within: 被代理的對象應(yīng)包含指定注解
@within(org.springframework.transaction.annotation.Transactional)
- @annotation:切入點包含指定的注解。
@annotation(org.springframework.transaction.annotation.Transactional)
我們可以通過“&&”和“||”對多個條件進(jìn)行組合,AspectJ還有很多其它的表達(dá)式,但是Spring不支持除上述表達(dá)式以外的其它表達(dá)式。AspectJ其它表達(dá)式包含: call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this, @withincode等。
我們在使用Spring的代理方法之前,應(yīng)該知道其代理原理。Java動態(tài)代理只能攔截public接口方法上的調(diào)用,CGLib只能攔截public、protected和defult方法。如果你需要更深層次的攔截,可以考慮使用底層的Aspectj。
切面的增強
我們在上面的步驟定義好了一個切入點,我們現(xiàn)在就可以對這個切入點進(jìn)行額外操作,這些額外操作被稱為增強,Spring支持四種增強方式:前增強、后增強、異常增強和環(huán)繞增強。Spring支持在增強方法的定義上直接定義切入點。
前增強BeforeAdvice
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class BeforeExample { @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()") public void doAccessCheck() { // ... } }
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class BeforeExample { @Before("execution(* com.xyz.myapp.dao.*.*(..))") public void doAccessCheck() { // ... } }
后增強
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterReturning; @Aspect public class AfterReturningExample { @AfterReturning( pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()", returning="retVal") public void doAccessCheck(Object retVal) { // ... } }
異常增強
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterThrowing; @Aspect public class AfterThrowingExample { @AfterThrowing( pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()", throwing="ex") public void doRecoveryActions(DataAccessException ex) { // ... } }
環(huán)繞增強
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.ProceedingJoinPoint; @Aspect public class AroundExample { @Around("com.xyz.myapp.CommonPointcuts.businessService()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed(); // stop stopwatch return retVal; } }
代理機(jī)制
我們前面說過,Spring AOP通過動態(tài)代理和CGLIB實現(xiàn)AOP對象的代理。我們可以通過如下配置設(shè)置動態(tài)代理全部走CGLIB。
<aop:config proxy-target-class="true"> <!-- other beans defined here... --> </aop:config>
代理工廠的使用
Spring AOP實現(xiàn)代理的核心類是 AspectJProxyFactory
,我們可以使用這個類編程式生成代理對象:
// create a factory that can generate a proxy for the given target object AspectJProxyFactory factory = new AspectJProxyFactory(targetObject); // add an aspect, the class must be an @AspectJ aspect // you can call this as many times as you need with different aspects factory.addAspect(SecurityManager.class); // you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect factory.addAspect(usageTracker); // now get the proxy object... MyInterfaceType proxy = factory.getProxy()
到此這篇關(guān)于Spring基礎(chǔ)之AOP的概念介紹的文章就介紹到這了,更多相關(guān)spring aop概念內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 中用split分割字符串,最后的空格等不被拆分的方法
下面小編就為大家?guī)硪黄猨ava 中用split分割字符串,最后的空格等不被拆分的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02Java連接 JDBC基礎(chǔ)知識(操作數(shù)據(jù)庫:增刪改查)
這篇文章主要介紹了Java連接 JDBC基礎(chǔ)知識,包括操作數(shù)據(jù)庫之增刪改查操作,需要的朋友可以參考下2021-04-04Java Swing實現(xiàn)坦克大戰(zhàn)游戲
這篇文章主要介紹了Java Swing實現(xiàn)坦克大戰(zhàn)游戲,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java的小伙伴們有很大的幫助喲,需要的朋友可以參考下2021-05-05關(guān)于java中構(gòu)造函數(shù)的一些知識詳解
下面小編就為大家?guī)硪黄P(guān)于java中構(gòu)造函數(shù)的一些知識詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12