AOP概述
AOP(Aspect-Oriented Programming), 即面向切面编程 , 它与OOP( Object-Oriented Programming, 面向对象编程) 相辅相成。AOP的基本单元是Aspect(切面)
。
Aspect = Advice + PointCut
- Advice(通知): 业务逻辑中一些附加操作称之为通知,可分为Before(前置),After(后置),Around(环绕),AfterThrowing(异常通知),AfterRuturning(返回通知)。
- PointCut(切入点):匹配 join point 的谓词(a predicate that matches join points). Advice 是和特定的 point cut 关联的, 并且在 point cut 相匹配的 join point 中执行.
- JointPoint(连接点):程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理.在 Spring AOP 中, join point 总是方法的执行点, 即只有方法连接点.
关于JointCut和JointPoint的区别和联系
在SpringAOP
中,所有的方法执行都是joinpoint
.而pointcut
是一个描述信息,它修饰的是joinpoint
,通过pointcut
,我们就可以确定哪些joinpoint
可以被织入Advice
. 因此joinpoint
和pointcut
本质上就是两个不同纬度上的东西. 总之,advice
是在joinpoint
上执行的,而pointcut
规定了哪些joinpoint
可以执行哪些advice
.
相关注解
- @Aspect :该注解只能用在类上,作用:代表当前类是一个切面类
- @Before :前置通知,在方法执行之前执行
- value:PointCut(切入点)表达式 二者加起来构建成为一个切面
- JoinPoint:通过JoinPoint可以获得通知的签名信息,如目标方法名、目标方法参数信息等
- @After :后置通知,在方法执行之后执行
- value:PointCut(切入点)表达式 二者加起来构建成为一个切面
- JoinPoint:通过JoinPoint可以获得通知的签名信息,如目标方法名、目标方法参数信息等
- @AfterRuturning :返回通知,在方法返回结果之后执行
- returning:限定了只有目标方法返回值与通知方法相应参数类型一致时才能执行后置返回通知,否则不执行,一般设置方法参数类型为Object
- @AfterThrowing :异常通知,在方法抛出异常之后执行
- throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型一致时才能执行后置异常通知,否则不执行,一般设置方法参数类型为Throwable
- @Around :环绕通知,围绕着方法执行,一般单独使用,环绕通知可以替代上述四种通知方式。
- @Pointcut :切入点,PointCut(切入点)表达式有很多种,其中execution用于使用切面的连接点。
- ProceedingJoinPoint:环绕通知的正在执行中的连接点(这是环绕通知独有的参数)
- return:目标方法执行的返回值
上面所提到的五种通知方法中,除了环绕通知外,其他的四个通知注解中,加或者不加参数JoinPoint都可以,如果有用到JoinPoint的地方就加,用不到就可以不加。
应用
- 引入相关依赖
<!--aop相关的依赖引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 创建测试类
@RestController
public class AopController {
@RequestMapping("/Aop")
public Object aop() {
return "hello aop";
}
@RequestMapping("/Error")
public Object error() {
return 1 / 0;
}
@RequestMapping("/Around")
public Object aroundTest() {
return "around test";
}
}
- 创建切面Aspect
@Aspect
@Component
public class MyAop {
//切入点:待增强的方法
@Pointcut("execution(public * com.dmgr.shence.rest.*.*(..))")
//切入点签名
public void sign() {
System.out.println("pointCut签名...");
}
//前置通知
@Before("sign()")
public void deBefore(JoinPoint jp) {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
System.out.println("URL : " + request.getRequestURL().toString());
System.out.println("HTTP_METHOD : " + request.getMethod());
System.out.println("CLASS_METHOD : " + jp);
System.out.println("ARGS : " + Arrays.toString(jp.getArgs()));
}
//返回通知
@AfterReturning(returning = "ret", pointcut = "sign()")
public void doAfterReturning(JoinPoint jp, Object ret) {
// 处理完请求,返回内容
System.out.println("方法:" + jp + "返回通知:方法的返回值 : " + ret);
}
//异常通知
@AfterThrowing(throwing = "ex", pointcut = "sign()")
public void throwss(JoinPoint jp, Exception ex) {
System.out.println("异常通知:方法异常时执行.....");
System.out.println("产生异常的方法:" + jp);
System.out.println("异常种类:" + ex);
}
//后置通知
@After("sign()")
public void after(JoinPoint jp) {
System.out.println("方法:" + jp + "后置通知:最后且一定执行.....");
}
@Around("")
public void arount() {
}
}
- 启动项目测试
- 环绕通知
环绕通知一般单独使用,且spring提供了一个接口:
ProceedingJoinPoint
,该接口有一个方法process()
,在调用过程中,在process()
方法之前的是前置通知,之后的为返回通知,catch中的是异常通知,finally中的为后置通知。
示例如下:
@Aspect
@Component
public class MyAroundAop {
//切入点:待增强的方法
@Pointcut("execution(public * com.aismall.testaop.controller.*.*(..))")
//切入点签名
public void logAround() {
System.out.println("pointCut签名。。。");
}
//环绕通知,环绕增强,相当于MethodInterceptor
@Around("logAround()")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
Object rtValue = null;
try {
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("通知类中的aroundAdvice方法执行了。。前置");
rtValue = pjp.proceed(args);//明确调用切入点方法(切入点方法)
System.out.println("通知类中的aroundAdvice方法执行了。。返回");
System.out.println("返回通知:"+rtValue);
return rtValue;
} catch (Throwable e) {
System.out.println("通知类中的aroundAdvice方法执行了。。异常");
throw new RuntimeException(e);
} finally {
System.out.println("通知类中的aroundAdvice方法执行了。。后置");
}
}
}