AOP(Aspect Oriented Programming),面向切面思想,是Spring的三大核心思想之一(两外两个:IOC-控制反转、DI-依赖注入)
个人理解:Aop是在业务流程中增加新的通用流程时,如添加日志,统计方法运行时间等时,做一个拦截,在方法执行前后,都可以做额外的动作,可以做一些无关业务流程的工作如添加日志,也可以对业务流程进行干涉,如对参数进行修改,返回值进行修改等。类似python的装饰器的作用。
1、定义一个切点Pointcut,切点主要是用来定义在哪里切入,比如对所有的get请求进行请求前添加日志,那么就需要针对所有@GetMapping注解的方法进行拦截
2.定义一个针对切点的处理方法Advice,这个主要来定义什么时候进行处理,方法执行前,方法执行后(专业术语:前置和后置处理)
-
org.springframework.boot -
spring-boot-starter-aop
使用@Aspect进行注解,来标记是一个切面类
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
可以使用上边注解直接加切入点表达式的方式,也可以使用下边方式进行切入点表达式重用
使用@Pointcut进行方法注解,注解参数表明是对什么方法或类等进行拦截
该注解有两种类型的参数:一个是使用 execution(),另一个是使用 annotation()
execution表达式可以详细参考
execution (* com.sample.service.impl..*.*(..))
annotation() 方式是针对某个注解来定义切点
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")

