Dependency injection, annotations, and aspects 将成为今后一段时间J2EE领域里面技术层上的主流。所以应该成为我们小组的研究重点……

这三个东西是我在刚开始学习编程序时怎么也想不到的:原来程序也可以这么编!
这三个东西有一个共同点,就是他们都是代码外面的代码,程序外面的程序。
 

The New Holy Trinity

No, this isn't a religious post. It's about an approach to developing applications. I'm not the only one advocating this approach, and I'm not the first to write about some of the ideas here. What I want to do in this post though is put all the pieces together in one place and boil the approach down to its essence to give you a simple way of thinking about it. So here it is:

Dependency injection, annotations, and aspects are the three foundations on which the next generation of world-class POJO-based applications should be built.

At the centre of an application designed according to this principle are plain-old Java objects (POJOs). Each POJO should do one thing and one thing only. Each POJO should also know one thing and one thing only - how to do what it is that it does. This is the 'secret' that the the POJO hides from the rest of the application. So for example, a BankAccount POJO should know about the internal state of a bank account, and the operations that it supports. It should not know anything about the way any services it needs are created or deployed. It should not know anything about the authentication or authorization requirements that may be placed on it in a particular deployment scenario - or worse still, anything about the implementation of authenticaton and authorization services in the environment in which it is deployed. It should not know anything about account activity monitoring to detect potentially fradulent use. And so on.

I've written about this notion of a 1:1 mapping between distinct concepts in the design and constructs (types) in the implementation before in "AOP without the buzzwords" (this essay was also updated and expanded to form the introduction for our book, Eclipse AspectJ).

To use some old software engineering terms, what's at the centre of applications designed according to these principles are highly cohesive POJOs. These POJOs are very loosely coupled to the rest of the application. In particular, if you look at the three sides of the triangle in the illustration, you'll see that dependency injection ensures the POJO remains unaware of any of the details of how its dependencies are instantiated and configured. DI shields the POJO from this information. Aspects ensure the POJO remains unaware of any crosscutting concerns in the application (such as the account activity monitoring concern in the example above). Aspects are what enable the POJO to focus on doing just one thing and to do it well. Annotations provide additional metadata about the POJO that facilitate easy integration with external services (such as transaction services). They ensure that the POJO remains unaware of any of the details of such services. Annotations coupled with aspects can do much more than this though - they also support the creation of domain specific abstractions that easily bring back to the programmer Abelman and Sussman's often missing third tool in the war against software complexity: the creation of "little languages" (see my post "A Beautiful Language").

This leads me to a really important point about this trinity of DI, aspects, and annotations. The three techniques are mutually self-reinforcing, the whole is much greater than the sum of the parts.

Dependency injection can be used to configure aspects. This is important since when you're building an application using the 1:1 principle, some of the POJOs in the centre of the triangle will themselves be aspects (in a language like AspectJ, this is perfectly natural). Anecdotal evidence from a number of projects across a number of organizations suggests that maybe about 10% of your "POJOs" will be aspects. Dependency injection can also be guided by annotations. You see an example of this in the EJB 3.0 specification (you may recall my own thoughts on the suitability of the chosen annotation name in that spec "What's wrong with @Inject?", but that's not material to this discussion). Completing the circle, aspects can also be used to implement dependency injection scenarios. In general you're best leaving this job to an IoC container, but if you have DI requirements that go beyond the wiring of beans as they are instantiated by the container, aspects are a great solution. To give a couple of examples, in the Eclipse AspectJ book we use aspects to implement a 1:1 design for POJO persistence using Hibernate. We use dependency injection on a per-request basis to inject the Session object that DAOs use for communicating with Hibernate. A second example is the use of an annotation @SpringConfigured(beanName="XXX") on POJOs. It's easy to write an aspect (configured by DI to be BeanFactoryAware in Spring terms) that uses Spring to autowire any instance of an @SpringConfigured object when it is instantiated. This lets you separate instantiation and configuration (normally an IoC container like Spring does both for you) so that you can place instantiation under programmatic control, but still get configuration via DI.

Let's look at another really important way in which these techniques work together. Annotations on their own give you a way of specifying additional metadata for program elements. This is useful when you have tools or services that can understand those annotations and do something with them. Annotations coupled with aspects is a whole other ball game altogether. Aspects let you use annotations to easily implement declarative domain-specific languages (DSLs) as part of your application (or aspect library). This is a really big deal. Aspects can do this since they let you associate behaviour with join points relating to annotated elements. To give an example I've written about recently ("Making Concurrency a Little Bit Easier"), you can easily write a set of annotations that form a declarative DSL for concurrent applications (the domain of this DSL is concurrency). Another example I'll write about in my next post: after attending Gregor Hohpe's talk at TSS yesterday on event-driven architectures I quickly implemented an annotatation-based DSL ("@DSL") for simple event-driven applications. It took me less than an hour. That's pretty powerful (you can judge for yourself when you read the post). Annotations plus aspects let you write these @DSLs.

When you're using @DSLs like this in the construction of your application, you're going to want an aspect language that has rich support for join point matching based on annotations. There's a surprising amount to this, and you can see how AspectJ 5 handles it by reading the "Annotations" chapter of the AspectJ 5 Developer's Notebook.

There's another way in which the combination of aspects and annotations are self-reinforcing too. If you are using an annotation-driven tool or service for a particular deployment of your application, you may need to annotate your POJOs with annotations specific to that tool or service. If you don't want to tie your POJOs to that annotation set (see "When is a POJO not a POJO?") you can use the AspectJ 5 support for "declare annotation" to keep the annotations separate and maintain a loose coupling. You might also want to use declare annotation if a particular tool or service requires you to spread information you consider to be configuration information around your source code (in the form of annotations). Here an aspect using declare annotation can at least get that all back in one place for you.

If this all sounds a bit circular (DI of aspects, aspects to perform DI, annotations to control aspect behaviour, aspects to declare annotations,...) that's because it's meant too. The sides of the triangle all support each other and can be used together in many different ways to meet the needs of your particular situation. The whole is much greater than the sum of the parts.

In summary, I believe that dependency injection, annotations, and aspects are the three foundations on which the next generation of world-class POJO-based applications should be built. At the heart of such applications are POJOs that do one thing only and do it well. The triangle illustration provides a simple visual mnemonic to help you remember the principles. An application constructed according to these principles could be said to follow a "triangle design". You can even easily communicate your design intentions in good old ascii:

        /o\
	 -