Quarkus:如何为Timed注解的double[]类型percentiles参数配置YAML值
解决Quarkus中Timed注解percentiles参数从YAML配置读取的问题
因为@Timed注解的percentiles是double[]类型,直接用${}表达式赋值不被原生支持,这里提供两种可行方案:
方案一:使用编程式Timer替代注解
放弃注解方式,通过代码从配置读取百分位数,手动创建并使用Timer:
- 在
application.yaml中配置百分位数:
percentile: default: setting: 0.90,0.95
- 创建配置类绑定该参数:
import io.smallrye.config.ConfigMapping; import java.util.List; @ConfigMapping(prefix = "percentile.default") public interface PercentileConfig { List<Double> setting(); }
- 在业务类中注入配置,编程式创建Timer:
import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.eclipse.microprofile.metrics.MetricRegistry; import org.eclipse.microprofile.metrics.Timer; import org.eclipse.microprofile.metrics.annotation.RegistryType; import java.util.concurrent.TimeUnit; @ApplicationScoped public class YourBusinessService { @Inject PercentileConfig percentileConfig; @Inject @RegistryType(type = MetricRegistry.Type.APPLICATION) MetricRegistry registry; public void targetMethod() { // 将配置的List转为double数组 double[] percentiles = percentileConfig.setting() .stream() .mapToDouble(Double::doubleValue) .toArray(); // 创建带指定百分位数的Timer Timer timer = registry.timer("some_metric", Timer.builder() .percentiles(percentiles) .build()); // 用Timer包裹业务逻辑 try (Timer.Context ignored = timer.time()) { // 你的业务代码逻辑 } } }
方案二:自定义注解+拦截器实现动态配置
如果想保留注解风格,可以自定义注解,结合拦截器读取配置并处理Timer逻辑:
- 自定义注解:
import jakarta.interceptor.InterceptorBinding; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @InterceptorBinding @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ConfigurableTimed { String value(); // 指标名称 String percentilesKey(); // 配置文件中的键名 }
- 创建拦截器实现:
import jakarta.annotation.Priority; import jakarta.inject.Inject; import jakarta.interceptor.AroundInvoke; import jakarta.interceptor.Interceptor; import jakarta.interceptor.InvocationContext; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.metrics.MetricRegistry; import org.eclipse.microprofile.metrics.Timer; import org.eclipse.microprofile.metrics.annotation.RegistryType; import java.util.Arrays; import java.util.concurrent.TimeUnit; @Interceptor @ConfigurableTimed(value = "", percentilesKey = "") @Priority(Interceptor.Priority.APPLICATION) public class ConfigurableTimedInterceptor { @Inject Config config; @Inject @RegistryType(type = MetricRegistry.Type.APPLICATION) MetricRegistry registry; @AroundInvoke public Object invoke(InvocationContext ctx) throws Exception { ConfigurableTimed annotation = ctx.getMethod().getAnnotation(ConfigurableTimed.class); String metricName = annotation.value(); String configKey = annotation.percentilesKey(); // 读取配置并转为double数组 String percentilesStr = config.getValue(configKey, String.class); double[] percentiles = Arrays.stream(percentilesStr.split(",")) .map(String::trim) .mapToDouble(Double::parseDouble) .toArray(); // 创建Timer并执行业务逻辑 Timer timer = registry.timer(metricName, Timer.builder() .percentiles(percentiles) .build()); try (Timer.Context ignored = timer.time()) { return ctx.proceed(); } } }
- 在业务方法上使用自定义注解:
@ApplicationScoped public class YourBusinessService { @ConfigurableTimed(value = "some_metric", percentilesKey = "percentile.default.setting") public void targetMethod() { // 你的业务代码逻辑 } }
- 保持
application.yaml中的配置不变:
percentile: default: setting: 0.90,0.95
补充说明
- 方案一更直接,适合不需要大量复用注解的场景;方案二更贴近原生注解体验,适合多个方法需要统一配置逻辑的场景。
- 确保Quarkus依赖中包含
quarkus-smallrye-metrics扩展,否则相关Metrics API无法使用。
内容的提问来源于stack exchange,提问作者simonC
相关产品推荐
相关产品推荐

