Spring Boot 集成 AOP 教程
学习知识点
- 什么是切面编程,什么是切入点?
- 切入点能做什么?实际应用?
- AOP的各个通知的实际用途?
- 通过Spring Boot 集成 AOP 使用?
项目结构图
准备工作
- 常用IDE,这里是IDEA 2020.3
- JDK1.8
- Maven 3.5 +
AOP 介绍
通常我们的APP有接口控制层
、业务逻辑层
、数据交互层
,各自负责的模块不同,但是也有很多共通点,例如日志
与权限
日常项目使用:
- 日志:统一记录,不用麻烦重复的每个方法都记录一个操作人,代码冗余很多
- 权限:使用aop灵活的校验一些特殊的方法权限
- 性能:记录每个方法执行的时间,耗时久的发出短信/邮件通知等
什么是通知,连接点或切入点?
连接点
是程序的执行点,例如方法的执行或异常的处理,在Spring AOP中,连接点始终意味着方法的执行。切点
是与连接点匹配的表达式。通知
与切入点表达式关联,并在与该切入点匹配的连接点处运行
AOP通知的类型
SpringBoot AOP 示例
先定义一个切面
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Aspect
@Configuration
public class TestAspectJ {
// 定义拦截通知
}
在启动类开启 AOP
代理
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringbootAopApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAopApplication.class, args);
}
}
前置通知
// 前置通知
@Before("execution(* com.github.gleans.springbootaop.controller..*(..))")
public void before(JoinPoint joinPoint){
// 通知
log.info("前置通知测试");
log.info(" 当前节点:{}", joinPoint);
}
里面的表达式,..
表示当前包及子包,第一个*
表示返回值的类型任意
定义一个接口,来测试前置通知
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RequestMapping("test")
@RestController
public class TestController {
@GetMapping("/before")
public String before() {
log.info("请求 test/before 接口");
return "前置通知测试";
}
}
请求接口:http://127.0.0.1:8080/test/before
看日志
环绕通知
下面我们来实战,写一个计算方法耗时的AOP
通知
// 环绕通知
@Around("execution(* com.github.gleans.springbootaop.controller..*(..))")
public Object handlerControllerMethod(ProceedingJoinPoint joinPoint) {
long startTime = System.currentTimeMillis();
Object res = null;
try {
res = joinPoint.proceed();
} catch (Throwable throwable) {
log.error("出错了:{}", throwable.getLocalizedMessage());
}
log.info(joinPoint.getSignature() + "=耗时:=" + (System.currentTimeMillis() - startTime));
return res;
}
效果
正常返回通知
增强处理是在目标方法正常请求后被织入
//也可以定义切入点
@Pointcut(value = "execution(* com.github.gleans.springbootaop.controller..*(..))")
public void testAfterReturing(){};
// 正常返回通知
@AfterReturning(value = "testAfterReturing()", returning = "res")
public Object afterReturning(String res) {
// 通知
log.info("正常返回通知测试,返回值:{}", res);
return "222";
}
结果
异常返回通知
@AfterThrowing(value = "testAfterReturing()", throwing="ex")
public void AfterThrowing(JoinPoint jp, Throwable ex) {
log.info("=====异常返回通知====:{}", ex.getLocalizedMessage());
}
后置通知
// 后置通知
@After("execution(* com.github.gleans.springbootaop.controller..*(..))")
public void after(JoinPoint joinPoint) {
// 通知
log.info("后置通知测试,当前节点:{}", joinPoint);
}