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 forms of introduction.
Each introduction form is a member of the aspect defining it, but defines a new member of another type.
A method introduction looks like
Modifiers Type TypePattern . Id(Formals) { Body } |
abstract Modifiers Type TypePattern . Id(Formals); |
The effect of such an introduction is to make all the types in TypePattern support the new method. Interfaces in TypePattern will support the new method as well, even if the method is neither public nor abstract.
A constructor introduction looks like
Modifiers TypePattern.new(Formals) { Body } |
The effect of such an introduction is to make all the types in TypePattern support the new constructor. It is an error for TypePattern to include interface types in this case.
Any occurrence of the identifier this refers to the target type from the TypePattern rather than to the aspect type.
A field introduction looks like one of
Modifiers Type TypePattern.Id = Expression; |
Modifiers Type TypePattern.Id; |
The effect of such an introduction is to make all the types in TypePattern support the new field. Interfaces in TypePattern will support the new field as well, even if the method is neither public, nor static, nor final. The keyword this, if present in the initializer of a non-static field, refers to the target type from the TypePattern rather than to the aspect type.
Members may be introduced with access modifiers public or private, or the default package-protected (protected introduction is not supported).
The access modifier applies in relation to the aspect, not in relation to the target type. So a member that is privately introduced is visible only from code that is defined within the aspect. One that is package-protectedly introduced is visible only from code that is defined within the aspect's package.
Note that privately introducing a method (which AspectJ supports) is very different from introducing a private method (which AspectJ previously supported). AspectJ does not allow the introduction of the private method "void writeObject(ObjectOutputStream)" required to implement the interface java.io.Serializable.
Introduction may cause conflicts among introduced members and between introduced members and defined members.
Assuming otherPackage is not the package defining the aspect A, the code
aspect A { private Registry otherPackage.*.r; public void otherPackage.*.register(Registry r) { r.register(this); this.r = r; } }
adds a field "r" to every type in otherPackage. This field is only accessible from the code inside of aspect A. The aspect also adds a "register" method to every type in otherPackage. This method can is accessible everywhere.
If any type in otherPackage 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 introduced "r".
If any type in otherPackage defines a public field "r", there is a conflict: The expression
this.r = r
is an error, since it is ambiguous whether the introduced "r" or the public "r" should be used.
If any type in otherPackage defines any 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. This means that fields are not conflicting until there is an ambiguous reference to the fields, but methods and constructors are conflicting if they are defined such that there could be an ambiguity.
An aspect may introduce a superinterface or superclass onto a type, with the declarations
declare parents: TypePattern extends TypeList; |
declare parents: TypePattern implements TypeList; |
For example, if an aspect wished to make a particular class runnable, it might add an appropriate void run() method, but it should also change the type of the class to specify that it fulfills the Runnable interface:
aspect A { declare parents: SomeClass implements Runnable; void SomeClass.run() { ... } }
Through the use of introduction, 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, the order of super-interface instantiation is observable. We fix this order with the following three properties: A supertype is initialized before a subtype, that initialized code runs only once, and initializers for supertypes run in left-to-right order. Consider the following hierarchy where {Object, C, D, E} are classes, {M, N, O, P, Q} are interfaces.
Object M O \ / \ / C N Q \ / / D P \ / E
when a new E is instantiated, the initializers run in this order:
Object M C O N D Q P E
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.
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: TypePattern: Pointcut; |
For example, the aspect
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
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 effects Java's static exception checking mechanism.
Pointcuts that appear inside of declare forms have certain restrictions. Unlike regular pointcuts, they do not pick out join points, but rather places in a program that might correspond to join points if the program were run.
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 |