skip to content

What does the @Observed annotation do in Spring, and what bean must you register for it to work?

level: juniorimportance: must knowfreq 45%

answer

  1. @Observed = declarative Observation around a method
  2. must register ObservedAspect bean (not auto-configured)
  3. Spring AOP proxy — no self-invocation
  4. name / contextualName / lowCardinalityKeyValues
  5. one Observation -> metric + span

basics

~20 s

@Observed marks a method so Micrometer creates an Observation around each call (producing metrics and/or a trace span). For it to work you must register an ObservedAspect bean, which uses Spring AOP to wrap the annotated method.

solid answer

~40 s

@Observed (from io.micrometer.observation.annotation.Observed) is a declarative alternative to hand-writing Observation code. Placed on a method or class, it makes Micrometer start an Observation before the method runs and stop it after, emitting a timer metric and, if tracing is configured, a span. You can set name, contextualName, and lowCardinalityKeyValues on the annotation. It does NOT work by itself: Spring Boot does not auto-configure the aspect, so you must declare a bean of type io.micrometer.observation.aop.ObservedAspect, giving it the ObservationRegistry. ObservedAspect is a Spring AOP aspect, so the usual proxy rules apply — the call must go through the Spring-managed proxy (public method, external caller), and self-invocation is not intercepted.

code

java · 25 lines
java
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.annotation.Observed;
import io.micrometer.observation.aop.ObservedAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;

@Configuration
class ObservationConfig {
    // REQUIRED for @Observed to do anything — Boot does not add this for you.
    @Bean
    ObservedAspect observedAspect(ObservationRegistry registry) {
        return new ObservedAspect(registry);
    }
}

@Service
class UserService {
    @Observed(name = "user.lookup",
              contextualName = "lookup-user",
              lowCardinalityKeyValues = {"module", "user"})
    public User findUser(String id) {
        return /* ... */ null;
    }
}

go deeper

for a junior

Know @Observed wraps a method in an Observation and that ObservedAspect must be a bean.

for a middle

Explain the annotation attributes and the Spring AOP proxy/self-invocation limitation.

for a senior

Contrast @Observed vs manual Observation vs @Timed; know how to supply a custom convention or skip-predicate to ObservedAspect.

for a principal

Decide where annotation-driven observation belongs vs framework instrumentation; govern naming/cardinality conventions across teams.

## What @Observed is `@Observed` is an annotation in `io.micrometer.observation.annotation.Observed`. An **Observation** is Micrometer's unified concept for "something worth measuring": recording it once can feed both **metrics** (a timer) and **distributed tracing** (a span), depending on which `ObservationHandler`s are registered. `@Observed` lets you get an Observation around a method **declaratively** instead of writing `Observation.createNotStarted(...).observe(() -> ...)` by hand. Example: ```java @Observed(name = "user.lookup", contextualName = "lookup-user", lowCardinalityKeyValues = {"module", "user"}) public User findUser(String id) { ... } ``` - **name** — the metric/observation name (becomes the timer name, e.g. `user.lookup`). If omitted, ObservedAspect uses a default (`method.observed`). - **contextualName** — a more human-friendly name used for the span. - **lowCardinalityKeyValues** — fixed tags added to every recording (see the cardinality question for what "low cardinality" means). ## The required bean: ObservedAspect `@Observed` does nothing on its own. It is activated by `io.micrometer.observation.aop.ObservedAspect`, a **Spring AOP aspect** that intercepts calls to annotated methods and wraps them in an Observation. **Spring Boot does not register this aspect automatically**, so you must declare it yourself: ```java @Configuration class ObservationConfig { @Bean ObservedAspect observedAspect(ObservationRegistry registry) { return new ObservedAspect(registry); } } ``` Because it is AspectJ-style Spring AOP, you also need AOP on the classpath (`spring-boot-starter-aop`, i.e. `@EnableAspectJAutoProxy`, which Boot enables when AspectJ is present). ## What it does at runtime For each intercepted call the aspect: creates an Observation (using an `ObservedAspect.ObservedAspectObservationConvention` unless you supply one), starts it, opens a scope, `proceed()`s the method, records any thrown exception via `observation.error(...)`, then stops the observation in a finally block. Downstream, the registered handlers turn that into a timer sample and/or a span. ## Proxy gotchas (the classic trap) Because interception is via Spring AOP proxies: - **Self-invocation is not observed.** If a bean calls its own `@Observed` method (`this.findUser(...)`), the call bypasses the proxy and no Observation is created. - The method generally must be **public** and invoked from **outside** the bean. - Works on interface-proxied or CGLIB-proxied beans like any other Spring AOP advice. ## Customizing `ObservedAspect` can be given: a custom default `ObservationConvention`, and a `Predicate<ProceedingJoinPoint>` to **skip** certain join points (e.g., don't observe some methods). Constructor overloads accept these. ## When to use Use `@Observed` for coarse-grained, business-meaningful methods (service entry points, expensive operations) where you want a metric+span for free. For hot inner loops or very fine control, prefer manual `Observation` code or existing framework instrumentation (web, JDBC) rather than annotating everything.

  • Why does calling an @Observed method from within the same bean produce no observation?
    ObservedAspect works through the Spring AOP proxy. Self-invocation (this.method()) calls the raw target directly, bypassing the proxy, so the advice never runs. You must call through the injected proxy or refactor into another bean.
  • Does Spring Boot auto-configure ObservedAspect?
    No. Boot auto-configures the ObservationRegistry and the metric/tracing handlers, but you must declare the ObservedAspect bean yourself for @Observed to be honored.

saying these in an interview costs you the question

  • Believing @Observed works with no extra beans (forgetting ObservedAspect)
  • Thinking self-invocation is intercepted
  • Confusing @Observed with @Timed (the older Micrometer annotation) — @Observed feeds both metrics and tracing

context