Spring-AOP基礎(chǔ)

1.概述

Aop(Aspect Oriented Programming),即面向切面編程萨惑,這是面向?qū)ο笏枷氲囊环N補(bǔ)充,面向切面編程就是在程序運(yùn)行時(shí),在不改變程序源碼的情況下沮榜,動態(tài)的增強(qiáng)方法的功能。
Aop的使用場景非常多喻粹,例如:日志 蟆融、事物等等

2.概念

Aop的世界有很多概念,其中重要的有切面守呜、通知型酥、連接點(diǎn)、切點(diǎn)查乒、織入弥喉。

切面(Aspect)

橫切關(guān)注點(diǎn)可以被模塊化為特殊的類,這些類被稱為切面
一個(gè)切面包括切點(diǎn)和通知

通知(Advice)

一個(gè)切面的工作被稱為通知玛迄,它定義了切面要增強(qiáng)的具體內(nèi)容
Spring切面可以有5種類型的通知:

  • 前置通知(Before)
    通知方法在目標(biāo)方法被調(diào)用前執(zhí)行
  • 后置通知/最終通知(After)
    通知方法在目標(biāo)方法返回或者拋出異常后執(zhí)行
    注意??:
    1.不管目標(biāo)方法的返回值類型是否為void
    2.不管目標(biāo)方法有沒有返回確切的值還是null
  • 返回通知(After-returning)
    通知方法在目標(biāo)方法返回后執(zhí)行
    注意??:
    1.目標(biāo)方法的返回值類型不能為void由境,否則無此通知
    2.不管目標(biāo)方法有沒有返回確切的值還是null
  • 異常通知(After-throwing)
    通知方法在目標(biāo)方法拋出異常后執(zhí)行
  • 環(huán)繞通知(Around)
    通知包裹了被通知的方法,在被通知的方法調(diào)用之前和調(diào)用之后執(zhí)行自定義的行為
    實(shí)際上就像在一個(gè)通知方法中同時(shí)編寫前置通知和后置通知

通俗的理解:通知定義了切面“干什么”憔晒、“什么時(shí)候干”的問題

連接點(diǎn)(Join point)

我們的應(yīng)用程序可能有各種時(shí)機(jī)應(yīng)用通知藻肄,這些時(shí)機(jī)被稱為連接點(diǎn)

切點(diǎn)(Pointcut)

一個(gè)切面并不是需要通知應(yīng)用中的所有連接點(diǎn)蔑舞,我們的某個(gè)切面真正通知的那個(gè)連接點(diǎn)被稱為切點(diǎn)

通俗的理解:切點(diǎn)定義了切面“在哪里干”的問題

織入(Weaving)

織入是把切面應(yīng)用到目標(biāo)對象并創(chuàng)建新的代理對象的過程
切面在指定的連接點(diǎn)織入到目標(biāo)對象中
在目標(biāo)對象的生命周期里有多個(gè)點(diǎn)可以進(jìn)行織入:

  • 編譯期
    切面在目標(biāo)類編譯時(shí)被織入
    這種方式需要特殊的編譯器,AspectJ的織入編譯器就是以這種方式織入切面的
  • 類加載期
    切面在目標(biāo)類加載到JVM時(shí)被織入
    這種方式需要特殊的類加載器(ClassLoader)嘹屯,它可以 在目標(biāo)類被引入應(yīng)用之前增強(qiáng)該目標(biāo)類的字節(jié)碼攻询,AspectJ5的加載時(shí)織入就支持以這種方式織入切面
  • 運(yùn)行期
    切面在應(yīng)用運(yùn)行的某個(gè)時(shí)刻被織入
    一般情況下,在織入切面時(shí)州弟,容器會為目標(biāo)對象動態(tài)的創(chuàng)建一個(gè)代理對象
    Spring AOP就是以這種方式織入切面的

3.Spring對AOP的支持

