17、spring-AOP-注解

六、基于@Aspect注解編程(重點(diǎn))

1局服、說(shuō)明

Spring 使用了和AspectJ 一樣的注解并使用AspectJ來(lái)做切入點(diǎn)解析和匹配。但是驳遵,AOP在運(yùn)行時(shí)仍舊是純的Spring AOP淫奔,并不依賴于AspectJ的編譯器或者織入器(weaver)(編譯器與織入器暫時(shí)不要管)

2、啟用@AspectJ支持

  1. 說(shuō)明
    為了在Spring中使用@AspectJ切面堤结,你首先必須啟用Spring對(duì)@AspectJ切面配置的支持唆迁,并確保開(kāi)啟自動(dòng)代理。自動(dòng)代理是指Spring會(huì)判斷一個(gè)bean是否使用了一個(gè)或多個(gè)切面通知竞穷,并據(jù)此自動(dòng)生成相應(yīng)的代理以攔截其方法調(diào)用唐责,并且確保通知在需要時(shí)執(zhí)行
  2. 新建spring-aspect.xml配置文件
    <?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:aop="http://www.springframework.org/schema/aop"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
        <context:component-scan base-package="com.wener.example.aop.aspect"/
     <!-- 有了這個(gè)Spring就能夠自動(dòng)掃描被@Aspect標(biāo)注的切面了 --
        <!-- 開(kāi)啟自動(dòng)代理 --
        <aop:aspectj-autoproxy/
    </beans
    

2、聲明一個(gè)切面

  1. 說(shuō)明
    在代碼中定義一個(gè)類任意在類上使用@Aspect注解
  2. 示例代碼
    import org.aspectj.lang.annotation.Aspect;
    @Aspect
    public class LogAspect {
    }
    

3瘾带、聲明一個(gè)切入點(diǎn)

  1. 說(shuō)明
    切入點(diǎn)決定了連接點(diǎn)關(guān)注的內(nèi)容鼠哥,使得我們可以控制通知什么時(shí)候執(zhí)行。Spring AOP只支持Spring bean的方法執(zhí)行連接點(diǎn)月弛。所以你可以把切入點(diǎn)看做是Spring bean上方法執(zhí)行的匹配肴盏。一個(gè)切入點(diǎn)聲明有兩個(gè)部分:
    • 包含名字和任意參數(shù)的簽名:一個(gè)切入點(diǎn)簽名通過(guò)一個(gè)普通的方法定義來(lái)提供,并且切入點(diǎn)表達(dá)式使用@Pointcut注解來(lái)表示(作為切入點(diǎn)簽名的方法必須返回void 類型)
    • 切入點(diǎn)表達(dá)式:切入點(diǎn)表達(dá)式?jīng)Q定了我們關(guān)注哪些方法的執(zhí)行,詳細(xì)表達(dá)式語(yǔ)法后面在說(shuō)帽衙。
  2. 語(yǔ)法格式
    @Pointcut(value="", argNames = "")
    
  3. 參數(shù)說(shuō)明
    • value
      指定切入點(diǎn)表達(dá)式
    • argNames
      指定命名切入點(diǎn)方法參數(shù)列表參數(shù)名字菜皂,可以有多個(gè)用“,”分隔厉萝,這些參數(shù)將傳遞給通知方法同名的參數(shù)
  4. 示例代碼
    @Aspect
    public class LogAspect {
        // 也可以在通知上定義,當(dāng)需要復(fù)用切入點(diǎn)的時(shí)候
        @Pointcut("execution(* com.wener.example.aop.aspect.*.*(..))")
        // 返回值 必須是void類型
        public void log() {
        }
    }
    
  5. 備注
    切入點(diǎn)的定義是非必要的,也可以直接在通知上使用切入點(diǎn)表達(dá)式

4恍飘、聲明通知

4.1榨崩、說(shuō)明

通知是跟一個(gè)切入點(diǎn)表達(dá)式關(guān)聯(lián)起來(lái)的,并且在切入點(diǎn)匹配的方法執(zhí)行之前或者之后或者前后運(yùn)行章母。 切入點(diǎn)表達(dá)式可能是指向已命名的切入點(diǎn)的簡(jiǎn)單引用或者是一個(gè)已經(jīng)聲明過(guò)的切入點(diǎn)表達(dá)式母蛛,通知的類型就是我們前面提到過(guò)的類型

