123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- [[pointcuts]]
- = Debugging Pointcuts
-
- [[pointcuts-introduction]]
- == Introduction
-
- This section describes how to write and debug pointcuts using the usual
- approach of iteration and decomposition. New users are often stumped
- when their advice does not match. That means the pointcut doesn't match;
- they rewrite the pointcut and it still doesn't match, with no new
- information. This can be frustrating if each iteration involves
- building, deploying, and testing a complex application. Learning to
- break it down, particularly into parts that can be checked at
- compile-time, can save a lot of time.
-
- [[pointcuts-debugging]]
- == Debugging pointcuts
-
- Go at it top-down and then bottom-up.
-
- === Top-down
-
- Top-down, draft significant
- aspects by first writing the comments to specify responsibilities.
- Advice responsibility usually takes the form, _"When X, do Y"_. Pointcut
- responsibility for _"When X"_ often takes the form, _"When [join points]
- [in locations] [are ...]"_. These __[]__'s often translate to named pointcuts
- like `libraryCalls() && within(Client) && args(Context)`, which form a
- semantic bridge to the plain-text meaning in a comment, e.g. `// when
- the client passes only context into the library`. This gets you to a
- point where you can debug the parts of the pointcut independently.
-
- === Bottom-up
-
- Bottom-up (to build each part), consider each primitive pointcut
- designator (PCD), then the composition, and then any implicit
- constraints:
-
- [arabic]
- . What kinds of join points should it match? (constructor-call?
- field-get?)? This translates to using the kinded pointcuts (`call(..)`,
- `get(..)`, etc.).
- . Are these restricted to being lexically within something? This
- translates to using `within\{code}(..)`. If this is true, it should
- always be used, to speed up weaving.
- . What runtime constraints and context should be true and available at
- each join point? This translates to `this()`, `target()`, `args()`,
- `cflow\{below}()` and `if(..)`.
- . Are there any advice or implementation limitations at issue? This
- involves knowing the few constraints on AspectJ imposed by Java bytecode
- as listed in the AspectJ Programming Guide section on
- xref:../progguide/implementation.adoc#implementation[Implementation Notes].
-
- It's much faster to iterate a pointcut at compile-time using declare
- warning (even better, some errors are identified at parse-time in the
- latest versions of AJDT). Start with the parts of the pointcut that are
- staticly-determinable (i.e., they do not involve the runtime PCD's
- listed above). If compiles themselves take too long because of all the
- AspectJ weaving, then try to only include the debugging aspect with the
- prototype pointcut, and limit the scope using `within(..)`.
-
- == Common pointcut mistakes
-
- There are some typical types of mistakes developers make when designing pointcuts.
- Here are a few examples:
-
- === Mistakes in primitive pointcuts
-
- * `this(Foo) && execution(static * *(..))`: There is no `this` in a
- static context, so `this()` or `target()` should not be used in a static
- context or when targetting a static context (respectively). This happens
- most often when you want to say things like "all calls to `Foo` from ``Bar``"
- and you only pick out calls to instance methods of `Foo` or you try to
- pick out calls from static methods of `Bar`.
-
- * `target(Foo) && call(new(..)`: This will never match. In
- constructor-call join points, there is no target because the object has
- not been created yet.
-
- * `call(* Foo.*(..))`: `Foo` refers to the compile-time type of the
- invoking reference, not the implementing class. In Java before 1.4, the
- compile-time type was rendered as the defining type, not the reference
- type; this was corrected in 1.4 (as shown when using ajc with the -1.4
- flag) Most people should use `target(Foo) && call(...)`.
-
- * `execution(* Foo.bar(..))`: An execution join point for `Foo` is always
- within `Foo`, so this won't pick out any overrides of `bar(..)`. Use
- `target(Foo) && execution(* bar(..))` for instance methods.
-
- * `within(Foo)`: anonymous types are not known at weave-time to be
- within the lexically-enclosing type (a limitation of Java bytecode).
-
- === Mistakes in composition
-
- * `call(* foo(Bar, Foo)) && args(Foo)`: This will never match. The
- parameters in `args(..)` are position-dependent, so `args(Foo)` only
- picks out join points where there is only one argument possible, of type
- Foo. Use the indeterminate-arguments operator `..` as needed, e.g.,
- `args(Foo, ..)`.
-
- * `call(* foo()) && execution(* foo())`: This will never match. Each
- pointcut must be true at each join point matched. For a union of
- different kinds of join points (here, call or execution), use `||`.
- E.g., to match both method-call and field-get join points, use
- `call(* ...) || get(...)`.
-
- === Mistakes in implicit advice constraints
-
- * `after () returning (Foo foo) : ...`: after advice can bind the
- returned object or exception thrown. That effectively acts like
- `target()`, `this()`, or `args()` in restricting when the advice runs
- based on the runtime type of the bound object, even though it is not
- explicitly part of the pointcut.
-
- === Mistakes in implementation requirements
-
- * _ajc_ has to control the code for a join point in order to implement
- the join point. This translates to an implicit `within({code under the
- control of the compiler})` for all join points, with additional caveat
- for some join points. Take exception handlers, for example: there is no
- way to be sure from the bytecode where the original handler ends, so
- _ajc_ can't implement after advice on handler join points. (Since these
- are on a per-join-point basis, they should be considered for each
- corresponding primitive pointcut designator.) Unlike the mistakes with
- the primitive PCDs above, the compiler will emit an error for these
- caveats.
-
- * `call(@SuperAnnotation Subclass.meth()`: Annotations are not inherited
- by default, so e.g., if the pointcut specifies an annotation, then
- subclass implementations of that method will not be matched.
|