Chapter 5. Introducing Spring AOP

Over the last few years, aspect-oriented programming (AOP) has become a hot topic in the Java world, and many articles, discussions, and implementations of AOP have become available for Java programmers. AOP is often referred to as a tool for implementing crosscutting concerns, which means that you use AOP for modularizing individual pieces of logic, known as concerns, and you apply these concerns to many parts of an application. Logging and security are typical examples of crosscutting concerns that are present in many applications. Consider an application that logs the start and end of every method for debugging purposes. You will probably refactor the logging code into a special class, but you still have to call methods on that class twice per method in your application in order to perform the logging. Using AOP, you can simply specify that you want the methods on your logging class to be invoked before and after each method call in your application.

To use AOP, you should understand how it complements, rather than competes with, object-oriented programming (OOP). OOP is very good at solving a wide variety of problems that we as programmers encounter. However, if you take the logging example again, you can plainly see that OOP is lacking when it comes to implementing crosscutting logic on a large scale. Using AOP on its own to develop an entire application is practically impossible, given that AOP functions on top of OOP. Likewise, although developing entire applications using OOP is certainly possible, you can work smarter by employing AOP to solve particular problems that involve crosscutting logic.

We are going to cover AOP in this chapter and the next. In this chapter, we cover Spring AOP in isolation from much of the rest of the framework.

First, we cover the basics of AOP as a technology in this chapter. Most of the concepts covered in the "AOP Concepts" section are not specific to Spring and can be found in any AOP implementation. If you are already familiar with another AOP implementation, feel free to skip over this section.

Next, we'll explain the two distinct types of AOP: static and dynamic. In static AOP, like AspectJ's AOP (http://eclipse.org/aspectj/), the crosscutting logic is applied to your code at compile time, and you cannot change it without modifying the code and recompiling. With dynamic AOP, like Spring's AOP, crosscutting logic is applied dynamically, at runtime. This allows you to make changes in the distribution of crosscutting without recompiling the application. These types of AOP are complementary, and when used together, they form a powerful combination in your applications.

Note

Static and dynamic AOP are distinct from the static and dynamic crosscutting concepts. The differentiation between static and dynamic crosscutting is largely academic and is of no relevance to Spring AOP. For more information on this topic and on AOP as a whole, we recommend that you read AspectJ in Action: Practical Aspect-Oriented Programming by Ramnivas Laddad (Manning, 2003).

Once you understand the types of AOP, we'll get down to the details of Spring's AOP implementation. Spring AOP is only a subset of the full AOP feature set found in other implementations like AspectJ. In this section, we take a high-level look at which features are present in Spring, how they are implemented, and why some features are excluded from the Spring implementation.

Proxies are a huge part of how Spring AOP works, and you must understand them to get the most out of Spring AOP. So next, we'll look at the two different kinds of proxies: JDK dynamic proxy and CGLIB proxy. In particular, we look at the different scenarios in which Spring uses each proxy, the performance of the two proxy types, and some simple guidelines to follow in your application to get the most from Spring AOP.

Finally, we'll look at some practical examples of AOP usage. We start off with a simple "Hello, World" example to ease you into Spring's AOP code; then we continue with a detailed description of the different AOP features that are available in Spring, complete with examples.In Chapter 6, we take a much more framework-oriented view of Spring AOP, including how to configure AOP using @AspectJ annotations as well as XML Spring configuration.

AOP Concepts

As with most technologies, AOP comes with its own specific set of concepts and terms, and you should understand what these terms mean before we explain how to use AOP in an application. The following list explains the core concepts of AOP:

  • Joinpoint: A joinpoint is a well-defined point during the execution of your application. Typical examples of joinpoints include a call to a method, the method invocation itself, class initialization, and object instantiation. Joinpoints are a core concept of AOP and define the points in your application at which you can insert additional logic using AOP.

  • Advice: The code that is executed at a particular joinpoint is called the advice. There are many different types of advice, including before advice, which executes before the joinpoint, and after advice, which executes after it.

  • Pointcuts: A pointcut is a collection of joinpoints that you use to define when advice should be executed. By creating pointcuts, you gain fine-grained control over how you apply advice to the components in your application. As mentioned previously, a typical joinpoint is a method invocation. A typical pointcut is the collection of all method invocations in a particular class. Often, you can compose pointcuts in complex relationships to further constrain when advice is executed. We discuss pointcut composition in more detail in the next chapter.

  • Aspects: An aspect is the combination of advice and pointcuts. This combination results in a definition of the logic that should be included in the application and where it should execute.

  • Weaving: This is the process of actually inserting aspects into the application code at the appropriate point. For compile-time AOP solutions, this is, unsurprisingly, done at compile time, usually as an extra step in the build process. Likewise, for runtime AOP solutions, the weaving process is executed dynamically at runtime.

  • Target: An object whose execution flow is modified by some AOP process is referred to as the target object. Often, you see the target object referred to as the advised object.

  • Introduction: Introduction is the process by which you can modify the structure of an object by introducing additional methods or fields to it. You can use introduction to make any object implement a specific interface without needing the object's class to implement that interface explicitly.

Don't worry if you find these concepts confusing; they will all become clear when you see some examples. Also be aware that you are shielded from many of these concepts in Spring AOP, and some are not relevant due to Spring's choice of implementation. We will discuss each of these features in the context of Spring as we progress through the chapter.

Types of AOP

As we mentioned earlier, there are two distinct types of AOP: static and dynamic. The difference between them is really the point at which the weaving process occurs and how this process is achieved.

Static AOP

Many of the first AOP implementations were static. In static AOP, the weaving process forms another step in the build process for an application. In Java terms, you achieve the weaving process in a static AOP implementation by modifying the actual bytecode of your application, changing and extending the application code as necessary. Clearly, this is a well-performing way of achieving the weaving process, because the end result is just Java bytecode, and you do not need any special tricks at runtime to determine when advice should be executed.

The drawback of this mechanism is that any modifications you make to the aspects, even if you simply want to add another joinpoint, require you to recompile the entire application.

AspectJ is an excellent example of a static AOP implementation.

Dynamic AOP

Dynamic AOP implementations like Spring AOP differ from static AOP implementations in that the weaving process is performed dynamically at runtime. The way that this is achieved is implementation dependent, but as you will see, Spring's approach is to create proxies for all advised objects, allowing for advice to be invoked as required. The slight drawback of dynamic AOP is that, typically, it does not perform as well as static AOP, but its performance is steadily improving. The major benefit of dynamic AOP implementations is the ease with which you can modify the entire aspect set of an application without needing to recompile the main application code.

Choosing an AOP Type

Choosing whether to use static or dynamic AOP is actually quite a hard decision. There is no reason for you to choose either implementation exclusively, because both have their benefits. Spring 1.1 introduced integration with AspectJ, allowing you to use both types of AOP with ease. We cover this feature in more detail in Chapter 6. In general, static AOP implementations have been around longer, and they tend to have more feature-rich implementations, with a greater number of available joinpoints. Indeed, Spring supports only a subset of the features available with AspectJ. Typically, if performance is absolutely critical or you need an AOP feature that is not implemented in Spring, then you will want to use AspectJ. In most other cases, Spring AOP is better. Remember that many AOP-based features are already available in Spring, such as declarative transaction management. Reimplementing these using AspectJ is a waste of time and effort, especially since Spring has tried-and-tested implementations ready for you to use.

Most importantly, let the requirements of your application drive your choice of AOP implementation, and don't restrict yourself to a single implementation if a combination of implementations would better suit your application. In general, we have found that Spring AOP is less complex than AspectJ, so it tends to be our first choice. If we find that Spring AOP won't do what we want it to do, or we discover during application tuning that performance is poor, then we move to AspectJ instead.

AOP in Spring

You can think of Spring's AOP implementation as coming in two logical parts. The first part is the AOP core, which provides fully decoupled, purely programmatic AOP functionality. The second part of the AOP implementation is the set of framework services that make AOP easier to use in your applications. On top of this, other components of Spring, such as the transaction manager and EJB helper classes, provide AOP-based services to simplify the development of your application. In this chapter, we focus solely on the basics of the AOP core. The framework services and the advanced functionality of the core are covered in Chapter 6.

Spring AOP is really a subset of the full AOP feature set, implementing only a handful of the constructs available in implementations like AspectJ. Don't be fooled into thinking Spring AOP is not useful, however. Indeed, one of the most powerful aspects of Spring AOP is that it is so simple to use because it is unencumbered with extra features that you often do not need. The implementation of only a subset of the AOP feature set is a specific design goal of Spring, allowing Spring to focus on simple access to the most common features of AOP. To make sure that you are not left without the AOP features that you need, in the 1.1 release Spring's designers added full integration with AspectJ.

The AOP Alliance

The AOP Alliance (http://aopalliance.sourceforge.net/) is a joint effort between representatives of many open source AOP projects, including Spring, to define a standard set of interfaces for AOP implementations. The AOP Alliance is very conservative, resisting the temptation to over-constrain AOP while it is still growing, and as a result, they have only defined interfaces for a subset of AOP features. Wherever applicable, Spring uses the AOP Alliance interfaces rather than defining its own. This allows you to reuse certain advice across multiple AOP implementations that support the AOP Alliance interfaces.

"Hello, World" in AOP

Before we dive into discussing the Spring AOP implementation in detail, we want to present a simple example to provide some context for these discussions. In this example, we take a simple class that outputs the message "World", and then using AOP, we transform an instance of this class at runtime to output "Hello, World!" instead. Listing 5-1 shows the basic MessageWriter class.

Example 5.1. The MessageWriter Class

package com.apress.prospring.ch05.simple;

public class MessageWriter {

    public void writeMessage() {
        System.out.print("World");
    }
}

The MessageWriter class is nothing special; it has just one method that writes the message "World" to stdout. We want to advise—that is, add some advice to—this class so that the writeMessage() method actually writes "Hello World!" instead.

To do this, we need to execute some code before the method body executes to write "Hello", and some code after the method body executes to write "!". In AOP terms, what we need is an around advice—that is, advice that executes around a joinpoint. In this case, the joinpoint is the invocation of the writeMessage() method. Listing 5-2 shows the implementation of the around advice, the MessageDecorator class.

Example 5.2. Implementing Around Advice

package com.apress.prospring2.ch05.simple;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MessageDecorator implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.print("Hello ");
        Object retVal = invocation.proceed();
        System.out.println("!");
        return retVal;
    }
}

The MethodInterceptor interface is the AOP Alliance standard interface for implementing around advice for method invocation joinpoints. The MethodInvocation object represents the method invocation that is being advised, and using this object, we control when the method invocation is actually allowed to proceed. Because this is around advice, we are essentially capable of performing some actions before the method is invoked and some actions after it is invoked but before it returns. In Listing 5-2, we simply write Hello to stdout, invoke the method with a call to MethodInvocation.proceed(), and then write ! to stdout.

The final step in this sample is to weave the MessageDecorator advice into the code. To do this, we create an instance of MessageWriter, the target, and then create a proxy of this instance, instructing the proxy factory to weave in the MessageDecorator advice. This is shown in Listing 5-3.

Example 5.3. Weaving the MessageDecorator Advice

package com.apress.prospring2.ch05.simple;

import org.springframework.aop.framework.ProxyFactory;

public class HelloWorldWeaver {

    public static void main(String[] args) {
        MessageWriter target = new MessageWriter();

        // create the proxy
        ProxyFactory pf = new ProxyFactory();

        pf.addAdvice(new MessageDecorator());
        pf.setTarget(target);

        MessageWriter proxy = (MessageWriter) pf.getProxy();

        // write the messages
        target.writeMessage();
        System.out.println("");
        proxy.writeMessage();
    }
}

The important part here is that we use the ProxyFactory class to create the proxy of the target object, weaving in the advice at the same time. We pass the MessageDecorator advice to the ProxyFactory with a call to addAdvice() and specify the target for weaving with a call to setTarget(). Once the target is set and some advice is added to the ProxyFactory, we generate the proxy with a call to getProxy(). Finally, we call writeMessage() on both the original target object and the proxy object. Here are the results of running this example:

World
Hello World!