4.2、前置通知

  1. 說(shuō)明
    在關(guān)注點(diǎn)執(zhí)行前運(yùn)行的方法乳怎,切面里使用 @Before 注解聲明前置通知
  2. 語(yǔ)法格式
    @Before(value = "", argNames = "")
    
  3. 參數(shù)說(shuō)明
    • value :指定切入點(diǎn)表達(dá)式或切入點(diǎn)名字彩郊;
    • argNames: 用來(lái)接收AspectJ表達(dá)式中的參數(shù),并指定通知方法中的參數(shù)
  4. 示例代碼
    import org.springframework.stereotype.Component;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.annotation.Before;
    @Aspect
    @Component
    public class LogAspect {
        /**
         * @Pointcut() 切入點(diǎn)表達(dá)式
         */
        @Pointcut("execution(* com.wener.example.aop.aspect.*.*(..))")
        public void logPointcut() {
        }
        /**
         * @Before 前置通知
         * value:指定切入點(diǎn)表達(dá)式或命名切入點(diǎn);
         * argNames:與Schema方式配置中的同義蚪缀;
         */
        @Before("logPointcut()")
        public void before() {
            System.out.println("前置通知");
        }
    }
    

4.3秫逝、后置通知(最終通知)

  1. 說(shuō)明
    不論一個(gè)方法是如何結(jié)束的,最終通知都會(huì)運(yùn)行询枚。使用@After 注解來(lái)聲明违帆。最終通知必須準(zhǔn)備處理正常返回和異常返回兩種情況。通常用它來(lái)釋放資源金蜀。相當(dāng)于異常處理里finally的代碼
  2. 語(yǔ)法格式
    @After(value = "", argNames = "")
    
  3. 參數(shù)
    • value :指定切入點(diǎn)表達(dá)式或切入點(diǎn)名字刷后;
    • **argNames: **用來(lái)接收AspectJ表達(dá)式中的參數(shù),并指定通知方法中的參數(shù)
  4. 示例代碼
    import org.springframework.stereotype.Component;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Before;
    @Aspect
    @Component
    public class LogAspect {
        /**
         * @Pointcut() 切入點(diǎn)表達(dá)式
         */
        @Pointcut("execution(* com.wener.example.aop.aspect.*.*(..))")
        public void logPointcut() {
        }
        /**
         * @After 后置通知
         */
        @After(value = "logPointcut()")
        public void after() {
            System.out.println("后置通知");
        }
    }
    

4.4、返回通知

  1. 說(shuō)明
    返回后通知通常在一個(gè)匹配的方法返回的時(shí)候執(zhí)行渊抄。使用 @AfterReturning 注解來(lái)聲明
  2. 語(yǔ)法格式
    @AfterReturning(value="",pointcut="",returning="",argNames="")
    
  3. 參數(shù)說(shuō)明
    • value:指定切入點(diǎn)表達(dá)式或切入點(diǎn)名字尝胆;
    • pointcut:指定切入點(diǎn)表達(dá)式或命名切入點(diǎn),如果指定了將覆蓋value屬性的护桦,pointcut具有高優(yōu)先級(jí)班巩;
    • returning:如果你想獲取方法的返回值可以使用該參數(shù),在通知方法中定義參數(shù)就可以了
    • argNames:用來(lái)接收AspectJ表達(dá)式中的參數(shù),并指定通知方法中的參數(shù)
  4. 示例代碼
    import org.springframework.stereotype.Component;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.ProceedingJoinPoint;
    @Aspect
    @Component
    public class LogAspect {
        /**
         * @Pointcut() 切入點(diǎn)表達(dá)式
         */
        @Pointcut("execution(* com.wener.example.aop.aspect.*.*(..))")
        public void logPointcut() {
        }
     /**
      * 不獲取方法的返回值
         */
        @AfterReturning(value = "logPointcut()")
        public void AfterReturning1() {
            System.out.println("異常通知");
        }
        /**
         * 獲取方法的返回值
         * returning的賦值的名字,必須跟通知方法中參數(shù)的名字保持一致
         */
        @AfterReturning(value = "logPointcut()", returning = "val")
        public Object afterReturning(Object val) {
            System.out.println("返回后通知");
            return val;
        }
    }
    