并不是所有的AOP框架都是相同的钧栖,它們在連接點(diǎn)模型上可能有強(qiáng)弱之分
有些框架允許在字段修飾符級別應(yīng)用通知,而有一些框架只支持方法調(diào)用級別的連接點(diǎn)婆翔。除此之外拯杠,它們織入切面的方式和時(shí)機(jī)也所有不同。
Spring和AspectJ項(xiàng)目之間有大量的協(xié)作啃奴,而且Spring對AOP的支持也在很多方面借鑒了AspectJ項(xiàng)目潭陪。
Spring提供了4種類型的AOP支持:

  • 基于代理的經(jīng)典Spring AOP
  • 純POJO切面
  • @AspectJ注解驅(qū)動的切面
  • 注入式AspectJ切面(適用于Spring各個(gè)版本)

SpringAOP構(gòu)建在動態(tài)代理的基礎(chǔ)上,因此Spring對AOP的支持局限于方法攔截最蕾。
Spring的通知都是Java編寫的依溯,定義通知所應(yīng)用的切點(diǎn)通常會使用注解或者在Spring的配置文件里采用XML來編寫
Spring在運(yùn)行期把切面織入到Spring管理的bean中,實(shí)際上Spring是基于動態(tài)代理實(shí)現(xiàn)的瘟则,代理類封裝了目標(biāo)類黎炉,并攔截被通知方法的調(diào)用,再把調(diào)用轉(zhuǎn)發(fā)給真正的目標(biāo)bean醋拧,當(dāng)代理攔截到方法調(diào)用是慷嗜,在調(diào)用目標(biāo)bean方法之前會執(zhí)行切面邏輯。

4.基于注解聲明切面

在Spring AOP中丹壕,要使用AspectJ的切點(diǎn)表達(dá)式語言來定義切點(diǎn)庆械,但是Spring僅支持AspectJ切點(diǎn)指示器的一個(gè)子集
SpirngAOP支持的AspectJ切點(diǎn)指示器有:

AspectJ指示器 描述
arg() 限制連接點(diǎn)匹配參數(shù)為指定類型的執(zhí)行方法
@args() 限制連接點(diǎn)匹配參數(shù)由指定注解標(biāo)注的執(zhí)行方法
execution() 用于匹配是連接點(diǎn)的執(zhí)行方法
this() 限制連接點(diǎn)匹配AOP代理的bean引用為指定類型的類
target 限制連接點(diǎn)匹配目標(biāo)對象為指定類型的類
@target() 限制連接點(diǎn)匹配特定的執(zhí)行對象,這些對象對應(yīng)的類要具有指定類型的注解
within() 限制連接點(diǎn)匹配的指定類型
@withinn() 限制連接點(diǎn)匹配指定注解所標(biāo)注的類型(當(dāng)使用Spring AOP時(shí)雀费,方法定義由指定的注解所標(biāo)注的類里)
@annotation 限定匹配帶有指定注解的連接點(diǎn)

在Spring中嘗試使用AspectJ其他指示器干奢,將會拋出IllegalArgumentException異常
如上表格中所展示的Spring支持的指示器,注意只有execution指示器是實(shí)際執(zhí)行匹配的盏袄,其他指示器都是用來限制匹配的(execution指示器是我們在編寫切點(diǎn)定義時(shí)最主要使用的指示器)

4.1編寫切點(diǎn)

為了演示Spring的AOP忿峻,我們定義了如下一個(gè)接口:

public interface IUserService {

    /**
     * 根據(jù)ID獲取用戶
     *
     * @param id 主鍵ID
     * @return 用戶信息
     */
    User getById(Integer id);

    /**
     * 保存用戶信息
     *
     * @param user 用戶信息
     * @return 影響的行數(shù)
     */
    int saveUser(User user);

}

假設(shè)我們希望在getById()方法觸發(fā)通知的調(diào)用,那么切點(diǎn)表達(dá)式寫法如下:

4.2使用注解創(chuàng)建切面

使用注解創(chuàng)建切面是AspectJ 5所引入的關(guān)鍵特性辕羽。
Spring的切面是一個(gè)純Java類逛尚,@Aspect注解能夠表明一個(gè)一個(gè)類不僅僅是一個(gè)POJO,還是一個(gè)切面:

