Compare schema-based XML AOP with @AspectJ annotation-based AOP. What are the semantic differences, limitations, and when would you deliberately choose XML?
answer
- Same runtime: proxies, method-execution join points, AspectJ PCL
- XML externalizes metadata; annotations co-locate
- XML = singleton aspects only; @AspectJ adds perthis/pertarget
- XML shines for un-annotatable/third-party + tx wiring
- self-invocation bypass affects both
basics
~20 sBoth are proxy-based Spring AOP using the same AspectJ pointcut language and produce identical proxies. XML keeps advice metadata outside the class (good for third-party beans or externalized config); annotations keep it cohesive. XML supports only singleton aspects; @AspectJ also supports perthis/pertarget.
solid answer
~40 sFunctionally the two are the same runtime: both build Spring AOP proxies, support only method-execution join points, share the AspectJ pointcut expression language, and can coexist in one context. The differences are ergonomic and a few semantic edges. XML (<aop:config>) puts all AOP metadata outside the advice class, so the advice POJO has no Spring dependency — ideal for beans you can't modify, for policy-driven externalized configuration, and for framework wiring like <tx:advice>+<aop:advisor>. @AspectJ keeps pointcuts and advice together, is refactor-friendly, and is the only style supporting non-singleton aspect instantiation (perthis, pertarget, percflow) and reusable named pointcut methods with inheritance. XML pointcuts can't be inherited or composed across files as cleanly, and use and/or/not instead of &&/||/!. I'd pick XML for externalized/transaction wiring or un-annotatable classes, annotations for everything I own.
code
java · 25 lines// SAME behavior, two styles.
// (A) @AspectJ style — metadata on the class:
@org.aspectj.lang.annotation.Aspect
public class AnnotatedTiming {
@org.aspectj.lang.annotation.Around("execution(* com.app.service..*(..))")
public Object time(org.aspectj.lang.ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed();
}
}
// Requires: <aop:aspectj-autoproxy/> (or @EnableAspectJAutoProxy)
// (B) Schema style — same POJO, metadata in XML, NO annotations:
public class PlainTiming {
public Object time(org.aspectj.lang.ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed();
}
}
/*
<aop:config>
<aop:aspect ref="plainTiming">
<aop:around pointcut="execution(* com.app.service..*(..))" method="time"/>
</aop:aspect>
</aop:config>
*/go deeper
Know both exist, do the same job, and differ mainly in metadata-in-XML vs metadata-in-annotations.
Cite shared pointcut language and the singleton-only limitation of XML; know the two can coexist.
Compare instantiation models, pointcut reuse/inheritance, arg-names binding, and the third-party/tx-wiring drivers for XML.
Frame it as a configuration-locality and capability-edge decision over one identical runtime; weigh self-invocation, ordering, proxy-type, and maintainability trade-offs at architecture level.
**Same engine, different front-end.** Schema-based and @AspectJ AOP are two *authoring styles* over the **same Spring AOP runtime**. Both are decomposed internally into `Advisor`s (advice + pointcut), both are applied by an auto-proxy `BeanPostProcessor`, both produce JDK-dynamic or CGLIB proxies, both support **only method-execution join points** on Spring-managed beans, and both are subject to the **self-invocation limitation** (a call through `this` bypasses the proxy). Neither does compile-time or load-time weaving by default — that's full AspectJ, a different thing. **What's shared:** - The **AspectJ pointcut expression language** (`execution`, `within`, `args`, `@annotation`, `bean`, …). - Advice kinds: before, after-returning, after-throwing, after (finally), around. - Auto-proxy machinery — enabling one style (`<aop:config>` vs `<aop:aspectj-autoproxy/>`) registers a compatible auto-proxy creator; the two **can coexist** and their advisors are merged and ordered together. **Where they differ:** 1. **Location of metadata.** XML externalizes it: the advice class is a bare POJO. @AspectJ co-locates pointcut + advice on the class via `@Aspect`, `@Before`, `@Around`, etc. Co-location aids readability and refactoring; externalization decouples wiring from code. 2. **Aspect instantiation model.** @AspectJ supports `perthis`, `pertarget`, and (with limits) `percflow` instantiation — a new aspect instance per matched object. **Schema-based supports only the singleton model** — one aspect instance for the whole container. This is the sharpest functional gap. 3. **Named pointcut reuse.** In @AspectJ a `@Pointcut`-annotated method can be referenced by name, inherited, and combined across aspect classes. XML `<aop:pointcut>` reuse is by `id`/`pointcut-ref` and is more limited in composition and inheritance across files. 4. **Operator syntax.** XML must use `and`/`or`/`not` (XML-safe) where annotations use `&&`/`||`/`!`. 5. **Parameter binding.** @AspectJ can often infer parameter names from annotated methods; XML relies on `arg-names` when bytecode lacks parameter names. 6. **Third-party / un-annotatable classes.** You can advise or wire a class you cannot annotate only via XML aspects/advisors (or `@Bean`-based programmatic config) — a real advantage of the schema style. 7. **Framework wiring idioms.** Declarative transactions and similar are classically expressed as `<tx:advice>` + `<aop:advisor>` in XML; the annotation equivalent is `@Transactional` + `@EnableTransactionManagement`. Both compile to the same interceptor. **When to deliberately choose XML:** - The advice/interceptor lives in a **library you can't annotate**. - Organizational policy keeps **cross-cutting wiring external** to business code, or aspects must be **toggled per environment** without recompiling. - **Transaction/interceptor wiring** where `<aop:advisor>` + a generated interceptor is the natural fit. - You want the *business* class utterly free of AOP concerns/imports. **When to prefer @AspectJ:** - You own the code and want **cohesive, refactor-safe** aspects. - You need **non-singleton aspect instantiation** or advanced pointcut inheritance/composition. - You favor annotation-driven, XML-free configuration. **Architectural cautions common to both:** - **Self-invocation** bypasses advice — a frequent production surprise; mitigate with `expose-proxy` + `AopContext.currentProxy()`, restructuring, or full AspectJ weaving. - **Ordering** across multiple aspects/advisors must be explicit (`order`), especially when transactional and non-transactional advice interleave. - **Proxy type** (`proxy-target-class`) affects whether final classes/methods can be advised (CGLIB can't proxy final). - Overusing AOP hurts traceability; reserve it for genuinely cross-cutting concerns. **Bottom line for a principal.** The choice is mostly about *where configuration should live* and a couple of capability edges (singleton-only aspects, third-party wiring), not about performance or power at the join-point level — those are identical because it's one runtime.
- Name one thing @AspectJ can do that schema-based AOP cannot.Non-singleton aspect instantiation — perthis/pertarget (and percflow) create a new aspect instance per matched object/flow. Schema-based aspects are singleton-only. @AspectJ also supports richer named-pointcut inheritance/composition.
- Do XML and annotation aspects proxy differently or perform differently?No. Both produce the same Spring AOP proxies (JDK or CGLIB) via the same auto-proxy creator, use the same pointcut language, and have identical join-point limitations and performance characteristics. They can even coexist in one context.
- You must advise a class from a third-party jar you cannot annotate. Which style?Schema-based XML (an <aop:aspect> or <aop:advisor> with a pointcut targeting that class), or equivalent programmatic @Configuration. Annotation style needs @Aspect on a class you control, so it doesn't fit an un-modifiable third-party type.
saying these in an interview costs you the question
- Claiming XML AOP is faster/slower or more powerful at the join-point level than @AspectJ (same runtime, same limits).
- Saying schema-based supports perthis/pertarget (it's singleton-only).
- Believing you must choose one style globally (they coexist), or that either does compile/load-time weaving by default (both are runtime proxies).