4.5、異常通知

  1. 說(shuō)明
    拋出異常通知在一個(gè)方法拋出異常后執(zhí)行嘶炭。使用@AfterThrowing注解來(lái)聲明
  2. 語(yǔ)法格式
    @AfterThrowing(value="",pointcut="",throwing="",argNames="")
    
  3. 參數(shù)說(shuō)明
    • value:指定切入點(diǎn)表達(dá)式或命名切入點(diǎn);
    • pointcut:指定切入點(diǎn)表達(dá)式或命名切入點(diǎn)逊桦,如果指定了將覆蓋value屬性的眨猎,pointcut具有高優(yōu)先級(jí);
    • throwing:異常類型强经;并且在通知方法中定義異常參數(shù)睡陪;
    • argNames:用來(lái)接收AspectJ表達(dá)式中的參數(shù),并指定通知方法中的參數(shù);
  4. 示例代碼
    import org.springframework.stereotype.Component;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.ProceedingJoinPoint;
    @Aspect
    @Component
    public class LogAspect {
        /**
         * @Pointcut() 切入點(diǎn)表達(dá)式
         */
        @Pointcut("execution(* com.wener.example.aop.aspect.*.*(..))")
        public void logPointcut() {
        }
        /**
         * @AfterThrowing 異常通知
         *   value:指定切入點(diǎn)表達(dá)式或命名切入點(diǎn)匿情;
         *   throwing:異常類型兰迫。
         */
        @AfterThrowing("logPointcut()")
        public void afterThrowing() {
            System.out.println("異常通知");
        }
        /**
         * 如果想要限制通知只在某種特定的異常被拋出的時(shí)候匹配,同時(shí)還想知道異常的一些信息炬称。
         * 那我們就需要使用throwing屬性聲明響應(yīng)
         */
     @AfterThrowing(value = "logPointcut()", throwing = "exception")
        public void afterThrowing(Exception exception) {
            System.out.println("異常通知");
        }
    }
    

4.6汁果、環(huán)繞通知

  1. 說(shuō)明
    環(huán)繞通知在一個(gè)方法執(zhí)行之前和之后執(zhí)行。它使得通知有機(jī)會(huì) 在一個(gè)方法執(zhí)行之前和執(zhí)行之后運(yùn)行玲躯。而且它可以決定這個(gè)方法在什么時(shí)候執(zhí)行据德,如何執(zhí)行鳄乏,甚至是否執(zhí)行。 環(huán)繞通知經(jīng)常在某線程安全的環(huán)境下棘利,你需要在一個(gè)方法執(zhí)行之前和之后共享某種狀態(tài)的時(shí)候使用橱野。 請(qǐng)盡量使用最簡(jiǎn)單的滿足你需求的通知。(比如如果簡(jiǎn)單的前置通知也可以適用的情況下不要使用環(huán)繞通知)善玫。
    • 使用@Around注解水援;
    • 環(huán)繞通知需要攜帶ProceedingJoinPoint類型的參數(shù);
    • 且環(huán)繞通知必須有返回值茅郎,返回值即為有目標(biāo)方法的返回值蜗元。
  2. 語(yǔ)法格式
    @Around(value = "", argNames = "")
    
  3. 參數(shù)
    • value :指定切入點(diǎn)表達(dá)式或切入點(diǎn)名字;
    • **argNames: **用來(lái)接收AspectJ表達(dá)式中的參數(shù),并指定通知方法中的參數(shù)
  4. 示例代碼
    import org.springframework.stereotype.Component;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.ProceedingJoinPoint;
    @Aspect
    @Component
    public class LogAspect {
        /**
         * @Pointcut() 切入點(diǎn)表達(dá)式
         */
        @Pointcut("execution(* com.wener.example.aop.aspect.*.*(..))")
        public void logPointcut() {
        }
        /**
         * @Around 環(huán)繞通知
         * 比如 緩存切面只洒,如果緩存中有值许帐,就返回該值,否則調(diào)用proceed()方法
         * value:指定切入點(diǎn)表達(dá)式或命名切入點(diǎn)毕谴;
         * 注意 第一個(gè)參數(shù)必須是 ProceedingJoinPoint對(duì)象 具體這個(gè)類的更多詳細(xì)使用看附錄:
         */
        @Around(value = "logPointcut()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("環(huán)繞通知1");
            Object obj = pjp.proceed();
            System.out.println("環(huán)繞通知2");
            return obj;
        }
    }
    

