skip to content

How do you control ordering between multiple aspects, and how does that differ from ordering within one aspect? When would you split advice across aspects for this reason?

level: principalimportance: should knowfreq 30%

answer

  1. @Order / Ordered on the aspect CLASS
  2. lower value = higher precedence = outermost
  3. within-aspect = fixed by type, same-type undefined
  4. split concerns into ordered aspects
  5. @Transactional = LOWEST_PRECEDENCE by default

basics

~20 s

Order whole aspects with @Order or by implementing Ordered — lower value = higher precedence, runs first on entry. This is the ONLY way to get deterministic ordering, because two same-type advices inside one aspect are unordered. So split advice that must be ordered into separate, @Order-ed aspects.

solid answer

~50 s

Ordering **between aspects** is controlled with `@org.springframework.core.annotation.Order` on the aspect class, or by implementing `org.springframework.core.Ordered`. **Lower order value = higher precedence**, and the highest-precedence aspect's 'before' advice runs first while its 'after' advice runs last (it wraps the others). AspectJ's `@DeclarePrecedence` is an alternative. This is fundamentally different from **within-aspect** ordering, which is fixed by advice type and *undefined* for two same-type advices (Spring can't read source order from javac bytecode, and `@Order` on a method is ignored). So the standard design pattern when you need a guaranteed sequence — e.g. security check must run before transaction management must run before audit logging — is to put each concern in its **own aspect** and assign explicit `@Order` values. `@Transactional` internally sits at `Ordered.LOWEST_PRECEDENCE`, so custom aspects often need a lower value to run outside the transaction.

code

java · 21 lines
java
@Aspect
@Component
@Order(1)                     // highest precedence: outermost wrapper
public class SecurityAspect {
    @Before("execution(* svc..*(..))")
    public void checkAccess() { /* runs FIRST on entry */ }
}

@Aspect
@Component
@Order(2)                     // runs inside SecurityAspect
public class AuditAspect {
    @After("execution(* svc..*(..))")
    public void audit() { /* runs later on exit */ }
}

// Rationale: these two concerns needed a guaranteed order.
// Two advice methods in ONE aspect could NOT be ordered
// deterministically, so each concern is its own @Order-ed aspect.
// To wrap Spring's @Transactional advice, use an order value
// lower than Ordered.LOWEST_PRECEDENCE (its default).

go deeper

for a junior

Know @Order exists to order aspects; details not expected.

for a middle

Know @Order/Ordered on the class and that lower = higher precedence.

for a senior

Explain outer-wraps-inner semantics and that within-aspect same-type order is undefined, motivating splitting.

for a principal

Design ordered aspect chains, reason about @Transactional's LOWEST_PRECEDENCE interplay, @DeclarePrecedence, and cohesion trade-offs of one-concern-per-aspect.

**Two independent ordering layers.** 1. **Across aspects** — configurable and deterministic. 2. **Within one aspect** — fixed by advice type, and *undefined* between two advices of the same type. **Across-aspect ordering mechanism.** When advice from *different* aspects applies to the same join point, Spring orders the aspects by precedence: - Annotate the aspect class with `@Order(n)`, or - Implement `org.springframework.core.Ordered` and return a value from `getOrder()`. **Semantics of the value:** **lower value = higher precedence.** The highest-precedence aspect is the *outermost* — its `@Before`/around-before runs **first** on the way in, and its after/around-after runs **last** on the way out. Think of aspects as nested wrappers: lowest order number on the outside. `Ordered.HIGHEST_PRECEDENCE` = `Integer.MIN_VALUE`, `Ordered.LOWEST_PRECEDENCE` = `Integer.MAX_VALUE`. **AspectJ alternative.** `@DeclarePrecedence("AspectA, AspectB, ...")` declares an explicit precedence chain and can be used instead of/along with `@Order`. **Contrast with within-aspect ordering.** Inside one aspect, ordering is by advice **type** (`@Around` > `@Before` > `@After` > `@AfterReturning` > `@AfterThrowing`, since 5.2.7), and two advices of the **same type** are **undefined** because declaration order isn't recoverable from javac-compiled bytecode. Critically, **`@Order` on an advice method does nothing** — it only orders aspects. So there is no in-aspect knob for same-type sequencing. **The design consequence — split to order.** Because you cannot deterministically order two same-type advices within one aspect, the idiomatic solution when order matters is to **refactor each concern into its own `@Aspect` and assign `@Order` values**. Classic ordered chain: security/authorization (outermost) -> transaction management -> caching -> audit/metrics (innermost), each its own aspect with increasing order numbers. This also improves cohesion — one aspect = one concern. **Interaction with framework aspects.** Spring's `@Transactional` support is itself an ordered aspect at `Ordered.LOWEST_PRECEDENCE` by default (configurable via `@EnableTransactionManagement(order=...)`). If your custom aspect must run **outside** the transaction (e.g. open/close a resource around the tx, or run before the tx begins), give it a **lower order value** so it has higher precedence and wraps the transactional advice. Getting this wrong is a common production bug: an aspect meant to run before the transaction ends up nested inside it. **Edge cases / gotchas.** - Aspects with **no** `@Order`/`Ordered` have effectively undefined relative order — don't rely on registration/scan order. - `@Order` value comparison is a total order; ties fall back to undefined ordering. - Ordering applies at a **join point** — two aspects that don't both match a given join point simply don't interact there. - Mixing `@DeclarePrecedence` and `@Order` should be done carefully to avoid contradictory declarations. **Interview framing.** A principal-level answer connects the *mechanism* (@Order/Ordered, lower=higher, outer wraps) to the *design decision* (split concerns into separately-ordered aspects precisely because intra-aspect same-type ordering is unavailable) and to *real framework interplay* (@Transactional's LOWEST_PRECEDENCE default).

  • An aspect must run before the database transaction opens. What order value does it need relative to @Transactional?
    Spring's transaction advice defaults to Ordered.LOWEST_PRECEDENCE. Your aspect needs a lower order value (higher precedence) so it becomes the outer wrapper and runs before the transaction begins / after it commits.
  • Does lower @Order mean it runs first or last?
    Lower value = higher precedence = outermost. Its before/entry advice runs first; its after/exit advice runs last. It wraps the lower-precedence aspects.
  • Why is splitting into separate aspects the recommended way to order same-type advice?
    Because within one aspect two same-type advices have undefined order and @Order on a method is ignored. Only aspect-level @Order/Ordered gives determinism, so each orderable concern must be its own aspect.

saying these in an interview costs you the question

  • Thinking @Order on an advice method orders advice within an aspect
  • Believing higher @Order value means higher precedence
  • Assuming aspects without @Order have a stable, predictable order
  • Not knowing @Transactional advice defaults to LOWEST_PRECEDENCE, leading to wrong wrapping

context