1、基于配置文件的切点描述及通知
  1. 首先,我们得向项目配置XML内加入Aspect的相关依赖:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.8</version>
</dependency>
  1. 然后我们要向Spring配置XML内加入AOP的命名空间(xmlns)和xsi:
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
  1. 通过 Bean 将 Aspect 对象进行实例化:
<!-- 例如我的Aspect对象叫LoginServiceAdvisor -->
<bean id="loginServiceAdvisor" class="top.halvez.springdemo.dto.LoginServiceAdvisor" />
  1. 定义切面及通知:
  • 我们先认识一下切面表达式的定义:
    例:execution(* top.halvez.springdemo.dto.LoginService.*(..)) 在例中,第一个 \* 号代表的是返回值类型,\* 号表示任意, top.halvez.springdemo.dto.LoginService.*(..)中的 \* 号代表的是类中的方法,这里的 \* 号表示所有方法,() 中的 .. 代表的是传入的参数类型, 这里的 .. 表示任意个任意类型的参数,top.halvez.springdemo.dto.LoginService代表的是要模切通知的类。
  • 切面定义之后,我们认识一下通知的类型:
    1、前置通知(Before advice):在**连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。
    2、后置通知(After advice):在
    连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
    3、正常返回通知(After returning advice):在
    连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。
    4、异常返回通知(After throwing advice):在
    连接点抛出异常后执行。
    5、环绕通知(Around advice):环绕通知围绕在
    连接点**前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。
  • 我们用一个例子来解释说明一下:
    Spring XML配置文件:
<!-- 定义切点 -->
<aop:config>
    <!-- 定义切面,其中,ref= 指向的是之前定义好的Aspect通知对象 -->
    <aop:aspect id="aopLoginService" ref="loginServiceAdvisor">
        <!-- 定义切点,其中,expression= 指向的是切点的表达式 -->
        <aop:pointcut id="aopLoginServicePointCut" expression="execution(* top.halvez.springdemo.dto.LoginService.*(..))" />
        <!-- 定义前置通知的切点及通知方法,其中,method= 指向的是此通知的调用方法,定义在LoginServiceAdvisor类中 -->
        <aop:before method="before" pointcut-ref="aopLoginServicePointCut" />
        <!-- 定义后置通知的切点及通知方法 -->
        <aop:after method="before" pointcut-ref="aopLoginServicePointCut" />
        <!-- 定义正常返回通知的切点及通知方法,其中,returning= 指向被模切对象方法的返回参数 -->
        <aop:after-returning method="afterReturning" pointcut-ref="aopLoginServicePointCut" returning="ret" />
        <!-- 定义异常返回通知的切点及通知方法,其中,throwing= 指向被模切对象抛出的异常参数 -->
        <aop:after-returning method="afterReturning" pointcut-ref="aopLoginServicePointCut" throwing="thr" />
        <!-- 定义环绕通知切点的及通知方法 -->
        <aop:around method="around" pointcut-ref="aopLoginServicePointCut" />
    </aop:aspect>
</aop:config>

LoginServiceAdvisor类:

// 导入必要的包
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
// LoginServiceAdvisor类
public class LoginServiceAdvisor {
    // 前置通知方法
    public void before() {
        System.out.println("--before--");
    }
    // 后置通知方法
    public void after() {
        System.out.println("--after--");
    }
    // 正常返回通知方法
    public void afterReturning(Object ret) {
        System.out.println("--after returning--");
        System.out.println(ret);
    }
    // 异常返回通知方法
    public void afterThrowing(Throwable thr) {
        System.out.println("--after throwing--");
        System.out.println(thr);
    }
    // 环绕通知方法
    public Object around(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName());

        System.out.println("--around before--");
        try {
            joinPoint.proceed();
        } catch (Throwable e) {
            System.out.println("--around join point--");
        } finally {
            System.out.println("--around after--");
        }
        return "Meew";
    }
}
2、基于注解(Annotation)的切点描述及通知
  1. 和基于配置文件的方法相同,我们也得导入依赖,并且通过 Bean 将要模切类及 Aspect 类实例化:
  2. 在 Spring XML 配置文件中加入 AOP 注解代理:<aop:aspectj-autoproxy />
  3. 在 Aspect 类(以 LoginServiceAdvisor 类为例)中加入对应的注解:
// 导入相应的包
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;

// 添加 @Aspect 注解表示这是切面
@Aspect
public class LoginServiceAdvisor {
    // 添加 @Pointcut 标识模切点方法和切面表达式
    @Pointcut("execution(* top.halvez.springdemo.dto.LoginService.*(..))")
    private void pointCut() {}

    // 添加 @Before 标识前置通知方法
    @Before("pointCut()")
    public void before() {
        System.out.println("--before--");
    }

    // 添加 @After 标识后置通知方法
    @After("pointCut()")
    public void after() {
        System.out.println("--after--");
    }

    // 添加 @AfterReturning 标识正常返回通知方法
    @AfterReturning(pointcut = "pointCut()", returning = "ret")
    public void afterReturning(Object ret) {
        System.out.println("--after returning--");
        System.out.println(ret);
    }

    // 添加 @AfterThrowing 标识异常返回通知方法
    @AfterThrowing(pointcut = "pointCut()", throwing = "thr")
    public void afterThrowing(Throwable thr) {
        System.out.println("--after throwing--");
        System.out.println(thr);
    }

    // 添加 @Around 标识环绕通知方法
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName());

        System.out.println("--around before--");
        try {
            joinPoint.proceed();
        } catch (Throwable e) {
            System.out.println("--around join point--");
        } finally {
            System.out.println("--around after--");
        }
        return "Meew";
    }
}