1 理论基础
1.1 AOP是什么
-
面向切面编程(AOP,Aspect Oriented Programming)
-
可以通过预编译方式和运行时动态代理,实现在不修改源代码的情况下,给程序动态增强功能的的一种技术
1.2 AOP能做什么
- 统计接口访问次数
- 数据库事务处理
- 增强功能:在不改动源代码的基础上,为接口增加一些额外的功能
2 Demo代码
2.1 引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
2.2 API访问历史统计
/**
* API访问历史统计
*/
@Component
@Aspect
public class ApiVisitHistory {
private Logger log = LoggerFactory.getLogger(ApiVisitHistory.class);
private ThreadLocal<Long> startTime = new ThreadLocal<>();
/**
* 定义切面
* - 此处代表com.smile.demo.controller包下的所有接口都会被统计
*/
@Pointcut("execution(* com.smile.demo.controller..*.*(..))")
public void pointCut(){
}
/**
* 在接口原有的方法执行前,将会首先执行此处的代码
*/
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint) {
startTime.set(System.currentTimeMillis());
//获取传入目标方法的参数
Object[] args = joinPoint.getArgs();
log.info("类名:{}", joinPoint.getSignature().getDeclaringType().getSimpleName());
log.info("方法名:{}", joinPoint.getSignature().getName() );
// 计数
AtomicCounter.getInstance().increase();
}
/**
* 只有正常返回才会执行此方法
* 如果程序执行失败,则不执行此方法
*/
@AfterReturning(returning = "returnVal", pointcut = "pointCut()")
public void doAfterReturning(JoinPoint joinPoint, Object returnVal) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
log.info("URI:[{}], 耗费时间:[{}] ms, 访问次数:{}", request.getRequestURI(), System.currentTimeMillis() - startTime.get(), AtomicCounter.getInstance().getValue());
}
/**
* 当接口报错时执行此方法
*/
@AfterThrowing(pointcut = "pointCut()")
public void doAfterThrowing(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
log.info("接口访问失败,URI:[{}], 耗费时间:[{}] ms", request.getRequestURI(), System.currentTimeMillis() - startTime.get());
}
}
2.3 定义测试接口
@RestController
@RequestMapping("/demo/aop")
public class HelloController {
@GetMapping("hello")
public String hello() {
return "hello World!";
}
}
@RestController
@RequestMapping("/demo/aop")
public class ErrorController {
@GetMapping("error")
public String error() {
int i = 1 / 0;
return "测试报错的AOP方法";
}
}
2.4 单例计数器
/**
* 计数器,统计当前执行的任务数
*
*/
public class AtomicCounter {
private static final AtomicCounter atomicCounter = new AtomicCounter();
/**
* 单例,不允许外界主动实例化
*/
private AtomicCounter() {
}
public static AtomicCounter getInstance() {
return atomicCounter;
}
private static AtomicInteger counter = new AtomicInteger();
public int getValue() {
return counter.get();
}
public int increase() {
return counter.incrementAndGet();
}
public int decrease() {
return counter.decrementAndGet();
}
}