4.7成畦、通知參數(shù)

  1. 說(shuō)明
    若想要在通知方法獲取被通知方法的參數(shù)共有兩種方式:自動(dòng)獲取、手動(dòng)指定
    • 自動(dòng)獲取參數(shù):通知類型可以通過(guò)參數(shù)JoinPoint或者 ProceedingJoinPoint 自動(dòng)獲取被通知方法的參數(shù)值并調(diào)用該方法
    • 手動(dòng)指定參數(shù):即在配置切面時(shí)涝开,需在切面的通知與切面的切點(diǎn)中明確指定參數(shù)循帐。
  2. 手動(dòng)指定
    • 在@pointcut中切入表達(dá)式中使用args聲明匹配的參數(shù),注意使用&&連接args
    • 在@pointcut中切入表達(dá)式中使用參數(shù)argNames用來(lái)接收AspectJ表達(dá)式中的參數(shù),
      argNames屬性是用于指定在表達(dá)式中應(yīng)用的參數(shù)名與Advice方法參數(shù)是如何對(duì)應(yīng)的
    • 在通知方法中定義參數(shù)
  3. 手動(dòng)獲取指定參數(shù)
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    @Aspect
    @Component
    public class LogAdviceParamsAspect {
     // 注意參數(shù)的個(gè)數(shù)必須一致,否則匹配不到
        @Before(value = "execution(* com.wener.example.aop.aspect.*.*(..))&& args(id,name)", argNames = "id,name")
        public void testArgs(Object id, Object name) {
            System.out.println(id);
            System.out.println(name);
        }
    }
    
  4. 混用使用
    當(dāng)同時(shí)采用自動(dòng)獲取參數(shù)與手動(dòng)指定參數(shù)時(shí)舀武,自動(dòng)獲取參數(shù)必須是第一個(gè)參數(shù)拄养,即ProceedingJoinPoint 等參數(shù)并需是通知方法定義的第一個(gè)參數(shù)
    import org.aopalliance.intercept.Joinpoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    @Aspect
    @Component
    public class LogAdviceParamsAspect {
      // args、argNames的參數(shù)名與testArgs()方法中參數(shù)名 保持一致
        @Before(value = "execution(* com.wener.example.aop.aspect.*.*(..))&& args(id,name)", argNames = "id,name")
        public void testArgs(Object id, Object name) {
            System.out.println(id);
            System.out.println(name);
        }
     // 也可以不用argNames
        @Before(value = "execution(* com.wener.example.aop.aspect.*.*(..))&& args(id,name)")
        public void testArgs(Object id, Object name) {
            System.out.println(id);
            System.out.println(name);
        }
        @Around(value = "execution(* com.wener.example.aop.aspect.*.*(..))&&(args(id,name,..))", argNames = "pjp,id,name")
        public Object testAroundArgs(ProceedingJoinPoint pjp, Object id, Object name) throws Throwable {
            System.out.println("Around之前");
            Object obj = pjp.proceed();
            System.out.println();
            return obj;
        }
    }
    

4.8 银舱、引入

  1. 說(shuō)明
    有時(shí)候有一組共享公共行為類瘪匿。在OOP中,它們必須擴(kuò)展相同的基類或者實(shí)現(xiàn)相同的接口寻馏。此外棋弥,Java的單繼承機(jī)制僅允許一個(gè)類最多擴(kuò)展一個(gè)基類。所以诚欠,不能同時(shí)從多個(gè)實(shí)現(xiàn)類中繼承行為顽染。
    解決方案:引入是AOP中的一種特殊的通知。它允許為一個(gè)接口提供實(shí)現(xiàn)類轰绵,使對(duì)象動(dòng)態(tài)的實(shí)現(xiàn)接口粉寞。就像對(duì)象在運(yùn)行時(shí)擴(kuò)展了實(shí)現(xiàn)類。而且左腔,可以用多個(gè)實(shí)現(xiàn)類將多個(gè)接口同時(shí)引入對(duì)象唧垦。這可以實(shí)現(xiàn)與多重繼承相同的效果。
  2. 在開(kāi)發(fā)中用的不是很多,所以不做過(guò)多的分析