有5个注解
1.@Before 标注的方法在切面切入目标方法之前执行
2.@After 标注的方法在切面切入目标方法之后执行
3.@Arround 可以自由选择增强动作与目标方法的执行顺序,也就是说可以在增强动作前后,甚至过程中执行目标方法,因为被@Arround标注的方法第一个形参必须是ProceedingJoinPoint类型,调用ProceedingJoinPoint参数的procedd()方法才会执行目标方法,这个方法的调用时机自己可以进行控制,没有调用ProceedingJoinPoint的proceed方法,则目标方法不会执行。
@Around可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值
调用ProceedingJoinPoint的proceed方法时,还可以传入一个Object[ ]对象,该数组中的值将被传入目标方法作为实参,所以可以改变执行目标方法的参数。传入的Object[ ]数组长度与目标方法所需要的参数必须相等。
4、@AfterReturning 注解和 @After 有些类似,区别在于 @AfterReturning 注解可以用来捕获切入方法执行完之后的返回值,对返回值进行业务逻辑上的增强处理
5、当被切方法执行过程中抛出异常时,会进入 @AfterThrowing 注解的方法中执行,在该方法中可以做一些异常的处理逻辑
6、多个
- // @Aspect表示这个类是一个切面类
- @Aspect
- // @Component注解保证这个切面类能够放入IOC容器
- @Component
- public class LogAspect {
-
- @Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
- public void beforeMethod(JoinPoint joinPoint){
- String methodName = joinPoint.getSignature().getName();
- String args = Arrays.toString(joinPoint.getArgs());
- System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
- }
-
- @After("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
- public void afterMethod(JoinPoint joinPoint){
- String methodName = joinPoint.getSignature().getName();
- System.out.println("Logger-->后置通知,方法名:"+methodName);
- }
-
- @AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
- public void afterReturningMethod(JoinPoint joinPoint, Object result){
- String methodName = joinPoint.getSignature().getName();
- System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
- }
-
- @AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
- public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
- String methodName = joinPoint.getSignature().getName();
- System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
- }
-
- @Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
- public Object aroundMethod(ProceedingJoinPoint joinPoint){
- String methodName = joinPoint.getSignature().getName();
- String args = Arrays.toString(joinPoint.getArgs());
- Object result = null;
- try {
- System.out.println("环绕通知-->目标对象方法执行之前");
- //目标对象(连接点)方法的执行
- result = joinPoint.proceed();
- System.out.println("环绕通知-->目标对象方法返回值之后");
- } catch (Throwable throwable) {
- throwable.printStackTrace();
- System.out.println("环绕通知-->目标对象方法出现异常时");
- } finally {
- System.out.println("环绕通知-->目标对象方法执行完毕");
- }
- return result;
- }
-
- }
-
- package com.ljx.splearn.AopDemo;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.Signature;
- import org.aspectj.lang.annotation.*;
- import org.springframework.core.annotation.Order;
- import org.springframework.stereotype.Component;
-
- import java.util.HashMap;
- import java.util.Map;
-
- /**
- * @author lijianxi
- * @date 2022年11月09日 10:58 上午
- */
- @Order(0)
- //标注该类是切面类
- @Aspect
- //交给spring容器
- @Component
- public class LogAdvice {
- //@Pointcut 注解定义什么时机进行拦截,要拦截的是什么东西,一个是使用 execution(),另一个是使用 annotation()。
- //annotation() 方式是针对某个注解来定义切点,execution指明哪些类或包或方法被执行的表达式
- //表明什么时机进行切入,本例中是被getmapping注解到的方法被调用时(参数是一个注解,表示被该注解标注的方法调用时进行切入)
- @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
- private void pointCut(){}
- public long t1 ;
- public long t2;
-
- //指定的方法在切面切入目标方法之前执行,注入后做什么动作,参数是切点方法
- @Before("pointCut()")
- public void logAdvice(JoinPoint joinPoint){
-
- // 获取签名
- Signature signature = joinPoint.getSignature();
- // 获取切入的包名
- String declaringTypeName = signature.getDeclaringTypeName();
- // 获取即将执行的方法名
- String funcName = signature.getName();
- System.out.println("执行前开始记录");
- t1 = System.currentTimeMillis();
- }
-
- //指定的方法在切面切入目标方法之后执行
- @After("pointCut()")
- public void logAdvice1(JoinPoint joinPoint) throws Throwable {
- Object[] args = joinPoint.getArgs();
- String name =(String) args[0];
- System.out.println("结束记录1"+name);
- t2=System.currentTimeMillis();
- System.out.println("执行时间"+(t2-t1));
- }
- //自由选择增强动作与目标方法的执行顺序
- @Around("pointCut()")
- //方法参数必须是ProceedingJoinPoint,而不能是JoinPoint
- public Object logAdvice2(ProceedingJoinPoint joinPoint) throws Throwable {
- //获取请求参数
- Object[] args = joinPoint.getArgs();
- String name =(String) args[0];
- System.out.println("结束记录2"+name);
- t2=System.currentTimeMillis();
- System.out.println("执行时间1"+(t2-t1));
- //修改参数
- args[0]="王五";
- joinPoint.proceed(args);
- //修改返回值
- return "hello ,zhangsan";
- }
-
- /**
- * 在上面定义的切面方法返回后执行该方法,可以捕获返回对象或者对返回对象进行增强
- * @param joinPoint joinPoint
- * @param result result
- * 属性 returning 的值必须要和参数保持一致
- */
- @AfterReturning(pointcut = "pointCut()", returning = "result")
- public void doAfterReturning(JoinPoint joinPoint, Object result) {
-
- Signature signature = joinPoint.getSignature();
- String classMethod = signature.getName();
- System.out.println((String.format("方法%s执行完毕",classMethod)));
- System.out.println(result);
- // 实际项目中可以根据业务做具体的返回值增强
- System.out.println("对返回参数进行业务上的增强:{}"+result + "增强版");
- }
- /**
- * 在上面定义的切面方法执行抛异常时,执行该方法
- * @param joinPoint jointPoint
- * @param ex ex
- */
- @AfterThrowing(pointcut = "pointCut()", throwing = "ex")
- public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
- Signature signature = joinPoint.getSignature();
- String method = signature.getName();
- // 处理异常的逻辑
- System.out.println((String)(String.format("执行方法{}出错,异常为:{}", method, ex)));
- }
-
-
-
- }
- package com.ljx.splearn.controller;
-
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.Mapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import java.sql.Time;
-
- /**
- * @author lijianxi
- * @date 2022年11月09日 10:55 上午
- */
- @RestController
- @RequestMapping("/demo")
- public class DemoController {
- @GetMapping("/hello")
- String sayHello(String name) throws InterruptedException {
- System.out.println("传递过来参数是"+name);
- Thread.sleep(2000);
- return "hello"+name;
- }
- }
发起请求,参数是lisi,中间对参数修改传递到controller层时是王五,最终结果被@Around修改
最终返回是zhangsan

日志打印
