by Alexandre Vasseur, Jonas Bonér, and Joakim Dahlstedt
Originally published on BEA Dev2Dev August 2005
The previous article introduced you to aspect-oriented programming and the concept of separation of concerns to enhance software modularity with the help of the aspect construct, and how it is used as a complement to object-oriented programming. The aspect represents the unit of modularity, and is composed of pointcuts (the where), advice (the what), and inter-type declarations (that complement the object model in this new dimension). The concerns are woven into the application using different techniques, the most common, and widely adopted in today's Java landscape being bytecode instrumentation, as implemented in AspectWerkz and AspectJ (since 1.1).
Nevertheless, several shortcomings appear as consequences to this way of implementing AOP, as detailed in the first article of this series. Bytecode instrumentation is expensive despite a lot of ongoing work in this area (including the JVMTI/JSR-163 instrumentation agent specification in Java 5 and efficient bytecode manipulation libraries like ObjectWeb ASM). Using bytecode instrumentation to implement AOP weavers also proves to be incomplete, for example with reflective method calls or field
set not matched by pointcuts without very specific and inefficient solutions. In general, all bytecode instrumentation-based products suffer from several problems tied to the bytecode instrumentation technique, and the problems will increase as this technique gains popularity.
All these shortcomings have led us—the JRockit team—to propose JVM support for AOP. The goal is to implement the widest set of current AOP semantics, while not locking the JVM with particular language details and programming model targeted by a specific aspect-oriented framework.
This article guides you through the proposed API, with concrete code samples, and then describes the benefits and discusses future directions.
Let's quickly revise the technical motivation behind introducing JVM support for implementing AOP.
JVM weaving is the natural answer to most of the issues discussed above. To understand why, we will look at a couple examples that show that the JVM is already doing most of the work involved in weaving: When a class loads, the JVM reads the bytecode to build up the data needed to serve the
java.lang.reflect.* API. Another example is method dispatching. Modern JVMs compile the bytecode of methods or code blocks to more advanced and efficient constructs and execution flows (doing code inlining where applicable). Due to the HotSwap API requirements, the JRockit JVM (and probably other JVMs too) also keeps track of which method calls which other method, so that a method body can still be hotswapped in all expected places—inlined or not—if its defining class is redefined at runtime.
As a consequence, instead of changing the bytecode to weave in an advice invocation—say, before a specific method call—the JVM could actually have knowledge about it and simply do a dispatch to the advice at any matching join point prior to dispatching to the actual method.
Because bytecode would be untouched, you can expect immediate advantages such as:
This is quite different from well-known C-level events that have been defined in the JVMDI specification such as
JVMDI_EVENT_FIELD_ACCESS. In the JVMDI case, first one would have to deal with the C-level API, which makes it complex for most developers and fragile or complex to distribute; and second, the specification does not provide a fine-grained join point matching mechanism but actually requires you to subscribe to all such events. This still incurs an undeniable overhead, therefore the D(ebug) in "JVMDI."
We'd like to give you a taste of how we include JVM support for AOP. The key ingredient is that we expose action dispatch and subscription (described below) at the Java API level. You can then write code such as the following:
Weaver w = WeaverFactory.getWeaver(); Method staticActionMethod = SimpleAction.class.getDeclaredMethod( "simpleStaticAction", new Class//no arguments ); MethodSubscription ms = new MethodSubscription( /* where to match*/, InsertionType.BEFORE, staticActionMethod ); w.addSubscription(ms);
As you can see, we provide an accessible JVM API that can be used to implement the more traditional AOP approaches. This provides great flexibility in solving the traditional AOP implementation problems noted before, and opens the door for additional uses. The following sections examine this API in more detail.