异常通知(After Throwing Advice)
在方法拋出異常退出時執行的通知。ApplicationContext 中在<aop:aspect> 里面使用<aop:after-throwing>元素進行聲明。例如,ServiceAspect 中的returnThrow 方法。
注:可以將多個通知應用到一個目標對象上,即可以將多個切面織入到同一目標對象。
使用Spring AOP 可以基于兩種方式,一種是比較方便和強大的注解方式,另一種則是中規中矩的xml配置方式。
先說注解,使用注解配置Spring AOP 總體分為兩步,第一步是在xml 文件中聲明激活自動掃描組件功能,同時激活自動代理功能(來測試AOP 的注解功能):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"><context:component-scan base-package="com.leon"/><context:annotation-config /></beans>第二步是為Aspect 切面類添加注解:
/*** Annotation版Aspect切面Bean*/ //聲明這是一個組件 @Component //聲明這是一個切面Bean,AnnotaionAspect是一個面,由框架實現的 @Aspect public class AnnotaionAspect {private final static Logger log = Logger.getLogger(AnnotaionAspect.class);//配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點//切點的集合,這個表達式所描述的是一個虛擬面(規則)//就是為了Annotation掃描時能夠拿到注解中的內容@Pointcut("execution(* com.gupaoedu.vip.aop.service..*(..))")public void aspect(){}/** 配置前置通知,使用在方法aspect()上注冊的切入點* 同時接受JoinPoint切入點對象,可以沒有該參數*/@Before("aspect()")public void before(JoinPoint joinPoint){log.info("before " + joinPoint);}//配置后置通知,使用在方法aspect()上注冊的切入點@After("aspect()")public void after(JoinPoint joinPoint){log.info("after " + joinPoint);}//配置環繞通知,使用在方法aspect()上注冊的切入點@Around("aspect()")public void around(JoinPoint joinPoint){long start = System.currentTimeMillis();try {((ProceedingJoinPoint) joinPoint).proceed();long end = System.currentTimeMillis();log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");} catch (Throwable e) {long end = System.currentTimeMillis();log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());}}//配置后置返回通知,使用在方法aspect()上注冊的切入點@AfterReturning("aspect()")public void afterReturn(JoinPoint joinPoint){log.info("afterReturn " + joinPoint);}//配置拋出異常后通知,使用在方法aspect()上注冊的切入點@AfterThrowing(pointcut="aspect()", throwing="ex")public void afterThrow(JoinPoint joinPoint, Exception ex){log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());}}測試代碼
@ContextConfiguration(locations = {"classpath*:application-context.xml"}) @RunWith(SpringJUnit4ClassRunner.class) public class AnnotationTest {@Autowired MemberService memberService; // @Autowired ApplicationContext app;@Test // @Ignorepublic void test(){System.out.println("=====這是一條華麗的分割線======");// AnnotaionAspect aspect = app.getBean(AnnotaionAspect.class); // System.out.println(aspect);memberService.save(new Member()); //System.out.println("=====這是一條華麗的分割線======");try {memberService.delete(1L);} catch (Exception e) {//e.printStackTrace();}}}可以看到,正如我們預期的那樣,雖然我們并沒有對MemberService 類包括其調用方式做任何改變,但是Spring 仍然攔截到了其中方法的調用,或許這正是AOP 的魔力所在。
再簡單說一下xml 配置方式,其實也一樣簡單:
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-4.3.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.3.xsd"><!-- 注解驅動加上這句話 --><!--<aop:aspectj-autoproxy proxy-target-class="true"/>--><bean id="xmlAspect" class="com.leon.vip.aop.aspect.XmlAspect"></bean><!--AOP配置 --><aop:config><!--聲明一個切面,并注入切面Bean,相當于@Aspect --><aop:aspect ref="xmlAspect" ><!--配置一個切入點,相當于@Pointcut --><aop:pointcut expression="execution(* com.leon.vip.aop.service..*(..))" id="simplePointcut"/><!--配置通知,相當于@Before、@After、@AfterReturn、@Around、@AfterThrowing --><aop:before pointcut-ref="simplePointcut" method="before"/><aop:after pointcut-ref="simplePointcut" method="after"/><aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/><aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/><aop:around pointcut-ref="simplePointcut" method="around"/></aop:aspect></aop:config></beans>個人覺得不如注解靈活和強大,你可以不同意這個觀點,但是不知道如下的代碼會不會讓你的想法有所改善:
/*** 獲取參數Aspect切面Bean*/ //聲明這是一個組件 @Component //聲明這是一個切面Bean @Aspect public class ArgsAspect {private final static Logger log = Logger.getLogger(ArgsAspect.class);//配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點@Pointcut("execution(* com.leon.vip.aop.service..*(..))")public void aspect(){ }//配置前置通知,攔截返回值為com.leon.vip.model.Member的方法@Before("execution(com.leon.vip.model.Member com.leon.vip.aop.service..*(..))")public void beforeReturnUser(JoinPoint joinPoint){log.info("beforeReturnUser " + joinPoint);}//配置前置通知,攔截參數為com.leon.vip.model.Member的方法@Before("execution(* com.leon.vip.aop.service..*(com.leon.vip.model.Member))")public void beforeArgUser(JoinPoint joinPoint){log.info("beforeArgUser " + joinPoint);}//配置前置通知,攔截含有long類型參數的方法,并將參數值注入到當前方法的形參id中@Before("aspect()&&args(id)")public void beforeArgId(JoinPoint joinPoint, long id){log.info("beforeArgId " + joinPoint + "\tID:" + id);}}以下是MemberService 的代碼:
/*** 注解版業務操作類*/ @Service public class MemberService {private final static Logger log = Logger.getLogger(AnnotaionAspect.class);public Member get(long id){log.info("getMemberById method . . .");return new Member();}public Member get(){log.info("getMember method . . .");return new Member();}public void save(Member member){log.info("save member method . . .");}public boolean delete(long id) throws Exception{log.info("delete method . . .");throw new Exception("spring aop ThrowAdvice演示");}}應該說學習Spring AOP 有兩個難點,第一點在于理解AOP 的理念和相關概念,第二點在于靈活掌握和使用切入點表達式。概念的理解通常不在一朝一夕,慢慢浸泡的時間長了,自然就明白了,下面我們簡單地介紹一下切入點表達式的配置規則吧。通常情況下,表達式中使用”execution“就可以滿足大部分的要求。表達式格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?modifiers-pattern:方法的操作權限
ret-type-pattern:返回值
declaring-type-pattern:方法所在的包
name-pattern:方法名
parm-pattern:參數名
throws-pattern:異常
其中, 除ret-type-pattern 和name-pattern 之外, 其他都是可選的。上例中, execution(*com.spring.service.*.*(..))表示com.spring.service 包下,返回值為任意類型;方法名任意;參數不作限制的所有方法。
最后說一下通知參數,可以通過args 來綁定參數,這樣就可以在通知(Advice)中訪問具體參數了。
例如,<aop:aspect>配置如下:
<aop:config><aop:aspect ref="xmlAspect"><aop:pointcut id="simplePointcut"expression="execution(* com.leon.aop.service..*(..)) and args(msg,..)" /><aop:after pointcut-ref="simplePointcut" Method="after"/></aop:aspect> </aop:config>上面的代碼args(msg,..)是指將切入點方法上的第一個String 類型參數添加到參數名為msg 的通知的入參上,這樣就可以直接使用該參數啦。
在上面的Aspect 切面Bean 中已經看到了,每個通知方法第一個參數都是JoinPoint。其實,在Spring中,任何通知(Advice)方法都可以將第一個參數定義為org.aspectj.lang.JoinPoint 類型用以接受當前連接點對象。JoinPoint 接口提供了一系列有用的方法, 比如getArgs() (返回方法參數)、getThis() (返回代理對象)、getTarget() (返回目標)、getSignature() (返回正在被通知的方法相關信息)和toString() (打印出正在被通知的方法的有用信息)。
?
總結
以上是生活随笔為你收集整理的异常通知(After Throwing Advice)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 环绕通知(Around Advice)
- 下一篇: Spring AOP 源码分析-寻找入口