As you can see, calling writeMessage on the untouched target object resulted in a standard method invocation, and no extra content was written to stdout. However, the invocation of the proxy caused the code in the MessageDecorator to execute, creating the desired output Hello World! From this example, you can see that the advised class had no dependencies on Spring or the AOP Alliance interfaces; the beauty of Spring AOP, and indeed AOP in general, is that you can advise almost any class, even if that class was created without AOP in mind. The only restriction, in Spring AOP at least, is that you can't advise final classes, because they cannot be overridden and therefore cannot be proxied.

Spring AOP Architecture

The core architecture of Spring AOP is based around proxies. When you want to create an advised instance of a class, you must use the ProxyFactory class to create a proxy of an instance of that class, first providing the ProxyFactory with all the aspects that you want to be woven into the proxy. Using ProxyFactory is a purely programmatic approach to creating AOP proxies. For the most part, you don't need to use this in your application; instead, you can rely on the ProxyFactoryBean class to provide declarative proxy creation. However, it is important to understand how proxy creation works. For the rest of this chapter, we will use the programmatic approach to proxy creation. In the next chapter, we discuss using ProxyFactoryBean when creating proxies.

Internally, Spring has two proxy implementations: JDK dynamic proxy and CGLIB proxy. In previous releases of Spring, there was not much difference between the two proxy types, and CGLIB proxies were only used when you wanted to proxy classes rather than interfaces or when you explicitly specified them. As of the 1.1 release of Spring, the CGLIB proxy is noticeably faster than JDK dynamic proxies in most cases. This is especially true when you are running on a release 1.3 virtual machine, which suffers from poor reflection performance. Understanding proxies and how they are used internally is key to getting the best performance out of your application. We discuss proxies in great detail later in the chapter, in the section titled "All About Proxies."

Joinpoints in Spring

One of the more noticeable simplifications in Spring AOP is that it only supports one joinpoint type: method invocation. At first glance, this might seem like a severe limitation if you are familiar with other AOP implementations like AspectJ, which supports many more joinpoints, but in fact, it actually makes Spring more accessible.

The method invocation joinpoint is by far the most useful joinpoint available, and using it, you can achieve many of the tasks that make AOP useful in day-to-day programming. Remember that if you need to advise some code at a joinpoint other than a method invocation, you can always use Spring and AspectJ together.

Aspects in Spring

In Spring AOP, an aspect is represented by an instance of a class that implements the Advisor interface. Spring provides a selection of convenience Advisor implementations that you can use in your applications, thus removing the need for you to create lots of different Advisor implementations for your example. There are two subinterfaces of Advisor: IntroductionAdvisor and PointcutAdvisor. The PointcutAdvisor interface is implemented by all Advisors that use pointcuts to control the applicability of advice to joinpoints.

In Spring, introductions are treated as special kinds of advice. Using the IntroductionAdvisor interface, you can control those classes to which an introduction applies. We cover this in more detail in the next chapter.

We discuss the different PointcutAdvisor implementations in detail later in this chapter in the section titled "Advisors and Pointcuts in Spring."

The ProxyFactory Class

The ProxyFactory class controls the weaving and proxy creation process in Spring AOP. Before you can actually create a proxy, you must specify the advised or target object. You can do this, as you saw earlier, using the setTarget() method. Internally, ProxyFactory delegates the proxy creation process to an instance of DefaultAopProxyFactory, which in turn delegates to either Cglib2AopProxy or JdkDynamicAopProxy, depending on the settings of your application. We discuss proxy creation in more detail later in this chapter.

Using the ProxyFactory class, you control which aspects you want to weave into the proxy. As mentioned earlier, you can weave only an aspect—that is, advice combined with a pointcut—into advised code. However, in some cases you want advice to apply to the invocation of all methods in a class, not just a selection. For this reason, the ProxyFactory class provides the addAdvice() method that you saw in Listing 6-3. Internally, addAdvice() wraps the advice you pass it in an instance of DefaultPointcutAdvisor, which is the standard implementation of PointcutAdvisor, and configures it with a pointcut that includes all methods by default. When you want more control over the Advisor that is created, or when you want to add an introduction to the proxy, create the Advisor yourself and use the addAdvisor method of the ProxyFactory().

You can use the same ProxyFactory instance to create many different proxies, each with different aspects. To help with this, ProxyFactory has removeAdvice() and removeAdvisor() methods, which allow you to remove any advice or Advisor from the ProxyFactory that you previously passed to it. To check whether or not a ProxyFactory has particular advice attached to it, call adviceIncluded(), passing in the advice object for which you want to check.

Be aware that ProxyFactory defines quite a few methods that have been deprecated in favor of other methods such as addAdvice(). You can find full details of these methods in the Javadoc. Avoid using the deprecated methods, because they will likely be removed in future versions of Spring, and each has an alternative. If you stick with the methods used in this book, you will be OK.

Creating Advice in Spring

Spring supports five different flavors of advice, described in Table 5-1.

Table 5.1. Advice Types in Spring

Advice Name

Interface

Description

Before

org.springframework.aop.MethodBeforeAdvice

Using before advice, you can perform custom processing before a joinpoint executes. Because a joinpoint in Spring is always a method invocation, this essentially allows you to perform preprocessing before the method executes. Before advice has full access to the target of the method invocation as well as the arguments passed to the method, but it has no control over the execution of the method itself.

After returning

org.springframework.aop.AfterReturningAdvice

After returning advice is executed after the method invocation at the joinpoint has finished executing and has returned a value. The after returning advice has access to the target of the method invocation, the arguments passed to the method, and the return value as well. Because the method has already executed when the after returning advice is invoked, it has no control over the method invocation at all.

Around

org.aopalliance.intercept.MethodInterceptor

In Spring, around advice is modeled using the AOP Alliance standard of a method interceptor. Your advice is allowed to execute before and after the method invocation, and you can control the point at which the method invocation is allowed to proceed. You can choose to bypass the method altogether if you want, providing your own implementation of the logic.

Throws

org.springframework.aop.ThrowsAdvice

Throws advice is executed after a method invocation returns but only if that invocation threw an exception. It is possible for throws advice to catch only specific exceptions, and if you choose to do so, you can access the method that threw the exception, the arguments passed into the invocation, and the target of the invocation.

Introduction

org.springframework.aop.IntroductionInterceptor

Spring models introductions as special types of interceptors. Using an introduction interceptor, you can specify the implementation for methods that are being introduced by the advice. Introductions are covered in more detail in the next chapter.

We have found that these advice types, coupled with the method invocation joinpoint, allow us to perform about 90 percent of the tasks we want to perform with AOP. For the other 10 percent, which we use only rarely, we fall back on AspectJ.

Interfaces for Advice

From our previous discussion of the ProxyFactory class, recall that advice is added to a proxy either directly using the addAdvice() method or indirectly using an Advisor, with the addAdvisor() method. In previous releases of Spring, we had an addXXX() method for each type of advice (these methods are still present, albeit deprecated). Originally, each advice interface was separate from the others, but more recently, a well-defined hierarchy has been created for advice interfaces. This hierarchy is based on the AOP Alliance interfaces and is shown in detail in Figure 5-1.

This kind of hierarchy not only has the benefit of being sound OO design, but it also means that you can deal with advice types generically, as in using a single addAdvice() method on the ProxyFactory, and you can add new advice types easily without having to modify the ProxyFactory class.

Interfaces for Spring advice types

Figure 5.1. Interfaces for Spring advice types

Creating Before Advice

Before advice is one of the most useful advice types available in Spring. Before advice can modify the arguments passed to a method and can prevent the method from executing by raising an exception. In the next chapter, you will see before advice used frequently when we look at how AOP is used in the SpringBlog application. In this section, we show you two examples of using before advice: a simple example that writes a message to stdout containing the name of the method before the method executes and a simple security advice that you can use to restrict access to methods on an object.

In Listing 5-4, you can see the code for the SimpleBeforeAdvice class.

Example 5.4. The SimpleBeforeAdvice Class

package com.apress.prospring2.ch05.simple;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;

public class SimpleBeforeAdvice implements MethodBeforeAdvice {
public static void main(String[] args) {
        MessageWriter target = new MessageWriter();

        // create the proxy
        ProxyFactory pf = new ProxyFactory();

        pf.addAdvice(new SimpleBeforeAdvice());
        pf.setTarget(target);

        MessageWriter proxy = (MessageWriter) pf.getProxy();

        // write the messages
        proxy.writeMessage();
    }

    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        System.out.println("Before method: " + method.getName());
    }

}

In this code, you can see that we have advised an instance of the MessageWriter class that we created earlier with an instance of the SimpleBeforeAdvice class. The MethodBeforeAdvice interface, which is implemented by SimpleBeforeAdvice, defines a single method, before(), which the AOP framework calls before the method at the joinpoint is invoked. Remember that, for now, we are using the default pointcut provided by the addAdvice() method, which matches all methods in a class. The before() method is passed three arguments: the method that is to be invoked, the arguments that will be passed to that method, and the Object that is the target of the invocation. The SimpleBeforeAdvice class uses the Method argument of the before() method to write a message to stdout containing the name of the method to be invoked. Running this example gives us the following output:

Before method: writeMessage
World

As you can see, the output from the call to writeMessage is shown, but just before it, you can see the output generated by the SimpleBeforeAdvice.

Securing Method Access Using Before Advice

The previous example was fairly trivial and didn't really show the power of AOP. In this section, we are going to build before advice that checks user credentials before allowing the method invocation to proceed. If the user credentials are invalid, an exception is thrown by the advice, thus preventing the method from executing. The example in this section is simplistic. It allows users to authenticate with any password, and it also allows only a single, hard-coded user access to the secured methods. However, it does illustrate how easy it is to use AOP to implement a crosscutting concern such as security.

Listing 5-5 shows the SecureBean class. This is the class that we will be securing using AOP.

Example 5.5. The SecureBean Class

package com.apress.prospring2.ch05.security;

public class SecureBean {
public void writeSecureMessage() {
        System.out.println("Every time I learn something new, "
                + "it pushes some old stuff out of my brain");
    }
}

The SecureBean class imparts a small pearl of wisdom from Homer Simpson, wisdom that we don't want everyone to see. Because this example requires users to authenticate, we are somehow going to need to store their details. Listing 5-6 shows the UserInfo class we use to store a user's credentials.

Example 5.6. The UserInfo Class

package com.apress.prospring2.ch05.security;

public class UserInfo {
    private String username;
    private String password;

    public UserInfo(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getPassword() {
        return password;
    }

    public String getUsername() {
        return username;
    }
}

There is nothing of particular interest in this class; it simply holds data about the user so that we can do something useful with it. Listing 5-7 shows the SecurityManager class, which is responsible for authenticating users and storing their credentials for later retrieval.

Example 5.7. The SecurityManager Class

package com.apress.prospring2.ch05.security;

public class SecurityManager {

    private static ThreadLocal<UserInfo> threadLocal = new ThreadLocal<UserInfo>();

    public void login(String username, String password) {
        // assumes that all credentials
        // are valid for a login
        threadLocal.set(new UserInfo(username, password));
    }

    public void logout() {
        threadLocal.set(null);
    }
public UserInfo getLoggedOnUser() {
        return threadLocal.get();
    }
}

The application uses the SecurityManager class to authenticate a user and, later, to retrieve the details of the currently authenticated user. The application authenticates a user using the login() method. In a real application, the login() method would probably check the supplied application against a database or Lightweight Directory Access Protocol (LDAP) directory, but here, we assume all users are allowed to authenticate. The login() method creates a UserInfo object for the user and stores it on the current thread using a ThreadLocal. The logout() method sets any value that might be stored in the ThreadLocal to null. Finally, the getLoggedOnUser() method returns the UserInfo object for the currently authenticated user. This method returns null if no user is authenticated.

To check whether or not a user is authenticated and, if so, whether or not the user is permitted to access the methods on SecureBean, we need to create advice that executes before the method and checks the UserInfo object returned by SecurityManager.getLoggedOnUser() against the set of credentials for allowed users. The code for this advice, SecurityAdvice, is shown in Listing 5-8.

Example 5.8. The SecurityAdvice Class

package com.apress.prospring2.ch05.security;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class SecurityAdvice implements MethodBeforeAdvice {

    private SecurityManager securityManager;

    public SecurityAdvice() {
        this.securityManager = new SecurityManager();
    }

    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        UserInfo user = securityManager.getLoggedOnUser();

        if (user == null) {
            System.out.println("No user authenticated");
            throw new SecurityException(
                    "You must log in before attempting to invoke the method: "
                            + method.getName());
        } else if ("janm".equals(user.getUsername())) {
            System.out.println("Logged in user is janm - OKAY!");
        } else {
            System.out.println("Logged in user is " + user.getUsername()
                    + " NOT GOOD :(");
            throw new SecurityException("User " + user.getUsername()
                    + " is not allowed access to method " + method.getName());
        }

    }
}