5翔悠、聲明代理類

  1. 說(shuō)明
    被代理的對(duì)象,跟前面說(shuō)的一樣,代理接口或者類都可以
  2. 示例代碼
    public interface AspectDao {
        public void test();
        public void testParams(int id, String name);
        public void testParams(Joinpoint jp, int id, String name);
    }
    @Component("aspectDao")
    public class AspectDaoImpl implements AspectDao {
        @Override
        public void test() {
            System.out.println("核心測(cè)試方法");
        }
        @Override
        public void testParams(int id, String name) {
            System.out.println("帶參數(shù)的方法:" + "ID:" + id + "name:" + name);
        }
    }
    

6业崖、測(cè)試

  1. 示例代碼
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-aspect.xml");
    AspectDao dao = (AspectDao) context.getBean("aspectDao");
    dao.test();
    dao.testParams(1,"hello");
    

7野芒、總結(jié)

  1. 使用@Aspect將POJO聲明為切面;
  2. 在切面類中使用@Pointcut進(jìn)行命名切入點(diǎn)聲明双炕;
  3. 定義通知方法,使用5中注解聲明狞悲,其中value用于定義切入點(diǎn)表達(dá)式或引用命名切入點(diǎn);
  4. 配置文件需要使用<aop:aspectj-autoproxy/來(lái)開(kāi)啟注解風(fēng)格的@AspectJ支持妇斤;
  5. 將切面類和POJO類注冊(cè)到Spring容器中

七摇锋、基于xml的AOP編程(掌握)

1、說(shuō)明

如果比較喜歡使用XML格式站超,Spring2.0也提供了使用新的"aop"命名空間來(lái)定義一個(gè)切面荸恕。 和使用@AspectJ風(fēng)格完全一樣,切入點(diǎn)表達(dá)式和通知類型同樣得到了支持

AOP配置元素 用途
<aop:config 頂層的AOP配置元素,大多數(shù)的<aop:*必須包含在<aop:config元素內(nèi)
<aop:aspect 定義一個(gè)切面
<aop:pointcut 定義一個(gè)切點(diǎn)
<aop:advisor 定義AOP通知器
<aop:before 定義AOP前置通知
<aop:around 定義AOP環(huán)繞通知
<aop:after-returning 定義AOP返回通知
<aop:after-throwing 定義AOP異常通知
<aop:after 定義AOP后置通知(不管被通知的方法是否執(zhí)行成功)
<aop:aspectj-autoproxy 啟用@Aspect注解的切面
<aop:declare-parents 以透明的方式為被通知的對(duì)象引入額外的接口

2死相、引入aop命名空間標(biāo)簽

  1. 說(shuō)明
    在beans元素下 引入aop融求,聲明<aop-config,在配置文件中算撮,我們可以聲明多個(gè)<aop-config生宛。
    注意:
    • 所有的切面和通知都必須定義在<aop:config元素內(nèi)部。
    • 一個(gè)<aop:config可以包含pointcut肮柜,advisor和aspect元素 (注意這三個(gè)元素必須按照這個(gè)順序進(jìn)行聲明)
  2. 示例代碼
    <beans
           ...
           xmlns:aop="http://www.springframework.org/schema/aop"
         ...
       <aop:config
       </aop:config
    </beans
    

3陷舅、聲明一個(gè)切面

  1. 說(shuō)明
    切面使用<aop:aspect來(lái)聲明
  2. 示例代碼
    <aop:config
      <aop:aspect id="myAspect" ref="myBean"
        ...
      </aop:aspect
    </aop:config
    <bean id="myBean" class="..."
      ...
    </bean
    

4、聲明一個(gè)切入點(diǎn)

  1. 說(shuō)明
    一個(gè)命名切入點(diǎn)可以在<aop:config元素中定義审洞,使用<aop:pointcut聲明莱睁,這樣多個(gè)切面和通知就可以共享該切入點(diǎn),你也可以在切面中定義
  2. 示例代碼
    <aop:config
      <aop:pointcut id="servicePointcut"
            expression="execution(* *.*(..))"/
    </aop:config
    
    <aop:config
      <aop:aspect id="myAspect" ref="myBean"
          <!--這個(gè)切入點(diǎn)只能在該 切面中使用  --
         <aop:pointcut id="servicePointcut"
            expression="execution(* *.*(..))"/
      </aop:aspect
    </aop:config
    

