1.@Aspect注解
(1) @Aspect注解用于聲明一個(gè)切面類,我們可在該類中來自定義切面,早在Spring之前,AspectJ框架中就已經(jīng)存在了這么一個(gè)注解,而Spring為了提供統(tǒng)一的注解風(fēng)格,因此采用了和AspectJ框架相同的注解方式,這便是@Aspect注解的由來,換句話說,在Spring想做AOP框架之前,AspectJ AOP框架就已經(jīng)很火了,而直接把AspectJ搬過來又不現(xiàn)實(shí),因此,Spring想了一個(gè)折中的方案,即只使用AspectJ框架的聲明,寫法和定義方式(比如@Aspect注解),而底層由Spring自己實(shí)現(xiàn),這樣,就避免了我們程序員從AspectJ AOP切換到Spring AOP后,還要再去學(xué)一套新的寫法了,也正因?yàn)槿绱?,如果想要使用Spring AOP,就必須依賴aspectjweaver.jar包(不然誰來提供寫法和定義方式),我們可以通過maven進(jìn)行導(dǎo)入,如下
<!-- 添加對AspectJ框架的依賴 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<!-- 除了上面的方式外,也可以直接使用spring-aspects依賴,它里面包含了對AspectJ的依賴 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.framework.version}</version>
</dependency>
(2) 同時(shí)還需使用@EnableAspectJAutoProxy注解來開啟Spring對于AspectJ注解的支持,如下
@Configuration
@EnableAspectJAutoProxy
public class Config {
}
如果是基于xml的配置,可通過如下標(biāo)簽進(jìn)行開啟
<aop:aspectj-autoproxy/>
2.自定義一個(gè)切面類
(1) 在基于注解的配置下,除了使用@Aspect注解外,還需要聲明該切面是一個(gè)bean,否則,spring在掃描過程中是會忽略掉這個(gè)類的,如下
@Aspect
@Component
public class Logger {
}
(2) 對上面的例子,基于xml配置的寫法如下
@Aspect
public class Logger {
}
<!-- xml配置文件中 -->
<beans ...>
<!-- 無論何種配置方式,不要忘了將切面類注冊為spring的一個(gè)bean -->
<bean id="logger" class="cn.example.spring.boke.Logger"></bean>
</beans>
(3) 由@Aspect注解標(biāo)注的類,稱之為切面類,與普通的類一樣,都有成員方法與成員變量,不同的是,切面類還可以包含連接點(diǎn),通知,引介等與AOP有關(guān)的東西
(4) 切面不能再被增強(qiáng),如果想拿一個(gè)切面來增強(qiáng)另一個(gè)切面,是不可能的,Spring會將切面類從自動(dòng)代理(auto-proxying)中排除
3.自定義一個(gè)切入點(diǎn)
(1) Spring AOP中的切入點(diǎn)目前只可能是bean中的方法,而對于一個(gè)普通類中的方法,是不可能成為切入點(diǎn)的,在Spring中,聲明一個(gè)切入點(diǎn)主要包括兩個(gè)部分:一個(gè)切入點(diǎn)簽名以及一個(gè)切入點(diǎn)表達(dá)式,如下
//如下定義了一個(gè)叫做anyExampleAMethod的切入點(diǎn),這個(gè)切入點(diǎn)會匹配cn.example.spring.boke包下的ExampleA類中的任何方法
//其中,(1)就代表的是切入點(diǎn)表達(dá)式,(2)就代表的是切入點(diǎn)簽名,注意,這個(gè)簽名的返回值必須是void
@Pointcut("execution(* cn.example.spring.boke.ExampleA.*(..))") //(1)
public void anyExampleAMethod() {} //(2)
(2) Spring AOP的切入點(diǎn)表達(dá)式中,支持如下等切入點(diǎn)標(biāo)識符
-
execution:最為常用,用于匹配某個(gè)包,某個(gè)類中的方法
-
within:進(jìn)行類型匹配,用于匹配某個(gè)包下所有類的所有方法或某個(gè)指定類中的所有方法,如下
//指定了within的類型,這個(gè)切入點(diǎn)會匹配cn.example.spring.boke包下ExampleA類中的任何方法
@Pointcut("within(cn.example.spring.boke.ExampleA)")
public void withinDesignator(){}
- this:進(jìn)行類型匹配,用于匹配生成的代理對象的類型是否為指定類型,如下
//此前我們提到過,Spring AOP中的底層實(shí)現(xiàn)分為jdk動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理,jdk動(dòng)態(tài)代理基于接口,要求目標(biāo)對象必須實(shí)現(xiàn)某個(gè)接口,而cglib動(dòng)態(tài)代理基于繼承,因此不同的實(shí)現(xiàn)方式下,導(dǎo)致Spring生成的代理對象的類型可能不同,這就是this標(biāo)識符的基礎(chǔ)
//首先定義一個(gè)接口
public interface Parent {
void register();
void sendEmail();
}
//讓我們的ExampleA類,實(shí)現(xiàn)這個(gè)接口
@Component
public class ExampleA implements Parent{
public void register() {
}
public void sendEmail() {
}
}
//設(shè)置@EnableAspectJAutoProxy注解中的proxyTargetClass屬性值為false,表示使用jdk動(dòng)態(tài)代理,為true,表示使用cglib動(dòng)態(tài)代理,默認(rèn)值為false,不過我們這里顯式的聲明出來
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ComponentScan(basePackages = "cn.example.spring.boke")
public class Config {
}
//切面類,在其中聲明一個(gè)this標(biāo)識符,并指定類型為ExampleA
@Aspect
@Component
public class Logger {
/**
* this標(biāo)識符,進(jìn)行類型匹配,用于匹配代理對象的類型是否為指定類型
*/
@Pointcut(value = "this(cn.example.spring.boke.ExampleA)")
public void thisDesignator(){}
@Around(value = "thisDesignator()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(new Date() + " 開始執(zhí)行...");
joinPoint.proceed();
System.out.println(new Date() + " 結(jié)束執(zhí)行...");
}
}
//執(zhí)行如下打印方法,可見通知未被執(zhí)行,原因就是因?yàn)槲覀兪褂昧薺dk動(dòng)態(tài)代理,Spring為我們生成的代理對象繼承自jdk中的Proxy類并實(shí)現(xiàn)了Parent接口,它不屬于ExampleA類型,自然而然切入點(diǎn)匹配失敗,我們的通知未被執(zhí)行
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ctx.getBean(Parent.class).register();
ctx.getBean(Parent.class).sendEmail();
//打印一下系統(tǒng)中代理對象的類型是否為ExampleA,結(jié)果為false
System.out.println(ctx.getBean(Parent.class) instanceof ExampleA);
//為了能進(jìn)行匹配,我們可以將@EnableAspectJAutoProxy中的proxyTargetClass屬性設(shè)置為true,使用cglib動(dòng)態(tài)代理,這時(shí)再執(zhí)行上面的打印方法,通知就會被執(zhí)行了,原因就是因?yàn)槭褂昧薱glib動(dòng)態(tài)代理后,Spring為我們生成的代理對象是繼承自ExampleA,當(dāng)然屬于ExampleA類型,因此通知會被執(zhí)行
@EnableAspectJAutoProxy(proxyTargetClass = true)
-
target:進(jìn)行類型匹配,用于匹配目標(biāo)對象的類型是否為指定類型,跟上面的this類似
-
args:進(jìn)行方法參數(shù)匹配,用于匹配方法的參數(shù)類型是否為指定類型,如下
//ExampleA中的register方法的參數(shù)為String
@Component
public class ExampleA{
public void register(String name) {
}
public void sendEmail() {
}
}
@Aspect
@Component
public class Logger {
/**
* 指定了args參數(shù)的類型為String,因此只會與ExampleA中的register方法匹配
*/
@Pointcut(value = "args(java.lang.String)")
public void argsDesignator() {}
@Around(value = "argsDesignator()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(new Date() + " 開始執(zhí)行...");
joinPoint.proceed();
System.out.println(new Date() + " 結(jié)束執(zhí)行...");
}
}
-
@target:用于匹配目標(biāo)對象的類上有沒有標(biāo)注指定注解
-
@args:用于匹配方法的參數(shù)的所屬類上有沒有標(biāo)注指定注解
-
@within:用于匹配某個(gè)類上有沒有標(biāo)注指定注解
-
@annotation:最常用,用于匹配某個(gè)方法上有沒有標(biāo)注指定注解
(3) Spring的AOP是基于代理實(shí)現(xiàn)的,因此,在目標(biāo)對象中進(jìn)行內(nèi)部調(diào)用是不會被攔截的(即this指針會導(dǎo)致AOP失效問題),此外,對于jdk動(dòng)態(tài)代理,只能攔截public方法,而對于cglib動(dòng)態(tài)代理,會攔截public和protected方法(package-visible 方法在配置后也能被攔截)
(4) Spring AOP還提供了一個(gè)PCD bean,用于按照bean的名稱進(jìn)行切入,它是Spring AOP獨(dú)有的,如下
//匹配所有beanName以A結(jié)尾的bean
@Pointcut("bean(*A)")
public void pcd() {}
4.組合切入點(diǎn)表達(dá)式
(1) 可以通過 &&,|| 和 !來組合切入點(diǎn)表達(dá)式,如下
//切入所有public方法
@Pointcut("execution(public * *(..))")
public void allPublicMethod() {}
//切入boke包下所有類中的所有方法
@Pointcut("within(cn.example.spring.boke.*)")
public void methodInBokePackage() {}
//使用 && 操作符,將上面兩個(gè)切入點(diǎn)表達(dá)式組合起來,即切入boke包下所有類中的所有public方法
@Pointcut("allPublicMethod() && methodInBokePackage()")
public void allPublicMethodInBokePackage() {}
5.常見切入點(diǎn)表達(dá)式例子
(1)在實(shí)際工作中,我們的切入點(diǎn)表達(dá)式的通常形式為:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?),其中ret-type-pattern表示一個(gè)方法的返回類型, 用 * 號可以代表任何類型; name-pattern表示方法名稱,用 * 可以進(jìn)行全部或部分名稱匹配; param-pattern表示方法參數(shù),其中用()代表無參方法,用(..)代表任何數(shù)量的參數(shù)(0個(gè)或多個(gè)),用()代表1個(gè)參數(shù),(, String)代表有兩個(gè)參數(shù),第一個(gè)參數(shù)可以是任何類型,而第二個(gè)參數(shù)只能是String類型; 除此之外,其他帶有 ? 的都是選填項(xiàng),可不填寫
(2)常見例子
//匹配任意public方法
execution(public * *(..))
//匹配任意名稱以set開頭的方法
execution(* set*(..))
//匹配com.xyz.service包下,AccountService類下的任意方法
execution(* com.xyz.service.AccountService.*(..))
//匹配com.xyz.service包下,任意類下的任意方法
execution(* com.xyz.service.*.*(..))
//匹配com.xyz.service包及其子包下,任意類下的任意方法
execution(* com.xyz.service..*.*(..))
//匹配com.xyz.service包下,任意類下的任意方法
within(com.xyz.service.*)
//匹配com.xyz.service包及其子包下,任意類下的任意方法
within(com.xyz.service..*)
//匹配代理對象的類型為AccountService的類下的任意方法
this(com.xyz.service.AccountService)
//匹配目標(biāo)對象的類型為AccountService的類下的任意方法
target(com.xyz.service.AccountService)
//匹配方法參數(shù)只有一個(gè)且參數(shù)類型為Serializable的方法,注意它與execution(* *(java.io.Serializable))的一點(diǎn)區(qū)別:execution這個(gè)例子只能匹配參數(shù)類型為Serializable的方法,如果說某個(gè)方法的參數(shù)類型是Serializable的子類,是不會匹配的,而下面args這個(gè)例子可以匹配參數(shù)類型為Serializable或其子類的方法
args(java.io.Serializable)
//匹配標(biāo)注了@Transactional注解的目標(biāo)對象中的任意方法
@target(org.springframework.transaction.annotation.Transactional)
//匹配標(biāo)注了@Transactional注解的類中的任意方法
@within(org.springframework.transaction.annotation.Transactional)
//匹配標(biāo)注了@Transactional注解的任意方法
@annotation(org.springframework.transaction.annotation.Transactional)
//匹配方法的參數(shù)有且只有一個(gè)且該參數(shù)的所屬類上標(biāo)注了@Classified注解的任意方法
@args(com.xyz.security.Classified)
//匹配beanName為tradeService的bean中的任意方法
bean(tradeService)
//匹配所有以Service作為beanName結(jié)尾的bean中的任意方法
bean(*Service)
6.編寫良好的pointcuts
(1)Spring將切入點(diǎn)標(biāo)識符分為3大類,分別為:
- Kinded:類型標(biāo)識符,如execution, get, set, call等,它們都是根據(jù)類型進(jìn)行選擇,比如execution選擇的都是可執(zhí)行方法這一類型,其中除了execution,其他的都是AspectJ框架提供的
- Scoping:范圍標(biāo)識符,如within;
- Contextual:上下文標(biāo)識符,如this, target和@annotation,它們都是根據(jù)方法所處的環(huán)境(比如在哪個(gè)類中)進(jìn)行選擇
Spring建議一個(gè)良好的切入點(diǎn)表達(dá)式應(yīng)該至少包括前兩種類型(kinded和scoping,在這兩種標(biāo)識符中scoping又特別重要,因?yàn)樗钠ヅ渌俣确浅??,可以快速的排除掉那些不?yīng)該被處理的方法),此外在有根據(jù)上下文環(huán)境的需求時(shí),可以包括contextual標(biāo)識符
7.聲明一個(gè)通知
(1)在前面已經(jīng)提及過通知,它是增強(qiáng)的邏輯,與切入點(diǎn)相關(guān)聯(lián),會在切入點(diǎn)執(zhí)行前或執(zhí)行后執(zhí)行,在Spring中總共分為5大類,如下
- Before Advice:使用@Before注解可定義前置通知,它會在切入點(diǎn)執(zhí)行之前執(zhí)行
@Aspect
@Component
public class Logger {
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void before() {
//...
}
}
- After Returning Advice:使用@AfterReturning注解可定義返回通知,它會在切入點(diǎn)"正常"執(zhí)行之后執(zhí)行
//一個(gè)普通的bean ExampleA
@Component
public class ExampleA{
public String doSomething() {
return "finish";
}
}
//有時(shí)候,我們可能需要訪問切入點(diǎn)執(zhí)行后的返回值,那么我們可以使用@AfterReturning注解中的returning屬性來指定返回值的名稱,然后再給這個(gè)切面方法添加一個(gè)形參,這個(gè)形參類型即為切入點(diǎn)執(zhí)行后的返回值類型(或其父類型,但不能完全不一致,否則切面會切入失敗),形參名要與剛剛設(shè)置過的returning屬性值一致,如下例
@Aspect
@Component
public class Logger {
@AfterReturning(value = "execution(* cn.example.spring.boke.ExampleA.*(..))", returning = "returnVal")
public void afterReturning(Object returnVal) {
System.out.println(new Date() + " 開始執(zhí)行...");
System.out.println(returnVal);
System.out.println(new Date() + " 結(jié)束執(zhí)行...");
}
}
- After Throwing Advice:使用@AfterThrowing注解可定義異常通知,它會在切入點(diǎn)觸發(fā)異常之后執(zhí)行
//同樣,我們有時(shí)候也期望訪問切入點(diǎn)執(zhí)行過程中拋出的異常,與返回通知一致,例子如下
@Aspect
@Component
public class Logger {
@AfterThrowing(value = "execution(* cn.example.spring.boke.ExampleA.*(..))", throwing = "throwable")
public void afterReturning(Throwable throwable) {
System.out.println(new Date() + " 開始執(zhí)行...");
System.out.println(throwable);
System.out.println(new Date() + " 結(jié)束執(zhí)行...");
}
}
- After (Finally) Advice:使用@After注解可定義后置通知,它會在切入點(diǎn)無論以何種方式執(zhí)行(正?;虍惓#┖髨?zhí)行,常用于釋放資源等目的,類似于try-catch語句中的finally塊,它與@AfterReturning的區(qū)別是,@AfterReturning只適用于切入點(diǎn)正常返回
@Aspect
@Component
public class Logger {
@After(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void afterReturning() {
//...
}
}
- Around advice:使用@After注解可定義環(huán)繞通知,它既可以在切入點(diǎn)之前執(zhí)行通知,又可以在切入點(diǎn)之后執(zhí)行,甚至可以不用執(zhí)行切入點(diǎn),是最為靈活強(qiáng)大的通知
@Aspect
@Component
public class Logger {
//環(huán)繞通知方法可不聲明形參,但如果要聲明形參,第一個(gè)形參的類型必須是ProceedingJoinPoint類型,對ProceedingJoinPoint調(diào)用proceed方法后會導(dǎo)致切入點(diǎn)真正的執(zhí)行,此外,proceed方法還有一個(gè)重載方法,我們可以對它傳遞一個(gè)Object[],那么當(dāng)切入點(diǎn)執(zhí)行時(shí)會以這個(gè)數(shù)組中的值作為方法參數(shù)值來執(zhí)行
//我們可以調(diào)用一次,多次或根本不調(diào)用proceed方法
@Around(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void afterReturning(ProceedingJoinPoint joinPoint) {
Object returnVal = null;
try {
//...
returnVal = joinPoint.proceed();
//...
} catch (Throwable e) {
//...
e.printStackTrace();
}
return returnVal;
}
}
8.切入點(diǎn)信息獲取
(1)有時(shí)候,我們期望獲取到切入點(diǎn)相關(guān)信息,比如它的簽名,形參等信息,Spring為我們提供了JoinPoint類型,用于獲取相關(guān)信息,在前面的環(huán)繞通知的例子中,我們就已經(jīng)使用了JoinPoint的子類型ProceedingJoinPoint,它添加了proceed方法,來顯式的調(diào)用執(zhí)行切入點(diǎn)
@Aspect
@Component
public class Logger {
//除了下面的例子外,使用JoinPoint,還可以獲取到切入點(diǎn)的其他一些信息,可參考api文檔
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void before(JoinPoint joinPoint) {
System.out.println("被攔截的類:" + joinPoint.getTarget().getClass().getName());
System.out.println("被攔截的方法:" + ((MethodSignature) joinPoint.getSignature()).getMethod().getName());
System.out.println("被攔截的方法參數(shù):" + Arrays.toString(joinPoint.getArgs()));
}
}
9.通知執(zhí)行順序
(1)不同切面類中的通知,在默認(rèn)情況下,按照所在切面類名的字典序排序,若其排序越高則優(yōu)先級也就越高,如下
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "cn.example.spring.boke")
public class Config { }
@Component
public class ExampleA{
public void doSomething() {
System.out.println("doSomething...");
}
}
//聲明兩個(gè)切面類TimerLogger和OperationLogger
@Aspect
@Component
public class TimerLogger {
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void a() {
System.out.println("timer...");
}
}
@Aspect
@Component
public class OperationLogger {
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void a() {
System.out.println("operation...");
}
}
//啟動(dòng)容器,觀察打印結(jié)果
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ctx.getBean(ExampleA.class).doSomething();
//打印結(jié)果如下,OperationLogger中的前置通知先執(zhí)行,TimerLogger中的前置通知后執(zhí)行,就是因?yàn)镺的字典序列大于T,因此OperationLogger中的通知的優(yōu)先級高于TimerLogger中的,而對于前置通知而言,優(yōu)先級越高的越先執(zhí)行,對于后置通知,優(yōu)先級越高的越后執(zhí)行
operation...
timer...
doSomething...
//我們可以將TimerLogger改為ATimerLogger,這樣的話它里面的前置通知就會先執(zhí)行了
@Aspect
@Component
public class ATimerLogger {
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void a() {
System.out.println("timer...");
}
}
(2)我們可以對切面類實(shí)現(xiàn)Ordered接口或添加@Order注解來顯示的指定優(yōu)先級,其中指定的值越小,優(yōu)先級越高
//此時(shí)TimerLogger的優(yōu)先級高于OperationLogger
@Aspect
@Component
@Order(1)
public class TimerLogger {
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void a() {
System.out.println("timer...");
}
}
@Aspect
@Component
@Order(2)
public class OperationLogger {
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void a() {
System.out.println("operation...");
}
}
(3)對于同個(gè)切面類中的相同類型的通知,其優(yōu)先級只與通知方法名字典序的排序有關(guān),排序越高,優(yōu)先級越高,如下
//Logger切面類中定義了兩個(gè)前置通知為aPrint和bPrint
@Aspect
@Component
public class Logger {
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void aPrint() {
System.out.println("a");
}
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
public void bPrint() {
System.out.println("b");
}
}
//啟動(dòng)容器,可見aPrint先于bPrint,這就是因?yàn)閍的字典序高于b
a
b
doSomething...
//將aPrint改為caPrint,這時(shí)bPrint會先執(zhí)行,因?yàn)榇藭r(shí)它的字典序高
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
publicvoid caPrint() {
System.out.println("a");
}
//此外使用@Order注解,無法改變優(yōu)先級,因?yàn)榇藭r(shí)顯式指定優(yōu)先級的策略已經(jīng)失效了,如下面這個(gè)例子還是按照之前默認(rèn)的優(yōu)先級進(jìn)行執(zhí)行
@Aspect
@Component
public class Logger {
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
@Order(Ordered.LOWEST_PRECEDENCE)
public void aPrint() {
System.out.println("a");
}
@Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
@Order(Ordered.HIGHEST_PRECEDENCE)
public void bPrint() {
System.out.println("b");
}
}
10.Introductions(引介)文章來源:http://www.zghlxwxcb.cn/news/detail-416254.html
(1)引介能夠使指定的對象實(shí)現(xiàn)某些接口,并提供對這些接口的實(shí)現(xiàn),以達(dá)到向?qū)ο笾袆?dòng)態(tài)添加它所沒有方法的目的,例子如下文章來源地址http://www.zghlxwxcb.cn/news/detail-416254.html
//我們希望向ExampleA類中增加某些新的方法
@Component
public class ExampleA{ }
//聲明一個(gè)接口,這個(gè)接口里的方法即為我們希望增加的新的方法
public interface Extention {
void doSomething();
}
//新方法的具體實(shí)現(xiàn)
public class ExtentionImpl implements Extention{
@Override
public void doSomething() {
System.out.println("doSomething...");
}
}
//定義一個(gè)切面
@Component
@Aspect
public class MyAspect {
//使用@DeclarePrents注解,聲明被攔截的類有一個(gè)新的父類型,其中value指定攔截哪些類,在下面這個(gè)例子中指定攔截cn.example.spring.boke包下的所有類,同時(shí)指定它們的父類型均為Extention,具體的實(shí)現(xiàn)為ExtentionImpl
@DeclareParents(value = "cn.example.spring.boke.*", defaultImpl = ExtentionImpl.class)
public Extention extention;
}
//開啟AOP
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "cn.example.spring.boke")
public class Config { }
//啟動(dòng)容器,從容器中獲取到exampleA并將其強(qiáng)制轉(zhuǎn)換為Extention,這樣我們就能使用向ExampleA中新添加的方法了
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
Extention exampleA = (Extention)ctx.getBean("exampleA");
exampleA.doSomething();
到了這里,關(guān)于Spring AOP官方文檔學(xué)習(xí)筆記(二)之基于注解的Spring AOP的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!