skip to content

Break down the full grammar of an execution() pointcut. Which parts are optional, and which are required?

level: middleimportance: must knowfreq 48%

answer

  1. mod? ret decl? name(params) throws?
  2. required: ret-type, name, params
  3. optional: modifiers, decl-type, throws
  4. () vs (*) vs (..)
  5. params fully-qualified except java.lang

basics

~10 s

Grammar: execution(modifiers? ret-type decl-type? name(params) throws?). Required are the return-type pattern, the method-name pattern, and the parameter pattern. Optional are modifiers, the declaring-type prefix, and the throws clause.

solid answer

~40 s

The AspectJ execution grammar is: execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?). Only three parts are mandatory: the return-type pattern (e.g. * or void or String), the name-pattern with its parentheses (the method name and parameter list), and the param-pattern inside them. The optional parts are the modifiers (public, protected — private isn't really advisable via proxies), the declaring-type prefix (fully-qualified type before the method name, joined by a dot), and the throws-pattern (throws SomeException). So execution(* find*(..)) is legal — no modifiers, no type, no throws — and matches any method named find… on any advised bean. Adding a declaring type: execution(* com.app.OrderService.save(..)) pins it to that class. You compose these with &&, ||, ! to build precise selectors.

code

java · 16 lines
java
@Aspect
@Component
public class GrammarExamplesAspect {

    // Minimal legal form: return-type, name, params only
    @Pointcut("execution(* *(..))")
    public void anyMethod() {}

    // Modifiers + declaring-type + one-arg param pattern
    @Pointcut("execution(public * com.app..*Service.set*(*))")
    public void publicSingleArgSetters() {}

    // throws-pattern: only methods declaring IOException
    @Pointcut("execution(* com.app..*(..) throws java.io.IOException)")
    public void ioThrowers() {}
}

go deeper

for a junior

May only know the wildcard basics; not expected to recite optional vs required.

for a middle

Should recite the grammar and which parts are optional; know param-pattern variants.

for a senior

Should explain fully-qualified param types and the type-vs-name parse rule.

for a principal

Should tie modifier patterns back to proxy reachability (why private never matches).

## The full pattern ``` execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) ``` The `?` marks **optional** segments. ### Required parts 1. **ret-type-pattern** — the return type. Cannot be omitted. Use `*` for any type, or a concrete type like `void`, `String`, `java.util.List`. 2. **name-pattern** — the method name, which may contain `*` wildcards (`find*`, `*ById`, `*`). 3. **param-pattern** — inside the parentheses. `()` = no args; `(..)` = any args; `(*)` = exactly one arg of any type; `(String, ..)` = a String first then anything; `(*, int)` = two params, any-then-int. ### Optional parts 1. **modifiers-pattern** — `public`, `protected`. If omitted, any modifier matches. (In Spring AOP only public — and with CGLIB, protected/package — methods are actually reachable through the proxy, so specifying `private` never matches an intercepted call.) 2. **declaring-type-pattern** — a fully-qualified type placed *before* the method name and joined to it by a dot: `com.app.OrderService.save`. If omitted, methods on any type match. Supports `*` and `..` wildcards (`com.app..*Service`). 3. **throws-pattern** — restricts by declared checked exceptions: `execution(* *(..) throws java.io.IOException)` matches only methods declaring IOException. ## Worked examples | Pointcut | Matches | |----------|---------| | `execution(* *(..))` | every method (min-legal form) | | `execution(String get*())` | no-arg methods named get… returning String | | `execution(* com.app.OrderService.*(..))` | any method on OrderService | | `execution(public * com.app..*Service.*(..))` | public methods of any *Service type under com.app | | `execution(* set*(*))` | any setter with exactly one argument | | `execution(* *(..) throws javax.security.auth.login.LoginException)` | methods declaring that checked exception | ## Return type + declaring type ambiguity Because the declaring type is optional and joined by a dot, the parser distinguishes it from the method name by the **last dot before the parenthesis**. `execution(* com.app.OrderService.save(..))` → type `com.app.OrderService`, method `save`. `execution(* save(..))` → no type, method `save`. ## Parameter pattern deep-dive - `()` — zero parameters exactly - `(*)` — exactly one, any type - `(..)` — zero or more, any types - `(String)` — exactly one String - `(String, ..)` — String followed by anything - `(.., String)` — anything ending with a String Parameter types must be **fully qualified** unless they are in `java.lang` (e.g. `String` works, but `List` must be `java.util.List`). ## When to use each optional part - Add **modifiers** to restrict visibility. - Add a **declaring type** to scope to a class/package (most common for real aspects). - Add **throws** rarely — mostly for cross-cutting exception-translation aspects.

  • What is the minimal legal execution() expression?
    execution(* *(..)) — a return-type pattern (*), a method-name pattern (*), and a parameter pattern ((..)). Everything else (modifiers, declaring type, throws) is optional.
  • How do you match exactly one String argument versus a String followed by anything?
    (String) matches exactly one String parameter; (String, ..) matches a String first followed by zero or more other parameters.

saying these in an interview costs you the question

  • Saying modifiers or declaring-type are required
  • Forgetting the return-type pattern is mandatory
  • Writing List instead of java.util.List in a param pattern
  • Confusing (*) (exactly one) with (..) (any number)

context