5芒澜、聲明通知

5.1仰剿、說(shuō)明

和@AspectJ風(fēng)格一樣,基于xml的風(fēng)格也支持5種通知類型并且兩者具有同樣的語(yǔ)義

5.2痴晦、前置通知

  1. 說(shuō)明
    前置通知在匹配方法執(zhí)行前運(yùn)行酥馍。在<aop:aspect中使用<aop:before 元素來(lái)聲明它
  2. 示例代碼
    <aop:config
      <aop:pointcut id="servicePointcut"
            expression="execution(* *.*(..))"/
      <aop:aspect id="beforeExample" ref="myBean"
         <aop:before
              pointcut-ref="servicePointcut"
              method="doBefore"/
     </aop:aspect
    </aop:config
    

5.3、后置通知

  1. 說(shuō)明
    后置通知在匹配的方法完全執(zhí)行后運(yùn)行阅酪。和前置通知一樣,在<aop:aspect 里面使用<aop:after-returning聲明汁针,通知方法可以得到返回值术辐。使用returning屬性來(lái)指定傳遞返回值的參數(shù)名。
  2. 示例代碼
    <aop:aspect id="afterReturningExample" ref="myBean"
        <aop:after-returning
          pointcut-ref="servicePointcut"
          method="doAfterReturning"/
        ...
    </aop:aspect
    
    <aop:aspect id="afterReturningExample" ref="myBean"
        <aop:after-returning
          pointcut-ref="servicePointcut"
          method="doAfterReturning"/
        ...
    </aop:aspect
    

5.4施无、異常通知

  1. 說(shuō)明
    異常通知在匹配方法拋出異常退出時(shí)執(zhí)行辉词。在<aop:aspect中使用 <after-throwing元素來(lái)聲明,還可以使用throwing屬性來(lái)指定傳遞異常的參數(shù)名
  2. 示例代碼
    <!-- 無(wú)返回值 --
    <aop:aspect id="afterThrowingExample" ref="myBean"
        <aop:after-throwing
          pointcut-ref="servicePointcut"
          throwing="exception"
          method="doAfterThrowing"/
        ...
    </aop:aspect
    

5.5猾骡、最終通知

  1. 說(shuō)明
    最終通知無(wú)論如何都會(huì)在匹配方法退出后執(zhí)行瑞躺。在<aop:aspect中使用<aop:after元素來(lái)聲明
  2. 示例代碼
    <aop:aspect id="afterFinallyExample" ref="myBean"
        <aop:after
          pointcut-ref="servicePointcut"
          method="doAfter"/
        ...
    </aop:aspect
    

5.6敷搪、環(huán)繞通知

  1. 說(shuō)明
    環(huán)繞通知在匹配方法運(yùn)行期的“周?chē)眻?zhí)行。 它有機(jī)會(huì)在目標(biāo)方法的前面和后面執(zhí)行幢哨,并決定什么時(shí)候運(yùn)行赡勘,怎么運(yùn)行,甚至是否運(yùn)行捞镰。環(huán)繞通知經(jīng)常在需要在一個(gè)方法執(zhí)行前后共享狀態(tài)信息闸与,并且是在線程安全的情況下使用
  2. 示例代碼
    <aop:aspect id="aroundExample" ref="myBean"
        <aop:around
          pointcut-ref="servicePointcut"
          method="doAround"/
        ...
    </aop:aspect
    

八、切面的優(yōu)先級(jí)

1岸售、說(shuō)明

在同一個(gè)連接點(diǎn)上應(yīng)用不止一個(gè)切面時(shí), 除非明確指定, 否則它們的優(yōu)先級(jí)是不確定的

  • 切面的優(yōu)先級(jí)可以通過(guò)實(shí)現(xiàn) Ordered 接口或利用 @Order 注解指定.
    實(shí)現(xiàn) Ordered 接口, getOrder() 方法的返回值越小, 優(yōu)先級(jí)越高.
  • 若使用 @Order 注解, 序號(hào)出現(xiàn)在注解中践樱,值越小優(yōu)先級(jí)越高

