|
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176 |
- [[semantics]]
- = Language Semantics
-
- [[semantics-intro]]
- == Introduction
-
- AspectJ extends Java by overlaying a concept of join points onto the
- existing Java semantics and adding a few new program elements to Java:
-
- A join point is a well-defined point in the execution of a program.
- These include method and constructor calls, field accesses and others
- described below.
-
- A pointcut picks out join points, and exposes some of the values in the
- execution context of those join points. There are several primitive
- pointcut designators, and others can be named and defined by the
- `pointcut` declaration.
-
- A piece of advice is code that executes at each join point in a
- pointcut. Advice has access to the values exposed by the pointcut.
- Advice is defined by `before`, `after`, and `around` declarations.
-
- Inter-type declarations form AspectJ's static crosscutting features,
- that is, is code that may change the type structure of a program, by
- adding to or extending interfaces and classes with new fields,
- constructors, or methods. Some inter-type declarations are defined
- through an extension of usual method, field, and constructor
- declarations, and other declarations are made with a new `declare`
- keyword.
-
- An aspect is a crosscutting type that encapsulates pointcuts, advice,
- and static crosscutting features. By type, we mean Java's notion: a
- modular unit of code, with a well-defined interface, about which it is
- possible to do reasoning at compile time. Aspects are defined by the
- `aspect` declaration.
-
- [[semantics-joinPoints]]
- == Join Points
-
- While aspects define types that crosscut, the AspectJ system does not
- allow completely arbitrary crosscutting. Rather, aspects define types
- that cut across principled points in a program's execution. These
- principled points are called join points.
-
- A join point is a well-defined point in the execution of a program. The
- join points defined by AspectJ are:
-
- Method call::
- When a method is called, not including super calls of non-static
- methods.
- Method execution::
- When the body of code for an actual method executes.
- Constructor call::
- When an object is built and that object's initial constructor is
- called (i.e., not for `super` or `this` constructor calls). The object
- being constructed is returned at a constructor call join point, so its
- return type is considered to be the type of the object, and the object
- itself may be accessed with `after returning` advice.
- Constructor execution::
- When the body of code for an actual constructor executes, after its
- this or super constructor call. The object being constructed is the
- currently executing object, and so may be accessed with the `this()` pointcut.
- The constructor execution join point for a constructor that
- calls a super constructor also includes any non-static initializers of
- enclosing class. No value is returned from a constructor execution
- join point, so its return type is considered to be `void`.
- Static initializer execution::
- When the static initializer for a class executes. No value is returned
- from a static initializer execution join point, so its return type is
- considered to be `void`.
- Object pre-initialization::
- Before the object initialization code for a particular class runs.
- This encompasses the time between the start of its first called
- constructor and the start of its parent's constructor. Thus, the
- execution of these join points encompass the join points of the
- evaluation of the arguments of `this()` and `super()` constructor calls.
- No value is returned from an object pre-initialization join point, so its
- return type is considered to be `void`.
- Object initialization::
- When the object initialization code for a particular class runs. This
- encompasses the time between the return of its parent's constructor
- and the return of its first called constructor. It includes all the
- dynamic initializers and constructors used to create the object. The
- object being constructed is the currently executing object, and so may
- be accessed with the `this()` pointcut. No value is returned from a constructor
- execution join point, so its return type is considered to be `void`.
- Field reference::
- When a non-constant field is referenced. [Note that references to
- constant fields (static final fields bound to a constant string object
- or primitive value) are not join points, since Java requires them to
- be inlined.]
- Field set::
- When a field is assigned to. Field set join points are considered to
- have one argument, the value the field is being set to. No value is
- returned from a field set join point, so its return type is considered
- to be void. [Note that the initializations of constant fields (`static final`
- fields where the initializer is a constant string object or
- primitive value) are not join points, since Java requires their
- references to be inlined.]
- Handler execution::
- When an exception handler executes. Handler execution join points are
- considered to have one argument, the exception being handled. No value
- is returned from a field set join point, so its return type is
- considered to be void.
- Advice execution::
- When the body of code for a piece of advice executes.
-
- Each join point potentially has three pieces of state associated with
- it: the currently executing object, the target object, and an object
- array of arguments. These are exposed by the three state-exposing
- pointcuts, `this`, `target`, and `args`, respectively.
-
- Informally, the currently executing object is the object that a `this`
- expression would pick out at the join point. The target object is where
- control or attention is transferred to by the join point. The arguments
- are those values passed for that transfer of control or attention.
-
- [cols=",,,",options="header",]
- |===
- |*Join Point* |*Current Object* |*Target Object* |*Arguments*
- |Method Call |executing object* |target object** |method arguments
-
- |Method Execution |executing object* |executing object* |method
- arguments
-
- |Constructor Call |executing object* |None |constructor arguments
-
- |Constructor Execution |executing object |executing object |constructor
- arguments
-
- |Static initializer execution |None |None |None
-
- |Object pre-initialization |None |None |constructor arguments
-
- |Object initialization |executing object |executing object |constructor
- arguments
-
- |Field reference |executing object* |target object** |None
-
- |Field assignment |executing object* |target object** |assigned value
-
- |Handler execution |executing object* |executing object* |caught
- exception
-
- |Advice execution |executing aspect |executing aspect |advice arguments
- |===
-
- +++*+++ There is no executing object in static contexts such as static method
- bodies or static initializers.
-
- +++**+++ There is no target object for join points associated with static
- methods or fields.
-
- [[semantics-pointcuts]]
- == Pointcuts
-
- A pointcut is a program element that picks out join points and exposes
- data from the execution context of those join points. Pointcuts are used
- primarily by advice. They can be composed with boolean operators to
- build up other pointcuts. The primitive pointcuts and combinators
- provided by the language are:
-
- `call(MethodPattern)`::
- Picks out each method call join point whose signature matches `_MethodPattern_`.
- `execution(MethodPattern)`::
- Picks out each method execution join point whose signature matches `_MethodPattern_`.
- `get(FieldPattern)`::
- Picks out each field reference join point whose signature matches `_FieldPattern_`. [Note that references to constant fields (static final fields bound
- to a constant string object or primitive value) are not join points,
- since Java requires them to be inlined.]
- `set(FieldPattern)`::
- Picks out each field set join point whose signature matches `_FieldPattern_`. [Note that the initializations of constant fields (static final
- fields where the initializer is a constant string object or primitive
- value) are not join points, since Java requires their references to be
- inlined.]
- `call(ConstructorPattern)`::
- Picks out each constructor call join point whose signature matches `_ConstructorPattern_`.
- `execution(ConstructorPattern)`::
- Picks out each constructor execution join point whose signature
- matches `_ConstructorPattern_`.
- `initialization(ConstructorPattern)`::
- Picks out each object initialization join point whose signature
- matches `_ConstructorPattern_`.
- `preinitialization(ConstructorPattern)`::
- Picks out each object pre-initialization join point whose signature
- matches `_ConstructorPattern_`.
- `staticinitialization(TypePattern)`::
- Picks out each static initializer execution join point whose signature
- matches `_TypePattern_`.
- `handler(TypePattern)`::
- Picks out each exception handler join point whose signature matches `_TypePattern_`.
- `adviceexecution()`::
- Picks out all advice execution join points.
- `within(TypePattern)`::
- Picks out each join point where the executing code is defined in a
- type matched by `_TypePattern_`.
- `withincode(MethodPattern)`::
- Picks out each join point where the executing code is defined in a
- method whose signature matches `_MethodPattern_`.
- `withincode(ConstructorPattern)`::
- Picks out each join point where the executing code is defined in a
- constructor whose signature matches `_ConstructorPattern_`.
- `cflow(Pointcut)`::
- Picks out each join point in the control flow of any join point `_P_` picked out by `_Pointcut_` , including `_P_` itself.
- `cflowbelow(Pointcut)`::
- Picks out each join point in the control flow of any join point `_P_` picked out by `_Pointcut_`, but not `_P_` itself.
- `this(Type or Id)`::
- Picks out each join point where the currently executing object (the
- object bound to `_this_`) is an instance of `_Type_` , or of the type of the identifier `_Id_` (which must be bound in the enclosing advice or pointcut definition).
- Will not match any join points from static contexts.
- `target(Type or Id)`::
- Picks out each join point where the target object (the object on which
- a call or field operation is applied to) is an instance of `_Type_` , or of the type of the identifier `_Id_` (which must be bound in the enclosing advice or pointcut definition).
- Will not match any calls, gets, or sets of static members.
- `args(Type or Id, ...)`::
- Picks out each join point where the arguments are instances of the
- appropriate type (or type of the identifier if using that form). A `_null_` argument is matched iff the static type of the argument (declared
- parameter type or field type) is the same as, or a subtype of, the
- specified args type.
- `PointcutId(TypePattern or Id, ...)`::
- Picks out each join point that is picked out by the user-defined
- pointcut designator named by `_PointcutId_` .
- `if(BooleanExpression)`::
- Picks out each join point where the boolean expression evaluates to `_true_` . The boolean expression used can only access static members,
- parameters exposed by the enclosing pointcut or advice, and `_thisJoinPoint_` forms. In particular, it cannot call non-static methods on the aspect
- or use return values or exceptions exposed by after advice.
- `! Pointcut`::
- Picks out each join point that is not picked out by `_Pointcut_` .
- `Pointcut0 && Pointcut1`::
- Picks out each join points that is picked out by both `_Pointcut0_` and `_Pointcut1_` .
- `Pointcut0 || Pointcut1`::
- Picks out each join point that is picked out by either pointcuts. `_Pointcut0_` or `_Pointcut1_` .
- `( Pointcut )`::
- Picks out each join points picked out by `_Pointcut_` .
-
- === Pointcut definition
-
- Pointcuts are defined and named by the programmer with the `pointcut`
- declaration.
-
- [source, java]
- ....
- pointcut publicIntCall(int i):
- call(public * *(int)) && args(i);
- ....
-
- A named pointcut may be defined in either a class or aspect, and is
- treated as a member of the class or aspect where it is found. As a
- member, it may have an access modifier such as `public` or `private`.
-
- [source, java]
- ....
- class C {
- pointcut publicCall(int i):
- call(public * *(int)) && args(i);
- }
-
- class D {
- pointcut myPublicCall(int i):
- C.publicCall(i) && within(SomeType);
- }
- ....
-
- Pointcuts that are not final may be declared abstract, and defined
- without a body. Abstract pointcuts may only be declared within abstract
- aspects.
-
- [source, java]
- ....
- abstract aspect A {
- abstract pointcut publicCall(int i);
- }
- ....
-
- In such a case, an extending aspect may override the abstract pointcut.
-
- [source, java]
- ....
- aspect B extends A {
- pointcut publicCall(int i): call(public Foo.m(int)) && args(i);
- }
- ....
-
- For completeness, a pointcut with a declaration may be declared `final`.
-
- Though named pointcut declarations appear somewhat like method
- declarations, and can be overridden in subaspects, they cannot be
- overloaded. It is an error for two pointcuts to be named with the same
- name in the same class or aspect declaration.
-
- The scope of a named pointcut is the enclosing class declaration. This
- is different than the scope of other members; the scope of other members
- is the enclosing class _body_. This means that the following code is
- legal:
-
- [source, java]
- ....
- aspect B percflow(publicCall()) {
- pointcut publicCall(): call(public Foo.m(int));
- }
- ....
-
- === Context exposure
-
- Pointcuts have an interface; they expose some parts of the execution
- context of the join points they pick out. For example, the PublicIntCall
- above exposes the first argument from the receptions of all public unary
- integer methods. This context is exposed by providing typed formal
- parameters to named pointcuts and advice, like the formal parameters of
- a Java method. These formal parameters are bound by name matching.
-
- On the right-hand side of advice or pointcut declarations, in certain
- pointcut designators, a Java identifier is allowed in place of a type or
- collection of types. The pointcut designators that allow this are
- `this`, `target`, and `args`. In all such cases, using an identifier
- rather than a type does two things. First, it selects join points as
- based on the type of the formal parameter. So the pointcut
-
- [source, java]
- ....
- pointcut intArg(int i): args(i);
- ....
-
- picks out join points where an `int` (or a `byte`, `short`, or `char`;
- anything assignable to an `int`) is being passed as an argument. Second,
- though, it makes the value of that argument available to the enclosing
- advice or pointcut.
-
- Values can be exposed from named pointcuts as well, so
-
- [source, java]
- ....
- pointcut publicCall(int x): call(public *.*(int)) && intArg(x);
- pointcut intArg(int i): args(i);
- ....
-
- is a legal way to pick out all calls to public methods accepting an int
- argument, and exposing that argument.
-
- There is one special case for this kind of exposure. Exposing an
- argument of type Object will also match primitive typed arguments, and
- expose a "boxed" version of the primitive. So,
-
- [source, java]
- ....
- pointcut publicCall(): call(public *.*(..)) && args(Object);
- ....
-
- will pick out all unary methods that take, as their only argument,
- subtypes of Object (i.e., not primitive types like `int`), but
-
- [source, java]
- ....
- pointcut publicCall(Object o): call(public *.*(..)) && args(o);
- ....
-
- will pick out all unary methods that take any argument: And if the
- argument was an `int`, then the value passed to advice will be of type
- `java.lang.Integer`.
-
- The "boxing" of the primitive value is based on the _original_ primitive
- type. So in the following program
-
- [source, java]
- ....
- public class InstanceOf {
- public static void main(String[] args) {
- doInt(5);
- }
-
- static void doInt(int i) { }
- }
-
- aspect IntToLong {
- pointcut el(long l) :
- execution(* doInt(..)) && args(l);
-
- before(Object o) : el(o) {
- System.out.println(o.getClass());
- }
- }
- ....
-
- The pointcut will match and expose the integer argument, but it will
- expose it as an `Integer`, not a `Long`.
-
- === Primitive pointcuts
-
- ==== Method-related pointcuts
-
- AspectJ provides two primitive pointcut designators designed to capture
- method call and execution join points.
-
- * `call( MethodPattern )`
- * `execution( MethodPattern )`
-
- ==== Field-related pointcuts
-
- AspectJ provides two primitive pointcut designators designed to capture
- field reference and set join points:
-
- * `get( FieldPattern )`
- * `set( FieldPattern )`
-
- All set join points are treated as having one argument, the value the
- field is being set to, so at a set join point, that value can be
- accessed with an `args` pointcut. So an aspect guarding a static integer
- variable x declared in type T might be written as
-
- [source, java]
- ....
- aspect GuardedX {
- static final int MAX_CHANGE = 100;
-
- before(int newval): set(static int T.x) && args(newval) {
- if (Math.abs(newval - T.x) > MAX_CHANGE)
- throw new RuntimeException();
- }
- }
- ....
-
- ==== Object creation-related pointcuts
-
- AspectJ provides primitive pointcut designators designed to capture the
- initializer execution join points of objects.
-
- * `call( ConstructorPattern )`
- * `execution( ConstructorPattern )`
- * `initialization( ConstructorPattern )`
- * `preinitialization( ConstructorPattern )`
-
- ==== Class initialization-related pointcuts
-
- AspectJ provides one primitive pointcut designator to pick out static
- initializer execution join points.
-
- * `staticinitialization( TypePattern )`
-
- ==== Exception handler execution-related pointcuts
-
- AspectJ provides one primitive pointcut designator to capture execution
- of exception handlers:
-
- * `handler( TypePattern )`
-
- All handler join points are treated as having one argument, the value of
- the exception being handled. That value can be accessed with an `args`
- pointcut. So an aspect used to put `FooException` objects into some
- normal form before they are handled could be written as
-
- [source, java]
- ....
- aspect NormalizeFooException {
- before(FooException e): handler(FooException) && args(e) {
- e.normalize();
- }
- }
- ....
-
- ==== Advice execution-related pointcuts
-
- AspectJ provides one primitive pointcut designator to capture execution
- of advice
-
- * `adviceexecution()`
-
- This can be used, for example, to filter out any join point in the
- control flow of advice from a particular aspect.
-
- [source, java]
- ....
- aspect TraceStuff {
- pointcut myAdvice(): adviceexecution() && within(TraceStuff);
-
- before(): call(* *(..)) && !cflow(myAdvice) {
- // do something
- }
- }
- ....
-
- ==== State-based pointcuts
-
- Many concerns cut across the dynamic times when an object of a
- particular type is executing, being operated on, or being passed around.
- AspectJ provides primitive pointcuts that capture join points at these
- times. These pointcuts use the dynamic types of their objects to pick
- out join points. They may also be used to expose the objects used for
- discrimination.
-
- * `this( Type or Id )`
- * `target( Type or Id )`
-
- The `this` pointcut picks out each join point where the currently
- executing object (the object bound to `this`) is an instance of a
- particular type. The `target` pointcut picks out each join point where
- the target object (the object on which a method is called or a field is
- accessed) is an instance of a particular type. Note that `target` should
- be understood to be the object the current join point is transfering
- control to. This means that the target object is the same as the current
- object at a method execution join point, for example, but may be
- different at a method call join point.
-
- * `args( Type or Id or "..", ...)`
-
- The args pointcut picks out each join point where the arguments are
- instances of some types. Each element in the comma-separated list is one
- of four things. If it is a type name, then the argument in that position
- must be an instance of that type. If it is an identifier, then that
- identifier must be bound in the enclosing advice or pointcut
- declaration, and so the argument in that position must be an instance of
- the type of the identifier (or of any type if the identifier is typed to
- Object). If it is the `*` wildcard, then any argument will match, and if
- it is the special wildcard `..`, then any number of arguments will
- match, just like in signature patterns. So the pointcut
-
- [source, java]
- ....
- args(int, .., String)
- ....
-
- will pick out all join points where the first argument is an `int` and
- the last is a `String`.
-
- ==== Control flow-based pointcuts
-
- Some concerns cut across the control flow of the program. The `cflow`
- and `cflowbelow` primitive pointcut designators capture join points
- based on control flow.
-
- * `cflow( Pointcut )`
- * `cflowbelow( Pointcut )`
-
- The `cflow` pointcut picks out all join points that occur between entry
- and exit of each join point `P` picked out by `Pointcut`, including `P`
- itself. Hence, it picks out the join points _in_ the control flow of the
- join points picked out by `Pointcut`.
-
- The `cflowbelow` pointcut picks out all join points that occur between
- entry and exit of each join point `P` picked out by `Pointcut`, but not
- including `P` itself. Hence, it picks out the join points _below_ the
- control flow of the join points picked out by `Pointcut`.
-
- ===== Context exposure from control flows
-
- The `cflow` and `cflowbelow` pointcuts may expose context state through
- enclosed `this`, `target`, and `args` pointcuts.
-
- Anytime such state is accessed, it is accessed through the _most recent_
- control flow that matched. So the "current arg" that would be printed by
- the following program is zero, even though it is in many control flows.
-
- [source, java]
- ....
- class Test {
- public static void main(String[] args) {
- fact(5);
- }
- static int fact(int x) {
- if (x == 0) {
- System.err.println("bottoming out");
- return 1;
- }
- else return x * fact(x - 1);
- }
- }
-
- aspect A {
- pointcut entry(int i): call(int fact(int)) && args(i);
- pointcut writing(): call(void println(String)) && ! within(A);
-
- before(int i): writing() && cflow(entry(i)) {
- System.err.println("Current arg is " + i);
- }
- }
- ....
-
- It is an error to expose such state through _negated_ control flow
- pointcuts, such as within `!cflowbelow(P)`.
-
- ==== Program text-based pointcuts
-
- While many concerns cut across the runtime structure of the program,
- some must deal with the lexical structure. AspectJ allows aspects to
- pick out join points based on where their associated code is defined.
-
- * `within( TypePattern )`
- * `withincode( MethodPattern )`
- * `withincode( ConstructorPattern )`
-
- The `within` pointcut picks out each join point where the code executing
- is defined in the declaration of one of the types in `TypePattern`. This
- includes the class initialization, object initialization, and method and
- constructor execution join points for the type, as well as any join
- points associated with the statements and expressions of the type. It
- also includes any join points that are associated with code in a type's
- nested types, and that type's default constructor, if there is one.
-
- The `withincode` pointcuts picks out each join point where the code
- executing is defined in the declaration of a particular method or
- constructor. This includes the method or constructor execution join
- point as well as any join points associated with the statements and
- expressions of the method or constructor. It also includes any join
- points that are associated with code in a method or constructor's local
- or anonymous types.
-
- ==== Expression-based pointcuts
-
- * `if( BooleanExpression )`
-
- The if pointcut picks out join points based on a dynamic property. its
- syntax takes an expression, which must evaluate to a boolean true or
- false. Within this expression, the `thisJoinPoint` object is available.
- So one (extremely inefficient) way of picking out all call join points
- would be to use the pointcut
-
- [source, java]
- ....
- if(thisJoinPoint.getKind().equals("call"))
- ....
-
- Note that the order of evaluation for pointcut expression components at
- a join point is undefined. Writing `if` pointcuts that have side-effects
- is considered bad style and may also lead to potentially confusing or
- even changing behavior with regard to when or if the test code will run.
-
- === Signatures
-
- One very important property of a join point is its signature, which is
- used by many of AspectJ's pointcut designators to select particular join
- points.
-
- ==== Methods
-
- Join points associated with methods typically have method signatures,
- consisting of a method name, parameter types, return type, the types of
- the declared (checked) exceptions, and some type that the method could
- be called on (below called the "qualifying type").
-
- At a method call join point, the signature is a method signature whose
- qualifying type is the static type used to _access_ the method. This
- means that the signature for the join point created from the call
- `((Integer)i).toString()` is different than that for the call
- `((Object)i).toString()`, even if `i` is the same variable.
-
- At a method execution join point, the signature is a method signature
- whose qualifying type is the declaring type of the method.
-
- ==== Fields
-
- Join points associated with fields typically have field signatures,
- consisting of a field name and a field type. A field reference join
- point has such a signature, and no parameters. A field set join point
- has such a signature, but has a has a single parameter whose type is the
- same as the field type.
-
- ==== Constructors
-
- Join points associated with constructors typically have constructor
- signatures, consisting of a parameter types, the types of the declared
- (checked) exceptions, and the declaring type.
-
- At a constructor call join point, the signature is the constructor
- signature of the called constructor. At a constructor execution join
- point, the signature is the constructor signature of the currently
- executing constructor.
-
- At object initialization and pre-initialization join points, the
- signature is the constructor signature for the constructor that started
- this initialization: the first constructor entered during this type's
- initialization of this object.
-
- ==== Others
-
- At a handler execution join point, the signature is composed of the
- exception type that the handler handles.
-
- At an advice execution join point, the signature is composed of the
- aspect type, the parameter types of the advice, the return type (void
- for all but around advice) and the types of the declared (checked)
- exceptions.
-
- === Matching
-
- The `withincode`, `call`, `execution`, `get`, and `set` primitive
- pointcut designators all use signature patterns to determine the join
- points they describe. A signature pattern is an abstract description of
- one or more join-point signatures. Signature patterns are intended to
- match very closely the same kind of things one would write when
- declaring individual members and constructors.
-
- Method declarations in Java include method names, method parameters,
- return types, modifiers like static or private, and throws clauses,
- while constructor declarations omit the return type and replace the
- method name with the class name. The start of a particular method
- declaration, in class `Test`, for example, might be
-
- [source, java]
- ....
- class C {
- public final void foo() throws ArrayOutOfBoundsException { ... }
- }
- ....
-
- In AspectJ, method signature patterns have all these, but most elements
- can be replaced by wildcards. So
-
- [source, java]
- ....
- call(public final void C.foo() throws ArrayOutOfBoundsException)
- ....
-
- picks out call join points to that method, and the pointcut
-
- [source, java]
- ....
- call(public final void *.*() throws ArrayOutOfBoundsException)
- ....
-
- picks out all call join points to methods, regardless of their name name
- or which class they are defined on, so long as they take no arguments,
- return no value, are both `public` and `final`, and are declared to
- throw ``ArrayOutOfBoundsException``s.
-
- The defining type name, if not present, defaults to *, so another way of
- writing that pointcut would be
-
- [source, java]
- ....
- call(public final void *() throws ArrayOutOfBoundsException)
- ....
-
- The wildcard `..` indicates zero or more parameters, so
-
- [source, java]
- ....
- execution(void m(..))
- ....
-
- picks out execution join points for void methods named `m`, of any
- number of arguments, while
-
- [source, java]
- ....
- execution(void m(.., int))
- ....
-
- picks out execution join points for void methods named `m` whose last
- parameter is of type `int`.
-
- The modifiers also form part of the signature pattern. If an AspectJ
- signature pattern should match methods without a particular modifier,
- such as all non-public methods, the appropriate modifier should be
- negated with the `!` operator. So,
-
- [source, java]
- ....
- withincode(!public void foo())
- ....
-
- picks out all join points associated with code in null non-public void
- methods named `foo`, while
-
- [source, java]
- ....
- withincode(void foo())
- ....
-
- picks out all join points associated with code in null void methods
- named `foo`, regardless of access modifier.
-
- Method names may contain the * wildcard, indicating any number of
- characters in the method name. So
-
- [source, java]
- ....
- call(int *())
- ....
-
- picks out all call join points to `int` methods regardless of name, but
-
- [source, java]
- ....
- call(int get*())
- ....
-
- picks out all call join points to `int` methods where the method name
- starts with the characters "get".
-
- AspectJ uses the `new` keyword for constructor signature patterns rather
- than using a particular class name. So the execution join points of
- private null constructor of a class `C` defined to throw an
- `ArithmeticException` can be picked out with
-
- [source, java]
- ....
- execution(private C.new() throws ArithmeticException)
- ....
-
- ==== Matching based on the declaring type
-
- The signature-matching pointcuts all specify a declaring type, but the
- meaning varies slightly for each join point signature, in line with Java
- semantics.
-
- When matching for pointcuts `withincode`, `get`, and `set`, the
- declaring type is the class that contains the declaration.
-
- When matching method-call join points, the declaring type is the static
- type used to access the method. A common mistake is to specify a
- declaring type for the `call` pointcut that is a subtype of the
- originally-declaring type. For example, given the class
-
- [source, java]
- ....
- class Service implements Runnable {
- public void run() { ... }
- }
- ....
-
- the following pointcut
-
- [source, java]
- ....
- call(void Service.run())
- ....
-
- would fail to pick out the join point for the code
-
- [source, java]
- ....
- ((Runnable) new Service()).run();
- ....
-
- Specifying the originally-declaring type is correct, but would pick out
- any such call (here, calls to the `run()` method of any `Runnable`). In
- this situation, consider instead picking out the target type:
-
- [source, java]
- ....
- call(void run()) && target(Service)
- ....
-
- When matching method-execution join points, if the execution pointcut
- method signature specifies a declaring type, the pointcut will only
- match methods declared in that type, or methods that override methods
- declared in or inherited by that type. So the pointcut
-
- [source, java]
- ....
- execution(public void Middle.*())
- ....
-
- picks out all method executions for public methods returning void and
- having no arguments that are either declared in, or inherited by,
- `Middle`, even if those methods are overridden in a subclass of `Middle`. So
- the pointcut would pick out the method-execution join point for `Sub.m()`
- in this code:
-
- [source, java]
- ....
- class Super {
- protected void m() { /*...*/ }
- }
-
- class Middle extends Super {}
-
- class Sub extends Middle {
- public void m() { /*...*/ }
- }
- ....
-
- ==== Matching based on the `throws` clause
-
- Type patterns may be used to pick out methods and constructors based on
- their `throws` clauses. This allows the following two kinds of extremely
- wildcarded pointcuts:
-
- [source, java]
- ....
- pointcut throwsMathlike():
- // each call to a method with a throws clause containing at least
- // one exception exception with "Math" in its name.
- call(* *(..) throws *..*Math*);
-
- pointcut doesNotThrowMathlike():
- // each call to a method with a throws clause containing no
- // exceptions with "Math" in its name.
- call(* *(..) throws !*..*Math*);
- ....
-
- A `ThrowsClausePattern` is a comma-separated list of ``ThrowsClausePatternItem``s, where
-
- [source, text]
- ....
- ThrowsClausePatternItem := [ ! ] TypeNamePattern
- ....
-
- A `ThrowsClausePattern` matches the `throws` clause of any code member
- signature. To match, each `ThrowsClausePatternItem` must match the
- `throws` clause of the member in question. If any item doesn't match, then
- the whole pattern doesn't match.
-
- If a `ThrowsClausePatternItem` begins with `!`, then it matches a
- particular `throws` clause if and only if _none_ of the types named in the
- `throws` clause is matched by the `TypeNamePattern`.
-
- If a `ThrowsClausePatternItem` does not begin with `!`, then it matches
- a throws clause if and only if _any_ of the types named in the `throws`
- clause is matched by the `TypeNamePattern`.
-
- The rule for `!` matching has one potentially surprising property, in
- that these two pointcuts
-
- . `call(* *(..) throws !IOException)`
- . `call(* *(..) throws (!IOException))`
-
- will match differently on calls to
-
- [source, java]
- ....
- void m() throws RuntimeException, IOException {}
- ....
-
- [1] will *not* match the method `m()`, because ``m``'s throws clause
- declares that it `throws IOException`.
-
- [2] *will* match the method `m()`, because ``m``'s throws clause declares that
- it throws some exception which does not match `IOException`, i.e. `RuntimeException`.
-
- === Type patterns
-
- Type patterns are a way to pick out collections of types and use them in
- places where you would otherwise use only one type. The rules for using
- type patterns are simple.
-
- ==== Exact type pattern
-
- First, all type names are also type patterns. So `Object`,
- `java.util.HashMap`, `Map.Entry`, `int` are all type patterns.
-
- If a type pattern is an exact type - if it doesn't include a wildcard -
- then the matching works just like normal type lookup in Java:
-
- * Patterns that have the same names as primitive types (like `int`) match those
- primitive types.
- * Patterns that are qualified by package names (like `java.util.HashMap`) match
- types in other packages.
- * Patterns that are not qualified (like `HashMap`) match types that are resolved
- by Java's normal scope rules. So, for example, `HashMap` might match a package-level
- type in the same package or a type that have been imported with Java's `import`
- form. But it would not match `java.util.HashMap` unless the aspect were in `java.util`
- or the type had been imported.
-
- So exact type patterns match based on usual Java scope rules.
-
- ==== Type name patterns
-
- There is a special type name, `\*`, which is also a type pattern. `*` picks
- out all types, including primitive types. So
-
- [source, java]
- ....
- call(void foo(*))
- ....
-
- picks out all call join points to void methods named foo, taking one
- argument of any type.
-
- Type names that contain the two wildcards `\*` and `..` are also type
- patterns. The `*` wildcard matches zero or more characters characters
- except for `.`, so it can be used when types have a certain naming
- convention. So
-
- [source, java]
- ....
- handler(java.util.*Map)
- ....
-
- picks out the types `java.util.Map` and `java.util.java.util.HashMap`, among
- others, and
-
- [source, java]
- ....
- handler(java.util.*)
- ....
-
- picks out all types that start with `java.util.` and don't have any
- more ``.``s, that is, the types in the `java.util` package, but not inner
- types (such as `java.util.Map.Entry`).
-
- The `..` wildcard matches any sequence of characters that start and
- end with a `.`, so it can be used to pick out all types in any
- subpackage, or all inner types. So
-
- [source, java]
- ....
- within(com.xerox..*)
- ....
-
- picks out all join points where the code is in any declaration of a type
- whose name begins with `com.xerox.`.
-
- Type patterns with wildcards do not depend on Java's usual scope rules -
- they match against all types available to the weaver, not just those
- that are imported into an Aspect's declaring file.
-
- ==== Subtype patterns
-
- It is possible to pick out all subtypes of a type (or a collection of
- types) with the `+` wildcard. The `+` wildcard follows immediately a
- type name pattern. So, while
-
- [source, java]
- ....
- call(Foo.new())
- ....
-
- picks out all constructor call join points where an instance of exactly
- type `Foo` is constructed,
-
- [source, java]
- ....
- call(Foo+.new())
- ....
-
- picks out all constructor call join points where an instance of any
- subtype of `Foo` (including `Foo` itself) is constructed, and the unlikely
-
- [source, java]
- ....
- call(*Handler+.new())
- ....
-
- picks out all constructor call join points where an instance of any
- subtype of any type whose name ends in `Handler` is constructed.
-
- ==== Array type patterns
-
- A type name pattern or subtype pattern can be followed by one or more
- sets of square brackets to make array type patterns. So `Object[]` is an
- array type pattern, and so is `com.xerox..*[][]`, and so is `Object+[]`.
-
- ==== Type patterns
-
- Type patterns are built up out of type name patterns, subtype patterns,
- and array type patterns, and constructed with boolean operators `&&`,
- `||`, and `!`. So
-
- [source, java]
- ....
- staticinitialization(Foo || Bar)
- ....
-
- picks out the static initializer execution join points of either `Foo` or
- `Bar`, and
-
- [source, java]
- ....
- call((Foo+ && ! Foo).new(..))
- ....
-
- picks out the constructor call join points when a subtype of `Foo`, but
- not `Foo` itself, is constructed.
-
- === Pattern Summary
-
- Here is a summary of the pattern syntax used in AspectJ:
-
- [source, text]
- ....
- MethodPattern =
- [ModifiersPattern] TypePattern
- [TypePattern . ] IdPattern (TypePattern | ".." , ... )
- [ throws ThrowsPattern ]
- ConstructorPattern =
- [ModifiersPattern ]
- [TypePattern . ] new (TypePattern | ".." , ...)
- [ throws ThrowsPattern ]
- FieldPattern =
- [ModifiersPattern] TypePattern [TypePattern . ] IdPattern
- ThrowsPattern =
- [ ! ] TypePattern , ...
- TypePattern =
- IdPattern [ + ] [ [] ... ]
- | ! TypePattern
- | TypePattern && TypePattern
- | TypePattern || TypePattern
- | ( TypePattern )
- IdPattern =
- Sequence of characters, possibly with special * and .. wildcards
- ModifiersPattern =
- [ ! ] JavaModifier ...
- ....
-
- [[semantics-advice]]
- == Advice
-
- Each piece of advice is of the form
-
- [source, text]
- ....
- [ strictfp ] AdviceSpec [ throws TypeList ] : Pointcut { Body }
- ....
-
- where `AdviceSpec` is one of
-
- * `before( Formals )`
- * `after( Formals ) returning [ ( Formal ) ]`
- * `after( Formals ) throwing [ ( Formal ) ]`
- * `after( Formals )`
- * `Type around( Formals )`
-
- and where `Formal` refers to a variable binding like those used for
- method parameters, of the form `Type` `Variable-Name`, and `Formals`
- refers to a comma-delimited list of `Formal`.
-
- Advice defines crosscutting behavior. It is defined in terms of
- pointcuts. The code of a piece of advice runs at every join point picked
- out by its pointcut. Exactly how the code runs depends on the kind of
- advice.
-
- AspectJ supports three kinds of advice. The kind of advice determines
- how it interacts with the join points it is defined over. Thus AspectJ
- divides advice into that which runs *before* its join points, that which
- runs *after* its join points, and that which runs *in place of (or
- "around")* its join points.
-
- While `before` advice is relatively unproblematic, there can be three
- interpretations of `after` advice: After the execution of a join point
- completes normally, after it throws an exception, or after it does
- either one. AspectJ allows `after` advice for any of these situations:
-
- [source, java]
- ....
- aspect A {
- pointcut publicCall(): call(public Object *(..));
-
- after() returning (Object o): publicCall() {
- System.out.println("Returned normally with " + o);
- }
-
- after() throwing (Exception e): publicCall() {
- System.out.println("Threw an exception: " + e);
- }
-
- after(): publicCall(){
- System.out.println("Returned or threw an Exception");
- }
- }
- ....
-
- `after returning` advice may not care about its returned object, in which
- case it may be written
-
- [source, java]
- ....
- after() returning: call(public Object *(..)) {
- System.out.println("Returned normally");
- }
- ....
-
- If `after returning` does expose its returned object, then the type of the
- parameter is considered to be an `instanceof`-like constraint on the
- advice: it will run only when the return value is of the appropriate
- type.
-
- A value is of the appropriate type if it would be assignable to a
- variable of that type, in the Java sense. That is, a `byte` value is
- assignable to a `short` parameter but not vice-versa, an `int` is
- assignable to a `float` parameter, `boolean` values are only assignable
- to `boolean` parameters, and reference types work by `instanceof`.
-
- There are two special cases: If the exposed value is typed to `Object`,
- then the advice is not constrained by that type: the actual return value
- is converted to an object type for the body of the advice: `int` values
- are represented as `java.lang.Integer` objects, etc, and no value (from
- `void` methods, for example) is represented as `null`.
-
- Secondly, the `null` value is assignable to a parameter `T` if the join
- point _could_ return something of type `T`.
-
- `around` advice runs in place of the join point it operates over, rather
- than before or after it. Because `around` is allowed to return a value, it
- must be declared with a return type, like a method.
-
- Thus, a simple use of `around` advice is to make a particular method
- constant:
-
- [source, java]
- ....
- aspect A {
- int around(): call(int C.foo()) {
- return 3;
- }
- }
- ....
-
- Within the body of `around` advice, though, the computation of the
- original join point can be executed with the special syntax
-
- [source, java]
- ....
- proceed( ... )
- ....
-
- The `proceed` form takes as arguments the context exposed by the around's
- pointcut, and returns whatever the around is declared to return. So the
- following around advice will double the second argument to `foo`
- whenever it is called, and then halve its result:
-
- [source, java]
- ....
- aspect A {
- int around(int i): call(int C.foo(Object, int)) && args(i) {
- int newi = proceed(i*2)
- return newi/2;
- }
- }
- ....
-
- If the return value of `around` advice is typed to `Object`, then the
- result of proceed is converted to an object representation, even if it
- is originally a primitive value. And when the advice returns an `Object`
- value, that value is converted back to whatever representation it was
- originally. So another way to write the doubling and halving advice is:
-
- [source, java]
- ....
- aspect A {
- Object around(int i): call(int C.foo(Object, int)) && args(i) {
- Integer newi = (Integer) proceed(i*2)
- return new Integer(newi.intValue() / 2);
- }
- }
- ....
-
- Any occurence of `proceed(..)` within the body of around advice is
- treated as the special `proceed` form (even if the aspect defines a method
- named `proceed`), unless a target other than the aspect instance is
- specified as the recipient of the call. For example, in the following
- program the first call to `proceed` will be treated as a method call to
- the `ICanProceed` instance, whereas the second call to `proceed` is
- treated as the special `proceed` form.
-
- [source, java]
- ....
- aspect A {
- Object around(ICanProceed canProceed) : execution(* *(..)) && this(canProceed) {
- canProceed.proceed(); // a method call
- return proceed(canProceed); // the special proceed form
- }
-
- private Object proceed(ICanProceed canProceed) {
- // this method cannot be called from inside the body of around advice
- // in the aspect
- }
- }
- ....
-
- In all kinds of advice, the parameters of the advice behave exactly like
- method parameters. In particular, assigning to any parameter affects
- only the value of the parameter, not the value that it came from. This
- means that
-
- [source, java]
- ....
- aspect A {
- after() returning (int i): call(int C.foo()) {
- i = i * 2;
- }
- }
- ....
-
- will _not_ double the returned value of the advice. Rather, it will
- double the local parameter. Changing the values of parameters or return
- values of join points can be done by using `around` advice.
-
- With `proceed(..)` it is possible to change the values used by
- less-precedent advice and the underlying join point by supplying
- different values for the variables. For example, this aspect replaces
- the string bound to `s` in the named pointcut `privateData`:
-
- [source, java]
- ....
- aspect A {
- Object around(String s): MyPointcuts.privateData(s) {
- return proceed("private data");
- }
- }
- ....
-
- If you replace an argument to `proceed(..)`, you can cause a
- `ClassCastException` at runtime when the argument refers to a supertype
- of the actual type and you do not supply a reference of the actual type.
- In the following aspect, the around advice replaces the declared target
- `List` with an `ArrayList`. This is valid code at compile-time since the
- types match.
-
- [source, java]
- ....
- import java.util.*;
-
- aspect A {
- Object around(List list): call(* List+.*()) && target(list) {
- return proceed(new ArrayList());
- }
- }
- ....
-
- But imagine a simple program where the actual target is `LinkedList`. In
- this case, the advice would cause a `ClassCastException` at runtime, and
- `peek()` is not declared in `ArrayList`.
-
- [source, java]
- ....
- public class Test {
- public static void main(String[] args) {
- new LinkedList().peek();
- }
- }
- ....
-
- The `ClassCastException` can occur even in situations where it appears
- to be unnecessary, e.g., if the program is changed to call `size()`,
- declared in `List`:
-
- [source, java]
- ....
- public class Test {
- public static void main(String[] args) {
- new LinkedList().size();
- }
- }
- ....
-
- There will still be a `ClassCastException` because it is impossible to
- prove that there won't be a runtime binary-compatible change in the
- hierarchy of `LinkedList` or some other advice on the join point that
- requires a `LinkedList`.
-
- === Advice modifiers
-
- The `strictfp` modifier is the only modifier allowed on advice, and it
- has the effect of making all floating-point expressions within the
- advice be FP-strict.
-
- === Advice and checked exceptions
-
- An advice declaration must include a `throws` clause listing the checked
- exceptions the body may throw. This list of checked exceptions must be
- compatible with each target join point of the advice, or an error is
- signalled by the compiler.
-
- For example, in the following declarations:
-
- [source, java]
- ....
- import java.io.FileNotFoundException;
-
- class C {
- int i;
- int getI() { return i; }
- }
-
- aspect A {
- before(): get(int C.i) {
- throw new FileNotFoundException();
- }
-
- before() throws FileNotFoundException: get(int C.i) {
- throw new FileNotFoundException();
- }
- }
- ....
-
- both pieces of advice are illegal. The first because the body throws an
- undeclared checked exception, and the second because field get join
- points cannot throw ``FileNotFoundException``s.
-
- The exceptions that each kind of join point in AspectJ may throw are:
-
- method call and execution::
- the checked exceptions declared by the target method's `throws` clause.
- constructor call and execution::
- the checked exceptions declared by the target constructor's `throws` clause.
- field get and set::
- no checked exceptions can be thrown from these join points.
- exception handler execution::
- the exceptions that can be thrown by the target exception handler.
- static initializer execution::
- no checked exceptions can be thrown from these join points.
- pre-initialization and initialization::
- any exception that is in the `throws` clause of all constructors of the initialized class.
- advice execution::
- any exception that is in the `throws` clause of the advice.
-
- === Advice precedence
-
- Multiple pieces of advice may apply to the same join point. In such
- cases, the resolution order of the advice is based on advice precedence.
-
- ==== Determining precedence
-
- There are a number of rules that determine whether a particular piece of
- advice has precedence over another when they advise the same join point.
-
- If the two pieces of advice are defined in different aspects, then there
- are three cases:
-
- * If aspect `A` is matched earlier than aspect `B` in some `declare precedence`
- form, then all advice in concrete aspect `A` has precedence over all
- advice in concrete aspect `B` when they are on the same join point.
- * Otherwise, if aspect `A` is a subaspect of aspect `B`, then all advice
- defined in `A` has precedence over all advice defined in `B`. So, unless
- otherwise specified with `declare precedence`, advice in a subaspect has
- precedence over advice in a superaspect.
- * Otherwise, if two pieces of advice are defined in two different
- aspects, it is undefined which one has precedence.
-
- If the two pieces of advice are defined in the same aspect, then there
- are two cases:
-
- * If either are `after` advice, then the one that appears later in the aspect has precedence
- over the one that appears earlier.
- * Otherwise, then the one that appears earlier in the aspect has
- precedence over the one that appears later.
-
- These rules can lead to circularity, such as
-
- [source, java]
- ....
- aspect A {
- before(): execution(void main(String[] args)) {}
- after(): execution(void main(String[] args)) {}
- before(): execution(void main(String[] args)) {}
- }
- ....
-
- such circularities will result in errors signalled by the compiler.
-
- ==== Effects of precedence
-
- At a particular join point, advice is ordered by precedence.
-
- A piece of `around` advice controls whether advice of lower precedence
- will run by calling `proceed`. The call to `proceed` will run the advice
- with next precedence, or the computation under the join point if there
- is no further advice.
-
- A piece of `before` advice can prevent advice of lower precedence from
- running by throwing an exception. If it returns normally, however, then
- the advice of the next precedence, or the computation under the join
- pint if there is no further advice, will run.
-
- Running `after returning` advice will run the advice of next precedence,
- or the computation under the join point if there is no further advice.
- Then, if that computation returned normally, the body of the advice will
- run.
-
- Running `after throwing` advice will run the advice of next precedence,
- or the computation under the join point if there is no further advice.
- Then, if that computation threw an exception of an appropriate type, the
- body of the advice will run.
-
- Running `after` advice will run the advice of next precedence, or the
- computation under the join point if there is no further advice. Then the
- body of the advice will run.
-
- === Reflective access to the join point
-
- Three special variables are visible within bodies of advice and within
- `if()` pointcut expressions: `thisJoinPoint`, `thisJoinPointStaticPart`,
- and `thisEnclosingJoinPointStaticPart`. Each is bound to an object that
- encapsulates some of the context of the advice's current or enclosing
- join point. These variables exist because some pointcuts may pick out
- very large collections of join points. For example, the pointcut
-
- [source, java]
- ....
- pointcut publicCall(): call(public * *(..));
- ....
-
- picks out calls to many methods. Yet the body of advice over this
- pointcut may wish to have access to the method name or parameters of a
- particular join point.
-
- * `thisJoinPoint` is bound to a complete join point object.
-
- * `thisJoinPointStaticPart` is bound to a part of the join point object
- that includes less information, but for which no memory allocation is
- required on each execution of the advice. It is equivalent to
- `thisJoinPoint.getStaticPart()`.
-
- * `thisEnclosingJoinPointStaticPart` is bound to the static part of the
- join point enclosing the current join point. Only the static part of
- this enclosing join point is available through this mechanism.
-
- Standard Java reflection uses objects from the `java.lang.reflect`
- hierarchy to build up its reflective objects. Similarly, AspectJ join
- point objects have types in a type hierarchy. The type of objects bound
- to `thisJoinPoint` is `org.aspectj.lang.JoinPoint`, while
- `thisStaticJoinPoint` is bound to objects of interface type
- `org.aspectj.lang.JoinPoint.StaticPart`.
-
- [[semantics-declare]]
- == Static crosscutting
-
- Advice declarations change the behavior of classes they crosscut, but do
- not change their static type structure. For crosscutting concerns that
- do operate over the static structure of type hierarchies, AspectJ
- provides inter-type member declarations and other `declare` forms.
-
- === Inter-type member declarations
-
- AspectJ allows the declaration of members by aspects that are associated
- with other types.
-
- An inter-type method declaration looks like
-
- * `[ Modifiers ] Type OnType . Id ( Formals ) [ ThrowsClause ] { Body }`
- * `abstract [ Modifiers ] Type OnType . Id ( Formals ) [ ThrowsClause ] ;`
-
- The effect of such a declaration is to make `OnType` support the new
- method. Even if `OnType` is an interface. Even if the method is neither
- public nor abstract. So the following is legal AspectJ code:
-
- [source, java]
- ....
- interface Iface {}
-
- aspect A {
- private void Iface.m() {
- System.err.println("I'm a private method on an interface");
- }
-
- void worksOnI(Iface iface) {
- // calling a private method on an interface
- iface.m();
- }
- }
- ....
-
- An inter-type constructor declaration looks like
-
- * `[ Modifiers ] OnType . new ( Formals ) [ ThrowsClause ] { Body }`
-
- The effect of such a declaration is to make `OnType` support the new
- constructor. It is an error for `OnType` to be an interface.
-
- Inter-type declared constructors cannot be used to assign a value to a
- final variable declared in `OnType`. This limitation significantly
- increases the ability to both understand and compile the `OnType` class
- and the declaring aspect separately.
-
- Note that in the Java language, classes that define no constructors have
- an implicit no-argument constructor that just calls `super()`. This
- means that attempting to declare a no-argument inter-type constructor on
- such a class may result in a conflict, even though it _looks_ like no
- constructor is defined.
-
- An inter-type field declaration looks like one of
-
- * `[ Modifiers ] Type OnType . Id = Expression ;`
- * `[ Modifiers ] Type OnType . Id ;`
-
- The effect of such a declaration is to make `OnType` support the new
- field. Even if `OnType` is an interface. Even if the field is neither
- public, nor static, nor final.
-
- The initializer, if any, of an inter-type field declaration runs before
- the class-local initializers defined in its target class.
-
- Any occurrence of the identifier `this` in the body of an inter-type
- constructor or method declaration, or in the initializer of an
- inter-type field declaration, refers to the `OnType` object rather than
- to the aspect type; it is an error to access `this` in such a position
- from a `static` inter-type member declaration.
-
- === Access modifiers
-
- Inter-type member declarations may be `public` or `private`, or have default
- (package-protected) visibility. AspectJ does not provide protected
- inter-type members.
-
- The access modifier applies in relation to the aspect, not in relation
- to the target type. So a private inter-type member is visible only from
- code that is defined within the declaring aspect. A default-visibility
- inter-type member is visible only from code that is defined within the
- declaring aspect's package.
-
- Note that a declaring a private inter-type method (which AspectJ
- supports) is very different from inserting a private method declaration
- into another class. The former allows access only from the declaring
- aspect, while the latter would allow access only from the target type.
- Java serialization, for example, uses the presense of a private method
- `void writeObject(ObjectOutputStream)` for the implementation of
- `java.io.Serializable`. A private inter-type declaration of that method
- would not fulfill this requirement, since it would be private to the
- aspect, not private to the target type.
-
- The access modifier of abstract inter-type methods has one constraint:
- It is illegal to declare an abstract non-public inter-type method on a
- public interface. This is illegal because it would say that a public
- interface has a constraint that only non-public implementors must
- fulfill. This would not be compatible with Java's type system.
-
- === Conflicts
-
- Inter-type declarations raise the possibility of conflicts among locally
- declared members and inter-type members. For example, assuming
- `otherPackage` is not the package containing the aspect `A`, the code
-
- [source, java]
- ....
- aspect A {
- private Registry otherPackage.onType.r;
-
- public void otherPackage.onType.register(Registry r) {
- r.register(this);
- this.r = r;
- }
- }
- ....
-
- declares that `onType` in `otherPackage` has a field `r`. This field,
- however, is only accessible from the code inside of aspect `A`. The
- aspect also declares that `onType` has a method "`register`", but makes
- this method accessible from everywhere.
-
- If `onType` already defines a private or package-protected field `r`,
- there is no conflict: The aspect cannot see such a field, and no code in
- `otherPackage` can see the inter-type `r`.
-
- If `onType` defines a public field `r`, there is a conflict: The
- expression
-
- [source, java]
- ....
- this.r = r
- ....
-
- is an error, since it is ambiguous whether the private inter-type `r`
- or the public locally-defined `r` should be used.
-
- If `onType` defines a method `register(Registry)` there is a conflict,
- since it would be ambiguous to any code that could see such a defined
- method which `register(Registry)` method was applicable.
-
- Conflicts are resolved as much as possible as per Java's conflict
- resolution rules:
-
- * A subclass can inherit multiple fields from its superclasses, all with the
- same name and type. However, it is an error to have an ambiguous reference
- to a field.
- * A subclass can only inherit multiple methods with the same name and argument
- types from its superclasses if only zero or one of them is concrete (i.e., all
- but one is abstract, or all are abstract).
-
- Given a potential conflict between inter-type member declarations in
- different aspects, if one aspect has precedence over the other its
- declaration will take effect without any conflict notice from compiler.
- This is true both when the precedence is declared explicitly with
- `declare precedence` as well as when when sub-aspects implicitly have
- precedence over their super-aspect.
-
- === Extension and Implementation
-
- An aspect may change the inheritance hierarchy of a system by changing
- the superclass of a type or adding a superinterface onto a type, with
- the `declare parents` form.
-
- * `declare parents: TypePattern extends Type ;`
- * `declare parents: TypePattern implements TypeList ;`
-
- For example, if an aspect wished to make a particular class runnable, it
- might define appropriate inter-type `void
- run()` method, but it should also declare that the class
- fulfills the `Runnable` interface. In order to implement the methods in
- the `Runnable` interface, the inter-type `run()` method must be public:
-
- [source, java]
- ....
- aspect A {
- declare parents: SomeClass implements Runnable;
-
- public void SomeClass.run() { ... }
- }
- ....
-
- === Interfaces with members
-
- Through the use of inter-type members, interfaces may now carry
- (non-public-static-final) fields and (non-public-abstract) methods that
- classes can inherit. Conflicts may occur from ambiguously inheriting
- members from a superclass and multiple superinterfaces.
-
- Because interfaces may carry non-static initializers, each interface
- behaves as if it has a zero-argument constructor containing its
- initializers. The order of super-interface instantiation is observable.
- We fix this order with the following properties: A supertype is
- initialized before a subtype, initialized code runs only once, and the
- initializers for a type's superclass are run before the initializers for
- its superinterfaces. Consider the following hierarchy where {`Object`,
- `C`, `D`, `E`} are classes, {`M`, `N`, `O`, `P`, `Q`} are interfaces.
-
- [source, text]
- ....
- Object M O
- \ / \ /
- C N Q
- \ / /
- D P
- \ /
- E
- ....
-
- when a new `E` is instantiated, the initializers run in this order:
-
- [source, text]
- ....
- Object M C O N D Q P E
- ....
-
- === Warnings and Errors
-
- An aspect may specify that a particular join point should never be
- reached.
-
- * `declare error: Pointcut : String ;`
- * `declare warning: Pointcut : String ;`
-
- If the compiler determines that a join point in `Pointcut` could
- possibly be reached, then it will signal either an error or warning, as
- declared, using the `String` for its message.
-
- === Softened exceptions
-
- An aspect may specify that a particular kind of exception, if thrown at
- a join point, should bypass Java's usual static exception checking
- system and instead be thrown as a `org.aspectj.lang.SoftException`,
- which is subtype of `RuntimeException` and thus does not need to be
- declared.
-
- * `declare soft: Type : Pointcut ;`
-
- For example, the aspect
-
- [source, java]
- ....
- aspect A {
- declare soft: Exception: execution(void main(String[] args));
- }
- ....
-
- Would, at the execution join point, catch any `Exception` and rethrow a
- `org.aspectj.lang.SoftException` containing original exception.
-
- This is similar to what the following advice would do
-
- [source, java]
- ....
- aspect A {
- void around() execution(void main(String[] args)) {
- try { proceed(); }
- catch (Exception e) {
- throw new org.aspectj.lang.SoftException(e);
- }
- }
- }
- ....
-
- except, in addition to wrapping the exception, it also affects Java's
- static exception checking mechanism.
-
- Like advice, the declare soft form has no effect in an abstract aspect
- that is not extended by a concreate aspect. So the following code will
- not compile unless it is compiled with an extending concrete aspect:
-
- [source, java]
- ....
- abstract aspect A {
- abstract pointcut softeningPC();
-
- before() : softeningPC() {
- Class.forName("FooClass"); // error: uncaught ClassNotFoundException
- }
-
- declare soft : ClassNotFoundException : call(* Class.*(..));
- }
- ....
-
- [[advice-precedence-cross]]
- === Advice Precedence
-
- An aspect may declare a precedence relationship between concrete aspects
- with the `declare precedence` form:
-
- * `declare precedence : TypePatternList ;`
-
- This signifies that if any join point has advice from two concrete
- aspects matched by some pattern in `TypePatternList`, then the
- precedence of the advice will be the order of in the list.
-
- In `TypePatternList`, the wildcard `*` can appear at most once, and it
- means "any type not matched by any other pattern in the list".
-
- For example, the constraints that (1) aspects that have Security as part
- of their name should have precedence over all other aspects, and (2) the
- Logging aspect (and any aspect that extends it) should have precedence
- over all non-security aspects, can be expressed by:
-
- [source, java]
- ....
- declare precedence: *..*Security*, Logging+, *;
- ....
-
- For another example, the `CountEntry` aspect might want to count the entry
- to methods in the current package accepting a Type object as its first
- argument. However, it should count all entries, even those that the
- aspect `DisallowNulls` causes to throw exceptions. This can be
- accomplished by stating that `CountEntry` has precedence over
- `DisallowNulls`. This declaration could be in either aspect, or in
- another, ordering aspect:
-
- [source, java]
- ....
- aspect Ordering {
- declare precedence: CountEntry, DisallowNulls;
- }
-
- aspect DisallowNulls {
- pointcut allTypeMethods(Type obj): call(* *(..)) && args(obj, ..);
- before(Type obj): allTypeMethods(obj) {
- if (obj == null) throw new RuntimeException();
- }
- }
-
- aspect CountEntry {
- pointcut allTypeMethods(Type obj): call(* *(..)) && args(obj, ..);
- static int count = 0;
- before(): allTypeMethods(Type) {
- count++;
- }
- }
- ....
-
- ==== Various cycles
-
- It is an error for any aspect to be matched by more than one TypePattern
- in a single decare precedence, so:
-
- [source, java]
- ....
- declare precedence: A, B, A ; // error
- ....
-
- However, multiple declare precedence forms may legally have this kind of
- circularity. For example, each of these declare precedence is perfectly
- legal:
-
- [source, java]
- ....
- declare precedence: B, A;
- declare precedence: A, B;
- ....
-
- And a system in which both constraints are active may also be legal, so
- long as advice from `A` and `B` don't share a join point. So this is an
- idiom that can be used to enforce that `A` and `B` are strongly independent.
-
- ==== Applies to concrete aspects
-
- Consider the following library aspects:
-
- [source, java]
- ....
- abstract aspect Logging {
- abstract pointcut logged();
-
- before(): logged() {
- System.err.println("thisJoinPoint: " + thisJoinPoint);
- }
- }
-
- abstract aspect MyProfiling {
- abstract pointcut profiled();
-
- Object around(): profiled() {
- long beforeTime = System.currentTimeMillis();
- try {
- return proceed();
- } finally {
- long afterTime = System.currentTimeMillis();
- addToProfile(thisJoinPointStaticPart, afterTime - beforeTime);
- }
- }
-
- abstract void addToProfile(
- org.aspectj.JoinPoint.StaticPart jp,
- long elapsed
- );
- }
- ....
-
- In order to use either aspect, they must be extended with concrete
- aspects, say, MyLogging and MyProfiling. Because advice only applies
- from concrete aspects, the declare precedence form only matters when
- declaring precedence with concrete aspects. So
-
- [source, java]
- ....
- declare precedence: Logging, Profiling;
- ....
-
- has no effect, but both
-
- [source, java]
- ....
- declare precedence: MyLogging, MyProfiling;
- declare precedence: Logging+, Profiling+;
- ....
-
- are meaningful.
-
- === Statically determinable pointcuts
-
- Pointcuts that appear inside of `declare` forms have certain
- restrictions. Like other pointcuts, these pick out join points, but they
- do so in a way that is statically determinable.
-
- Consequently, such pointcuts may not include, directly or indirectly
- (through user-defined pointcut declarations) pointcuts that discriminate
- based on dynamic (runtime) context. Therefore, such pointcuts may not be
- defined in terms of
-
- * `cflow`
- * `cflowbelow`
- * `this`
- * `target`
- * `args`
- * `if`
-
- all of which can discriminate on runtime information.
-
- [[semantics-aspects]]
- == Aspects
-
- An aspect is a crosscutting type defined by the `aspect` declaration.
-
- === Aspect Declaration
-
- The `aspect` declaration is similar to the `class` declaration in that
- it defines a type and an implementation for that type. It differs in a
- number of ways:
-
- ==== Aspect implementation can cut across other types
-
- In addition to normal Java class declarations such as methods and
- fields, aspect declarations can include AspectJ declarations such as
- advice, pointcuts, and inter-type declarations. Thus, aspects contain
- implementation declarations that can can cut across other types
- (including those defined by other aspect declarations).
-
- ==== Aspects are not directly instantiated
-
- Aspects are not directly instantiated with a new expression, with
- cloning, or with serialization. Aspects may have one constructor
- definition, but if so it must be of a constructor taking no arguments
- and throwing no checked exceptions.
-
- ==== Nested aspects must be `static`
-
- Aspects may be defined either at the package level, or as a `static`
- nested aspect -- that is, a `static` member of a class, interface, or
- aspect. If it is not at the package level, the aspect _must_ be defined
- with the `static` keyword. Local and anonymous aspects are not allowed.
-
- === Aspect Extension
-
- To support abstraction and composition of crosscutting concerns, aspects
- can be extended in much the same way that classes can. Aspect extension
- adds some new rules, though.
-
- ==== Aspects may extend classes and implement interfaces
-
- An aspect, abstract or concrete, may extend a class and may implement a
- set of interfaces. Extending a class does not provide the ability to
- instantiate the aspect with a new expression: The aspect may still only
- define a null constructor.
-
- ==== Classes may not extend aspects
-
- It is an error for a class to extend or implement an aspect.
-
- ==== Aspects extending aspects
-
- Aspects may extend other aspects, in which case not only are fields and
- methods inherited but so are pointcuts. However, aspects may only extend
- abstract aspects. It is an error for a concrete aspect to extend another
- concrete aspect.
-
- === Aspect instantiation
-
- Unlike class expressions, aspects are not instantiated with `new`
- expressions. Rather, aspect instances are automatically created to cut
- across programs. A program can get a reference to an aspect instance
- using the static method `aspectOf(..)`.
-
- Because advice only runs in the context of an aspect instance, aspect
- instantiation indirectly controls when advice runs.
-
- The criteria used to determine how an aspect is instantiated is
- inherited from its parent aspect. If the aspect has no parent aspect,
- then by default the aspect is a singleton aspect. How an aspect is
- instantiated controls the form of the `aspectOf(..)` method defined on
- the concrete aspect class.
-
- ==== Singleton Aspects
-
- * `aspect Id { ... }`
- * `aspect Id issingleton() { ... }`
-
- By default (or by using the modifier `issingleton()`) an aspect has
- exactly one instance that cuts across the entire program. That instance
- is available at any time during program execution from the static method
- `aspectOf()` automatically defined on all concrete aspects -- so, in the
- above examples, `A.aspectOf()` will return ``A``'s instance. This aspect
- instance is created as the aspect's classfile is loaded.
-
- Because the an instance of the aspect exists at all join points in the
- running of a program (once its class is loaded), its advice will have a
- chance to run at all such join points.
-
- (In actuality, one instance of the aspect `A` is made for each version of
- the aspect `A`, so there will be one instantiation for each time `A` is
- loaded by a different classloader.)
-
- ==== Per-object aspects
-
- * `aspect Id perthis( Pointcut ) { ... }`
- * `aspect Id pertarget( Pointcut ) { ... }`
-
- If an aspect `A` is defined `perthis(Pointcut)`, then one object of type `A`
- is created for every object that is the executing object (i.e., `this`)
- at any of the join points picked out by `Pointcut`. The advice defined
- in `A` will run only at a join point where the currently executing object
- has been associated with an instance of `A`.
-
- Similarly, if an aspect `A` is defined `pertarget(Pointcut)`, then one
- object of type `A` is created for every object that is the target object
- of the join points picked out by `Pointcut`. The advice defined in `A`
- will run only at a join point where the target object has been
- associated with an instance of `A`.
-
- In either case, the static method call `A.aspectOf(Object)` can be used
- to get the aspect instance (of type `A`) registered with the object. Each
- aspect instance is created as early as possible, but not before reaching
- a join point picked out by `Pointcut` where there is no associated
- aspect of type `A`.
-
- Both `perthis` and `pertarget` aspects may be affected by code the
- AspectJ compiler controls, as discussed in the xref:implementation.adoc#implementation[Implementation Notes]
- appendix.
-
- ==== Per-control-flow aspects
-
- * `aspect Id percflow( Pointcut ) { ... }`
- * `aspect Id percflowbelow( Pointcut ) { ... }`
-
- If an aspect `A` is defined `percflow(Pointcut)` or
- `percflowbelow(Pointcut)`, then one object of type `A` is created for each
- flow of control of the join points picked out by `Pointcut`, either as
- the flow of control is entered, or below the flow of control,
- respectively. The advice defined in `A` may run at any join point in or
- under that control flow. During each such flow of control, the static
- method `A.aspectOf()` will return an object of type `A`. An instance of
- the aspect is created upon entry into each such control flow.
-
- ==== Aspect instantiation and advice
-
- All advice runs in the context of an aspect instance, but it is possible
- to write a piece of advice with a pointcut that picks out a join point
- that must occur before asopect instantiation. For example:
-
- [source, java]
- ....
- public class Client
- {
- public static void main(String[] args) {
- Client c = new Client();
- }
- }
-
- aspect Watchcall {
- pointcut myConstructor(): execution(new(..));
-
- before(): myConstructor() {
- System.err.println("Entering Constructor");
- }
- }
- ....
-
- The before advice should run before the execution of all constructors in
- the system. It must run in the context of an instance of the Watchcall
- aspect. The only way to get such an instance is to have Watchcall's
- default constructor execute. But before that executes, we need to run
- the before advice...
-
- There is no general way to detect these kinds of circularities at
- compile time. If advice runs before its aspect is instantiated, AspectJ
- will throw a
- xref:../api/org/aspectj/lang/NoAspectBoundException.html[`org.aspectj.lang.NoAspectBoundException`].
-
- === Aspect privilege
-
- * `privileged aspect Id { ... }`
-
- Code written in aspects is subject to the same access control rules as
- Java code when referring to members of classes or aspects. So, for
- example, code written in an aspect may not refer to members with default
- (package-protected) visibility unless the aspect is defined in the same
- package.
-
- While these restrictions are suitable for many aspects, there may be
- some aspects in which advice or inter-type members needs to access
- private or protected resources of other types. To allow this, aspects
- may be declared `privileged`. Code in priviliged aspects has access to
- all members, even private ones.
-
- [source, java]
- ....
- class C {
- private int i = 0;
- void incI(int x) { i = i+x; }
- }
-
- privileged aspect A {
- static final int MAX = 1000;
-
- before(int x, C c): call(void C.incI(int)) && target(c) && args(x) {
- if (c.i+x > MAX) throw new RuntimeException();
- }
- }
- ....
-
- In this case, if `A` had not been declared `privileged`, the field reference
- `c.i` would have resulted in an error signaled by the compiler.
-
- If a privileged aspect can access multiple versions of a particular
- member, then those that it could see if it were not privileged take
- precedence. For example, in the code
-
- [source, java]
- ....
- class C {
- private int i = 0;
- void foo() { }
- }
-
- privileged aspect A {
- private int C.i = 999;
-
- before(C c): call(void C.foo()) target(c) {
- System.out.println(c.i);
- }
- }
- ....
-
- ``A``'s private inter-type field `C.i`, initially bound to 999, will be
- referenced in the body of the advice in preference to ``C``'s privately
- declared field, since `A` would have access to its own inter-type
- fields even if it were not privileged.
-
- Note that a privileged aspect can access private inter-type declarations
- made by other aspects, since they are simply considered private members
- of that other aspect.
|