package com.tp.aop;

import com.tp.models.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * FileName: UserServiceOperationAspect
 * Author:   TP
 * Description:SpringAop-Service層切面測試
 */
@Slf4j
@Aspect
@Component
public class UserServiceOperationAspect {

    /**
     * 切點(diǎn)定義
     */
    @Pointcut("execution(* com.tp.service.IUserService.getById(..))")
    public void serviceGetById() {
    }

    /**
     * 測試service前置通知
     *
     * @param id 用戶主鍵ID
     */
    @Before("serviceGetById() && args(id)")
    public void getUserBefore(Integer id) {
        log.info(">>>>AOP-Service:前置通知刁愿,請求參數(shù)id為{}", id);
    }

    /**
     * 測試service后置通知
     *
     * @param id 請求參數(shù)
     */
    @After("serviceGetById() && args(id)")
    public void getUserAfter(Integer id) {
        log.info(">>>>AOP-Service:后置通知绰寞,請求參數(shù)id為{}", id);
    }

    /**
     * 測試service返回通知
     *
     * @param id   主鍵ID
     * @param user 用戶信息
     */
    @AfterReturning(pointcut = "serviceGetById() && args(id)", returning = "user")
    public void getUserAfterReturning(Integer id, User user) {
        log.info(">>>>AOP-Service:返回通知,請求參數(shù)id為{},響應(yīng)結(jié)果User信息為:{}", id, user);
    }

    /**
     * 測試service異常通知
     *
     * @param id 請求參數(shù)
     */
    @AfterThrowing(pointcut = "serviceGetById() && args(id)", throwing = "e")
    public void getUserAfterTrowing(Integer id, Exception e) {
        log.info(">>>>AOP-Service:異常通知滤钱,請求參數(shù)id為{}觉壶,異常信息:{}", id, e);
    }

    /**
     * 測試service環(huán)繞通知
     * 需要注意的是,對于環(huán)繞通知必須給一個(gè)ProceedingJoinPoint參數(shù)件缸,并且放在參數(shù)的第一位
     * 方法體中必須調(diào)用proceedingJoinPoint.proceed()方法铜靶,否則會阻塞目標(biāo)方法的執(zhí)行
     *
     * @param proceedingJoinPoint proceedingJoinPoint
     * @param id                  請求參數(shù)id
     * @return 返回值,這里要注意:Around通知返回值不要寫成void(除非目標(biāo)方法返回值為void)他炊,否則最終返回結(jié)果為void
     */
    @Around("serviceGetById() && args(id)")
    public Object getUserAround(ProceedingJoinPoint proceedingJoinPoint, Integer id) throws Throwable{
        log.info(">>>>AOP-Service:環(huán)繞通知-開始争剿,請求參數(shù)id為{}", id);
        Object result = proceedingJoinPoint.proceed();
        log.info(">>>>AOP-Service:環(huán)繞通知-結(jié)束,請求參數(shù)id為{}", id);
        return result;
    }

}

我們在serviceGetById()方法上添加了@Pointcut注解痊末,并且為@Pointcut注解設(shè)置的值是一個(gè)切點(diǎn)表達(dá)式蚕苇,serviceGetById()方法的實(shí)現(xiàn)并不重要,這個(gè)方法本身只是一個(gè)標(biāo)識凿叠,供@Pointcut依附涩笤。

同時(shí)我們需要將UserServiceOperationAspect聲明個(gè)一個(gè)Spring管理的bean,所以我們增加了注解@Component幔嫂。

如果你就此止步的話辆它,UserServiceOperationAspect只會是Spring容器中的一個(gè)bean誊薄,即使使用了@Aspect對它進(jìn)行了標(biāo)注履恩,但是它還沒有真正成為一個(gè)可用的切面,這些注解不會解析也不會創(chuàng)建將其轉(zhuǎn)換為切面的代理呢蔫,我們還需要做的一件事是:啟用AspectJ自動代理功能