2、示例代碼

  1. 基于實(shí)現(xiàn)接口(了解)
    @Aspect
    @Component
    public class LoggingAspect implements Ordered {
        @Override
        public int getOrder() {
            return 2;
        }
    }
    
    @Aspect
    @Component
    public class ValidateAspect implements Ordered {
        @Override
        public int getOrder() {
            return 1;
        }
    }
    
  2. 基于注解
    @Order(2)
    @Aspect
    @Component
    public class LoggingAspect {
    }
    
    @Order(1)
    @Aspect
    @Component
    public class ValidateAspect {
    }
    

九凸丸、簡(jiǎn)單總結(jié)

  1. 切面的內(nèi)容可以復(fù)用
  2. 避免使用Proxy拷邢、CGLIB生成代理,這方面的工作全部框架去實(shí)現(xiàn)屎慢,開(kāi)發(fā)者可以專注于切面內(nèi)容本身
  3. 代碼與代碼之間沒(méi)有耦合瞭稼,如果攔截的方法有變化修改配置文件即可

十、附錄

1抛人、獲取目標(biāo)對(duì)象信息

1.1弛姜、JoinPoint 對(duì)象

  1. 說(shuō)明
    JoinPoint對(duì)象封裝了SpringAop中切面方法的信息,在切面方法中添加JoinPoint參數(shù),就可以獲取到封裝了該方法信息的JoinPoint對(duì)象
  2. 重要方法
    方法 說(shuō)明
    Signature getSignature(); 獲取封裝了署名信息的對(duì)象,在該對(duì)象中可以獲取到目標(biāo)方法名,所屬類的Class等信息
    Object[] getArgs(); 獲取連接點(diǎn)方法運(yùn)行時(shí)的入?yún)⒘斜?/strong>
    Object getTarget(); 獲取連接點(diǎn)所在的目標(biāo)對(duì)象
    Object getThis(); 獲取代理對(duì)象本身

1.2、ProceedingJoinPoint

  1. 說(shuō)明
    ProceedingJoinPoint繼承JoinPoint子接口妖枚,并且只能用于@Around的切面方法中
  2. 新增方法
    方法名 功能
    Object proceed() throws Throwable 執(zhí)行目標(biāo)方法
    Object proceed(Object[] var1) throws Throwable 傳入的新的參數(shù)去執(zhí)行目標(biāo)方法

