AOP
概念 : AOP为Aspect Oriented Programming的缩写, 意思为切面编程。
他是一种可以在不修改原来核心代码的情况下给程序动态统一进增强的一种技术
springAOP : 批量对spring容器中的bean的方法做增强,并且这种增强不会与原来方法中的代码耦合
依赖
1 2 3 4 5 6
| <dependency> <groupId>org.aspect</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency>
|
快速入门实现
定义切面类
1 2 3 4
| @Component @Aspect public class MyAspect{ }
|
切入点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Component @Aspect public class MyAspect{ @Pointcut("execution("需要增强的路径地址")") public void pt(){} @Before("pt()") public void methodbefore(){ } }
|
aop核心概念
连接点 : 【joinpoint】所谓的连接点就是能够被增强到的点,在spring中 只支持方法类型的连接点
切入点 : 【pointcut】所谓的切入点,就是指被增强的连接点
**通知/增强 :【Advice】 **所谓的通知是指jurisdiction增强的代码
**目标对象 : 【Target】 **被增强的对象就是目标对象
切面: 【Aspect】: 是指切入点 和通知(引介) 的 结合
**代理 : 【Proxy】 **一个类被aop增强后,所产生一个结果的代理类
切入点
- **切入点表达式: **
举例:
- 切点函数(用的不多)
我们可以在要增强的方法上加上注解。然后使用@annotation来表示对加了什么注解的方法进行增强。
1 2 3 4 5 6 7 8 9
|
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface SystemLog {
String businessName(); }
|
- 在自定义注解上需要加
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
- 在需要被注解增强的相关方法上加上我们自定义的注解的注解名就可以实现增强
- 在切面类上进行添加切入点等信息
通知
几种通知的分类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| package tech.pdai.springframework.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class LogAspect {
public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("-----------------------"); System.out.println("环绕通知: 进入方法"); Object o = pjp.proceed(); System.out.println("环绕通知: 退出方法"); return o; }
public void doBefore() { System.out.println("前置通知"); }
public void doAfterReturning(String result) { System.out.println("后置通知, 返回值: " + result); }
public void doAfterThrowing(Exception e) { System.out.println("异常通知, 异常: " + e.getMessage()); }
public void doAfter() { System.out.println("最终通知"); }
}
|
环绕通知
如何使用 ?
1 2 3 4 5 6 7 8 9
| @Around("pt()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("-----------------------"); System.out.println("环绕通知: 进入方法前"); Object o = pjp.proceed(); System.out.println("环绕通知: 退出方法后"); return o; }
|
注意点 : 如果目标方法有返回值 ,那么对应的切面类中的环绕通知的方法也必须有返回值
AspectJ注解方式
基于XML的声明式AspectJ存在一些不足,需要在Spring配置文件配置大量的代码信息,为了解决这个问题,Spring 使用了@AspectJ框架为AOP的实现提供了一套注解。
注解名称 |
解释 |
@Aspect |
用来定义一个切面。 |
@pointcut |
用于定义切入点表达式。在使用时还需要定义一个包含名字和任意参数的方法签名来表示切入点名称,这个方法签名就是一个返回值为void,且方法体为空的普通方法。 |
@Before |
用于定义前置通知,相当于BeforeAdvice。在使用时,通常需要指定一个value属性值,该属性值用于指定一个切入点表达式(可以是已有的切入点,也可以直接定义切入点表达式)。 |
@AfterReturning |
用于定义后置通知,相当于AfterReturningAdvice。在使用时可以指定pointcut / value和returning属性,其中pointcut / value这两个属性的作用一样,都用于指定切入点表达式。 |
@Around |
用于定义环绕通知,相当于MethodInterceptor。在使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。 |
@After-Throwing |
用于定义异常通知来处理程序中未处理的异常,相当于ThrowAdvice。在使用时可指定pointcut / value和throwing属性。其中pointcut/value用于指定切入点表达式,而throwing属性值用于指定-一个形参名来表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法抛出的异常。 |
@After |
用于定义最终final 通知,不管是否异常,该通知都会执行。使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。 |
@DeclareParents |
用于定义引介通知,相当于IntroductionInterceptor (不要求掌握)。 |
获取被增强方法相关信息
我们实际对方法进行增强时往往还需要获取到被增强方法的相关信息,比如 : 方法名,参数,返回值,异常信息….
我们除了在环绕通知外的所有通知方法中增加一个JoinPoint类型的参数。这个参数封装了被增强方法的相关信息 。我们还可以通过这个参数获取到除了异常对象 和返回值 之外的所有信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
private void handleBefore(ProceedingJoinPoint joinPoint) { ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); SystemLog systemLog = getSystemLog(joinPoint); log.info("============Start============"); log.info("URL : {}",request.getRequestURL()); log.info("BusinessName : {}",systemLog.businessName()); log.info("HTTP Method : {}",request.getMethod()); log.info("Class Method : {}.{}",joinPoint.getSignature().getDeclaringTypeName(),((MethodSignature) joinPoint.getSignature()).getName()); log.info("IP : {}",request.getRemoteHost()); log.info("Request Args : {}", JSON.toJSONString(joinPoint.getArgs()) );
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private void handleBefore(ProceedingJoinPoint joinPoint) { String className = joinPoint.getSignature().getDeclaringTypeName(); System.out.println("获取 类名: " + className);
String methodName = joinPoint.getSignature().getName(); System.out.println("获取方法名 : " + methodName);
Object[] args = joinPoint.getArgs(); System.out.println("获取参数;" + Arrays.toString(args)); System.out.println("获取参数(转为json格式);" + JSON.toJSONString(args));
}
|
异常
多切面增强顺序
通过@Order的大小来规定多个切面的顺序
参考学习说明:
视频学习 : 三更springAOP
相关资料参考 :https://pdai.tech/md/spring/spring-x-framework-aop.html