skip to content

@Timed & @Counted

@Timed and @Counted add metrics declaratively through aspects, with the usual proxy caveat that self-invoked methods are not instrumented. Interviewers use them to check whether you remember that AOP rule applies here too.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

You add @Timed to a service method but no metric shows up under /actuator/metrics. Why, and how do you fix it?

level: middleimportance: must knowfreq 48%

answer

  1. annotation = metadata only, aspect does the work
  2. need spring-boot-starter-aop (aspectjweaver)
  3. Boot 2.5+ MetricsAspectsAutoConfiguration auto-registers
  4. older Boot -> register TimedAspect/CountedAspect @Bean
  5. controller @Timed uses a different (web) path

basics

~20 s

The @Timed annotation does nothing by itself — a TimedAspect bean has to intercept the method. If Spring AOP or that aspect bean is missing, nothing is recorded. Add spring-boot-starter-aop and make sure TimedAspect (and CountedAspect) are registered.

solid answer

~40 s

@Timed/@Counted are inert metadata; the actual instrumentation is done by the `TimedAspect`/`CountedAspect` AOP aspects, which must exist as beans and require Spring AOP (AspectJ/aspectjweaver on the classpath). If neither is present, the annotation is silently ignored — no error, no metric. In Spring Boot 2.5+, adding `spring-boot-starter-aop` triggers `MetricsAspectsAutoConfiguration`, which registers both aspects automatically. On older Boot versions (or if you excluded AOP) you must register them yourself: `@Bean TimedAspect timedAspect(MeterRegistry r) { return new TimedAspect(r); }` and the same for `CountedAspect`. Also confirm the bean is Spring-managed and the call goes through the proxy — a self-invocation won't be intercepted. Finally, a Timer only appears after it has recorded at least once, so the method must have actually been called.

code

java · 15 lines
java
// Manual registration for Boot versions before 2.5,
// or when you want explicit control.
@Configuration
public class MicrometerAspectsConfig {

    @Bean
    public TimedAspect timedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);
    }

    @Bean
    public CountedAspect countedAspect(MeterRegistry registry) {
        return new CountedAspect(registry);
    }
}

go deeper

for a junior

Know the annotation alone isn't enough; something must process it.

for a middle

Name TimedAspect/CountedAspect, the AOP dependency, and Boot 2.5+ auto-registration; know how to register manually.

for a senior

Distinguish the web-metrics path from the aspect path and debug silent no-ops systematically.

for a principal

Decide project-wide whether to rely on auto-config vs explicit beans, and standardize so teams don't hit silent failures.

## The root cause: annotations don't self-execute `@Timed` and `@Counted` are just annotations. They carry no behavior. The behavior lives in two Spring AOP aspects: - `io.micrometer.core.aspect.TimedAspect` - `io.micrometer.core.aspect.CountedAspect` Each is an `@Aspect` with an `@Around` advice that matches annotated methods (and annotated classes), wraps the invocation in a `Timer`/`Counter`, and records the result. If these aspects are **not registered as beans**, or Spring AOP isn't active, the annotation is **silently ignored** — you get no exception and no metric. This silence is exactly why the symptom is confusing. ## Requirements to make them work 1. **Spring AOP must be on the classpath.** Add `spring-boot-starter-aop` (which pulls in `aspectjweaver`). Without it, `@Aspect` beans can't weave. 2. **The aspect beans must exist.** - **Spring Boot 2.5+:** `MetricsAspectsAutoConfiguration` (part of Actuator autoconfig) registers `TimedAspect` and `CountedAspect` **automatically** when AOP is present. So in a typical modern app, `starter-aop` + `actuator` is enough. - **Older Boot, or if you disabled it:** register manually. 3. **A `MeterRegistry` must be present** (Actuator provides one, e.g. the Prometheus registry). 4. **The target must be a Spring bean invoked through its proxy** — see the self-invocation gotcha. 5. **The method must actually run at least once.** Micrometer Timers are lazily materialized on first record; a never-called method shows no meter. ## Manual registration (older Boot / explicit control) ```java @Configuration public class MetricsAspectConfig { @Bean public TimedAspect timedAspect(MeterRegistry registry) { return new TimedAspect(registry); } @Bean public CountedAspect countedAspect(MeterRegistry registry) { return new CountedAspect(registry); } } ``` ## Distinguish from @Timed on Spring MVC controllers Note that `@Timed` on **Spring MVC/WebFlux controller** handler methods is honored by a **different** mechanism — the web metrics instrumentation (`http.server.requests` via a filter/observation), not `TimedAspect`. That path works even without the AOP aspect. The aspect is what handles `@Timed` on **arbitrary beans** (services, repositories). Candidates often conflate the two: controller `@Timed` working doesn't prove the aspect is wired. ## Debug checklist - Is `spring-boot-starter-aop` a dependency? - Is there a `TimedAspect`/`CountedAspect` bean? (`/actuator/beans` or context introspection) - Is `spring.aop.proxy-target-class` / AOP enabled (auto by default)? - Was the method called via another bean (proxy), not `this.method()`? - Did the method execute at least once, and are you looking at the right metric name (default `method.timed`)?

  • In a fresh Spring Boot 3 app with actuator and spring-boot-starter-aop, do you still need to register TimedAspect manually?
    No. MetricsAspectsAutoConfiguration registers TimedAspect and CountedAspect automatically once AOP is on the classpath. Manual registration is only needed on older Boot or if you exclude/override that autoconfiguration.
  • @Timed on your @RestController method works, but on a @Service it doesn't. What's different?
    Controller handler @Timed is honored by the web metrics instrumentation (http.server.requests), independent of the AOP aspect. Service @Timed needs TimedAspect + AOP. So the service path is missing the aspect/AOP, while the controller path uses a separate mechanism.

saying these in an interview costs you the question

  • Claiming @Timed always works with zero extra dependencies (ignores the AOP/aspect requirement).
  • Assuming controller @Timed and service @Timed use the same machinery.
  • Expecting an exception when the aspect is missing — it fails silently.

context

open as a page

Why does @Timed silently record nothing when an annotated method is called from another method in the same bean?

level: seniorimportance: must knowfreq 38%

basics

~10 s

@Timed relies on a Spring AOP proxy that wraps the bean. Self-invocation (this.method()) calls the method directly on the target object, bypassing the proxy, so the TimedAspect never runs and no metric is recorded.

open as a page

What do the @Timed and @Counted annotations do in a Micrometer/Spring Boot application?

level: juniorimportance: should knowfreq 40%

basics

~20 s

@Timed records how long a method takes (a Timer metric); @Counted counts how many times it runs (a Counter). You put them on a method and Micrometer publishes the metric automatically, without writing timing code by hand.

open as a page

Explain the longTask, percentiles, and histogram attributes of @Timed and when you'd use each.

level: seniorimportance: should knowfreq 32%

basics

~10 s

longTask=true measures long-running, still-in-progress calls with a LongTaskTimer (active count + in-flight duration). percentiles computes client-side percentile values (e.g. p95, p99). histogram=true publishes bucket counts so backends like Prometheus can aggregate percentiles across instances.

open as a page

As a principal engineer, when would you prefer declarative @Timed/@Counted over programmatic Micrometer instrumentation, and what are the risks at scale?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Use @Timed/@Counted for quick, low-boilerplate, method-level metrics on Spring beans. Use programmatic Micrometer when you need dynamic tags, to time part of a method, or to avoid proxy limits. At scale watch tag cardinality, the AOP proxy constraints, and consistent naming.

open as a page