Book Image

Spring 2.5 Aspect Oriented Programming

Book Image

Spring 2.5 Aspect Oriented Programming

Overview of this book

Developing powerful web applications with clean, manageable code makes the maintenance process much easier. Aspect-Oriented Programming (AOP) is the easiest and quickest way to achieve such results. Spring is the only Java framework to offer AOP features. The combined power of Spring and AOP gives a powerful and flexible platform to develop and maintain feature-rich web applications quickly. This book will help you to write clean, manageable code for your Java applications quickly, utilizing the combined power of Spring and AOP. You will master the concepts of AOP by developing several real-life AOP-based applications with the Spring Framework, implementing the basic components of Spring AOP: Advice, Joinpoint, Pointcut, and Advisor. This book will teach you everything you need to know to use AOP with Spring. It starts by explaining the AOP features of Spring and then moves ahead with configuring Spring AOP and using its core classes, with lot of examples. It moves on to explain the AspectJ support in Spring. Then you will develop a three-layered example web application designed with Domain-Driven Design (DDD) and built with Test-Driven Development methodology using the full potential of AOP for security, concurrency, caching, and transactions.
Table of Contents (13 chapters)

Chapter 1. Understanding AOP Concepts

This chapter presents an overview of Aspect-Oriented Programming concepts, and explains their capabilities and features. Here is a brief outline of the topics covered in this chapter:

  • Limits of Object-Oriented Programming

  • The AOP solutions

  • Spring AOP components

  • Spring AOP 2.5

In this chapter we will see what the designing and realization process of an application or software system consists of.

We have to stop and think about the problems that we will see, beginning from the designing phase: how to structure the application, what are the problems in the implementation phase if we use only object-oriented programming, and in which forms they show themselves. We will also see how aspect-oriented programming can support object-oriented programming to solve problems in the implementation phase. We will finally see what Spring provides to allow us to use aspect-oriented programming with Inversion of Control (IoC).

If we use a method such as the Extreme Programming, we iteratively focus hard on the functionalities and improve them following the clients' feedback.

Therefore, who does what is described so that the functionalities that the system provides to the user are clear.

After having found these entities, we model them as classes that contain data and have behavior.

To do this, we use some features of the object-oriented languages, such as inheritance, polymorphism, encapsulation, and interfaces, to create a model that helps us solve the domain problem in the simplest way possible.

Drawing, structuring, and building software systems in this way is now considered a common practice. Nevertheless, there are some inefficiencies that emerge at the moment of realizing the project. In fact, however accurately the design may have been made with highly cohesive classes and low coupling, there are still some situations where we have to make compromises.

Limits of object-oriented programming

The object-oriented paradigm provided the concepts and the right instruments for the creation of complex programs and had a great impact on the development of new disciplines in the domain of software design. In this sense, both engineering and software design disciplines developed greatly. Particularly important has been the development of the so-called Design Patterns that allow a certain degree of systemization of the activity of software design and development.

The concept of a class that includes data and functions that change its values allows for the realization of cohesive and independent entities (high cohesion, low coupling). This in turn realizes the required business functionalities through the exchange of messages.

Using design patterns and object-oriented programming, the development of an application can be realized by dividing the activities into independent groups of functionalities. In fact, as soon as the interfaces of every entity of the application have been defined, their implementation can be realized independently by different groups of developers.

Another advantage is the reliability offered by the object. If we consider that the access to an object's data and its modification can happen only by means of the methods that it exposes through its interface, no user can unpredictably corrupt this data and make that object's state inconsistent.

Finally, the concept of inheritance allows the definition of new classes that extend the functionality of the classes from which they derive. In this sense, we obtain the extendibility and the reuse of software.

After the advantages of the new instruments given by the object-oriented programming paradigm, we have to consider the limits that occurred in practical application.

The main problem is to manage to control the complexity. To face it, we will have to choose modularization: "divide et impera", according to the Latin maxim.

If architects look at the previous projects on which they have worked, they will notice that a common feature is the constant increase in the systems' complexity.

Separating the functionalities that have to be implemented into simpler and more manageable modules helps to control the complexity. Software systems are conceptually complex by their very nature, and increasing their complexity in the implementation means increasing the expense and the probability of its failure.

The code needed to integrate a complex implementation is expensive. The cost would be even higher if new features are required. In fact, those features imply deep changes in several parts of the implementation.

If we didn't take the way of modularization and simplification, we would have a monolithic system that would be unmanageable to modify.

First of all, we have to single out the modules that will implement the core business that justifies the design and the implementation of the software. Once we have completely understood how to implement the core business, we can think about designing the rest of the application so that the core business supports the system's users.

We are used to take the best practice of dividing the application into logical layers (presentation layer, business layer, and data layer). But, there are some functionalities that cross these layers transversally. They are named crosscutting concerns.

