如何在Spring中创建自定义调用计数器注解?
实现方案
完全可以实现,核心基于Spring AOP(面向切面编程) 能力,不用修改业务方法代码,只要给目标方法加自定义注解即可自动触发计数逻辑,具体步骤如下:
1. 定义自定义注解@InvocationCounter
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) // 限定注解只能作用在方法上 @Retention(RetentionPolicy.RUNTIME) // 运行时生效 public @interface InvocationCounter { String path(); // 自定义统计路径参数 }
2. 引入AOP依赖
如果是Spring Boot项目,直接在pom.xml引入对应starter即可:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
普通Spring项目引入aspectjweaver等相关aspectj依赖即可。
3. 编写计数切面逻辑
切面用于拦截所有加了@InvocationCounter注解的方法,自动触发计数服务:
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Aspect @Component public class InvocationCounterAspect { @Autowired private InvocationCounterService counterService; // 切点规则:匹配所有带@InvocationCounter注解的方法 @Pointcut("@annotation(com.yourpackage.InvocationCounter)") public void counterPointcut() {} // 方法执行前触发计数逻辑 @Before("counterPointcut()") public void doCount(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); InvocationCounter annotation = signature.getMethod().getAnnotation(InvocationCounter.class); // 拿到注解上配置的path参数传入计数服务 counterService.count(annotation.path()); } }
注意把代码中的com.yourpackage替换成你实际存放@InvocationCounter注解的包路径。
4. 修正计数服务的线程安全问题
你提供的InvocationCounterService存在线程安全风险,优化为如下实现:
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @Service public class InvocationCounterService { // 用ConcurrentHashMap保证多线程下的操作安全 private final Map<String, AtomicInteger> counter = new ConcurrentHashMap<>(); public void count(String path) { // computeIfAbsent是原子操作,避免多线程场景下重复初始化计数对象 counter.computeIfAbsent(path, k -> new AtomicInteger()).incrementAndGet(); } @Scheduled(fixedRate = 60000) public void persist() { // 可选逻辑:定时持久化或者处理统计数据 } }
5. 开启功能支持
- Spring Boot项目默认已经开启AOP自动配置,只要在启动类加
@EnableScheduling注解开启定时任务即可 - 普通Spring项目需要额外在配置类加
@EnableAspectJAutoProxy注解开启AOP支持
最终效果
你只要在目标Controller方法上加@InvocationCounter(path = "/test1")注解,每次接口被调用时都会自动触发计数逻辑,不需要修改业务方法内部的任何代码。
注意事项
如果是类内部调用本类的加注解方法,会因为绕过了代理对象导致AOP失效。你当前场景是Controller对外接口,都是外部请求触发,不会有这个问题。如果需要支持内部调用,可以用ApplicationContext手动获取代理对象调用,或者改用字节码增强的实现方式。
内容的提问来源于stack exchange,提问作者membersound
相关产品推荐
相关产品推荐

