AOP是Aspect Oriented Programming(面向切面编程)的缩写。可以对方法进行增强,底层使用的是动态代理。
快速上手
环境准备
面向切面编程需要引入相关的依赖,在pom.xml(如果是使用的maven构建的项目的话)文件中引入aop的起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
编写AOP程序
在类上加上@Aspect注解,表明这是一个切面类。
通过execution表达式指明对哪些方法进行加强。
@Component
@Aspect
public class RunAsepect {
@Around("execution(* com.example.demo.Service.impl.*.*(..))")
public void Demo (ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
proceedingJoinPoint.proceed();
}
}
通知类型
通知类型的注解有五个
@Around | 环绕通知,该通知方法在目标方法前、后都被执行 |
---|---|
@Before | 前置通知,在目标方法前执行 |
@Aftrer | 后置通知,在目标方法后执行,无论是否有异常都执行 |
@AfterReturning | 返回后通知,出现异常不执行 |
@AfterThrowing | 异常后通知 |
其中,@Around注解需要在方法中手动调用ProceedingJoinPoint类的proceed()方法调用原始方法。
@AfterReturning可以通过在形参中使用Object类型来接收原始方法的返回值。
通知顺序
默认通知顺序
当有多个切面类的execution表达式匹配到同一个方法时,默认的通知顺序与切面类的类名有关,
对于方法前通知,切面类名排名越靠前的越先执行
对于方法后通知,切面类名排名越靠后的越先执行。
更改通知顺序
除了通过更改切面类的类名更改通知顺序外,可以通过@Order注解来更改通知顺序
对于方法前通知,@Order注解内数字越小的越先执行。
对于方法后通知,@Order注解内数字越大的越先执行
@Order(1)
@Component
@Aspect
public class RunAsepect {
@AfterReturning(value = "execution(* com.example.demo.Service.impl.*.*(..))",returning = "result")
public void Demo (JoinPoint joinPoint,Object result)throws Throwable{
System.out.println("方法"+joinPoint.getSignature().getName()+"的返回值为"+result);
}
}
切入点表达式
execution表达式
execution(访问修饰符 返回值 包名.类名.方法名(方法参数) throws 异常)
execution表达式通过访问修饰符,返回值,包名,类名,方法名,异常等进行匹配,
其中:
-
访问修饰符、包名,类名.、异常都可以省略。
-
可使用 * 匹配任一结果
-
可使用 … 可匹配多个连续的任意值。
-
可通过逻辑运算符(||、&&、!)进行较为复杂的execution表达式匹配。
@annotation表达式
@annotation表达式通过指定注解名进行匹配,配合自定义注解便可以对于一些规则性不强的方法进行匹配。
首先自定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyLog {
}
将该注解加在需要增强的方法上
@Repository
public class Service1 implements MyService {
@MyLog
public String run(){
System.out.println("原始方法已被执行");
return "这是结果";
}
}
然后通过@annotation注解对切入点进行描述
@Order(1)
@Component
@Aspect
public class RunAsepect {
@AfterReturning(value = "@annotation(com.example.demo.aop.MyLog)",returning = "result")
//@AfterReturning(value = "execution(* com.example.demo.Service.impl.*.*(..))",returning = "result")
public void Demo (JoinPoint joinPoint,Object result)throws Throwable{
System.out.println("方法"+joinPoint.getSignature().getName()+"的返回值为"+result);
}
}
连接点
Spring中使用JoinPoint抽象了连接点,用它可以获取方法执行时的相关信息。
该类型有以下常用方法
joinPoint.getTarget().getClass().getName();//获得目标类名
joinPoint.getSignature();//获取目标方法签名
joinPoint.getSignature().getName();//获得目标方法名
joinPoint.getArgs();//获得目标方法运行参数
对于@Around类型的通知,由于需要手动地调用原始方法,因此需要使用ProceedingJoinPoint类型来接收。
proceedingJoinPoint.proceed();//调用原始方法