4.获取目标方法的详细信息
需要参数JoinPoint类,封装了目标方法的详细信息
获取方法参数,joinPoint.getSignature())可以获取方法名,joinPoint.getArgs()可以获取参数列表
@Before("addPointCut()") public void logStart(JoinPoint joinPoint ){ System.out.println(this.getClass().getName() + "切面类运行-目标方法调用前的输出-@Before前置通知"); System.out.println("目标方法名:" + joinPoint.getSignature()); System.out.println("目标方法参数:" + Arrays.toString(joinPoint.getArgs())); } 复制代码
执行测试方法中的add()
获取返回值,需要定义一个变量来接收,方法在入参中,为了能接收各种类型的参数因此最好定义为Object类型
@AfterReturning(value = "addPointCut()", returning = "result") public void logReturn(Object result){ System.out.println(this.getClass().getName() + "切面类运行-目标方法返回:" + result + ",@AfterReturning正常返回"); } 复制代码
异常同样
@AfterThrowing(value = "divPointCut()", throwing = "e") public void logException(Exception e){ System.out.println(this.getClass().getName() + "切面类运行-目标方法抛出异常:" + e); } 复制代码
异常也可以指定传入哪种异常,方法执行爆出的异常符合传入的异常才会打印出异常信息,否则不会,所以传入的异常类型要尽量是Exception,如果可以确切的知道方法爆出的异常,可以指定具体异常来接收, Result同样也可以指定具体类型来接收返回值
执行add方法和div方法的测试
5.切入点表达式
@Pointcut("execution(public * com.citi.util.impl.AppleCalculator.add(int, int))") public void addPointCut(){ } @Pointcut("execution(public * com.citi.util.impl.AppleCalculator.div(int, int))") public void divPointCut(){ } 复制代码
@Pointcut注解定一个切点表达式,方法内部不用实现任何功能
6.环绕通知
其实就是一个动态代理
@Around("addPointCut()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); System.out.println("环绕通知-方法执行前"); Object o = null; try { System.out.println("环绕通知-前置通知," + methodName + "方法开始执行"); // 反射执行目标方法 o = joinPoint.proceed(); System.out.println("环绕通知-返回通知," + methodName + "返回值:" + o.toString()); } catch (Exception e){ System.out.println("环绕通知-异常通知," + methodName + "异常信息为:" + e); } finally { System.out.println("环绕通知-后置通知," + methodName + "方法执行结束"); } return o; } 复制代码
将普通通知的注解注释,执行add方法
将切入点表达式改为divPointcut,执行div方法的测试
普通通知和环绕通知同时存在时的执行顺序,将环绕通知的切点表达式改为addPointcut,将普通通知方法的注解注释取消,执行add方法的测试
7.有多个切面类的情况下的运行顺序
新增切面类
@Component @Aspect public class VerifyAspect { @Pointcut("execution(public * com.citi.util.impl.AppleCalculator.add(int, int))") public void addPointCut(){ } @Pointcut("execution(public * com.citi.util.impl.AppleCalculator.div(int, int))") public void divPointCut(){ } @Before("addPointCut()") public void verifyStart(JoinPoint joinPoint ){ System.out.println(this.getClass().getName() + "切面类执行-目标方法调用前的输出-@Before"); //System.out.println("目标方法名:" + joinPoint.getSignature()); //System.out.println("目标方法参数:" + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(value = "addPointCut()",returning = "result") public void verifyReturn(Object result){ System.out.println(this.getClass().getName() + "切面类执行-方法执行后输出返回值:" + result + ",@AfterReturning正常返回"); } @After(value = "addPointCut()") public void verifyEnd(JoinPoint joinPoint){ System.out.println(this.getClass().getName() + "切面类执行-目标方法调用结束,@After后置通知"); } @AfterThrowing(value = "divPointCut()",throwing = "e") public void verifyException(Exception e){ System.out.println(this.getClass().getName() + "切面类执行-方法抛出异常:" + e); } } 复制代码
LogAspect暂时注销环绕通知
@Component @Aspect public class LogAspect { @Pointcut("execution(public * com.citi.util.impl.AppleCalculator.add(int, int))") public void addPointCut(){ } @Pointcut("execution(public * com.citi.util.impl.AppleCalculator.div(int, int))") public void divPointCut(){ } @Before("addPointCut()") public void logStart(JoinPoint joinPoint ){ System.out.println(this.getClass().getName() + "切面类运行-目标方法调用前的输出-@Before前置通知"); //System.out.println("目标方法名:" + joinPoint.getSignature()); //System.out.println("目标方法参数:" + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(value = "addPointCut()", returning = "result") public void logReturn(Object result){ System.out.println(this.getClass().getName() + "切面类运行-目标方法返回:" + result + ",@AfterReturning正常返回"); } @After("addPointCut()") public void logEnd(JoinPoint joinPoint){ System.out.println(this.getClass().getName() + "方法调用结束,@After后置通知"); } @AfterThrowing(value = "addPointCut()", throwing = "e") public void logException(Exception e){ System.out.println(this.getClass().getName() + "切面类运行-目标方法抛出异常:" + e); } //@Around("addPointCut()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); System.out.println("环绕通知-方法执行前"); Object o = null; try { System.out.println("环绕通知-前置通知," + methodName + "方法开始执行"); // 反射执行目标方法 o = joinPoint.proceed(); System.out.println("环绕通知-返回通知," + methodName + "返回值:" + o.toString()); } catch (Exception e){ System.out.println("环绕通知-异常通知," + methodName + "异常信息为:" + e); } finally { System.out.println("环绕通知-后置通知," + methodName + "方法执行结束"); } return o; } } 复制代码
运行add方法的测试方法
执行顺序,先进后出
切面类先后执行是根据切面类名字首字母排序,讲VerifyAspect改为AerifyAspect,再次执行add的测试方法
要想改变按照切面类字母顺序执行切面类,可以在类上增加@Order注解,数值越小优先级越高,给LogAspect加上@Order(1),默认是2147483647,再次执行add方法的测试方法
有环绕通知的情况下,将LogAspect的环绕通知注解注释取消,并在输出中加上类名标识
环绕通知加在哪个切面,那个切面执行环绕,环绕通知优先执行,环绕通知中执行方法
AOP使用场景:
- AOP加日志保存到数据库
- AOP作权限验证
- AOP作安全检查
- AOP作事务控制