The SecurityAdvice class creates an instance of SecurityManager in its constructor and stores this instance in a field. You should note that the application and the SecurityAdvice don't need to share the same SecurityManager instance, because all data is stored with the current thread using ThreadLocal. In the before() method, we perform a simple check to see if the user name of the authenticated user is janm. If so, we allow the user access; otherwise, an exception is raised. Also notice that we check for a null UserInfo object, which indicates that the current user is not authenticated.

In Listing 5-9, you can see a sample application that uses the SecurityAdvice class to secure the SecureBean class.

Example 5.9. The SecurityExample Class

package com.apress.prospring2.ch05.security;

import org.springframework.aop.framework.ProxyFactory;

public class SecurityExample {

    public static void main(String[] args) {
        // get the security manager
        SecurityManager mgr = new SecurityManager();

        // get the bean
        SecureBean bean = getSecureBean();

        // try as janm
        mgr.login("janm", "*****");
        bean.writeSecureMessage();
        mgr.logout();

        // try as aleksav
        try {
            mgr.login("aleksav", "****");
            bean.writeSecureMessage();
        } catch(SecurityException ex) {
            System.out.println("Exception Caught: " + ex.getMessage());
        } finally {
            mgr.logout();
        }

        // try with no credentials
        try {
            bean.writeSecureMessage();
        } catch(SecurityException ex) {
            System.out.println("Exception Caught: " + ex.getMessage());
        }

    }

    private static SecureBean getSecureBean() {
        // create the target
        SecureBean target = new SecureBean();

        // create the advice
        SecurityAdvice advice = new SecurityAdvice();
// get the proxy
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvice(advice);

        return (SecureBean)factory.getProxy();

    }
}

In the getSecureBean() method, we create a proxy of the SecureBean class that is advised using an instance of SecurityAdvice. This proxy is returned to the caller. When the caller invokes any method on this proxy, the call is first routed to the instance of SecurityAdvice for a security check. In the main() method, we test three different scenarios, invoking the SecureBean.writeSecureMessage() method with two different sets of user credentials and then no user credentials at all. Because the SecurityAdvice only allows method calls to proceed if the currently authenticated user is janm, we expect that the only successful scenario in Listing 5-9 is the first of these scenarios. Running this example gives the following output:

Logged in user is janm - OKAY!
Every time I learn something new, it pushes some old stuff out of my brain
Logged in user is aleksav NOT GOOD :(
Exception Caught: User aleksav is not allowed access to method writeSecureMessage
No user authenticated
Exception Caught: You must log in before attempting to invoke the method: 
The SecurityExample Class
writeSecureMessage

As you can see, only the first invocation of SecureBean.writeSecureMessage() was allowed to proceed. The remaining invocations were prevented by the SecurityException thrown by the SecurityAdvice.

This example is simple, but it does highlight the usefulness of the before advice. Security is a typical example of before advice, but we also find it useful when a scenario demands the modification of arguments going into the method. In Chapter 6, we show you how to use before advice to create an obscenity filter.

Creating After Returning Advice

As its name implies, after returning advice is executed after the method invocation at the joinpoint returns. Given that the method has already executed, you can't change the arguments that are passed to it. Although you can read these, you can neither change the execution path nor prevent the method from executing. These restrictions are expected; what you might not expect, however, is that you cannot modify the return value in the after returning advice; you are restricted to performing some additional processing. Although after returning advice cannot modify the return value of a method invocation, it can throw an exception that can be sent up the stack instead of the return value.

In this section, we look at two examples of using after returning advice in an application. The first example simply writes a message to stdout after the method has been invoked. The second example shows how you can use after returning advice to add additional error checking to a method. Consider a class KeyGenerator that generates keys for cryptographic purposes. Many cryptographic algorithms suffer from the problem that a small number of keys in the keyspace are considered weak. A weak key is any key whose characteristics make it significantly easier to derive the original message without knowing the key. For the DES algorithm, there are a total of 256 possible keys. From this keyspace, four keys are considered weak and another twelve are considered semiweak. Although the chance of one of these keys being generated randomly is ridiculously small (1 in 252), testing for the keys is so simple that it seems lax not to do so. In the second example of this section, we build an after returning advice that checks for weak keys generated by the KeyGenerator and raises an exception if one is found.

Note

For more information on weak keys and cryptography at large, we recommend that you read Applied Cryptography by Bruce Schneier (Wiley, 1995).

In Listing 5-10, you can see the SimpleAfterReturningAdvice class, which demonstrates the use of after returning advice by writing a message to stdout after a method has returned.

Example 5.10. The SimpleAfterReturningAdvice Class

package com.apress.prospring2.ch05.simple;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.framework.ProxyFactory;

public class SimpleAfterReturningAdvice implements AfterReturningAdvice {

    public static void main(String[] args) {
        MessageWriter target = new MessageWriter();

        // create the proxy
        ProxyFactory pf = new ProxyFactory();

        pf.addAdvice(new SimpleAfterReturningAdvice());
        pf.setTarget(target);

        MessageWriter proxy = (MessageWriter) pf.getProxy();

        // write the messages
        proxy.writeMessage();
    }

    public void afterReturning(Object returnValue, Method method, Object[] args,
            Object target) throws Throwable {
        System.out.println("");
        System.out.println("After method: " + method.getName());
    }
}

This example is really not that different from the SimpleBeforeAdvice class that you saw earlier. Notice that the AfterReturningAdvice interface declares a single method, afterReturning(), which is passed the return value of method invocation, a reference to the method that was invoked, the arguments that were passed to the method, and the target of the invocation. Running this example results in the following output:

World
After method: writeMessage

The output is very similar to that of the before advice example except that, as expected, the message written by the advice appears after the message written by the writeMessage() method.

A good use of after returning advice is to perform some additional error checking when it is possible for a method to return an invalid value. In the scenario we described earlier, it is possible for a cryptographic key generator to generate a key that is considered weak for a particular algorithm. Ideally, the key generator would check for these weak keys, but since the chance of these keys arising is often very small, many generators do not check. By using after returning advice, we can advise the method that generates the key and performs this additional check. Listing 5-11 shows an extremely primitive key generator.

Example 5.11. The KeyGenerator Class

package com.apress.prospring2.ch05.crypto;

import java.util.Random;

public class KeyGenerator {

    public static final long WEAK_KEY = 0xFFFFFFF0000000L;
    public static final long STRONG_KEY = 0xACDF03F590AE56L;

    private Random rand = new Random();

    public long getKey() {
        int x = rand.nextInt(3);

        if(x == 1) {
            return WEAK_KEY;
        } else {
            return STRONG_KEY;
        }
    }
}

It is plain to see that this key generator is ridiculously insecure, but we didn't want you to have to wait around for years while a real key generator produced a weak key, so we created this generator, which has a one-in-three chance of producing a weak key. In Listing 5-12, you can see the WeakKeyCheckAdvice that checks to see if the result of the getKey() method is a weak key.

Example 5.12. Checking for Weak Keys

package com.apress.prospring2.ch05.crypto;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class WeakKeyCheckAdvice implements AfterReturningAdvice {

    public void afterReturning(Object returnValue, Method method,
            Object[] args, Object target) throws Throwable {

        if ((target instanceof KeyGenerator)
                && ("getKey".equals(method.getName()))) {
            long key = (Long) returnValue;
if (key == KeyGenerator.WEAK_KEY) {
                throw new SecurityException(
                        "Key Generator generated a weak key. Try again");
            }
        }
    }
}

In the afterReturning() method, we check first to see if the method that was executed at the joinpoint was the getKey() method. If so, we then check the result value to see if it was the weak key. If we find that the result of the getKey() method was a weak key, we throw a SecurityException to inform the calling code of this. Listing 5-13 shows a simple application that demonstrates the use of this advice.

Example 5.13. Testing the WeakKeyCheckAdvice Class

package com.apress.prospring2.ch05.crypto;

import org.springframework.aop.framework.ProxyFactory;

public class AfterAdviceExample {

    public static void main(String[] args) {
        KeyGenerator keyGen = getKeyGenerator();

        for(int x = 0; x < 10; x++) {
            try {
                long key = keyGen.getKey();
                System.out.println("Key: " + key);
            } catch(SecurityException ex) {
                System.out.println("Weak Key Generated!");
            }
        }

    }

    private static KeyGenerator getKeyGenerator() {

        KeyGenerator target = new KeyGenerator();

        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvice(new WeakKeyCheckAdvice());

        return (KeyGenerator)factory.getProxy();
    }
}

After creating an advised proxy of a KeyGenerator target, the AfterAdviceExample class attempts to generate ten keys. If a SecurityException is thrown during a single generation, a message is written to stdout informing the user that a weak key was generated; otherwise, the generated key is displayed. A single run of this on our machine generated the following output:

Weak Key Generated!
Key: 48658904092028502
Key: 48658904092028502
Key: 48658904092028502
Key: 48658904092028502
Key: 48658904092028502
Weak Key Generated!
Weak Key Generated!
Key: 48658904092028502
Weak Key Generated!

As you can see, the KeyGenerator class sometimes generates weak keys, as expected, and the WeakKeyCheckAdvice ensures that a SecurityException is raised whenever a weak key is encountered.

Creating Around Advice

Around advice functions like a combination of before and after advice, with one big difference—you can modify the return value. Not only that, you can also prevent the method from actually executing. This means that using around advice, you can essentially replace the entire implementation of a method with new code. Around advice in Spring is modeled as an interceptor using the MethodInterceptor interface. There are many uses for around advice, and you will find that many features of Spring use method interceptors, such as the remote proxy support and the transaction management features. Method interception is also a good mechanism for profiling the execution of your application, and it forms the basis of the example in this section.

We are not going to build a simple example for method interception; instead, we refer to the first example in Listing 5-2, which shows how to use a basic method interceptor to write out a message on either side of a method invocation. Notice from this earlier example that the invoke method of the MethodInterceptor class does not provide the same set of arguments as the MethodBeforeAdvice and AfterReturningAdvice—that is, the method is not passed the target of the invocation, the method that was invoked, or the arguments used. However, you can get access to this data using the MethodInvocation object that is passed to invoke(). You will see a demonstration of this in the following example.

For this example, we want to achieve some way to advise a class so that we get basic information about the runtime performance of its methods. Specifically, we want to know how long the method took to execute. To achieve this, we can use the StopWatch class included in Spring, and we clearly need a MethodInterceptor, because we need to start the StopWatch before the method invocation and stop it right afterward.

Listing 5-14 shows the WorkerBean class that we are going to profile using the StopWatch class and an around advice.

Example 5.14. The WorkerBean Class

package com.apress.prospring2.ch05.profiling;

public class WorkerBean {

    public void doSomeWork(int noOfTimes) {
        for(int x = 0; x < noOfTimes; x++) {
            work();
        }
    }
private void work() {
        System.out.print("");
    }
}

This is a very simple class. The doSomeWork() method accepts a single argument, noOfTimes, and calls the work() method exactly the number of times specified by this method. The work() method simply has a dummy call to System.out.print(), which passes in an empty String. This prevents the compiler from optimizing out the work method and thus the call to work().

In Listing 5-15, you can see the ProfilingInterceptor class that uses the StopWatch class to profile method invocation times. We use this interceptor to profile the WorkerBean class shown in Listing 5-14.

Example 5.15. The ProfilingInterceptor Class

package com.apress.prospring2.ch05.profiling;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.StopWatch;

public class ProfilingInterceptor implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        // start the stop watch
        StopWatch sw = new StopWatch();
        sw.start(invocation.getMethod().getName());

        Object returnValue = invocation.proceed();

        sw.stop();
        dumpInfo(invocation, sw.getTotalTimeMillis());
        return returnValue;
    }

    private void dumpInfo(MethodInvocation invocation, long ms) {
        Method m = invocation.getMethod();
        Object target = invocation.getThis();
        Object[] args = invocation.getArguments();

        System.out.println("Executed method: " + m.getName());
        System.out.println("On object of type: " + target.getClass().getName());

        System.out.println("With arguments:");
        for (int x = 0; x < args.length; x++) {
            System.out.print("    > " + args[x]);
        }
        System.out.print("
");

        System.out.println("Took: " + ms + " ms");
    }
}