A crosscutting concern is, therefore, an independent entity that transversally crosses other functionalities of software. Take a look at the following figure:

The most common crosscutting concerns are: security, logging, transactions management, caching, performance checking, concurrency control, and exception management.

These crosscutting concerns, if implemented only with object-oriented programming, realize a bad matching between the core business and the modules that implement its functionalities. We are forced to deal with the implementation of these transversal functionalities into various modules, moreover, adding other transversal modules or modifying the existing ones. We are also forced to modify the code in which these modules are used. This is owing to the undesired, but necessary, matching that the object-oriented implementation unavoidably brings with it.

The followings graphs (extracts from http://www.parc.com/research/projects/aspectj/downloads/SDWest2002-BetterJavaWithAJ.ppt), show the code of Servlet Engine Tomcat 4 divided in modules:

In the figure above, XML parsing fits in one module.

In the figure above, the URL pattern matching fits in two modules.

In the figure above, logging is scattered in too many modules.

This figure shows the points where Tomcat classes' logging functionalities are called (underlined in red). As we can see, they are scattered in the points of the modules where the functionality is required.

The problem of scattering code derived from the crosscutting concerns in object-oriented programming arises due to its transversality to the crosscutting concerns, which is implemented in the classes. More correctly, the crosscutting concerns should be analysed as a third dimension of the design. Whereas in the implementation there are two dimensions, as shown in the following figure:

In these situations, aspect-oriented programming provides support to object-oriented programming for uncoupling modules that implement crosscutting concerns.

Its purpose is the separation of concerns.

In object-oriented programming the basic unit is the Class, whereas in aspect-oriented programming it's the Aspect.

The aspect contains the implementation of a crosscutting concern, which in the class should coexist with the objects that collaborate with it, for each class that needs it.

In this way, we can write the object-oriented classes without involving the crosscutting concerns in the implementation.

So, classes can freely evolve without taking into account this dependency.

The functionalities provided by the crosscutting concerns in the aspects will be applied to the objects through an aspect weaver or through some proxy classes. We will deal with this in the later chapters.

Now we will see how the problems we exposed, arise in the code.

Code scattering

Code scattering appears when the functionality is scattered because it's implemented in several modules.

There are two sorts of code scattering:

  • Blocks of duplicated code (that is, the same code appears in different modules)

  • Blocks of complementary code, and different modules implementing complementary parts of the concern (for example, in Access Control, one module performs authentication and a second performs authorization)

Let's see the following code to illustrate the cases in which the code is duplicated in different modules:

The Info interface is implemented in the same way by two different classes, ScatteringA and ScatteringB. Therefore, this is a useless duplication of code.

public interface Info {
public String getName();
public Date getCreationDate();
}
public class ScatteringA implements Info{
public ScatteringA(String name, String author){
creation = new Date();
this.name = name;
this.autor = author;
}
public ScatteringA(Date creation, String name, String author){
this.creation = creation;
this.name = name;
this.autor = author;
}
public Date getCreationDate() {
return (Date)creation.clone();
}
public String getName() {
return name;
}
public String getAutor() {
return autor;
}
private Date creation;
private String name;
private String autor;
}
public class ScatteringB implements Info{
public ScatteringB(String name, String address){
creation = new Date();
this.name = name;
this.address = address;
}
public ScatteringB(Date creation, String name, String address){
this.creation = creation;
this.name = name;
this.address = address;
code scattering, object-oriented programmingcode duplication}
public Date getCreationDate() {
return (Date)creation.clone();
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
private Date creation;
private String name;
private String address;
}

Code tangling

Code tangling occurs when a module has to manage several concerns at the same time such as logging, exception handling, security, caching, and more or when a module has elements of the implementation of other concerns inside.

In order to show what we mean by code tangling, let's look at the following code:

public class TanglingListUserController extends MultiActionController{
public ModelAndView list(HttpServletRequest req,
HttpServletResponse res) throws Exception {
//logging
log(req);
// authorization
if(req.isUserInRole("admin")){
String username = req.getRemoteUser();
List users ;
//exception handling
try {
//cache with authorization
users = cache.get(Integer.valueOf( conf.getValue("numberOfUsers")), username);
} catch (Exception e) {
users = usersManager.getUsers();
}
return new ModelAndView("usersTemplate", "users", users);
}else{
return new ModelAndView("notAllowed");
}
}
private void log(HttpServletRequest req) {
StringBuilder sb = new StringBuilder("remoteAddress:");
sb.append(req.getRemoteAddr());
sb.append("username:");
sb.append(req.getRemoteUser());
log.fine(sb.toString());
}
…
}

In this Spring MultiActionController, we can see how many features are managed: logging, authorisation, exception management, and caching.

In spite of dealing with just the presentation of a list of users, this controller has to do many things, and the consequence is that other concerns are heavier in its implementation. That is code tangling.