我正在参加「掘金·启航计划」
前言
实习的时候用过AOP做过一个操作员记录日志的功能,可是只是能完成这个功能,并没有深入理解其中的各个注解,决定写下来,加深自己的印象。
AOP作用
Spring AOP又叫做面向切面编程,即通过运用切面向业务代码层插入某些功能(日志、权限校验等),等于说把一个公共方法;以不那么耦合的方式,插入到我们想要的功能里面。
AOP主要关键字
Aspect(切面)
Aspect主要作用是将需要进行横切的业务进行集中处理,例如有一个订单服务,我将在切面类里面定义订单方法前后需要做一些什么操作。
JoinPoint(连接点)
在程序运行中切入切面的一个点。ProceedingJoinPoint继承JoinPoint,目的是增强JoinPoint的行为。
Pointcut(切入点)
匹配连接点的断言,通过一个表达式切入到特定包/类/方法中,表名需要在哪个方法上进行操作。
Advice(通知)
在切面某个特定的连接点上做的操作,包括@Before(前置通知)、@After(后置通知)、@Around(环绕通知)等不同类型。
- 前置通知(Before advice) :在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
- 返回通知(After returning advice) :在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
- 异常通知(After throwing advice) :在方法抛出异常退出时执行的通知。
- 后置通知(After (finally) advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
- 环绕通知(Around Advice) :包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
使用实例
引入Maven 依赖
<!--spring aop支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<!--aspectj支持-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.5</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
定义一个下单接口
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
RestTemplate restTemplate;
@Autowired
private StockClients stockClients;
@RequestMapping("/add")
public String add(){
System.out.println("下单成功!");
String msg = stockClients.findById(1);
return "Hello World" + msg;
}
}
声明一个切面类、通知类型
/**
声明一个切面
**/
@Aspect
@Component
public class OrderAspect {
/**
定义一个切点
**/
@Pointcut("execution(* com.little.order.controller.OrderController.add(..))")
public void order(){
System.out.println("test");
}
/**
定义一个前置通知
**/
@Before("order()")
public void checkStock(){
System.out.println("【前置通知】:新增订单前查询库存.......");
}
/**
定义一个后置通知 通知方法会在返回或者抛出异常后调用
**/
@After("order()")
public void sendMsg(){
System.out.println("【后置通知】:新增订单后发送短信.......");
}
/**
定义一个返回通知 通知方法会在目标方法返回后调用
**/
@AfterReturning("order()")
public void endAdd(){
System.out.println("【返回通知】:下单环节结束.......");
}
}
结果
简单理解的话可以这样认为 环绕通知 = 前置通知+后置通知
环绕通知
环绕通知连接点的参数必须是ProceedingJopinPoint,并且抛出异常。
/**
声明一个切面
**/
@Aspect
@Component
public class AroundOrderAspect {
/**
定义一个切点
**/
@Pointcut("execution(* com.little.order.controller.OrderController.reduce(..))")
public void reduce(){
System.out.println("test");
}
/**
定义一个环绕通知 调用目标方法前后,自定义自己的逻辑
**/
@Around("reduce()")
public Object reductStock(ProceedingJoinPoint point) throws Throwable {
//等于前置通知
System.out.println("扣除库存前做的事情.....");
//执行方法
Object proceed = point.proceed();
//等于后置通知
System.out.println("扣除成功.......");
return proceed;
}
}
结果