本篇文章旨在整理个人对SpringAOP模块的一个整体掌握。主要会围绕AOP的基本概念、专业术语、使用方法、使用特性等方面进行展开介绍。
基本概念
OOP(面向对象编程):OOP的关注点是对象,主要考虑的是对象的属性和行为
AOP(面向切面编程):AOP是OOP的一个补充,关注点是那些业务无关但是多个类共同拥有的行为逻辑,AOP主要作用就是将这些共同拥有的行为逻辑抽象成为一个模块,以减少代码重复。抽象出来的模块又被称之为切面。
专业术语
- 切面(Aspect):是多个类公共行为逻辑的一个模块化,在Spring中由
@Component和@Aspect共同标记的类即为切面,切面中包含切入点(Pointcut)和通知(Advice) - 连接点(Join point):所有可能被织入通知的点,在SpringAOP中所有运行中的方法都是可以被织入通知的,在SpringAOP中连接点就是所有运行中的方法
 - 切入点(Pointcut):切入点是真正被织入通知的方法。在SpringAOP中通过@Pointcut指定规则,定义哪些方法是切入点
 - 通知(Advice):定义公共行为的代码,织入到切入点,SpringAOP中有5种通知类型,可以织入到切入点的不同地方,
- 前置通知(@Before):在方法执行前执行
 - 后置通知(@AfterReturning):在方法正常返回后执行
 - 环绕通知(@Around):可以方法的执行前后定义逻辑,也可以控制方法执行
 - 异常通知(@AfterThrowing):在方法抛出异常时执行
 - 最终通知(@After):最终执行的方法不管方法是否正常退出
 
 - 目标对象(Target object):被AOP代理对象所代理的对象,目标对象中的方法是需要织入通知的
 - AOP代理(AOP proxy):为了能够让目标对象的方法织入通知,需要生成目标对象的代理对象,这个代理对象目的是完成AOP操作,所以叫AOP代理
 织入(Weaving):将通知增强到切入点中的动作。有以下三种织入时机
- 基于XML方式:
 基于注解方式:@EnableAspectJAutoProxy
切面、切入点、通知定义
```yaml @Component @Aspect public class MyAspect { /**
切入点规则定义 / @Pointcut(“execution( com.gao..(..))”) public void myPointCut(){ }
@Before(“execution( com.gao..*(..))”) public void before(){ System.out.println(“before advice”); }
@After(value = “myPointCut()”) public void after(){ System.out.println(“after”); }
@AfterReturning(“myPointCut()”) public void afterReturning(){ System.out.println(“after returning”); }
@AfterThrowing(“myPointCut()”) public void afterThrowing(){ System.out.println(“after throwing”); }
@Around("myPointCut()")public void around(ProceedingJoinPoint proceedingJoinPoint){System.out.println("around 前");try {proceedingJoinPoint.proceed();System.out.println("around try 后");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("around throw");}finally {System.out.println("around finally");}System.out.println("around 后");}
}
<a name="WMsUl"></a>## 基于XML配置由于现在都是SpringBoot,基于XML方式这里就不费时间写了<a name="ZcTMc"></a># 使用特性<a name="ahweH"></a>## 执行顺序切面的执行顺序可以通过`@Order`指定,下面来看下通知的执行顺序1. 单切面2. 多切面<a name="UVfnO"></a>## 如何修改入参1. 对于@Around通知,可以控制方法的运行和传入的参数,所以很好解决2. 对于其他通知类型1. 我们可以拿到目标方法的参数但是无法修改其值2. 若是引用类型我们是可以改对象的里面属性的,但是无法直接将其对象替换<a name="ZtYF4"></a>## 如何获取入参获取入参的方式官方提供了1个,这边我们还可以通过自定义注解+JoinPoint+SpEL表达式的方式获取1. 方式一:args表达式2. 方式二:自定义注解+JointPoint+SpEL表达式1. 自定义注解中声明一个字段用于指定SpEL表达式2. 将JoinPoiont的所有参数名和参数值放入SpELContext中3. 通过注解中的SpEL表达式获取参数中的值```yamlpackage com.yurun.micro.dms.annotations;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/*** Sap接口注解** @author GaoXi* @date 2022/4/28 19:38*/@Documented@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface SapInterface {/*** 指定全量标识字段: #参数名.全量标识字段*/String fullSign() default "";}
@SapInterface(fullSign = "#maraMasterRequest.zzall")@Overridepublic R<List<SapOtMara>> maraMasterList(MaraMasterRequest maraMasterRequest) {return remoteSapDmsService.maraMasterList(maraMasterRequest);}
/*** 获取是否全量标识字符串** @param joinPoint 连接点* @param fullSign #参数名.字段名* @return 0-非全量 1-全量*/private String getIsFullSign(ProceedingJoinPoint joinPoint, String fullSign) {// 参数值Object[] args = joinPoint.getArgs();// 参数名String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();// SpELContext初始化并解析表达式Expression expression = spelExpressionParser.parseExpression(fullSign);EvaluationContext context = new StandardEvaluationContext();for (int i = 0; i < args.length; i++) {context.setVariable(argNames[i], args[i]);}return Objects.requireNonNull(expression.getValue(context)).toString();}
如何定义一个切入点
- execution:用于定义匹配某些方法
- public 包名.类名.方法名(参数类型,参数类型) 声明异常类型
 
 - within:用于定义在某些类下的方法
- 包名.类名
 
 - this:暂不理解
- 类全限定名
 
 - target:暂不理解
- 类全限定名
 
 - args:匹配方法传入参数类型为指定类型的方法
- 类型,类型
 
 - @target:暂不理解
 - @args:指定方法参数中有指定注解的
 - @within:类上有指定注解的类下所有方法
 - @annotation:匹配有特定注解的方法
 
常见特殊符号
..:所有参数类型.*:一般指下一级的所有东西..*:一般指所有下一级下的所有东西如何定义通知
Before通知
before通知没有什么特别的,正常定义就好
AfterReturning通知
afterReturning可以接收一个返回值
AfterThrowing通知
afterThrowing通知可以接收一个异常
After通知
Around通知
around的可以控制方法的执行,需要在参数中加入ProcessJoinPoint且在参数的首位
JoinPoint方法
所有的通知都可以将JoinPoint类放在方法的首个参数中。JoinPoint提供了如下方法

- 方式二:通过@annotation传入方法上的注解
 
动态代理实现方式选择
Spring的AOP动态代理实现方式有两种:CGLIB和JDK动态代理
- 若被代理的目标对象没有实现接口,则使用CGLIB
 - 若被代理的目标对象实现了接口,则使用JDK动态代理
 - 可以通过强制使用CGLIB动态代理,其方式是将proxyTargetClass设置为true
@EnableAspectJAutoProxy(proxyTargetClass = true) 