In the invoke() method, which is the only method in the MethodInterceptor interface, we create an instance of StopWatch and start it running immediately, allowing the method invocation to proceed with a call to MethodInvocation.proceed(). As soon as the method invocation has ended and the return value has been captured, we stop the StopWatch and pass the total number of milliseconds taken, along with the MethodInvocation object, to the dumpInfo() method. Finally, we return the Object returned by MethodInvocation.proceed() so that the caller obtains the correct return value. In this case, we did not want to disrupt the call stack in any way; we were simply acting as an eavesdropper on the method invocation. If we had wanted to, we could have changed the call stack completely, redirecting the method call to another object or a remote service, or we could simply have reimplemented the method logic inside the interceptor and returned a different return value.

The dumpInfo method writes some information about the method call to stdout, along with the time taken for the method to execute. In the first three lines of dumpInfo(), you can see how you can use the MethodInvocation object to determine the method that was invoked, the original target of the invocation, and the arguments used.

Listing 5-16 shows the ProfilingExample class that first advises an instance of WorkerBean with a ProfilingInterceptor and then profiles the doSomeWork() method.

Example 5.16. The ProfilingExample Class

package com.apress.prospring2.ch05.profiling;

import org.springframework.aop.framework.ProxyFactory;

public class ProfilingExample {

    public static void main(String[] args) {
        WorkerBean bean = getWorkerBean();
        bean.doSomeWork(10000000);
    }

    private static WorkerBean getWorkerBean() {
        WorkerBean target = new WorkerBean();

        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvice(new ProfilingInterceptor());

        return (WorkerBean)factory.getProxy();
    }
}

You should be more than familiar with this code by now. Running this example on our machine produces the following output:

Executed method: doSomeWork
On object of type: com.apress.prospring.ch05.profiling.WorkerBean
With arguments:
    > 10000000
Took: 1898 ms

From this output, you can see which method was executed, what the class of the target was, what arguments were passed in, and how long the invocation took.

Creating Throws Advice

Throws advice is similar to after returning advice in that it executes after the joinpoint, which is always a method invocation, but throws advice only executes if the method threw an exception. Throws advice is also similar to after returning advice in that it has little control over program execution. If you are using a throws advice, you can't choose to ignore the exception that was raised and return a value for the method instead. The only modification you can make to the program flow is to change the type of exception that is thrown. This is actually quite a powerful idea and can make application development much simpler. Consider a situation where you have an API that throws an array of poorly defined exceptions. Using throws advice, you can advise all classes in that API and reclassify the exception hierarchy into something more manageable and descriptive. Of course, you can also use throws advice to provide centralized error logging across your application, thus reducing the amount of error logging code that is spread across your application.

As you saw from the diagram in Figure 5-1, throws advice is implemented by the ThrowsAdvice interface. Unlike the interfaces you have seen so far, ThrowsAdvice does not define any methods; instead, it is simply a marker interface used by Spring. The reason for this is that Spring allows typed throws advice, which allows you to define exactly which Exception types your throws advice should catch. Spring achieves this by detecting methods with certain signatures using reflection. Spring looks for two distinct method signatures. This is best demonstrated with an example. Listing 5-17 shows a simple bean with two methods that throw exceptions of different types.

Example 5.17. The ErrorBean Class

package com.apress.prospring2.ch05.simple;

public class ErrorBean {

    public void errorProneMethod() throws Exception {
        throw new Exception("Foo");
    }

    public void otherErrorProneMethod() throws IllegalArgumentException {
        throw new IllegalArgumentException("Bar");
    }
}

In Listing 5-18, you can see the SimpleThrowsAdvice class that demonstrates both of the method signatures that Spring looks for on a throws advice.

Example 5.18. The SimpleThrowsAdvice Class

package com.apress.prospring2.ch05.simple;

import java.lang.reflect.Method;

import org.springframework.aop.ThrowsAdvice;
import org.springframework.aop.framework.ProxyFactory;

public class SimpleThrowsAdvice implements ThrowsAdvice {

    public static void main(String[] args) throws Exception {
        ErrorBean errorBean = new ErrorBean();
ProxyFactory pf = new ProxyFactory();
        pf.setTarget(errorBean);
        pf.addAdvice(new SimpleThrowsAdvice());

        ErrorBean proxy = (ErrorBean) pf.getProxy();

        try {
            proxy.errorProneMethod();
        } catch (Exception ignored) {

        }

        try {
            proxy.otherErrorProneMethod();
        } catch (Exception ignored) {

        }

    }

    public void afterThrowing(Exception ex) throws Throwable {
        System.out.println("***");
        System.out.println("Generic Exception Capture");
        System.out.println("Caught: " + ex.getClass().getName());
        System.out.println("***
");
    }

    public void afterThrowing(Method method, Object[] args, Object target,
            IllegalArgumentException ex) throws Throwable {
        System.out.println("***");
        System.out.println("IllegalArgumentException Capture");
        System.out.println("Caught: " + ex.getClass().getName());
        System.out.println("Method: " + method.getName());
        System.out.println("***
");
    }
}

We are sure that you understand the code in the main() method, so now we will just focus on the two afterThrowing() methods. The first thing Spring looks for in a throws advice is one or more public methods called afterThrowing(). The return type of the methods is unimportant, although we find it best to stick with void because this method can't return any meaningful value. The first afterThrowing() method in the SimpleThrowsAdvice class has a single argument of type Exception. You can specify any type of Exception as the argument, and this method is ideal when you are not concerned about the method that threw the exception or the arguments that were passed to it. Note that this method catches Exception and any subtypes of Exception unless the type in question has its own afterThrowing() method.

In the second afterThrowing() method, we declared four arguments to catch the Method that threw the exception, the arguments that were passed to the method, and the target of the method invocation. The order of the arguments in this method is important, and you must specify all four. Notice that the second afterThrowing() method catches exceptions of type IllegalArgumentException (or its subtype). Running this example produces the following output:

***
Generic Exception Capture
Caught: java.lang.Exception
***

***
IllegalArgumentException Capture
Caught: java.lang.IllegalArgumentException
Method: otherErrorProneMethod
***

As you can see, when a plain old Exception is thrown, the first afterThrowing() method is invoked, but when an IllegalArgumentException is thrown, the second afterThrowing() method is invoked. Spring only invokes a single afterThrowing() method for each Exception, and as you saw from the example in Listing 5-18, Spring uses the method whose signature contains the best match for the Exception type. In the situation where your after throwing advice has two afterThrowing() methods, both declared with the same Exception type but one with a single argument and the other with four arguments, Spring invokes the four-argument afterThrowing() method.

As we mentioned earlier, after throwing advice is useful in a variety of situations; it allows you to reclassify entire Exception hierarchies as well as build centralized Exception logging for your application. We have found that after throwing advice is particularly useful when we are debugging a live application, because it allows us to add extra logging code without needing to modify the application's code.

Choosing an Advice Type

In general, the choice of which advice type you want to use is driven by the requirements of your application, but you should choose the most specific advice type for your needs. That is to say, don't use around advice when a before advice will do. In most cases, around advice can accomplish everything that the other three advice types can, but it may be overkill for what you are trying to achieve. By using the type of advice most appropriate for your specific circumstances, you are making the intention of your code clearer and reducing the possibility of errors. Consider an advice that counts method calls. When you are using before advice, all you need to code is the counter, but with around advice, you need to remember to invoke the method and return the value to the caller. These small things can allow spurious errors to creep into your application. By keeping the advice type as focused as possible, you reduce the scope for errors.

Advisors and Pointcuts in Spring

Thus far, all the examples you have seen have used the ProxyFactory.addAdvice method to configure advice for a proxy. As we mentioned earlier, this method delegates to addAdvisor() behind the scenes, creating an instance of DefaultPointcutAdvisor and configuring it with a pointcut that points to all methods. In this way, the advice is deemed to apply to all methods on the target. In some cases, such as when you are using AOP for logging purposes, this may be desirable, but in other cases, you may want to limit the methods to which an advice applies.

Of course, you could simply check, in the advice itself, that the method that is being advised is the correct one, but this approach has several drawbacks. First, hard coding the list of acceptable methods into the advice reduces the advice's reusability. By using pointcuts, you can configure the methods to which an advice applies, without needing to put this code inside the advice; this clearly increases the reuse value of the advice. The second and third drawbacks with hard-coding the list of methods into the advice are performance related. To check the method being advised in the advice, you need to perform the check each time any method on the target is invoked. This clearly reduces the performance of your application. When you use pointcuts, the check is performed once for each method, and the results are cached for later use. The other performance-related drawback of not using pointcuts to restrict the list-advised methods is that Spring can make optimizations for nonadvised methods when creating a proxy, which results in faster invocations on nonadvised methods. These optimizations are covered in greater detail when we discuss proxies later in the chapter.

We strongly recommend that you avoid the temptation to hard-code method checks into your advice and instead use pointcuts wherever possible to govern the applicability of advice to methods on the target. That said, in some cases, you need to hard-code the checks into your advice. Consider the earlier example of the after returning advice designed to catch weak keys generated by the KeyGenerator class. This kind of advice is closely coupled to the class it is advising, and checking inside the advice is wise, to ensure that the advice is applied to the correct type. We refer to this coupling between advice and target as target affinity. In general, you should use pointcuts when your advice has little or no target affinity—that is, it can apply to any type or a wide range of types. When your advice has strong target affinity, try to check that the advice is being used correctly in the advice itself; this helps reduce head-scratching errors when advice is misused. We also recommend that you avoid advising methods needlessly. As you will see, needless advice results in a noticeable drop in invocation speed that can have a large impact on the overall performance of your application.

The Pointcut Interface

Pointcuts in Spring are created by implementing the Pointcut interface, shown in Listing 5-19.

Example 5.19. The Pointcut Interface

public interface Pointcut {

    ClassFilter getClassFilter ();

    MethodMatcher getMethodMatcher();
}

As you can see from this code, the Pointcut interface defines two methods, getClassFilter() and getMethodMatcher(), which return instances of ClassFilter and MethodMatcher, respectively. When creating your own pointcuts from scratch, you must implement both the ClassFilter and MethodMatcher interfaces as well. Thankfully, as you will see in the next section, this is usually unnecessary because Spring provides a selection of Pointcut implementations that cover most, if not all, of your use cases.

When determining whether a Pointcut applies to a particular method, Spring first checks to see if the Pointcut applies to the method's class using the ClassFilter instance returned by Pointcut.getClassFilter(). Listing 5-20 shows the ClassFilter interface.

Example 5.20. The ClassFilter Interface

public interface ClassFilter {

    boolean matches(Class clazz);
}

As you can see, the ClassFilter interface defines a single method, matches(), that is passed an instance of Class that represents the class to be checked. As you have no doubt determined, the matches() method returns true if the pointcut applies to the class and false otherwise.

The MethodMatcher interface is more complex than the ClassFilter interface, as shown in Listing 5-21.

Example 5.21. The MethodMatcher Interface

public interface MethodMatcher {

    boolean matches(Method m, Class targetClass);

    boolean isRuntime();

    boolean matches(Method m, Class targetClass, Object[] args);

}

Spring supports two different types of MethodMatcher, static and dynamic, determined by the return value of isRuntime(). Before using a MethodMatcher, Spring calls isRuntime() to determine whether the MethodMatcher is static, indicated by a return value of false, or dynamic, indicated by a return value of true.

For a static pointcut, Spring calls the matches(Method, Class) method of the MethodMatcher once for every method on the target, caching the return value for subsequent invocations of those methods. In this way, the check for method applicability is performed only once for each method and subsequent invocations of a method do not result in an invocation of matches().

With dynamic pointcuts, Spring still performs a static check using matches(Method, Class) the first time a method is invoked to determine the overall applicability of a method. However, in addition to this and provided that the static check returned true, Spring performs a further check for each invocation of a method using the matches(Method, Class, Object[]) method. In this way, a dynamic MethodMatcher can determine whether a pointcut should apply based on a particular invocation of a method, not just on the method itself.

Clearly, static pointcuts—that is, pointcuts whose MethodMatcher is static—perform much better than dynamic pointcuts, because they avoid the need for an additional check per invocation. That said, dynamic pointcuts provide a greater level of flexibility for deciding whether to apply advice. In general, we recommend that you use static pointcuts wherever you can. However, in cases where your advice adds substantial overhead, it may be wise to avoid any unnecessary invocations of your advice by using a dynamic pointcut.

In general, you rarely create your own Pointcut implementations from scratch, because Spring provides abstract base classes for both static and dynamic pointcuts. We look at these base classes, along with other Pointcut implementations, over the next few sections.

Available Pointcut Implementations

Spring provides ten implementations of the Pointcut interface: two abstract classes intended as convenience classes for creating static and dynamic pointcuts and eight concrete classes. These implementations are summarized in Table 5-2.

Table 5.2. Summary of Spring Pointcut Implementations

Implementation Class

Description

org.springframework.aop. support.ComposablePointcut

The ComposablePointcut class is used to compose two or more pointcuts together with operations such as union() and intersection(). This class is covered in more detail in the next chapter.

org.springframework.aop. support.ControlFlowPointcut

The ControlFlowPointcut is a special case pointcut that matches all methods within the control flow of another method, that is, any method that is invoked either directly or indirectly as the result of another method being invoked. We cover ControlFlowPointcut in more detail in the next chapter.

org.springframework.aop. support.JdkRegexpMethodPointcut

The JdkRexepMethodPointcut allows you to define pointcuts using JDK 1.4 regular expression support. This class requires JDK 1.4 or higher.

org.springframework.aop. support.NameMatchMethodPointcut

Using the NameMatchMethodPointcut, you can create a pointcut that performs simple matching against a list of method names.

org.springframework.aop. StaticMethodMatcherPointcut

The StaticMethodMatcherPointcut class is intended as a base for building static pointcuts.

org.springframework.aop. DynamicMethodMatcherPointcut

The DynamicMethodMatcherPointcut class is a convenient superclass for building dynamic pointcuts that are aware of method arguments at runtime.

org.springframework.aop. AnnotationMatchingPointcut

The AnnotationMatchingPointcut class is used for creating Java 5 annotated pointcuts.

org.springframework.aop. AspectJExpressionPointcut

The AspectJExpressionPointcut convenient class is used for defining pointcuts using AspectJ expression language. Note that only method execution pointcuts can be defined, as Spring AOP does not support other AspectJ pointcuts in the current version.

We cover the basic implementations in detail in the following sections.

Using DefaultPointcutAdvisor

Before you can use any Pointcut implementation, you must first create an Advisor, more specifically a PointcutAdvisor. Remember from our earlier discussions that an Advisor is Spring's representation of an aspect, a coupling of advice and pointcuts that governs which methods should be advised and how they should be advised. Spring provides four implementations of PointcutAdvisor, but for now, we concern ourselves we just one—DefaultPointcutAdvisor. DefaultPointcutAdvisor is a simple PointcutAdvisor for associating a single Pointcut with a single Advice.

Creating a Static Pointcut Using StaticMethodMatcherPointcut

In this section, we will create a simple static pointcut using the StaticMethodMatcherPointcut class as a base. StaticMethodMatcherPointcut requires you to implement only a single method, matches(Method, Class); the rest of the Pointcut implementation is handled automatically. Although this is the only method you are required to implement, you may also want to override the getClassFilter() method as we do in this example to ensure that only methods of the correct type get advised.

For this example, we have two classes, BeanOne and BeanTwo, with identical methods defined in both. Listing 5-22 shows the BeanOne class.

Example 5.22. The BeanOne Class

package com.apress.prospring2.ch05.staticpc;

public class BeanOne {

    public void foo() {
        System.out.println("foo");
    }

    public void bar() {
        System.out.println("bar");
    }
}

The BeanTwo class has identical methods to BeanOne. With this example, we want to be able to create a proxy of both classes using the same DefaultPointcutAdvisor but have the advice apply to only the foo() method of the BeanOne class. To do this, we created the SimpleStaticPointcut class shown in Listing 5-23.

Example 5.23. The SimpleStaticPointcut Class

package com.apress.prospring2.ch05.staticpc;

import java.lang.reflect.Method;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcut;

public class SimpleStaticPointcut extends StaticMethodMatcherPointcut {

    public boolean matches(Method method, Class cls) {
        return ("foo".equals(method.getName()));
    }

    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            public boolean matches(Class cls) {
                return (cls == BeanOne.class);
            }
        };

    }
}