如果你使用JavaConfigg的話切心,可以在配置類的類級別上通過使用@EnableAspectJAutoProxy注解啟動自動代理功能:

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class AspectJConfig {
    //聲明bean
    //.....
}

如果你使用Spring的xml裝配bean,那么需要使用Spring AOP命名空間的<aop:aspectj-autoproxy />元素
例如:

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <aop:aspectj-autoproxy />
    <!--bean聲明略-->
</beans>

無論你使用JavaConfig還是XML片吊,AspectJ自動代理都會為使用@Aspect注解的bean創(chuàng)建一個(gè)代理绽昏,這個(gè)代理會圍繞著所有該切面的切點(diǎn)所匹配的bean。

值得注意的是俏脊,對于環(huán)繞通知全谤,它是最為強(qiáng)大的通知類型,它能夠讓你編寫的邏輯將被通知的目標(biāo)方法完全包裝起來爷贫,實(shí)際上就像在一個(gè)通知方法中同時(shí)編寫前置通知和后置通知认然。

另外,在使用環(huán)繞通知的時(shí)候漫萄,我們一般都會在通知的形參上增加一個(gè)ProceedingJoinPoint參數(shù)卷员,并且保證這個(gè)參數(shù)在第一個(gè)位置上,我們需要在通知中通過它來調(diào)用被通知的方法腾务,通知方法中我們可以做任何事情作為增強(qiáng)邏輯毕骡,當(dāng)要將控制權(quán)交給被通知的方法時(shí),需要調(diào)用ProceedingJoinPoint的peoceed()方法,如果不調(diào)用proceed()方法未巫,會阻塞對被通知方法的訪問窿撬。

4.3運(yùn)行測試觀察效果

我們啟動服務(wù),讓IUserService的getById()方法得以執(zhí)行叙凡,觀看效果:

我們將IUserService的實(shí)現(xiàn)類中增加一個(gè)int i = 2/0;讓程序出現(xiàn)異常尤仍,觀察異常通知效果:

@Override
public User getById(Integer id) {
    if (null != id && id > 0) {
        int i = 2/0;
        return userMapper.getById(id);
    }
    return null;
}

效果:

5.使用XML聲明切面

當(dāng)然我們也可以使用Xml聲明切面,我們再寫一個(gè)切面類XmlUserServiceOperationAspect狭姨,這次我們不使用注解的開發(fā)模式宰啦,具體代碼如下:

package com.tp.aop;

import com.tp.models.entity.User;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;

/**
 * FileName: XmlUserServiceOperationAspect
 * Author:   TP
 * Description:基于XML形式聲明切面,切面測試
 */
public class XmlUserServiceOperationAspect {
    private static final Logger log = LoggerFactory.getLogger(XmlUserServiceOperationAspect.class);

    /**
     * 測試service前置通知
     *
     * @param joinPoint 連接點(diǎn)信息
     */
    public void getUserBefore(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        log.info(">>>>AOP-Service:前置通知饼拍,方法名:{}赡模,請求參數(shù):{}", methodName, Arrays.toString(args));
    }

    /**
     * 測試service后置通知
     *
     * @param joinPoint 連接點(diǎn)信息
     */
    public void getUserAfter(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        log.info(">>>>AOP-Service:后置通知,方法名:{}师抄,請求參數(shù):{}", methodName, Arrays.toString(args));
    }

    /**
     * 測試service返回通知
     *
     * @param joinPoint 連接點(diǎn)信息
     * @param user      返回值
     */
    public void getUserAfterReturning(JoinPoint joinPoint, User user) {
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        log.info(">>>>AOP-Service:返回通知漓柑,方法名:{},請求參數(shù)id為{}叨吮,響應(yīng)結(jié)果User信息為:{}", methodName, Arrays.toString(args), user);
    }

    /**
     * 測試service異常通知
     *
     * @param joinPoint 連接點(diǎn)信息
     * @param e         異常信息
     */
    public void getUserAfterTrowing(JoinPoint joinPoint, Exception e) {
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        log.info(">>>>AOP-Service:異常通知辆布,方法名:{},請求參數(shù)id為{}茶鉴,異常信息:{}", methodName, Arrays.toString(args), e);
    }

