切面类:有方法开始、方法返回、方法异常、方法结束。(通知方法)
~切入点: 执行通知方法的叫切入点,通过切入点表达式------>选择有需要的切入点。
作用:切面类定义的通知方法动态发现目标对象执行哪个位置,动态插入目标对象中。
编写通知方法,告诉spring一下通知方法何时何地运行(切入点表达式)。
~连接点:封装了当前目标对象详细信息,在参数使用JoinPoint。
切入点
一、何时?
@Before方法执行之前运行
@AfterReturning方法执行正常返回结果运行
@AfterThrowing方法抛出异常运行
@After方法执行之后运行
@Around环绕通知
二、何地?
切入点表达式:
1)execution(方法的全签名)
1.全写法:public int com.scoro.spring.aop.calculator.MathCalculator.add(int,int)
2.省略写法:int add(int ,int)
通配符:
..; ①任意多个参数和类型(参数位置)②代表多个层级(类型位置)
*;代表所有(任意字符)
2)args(int,int)------>以指定参数切入
3)@annotation("注解全类名")------>标了注解的切(方法上有没有标注解)
java
@Component
public class MathCalculatorImpl implements MathCalculator {
@Override
public int add(int a, int b) {
int sun=a+b;
System.out.println("sum="+sun);
return sun;
}
@Override
public int subtract(int a, int b) {
int sun=a-b;
System.out.println("sum="+sun);
return sun;
}
@Override
public int multiply(int a, int b) {
int sun=a*b;
System.out.println("sum="+sun);
return sun;
}
@Override
public int divide(int a, int b) {
int sun=a/b;
System.out.println("sum="+sun);
return sun;
}
@MyAn
@Override
public void annMethod(int a, int b) {
System.out.println("annotation"+a);
}
}
java
@Component
@Aspect //告诉spring这个组件是切面
public class LogAspect {
// 方法执行之前运行
@Before("execution(int com.scoro.spring.aop.calculator.MathCalculator.*(..))")
public void logBefore() {
System.out.println("切面~开始");
}
// 方法执行正常返回结果运行
@AfterReturning("execution(int *(int,int))")
public void logReturn() {
System.out.println("切面~返回");
}
// 方法抛出异常运行
@AfterThrowing("execution(int *(int,int))")
public void logException() {
System.out.println("切面~异常");
}
// 方法执行之后运行
@After("execution(int *(int,int))")
public void logAfter() {
System.out.println("切面~结束");
}
@Before("args(int,int)") //args:以指定参数切
public void arg(){
System.out.println("切面~parameter");
}
@Before("@annotation(com.scoro.spring.aop.annotation.MyAn)") //@annotation注解:标了注解的切(方法上有没有标注解)
public void annoMethod(){
System.out.println("切面~annotation");
}
}
java
@Target({ ElementType.PARAMETER,ElementType.METHOD}) //说明注解的作用目标,里面value值按需求添加
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAn {
}
@Pointcut 简化开发
java
@Component
@Aspect //告诉spring这个组件是切面
public class LogAspect {
@Pointcut("execution(int com.scoro.spring.aop.calculator.MathCalculator.*(..))")
public void pointCut(){} //空方法是为了给被人引用
@Before("pointCut()")
public void logBefore() {
System.out.println("切面~开始");
}
@AfterReturning(value = "pointCut()")
public void logReturn() {
System.out.println("切面~返回");
}
}
@Around环绕通知
环绕通知:①整合了前面四种通知方法
②可以控制目标方法是否执行,修改目标方法参数等执行结果。
java
@Aspect
@Component
public class AroundAspect {
@Pointcut("execution(int com.scoro.spring.aop.calculator.MathCalculator.*(..))")
public void pointCut(){}
/**
* 环绕通知固定写法:
* Object:返回值
* ProceedingJoinPoint:可以继续推进的切点
*/
@Around("pointCut()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
// 获取目标参数
Object[] args = proceedingJoinPoint.getArgs();
// 前置
System.out.println("环绕~前置");
Object proceed=null;
try {
// 接受传参数的proceed,在传参数前实现修改目标方法执行用的参数
args[1]=9; //修改参数
proceed = proceedingJoinPoint.proceed(args);//继续执行目标方法
System.out.println("环绕~返回"+proceed);
}catch (Exception e){
System.out.println("环绕~异常");
throw e; //吃掉异常,抛出异常
}finally {
System.out.println("环绕~后置");
}
// 修改返回值
return proceed;
}
}
连接点
JoinPoint 连接点:获取当前目标方法的详细信息
java
@Component
@Aspect
public class LogAspect {
@Before("execution(int com.scoro.spring.aop.calculator.MathCalculator.*(..))")
public void logBefore(JoinPoint joinPoint) {
// 拿到方法全签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 方法名
String name = signature.getName();
// 目标方法传过来的参数值
Object[] args = joinPoint.getArgs();
System.out.println(name+"切面~开始"+ Arrays.toString(args));
}
@AfterReturning(value = "execution(int *(int,int))",returning = "result") //returning = "result"获取目标方法返回值
public void logReturn(JoinPoint joinPoint,Object result) {
// 获取目标方法返回值
System.out.println("切面~返回"+result);
}
@AfterThrowing(value = "execution(int *(int,int))",throwing = "e")
public void logException(JoinPoint joinPoint,Exception e) {
System.out.println("切面~异常"+e);
}
}
多切面执行顺序
例:有两个切面对MathCalculatorImpl类进行干预,两个切面类是谁先干预?