Here, you can see that we implemented the matches(Method, Class) method as required by the StaticMethodMatcher base class. The implementation simply returns true if the name of the method is foo; otherwise, it returns false. Notice that we have also overridden the getClassFilter() method to return a ClassFilter instance whose matches() method only returns true for the BeanOne class. With this static pointcut, we are saying that only methods of the BeanOne class will be matched, and furthermore, only the foo() method of that class will be matched.

Listing 5-24 shows the SimpleAdvice class that simply writes out a message on either side of the method invocation.

Example 5.24. The SimpleAdvice Class

package com.apress.prospring2.ch05.staticpc;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class SimpleAdvice implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println(">> Invoking " + invocation.getMethod().getName());
        Object retVal = invocation.proceed();
        System.out.println(">> Done");
        return retVal;
    }
}

In Listing 5-25, you can see a simple driver application for this example that creates an instance of DefaultPointcutAdvisor using the SimpleAdvice and SimpleStaticPointcut classes.

Example 5.25. The StaticPointcutExample Class

package com.apress.prospring2.ch05.staticpc;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class StaticPointcutExample {

    public static void main(String[] args) {
        BeanOne one = new BeanOne();
        BeanTwo two = new BeanTwo();

        BeanOne proxyOne;
        BeanTwo proxyTwo;

        // create pointcut, advice, and advisor
        Pointcut pc = new SimpleStaticPointcut();
        Advice advice = new SimpleAdvice();
        Advisor advisor = new DefaultPointcutAdvisor(pc, advice);

        // create BeanOne proxy
        ProxyFactory pf = new ProxyFactory();
        pf.addAdvisor(advisor);
        pf.setTarget(one);
        proxyOne = (BeanOne)pf.getProxy();

        // create BeanTwo proxy
        pf = new ProxyFactory();
        pf.addAdvisor(advisor);
        pf.setTarget(two);
        proxyTwo = (BeanTwo)pf.getProxy();

        proxyOne.foo();
        proxyTwo.foo();
proxyOne.bar();
        proxyTwo.bar();

    }
}

Notice that the DefaultPointcutAdvisor instance is then used to create two proxies: one for an instance of BeanOne and one for an instance of BeanTwo. Finally, both the foo() and bar() methods are invoked on the two proxies.

Running this example results in the following output:

>> Invoking foo
foo
>> Done
foo
bar
bar

As you can see, the only method for which the SimpleAdvice was actually invoked was the foo() method for the BeanOne class, exactly as expected. Restricting the methods that an advice applies is quite simple and, as you will see when we discuss the different proxy options, is key to getting the best performance out of your application.

Creating a Dynamic Pointcut Using DynamicMethodMatcherPointcut

As we will demonstrate in this section, creating a dynamic pointcut is not much different from creating a static one. For this example, we create a dynamic pointcut for the class shown in Listing 5-26.

Example 5.26. The SampleBean Class

package com.apress.prospring2.ch05.dynamicpc;

public class SampleBean {

    public void foo(int x) {
        System.out.println("Invoked foo() with: "  +x);
    }

    public void bar() {
        System.out.println("Invoked bar()");
    }
}

For this example, we want to advise only the foo() method, but unlike the previous example, we want to advise this method only if the int argument passed to it is greater or less than 100.

As with static pointcuts, Spring provides a convenience base class for creating dynamic pointcuts—DynamicMethodMatcherPointcut. The DynamicMethodMatcherPointcut class has a single abstract method, matches(Method, Class, Object[]), that you must implement, but as you will see, implementing the matches(Method, Class) method to control the behavior of the static checks is also prudent. Listing 5-27 shows the SimpleDynamicPointcut class.

Example 5.27. The SimpleDynamicPointcut Class

package com.apress.prospring2.ch05.dynamicpc;

import java.lang.reflect.Method;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;

public class SimpleDynamicPointcut extends DynamicMethodMatcherPointcut {

    public boolean matches(Method method, Class cls) {
        System.out.println("Static check for " + method.getName());
        return ("foo".equals(method.getName()));
    }

    public boolean matches(Method method, Class cls, Object[] args) {
        System.out.println("Dynamic check for " + method.getName());
        int x = (Integer) args[0];
        return (x != 100);
    }

    public ClassFilter getClassFilter() {
        return new ClassFilter() {

            public boolean matches(Class cls) {
                return (cls == SampleBean.class);
            }
        };
    }
}

As Listing 5-27 shows, we override the getClassFilter() method in a similar manner to the previous example shown in Listing 5-23. This removes the need to check the class in the method-matching methods—something that is especially important for the dynamic check. Although we are only required to implement the dynamic check, we implement the static check as well, because we know the bar() method will never be advised. By indicating this using the static check, Spring ensures it never has to perform a dynamic check for this method. If we neglect the static check, Spring performs a dynamic check each time the bar method is invoked even though it always returns false. In the matches(Method, Class, Object[]) method, you can see that we return false if the value of the int argument passed to the foo() method is false; otherwise, we return true. Note that in the dynamic check, we know that we are dealing with the foo() method, because no other method makes it past the static check.

In Listing 5-28, you can see an example of this pointcut in action.

Example 5.28. The DynamicPointcutExample Class

package com.apress.prospring2.ch02.dynamicpc;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import com.apress.prospring.ch6.staticpc.SimpleAdvice;

public class DynamicPointcutExample {
public static void main(String[] args) {
        SampleBean target = new SampleBean();

        // create advisor
        Advisor advisor = new DefaultPointcutAdvisor(
                new SimpleDynamicPointcut(), new SimpleAdvice());

        // create proxy
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        SampleBean proxy = (SampleBean)pf.getProxy();

        proxy.foo(1);
        proxy.foo(10);
        proxy.foo(100);

        proxy.bar();
        proxy.bar();
        proxy.bar();
    }
}

Notice that we have used the same advice class as in the static pointcut example. However, in this example, only the first two calls to foo() should be advised. The dynamic check prevents the third call to foo() from being advised, and the static check prevents the bar() method from being advised. Running this example yields the following output:

Static check for foo
Static check for bar
Static check for toString
Static check for clone
Static check for foo
Dynamic check for foo
>> Invoking foo
Invoked foo() with: 1
>> Done
Dynamic check for foo
>> Invoking foo
Invoked foo() with: 10
>> Done
Dynamic check for foo
Invoked foo() with: 100
Static check for bar
Invoked bar()
Invoked bar()
Invoked bar()

As we expected, only the first two invocations of the foo() method were advised. Notice that none of the bar() invocations is subject to a dynamic check, thanks to the static check on bar(). An interesting point to note here is that the foo() method is actually subject to two static checks: one during the initial phase when all methods are checked and another when it is first invoked.

As you can see, dynamic pointcuts offer a greater degree of flexibility than static pointcuts, but due to the additional runtime overhead they require, you should only use a dynamic pointcut when absolutely necessary.

Using Simple Name Matching

Often when creating a pointcut, you want to match based on just the name of the method, ignoring the method signature and return type. In this case, you can avoid needing to create a subclass of StaticMethodMatcherPointcut and use the NameMatchMethodPointcut to match against a list of method names instead. When you are using NameMatchMethodPointcut, no consideration is given to the signature of the method, so if you have methods foo() and foo(int), they are both matched for the name foo.

Now for a demonstration, look at Listing 5-29, which shows a simple class with four methods.

Example 5.29. The NameBean Class

package com.apress.prospring2.ch05.namepc;

public class NameBean {

    public void foo() {
        System.out.println("foo");
    }

    public void foo(int x) {
        System.out.println("foo " + x);
    }

    public void bar() {
        System.out.println("bar");
    }

    public void yup() {
        System.out.println("yup");
    }
}

