Enterprise applications often face crosscutting concerns — concerns that might potentially affect code in many objects. Consider the following requirements:
Every operation on the AccountManager interface should be performed within a transaction.
The return values of certain expensive methods should be cached for a specified period of time.
Whenever an instance field is modified in a domain object, the object should be marked as dirty. All domain objects should implement an IsModified interface, exposing whether they're dirty.
A company is conducting a one-month marketing campaign and wants to analyze its effect on user activity. The temporary analysis code is better externalized from the service layer.
By definition, such crosscutting or orthogonal concerns are not well addressed by traditional OOP. For example, the Decorator design pattern can help with the first requirement (creating transactions around methods), but it leads to much code duplication. It will result in boilerplate code to begin and end transactions in every AccountManager method. If we add a new method, it won't automatically be transactional until we cut and paste that boilerplate code. There's no way to control which methods are transactional besides modifying the AccountManager class itself. The only other option has been specific middleware models such as EJB, in which the component model itself applies crosscutting behavior to handle well-known aspects such as transaction management. However, this is invasive — it works only if you jump through the hoops imposed by the particular component model — and it isn't extensible. The services in question are tied to the component model, and you can't add support for your own arbitrary service or behavior.
The second requirement (caching) also results in code duplication. Such per method operations can't easily be modularized using OOP alone.
The third requirement (dirty checking domain objects) will need to be addressed through keeping a dirty flag in every affected class, and setting it to true on every data change. If you remember using server-specific optimizations in EJB 1.1 entity beans, you'll remember how error-prone this approach is. It's not just a question of code bloat. We can't solve this problem using inheritance; the dirty flag could be held in an abstract base class that implemented the IsModified interface, but the crucial thing is updating that flag, and that can't be modularized using OO.
To address such problems, minimizing code duplication, we need a new way to think about program structure. AOP provides this, enabling us to target additional behavior at sets of methods or other points in our application structure using pointcuts. In this chapter, we'll see when, and how, to use this capability effectively.
Spring provides support for both proxy-based and class weaving AOP (the latter through integration with products such as AspectJ). This enables us easily to address requirements such as those mentioned — and many others.