AOP(Aspect-Oriented Programming)面向切面编程
是一种新的方法论,是对传统OOP(Object-Oriented Programming,面向对象编程)的补充,是一种通过动态代理实现程序功能扩展和统一维护的一种技术
OOP:面向对象编程,纵向继承机制,重点关注类
AOP:面向切面编程,横向提取机制,重点关注方法
一、动态代理
原理:一个代理将原本的对象包装起来,然后用该代理对象“取代”原始对象。任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。
二、AOP
1、准备工作
<!--spring-aspects的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 开启组件扫描【使用注解】-->
<context:component-scan base-package="com.atguigu.annotationAspectJ" ></context:component-scan>
<!-- 开启AspectJ注解支持【自动为目标对象创建代理对象】-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2、AOP术语:
(1)横切关注点:非核心业务称为横切关注点
(2)切面<Aspect>:将横切关注点提取到类中,类称为切面
(3)通知<Advice>:将横切关注点提取到类中后,横切关注点更名为:通知
(4)目标<Taeget>:目标对象【Calclmpl】
(5)代理<Proxy>:代理对象
@Autowired
private Calc calc; //com.sun.proxy.$Proxy21
(6)连接点<Joinpoint>:先将横切关注点提取切面类中【通知】,再动态将通知织入回业务代码中;织入位置称之为,连接点。【织入之前】
(7)切入点<pointcut>:先将横切关注点提取切面类中【通知】,再动态将通知织入回业务代码中;织入位置称之为,连接点。【织入之后】
3、AOP切入点表达式
(1) 基本语法
execution( [权限修饰符] [返回值类型] [简单类名/全类名] [方法名] ( [参数列表类型] )
execution(public int com.atguigu.annotationAspectJ.CalcImpl.add(int,int))
(2) 表达式常用通配符
【*】:可以代表任意权限修饰符和返回值类型 ; 任意包 | 任意类全限定名 ; 任意方法名
【..】:代表任意参数类型 | 参数数量
(3) 重用切入点表达式
提取公共切入点表达式
@Pointcut("execution(* com.atguigu.annotationAspectJ.CalcImpl.*(..))")
public void myPointCut(){
}
提取后的方法名,作为切入点表达式
@Before("myPointCut()")
public void methodBefore(JoinPoint joinPoint){
(4)AspectJ中支持通知
- 前置通知:@Before
在业务代码执行前执行,如有异常也执行
@Pointcut("execution(* com.atguigu.annotationAspectJ.CalcImpl.*(..))")
public void myPointCut(){
}
/**
* 配置通知【前置通知---在执行业务代码之前执行】
* @param joinPoint
*/
//@Before("execution(public int com.atguigu.annotationAspectJ.CalcImpl.add(int,int))")
@Before("myPointCut()")
public void methodBefore(JoinPoint joinPoint){
//通过方法签名【方法名形参列表】,获取方法名
String name = joinPoint.getSignature().getName();
//获取参数
Object[] args = joinPoint.getArgs();
System.out.println("日志功能【前置通知】:执行"+name+"()方法之前,参数:"+ Arrays.toString(args));
}
- 后置通知:@After
在业务代码执行之后执行,如有异常也执行
/**
* 后置通知【在执行业务代码之后执行】
* @param joinPoint
*/
@After("execution(* com.atguigu.annotationAspectJ.CalcImpl.*(..))")
public void methodAfter(JoinPoint joinPoint){
String name = joinPoint.getSignature().getName();
//获取参数
Object[] args = joinPoint.getArgs();
System.out.println("日志功能【后置通知】:执行"+name+"()方法之前,参数:"+ Arrays.toString(args));
}
- 返回通知:@AfterReturning
在业务代码返回结果后<后置通知之前>执行,如有异常不执行
返回通知要求,returning="属性值"与入参中参数名一致
@AfterReturning(value = "myPointCut()",returning = "rs")
public void methodReturn(JoinPoint joinPoint,Object rs){
String name = joinPoint.getSignature().getName();
System.out.println("日志功能【返回通知】:执行"+name+"()方法,返回结果rs:"+ rs);
}
- 异常通知:@AfterThrowing
在业务代码出现异常时<后置通知之前>执行,如有异常执行;无异常不执行
异常通知要求,throwing="属性值"与参数名一致
@AfterThrowing(value = "myPointCut()",throwing = "ex")
public void methodThrowing(JoinPoint joinPoint,Exception ex){
String name = joinPoint.getSignature().getName();
System.out.println("日志功能【异常通知】:执行"+name+"()方法,出现异常ex:"+ ex);
}
- 环绕通知:@Around
将业务代码<前置 | 后置 | 返回 | 异常>通知,整合一处
环绕通知的参数,只能ProcessingJoinPoint
@Around(value = "myPointCut()")
public Object methodAround(ProceedingJoinPoint proceedingJoinPoint){
Object rs = null;
//获取方法名
String name = proceedingJoinPoint.getSignature().getName();
//获取参数
Object[] args = proceedingJoinPoint.getArgs();
try {
//前置通知
System.out.println("日志功能【前置通知】:执行"+name+"()方法之前,参数:"+ Arrays.toString(args));
//执行目标对象的目标方法【add()】
Object object = proceedingJoinPoint.proceed();
//返回通知
System.out.println("日志功能【返回通知】:执行"+name+"()方法,返回结果rs:"+ rs);
} catch (Throwable ex) {
ex.printStackTrace();
System.out.println("日志功能【异常通知】:执行"+name+"()方法,出现异常ex:"+ ex);
} finally {
//后置通知
System.out.println("日志功能【后置通知】:执行"+name+"()方法之前,参数:"+ Arrays.toString(args));
}
return rs;
}
(5)切面优先级
@Order(value=index) index=正整数
作用:设置切面优先级 ,数值越小,优先级越高
/**
* Creates with IntelliJ IDEA.
* Author: 李龙
* Date: 2021/10/13 11:20
* Description: TODO
* Since version-1.0
* 日志切面
*/
@Component //将当前类装配IOC容器中
@Aspect //将当前类标识为一个切面【】
@Order(value = 2)
public class MyLogging {
}
/**
* Creates with IntelliJ IDEA.
* Author: 李龙
* Date: 2021/10/13 16:41
* Description: TODO
* Since version-1.0
* 验证切面
*/
@Component
@Aspect
@Order(value = 1)
public class MyValidate {
}