For this example, we want to match the foo(), foo(int), and bar() methods using the NameMatchMethodPointcut; this translates to matching the names foo and bar. This is shown in Listing 5-30.

Example 5.30. Using the NameMatchMethodPointcut

package com.apress.prospring2.ch05.namepc;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.NameMatchMethodPointcut;

import com.apress.prospring2.ch05.staticpc.SimpleAdvice;

public class NamePointcutExample {

    public static void main(String[] args) {
        NameBean target = new NameBean();

        // create the advisor
        NameMatchMethodPointcut pc = new NameMatchMethodPointcut();
        pc.addMethodName("foo");
pc.addMethodName("bar");
        Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

        // create the proxy
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        NameBean proxy = (NameBean)pf.getProxy();

        proxy.foo();
        proxy.foo(999);
        proxy.bar();
        proxy.yup();
    }
}

There is no need to create a class for the pointcut; you can simply create an instance of NameMatchMethodPointcut, and you are on your way. Notice that we have added two names to the pointcut, foo and bar, using the addMethodName() method. Running this example results in the following output:

>> Invoking foo
foo
>> Done
>> Invoking foo
foo 999
>> Done
>> Invoking bar
bar
>> Done
yup

As expected, the foo(), foo(int), and bar() methods are advised, thanks to the pointcut, but the yup() method is left unadvised.

Creating Pointcuts with Regular Expressions

In the previous section, we discussed how to perform simple matching against a predefined list of methods. But what if you don't know all of the methods' names in advance, and instead, you know the pattern that the names follow? For instance, what if you want to match all methods whose names start with get? In this case, you can use one of the regular expression pointcuts, either JdkRegexpMethodPointcut or Perl5RegexpMethodPointcut, to match a method name based on a regular expression.

The code in Listing 5-31 shows a simple class with three methods.

Example 5.31. The RegexpBean Class

package com.apress.prospring2.ch05.regexppc;

public class RegexpBean {

    public void foo1() {
        System.out.println("foo1");
    }
public void foo2() {
        System.out.println("foo2");
    }

    public void bar() {
        System.out.println("bar");
    }
}

Using a regular-expression-based pointcut, we can match all methods in this class whose name starts with foo. This is shown in Listing 5-32.

Example 5.32. Using Regular Expressions for Pointcuts

package com.apress.prospring2.ch05.regexppc;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;

import com.apress.prospring2.ch05.staticpc.SimpleAdvice;

public class RegexpPointcutExample {

    public static void main(String[] args) {
        RegexpBean target = new RegexpBean();

        // create the advisor
        JdkRegexpMethodPointcut pc = new JdkRegexpMethodPointcut();
        pc.setPattern(".*foo.*");
        Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

        // create the proxy
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        RegexpBean proxy = (RegexpBean)pf.getProxy();

        proxy.foo1();
        proxy.foo2();
        proxy.bar();
    }
}

Notice we do not need to create a class for the pointcut; instead, we just create an instance of JdkRegexpMethodPointcut (which could just as easily be Perl5RegexpMethodPointcut) and specify the pattern to match—and we are done. The interesting thing to note is the pattern. When matching method names, Spring matches the fully qualified name of the method, so for foo1(), Spring is matching against com.apress.prospring.ch6.regexppc.RegexpBean.foo1, hence the leading .* in the pattern. This is a powerful concept, because it allows you to match all methods within a given package, without needing to know exactly which classes are in that package and what the names of the methods are. Running this example yields the following output:

>> Invoking foo1
foo1
>> Done
>> Invoking foo2
foo2
>> Done
bar

As you would expect, only the foo1() and foo2() methods have been advised, because the bar() method does not match the regular expression pattern.

Convenience Advisor Implementations

For many of the Pointcut implementations, Spring also provides a convenience Advisor implementation that acts as the Pointcut as well. For instance, instead of using the NameMatchMethodPointcut coupled with a DefaultPointcutAdvisor in the previous example, we could simply have used a NameMatchMethodPointcutAdvisor, as shown in Listing 5-33.

Example 5.33. Using NameMatchMethodPointcutAdvisor

package com.apress.prospring.ch6.namepc;

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;

import com.apress.prospring2.ch6.staticpc.SimpleAdvice;

public class NamePointcutUsingAdvisor {

    public static void main(String[] args) {
        NameBean target = new NameBean();

        // create the advisor
        NameMatchMethodPointcutAdvisor advisor = new
            NameMatchMethodPointcutAdvisor(new SimpleAdvice());
        advisor.addMethodName("foo");
        advisor.addMethodName("bar");

        // create the proxy
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        NameBean proxy = (NameBean) pf.getProxy();

        proxy.foo();
        proxy.foo(999);
        proxy.bar();
        proxy.yup();
    }
}

Notice in Listing 6-33 that, rather than create an instance of NameMatchMethodPointcut, we configure the pointcut details on the instance of NameMatchMethodPointcutAdvisor itself. In this way, the NameMatchMethodPointcutAdvisor is acting as both the Advisor and the Pointcut.

You can find full details of the different convenience Advisor implementations by exploring the Javadoc for the org.springframework.aop.support package. There is no noticeable performance difference between the two approaches, and aside from slightly less code in the second approach, there are very few differences in the actual coding approaches. We prefer to stick with the first approach, because we feel that the intent is slightly clearer in the code. At the end of the day, the style you choose comes down to personal preference.

Using AspectJExpressionPointcut

The AspectJExpressionPointcut class enables you to write AspectJ expressions to define a pointcut. Let's see how this class is used. Listing 5-34 shows SimpleAfterAdvice that writes a message after returning from a method call.

Example 5.34. SimpleAfterAdvice Implementations

public class SimpleAfterAdvice implements AfterReturningAdvice{
    public void afterReturning(Object returnValue, Method method, Object[] args, 
SimpleAfterAdvice Implementations
Object target) throws Throwable { System.out.print("After method: " + method); } }

Now, let's create the simple bean SampleBean in Listing 5-35 shows. We will advise methods on this bean using SimpleAfterAdvice from Listing 5-34.

Example 5.35. SampleBean Implementation

public class SampleBean {

    public String getName() {
        return "Aleksa V";
    }

    public void setName(String name) {
        this.name=name;
    }

    public int getHeight() {
        return 201;
    }
}

Now, let's say that we need to log all calls to getter methods on the SampleBean class. For our SampleBean implementation, that means that we want to log calls to the getName and getHeight() methods but not calls to the setName() method. We can use an AspectJ expression to define methods we want to pointcut, using AspectJExpressionPointcut. Listing 5-36 shows the simple demonstration class that will apply SimpleAfterAdvice to our SampleBean class.

Example 5.36. AspectJExpressionPoincutDemo Class

public class AspectJExpressionPointcutDemo {

