Spring Boot 2.1显式注册TimedAspect后@Timed注解仍不生效
你碰到的这个问题其实是Spring AOP的常见“坑”——当同一个类内部的方法互相调用时,不会经过Spring生成的代理对象,所以TimedAspect的切面逻辑根本没机会执行。
具体到你的代码里:ExampleUtil的sayhellowithtimed方法调用了自己的sayhello方法(也就是那个标注了@Timed的实现),这是直接通过this引用调用的目标对象方法,完全绕开了代理,自然不会触发指标统计。
下面给你几个不同的解决思路,你可以根据自己的代码结构选最顺手的:
1. 暴露代理对象,通过AopContext调用
这个方法改动最小,只需要两步:
第一步:开启代理暴露
在你的启动类ExampleStarter上,给@EnableAspectJAutoProxy加上exposeProxy = true:
@EnableAspectJAutoProxy(exposeProxy = true) // 关键:让Spring暴露代理对象 @ComponentScan @Configuration @SpringBootApplication public class ExampleStarter{ // 原有代码保持不变 }
第二步:修改内部调用逻辑
在AbstractUtil的sayhellowithtimed方法里,用AopContext获取当前代理对象,再调用sayhello:
import org.springframework.aop.framework.AopContext; public abstract class AbstractUtil { public abstract void sayhello(); public void sayhellowithtimed(String passedVar) { System.out.println("Passed var =>"+passedVar); System.out.println("Calling abstract sayhello...."); // 换成通过代理对象调用,这样就能触发TimedAspect了 ((AbstractUtil) AopContext.currentProxy()).sayhello(); } }
2. 自注入代理对象
在ExampleUtil里注入自身的代理对象,然后用这个代理来调用sayhello:
@Component @Scope("prototype") public class ExampleUtil extends AbstractUtil { public static final String HELLO = "HELLO"; @Autowired private ExampleUtil selfProxy; // 注入自己的代理实例 // 原有方法不变... @Override public void sayhellowithtimed(String passedVar) { System.out.println("Passed var =>"+passedVar); System.out.println("Calling abstract sayhello...."); selfProxy.sayhello(); // 用代理对象调用,触发切面 } }
不用担心原型作用域的问题,Spring会正确给每个原型实例注入对应的代理对象。
3. 重构代码,把调用移到外部
如果不想碰AOP相关的配置,可以把sayhellowithtimed的逻辑抽出来放到另一个Spring组件里,比如在StartDirWatcher里直接调用util.sayhello(),或者新建一个专门的服务类来处理这个调用逻辑——只要是外部组件调用ExampleUtil的方法,就会经过代理,自然能触发@Timed统计。
4. 改用AspectJ织入(零业务代码修改)
如果完全不想改业务代码,可以用AspectJ的加载时或编译时织入,它能拦截所有方法调用(包括内部调用)。
第一步:添加AspectJ依赖
在pom.xml里加上:
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency>
第二步:开启加载时织入
把启动类的@EnableAspectJAutoProxy换成@EnableLoadTimeWeaving,或者配置Maven编译插件实现编译时织入。
改完之后,触发一次目录变更事件,然后访问/prometheus或/metrics端点,就能看到say_hello_example相关的指标了(比如say_hello_example_seconds_count、say_hello_example_seconds_sum这些)。
内容的提问来源于stack exchange,提问作者Rahul

