javascript
Spring AOP详解一文搞懂@Aspect、@Pointcut、@Before、@Around、@After、@AfterReturning、@AfterThrowing
文章目錄
- 1、AOP是什么
- 2、AOP中注解的含義
- 3、Pointcut切入點(diǎn)的語(yǔ)法
- 4、AOP代碼實(shí)現(xiàn)
1、AOP是什么
AOP:Aspect Oriented Programming,翻譯過(guò)來(lái)就是大名鼎鼎的“面向切面編程”,它是對(duì)面向?qū)ο蟮囊环N補(bǔ)充和完善。
AOP的使用場(chǎng)景一般有:數(shù)據(jù)源切換、事務(wù)管理、權(quán)限控制、日志打印等。根據(jù)它的名字我們不難理解,它的實(shí)現(xiàn)很像是將我們要實(shí)現(xiàn)的代碼切入業(yè)務(wù)實(shí)現(xiàn)的邏輯中。它有以下特點(diǎn):
1、侵入性小,幾乎可以不改動(dòng)原來(lái)邏輯的情況下把新的邏輯加入業(yè)務(wù)。
2、實(shí)現(xiàn)方便,使用幾個(gè)注解就可以實(shí)現(xiàn),使系統(tǒng)更容易擴(kuò)展。
3、更好的復(fù)用代碼,比如事務(wù)日志打印,簡(jiǎn)單邏輯適合所有情況。
相關(guān)代碼請(qǐng)參考: chapter-4-springmvc-sourcecode-analysis
https://gitee.com/leo825/spring-framework-learning-example.git
2、AOP中注解的含義
@Aspect:切面。表示一個(gè)橫切進(jìn)業(yè)務(wù)的一個(gè)對(duì)象。它里面包含切入點(diǎn)(Pointcut)和Advice(通知)。
@Pointcut:切入點(diǎn)。表示需要切入的位置,比如某些類或者某些方法,也就是先定一個(gè)范圍。
@Before:Advice(通知)的一種,切入點(diǎn)的方法體執(zhí)行之前執(zhí)行。
@Around:Advice(通知)的一種,環(huán)繞切入點(diǎn)執(zhí)行也就是把切入點(diǎn)包裹起來(lái)執(zhí)行。
@After:Advice(通知)的一種,在切入點(diǎn)正常運(yùn)行結(jié)束后執(zhí)行。
@AfterReturning:Advice(通知)的一種,在切入點(diǎn)正常運(yùn)行結(jié)束后執(zhí)行,異常則不執(zhí)行
@AfterThrowing:Advice(通知)的一種,在切入點(diǎn)運(yùn)行異常時(shí)執(zhí)行。
3、Pointcut切入點(diǎn)的語(yǔ)法
語(yǔ)法規(guī)則如下代碼中注釋所示:
/*** 1、使用within表達(dá)式匹配* 下面示例表示匹配com.leo.controller包下所有的類的方法*/@Pointcut("within(com.leo.controller..*)")public void pointcutWithin(){}/*** 2、this匹配目標(biāo)指定的方法,此處就是HelloController的方法*/@Pointcut("this(com.leo.controller.HelloController)")public void pointcutThis(){}/*** 3、target匹配實(shí)現(xiàn)UserInfoService接口的目標(biāo)對(duì)象*/@Pointcut("target(com.leo.service.UserInfoService)")public void pointcutTarge(){}/*** 4、bean匹配所有以Service結(jié)尾的bean里面的方法,* 注意:使用自動(dòng)注入的時(shí)候默認(rèn)實(shí)現(xiàn)類首字母小寫為bean的id*/@Pointcut("bean(*ServiceImpl)")public void pointcutBean(){}/*** 5、args匹配第一個(gè)入?yún)⑹荢tring類型的方法*/@Pointcut("args(String, ..)")public void pointcutArgs(){}/*** 6、@annotation匹配是@Controller類型的方法*/@Pointcut("@annotation(org.springframework.stereotype.Controller)")public void pointcutAnnocation(){}/*** 7、@within匹配@Controller注解下的方法,要求注解的@Controller級(jí)別為@Retention(RetentionPolicy.CLASS)*/@Pointcut("@within(org.springframework.stereotype.Controller)")public void pointcutWithinAnno(){}/*** 8、@target匹配的是@Controller的類下面的方法,要求注解的@Controller級(jí)別為@Retention(RetentionPolicy.RUNTIME)*/@Pointcut("@target(org.springframework.stereotype.Controller)")public void pointcutTargetAnno(){}/*** 9、@args匹配參數(shù)中標(biāo)注為@Sevice的注解的方法*/@Pointcut("@args(org.springframework.stereotype.Service)")public void pointcutArgsAnno(){}/*** 10、使用excution表達(dá)式* execution(* modifier-pattern? //用于匹配public、private等訪問(wèn)修飾符* ret-type-pattern //用于匹配返回值類型,不可省略* declaring-type-pattern? //用于匹配包類型* name-pattern(param-pattern) //用于匹配類中的方法,不可省略* throws-pattern? //用于匹配拋出異常的方法* )** 下面的表達(dá)式解釋為:匹配com.leo.controller.HelloController類中以hello開(kāi)頭的修飾符為public返回類型任意的方法*/@Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))")public void pointCut() {}需要注意:上面的匹配的類型中支持或(||)與(&&)非(!)運(yùn)算。
4、AOP代碼實(shí)現(xiàn)
首先配置pox.xml進(jìn)行注解jar包的支持
<!-- aop aspect注解導(dǎo)包--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.9</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.9</version></dependency>然后在配置文件中啟動(dòng)注解支持,例如我的是myspringmvc-servlet.xml文件,這里需要注意的是注解要跟配置文件在一塊,舉個(gè)例子:如果需要切入Controller的配置就需要在myspringmvc-servlet.xml中添加,如果是切入應(yīng)用的配置如Service則需要在applicationContext.xml中添加。
<!-- 開(kāi)啟Aop注解 --><aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>Aop核心代碼如下:
ThreadLocal<Long> startTime = new ThreadLocal<>();/*** 10、使用excution表達(dá)式* execution(* modifier-pattern? //用于匹配public、private等訪問(wèn)修飾符* ret-type-pattern //用于匹配返回值類型,不可省略* declaring-type-pattern? //用于匹配包類型* name-pattern(param-pattern) //用于匹配類中的方法,不可省略* throws-pattern? //用于匹配拋出異常的方法* )** 下面的表達(dá)式解釋為:匹配com.leo.controller.HelloController類中以hello開(kāi)頭的修飾符為public返回類型任意的方法*/@Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))")public void pointCut() {}/*** 在方法執(zhí)行之前執(zhí)行** @param joinPoint*/@Before(value = "pointCut()")public void beforeLog(JoinPoint joinPoint) {System.out.println("進(jìn)入LogAop的beforeLogger");startTime.set(System.currentTimeMillis());}/*** 在進(jìn)入類之前執(zhí)行,然后返回pjp.proceed()之前執(zhí)行before,再執(zhí)行方法體,在到after** @param*/@Around(value = "pointCut()")public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {System.out.println("進(jìn)入LogAop的aroundLogger");return pjp.proceed();}/*** 在方法執(zhí)行返回之后執(zhí)行*/@After(value = "pointCut()")public void afterLog() {System.out.println("進(jìn)入LogAop的afterLogger");long start = startTime.get();System.out.println("方法體執(zhí)行耗時(shí):" + (System.currentTimeMillis() - start) + "ms");startTime.remove();}/*** 在返回之后執(zhí)行* @param o*/@AfterReturning(value = "pointCut()",returning = "o")public void afterRunningLog(Object o){System.out.println("進(jìn)入LogAop的afterRunningLog");System.out.println(o.getClass());}/*** 在產(chǎn)生異常時(shí)執(zhí)行*/@AfterThrowing(value = "pointCut()")public void afterThrowingLog(){System.out.println("進(jìn)入LogAop的afterThrowingLog");}開(kāi)啟debug模式,查看各個(gè)標(biāo)簽執(zhí)行順序;
1、斷點(diǎn)進(jìn)入@Around位置
2、執(zhí)行完pjp.proceed()之后,開(kāi)始進(jìn)入@Before方法
3、執(zhí)行完@Before中的方法后進(jìn)入主方法
4、執(zhí)行完主方法體,又回到了@Around的返回值
5、然后開(kāi)始執(zhí)行@After中內(nèi)容
6.最后到了@AfterReturning方法體中
所以,正常的執(zhí)行順序是:@Around ->@Before->主方法體->@Around中pjp.proceed()->@After->@AfterReturning
如果存下異常那執(zhí)行順序是什么呢?以下模擬了一個(gè)異常。
1、首先進(jìn)入了@Around的方法
2、進(jìn)入了@After的方法
3、最后進(jìn)入了@AfterThrowing的方法
因此如果異常在Around中pjp.proceed()之前,則執(zhí)行順序?yàn)?#xff1a;@Around -> @After -> @AfterThrowing
同理,如果異常在Around中pjp.proceed()之后,則執(zhí)行順序?yàn)?#64;Around ->@Before->主方法體->@Around中pjp.proceed()->@After->@AfterThrowing*
總結(jié)
以上是生活随笔為你收集整理的Spring AOP详解一文搞懂@Aspect、@Pointcut、@Before、@Around、@After、@AfterReturning、@AfterThrowing的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 常见加密工具类Base64、DES、AE
- 下一篇: 随笔——电话有感