    /**
     * 測試service環(huán)繞通知
     * 需要注意的是锋玲,對于環(huán)繞通知必須給一個(gè)ProceedingJoinPoint參數(shù),并且放在參數(shù)的第一位
     * 方法體中必須調(diào)用proceedingJoinPoint.proceed()方法涵叮,否則會阻塞目標(biāo)方法的執(zhí)行
     *
     * @param proceedingJoinPoint proceedingJoinPoint
     * @return 返回值惭蹂,這里要注意:Around通知返回值不要寫成void(除非目標(biāo)方法返回值為void),否則最終返回結(jié)果為void
     */
    public Object getUserAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object[] args = proceedingJoinPoint.getArgs();
        String methodName = proceedingJoinPoint.getSignature().getName();
        log.info(">>>>AOP-Service:環(huán)繞通知-開始割粮,方法名:{}盾碗,請求參數(shù)id為{}", methodName, Arrays.toString(args));
        Object result = proceedingJoinPoint.proceed();
        log.info(">>>>AOP-Service:環(huán)繞通知-結(jié)束,方法名:{}舀瓢,請求參數(shù)id為{}廷雅,返回結(jié)果為{}", methodName, Arrays.toString(args), result);
        return result;
    }
}

這時(shí)候我們就可以在Spring的XML配置文件中配置和聲明AOP:

<aop:aspectj-autoproxy />

<bean id="xmlUserServiceOperationAspect" class="com.tp.aop.XmlUserServiceOperationAspect"/>
<aop:config>
    <!--聲明切點(diǎn)-->
    <aop:pointcut id="pointCut" expression="execution(* com.tp.service.IUserService.getById(..))"/>
    <!--聲明切面-->
    <aop:aspect ref="xmlUserServiceOperationAspect">
        <aop:before method="getUserBefore" pointcut-ref="pointCut"/>
        <aop:after method="getUserAfter" pointcut-ref="pointCut"/>
        <aop:after-returning method="getUserAfterReturning" pointcut-ref="pointCut" returning="user"/>
        <aop:after-throwing method="getUserAfterTrowing" pointcut-ref="pointCut" throwing="e"/>
        <aop:around method="getUserAround" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>

編寫一個(gè)測試類:

public class AopMain {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("config/applicationContext.xml");
        IUserService userService = (UserServiceImpl) ctx.getBean("userServiceImpl");
        System.out.println(userService);
        System.out.println("========================================================");
        userService.getById(2);
    }
}

執(zhí)行main函數(shù)效果如下:

6.如何在通知中獲取目標(biāo)方法信息

在日常開發(fā)中我們很可能會在通知的邏輯里需要知道目標(biāo)方法的信息,比如方法名京髓、參數(shù)信息航缀、方法的返回值等等...
訪問目標(biāo)方法最簡單的做法是定義增強(qiáng)處理方法時(shí),將第一個(gè)參數(shù)定義為JoinPoint類型朵锣,當(dāng)該增強(qiáng)處理方法被調(diào)用時(shí)谬盐,該JoinPoint參數(shù)就代表了織入增強(qiáng)處理的連接點(diǎn)。
JoinPoint里包含了如下幾個(gè)常用的方法:

  • Object[] getArgs:返回目標(biāo)方法的參數(shù)
  • Signature getSignature:返回目標(biāo)方法的簽名
  • Object getTarget:返回被織入增強(qiáng)處理的目標(biāo)對象
  • Object getThis:返回AOP框架為目標(biāo)對象生成的代理對象

注意??:當(dāng)使用@Around處理時(shí)诚些,我們需要將第一個(gè)參數(shù)定義為ProceedingJoinPoint類型飞傀,該類是JoinPoint的子類皇型。
具體用法參見上面的XmlUserServiceOperationAspect.java中的通知處理邏輯