2廷臼、示例代碼

  1. 案例1
    Aspect
    @Component
    public class JoinPointerAspect {
        /**
         * 定義一個(gè)切入點(diǎn)表達(dá)式,用來(lái)確定哪些類需要代理
         */
        @Pointcut("execution(* com.wener.example.aop.aspect.*.*(..))")
        public void declareJoinPointer() {}
        /**
         * 前置方法,在目標(biāo)方法執(zhí)行前執(zhí)行
         * @param joinPoint 封裝了代理方法信息的對(duì)象,若用不到則可以忽略不寫(xiě)
         */
        @Before("declareJoinPointer()")
        public void beforeMethod(JoinPoint joinPoint){
            System.out.println("目標(biāo)方法名:" + joinPoint.getSignature().getName());
            System.out.println("目標(biāo)方法所屬類的名:" +        joinPoint.getSignature().getDeclaringType().getSimpleName());
            System.out.println("目標(biāo)方法聲明類型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
            //獲取傳入目標(biāo)方法的參數(shù)
            Object[] args = joinPoint.getArgs();
            for (int i = 0; i < args.length; i++) {
                System.out.println("第" + (i+1) + "個(gè)參數(shù)為:" + args[i]);
            }
            System.out.println("被代理的對(duì)象:" + joinPoint.getTarget());
            System.out.println("代理對(duì)象自己:" + joinPoint.getThis());
        }
        /**
         * 環(huán)繞方法,可自定義目標(biāo)方法執(zhí)行的時(shí)機(jī)
         * @param pjd JoinPoint的子接口,添加了
         *  Object proceed() throws Throwable 執(zhí)行目標(biāo)方法
         *  Object proceed(Object[] var1) throws Throwable 傳入的新的參數(shù)去執(zhí)行目標(biāo)方法
         *
         * @return 此方法需要返回值,返回值視為目標(biāo)方法的返回值
         */
        @Around("declareJoinPointer()")
        public Object aroundMethod(ProceedingJoinPoint pjd){
            Object result = null;
            try {
                //前置通知
                System.out.println("目標(biāo)方法執(zhí)行前...");
                //執(zhí)行目標(biāo)方法
                //result = pjd.proeed();
                //用新的參數(shù)值執(zhí)行目標(biāo)方法
                result = pjd.proceed(new Object[]{"hello","world"});
                //返回通知
                System.out.println("目標(biāo)方法返回結(jié)果后...");
            } catch (Throwable e) {
                //異常通知
                System.out.println("執(zhí)行目標(biāo)方法異常后...");
                throw new RuntimeException(e);
            }
            //后置通知
            System.out.println("目標(biāo)方法執(zhí)行后...");
            return result;
        }
    }
    
  2. 執(zhí)行流程
    • AOP定義了一個(gè)切面(Aspect),一個(gè)切面包含了切入點(diǎn)绝页,通知荠商,引入,這個(gè)切面上定義了許多的切入點(diǎn)(Pointcut)续誉,一旦訪問(wèn)過(guò)程中有對(duì)象的方法跟切入點(diǎn)匹配那么就會(huì)被AOP攔截莱没。
    • 此時(shí)該對(duì)象就是目標(biāo)對(duì)象(Target Object)而匹配的方法就是連接點(diǎn)(Join Point)。
    • 緊接著AOP會(huì)用過(guò)JDK動(dòng)態(tài)代理或者CGLIB生成一個(gè)目標(biāo)對(duì)象的代理對(duì)象(AOP proxy)酷鸦,這個(gè)過(guò)程就是織入(Weaving)饰躲。
    • 這個(gè)時(shí)候我們就可以按照我們的需求對(duì)連接點(diǎn)進(jìn)行一些攔截處理。
    • 可以看到臼隔,我們可以引入(Introduction)一個(gè)新的接口嘹裂,讓代理對(duì)象來(lái)實(shí)現(xiàn)這個(gè)接口來(lái),以實(shí)現(xiàn)額外的方法和字段摔握。也可以在連接點(diǎn)上進(jìn)行通知(Advice)寄狼,通知的類型包括了前置通知,返回后通知氨淌,拋出異常后通知泊愧,后置通知伊磺,環(huán)繞通知。
    • 最后也是最騷的是整個(gè)過(guò)程不會(huì)改變代碼原有的邏輯
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末删咱,一起剝皮案震驚了整個(gè)濱河市屑埋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌腋腮,老刑警劉巖雀彼,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異即寡,居然都是意外死亡徊哑,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)聪富,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)莺丑,“玉大人,你說(shuō)我怎么就攤上這事墩蔓∩颐В” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵奸披,是天一觀的道長(zhǎng)昏名。 經(jīng)常有香客問(wèn)我,道長(zhǎng)阵面,這世上最難降的妖魔是什么轻局? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮样刷,結(jié)果婚禮上仑扑,老公的妹妹穿的比我還像新娘。我一直安慰自己置鼻,他們只是感情好镇饮,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著箕母,像睡著了一般储藐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嘶是,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天邑茄,我揣著相機(jī)與錄音,去河邊找鬼俊啼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛左医,可吹牛的內(nèi)容都是我干的授帕。 我是一名探鬼主播同木,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼跛十!你這毒婦竟也來(lái)了彤路?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤芥映,失蹤者是張志新(化名)和其女友劉穎洲尊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體奈偏,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坞嘀,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惊来。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丽涩。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖裁蚁,靈堂內(nèi)的尸體忽然破棺而出矢渊,到底是詐尸還是另有隱情,我是刑警寧澤枉证,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布矮男,位于F島的核電站,受9級(jí)特大地震影響室谚,放射性物質(zhì)發(fā)生泄漏毡鉴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一舞萄、第九天 我趴在偏房一處隱蔽的房頂上張望眨补。 院中可真熱鬧,春花似錦倒脓、人聲如沸撑螺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)甘晤。三九已至,卻和暖如春饲做,著一層夾襖步出監(jiān)牢的瞬間线婚,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工盆均, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留塞弊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像游沿,于是被迫代替她去往敵國(guó)和親饰抒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容