« December 2005 | Main | February 2006 »
January 30, 2006
What's on your bookshelf?
One of the things I like to do when I visit a client is to take a look at the books on their bookshelf (and those of the other members of their team). I do this for three reasons.
Firstly I'm always on the lookout for good books that I haven't read yet. If I see an interesting title that I've not read, I'll ask if it's any good.
Secondly, it tells me a lot about the background and mindset of the person I'm working with: you can often tell whether someone is interested primarily in the web-tier, data-tier, or domain modelling; you can tell what language they're likely to be familiar with when you're talking to them, and what technical background you can assume; you can tell whether they like abstract thinking or concrete how-tos. You can learn a lot from a few books...
The third reason is to guage how well different technologies are penetrating the marketplace. Seeing lots of "Pragmatic Ruby" books? That tells you that Ruby has made it onto the corporate developer's radar (it doesn't tell you whether or not Ruby is actually being used of course - you'll need to ask some more questions to find that out).
If I see books such as "Professional Java Development with the Spring Framework", "Pro Spring", and "Domain Driven Design" on the bookshelf that gives me a good feeling. The purpose of this entry is to highlight that there's a new book on the block I'm going to be looking out for: "POJOs in Action" by Chris Richardson. With a title like that, you may be unclear exactly what you're going to get. Rest assured, this is not a "lightweight" book - it has plenty of detailed discussion. The book is especially valuable for two reasons (maybe more!, but I'll pick on two here):
- It compares and contrasts different implementation techniques and approaches side by side. Want to see how dynamic paged queries pan out in iBATIS vs JDO vs Hibernate for example? Go to chapter 11. Same for detached object support, optimistic locking and more. There are also discussions on how to arrange business logic and the implications of EJB 3 ("if you are already using lightweight technologies such as Spring, Hibernate, and JDO you will be disappointed"). Too often in the software industry we tend to see things in black and white. The truth is that there are many shades of gray. When you see things put side by side and compared, you are better able to evaluate the strengths and weaknesses of different approaches, and the likely consequences of your architectural decisions.
- Chris encourages the reader to think for themselves, and not just rely on fashion and propaganda: "If we blindly used POJOs and lightweight frameworks, we would be repeating the mistake the enterprise Java community made with EJBs". (So here are the arguments for and against...).
I've only read part 1 and part 3 so far, but I've seen enough already to trigger that knowing smile if I see a copy of this on the bookshelf...
Posted by adrian at 07:52 PM [permalink] | Comments (1)
January 18, 2006
Typed Advice in Spring 2.0 (M2)
I spent a few days last week working on argument binding in advice for the new aop schema and @AspectJ support in Spring 2.0. In this article, I'll explain how the new support works and how you can use it.
First some background for those of you who haven't been following along with developments in Spring 2.0. Spring 2.0 supports schema-based configuration as well as DTD based. Schema gives better validation, a better experience in the IDE (in terms of code completion etc.), and a more concise and readable configuration file. It's important to state that however you specify your bean definitions (DTD-based, schema-based, script-based,...) everything comes down to the same bean metadata model at runtime so the different styles are compatible and interchangeable. For AOP support, we have the schema http://www.springframework.org/schema/aop/spring-aop.xsd.
This is what a skeletal config file looks like using the new schema support:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans>
The various AOP-related definitions are placed inside an <aop:config> element. Here's a cut-down example of a simple aspect from the test suite:
<aop:config> <aop:aspect id="beforeAdviceBindingTests" ref="testAspect"> <aop:advice kind="before" method="oneIntArg" pointcut="execution(* setAge(..)) and args(age)" /> </aop:aspect> </aop:config> <bean id="testAspect" class="org.springframework.aop.aspectj.AdviceBindingTestAspect"/>
I'm declaring an aspect called "beforeAdviceBindingTests", the aspect instance (holding the state for the aspect) is the bean "testAspect". This is just a regular bean, that can be configured by Spring in all the usual ways. The implementing class (in this case, AdviceBindingTestAspect") is just a regular POJO. The aspect contains only one piece of advice, which is "before" advice (you can specify one of the five AspectJ advice kinds in the "kind" attribute: before, afterReturning, afterThrowing, after, or around). The advice body is implemented by the method "oneIntArg", and the associated pointcut expression matches the execution of any method named "setAge" taking a single int argument. The argument is bound to a parameter named "age".
What this all boils down to, is that any time a matching method executes on a Spring bean in the application context, the "oneIntArg" method will be called on the "testAspect" bean, passing the age as a parameter.
Note that we've gained two important things here: the aspect class and advice method do not need to implement any special interfaces, and we have typed advice. By typed advice, I mean that the advice signature declares the parameters it needs, and gets passed those parameters only, preserving type information. Contrast this to typical AOP framework approach of always passing an Object[] from which the advice body must pick-and-choose and cast as needed.
To complete this introductory example, here's the aspect class:
public class AdviceBindingTestAspect { public void oneIntArg(int age) { // whatever we like here, using "age" as needed } }
The combination of the class declaration and XML definitions has the same effect as the following AspectJ aspect declaration:
public aspect AdviceBindingTestAspect { before(int age) : execution(* setAge(..)) && args(age) { // whatever we like here, using "age" as needed } }
So let's turn our attention to the main subject of this article: how does the binding support for typed advice work and how can you use it?
Determining argument names and types
Look at the AspectJ aspect definition I gave you above for a moment. The advice declares a parameter, "age" of type int, and in the pointcut expression, "age" is bound by the "args" clause. The expression "args(age)" actually does two things: it restricts matching to join points where there is a single argument of type "int" (the type of "age"), and it binds the argument value to the "age" parameter.
In the Spring 2.0 version, we have exactly the same pointcut expression, but the method declaring the "age" parameter is defined elsewhere - in the AdviceBindingTestAspect class. Java reflection lets us down at this point. All we can determine is that the "oneIntArg" method takes a single parameter of type "int": we can't determine the parameter names. In this case since the advice method takes only one argument, and the pointcut binds only one argument, you could deduce that the int parameter must corresponding to "age", but when binding multiple arguments this may not always be possible. Matching argument names to argument types is critical to the matching process, and of course to passing arguments to the advice method itself.
Spring 2.0 resolves this issue by supporting a number of "ParameterNameDiscoverers". ParameterNameDiscoverer is a strategy interface, and multiple implementations are arranged in a ChainOfResponsibility. Spring attempts to discover argument names by applying the following strategies in turn:
- If the argument names are explicitly specified, then the
given argument names are used. Argument names can be specified in
one of two ways:
- They can be specified using the "arg-names" attribute of the <:aop:advice> element. In the example we've been working with so far, we could have specified arg-names="age" for example. The string value of this attribute takes a comma-delimited list of argument names.
- If using the @AspectJ notation to define aspects, then the advice annotations (@Before, @AfterReturning, @AfterThrowing etc.) all support an optional "argNames" attribute. Again, this takes a comma-delimited list of argument names.
- If argument names have not been explicitly specified, then we attempt to recover the argument names from the class file of the class defining the method. This is possible so long as the class was compiled with debug information (at a minimum, -g:vars). From the user perspective this is certainly the easiest and most natural way of obtaining argument names. Compiling with debug information makes it much easier to diagnose problems in running code (stack traces can include source file and line number information for example). If you are worried about overhead, then -g:vars will ensure that the class files contain the minimum amount of debug information (LocalVariableTables) that Spring needs for this mechanism to work. What are the downsides of -g:vars? Your class files will be very slightly larger (this will not be an issue in the vast majority of cases), certain optimisations that the compiler would otherwise make (pertaining to the removal of unused local variables for example) will not be made, and reverse engineering of your application is slightly easier. If you are worried about the sound of lost optimisations, run some performance tests - you may well find the effect is not noticeable.
- If local variable table information is not available for the method, then Spring tries to deduce the argument names from the pointcut expression and the signature of the method. Consider a method that takes 2 parameters, one of a reference type, and one of a primitive type. Given a pointcut expression that binds one variable using "this()" and one variable using "args()" we know that the primitive argument must be the one bound by args, because "this" must be a reference type. In the AspectJ pointcut language all of the binding pointcut designators have two forms: one that takes a type name, and one that takes a variable name. For example, I can write "this(Float)" which simply matches any join point where "this" is an instance of Float, or I can write "this(f)" which matches any join point where "this" is an instance of the type of "f", and binds the value to the "f" argument. To find out what the variables in the pointcut expression are, Spring uses the simple assumption that any string inside a binding pointcut expression that is a valid Java identifier name and starts with a lower case letter is a variable, and anything else is a type.
Using typed advice
Let's look at some of the features available.
Information about the current join point
Firstly, if any advice method has a first argument of type org.aspectj.lang.JoinPoint then a JoinPoint instance representing the current join point will be passed to the advice method. This provides equivalent functionality to the use of 'thisJoinPoint' within AspectJ advice. The JoinPoint object can be useful when writing generic advice that applies across a wide range of join points since it can give you reflective information about the join point, such as the method being executed under the join point. For around advice, you must use the type org.aspectj.lang.ProceedingJoinPoint instead of JoinPoint - this subtype of JoinPoint provides the critical "proceed" method that you must call when you wish to proceed with the computation under the join point.
As an alternative to JoinPoint, a first parameter of type org.aspectj.lang.JoinPoint.StaticPart will be passed an instance of JoinPoint.StaticPart instead of JoinPoint. This provides only statically available information about the join point (method and signature for example) but not the arguments and target object for the invocation being advised. Use of JoinPoint.StaticPart instead of JoinPoint may allow optimisations inside the AOP framework in the future. For now it is no more efficient than JoinPoint when using Spring AOP.
Here's a simple example using JoinPoint from the test suite;
XML fragment for advice definition
<aop:advice kind="afterReturning" method="needsJoinPoint" pointcut="execution(* getAge())" />
Advice method declaration:
public void needsJoinPoint(JoinPoint tjp) { this.collaborator.needsJoinPoint(tjp.getSignature().getName()); }
And the test case that verifies a suitable JoinPoint object is passed to the advice method:
public void testNeedsJoinPoint() { mockCollaborator.needsJoinPoint("getAge"); mockControl.replay(); testBean.getAge(); mockControl.verify(); }
(I'll write more about the approach to testing aspects I've taken here in a separate post....)
Method execution arguments
You've already seen an example of using "args" to bind the arguments at the method execution join point (remember than Spring AOP only supports method execution join points). You can read up on "args" (and all of the other AspectJ pointcut designators" in the AspectJ programming guide at the AspectJ website. "args" restricts matching only to the execution join points for methods that have a corresponding number of arguments, and can bind one or more of those arguments.
Here are some very simple examples:
- args()
- matches the execution of methods that take no arguments (so clearly there is nothing to bind here
- args(..)
- matches the execution of methods that take zero or more arguments (but still performs no binding)
- args(x,..)
- matches methods with one or more parameters, that have a first parameter of the type of "x", and bind the argument value to "x"
- args(x,*,*,s,..)
- a more contrived example. Matches the execution of methods that take at least 4 parameters. The first parameter must be of the type of "x", and the argument value will be bound to "x". The second and third parameters may be anything, but the fourth parameter must be of the type of "s" and the argument value will be bound to "s".
By combining named arguments, "*" (which matches a single argument of any type), and ".." (which matches zero or more arguments of any type) you can expose exactly the context information you need to the advice that executes at the join point.
Method return values
If your advice needs access to the return value of a method execution join point you can use afterReturning advice and bind the returned value using the "returning" attribute.
Here's a simple example:
<aop:advice kind="afterReturning" method="afterGettingName" returning="name" pointcut="execution(* getName())" />
Couple this with the method definition
public void afterGettingName(String name) { // advice body }
and "afterGettingName" will be invoked on every successful return from an invocation of "getName" with the return value passed in as the parameter 'name'.
Note that the "returning" clause is doing two things here: it is restricting matching to only those method execution join points that return an instanceof String (the type of 'name'), and it is binding the return value to the parameter name.
If you use the returning attribute with a type name (rather than a variable name) it restricts matching to only those join points where the return value is an instance of the given type (but does not pass the actual return value to the advice).
Thrown exceptions
afterThrowing advice runs when an advised join point exits by throwing an exception. Often it's useful to have access to the actual exception that was thrown in the advice. You can use the "throwing" attribute to do this.
Consider the following aspect definition:
<aop:aspect id="legacyDaoExceptionTranslator" ref="exceptionTranslator"> <aop:pointcut name="legacyDaoMethodExecution" expression="execution(* org.xzy.dao..*.*(..))"/> <aop:advice kind="afterThrowing" method="translateException" pointcut-ref="legacyDaoMethodExecution" throwing="hibEx" /> </aop:aspect>
and the accompanying method definition
public void translateException(HibernateException hibEx) { throw SessionFactoryUtils.convertHibernateAccessException(hibEx); }
The advice will run whenever a legacy DAO method (written without Spring ;) ) throws a HibernateException, passing the thrown exception to the advice method where it can be converted to a Spring DataAccessException and rethrown.
(yes, that is all the code you need :) )
The executing instance
Consider the execution of a method that would result from a call to "testBean.getAge()". What if my advice needs access to the testBean instance on which the method executes? The pointcut designators "this" and "target" provide access to this instance. In AspectJ, both this and target are bound to the executing instance at the join point. In Spring AOP an advised object will be proxied. Using the "this" pointcut designator will bind the proxy object (the value of "this" for the object instance that represents the bean), and using the "target" pointcut designator will bind the true target.
For example:
<aop:advice kind="afterReturning" method="setAgeOnATestBean" pointcut="execution(* setAge(..)) and args(age) and this(bean)" arg-names="age,bean" />
This advice passes with the following test case:
public void testOneIntAndOneObjectArgs() { mockCollaborator.setAgeOnATestBean(5,testBean); mockControl.replay(); testBean.setAge(5); mockControl.verify(); }
If we use "target" instead of "this": "execution(* setAge(..)) and args(age) and target(bean)" the test would fail. The mock collaborator would be expecting "testBean" (which will be an AOP proxy) to be passed as an argument, but because we used "target", the proxy target object will be passed instead. This is demonstrated by the following test case:
public void testTargetBinding() { Advised advisedObject = (Advised) testBean; TestBean target = (TestBean) advisedObject.getTargetSource().getTarget(); mockCollaborator.setAgeOnATestBeanTarget(5,target); mockControl.replay(); testBean.setAge(5); mockControl.verify(); }
Annotations
The AspectJ pointcut language provides rich support for matching and binding annotations. For Spring applications, the two most useful pointcut designators are probably "@annotation" and "@within".
"@annotation" matches when the subject of the join point has an annotation of the given type. For Spring, this means when the method being advised has an annotation of the given type. For example, the advice:
<aop:advice kind="before" method="beforeTxMethodExecution" pointcut="@annotation(tx)" />
when coupled with the signature of the "beforeTxMethodExecution" method (in order to determine the type of annotation that "tx" will match):
public void beforeTxMethodExecution(Transactional tx) { if (tx.readOnly()) { ... }
will match the execution of any bean method that has the "@Transactional" annotation. Remember that Spring is implicitly limited to execution join points only. The corresponding pointcut when using the full AspectJ language would be: "execution(* *(..)) && @annotation(tx)".
"@within" matches any join point within a type that has a given annotation. So to match execution of any bean methods within a type that has the "@Transactional" annotation we could write:
<aop:advice kind="before" method="beforeTxMethodExecution" pointcut="@within(tx)" />
The @AspectJ style
All of the examples I've shown you so far have used the XML schema form of aspect definition. Spring 2.0 can also make use of aspects written using the "@AspectJ style". It's out of scope for this blog entry to give a full tutorial on @AspectJ, but here's a quick example of how it works...
The first thing that you need to do is enable auto-proxying from @AspectJ aspects in your application context. This is trivial using the new schema support for AOP:
<aop:aspectj-autoproxy/>
Then if we define any beans in the application context that are actually @AspectJ aspects (annotated using the AspectJ annotations), Spring will automatically use those beans to configure AOP proxies. The transactional method execution example, written as an @AspectJ aspect, would look like this:
@Aspect AnnotationDrivenTransactionManager { @Before("execution(* *(..)) && @annotation(tx)") public void beforeTxMethodExecution(Transactional tx) { // ... } }
To use this aspect, we would simply add a definition such as the following to the configuration.
<bean class="...AnnotationDrivenTransactionManager" >
of course, all of the usual dependency injection capabilities are avaible for configuring the aspect.
One nice advantage of using this style of aspect definition, is that you can switch from using Spring AOP proxying to full AspectJ weaving simply by removing the "aspectj-autoproxy" element from your configuration, and compiling (or binary weaving) your application using AspectJ instead. If you are dependency injecting those aspects, then either their bean definitions have to be changed slighty to obtain the aspect instance for injection via an AtAspectJFactoryBean, or you could simply annotate the aspects as @Configurable and use the spring-aspects.jar aspect library.
Posted by adrian at 10:05 AM [permalink] | Comments (6)
January 10, 2006
AspectJ and AJDT going strong...
Matt Chapman pulled together the download statistics for AspectJ and AJDT for the month of December 2005:
- AspectJ - 7,400
- AJDT - 20,000
It's great to see the numbers looking so healthy, and interesting to note just how many folks are getting AspectJ through the integrated Eclipse tooling (AJDT).
Posted by adrian at 02:36 PM [permalink] | Comments (0)
January 09, 2006
Eclipse AspectJ book examples now packaged for Eclipse 3.1 & AspectJ 5
Long overdue, but I've finally repackaged all of the examples from the Eclipse AspectJ book for easy installation on Eclipse 3.1 and the latest AJDT. Here's the download link and installation instructions.
Since the book was published, AJDT has introduced the Cross References View. If you are following along with the text, relationship information (such as "advises" and "advised by") is now shown in the Cross References View and not in the outline view. To run the junit tests for any project, simply select the testsrc folder in the package explorer and "run as... junit test" from the context menu. To run the main Simple Insurance application, select insurance.ui.SimpleInsuranceApp in the package explorer and "run as... SWT application" from the context menu. If you get an error about a missing dll, you're probably running it as a "Java application", and not as an "SWT application".
This version of the download includes all of the needed dependencies, bar MySQL which you will still need to download and install yourself to follow along with all of the Hibernate related examples.
Posted by adrian at 10:44 AM [permalink] | Comments (7)