另外如果只想訪問目標(biāo)方法的參數(shù),Spring還提供了一種更加簡潔的獲取參數(shù)的方法:我們可以在程序中使用args來綁定目標(biāo)方法的參數(shù)砸烦。如果在一個(gè)args表達(dá)式中指定了一個(gè)或多個(gè)參數(shù)弃鸦,該切入點(diǎn)將只匹配具有對應(yīng)形參的方法,且目標(biāo)方法的參數(shù)值將被傳入增強(qiáng)處理方法幢痘。
例如:

@Aspect
public class AccessArgAdviceTest {
    @AfterReturning(
            pointcut="execution(* com.tp.service.*.access*(..)) && args(time, name)",
            returning="returnValue")
    public void access(Date time, String name, Object returnValue) {
        System.out.println("目標(biāo)方法中的參數(shù)String = " + name);
        System.out.println("目標(biāo)方法中的參數(shù)Date = " + time);
        System.out.println("目標(biāo)方法的返回結(jié)果returnValue = " + returnValue);
    }
}

上面的程序中唬格,定義pointcut時(shí),表達(dá)式中增加了args(time, name)部分颜说,意味著可以在增強(qiáng)處理方法(即access方法)中定義time和name兩個(gè)屬性——這兩個(gè)形參的類型可以隨意指定购岗,但一旦指定了這兩個(gè)參數(shù)的類型,則這兩個(gè)形參類型將用于限制該切入點(diǎn)只匹配第一個(gè)參數(shù)類型為Date门粪,第二個(gè)參數(shù)類型為name的方法(方法參數(shù)個(gè)數(shù)和類型若有不同均不匹配)喊积。

注意,在定義returning的時(shí)候玄妈,這個(gè)值(即上面的returning="returnValue"中的returnValue)作為增強(qiáng)處理方法的形參時(shí)乾吻,位置可以隨意,即:如果上面access方法的簽名可以為:

public void access(Date time, Object returnValue, String name)

也可以為:

public void access(Object returnValue, Date time, String name)

還可以為:

public void access(Date time, String name, Object returnValue)

只需要滿足另外的參數(shù)名的順序和pointcut中args(param1, param2)的順序相同即可拟蜻。
除此之外绎签,使用args表達(dá)式時(shí),還可以使用如下形式:args(param1, param2, ..)
注意args參數(shù)中后面的兩個(gè)點(diǎn)酝锅,它表示可以匹配更多參數(shù)诡必。
在例子args(param1, param2, ..)中,表示目標(biāo)方法只需匹配前面param1和param2的類型即可

我們上面的基于注解創(chuàng)建切面的例子UserServiceOperationAspect.java就是使用這種方式獲取目標(biāo)方法參數(shù)的

7.通知及切面的執(zhí)行順序

5種通知的執(zhí)行順序

不同切面同一個(gè)切點(diǎn)時(shí)的執(zhí)行順序

Spring AOP采用和AspectJ一樣的有限順序來織入增強(qiáng)處理:

  • 在“進(jìn)入”連接點(diǎn)時(shí)屈张,最高優(yōu)先級的增強(qiáng)處理將先被織入(所以給定的兩個(gè)Before增強(qiáng)處理中擒权,優(yōu)先級高的那個(gè)會先執(zhí)行)
  • 在“退出”連接點(diǎn)時(shí),最高優(yōu)先級的增強(qiáng)處理會最后被織入(所以給定的兩個(gè)After增強(qiáng)處理中阁谆,優(yōu)先級高的那個(gè)會后執(zhí)行)。

當(dāng)不同的切面中的多個(gè)增強(qiáng)處理需要在同一個(gè)連接點(diǎn)被織入時(shí)愉老,Spring AOP將以隨機(jī)的順序來織入這些增強(qiáng)處理场绿。如果應(yīng)用需要指定不同切面類里的增強(qiáng)處理的優(yōu)先級,Spring提供了如下兩種解決方案:

  • 讓切面類實(shí)現(xiàn)org.springframework.core.Ordered接口:實(shí)現(xiàn)該接口只需要實(shí)現(xiàn)一個(gè)int getOrder()方法嫉入,該方法返回值越小焰盗,優(yōu)先級越高
  • 直接使用@Order注解來修飾一個(gè)切面類:使用這個(gè)注解時(shí)可以配置一個(gè)int類型的value屬性,該屬性值越小咒林,優(yōu)先級越高

