SpringBoot AOP实现接口次数访问统计

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();
	}
	
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值