
Advice生命周期
每個(gè)Advice都是一個(gè)Bean。Advice實(shí)例可以在所有Advisor之間共享,也可以對(duì)每個(gè)Advisor對(duì)象都是唯一的。這對(duì)應(yīng)于每個(gè)類或每個(gè)實(shí)例的Advice。
最常使用的是每類Advice。它適用于一般的Advice,例如事務(wù)Advisors。這些不依賴于代理對(duì)象的狀態(tài)或添加新?tīng)顟B(tài)。它們只是對(duì)方法和參數(shù)起作用。
每個(gè)實(shí)例Advice適用于引入,以支持mixin。在這種情況下,通知將狀態(tài)添加到代理對(duì)象。
你可以在同一個(gè)AOP代理中混合使用共享通知和每個(gè)實(shí)例通知。
Advice類型
Spring提供了幾種通知類型,并且可以擴(kuò)展以支持任意通知類型。
Spring中最基本的通知類型是圍繞通知的攔截。
Spring與AOP Alliance接口兼容,支持使用方法攔截的環(huán)繞通知。實(shí)現(xiàn)MethodInterceptor和around advice的類還應(yīng)該實(shí)現(xiàn)以下接口:
public interface MethodInterceptor extends org.aopalliance.intercept.Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
invoke()方法的MethodInvocation參數(shù)公開了被調(diào)用的方法、目標(biāo)連接點(diǎn)、AOP代理和方法的參數(shù)。invoke()方法應(yīng)該返回調(diào)用的結(jié)果:連接點(diǎn)的返回值。
面的例子展示了一個(gè)簡(jiǎn)單的MethodInterceptor實(shí)現(xiàn):
public class DebugInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before: invocation=[" + invocation + "]");
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}
注意:MethodInvocation中對(duì)proceed()方法的調(diào)用。這將沿著攔截器鏈向下直至連接點(diǎn)。大多數(shù)攔截器調(diào)用此方法并返回其返回值。然而,MethodInterceptor和任何around通知一樣,可以返回不同的值或拋出異常,而不是調(diào)用proceed方法。然而,如果沒(méi)有充分的理由,你不會(huì)想要這樣做。
MethodInterceptor實(shí)現(xiàn)提供了與其他遵循AOP聯(lián)盟的AOP實(shí)現(xiàn)的互操作性。雖然使用最具體的通知類型有好處,但如果你可能想在另一個(gè)AOP框架中運(yùn)行方面,請(qǐng)堅(jiān)持使用MethodInterceptor。注意,切入點(diǎn)目前不能在框架之間互操作,而且AOP聯(lián)盟目前不定義切入點(diǎn)接口。
一個(gè)簡(jiǎn)單的Advice類型是事前Adivce。它不需要MethodInvocation對(duì)象,因?yàn)樗辉谶M(jìn)入方法之前被調(diào)用。
before通知的主要優(yōu)點(diǎn)是不需要調(diào)用proceed()方法,因此不可能在無(wú)意中無(wú)法繼續(xù)執(zhí)行攔截器鏈。
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
注意:返回類型是void。Before通知可以在連接點(diǎn)運(yùn)行之前插入自定義行為,但不能更改返回值。如果before通知拋出異常,它將停止攔截器鏈的進(jìn)一步執(zhí)行。異常在攔截器鏈中向上傳播。如果未檢查或在被調(diào)用方法的簽名上,它將直接傳遞給客戶端。否則,它將被AOP代理包裝在未檢異常中。
下面的例子展示了Spring中的before通知,它統(tǒng)計(jì)了所有的方法調(diào)用:
public class CountingBeforeAdvice implements MethodBeforeAdvice {
private int count;
public void before(Method m, Object[] args, Object target) throws Throwable {
++count;
}
public int getCount(){
return count;
}
}
如果連接點(diǎn)拋出異常,則在連接點(diǎn)返回后調(diào)用Throws通知。Spring提供了類型化異常通知。注意,這意味著org.springframework.aop.ThrowsAdvice接口不包含任何方法。它是一個(gè)標(biāo)記接口,標(biāo)識(shí)給定對(duì)象實(shí)現(xiàn)了一個(gè)或多個(gè)類型化throws通知方法。格式如下:
afterThrowing([Method, args, target], subclassOfThrowable)
Method,args,target3個(gè)參數(shù)是可選的。
public class BusinessThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(BusinessException ex) throws Throwable {
// ...
}
}
下一個(gè)示例聲明了4個(gè)參數(shù),因此它可以訪問(wèn)被調(diào)用的方法、方法參數(shù)和目標(biāo)對(duì)象。如果拋出ServletException,將調(diào)用以下Advice:
public class ControllerAdviceWithArguments implements ThrowsAdvice {
public void afterThrowing(Method m, Object[] args, Object target, MethodArgumentNotValidException ex){
// ...
}
}
在一個(gè)異常通知類中定義多個(gè)不同異常的處理
public static class CombinedThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(BusinessException ex) throws Throwable {
// ...
}
public void afterThrowing(Method m, Object[] args, Object target, MethodArgumentNotValidException ex){
// ...
}
}
Spring中的后置通知必須實(shí)現(xiàn)org.springframework.aop.AfterReturningAdvice接口,如下:
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable;
}
返回通知可以訪問(wèn)返回值(它不能修改)、被調(diào)用的方法、方法的參數(shù)和目標(biāo)。
public class CountingAfterReturningAdvice implements AfterReturningAdvice {
private int count;
public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
++count;
}
public int getCount(){
return count;
}
}
如果它拋出異常,它將被拋出攔截器鏈,而不是返回值。
Spring將引介通知視為一種特殊的攔截通知。
Introduction需要一個(gè)IntroductionAdvisor和一個(gè)IntroductionInterceptor實(shí)現(xiàn)以下接口:
public interface IntroductionInterceptor extends MethodInterceptor {
boolean implementsInterface(Class intf);
}
從AOP Alliance方法攔截器接口繼承的invoke()方法必須實(shí)現(xiàn)引入。也就是說(shuō),如果被調(diào)用的方法在引入的接口上,則引入攔截器負(fù)責(zé)處理方法調(diào)用—它不能調(diào)用proceed()。
引介通知不能與任何切入點(diǎn)一起使用,因?yàn)樗贿m用于類級(jí)別,而不是方法級(jí)別。你只能在 IntroductionAdvisor中使用介紹建議,它有以下方法:
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
ClassFilter getClassFilter();
void validateInterfaces() throws IllegalArgumentException;
}
public interface IntroductionInfo {
Class<?>[] getInterfaces();
}
沒(méi)有MethodMatcher,因此也沒(méi)有與引介通知相關(guān)聯(lián)的切入點(diǎn)。只有類過(guò)濾。
getInterfaces()方法返回這個(gè)Advisor引入的接口。
validateInterfaces()方法在內(nèi)部使用,以查看引入的接口是否可以由配置的IntroductionInterceptor實(shí)現(xiàn)。下面直接給出示例,該示例的作用就是使某個(gè)類不具備某個(gè)接口能力時(shí)動(dòng)態(tài)給予該接口的能力:
接口:
public interface CountDAO {
public void count() ;
}
這里的引介攔截器必須實(shí)現(xiàn)我們期望的一個(gè)接口:
public class CustomIntroductionInterceptor implements IntroductionInterceptor, CountDAO {
@Override
public void count(){
System.out.println("訂單統(tǒng)計(jì)...") ;
}
@Override
public boolean implementsInterface(Class<?> intf){
return CountDAO.class.isAssignableFrom(intf) ;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
if (implementsInterface(invocation.getMethod().getDeclaringClass())) {
System.out.println("我是Introduction增強(qiáng)..." + "Class: " + invocation.getMethod().getDeclaringClass() + ", method: " + invocation.getMethod().getName()) ;
// 實(shí)際調(diào)用的就是當(dāng)前Advice實(shí)現(xiàn)的CountDAO#count方法。
return invocation.getMethod().invoke(this, invocation.getArguments()) ;
}
return invocation.proceed() ;
}
}
創(chuàng)建代理處理器:
@Component
public class OrderProxyCreater extends AbstractAutoProxyCreator {
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName,
TargetSource customTargetSource) throws BeansException {
return new Object[] {new DefaultIntroductionAdvisor(new CustomIntroductionInterceptor(), CountDAO.class)} ;
}
// 判斷只要不是OrderDAO類型的都進(jìn)行跳過(guò)(這里只代理是OrderDAO類型的Bean)
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName){
return !OrderDAO.class.isAssignableFrom(beanClass) ;
}
}
OrderDAO實(shí)現(xiàn),該DAO并沒(méi)有實(shí)現(xiàn)CountDAO:
@Service
public class OrderDAOImpl implements OrderDAO {
@Override
public void save(){
System.out.println("保存訂單...") ;
}
@Override
public void query(){
System.out.println("查詢訂單...") ;
}
}
測(cè)試:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("com.pack.aop") ;
ctx.registerShutdownHook();
OrderDAO persondao = ctx.getBean(OrderDAO.class) ;
persondao.save() ;
Object obj = ctx.getBean("orderDAOImpl") ;
if (obj instanceof CountDAO) {
CountDAO cdao = (CountDAO) obj ;
cdao.count() ;
}
運(yùn)行結(jié)果:
保存訂單...
我是Introduction增強(qiáng)...Class: interface com.pack.aop.CountDAO, method: count
從運(yùn)行結(jié)果看到OrderDAO具備了CountDAO接口能力,而具體實(shí)現(xiàn)CountDAO是我們的引介攔截器上實(shí)現(xiàn)的。