« January 2006 | Main | March 2006 »
February 23, 2006
Tips for using Eclipse effectively
In Germany they have an entire magazine devoted to Eclipse, called (imaginatively ;) "Eclipse Magazin"". I don't speak (or read) German, but I have to say the content always looks really good, and they've had some excellent coverage of AspectJ and AJDT in past issues. The magazine has a guest column, "MyEclipse" in which various users of Eclipse describe their favourite hints and tips for working with the IDE.
It was my turn to write the column this month.
I have to say, I enjoyed writing the piece more than I thought I would - amongst other things it made we really think about how I actually do use Eclipse, and whether I'm using it as effectively as possible. The original column can be found here. With permission, I'm making an English translation (strictly, the English original!) available here for those that don't read German. If you have any favourite Eclipse tips that I don't know about, please do let me know!
My Eclipse
I’ve seen a lot of different people using Eclipse over the last few years. Nearly every one of them was using Eclipse pretty much in its default configuration. My Eclipse looks quite different to that, and in this article I’ll walk you through a collection of hints and tips for getting the most out of the IDE for Java development.
I’ll show you how to get organized when working with multiple workspaces, multiple projects, and lots of jar files. I’ll show you how to configure the IDE to help you write better code, and how to organize the Java perspective for maximum efficiency. As you might expect, I’ll also give you a couple of hints and tips for working with AspectJ!
Getting Organized
Using multiple workspaces
I tend to have multiple workspaces on the go at any one time (about 8 at present having just done a quick check). I use different workspaces for each major project (or version of a project) that I’m working with. For example, right now I’ve got workspaces for AspectJ 1.5.1 development, the AspectJ 1.5.0 release branch, Spring development, a couple of demonstration workspaces I use when giving talks, a workspace I use when giving training courses, and so on. It’s not uncommon for me to have 2 or 3 eclipses open at the same time.
The first practical question becomes knowing which eclipse is which when looking at the task bar or selecting between open windows using Alt-Tab. I always launch eclipse from the command-line using a script. Passing the “-showlocation” flag adds the workspace location to the windows title bar so that you can easily see which window is for which workspace. I normally start Eclipse with a little extra memory too (I find it works a little snappier that way). A good default for launching Eclipse if you’re working with sizeable projects would be something like this:
eclipse.exe –showlocation –vmargs –Xmx512m > /dev/null 2>&1
(I use cygwin, and I find that Eclipse tends to dump nonsense to the console that I don’t want to see, hence the redirect).
Another problem with using multiple workspaces is keeping your preferences and settings up to date across all of them. There is no need to configure each workspace individually every time you create a new one! Get one workspace set up exactly how you like it, and then select File -> Export -> Preferences. This will create a file that contains all of your preferences settings that you can keep in (e.g.) your home directory. Whenever you create a fresh workspace, use File -> Import -> Preferences and you’re good to go.
(By the way, if you're adding plugins to the base eclipse set, see Colin Sampaleanu's excellent blog entry on how best to manage that).
Coping with lots of projects inside a workspace:
It’s also not uncommon for me to have a lot of projects inside a workspace (one project per module). This is where working sets can make a big difference. Take a look at how the package explorer shows my core spring training workspace by default:
It’s just a jumbled mess of projects. Working sets to the rescue! From the drop down menu at the top right-hand corner of the Package Explorer view click on “Select Working Sets…”. From here you can define logical groupings of projects. With your groupings defined, select “Show -> Working Sets” from the drop-down menu. Now the Package Explorer will look something like this:
Much better!
Sorting out the clutter inside a project:
When looking at an individual project inside the package explorer, all of the jar files on the project build path show up by default. Most of the time I find these just get in the way. Here’s how my spring development workspace looks with the jar files showing:
The list of jars goes on even further, and worse, there are additional folders that I do care about at the end of the list. Select “Filters…” from the Package Explorer drop-down menu and define a new name filter matching “*.jar”. This will hide all of these jars from view making it much easier to find what you really want.
Organizing jar files:
On the subject of jar files, if you have common groups of jar files it can be very effective to define them as “User Libraries” that can be named and added to a build path as a unit. You’ll find the settings page to define user libraries under “Windows -> Preferences…” and then select Java -> Build Path -> User Libraries.
Sharing projects with others:
To make projects easy to share with others (for example, checking into a shared CVS repository) make the maximum use of variables. Right next to the option to define User Libraries, you’ll find the option to define Classpath Variables. When adding an external library to a build path of a shared project, always define a classpath variable that points to the containing directory on your local machine (or just the jar file itself). Then you “extend” the variable when defining a build path entry. This way each user can define the variable appropriately for their own local environment, but the project settings can still be shared in the repository.
Using Eclipse for Java Development
Helping Eclipse to help you write better code:
I used to use a small editor font in order to see the maximum amount of code possible inside the Java editor window. A while ago I changed tactics. Now I use a larger font than normal – 14 pt bold. As a consequence, I can see a lot less code in the editor at a time. This helps me write better code.
Let me explain.
It’s natural to want to see a whole method in one go. Seeing less code on the screen at a time has the subtle effect of making me write shorter, clearer methods. It’s also easier to sit back and reflect on code, and easier to pair program and work with others. Give it a try and see if it works for you too.
Organising the Java Perspective:
It used to be that I was never satisfied with the width I’d set for the Package Explorer and JUnit views. Too narrow and I couldn’t see the package and type names properly, or the stack traces in a test failure. Too wide and the editor window would get crushed between the Package Explorer on the left, and the Outline View on the right. Especially if you follow my suggestion of using a bigger font this can be a real pain.
I found the answer in a combination of fast views and in-place views. Now my eclipse dedicates as much real estate as possible to the source code. I have no views on the right-hand side, and no views on the left-hand side. The views I typically work with are the JUnit View, Type Hierarchy View, Package Explorer, and Javadoc View. Open these views, right-click in the title bar, and select “Fast View”. By default fast views dock at the bottom of the screen, but it’s not as convenient to move a mouse up and down to open them as it is to left-to-right. Select the fast view bar and choose “Dock on… left”. You have now reclaimed all of the space on the left-hand side that these views used to take, and your perspective should look something like this:
Another hidden benefit of this mode is that you can make the views wider than they were before, so that you can see the content properly (or even better for e.g. JUnit and Search, set the orientation to "horizontal" from the context menu). You can also set the widths of each view individually (JUnit wider than the Package Explorer for example, which is a significant improvement). If you don’t like having to move the mouse across to open a view, I’ll discuss a solution to that in a moment.
Any views you may have on the right-hand side, just close them (for most people, this means the Outline View). Instead use the in-place views. Ctrl-O opens an in-place Outline View (over the top of the editor). It’s actually better than the more usual docked view because it supports type ahead to quickly find the member you are looking for. Along with Ctrl-O, you also need to learn Ctrl-T which opens an in-place “quick” type hierarchy. This is a really easy way to see all implementers of an interface for example.
A final tip on the subject of view arrangement. If you have a dual monitor setup (becoming very common in the client sites I visit) it is quite nice to undock the Javadoc view completely – just grab it and drag it right outside of your Eclipse window and onto your second monitor screen. Now you’ll have the javadoc permanently open as a reference tool while you work. (Yes, you can undock views in Eclipse now, a lot of people seem not to know that).
On the subject of key bindings, you can significantly improve your productivity in Eclipse by learning some of the basic keyboard short cuts for common tasks (and indeed, for some things in the Java editor that aren’t available from the menus at all). However it’s also true that some of the default bindings for common tasks are absurd: “Alt-Shift-X,T” to run a test case for example – one of the tasks you’ll be doing very frequently I trust. When using fast views, I also find it convenient to add key bindings to open the Package Explorer and JUnit views quickly without having to move the mouse at all.
I make the following changes to the default key bindings:
Key combination | Result |
---|---|
Ctrl-P | Open the Package Explorer (normally bound to print, but I hardly ever print from Eclipse, and will happily use the menu for that) |
Ctrl-R | Run as a test case. (normally bound to run-to-line in the debugger). Running tests should be a simple key-stroke away at any time. |
Ctrl-J | Open the JUnit View (normally bound to incremental search) |
Ctrl-I | Incremental search |
Ctrl-G | Generate getter/setter |
Ctrl-B | Toggle breakpoint |
Now that you have Ctrl-R to run tests, it’s worth knowing that you can use this shortcut from within the editor when editing a test case, when a package is selected (to run all the tests in the package), when a source folder is selected (to run all the tests under the source folder), and when a project is selected (to run all the tests in the project).
Here are some of the other key shortcuts that I find amongst the most useful:
You’re already familiar with using the arrow keys (left,right,up,down) to move around within the editor. Using the arrow keys in combination with Alt, Shift, and Ctrl put a whole new set of capabilities at your fingertips:
Key combination | Result |
---|---|
Arrow keys | Move cursor point in editor |
Shift + arrow keys | Increase/decrease selection in editor |
Alt+Shift + left,right | Select previous syntactic element / next syntactic element |
Alt+Shift + up, down | Select enclosing element / restore last selection |
Ctrl+Shift+left,right | Select next word / previous word |
Ctrl + left,right | Move left / right by one word |
Ctrl + up,down | Scroll up / down by one line |
Ctrl+Shift+up,down | Go to previous member / go to next member |
Alt + left, right | Go forward and backward in history (really useful if you’ve been following links exploring the code) |
Alt+up,down | Hidden treasure! Move the current line or selected lines up and down |
Ctrl+Alt+up,down | Duplicate line above / below |
Other useful keys (standard cut/copy/paste etc. not included as I assume you are already familiar with these):
Key combination | Result |
---|---|
Ctrl+O | In-place outline view |
Ctrl+T | In-place quick type hierarchy |
Ctrl+M | Maximize/restore current window/view |
Ctrl+D | Delete line |
Ctrl+Shift+delete | Delete to end of line |
Shift+Enter | Insert new line below |
Alt+Shift+R | Rename |
Alt+Shift+T | Quick refactoring menu |
F3 | Open declaration |
F4 | Open in type hierarchy |
F11 | Debug last launched |
Ctrl+F11 | Run last launched |
Ctrl+Shift+T | Open type |
Ctrl+Shift+R | Open resource |
Ctrl+Shift+G | Search for references in workspace |
Ctrl+F6 | In-place editor selection |
Ctrl+F7 | In-place view selection |
Ctrl+F8 | In-place perspective selection |
F12 | Activate editor (useful if you’re following my suggestion of using fast views :- F21 will dismiss the fast view and return you to the editor) |
AspectJ hints and tips
Eclipse has great support for aspect-oriented programming with AspectJ through the AJDT plugins. In this section I’ll show you how to set up projects using linked source folders, how to run JUnit tests with an aspect library using load-time weaving, and some things you can do with the Visualizer that you may not realise. See the AJDT site on Eclipse.org for a tutorial on using AJDT – this section just contains a few less-well-known pointers.
Before we start, if you’re trying out using fast views and closing all the views on the right-hand side, one of things you’ll be missing is the cross-references view from AJDT (with all of the advises and advised-by relationship information). You can either dock this at the bottom (alongside the console for example), or use the in-place quick cross references view (not a lot of people know about that!). It has a not-very-useful default key binding of Alt-Shift+P, I remap it to Ctrl+U (not many letters left to choose from!). Now just press Ctrl+U anywhere in the editor and get a pop-up view of the cross-references for the selected element. Press it again to get cross-references for the whole file. Perfect!
Linked source folders:
Linked source folders can be a great way to experiment with aspects on a project without touching the main (Java) project at all. Create an AspectJ project alongside the Java project that you want to work with. Put all of your aspects into the “src” folder of the AspectJ project. Now, inside the AspectJ project open the project properties (Alt+Enter, since we’re trying to wean you off the mouse!) and go to the Java Builder page, and then the “Source” tab. Click on “add folder” to create a new source folder, and then the “Create new folder…” button. Here you’ll see an “Advanced” button. If you press this, you’ll get the option to create a folder that is actually a symbolic link to another location. Use this technique to create source folders inside the AspectJ project that are symbolic links to the source folders inside the Java project you want to work with.
Now whenever you build the AspectJ project, you will get (in the bin directory of the AspectJ project) a woven version of the project files. You will also see any warnings or errors arising from your use of declare warning / declare error in this project. The “donor” Java project remains completely unaffected by this exercise. Any changes to the source files in the Java project are of course instantly visible in the AspectJ project (and vice versa). To get the best performance, you can now turn off incremental building for the AspectJ project (Project menu -> build automatically) and just build that project on demand using the AspectJ build button in the taskbar.
Load-time weaving
(See my entry on using an aspect libraryfor a more in-depth treatment of this)
If you’re using an aspect library (such as spring-aspects.jar that ships with Spring 2.0) it is very convenient during development to use “load-time weaving”. Load-time weaving is the name given to weaving your application classes at runtime as they are loaded into the virtual machine. In this way there is no need to convert your project to an AspectJ project, or to do anything special at all other than put the aspect library on your build path.
Say you have some integration tests that you kick off via JUnit, and that rely on the behaviour contributed by the aspects in order to pass. For example, you’re using @Configurable to dependency inject domain objects. With the needed library on the build path, create a launch profile for the test run. The easiest way to create a launch profile is just to highlight the test or set of tests that you want to run and run them with Ctrl+R (if you’ve followed my advice and remapped that key binding). This run will fail, because you haven’t activated load-time weaving yet. Now select “run…” from the launch menu and you’ll see the launch profile that was created for the tests you just ran. Click on the arguments tab, and in the “VM arguments” box add the following:
-javaagent:aspectj-install-dir/lib/aspectjweaver.jar
Where aspectj-install-dir is the directory where you installed AspectJ (it’s best to download the standalone compiler and associated tools from www.eclipse.org/aspectj when using load-time weaving, ant building etc..).
Now you can rerun the tests and you’ll find that AspectJ picks up and uses the aspect libraries that you have on your classpath. It really is as simple as that.
Visualizer:
One of the tools that you get when you install AspectJ is the Visualizer. In fact, you get a whole “Aspect Visualisation” perspective which is the best way to use the visualiser. By default, the visualiser shows you cross-cutting information such as advises and advised by relationships. The visualizer plugin actually provides a generic capability that is not tied to AspectJ though. Open the visualizer perspective, and from the drop-down menu of the main visualizer view select “preferences”. Here you can choose the data provider that the visualizer will use to display information. By default it uses the AspectJ provider as I said. If you change to the “JDT Search Results Provider” you can use the view to visualize the results of the current search (all the matches, and where they are in the project).
The following image shows the results of searching for references to “InitializingBean” in Spring. Just as with the advice visualization, you can navigate from the visualization to the source code locations simply by clicking on the bars.
If you select “Resource and Markers Provider” you can see where all of your breakpoints, Task markers, warnings and errors are. Clicking on the options in the visualizer menu enables you to choose the subset of these that you are interested in at the time:
And finally
If you haven’t done so already, you should check out the Mylar plugin for Eclipse (developed by one of the AspectJ team, Mik Kersten) (see www.eclipse.org/mylar, and then go to the getting started page and watch the flash videos there). Especially if you’re working with bugzilla, Mylar is fantastic. Mylar deserves an article all of its own so I won’t attempt to describe it here – just go and try it out for yourself, you’ll be glad you did!
Posted by adrian at 10:12 AM [permalink] | Comments (10)
February 20, 2006
A Practical Guide to Using an Aspect Library (part I)
This entry represents part one of a two-part guide to using an aspect library (with AspectJ). I wrote it in December of last year, and have been waiting to finish part II before publishing it. But I finally realised with everything else I've got on at the moment it's probably best just to make this part available anyway! So with no further ado:
Introduction
With the arrival of AspectJ 5 and the Spring 2.0 milestone builds, many of you will be working with AspectJ-based aspect libraries for the first time. This article is a practical guide to get you up and running as quickly as possible and with the least amount of hassle. I'll cover development and unit testing, integration testing and deployment, continuous integration, and production builds.
I'm assuming that for the time being you don't want to write your own aspects: you just want to use the capabilities of an existing library. As a running example, I'm going to use the aspect library that ships with Spring 2.0: spring-aspects.jar. This library contains an aspect that supports dependency injection of domain objects, following the principles I outlined in the developerWorks article "Dependency Injection with AspectJ and Spring" (http://www-128.ibm.com/developerworks/java/library/j-aopwork13.html).
The library supports an @Configurable annotation. When an instance of an @Configurable type is created, however it is created, it will be dependency injected by Spring. The following examples demonstrate some typical uses of the annotation:
@Configurable public class Account {...}
When the annotation is used like this without a value, an instance of Account will be dependency injected by Spring, using the fully-qualifed class name of Account as the bean name.
@Configurable("accountBean") public class Account {...}
When a value is supplied, it is used as the bean name: ;n instance of Account will be dependency injected by Spring, using "accountBean" as the bean name.
@Configurable(autowire=Autowire.BY_NAME) public class Account {...}
An instance of Account will be dependency injected by Spring using autowiring by name.
@Configurable(autowire=Autowire.BY_TYPE) public class Account {...}
An instance of Account will be dependency injected by Spring using autowiring by type.
When autowiring, it is also possible to require a dependency check by specifying dependencyCheck="true". For example,
@Configurable(autowire=Autowire.BY_TYPE, dependencyCheck="true") public class Account {...}
So let's get started developing an application that uses @Configurable...
Development, unit testing, and simple integration testing
Unit testing means testing the Account class in isolation. The standard benefits of dependency injection as related to enhanced testability apply here, and there is nothing special to do. In unit tests, we will manually pass in mock or stub implementations of the Account object's dependencies.
When it comes to simple integration testing (firing up a combination of objects in a Spring container and checking they behave as expected) we do want the dependency injection to happen (i.e. we do need the aspect in the library to be in effect).You can annotate any type with @Configurable, but just using the annotation on its own doesn't deliver the behaviour associated with that annotation by the aspects in the spring-aspects library. You need to link (or "weave") the aspects in the library with your application for it to behave as desired at runtime. In this section I'm going to focus on what you need to do to get your basic integration tests passing in your IDE. I'll describe configuration for both Eclipse and IntelliJ IDEA. If you use a different IDE it should be straightforward to setup a similar environment.
First let's look at the sample project we'll be working with. The project defines one domain object, "Account", a service interface "AccountService", and a default implementation of that service, "DefaultAccountService". There is an accompanying test suite, "AccountTests". The Account class looks like this:
/** * Sample domain class using the Spring @Configurable annotation. */ @Configurable public class Account { private AccountService accountService; public void setAccountService(AccountService anAccountService) { this.accountService = anAccountService; } /** so that we can test it has been set appropriately */ public AccountService getAccountService() { return accountService; } }
It is expecting to be configured with an "AccountService" when it is instantiated. The test case verifies this is the case:
public class AccountTests extends AbstractSpringContextTests { public void testAccountConfiguration() { Account acc = new Account(); assertNotNull( "account service should have been provided", acc.getAccountService()); } protected void setUp() throws Exception { super.setUp(); getContext(new String[] { "org/aspectprogrammer/samples/domain/beans.xml" }); } }
Note that "testAccountConfiguration" simply creates a new Account object using the default constructor, and expects it to be dependency injected. The test case configures a Spring application context from "beans.xml". Here it is (using the new Spring 2.0 configuration style):
<?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"> <!-- this element causes spring to configure the AnnotationBeanConfigurer aspect in spring-aspects.jar so that it has a reference to the bean factory --> <aop:spring-configured/> <!-- configuration of Account bean, because we don't specify an id, the class name will be used as the bean name. This conveniently matches the behaviour of @Configurable when no bean name is given in the annotation --> <bean class="org.aspectprogrammer.samples.domain.Account" lazy-init="true"> <property name="accountService" ref="accountService"/> </bean> <!-- service used by the Account bean --> <bean id="accountService" class="org.aspectprogrammer.samples.services.impl.DefaultAccountService"> </bean> </beans>
You'll need spring-aspects.jar on the classpath for your project.There was a packaging issue with the jar in Spring 2.0 M1, but if you're using M2 or higher everything should work as described in this article. You'll also need the AspectJ runtime library, aspectjrt.jar on the classpath.
If we run the test case under JUnit right now it will fail with the message "account service should have been provided". This is because we haven't woven the application with the aspects in the library yet, so there is no behaviour associated with the annotation. So how do we make the tests pass?
Introducing Load-time Weaving
AspectJ is first and foremost a programming language. It supports constructs called "aspects" in addition to the regular Java types. So one way to use AspectJ is to write a program in the AspectJ language, compile it with the AspectJ compiler (producing standard .class files), and run it. AspectJ also lets you take pre-compiled aspects (in a jar file for example) and link (weave) them with your application classes. This can be done as an additional stage in a build process (see later) or at runtime as classes are loaded into the virtual machine. Weaving classes at loadtime goes by the name of "Load-time weaving" or "LTW" for short.
Load-time weaving is a very easy and flexible way to use an existing aspect library during development. The simplest way to use it is under Java 5 using the AspectJ weaving Java agent.
Using LTW with JUnit in Eclipse and IDEA
To set this up in Eclipse, do the following:
Click on the Run... drop-down icon in the toolbar to open the run configurations dialog. Click on "JUnit" and then press "New". Give the configuration a name (I've called it "tests") and select the tests you want to run (I've chosen to run all the tests in the project):
Click on the "Arguments" tab and in the "VM arguments" section enter:
-javaagent:lib/aspectjweaver.jar
The part after the ":" should be the path to your copy of aspectjweaver.jar. In this case, I've copied the aspectjweaver.jar from the Spring distribution into the lib directory of my project (it doesnt' need to be on the project's classpath). You can use the jar from the AspectJ 5 final release too if you want to.
That's it!
Save and run the configuration - you should see a green bar.
To set this up in IDEA:
Configuration in IDEA is very similar. Open the "Run/Debug Configurations" dialog using the drop-down in the toolbar. Click the "+" icon to create a new configuration and name it e.g. "tests".
For this project, I've selected "All in package" and search for tests "In whole project".
Now all you have to do is add the VM startup parameter that brings in the AspectJ LTW agent:
-javaagent:lib/aspectjweaver.jar
The part after the ":" should be the path to your copy of aspectjweaver.jar. In this case, I've copied the aspectjweaver.jar from the Spring distribution into the lib directory of my project (it doesnt' need to be on the project's classpath). You can use the jar from AspectJ 5 final release too if you want to.
That's it!
Save and run the configuration - you should see a green bar.
Running a Java application with LTW
To run a Java application (class with a "main" method) from your IDE you can follow exactly the same process and set the javaagent VM parameter. In Eclipse if you have the AJDT plugin installed, there is also a dedicated launch type that gives you a few more options here.
In the run configurations dialog, create a new "AspectJ Load-Time Weaving Application" launch configuration.
You'll see a new tab appear, "LTW Aspectpath". By selecting that tab you can add aspect library jars from your project or from an external source, and even aspects that are built by another project in your workspace.
This can be a very convienent way of launching an application with a diagnostics or instrumentation aspect library for example. Remember that this all works with standard Java projects - you do not need to convert your project to an AspectJ project.
Getting more information
So how did that work?
AspectJ's load-time weaving agent is configured by the use of aop.xml files. It looks for one or more aop.xml files on the classpath in the location "META-INF/aop.xml" and aggregates the contents to determine the weaver configuration. If you look inside spring-aspects.jar, you'll find a META-INF/aop.xml file in there that defines the Spring aspects in the library to the weaver. (For full details on the aop.xml file format and load-time weaving in general, see the AspectJ Developer's Guide).
Because aop.xml files are aggregated we can define our own project file and use it to give additional instructions to the weaver. One useful option to pass is "-showWeaveInfo" which causes the weaving agent to tell us exactly what it is doing.
Create a META-INF folder under the source root of your project (e.g. "src/META-INF"). In that folder, create a simple aop.xml file as follows:
<aspectj> <weaver options="-showWeaveInfo"/> </aspectj>
When you run the tests again, take a closer look at the console output. Here's the edited output of running the tests on my machine (I've stripped the package names out for clarity):
23-Dec-2005 13:45:08 AbstractSpringContextTests loadContextLocations INFO: Loading config for: org/aspectprogrammer/samples/domain/beans.xml 23-Dec-2005 13:45:08 XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [org/aspectprogrammer/samples/domain/beans.xml] weaveinfo Join point 'initialization(void Account.())' in Type 'org.aspectprogrammer.samples.domain.Account'(Account.java:15) advised by afterReturning advice from 'org.springframework.beans.factory.aspectj.AnnotationBeanConfigurer' (AbstractBeanConfigurer.aj:96) 23-Dec-2005 13:45:08 AbstractRefreshableApplicationContext refreshBeanFactory INFO: Bean factory for application context .....
Notice the weaveinfo message? AspectJ will tell you exactly what it is doing - which join points in which types are advised by which aspects. In this case, the "initialization" join point for the default constructor of Account has been advised by "afterReturning" advice from the "AnnotationBeanConfigurer" aspect (the advice is defined in the super-aspect AbstractBeanConfigurer, at L96 in the source file).
If you want the AspectJ messages nicely integrated into the log output (especially useful when it comes to deploying LTW in servers) you can supply your own message handler class using the "-XmessageHandlerClass" option. Spring supplies a message handler class that integrates the AspectJ messages into commons logging output produced by Spring itself. To use it add
-XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler"
to the weaver options in your aop.xml file. This class isn't supplied in the Spring 2.0 M2 distribution, but is included in M3.
Here's an extract from a test run using the custom message handling class:
23-Dec-2005 14:34:58 AspectJWeaverMessageHandler handleMessage INFO: [AspectJ] register aspect org.springframework.beans.factory.aspectj.AnnotationBeanConfigurer ... INFO: [AspectJ] weaving 'org/aspectprogrammer/samples/domain/Account' 23-Dec-2005 14:34:58 AspectJWeaverMessageHandler handleMessage INFO: [AspectJ] Join point 'initialization(void domain.Account.())' in Type 'org.aspectprogrammer.samples.domain.Account' (Account.java:15) advised by afterReturning advice from 'org.springframework.beans.factory.aspectj.AnnotationBeanConfigurer' (AbstractBeanConfigurer.aj:96) 23-Dec-2005 14:34:58 AspectJWeaverMessageHandler handleMessage INFO: [AspectJ] weaving 'org/aspectprogrammer/samples/services/impl/DefaultAccountService' 23-Dec-2005 14:34:58 AspectJWeaverMessageHandler handleMessage INFO: [AspectJ] weaving 'org/aspectprogrammer/samples/services/AccountService' 23-Dec-2005 14:34:58 AbstractRefreshableApplicationContext refreshBeanFactory INFO: Bean factory for application context ...
Note that we're now also getting messages about the all of the aspects in use and all of the types that the weaver is looking at (the "weaving ..." messages). Of these, it is only the Account class in our project that has the @Configurable annotation, and hence it is only during the weaving of that type that we see a join point woven message.
Being able to get detailed information about exactly what the weaver is doing can be a useful aid in performance tuning and troubleshooting.
Controlling LTW with aop.xml
Controlling the weaving scope:
One of the things you might have noticed if you tried out the custom message handling class is that the weaver looked at a large number of types. This includes for example "junit.framework.TestSuite" and several other types in the junit package. Load-Time Weaving will be much more efficient if you scope it to just the types in your application that you actually want to weave. In the case of our sample application, that's the classes in the org.aspectprogrammer.samples package and sub-packages. The weaving scope is controlled by the aop.xml file. We can add one or more include and exclude elements to control the set of types that the weaver will look at. If there are no include statements, then the weaver will look at all types that are not explicitly included. If there are one or more include statements, then the weaver looks at all included but not excluded types. To weave only the sample application classes we can update the aop.xml file as follows:
<aspectj> <weaver options="...."> <include within="org.aspectprogrammer.samples..*"/> </weaver> </aspectj>
The "within" attribute syntax is actually an AspectJ type pattern as used in the AspectJ pointcut language. For now all you need to know is that a pattern like "org.xyz.*" will include all types in the "org.xyz" package, and a pattern like "org.xyz..*" will include all types in the "org.xzy" package or any sub-package thereof.
Controlling which aspects are used:
The spring-aspects.jar contains several aspects. Since they are all defined by the META-INF/aop.xml file in the jar, they will all be registered and used for weaving. What if you want to use the @Configurable (AnnotationBeanConfigurer) aspect, but not the @Transactional support (AnnotationTransactionAspect)? You can control this via the aop.xml file too. This time you need to use the <aspects> element:
<aspectj> <!-- definitions of aspects available to the weaver, and which ones should be used or not used --> <aspects> <exclude within="org.springframework.transaction.aspectj.AnnotationTransactionAspect"/> </aspects> <!-- control over the weaver itself and the types that will be woven --> <weaver options="...."> <include within="org.aspectprogrammer.samples..*"/> </weaver>
The include and exclude options in the <aspects> element work the same way they do in the <weaver> element: if there are no include elements specified then all defined aspects that are not explicitly excluded are included. If there are one or more include elements, then all included but not excluded aspects are used.
What if I can't use Java 5?
For development, of course you can use Java 5!
By which I mean that even if you are developing an application that has to run under 1.3 or 1.4, you can still use a Java 5 VM with -source and -target compiler flags set appropriately. The only requirement to use load-time weaving in the way we have been describing is that you run the tests on a 1.5 VM.
In part II, I'll be looking at integration testing and deployment, and there I'll cover your options if you really can't use Java 5 to run your test server.
(Note: as mentioned at the start of this article, part II will be forthcoming just as soon as I get some more writing cycles!)
Posted by adrian at 01:40 PM [permalink] | Comments (12)