    public static void main(String[] args) {
        SampleBean target = new SampleBean();
        AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
        pc.setExpression("execution(* com.apress.prospring2.
AspectJExpressionPoincutDemo Class
ch05.aspectj..SampleBean.get*(..))"); SampleBean proxy = getProxy(pc, target); proxy.getName(); proxy.setName("New Name"); proxy.getHeight(); } private static SampleBean getProxy(AspectJExpressionPointcut pc,
AspectJExpressionPoincutDemo Class
SampleBean target) { // create the advisor Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAfterAdvice()); // create the proxy ProxyFactory pf = new ProxyFactory(); pf.setTarget(target); pf.addAdvisor(advisor); return (SampleBean) pf.getProxy(); } }

In the bold code lines, we simply instantiate the AspectJExpressionPointcut and set the expression value. The expression execution(* com.apress.prospring2.ch05.aspectj..*.get*(..)) says we should pointcut all method executions in the class com.apress.prospring2.ch05.aspectj.SampleBean that start with get. After that, we simply instantiate the proxy, like in previous examples, with our AspectJExpressionPointcut and SimpleAfterAdvice.

We make calls to all three methods of the proxied SampleBean class, and we get the following console output:

After method: public java.lang.String com.apress.prospring2.
AspectJExpressionPoincutDemo Class
ch05.aspectj.SampleBean.getName() After method: public int com.apress.prospring2.
AspectJExpressionPoincutDemo Class
ch05.aspectj.SampleBean.getHeight()

As expected, advice has been called on methods getName() and getHeight() but not on method setName().

The AspectJ expression language is a very powerful language, allowing a lot of different joinpoints in a pointcut expression. However, since AspectJExpressionPointcut is used with Spring AOP, which supports only method execution joinpoints, execution is the only joinpoint from AspectJ expression language that can be used.

You can still write very powerful AspectJ pointcut expressions. In this section, you have seen only basic syntax and usage; more in-depth examples will follow in the next chapter.

Using AnnotationMatchingPointcut

Think about the following situation: we have several methods we want to advise for performance-monitoring purposes while testing. However, these methods come from different packages and classes. More, we want to be able to change the methods or classes that are monitored with as little configuration as possible. One solution is to implement annotation, and annotate all classes or methods we want to advise with this annotation. That is where AnnotationMatchingPointcut comes into play. We can define a pointcut for methods and classes annotated with specific annotation using this convenient class.

We will use our custom SimpleAnnotation, which Listing 5-37 shows.

Example 5.37. SimpleAnnotation Example

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleAnnotation {

}

This simple annotation can be applied to either methods or classes. Let's now modify the SampleBean class from Listing 5-35 by adding an annotation to the getName() method, as shown in Listing 5-38.

Example 5.38. Annotated SampleBean Class

public class SampleBean {

    @SimpleAnnotation
    public String getName() {
        return "Aleksa V";
    }

    public void setName(String name) {

    }

    public int getHeight() {
        return 201;
    }
}

We will reuse SimpleAfterAdvice from the previous section (see Listing 5-34) and apply it to all methods and/or classes annotated with @SimpleAnnotation. Listing 5-39 shows a demonstration for AnnotationMatchingPointcut.

Example 5.39. AnnotationMatchingPointcutDemo Example

public class AnnotationMatchingPointcutDemo {

    public static void main(String[] args) {
        SampleBean target = new SampleBean();

        AnnotationMatchingPointcut pc = 
AnnotationMatchingPointcutDemo Example
new AnnotationMatchingPointcut(null, SimpleAnnotation.class); SampleBean proxy = getProxy(pc, target); proxy.getName(); proxy.getHeight(); }
private static SampleBean getProxy(AnnotationMatchingPointcut pc, 
AnnotationMatchingPointcutDemo Example
SampleBean target) { // create the advisor Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAfterAdvice()); // create the proxy ProxyFactory pf = new ProxyFactory(); pf.setTarget(target); pf.addAdvisor(advisor); return (SampleBean) pf.getProxy(); } }

As you can see in the bold code in Listing 5-39, we have simply instantiated AnnotationMatchingPointcut and used it in proxy instantiation. The AnnotationMatchingPointcut constructor accepts two parameters, a class-level annotation class, and a method-level annotation class. In our example, the class-level annotation is null, so we are advising all methods annotated with method-level annotation (in our case, SimpleAnnotation.class). We then invoke two methods, getName() and getHeight(), on the proxied SampleBean instance.

When we run the example, we get the following console output:

After method: public java.lang.String 
AnnotationMatchingPointcutDemo Example
com.apress.prospring2.ch05.annotation.SampleBean.getName()

As expected, only the getName() method is advised, as it is the only method that is annotated with the method-level annotation, @SimpleAnnotation.

If the AnnotationMatchingPointcut constructor is instantiated with a class-level annotation (for example, new AnnotationMatchingPointcut(SimpleAnnotation.class, null)), all methods in annotated classes would be advised.

If you supply both class- and method-level annotations (new AnnotationMatchingPointcut(SimpleAnnotation.class, SimpleAnnotation.class)) for example), both will need to be applied for the method to be advised. See Listing 5-40 for this example.

Example 5.40. Modified SampleBean with Class-Level Annotation

@SimpleAnnotation
public class SampleBean {

    @SimpleAnnotation
    public String getName() {
        return "Aleksa V";
    }

    public void setName(String name) {

    }

    public int getHeight() {
        return 201;
    }
}

Using both method- and class-level annotations, the getName() method will be advised but the getHeight() method won't, as it isn't annotated on the method level.

Using Control Flow Pointcuts

Spring control flow pointcuts, implemented by the ControlFlowPointcut class, are similar to the cflow construct available in many other AOP implementations, although they are not quite as powerful. Essentially, a control flow pointcut in Spring pointcuts all method calls below a given method or below all methods in a class. This is quite hard to visualize and is better explained using an example.

Listing 5-41 shows a SimpleBeforeAdvice that writes a message out describing the method it is advising.

Example 5.41. The SimpleBeforeAdvice Class

public class SimpleBeforeAdvice implements MethodBeforeAdvice {

    public void before(Method method, Object[] args, Object target) 
The SimpleBeforeAdvice Class
throws Throwable { System.out.println("Before method " + method); } }

This advice class allows us to see which methods are being pointcut by the ControlFlowPointcut. In Listing 5-42, you can see a simple class with one method—the method that we want to advise.

Example 5.42. The TestBean Class

public class TestBean {

    public void foo() {
        System.out.println("foo");
    }

}

In Listing 5-42, you can see the simple foo() method that we want to advise. We have, however, a special requirement—we only want to advise this method when it is called from another specific method. Listing 5-43 shows a simple driver program for this example.

Example 5.43. Using the ControlFlowPointcut Class

public class ControlFlowDemo {

    public static void main(String[] args) {
        new ControlFlowDemo().run();
    }

    private void run() {
        TestBean target = new TestBean();

        // create advisor
        Pointcut pc = new ControlFlowPointcut(ControlFlowDemo.class, "test");
        Advisor advisor = new DefaultPointcutAdvisor(pc,
                new SimpleBeforeAdvice());

        // create proxy
        ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
        pf.addAdvisor(advisor);

        TestBean proxy = (TestBean) pf.getProxy();

        System.out.println("Trying normal invoke");
        proxy.foo();
        System.out.println("Trying under ControlFlowDemo.test()");
        test(proxy);
    }

    private void test(TestBean bean) {
        bean.foo();
    }

}

In Listing 5-43, the advised proxy is assembled with ControlFlowPointcut and the foo() method is invoked twice: once directly from the run method and once from the test() method. Here is the line of particular interest:

Pointcut pc = new ControlFlowPointcut(ControlFlowDemo.class, "test");

In this line, we are creating a ControlFlowPointcut instance for the test() method of the ControlFlowDemo class. Essentially, this says, "pointcut all methods that are called from the ControlFlowDemo.test() method." Note that although we said "pointcut all methods," in fact, this really means "pointcut all methods on the proxy object that is advised using the Advisor corresponding to this instance of ControlFlowPointcut." Running this example yields the following output:

Trying normal invoke
foo
Trying under ControlFlowExample.test()
Before method public void com.apress.prospring2.ch05.cflow.TestBean.foo()
foo

As you can see, when the foo() method is first invoked outside of the control flow of the test() method, it is unadvised. When it executes for a second time, this time inside the control flow of the test() method, the ControlFlowPointcut indicates that its associated advice applies to the method, and thus the method is advised. Note that if we had called another method from within the test() method, one that was not on the advised proxy, it would not have been advised.

Control flow pointcuts can be extremely useful, allowing you to advise an object selectively only when it is executed in the context of another. However, be aware that you take a substantial performance hit for using control flow pointcut over other pointcuts. Figures from the Spring documentation indicate that a control flow pointcut is typically five times slower than other pointcuts on a version 1.4 JVM.

Using ComposablePointcut

In previous pointcutting examples, we used just a single pointcut for each Advisor. In most cases, this is enough, but in some cases, you may need to compose two or more pointcuts together to achieve the desired goal. Consider the situation where you want to pointcut all getter and setter methods on a bean. You have a pointcut for getters and a pointcut for setters, but you don't have one for both. Of course, you could just create another pointcut with the new logic, but a better approach is to combine the two pointcuts into a single pointcut using ComposablePointcut.

ComposablePointcut supports two methods: union() and intersection(). By default, ComposablePointcut is created with a ClassFilter that matches all classes and a MethodMatcher that matches all methods, although you can supply your own initial ClassFilter and MethodMatcher during construction. The union() and intersection() methods are both overloaded to accept ClassFilter and MethodMatcher arguments.

Invoking the union() method for a MethodMatcher replaces the MethodMatcher of the ComposablePointcut with an instance of UnionMethodMatcher using the current MethodMatcher of the ComposablePointcut and the MethodMatcher passed to the union() method as arguments. The UnionMethodMatcher then returns true for a match if either of its wrapped MethodMatchers match. You can invoke the union() method as many times as you want, with each call creating a new UnionMethodMatcher that wraps the current MethodMatcher with the MethodMatcher passed to the union() method. A similar structure is followed when you are using ClassFilter with the union() method.

Internally, the intersection() method works in a similar way to union(). However, the IntersectionMethodMatcher class only returns true for a match if both of the embedded MethodMatcher instances match. Essentially, you can think of the union method as an any match, in that it returns true if any of the matchers it is wrapping return true. And you can think of the intersection() method as an all match, in that it only returns true if all its wrapped matchers return true.

As with control flow pointcuts, it is quite difficult to visualize the ComposablePointcut, and it is much easier to understand with an example. Listing 5-44 shows a simple bean with three methods.

Example 5.44. The SampleBean Class

public class SampleBean {

    public String getName() {
        return "Springfield Springy";
    }

    public void setName(String name) {

    }

    public int getAge() {
        return 100;
    }
}

With this example, we are going to generate three different proxies using the same ComposablePointcut instance, but each time, we are going to modify the ComposablePointcut using either the union or intersection() method. Following this, we will invoke all three methods on the SampleBean proxy and look at which ones have been advised. Listing 5-45 shows the code for this.

Example 5.45. Investigating ComposablePointcut

public class ComposablePointcutDemo {

    public static void main(String[] args) {
        // create target
        SampleBean target = new SampleBean();

        ComposablePointcut pc = new ComposablePointcut(ClassFilter.TRUE,
                new GetterMethodMatcher());

        System.out.println("Test 1");
        SampleBean proxy = getProxy(pc, target);
        testInvoke(proxy);
System.out.println("Test 2");
        pc.union(new SetterMethodMatcher());
        proxy = getProxy(pc, target);
        testInvoke(proxy);

        System.out.println("Test 3");
        pc.intersection(new GetAgeMethodMatcher());
        proxy = getProxy(pc, target);
        testInvoke(proxy);

    }

    private static SampleBean getProxy(ComposablePointcut pc, SampleBean target) {
        // create the advisor

        Advisor advisor = new DefaultPointcutAdvisor(pc,
                new SimpleBeforeAdvice());

        // create the proxy
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);

        return (SampleBean) pf.getProxy();
    }

    private static void testInvoke(SampleBean proxy) {
        proxy.getAge();
        proxy.getName();
        proxy.setName("John Doe");
    }

    private static class GetterMethodMatcher extends StaticMethodMatcher {

        public boolean matches(Method method, Class cls) {
            return (method.getName().startsWith("get"));
        }

    }

    private static class GetAgeMethodMatcher extends StaticMethodMatcher {
        public boolean matches(Method method, Class cls) {
            return "getAge".equals(method.getName());
        }
    }

    private static class SetterMethodMatcher extends StaticMethodMatcher {

        public boolean matches(Method method, Class cls) {
            return (method.getName().startsWith("set"));
        }

    }
}

The first thing to notice in this example is the set of three private MethodMatcher implementations. The GetterMethodMatcher matches all methods that start with get. This is the default MethodMatcher that we use to assemble the ComposablePointcut. Because of this, we expect that the first round of invocations on the SampleBean methods will result in only the getAge() and getName() methods being advised.

The SetterMethodMatcher matches all methods that start with set, and it is combined with the ComposablePointcut using union() for the second round of invocations. At this point, we have a union of two MethodMatcher instances: one that matches all methods starting with get and one that matches all methods starting with set. To this end, we expect that all invocations during the second round will be advised.

The GetAgeMethodMatcher is very specific and only matches the getAge() method. This MethodMatcher is combined with the ComposablePointcut using intersection() for the third round for invocations. Because the GetAgeMethodMatcher is being composed using intersection(), the only method that we expect to be advised in the third round of invocations is getAge, because this is the only method that matches all the composed MethodMatchers.

Running this example results in the following output:

Test 1
Before method public int com.apress.prospring2.ch06.cflow.SampleBean.getAge()
Before method public java.lang.String 
Investigating ComposablePointcut
com.apress.prospring2.ch06.cflow.SampleBean.getName() Test 2 Before method public int com.apress.prospring2.ch06.cflow.SampleBean.getAge() Before method public java.lang.String
Investigating ComposablePointcut
com.apress.prospring2.ch06.cflow.SampleBean.getName() Before method public void com.apress.prospring2.ch06.cflow.SampleBean.
Investigating ComposablePointcut
setName(java.lang.String) Test 3 Before method public int com.apress.prospring2.ch06.cflow.SampleBean.getAge()

As expected, the first round of invocations on the proxy saw only the getAge() and getName() methods being advised. For the second round, when the SetterMethodMatcher had been composed with the union() method, all methods were advised. In the final round, as a result of the intersection of the GetAgeMethodMatcher, only the getAge() method was advised.

Although this example only demonstrated the use of MethodMatchers in the composition process, you can see that Spring offers a powerful set of Pointcut implementations that should meet most, if not all, of your application's requirements. Remember that if you can't find a pointcut to suit your needs, you can create your own implementation from scratch by implementing Pointcut, MethodMatcher, and ClassFilter. Use ClassFilter when you are building the pointcut. Indeed, you can use a combination of MethodMatcher and ClassFilter implementations when building your composite pointcut.

Composition and the Pointcut Interface

In the last section, you saw how to create a composite pointcut using multiple MethodMatcher and ClassFilter instances. You can also create composite pointcuts using other objects that implement the Pointcut interface. You can perform an intersection of pointcuts using the ComposablePointcut.intersection() method, but for a union, you need to use the org.springframework.aop.support.Pointcuts class that has both intersection() and union() methods.

You can compose pointcuts in the same way you can for method matchers, so we do not go into any detail here. You can find more information about composition by reading the Javadoc for the Pointcuts class.

Pointcutting Summary

From the discussions in this and the previous chapter, you can see that Spring offers a powerful set of Pointcut implementations that should meet most, if not all, of your application's requirements. Remember that if you can't find a pointcut to suit your needs, you can create your own implementation from scratch by implementing Pointcut, MethodMatcher, and ClassFilter.

There are two patterns you use to combine pointcuts and advisors together. The first pattern, the one that we have used so far, involves having the pointcut implementation decoupled from the advisor. In the code you have seen up to this point, we have created instances of Pointcut implementations and used the DefaultPointcutAdvisor to add advice along with the Pointcut to the proxy.

The second option, one that is adopted by many of the examples in the Spring documentation, is to encapsulate the Pointcut inside your own Advisor implementation. That way, you have a class that implements both Pointcut and PointcutAdvisor, with the PointcutAdvisor.getPointcut() method simply returning this. This is an approach many classes, such as StaticMethodMatcherPointcutAdvisor, use in Spring.

We find that the first approach is the more flexible, allowing you to use different Pointcut implementations with different Advisor implementations. However, the second approach is useful in situations where you are going to be using the same combination of Pointcut and Advisor in different parts of your application or across many different applications. The second approach is useful when each Advisor must have a separate instance of a Pointcut; by making the Advisor responsible for creating the Pointcut, you can ensure that this is the case.

If you recall the discussion on proxy performance from the previous chapter, you will remember that unadvised methods perform much better than methods that are advised. For this reason, you should ensure that, by using a Pointcut, you only advise those methods that are absolutely necessary. This way, you reduce the amount of unnecessary overhead added to your application by using AOP.

All About Proxies

So far, we have taken only a cursory look at the proxies generated by ProxyFactory. We mentioned that there are two types of proxy available in Spring: JDK proxies created using the JDK Proxy class and CGLIB-based proxies created using the CGLIB Enhancer class. Understanding the differences between these proxies is key to making the AOP code in your application perform as well as it can. In this section, we take a detailed look at the differences between the proxies and how these differences affect the performance of your application.

You may be wondering exactly what the difference is between the two proxies and why Spring needs two different types of proxy. Prior to version 1.1 of Spring, the two types of proxy shared much in common in the way they were implemented, and the performance of both types of proxy was very similar. There are two types of proxy because of the poor performance of Proxy under JDK 1.3. Spring overcame this by providing CGLIB proxies, which performed better than the JDK 1.4 proxies when running on a version 1.3 JVM.

The initial intention of the CGLIB proxy was to overcome the performance issues of the Proxy class in JDK 1.3, so the implementation was as similar to the JDK proxy as possible. The only drawback with this was that Spring was not taking full advantage of the feature set available with CGLIB. In version 1.1, things have changed dramatically. The CGLIB proxy is now heavily optimized and outperforms the JDK proxy quite dramatically in many cases. Before we take a look at the differences in proxy implementations, we must make sure you understand exactly what the generated proxies have to do.

Warning

Before version 1.1 of Spring, a bug in the CGLIB proxy code resulted in an inordinate amount of dynamically generated classes being created unnecessarily. In the long run, this resulted in excess memory being used and eventually OutOfMemoryError errors occurring. This bug is fixed in Spring 1.1.

Understanding Proxies

The core goal of a proxy is to intercept method invocations, and where necessary, execute chains of advice that apply to a particular method. The management and invocation of advice is largely proxy independent and is managed by the Spring AOP Framework. However, the proxy is responsible for intercepting calls to all methods and passing them as necessary to the AOP framework for the advice to be applied.

In addition to this core functionality, the proxy must also support a set of additional features. You can configure the proxy to expose itself via the AopContext class so that you can retrieve the proxy and invoke advised methods on the proxy from the target object. The proxy is responsible for ensuring that, when this option is enabled via ProxyFactory.setExposeProxy(), the proxy class is appropriately exposed. In addition to this, all proxy classes implement the Advised interface by default, which (among other things) allows the advice chain to be changed after the proxy has been created. A proxy must also ensure that any methods that return this—that is, return the proxied target—do in fact return the proxy and not the target.

As you can see, a typical proxy has quite a lot of work to perform, and all of this logic is implemented in both the JDK and CGLIB proxies. As of version 1.1 of Spring, the way in which this logic is implemented differs quite drastically depending on which of the proxy types you are using.

Using JDK Dynamic Proxies

JDK proxies are the most basic type of proxy available in Spring. Unlike the CGLIB proxy, the JDK proxy can only generate proxies of interfaces, not classes. In this way, any object you wish to proxy must implement at least one interface. In general, it is good design to use interfaces for your classes, but doing so is not always possible, especially when you are working with third-party or legacy code. In this case, you must use the CGLIB proxy.

When you are using the JDK proxy, all method calls are intercepted by the JVM and routed to the invoke() method of the proxy. This method then determines whether or not the method in question is advised, and if so, it invokes the advice chain and then the method itself using reflection. In addition to this, the invoke() method performs all the logic discussed in the previous section.

The JDK proxy makes no determination between methods that are advised and unadvised until it is in the invoke() method. This means that for unadvised methods on the proxy, the invoke() method is still called, all the checks are still performed, and the method is still invoked using reflection. Obviously, invoking the method on the proxy incurs a runtime overhead each time the method is invoked, even though the proxy often performs no additional processing other than to invoke the unadvised method via reflection.

You can instruct the ProxyFactory to use a JDK proxy by specifying the list of interfaces to proxy using setProxyInterfaces().

Using CGLIB Proxies

With the JDK proxy, all decisions about how to handle a particular method invocation are taken at runtime each time the method is invoked. When you use CGLIB, you avoid this approach in favor of one that performs much better. A full discussion of the inner workings of CGLIB is well beyond the scope of this chapter, but essentially, CGLIB dynamically generates the bytecode for a new class on the fly for each proxy, reusing already generated classes wherever possible. This approach allows you to make extensive optimizations.

When a CGLIB proxy is first created, CGLIB asks Spring how it wants to handle each method. This means that many of the decisions that are performed in each call to invoke() on the JDK proxy are performed just once for the CGLIB proxy. Because CGLIB generates actual bytecode, there is also a lot more flexibility in the way you can handle methods. For instance, the CGLIB proxy generates the appropriate bytecode to invoke any unadvised methods directly, dramatically reducing the overhead introduced by the proxy. In addition to this, the CGLIB proxy determines whether it is possible for a method to return this; if not, it allows the method call to be invoked directly, again reducing the overhead substantially.

The CGLIB proxy also handles fixed advice chains differently than the JDK proxy. A fixed advice chain is one that you guarantee will not change after the proxy has been generated. By default, you are able to change the advisors and advice on a proxy even after it is created, although this is rarely a requirement. The CGLIB proxy handles fixed advice chains in a particular way, reducing the runtime overhead for executing an advice chain.

In addition to all these optimizations, the CGLIB proxy utilizes the bytecode generation capabilities to gain a slight increase in performance when invoking advised methods; this results in advised methods that perform slightly better than those on JDK proxies.

Comparing Proxy Performance

So far, all we have done is discuss in loose terms the differences in implementation between the different proxy types. In this section, we are going to run a simple performance test to compare the performance of the CGLIB proxy with the JDK proxy.

Listing 5-46 shows the code for the performance test.

Example 5.46. Testing Proxy Performance

package com.apress.prospring2.ch05.proxies;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class ProxyPerfTest {

    public static void main(String[] args) {
        ISimpleBean target = new SimpleBean();

        Advisor advisor = new DefaultPointcutAdvisor(new TestPointcut(),
                new NoOpBeforeAdvice());

        runCglibTests(advisor, target);
        runCglibFrozenTests(advisor, target);
        runJdkTests(advisor, target);
    }

    private static void runCglibTests(Advisor advisor, ISimpleBean target) {
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);

        ISimpleBean proxy = (ISimpleBean)pf.getProxy();
        System.out.println("Running CGLIB (Standard) Tests");
        test(proxy);
    }
private static void runCglibFrozenTests(Advisor advisor, ISimpleBean target) {
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        pf.setFrozen(true);

        ISimpleBean proxy = (ISimpleBean)pf.getProxy();
        System.out.println("Running CGLIB (Frozen) Tests");
        test(proxy);
    }

    private static void runJdkTests(Advisor advisor, ISimpleBean target) {
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        pf.setInterfaces(new Class[]{ISimpleBean.class});

        ISimpleBean proxy = (ISimpleBean)pf.getProxy();
        System.out.println("Running JDK Tests");
        test(proxy);
    }

    private static void test(ISimpleBean bean) {
        long before = 0;
        long after = 0;

        // test advised method
        System.out.println("Testing Advised Method");
        before = System.currentTimeMillis();
        for(int x = 0; x < 500000; x++) {
            bean.advised();
        }
        after = System.currentTimeMillis();;

        System.out.println("Took " + (after - before) + " ms");

        // testing unadvised method
        System.out.println("Testing Unadvised Method");
        before = System.currentTimeMillis();
        for(int x = 0; x < 500000; x++) {
            bean.unadvised();
        }
        after = System.currentTimeMillis();;

        System.out.println("Took " + (after - before) + " ms");

        // testing equals() method
        System.out.println("Testing equals() Method");
        before = System.currentTimeMillis();
        for(int x = 0; x < 500000; x++) {
            bean.equals(bean);
        }
        after = System.currentTimeMillis();;

        System.out.println("Took " + (after - before) + " ms");

        // testing hashCode() method
System.out.println("Testing hashCode() Method");
        before = System.currentTimeMillis();
        for(int x = 0; x < 500000; x++) {
            bean.hashCode();
        }
        after = System.currentTimeMillis();;

        System.out.println("Took " + (after - before) + " ms");

        // testing method on Advised
        Advised advised = (Advised)bean;

        System.out.println("Testing Advised.getProxyTargetClass() Method");
        before = System.currentTimeMillis();
        for(int x = 0; x < 500000; x++) {
            advised.getProxyTargetClass();
        }
        after = System.currentTimeMillis();;

        System.out.println("Took " + (after - before) + " ms");

        System.out.println(">>>
");
    }
}

In this code, you can see that we are testing three kinds of proxy: a standard CGLIB proxy, a CGLIB proxy with a frozen advice chain, and a JDK proxy. For each proxy type, we run the following five test cases:

  • Advised method: A method is advised. The advice type used in the test is a before advice that performs no processing, so it reduces the effects of the advice itself on the performance tests.

  • Unadvised method: A method on the proxy is unadvised. Often, your proxy has many methods that are not advised. This test looks at how well unadvised methods perform for the different proxies.

  • The equals() method: This test looks at the overhead of invoking the equals() method. This method is especially important when you use proxies as keys in a HashMap or similar collection.

  • The hashCode method: As with the equals() method, the hashCode() method is important when you are using HashMaps or similar collections.

  • Executing methods on the Advised interface: As we mentioned earlier, a proxy implements the Advised interface by default, allowing you to modify the proxy after creation and to query information about the proxy. This test looks at how quickly methods on the Advised interface can be accessed using the different proxy types.

We ran the test on two processors: a dual-core Xeon at 2.65 GHz with 2GB of RAM and a 32-bit Sun 1.5 JVM on Ubuntu Linux 7.10 i386. When running the test, we set the initial heap size of the JVM to 1024MB to reduce the effects of heap resizing on test results. The results are shown in Table 5-3.

Table 5.3. Proxy Performance Test Results (in milliseconds)

Test

Standard CGLIB

Frozen CGLIB

JDK

Advised method

239

155

442

Unadvised method

126

24

322

equals()

36

35

504

hashCode()

53

40

180

Advised.getProxyTargetClass()

24

28

212

The results in this table clearly show that the CGLIB proxy performs much better than the JDK proxies. A standard CGLIB proxy only performs marginally better than the JDK proxy when executing an advised method, but the difference is noticable when you are using a proxy with a frozen advice chain. For unadvised methods, the CGLIB proxy is over eight times faster than the JDK proxy. Similar figures apply to the equals() and hashCode() methods, which are noticeably faster when you are using the CGLIB proxy. Notice that hashCode is faster than equals(). The reason for this is that equals() is handled in a specific way to ensure that the equals() contract is preserved for the proxies. For methods on the Advised interface, you will notice that they are also faster on the CGLIB proxy, although not to the same degree. The reason why they are faster is that Advised methods are handled early on in the intercept() method, and they avoid much of the logic that is required for other methods.

In the test results, notice that, for the standard CGLIB proxy, the invocation on Advised.getProxyTargetClass() took 0 milliseconds. This indicates that this call was optimized out by the just-in-time (JIT) compiler. On subsequent runs of the test, we noticed that sometimes calls to hashCode() were also optimized out when either CGLIB proxy was used. Interestingly, none of the method calls was ever optimized out for the JDK proxy.

Which Proxy to Use?

Deciding which proxy to use is typically easy. The CGLIB proxy can proxy both classes and interfaces, whereas the JDK proxy can only proxy interfaces. Add to this the fact that the CGLIB proxy clearly performs better than the JDK proxy, and it becomes apparent that the CGLIB proxy is the correct choice. The only thing to be aware of when using the CGLIB proxy is that a new class is generated for each distinct proxy, although with version 1.1, the reuse of proxy classes is now functioning correctly, reducing the runtime overhead of frequent class generation and reducing the amount of memory used by the CGLIB proxy classes. When proxying a class, the CGLIB proxy is the default choice, because it is the only proxy capable of generating a proxy of a class. In order to use the CGLIB proxy when proxying an interface, you must set the value of the optimize flag in the ProxyFactory to true using the setOptimize method.

Summary

In this chapter, we introduced the core concepts of AOP and looked at how these concepts translate into the Spring AOP implementation. We discussed the features that are and are not implemented in Spring AOP, and we pointed to AspectJ as an AOP solution for those features Spring does not implement. We spent some time explaining the details of the advice types available in Spring, and you have seen examples of all types in action. We also looked at how you limit the methods to which advice applies using pointcuts. In particular, we looked at the pointcut implementations available with Spring. Finally, we covered the details of how the AOP proxies are constructed, the various options, and what makes the proxies different. We wrapped up the discussion of proxies with a comparison of the performance among three different proxy types, and we concluded that the CGLIB proxy performs the best and is suitable for most, if not all, the proxies you will use.

In Chapter 6, we will look at Spring's @AspectJ annotation support for easy configuration of your advices and pointcuts. We will also look at how AOP is supported by Spring Framework services, which means you can define and configure advice declaratively rather than programmatically. Chapter 6 finishes with a look at how to integrate AspectJ with Spring to extend the AOP feature set available to your application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.131.62