Skip to main content

AOP: Madness and Sanity

Posted by kgh on March 8, 2006 at 11:50 AM PST

When people ask me "what do you think of AOP?", I tend
to flinch, because the term AOP has come to be used to cover a
very wide range of different uses, some of which I think are
Totally Wonderful and some of which I think are Thoroughly Bad
Ideas. Here's a brief survey of the range and of my reactions.

Quick Overview of AOP


The core concept of Aspect Oriented Programming is that there are
common issues that apply across a range of otherwise unrelated
classes. In AOP, these common issues are often referred to as "cross
cutting" concerns because they cut across the normal inheritance
class structure. The goal of AOP is to allow cross cutting concerns
to be addressed by applying some common logic (or "aspects")
to a set of classes, without having to individually modify the source
code for each class.


The idea of wrapping extra functionality around target classes will
be very familiar to Java EE developers. Java EE containers typically
add extra functionality and semantics to target classes by intercepting
incoming calls to target objects. For example, an EJB container can add
transactional semantics to a target class.
(Yes, J2EE has been using AOP all along!)

Java Language Principles


The Java language has been designed and evolved with certain
specific underlying principles in mind.


We have put great emphasis on source code readability. As far as
possible we have tried to follow the principle that "what you
get is what you see". We have worked to maintain a clear and
consistent base semantic model and to avoid language constructs that
might undermine source code readability.
People should be able to quickly read Java source code and then
reliably understand what that Java source code does. That meaning
should be independent of the particular compilers, tools, or runtime
environments that are used.


Now, we aren't ivory tower philosophers. We want a Java language
that is productive for writing
and maintaining large applications. So there will always be a trade-off
between principles and pragmatics. However, in looking at the success
and popularity of the Java language, much of it
does seem to derive from these core principles. They seem
to have worked well and to be worth maintaining and protecting.

The Good: Container Based AOP


One use of AOP is based on running objects within containers.
These objects can only be accessed via their container. The container
can intercept incoming calls and add additional semantics. An example
of this approach is the way that Java EE containers can add
transactional semantics to EJB components by intercepting incoming
calls.


In general this use of AOP seems fairly benign. The source code
within the target objects is all behaving exactly as a developer
should expect. Developers do need to be aware that they are accessing
the objects indirectly and that the container may perform extra
tasks. However the idea of operating through a level of indirection
is a fairly normal Java concept.


Now, within this general use case, there are probably a range of
good and bad uses. Uses of container intervention such as
adding transactional semantics in EJB, or adding logging,
or adding extra security checks seem very reasonable.

The Bad: Modifying Language Semantics


Some uses of AOP rely on using runtime mechanisms to modify
semantics that are specified by the Java Language Specification
(JLS). For example, side files might specify that various
modifications are to be made to target classes to invoke additional
code when various operations are performed. Some uses of AOP allow
extra code to be invoked when fields are accessed or when methods are
called.


The JLS carefully specifies the precise semantics of the Java
language. For example, it precisely specifies the behavior that
occurs when fields are accessed and it precisely specifies the rules
by which one method may call another, including the cases when the
call may proceed through other code as well as the cases when it must
proceed directly from caller to callee.
Developers should be able to rely on these semantics when they are
reading source code.


To take an extreme example, if a developer has written a statement
"a = b;" then they should be able to
rely on the value of the field "b" being
assigned to the field "a",
with no side effects. Some AOP toolkits allow arbitrary extra
code to be executed when this statement runs, so that there can be
arbitrary extra side effects as part of this assignment or even so
that an arbitrary different value may be assigned
to "a". Yikes!
Similarly, some uses of AOP permit extra code to be inserted on a
method call from one method to another, even when the JLS clearly
specifies that that method call will occur directly. This enables
arbitrary extra code to be executed, with potentially arbitrary side
effects, in situations where a Java developer should expect there
will be no side effects.


This is the kind of arbitrary intervention that I think quickly
races across the boundary line to insanity. (And I think most
serious AOP proponents would agree.) I don't want the fundamental
behavior of a chunk of source code being changed by someone magically
reaching in and changing the code's meaning from the outside. That
seems like a really bad idea!


The core problem with these approaches is that if someone relies on
techniques like these in writing their code, then other developers
will no longer be able to understand that source code, because it
has been developed to rely on side effects that are both at odds with
the Java language definition and which are magically applied from
somewhere outside the source file. You should not need to have global
knowledge of a whole environment in order to understand some local
fragments of code.


Overall, using AOP to modify the meaning of the Java language
seems like a really bad way to go.

The New: Java Language Annotations


In J2SE 5.0, the concept of annotations was added to the
Java language. In many ways, annotations were designed to meet
similar goals to AOP. The concept of annotations is that developers
may apply special markers to target method, fields, or classes in
order to designate that they should be processed specially by tools
and/or runtime libraries.


Now, annotations are not allowed to modify the behavior defined
in the Java Language Specification. Within a source file that contains
annotations, at runtime all the source code must execute exactly as
defined by the JLS. That was a deliberate choice - we don't want
annotations to become a way of defining arbitrary magic behavior within
normal Java code. However, the annotations may cause changes to the
overall behavior of the program, for example by directing a container
to apply special behavior when forwarding calls into a target class.


For me, one big benefit of annotations over classic AOP
is that annotations
are clearly visible in source code. So if you are reading an
application you can clearly see which specific annotations are
affecting the code. That readability advantage normally
seems to outweigh the AOP advantage of being able to apply new
semantics silently without modifying the affected class.


My sense is that many use cases that were initially considered for
use with AOP can now be better solved by the use of annotations.
That is part of what is happening with the extensive use of
annotations in
Java EE 5.

Conclusion


The use of container based AOP seems to provide a reasonable
mechanism for supporting AOP "cross cutting concerns" within
Java environments, without breaking developers' expectations of
how Java source code behaves. Similarly, Java language annotations
appear to provide a convenient mechanism for supporting some of
the use cases originally targeted by AOP.


However, I think AOP quickly drifts from good to evil when it
starts being used to mutate the meaning of source code or to
otherwise modify what people can and should expect from Java
source code.


So, yes, I am a fan of AOP. But of a sane, restrained AOP!

  - Graham

Related Topics >>