What does the @Observed annotation do in Spring, and what bean must you register for it to work?
answer
- @Observed = declarative Observation around a method
- must register ObservedAspect bean (not auto-configured)
- Spring AOP proxy — no self-invocation
- name / contextualName / lowCardinalityKeyValues
- 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 linesimport 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
Know @Observed wraps a method in an Observation and that ObservedAspect must be a bean.
Explain the annotation attributes and the Spring AOP proxy/self-invocation limitation.
Contrast @Observed vs manual Observation vs @Timed; know how to supply a custom convention or skip-predicate to ObservedAspect.
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