例如xml中:

<aop:aspect ref="xmlUserServiceOperationAspect" order="2">
    <!--聲明通知-->
    <aop:before method="getUserBefore" pointcut-ref="pointCut"/>
    <aop:after method="getUserAfter" pointcut-ref="pointCut"/>
    <aop:after-returning method="getUserAfterReturning" pointcut-ref="pointCut" returning="user"/>
    <aop:after-throwing method="getUserAfterTrowing" pointcut-ref="pointCut" throwing="e"/>
    <aop:around method="getUserAround" pointcut-ref="pointCut"/>
</aop:aspect>

基于注解的切面:

@Aspect
@Order(1)
@Component
public class UserServiceOperationAspect {
  ....
}

將2種切面同時(shí)生效熬拒,運(yùn)行測試類后效果如下:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市垫竞,隨后出現(xiàn)的幾起案子澎粟,更是在濱河造成了極大的恐慌蛀序,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件活烙,死亡現(xiàn)場離奇詭異徐裸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)啸盏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門重贺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人回懦,你說我怎么就攤上這事气笙。” “怎么了怯晕?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵健民,是天一觀的道長。 經(jīng)常有香客問我贫贝,道長秉犹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任稚晚,我火速辦了婚禮崇堵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘客燕。我一直安慰自己鸳劳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布也搓。 她就那樣靜靜地躺著赏廓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪傍妒。 梳的紋絲不亂的頭發(fā)上幔摸,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機(jī)與錄音颤练,去河邊找鬼既忆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嗦玖,可吹牛的內(nèi)容都是我干的患雇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宇挫,長吁一口氣:“原來是場噩夢啊……” “哼苛吱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起器瘪,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤翠储,失蹤者是張志新(化名)和其女友劉穎绘雁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體彰亥,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咧七,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了任斋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片继阻。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖废酷,靈堂內(nèi)的尸體忽然破棺而出瘟檩,到底是詐尸還是另有隱情,我是刑警寧澤澈蟆,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布墨辛,位于F島的核電站,受9級特大地震影響趴俘,放射性物質(zhì)發(fā)生泄漏睹簇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一寥闪、第九天 我趴在偏房一處隱蔽的房頂上張望太惠。 院中可真熱鬧,春花似錦疲憋、人聲如沸凿渊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽埃脏。三九已至,卻和暖如春秋忙,著一層夾襖步出監(jiān)牢的瞬間彩掐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工翰绊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留佩谷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓监嗜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抡谐。 傳聞我的和親對象是個(gè)殘疾皇子裁奇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

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

  • 前言 只有光頭才能變強(qiáng) 上一篇已經(jīng)講解了Spring IOC知識點(diǎn)一網(wǎng)打盡!麦撵,這篇主要是講解Spring的AOP模...
    Java3y閱讀 6,884評論 8 181
  • **** AOP 面向切面編程 底層原理 代理9舫Α@7尽! 今天AOP課程1音五、 Spring 傳統(tǒng) AOP2惫撰、 Spri...
    luweicheng24閱讀 1,367評論 0 1
  • 一、AOP 簡介 AOP(Aspect-Oriented Programming, 面向切面編程): 是一種新的方...
    leeqico閱讀 796評論 0 1
  • 【spring-boot】spring aop 面向切面編程初接觸 眾所周知躺涝,spring最核心的兩個(gè)功能是aop...
    可可西里的星星閱讀 411評論 1 0
  • [TOC] 概述 在軟件開發(fā)中厨钻,散布于應(yīng)用中多處的功能被稱為橫切關(guān)注點(diǎn)(cross-cutting concern...
    0x70e8閱讀 610評論 0 0