1、基于配置文件的切点描述及通知
- 首先,我们得向项目配置XML内加入Aspect的相关依赖:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
</dependency>
- 然后我们要向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"
- 通过 Bean 将 Aspect 对象进行实例化:
<!-- 例如我的Aspect对象叫LoginServiceAdvisor -->
<bean id="loginServiceAdvisor" class="top.halvez.springdemo.dto.LoginServiceAdvisor" />
- 定义切面及通知:
- 我们先认识一下切面表达式的定义:
例: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)的切点描述及通知
- 和基于配置文件的方法相同,我们也得导入依赖,并且通过 Bean 将要模切类及 Aspect 类实例化:
略
- 在 Spring XML 配置文件中加入 AOP 注解代理:
<aop:aspectj-autoproxy />
- 在 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";
}
}