Глава 4 Spring in Action 2th edition |
Advising beans
This chapter covers
- Basics of aspect-oriented programming
- Creating aspects from POJOs
- Automatically proxying beans
- Using @AspectJ annotations
- Injecting dependencies into AspectJ aspects
As I’m writing this chapter, Texas (where I reside) is going through several days of record-high temperatures. It’s really hot. In weather like this, air-conditioning is a must. But the downside of air-conditioning is that it uses electricity and electricity costs money. And there’s very little we can do to avoid paying for a cool and comfortable home. That’s because every home has a meter that measures every single kilowatt, and once a month someone comes by to read that meter so that the electric company accurately knows how much to bill us.
Now imagine what would happen if the meter went away and nobody came by to measure our electricity usage. Suppose that it were up to each homeowner to contact the electric company and report their electricity usage. Although it’s possible that some obsessive homeowners would keep careful record of their lights, televisions, and air-conditioning, most wouldn’t bother. Most would estimate their usage and others wouldn’t bother reporting it at all. It’s too much trouble to monitor electrical usage and the temptation to not pay is too great.
Electricity on the honor system might be great for consumers, but it would be less than ideal for the electric companies. That’s why we all have electric meters on our homes and why a meter-reader drops by once per month to report the consumption to the electric company.
Some functions of software systems are like the electric meters on our homes. The functions need to be applied at multiple points within the application, but it’s undesirable to explicitly call them at every point.
Monitoring electricity consumption is an important function, but it isn’t foremost in most homeowners’ minds. Mowing the lawn, vacuuming the carpet, and cleaning the bathroom are the kinds of things that homeowners are actively involved in. Monitoring the amount of electricity used by their house is a passive event from the homeowner’s point of view.
In software, several activities are common to most applications. Logging, security, and transaction management are important things to do, but should they be activities that your application objects are actively participating in? Or would it be better for your application objects to focus on the business domain problems they’re designed for and leave certain aspects to be handled by someone else?
In software development, functions that span multiple points of an application are called cross-cutting concerns. Typically, these cross-cutting concerns are conceptually separate from (but often embedded directly within) the application’s business logic. Separating these cross-cutting concerns from the business logic is where aspect-oriented programming (AOP) goes to work.
In chapter 2, you learned how to use dependency injection (DI) to manage and configure your application objects. Whereas DI helps you decouple your application objects from each other, AOP helps you decouple cross-cutting concerns from the objects that they affect.
Logging is a common example of the application of aspects. But it isn’t the only thing aspects are good for. Throughout this book, you’ll see several practical applications of aspects, including declarative transactions, security, and caching.
This chapter explores Spring’s support for aspects, including the exciting new AOP features added in Spring 2.0. In addition, you’ll see how AspectJ—another popular AOP implementation—can complement Spring’s AOP framework. But first, before we get too carried away with transactions, security, and caching, let’s see how aspects are implemented in Spring, starting with a primer on a few of AOP’s fundamentals.
Introducing AOP
As stated earlier, aspects help to modularize cross-cutting concerns. In short, a cross-cutting concern can be described as any functionality that affects multiple points of an application. Security, for example, is a cross-cutting concern in that many methods in an application can have security rules applied to them. Figure 4.1 gives a visual depiction of cross-cutting concerns.
Figure 4.1 represents a typical application that is broken down into modules. Each module’s main concern is to provide services for its particular domain. However, each of these modules also requires similar ancillary functionalities, such as security and transaction management.
A common object-oriented technique for reusing common functionality is to apply inheritance or delegation. But inheritance can lead to a brittle object hierarchy if the same base class is used throughout an application, and delegation can be cumbersome because complicated calls to the delegate object may be required.
Aspects offer an alternative to inheritance and delegation that can be cleaner in many circumstances. With AOP, you still define the common functionality in one place, but you can declaratively define how and where this functionality is applied without having to modify the class to which you are applying the new feature. Cross-cutting concerns can now be modularized into special objects called aspects. This has two benefits. First, the logic for each concern is now in one place, as opposed to being scattered all over the code base. Second, our service modules are now cleaner since they only contain code for their primary concern (or core functionality) and secondary concerns have been moved to aspects.
Defining AOP terminology
Like most technologies, AOP has formed its own jargon. Aspects are often described in terms of advice, pointcuts, and joinpoints. Figure 4.2 illustrates how these concepts are tied together.
Unfortunately, many of the terms used to describe AOP features are not intuitive. Nevertheless, they are now part of the AOP idiom, and in order to understand AOP, you must know these terms. In other words, before you walk the walk, you have to learn to talk the talk.
Advice
When a meter-reader shows up at your house, their purpose is to report the number of kilowatt-hours back to the electric company. Sure, they have a list of houses that they must visit and the information that they report is important. But the actual act of recording electricity usage is the meter-reader’s main job.
Likewise, aspects have a purpose—a job that they are meant to do. In AOP terms, the job of an aspect is called advice.
Advice defines both the what and the when of an aspect. In addition to describing the job that an aspect will perform, advice addresses the question of when to perform the job. Should it be applied before a method is invoked? After the method is invoked? Both before and after method invocation? Or should it only be applied if a method throws an exception?
Joinpoint
An electric company services several houses, perhaps even an entire city. Each house will have an electric meter that needs to be read and thus each house is a potential target for the meter-reader. The meter-reader could potentially read all kinds of devices, but to do his job, he needs to target electric meters that are attached to houses.
In the same way, your application may have thousands of opportunities for advice to be applied. These opportunities are known as joinpoints. A joinpoint is a point in the execution of the application where an aspect can be plugged in. This point could be a method being called, an exception being thrown, or even a field being modified. These are the points where your aspect’s code can be inserted into the normal flow of your application to add new behavior.
Pointcut
It’s not possible for any one meter-reader to visit all houses serviced by the electric company. Instead, they are assigned a subset of all of the houses to visit. Likewise, an aspect doesn’t necessarily advise all joinpoints in an application. Pointcuts help narrow down the joinpoints advised by an aspect.
If advice defines the what and when of aspects then pointcuts define the where. A pointcut definition matches one or more joinpoints at which advice should be woven. Often you specify these pointcuts using explicit class and method names or through regular expressions that define matching class and method name patterns. Some AOP frameworks allow you to create dynamic pointcuts that determine whether to apply advice based on runtime decisions, such as the value of method parameters.
Aspect
When a meter-reader starts his day, he knows both what he is supposed to do (report electricity usage) and which houses to collect that information from. Thus he knows everything he needs to know to get his job done.
An aspect is the merger of advice and pointcuts. Taken together, advice and pointcuts define everything there is to know about an aspect—what it does and where and when it does it.
Introduction
An introduction allows you to add new methods or attributes to existing classes (kind of mind-blowing, huh?). For example, you could create an Auditable advice class that keeps the state of when an object was last modified. This could be as simple as having one method, setLastModified(Date), and an instance variable to hold this state. The new method and instance variable can then be introduced to existing classes without having to change them, giving them new behavior and state.
Target
A target is the object that is being advised. This can be either an object you write or a third-party object to which you want to add custom behavior. Without AOP, this object would have to contain its primary logic plus the logic for any cross-cutting concerns. With AOP, the target object is free to focus on its primary concern, oblivious to any advice being applied.
Proxy
A proxy is the object created after applying advice to the target object. As far as the client objects are concerned, the target object (pre-AOP) and the proxy object (post-AOP) are the same—as they should be. That is, the rest of your application will not have to change to support the proxy object.
Weaving
Weaving is the process of applying aspects to a target object to create a new, proxied object. The aspects are woven into the target object at the specified joinpoints. The weaving can take place at several points in the target object’s lifetime:
- Compile time—Aspects are woven in when the target class is compiled. This requires a special compiler. AspectJ’s weaving compiler weaves aspects this way.
- Classload time—Aspects are woven in when the target class is loaded into the JVM. This requires a special ClassLoader that enhances that target class’s bytecode before the class is introduced into the application. AspectJ 5’s load-time weaving (LTW) support weaves aspects in this way.
- Runtime—Aspects are woven in sometime during the execution of the application. Typically, an AOP container will dynamically generate a proxy object that will delegate to the target object while weaving in the aspects. This is how Spring AOP aspects are woven.
That’s a lot of new terms to get to know. Revisiting figure 4.2, you can now understand that advice contains the cross-cutting behavior that needs to be applied to an application’s objects. The joinpoints are all the points within the execution flow of the application that are candidates to have advice applied. The pointcut defines where (at what joinpoints) that advice is applied. The key concept you should take from this? Pointcuts define which joinpoints get advised.
Now that you’re familiar with some basic AOP terminology, let’s see how these core AOP concepts are implemented in Spring.
Spring’s AOP support
Not all AOP frameworks are created equal. They may differ in how rich of a joinpoint model they offer. Some allow you to apply advice at the field modification level, while others only expose the joinpoints related to method invocations. They may also differ in how and when they weave the aspects. Whatever the case, the ability to create pointcuts that define the joinpoints at which aspects should be woven is what makes it an AOP framework.
Much has changed in the AOP framework landscape in the past few years. There has been some housecleaning among the AOP frameworks, resulting in some frameworks merging and others going extinct. In 2005, the AspectWerkz project merged with AspectJ, marking the last significant activity in the AOP world and leaving us with three dominant AOP frameworks:
- AspectJ (https://eclipse.org/aspectj)
- Spring AOP (https://www.springframework.org)
Since this is a Spring book, we will, of course, focus on Spring AOP. Even so, there’s a lot of synergy between the Spring and AspectJ projects, and the AOP support in Spring 2.0 borrows a lot from the AspectJ project. In fact, the <aop:spring-configured /> configuration element described in chapter 3 (see section 3.3) takes advantage of AspectJ’s support for constructor pointcuts and load-time weaving.
Spring’s support for AOP comes in four flavors:
- Classic Spring proxy-based AOP (available in all versions of Spring)
- @AspectJ annotation-driven aspects (only available in Spring 2.0)
- Pure-POJO aspects (only available in Spring 2.0)
- Injected AspectJ aspects (available in all versions of Spring)
The first three items are all variations on Spring’s proxy-based AOP. Consequently, Spring’s AOP support is limited to method interception. If your AOP needs exceed simple method interception (constructor or property interception, for example), you’ll want to consider implementing aspects in AspectJ, perhaps taking advantage of Spring DI to inject Spring beans into AspectJ aspects.
I’ll talk about AspectJ and how it fits into Spring a little later in this chapter (in sections 4.3.2 and 4.5). Because Spring’s AOP support is proxy based, that will be the focus of most of this chapter. But before we get started, it’s important to understand a few key points of Spring’s AOP framework.
Spring advice is written in Java
All of the advice you create within Spring will be written in a standard Java class. That way, you will get the benefit of developing your aspects in the same integrated development environment (IDE) you would use for your normal Java development. What’s more, the pointcuts that define where advice should be applied are typically written in XML in your Spring configuration file. This means both the aspect’s code and configuration syntax will be familiar to Java developers.
Contrast this with AspectJ, which is implemented as a language extension to Java. There are benefits and drawbacks to this approach. By having an AOPspecific language, you get more power and fine-grained control, as well as a richer AOP toolset. However, you are required to learn a new tool and syntax to accomplish this.
Spring advises objects at runtime
In Spring, aspects are woven into Spring-managed beans at runtime by wrapping them with a proxy class. As illustrated in figure 4.3, the proxy class poses as the target bean, intercepting advised method calls and forwarding those calls to the target bean. Between the time that the proxy intercepts the method call and the time it invokes the target bean’s method, the proxy performs the aspect logic.
Spring does not create a proxied object until that proxied bean is needed by the application. If you are using an ApplicationContext, the proxied objects will be created when it loads all of the beans from the BeanFactory. Because Spring creates proxies at runtime, you do not need a special compiler to weave aspects in Spring’s AOP.
Spring generates proxied classes in two ways. If your target object implements an interface(s) that exposes the required methods, Spring will use the JDK’s java.lang.reflect.Proxy class. This class allows Spring to dynamically generate a new class that implements the necessary interfaces, weave in any advice, and proxy any calls to these interfaces to your target class.
If your target class does not implement an interface, Spring uses the CGLIB library to generate a subclass to your target class. When creating this subclass, Spring weaves in advice and delegates calls to the subclass to your target class. There are two important things to take note of when using this approach:
- Creating a proxy with interfaces is favored over proxying classes, since this leads to a more loosely coupled application. The ability to proxy classes is provided so that legacy or third-party classes that do not implement interfaces can still be advised. This approach should be taken as the exception, not the rule.
- Methods marked as final cannot be advised. Remember, Spring generates a subclass to your target class. Any method that needs to be advised is overridden and advice is woven in. This is not possible with final methods.
Spring only supports method joinpoints
As mentioned earlier, multiple joinpoint models are available through various AOP implementations. Because it is based on dynamic proxies, Spring only supports method joinpoints. This is in contrast to some other AOP frameworks, such as AspectJ and JBoss, which provide field and constructor joinpoints in addition to method pointcuts. Spring’s lack of field pointcuts prevents you from creating very fine-grained advice, such as intercepting updates to an object’s field. And without constructor pointcuts, there’s no way to apply advice when a bean is instantiated.
However, as Spring focuses on providing a framework for implementing J2EE services, method interception should suit most, if not all, of your needs. If you find yourself in need of more than method interception, you’ll want to complement Spring AOP with AspectJ. Now you have a general idea of what AOP does and how it is supported by Spring. It’s time to get our hands dirty creating aspects in Spring.
Creating classic Spring aspects
In chapter 2, we demonstrated dependency injection by putting on a talent show called Spring Idol. In that example, we wired up several performers as <bean>s to show their stuff. It was all greatly amusing. But a show like that needs an audience or else there’s little point in it.
Therefore, we’re now going to provide an audience for the talent show. The Audience class in listing 4.1 defines the functions of an audience.
Listing 4.1 Defining an audience for the Spring Idol competition
package com.springinaction.springidol;
public class Audience {
public Audience() {}
public void takeSeats() {
System.out.println("The audience is taking their seats.");
}
//Executes before performance
public void turnOffCellPhones() {
System.out.println("The audience is turning off " +
"their cellphones");
}
//Executes after performance
public void applaud() {
System.out.println("CLAP CLAP CLAP CLAP CLAP");
}
//Executes after bad performance
public void demandRefund() {
System.out.println("Boo! We want our money back!");
}
}
There’s nothing particularly special about the Audience class. In fact, it’s just a simple POJO. Nonetheless, this class will provide the basis for many of the examples in this chapter. It can be wired as Spring <bean> with the following XML:
<bean id="audience"
class="com.springinaction.springidol.Audience" />
Taking a closer look at the Audience class, you can see that it defines four different things that an Audience can do:* They can take their seats.
- They can courteously turn off their cell phones.
- They can give a round of applause.
- They can demand a refund.
Although these functions clearly define an Audience’s behavior, it’s not clear when each method will be called. What we’d like is for the Audience to take their seats and turn off their cell phones prior to the performance, to applaud when the performance is good, and to demand a refund when the performance goes bad.
One option would be for us to inject an Audience into each performer and to change the perform() method to call the Audience’s methods. For example, consider an updated version of Instrumentalist in listing 4.2.
Listing 4.2 An instrumentalist that tells its audience how to respond
package com.springinaction.springidol;
public class Instrumentalist implements Performer {
public Instrumentalist() {}
public void perform() throws PerformanceException {
audience.takeSeats(); // Manipulates Audience
audience.turnOffCellPhones(); // Manipulates Audience
try {
System.out.print("Playing " + song + " : ");
instrument.play();
audience.applaud(); // Manipulates Audience
} catch (Throwable e) {
audience.demandRefund(); // Manipulates Audience
}
}
private String song;
public void setSong(String song) {
this.song = song;
}
private Instrument instrument;
public void setInstrument(Instrument instrument) {
this.instrument = instrument;
}
// Injects Audience
private Audience audience;
public void setAudience(Audience audience) {
this.audience = audience;
}
}
This would certainly work, but doesn’t it seem odd that the Instrumentalist has to tell its Audience what to do? The performer’s job is to give a performance, not to prompt its audience to respond to the performance. The audience’s job is to respond to that performance on its own, without being prompted to do so.
Another caveat to this approach is that every performer will need to be injected with an Audience and will need to call the Audience’s methods. As a result, the various implementations of Performer are dependent and coupled to the Audience class. This would mean that a performer couldn’t perform without an audience. So much for singing in the shower!
The most notable thing about injecting an audience into a performer is that the performer is completely responsible for asking the audience to applaud. In real life, this would be like a performer holding up an “Applaud!” sign. But when you think about it, the performer should focus on performing and not concern himself with whether or not the audience applauds. The audience should react to the performer’s performance… not be prodded by the performer.
In other words, the audience is a cross-cutting concern relative to the performer. Since cross-cutting concerns are the province of aspects, perhaps we should define the audience as an aspect. And that’s precisely what we’re going to do. Let’s start by defining some advice.
Creating advice
As mentioned earlier in this chapter, advice defines what an aspect does and when it does it. In Spring AOP, there are five types of advice, each defined by an interface (see table 4.1).
Table 4.1 Spring AOP advice comes in five forms that let you choose when advice is executed relative to a joinpoint.
Advice type | |
---|---|
Before | org.springframework.aop.MethodBeforeAdvice |
After-returning | org.springframework.aop.AfterReturningAdvice |
After-throwing | org.springframework.aop.ThrowsAdvice |
Around | org.aopalliance.intercept.MethodInterceptor |
Introduction | org.springframework.aop.IntroductionInterceptor |
Notice that all of these interfaces are part of the Spring Framework, except for MethodInterceptor. When defining around advice, Spring takes advantage of a suitable interface that is already provided by the AOP Alliance, an open source project whose goal is to facilitate and standardize AOP. You can read more about the AOP Alliance on their website at https://aopalliance.sourceforge.net.
When you think about what an audience is expected to do and match it up against the advice types in table 4.1, it seems clear that taking their seats and turning off their cell phones is best performed as before advice. Likewise, applause is an after-returning advice. And after-throwing is an appropriate advice for demanding a refund.
AudienceAdvice (listing 4.3) is a class that implements three of the five advice interfaces from table 4.1 to define the advice applied by an audience. (We’ll talk about the other advice types a little later in this chapter.)
Listing 4.3 Advice that defines how an audience’s functionality is applied
package com.springinaction.springidol;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
public class AudienceAdvice implements
MethodBeforeAdvice, // Implements
AfterReturningAdvice, // three types
ThrowsAdvice { // of advice
public AudienceAdvice() {}
public void before(Method method, Object[] args, Object target)
throws Throwable {
audience.takeSeats(); // Invokes before method
audience.turnOffCellPhones(); // Invokes before method
}
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
audience.applaud(); // Executes after successful return
}
public void afterThrowing(Throwable throwable) {
audience.demandRefund(); //Executes after exception thrown
}
private Audience audience;
public void setAudience(Audience audience) {
this.audience = audience;
}
}
There’s a lot going on in listing 4.3, but one thing to take notice of is that AudienceAdvice has an Audience as a dependency. Therefore, we’ll need to declare the AudienceAdvice in Spring as follows:
<bean id="audienceAdvice"
class="com.springinaction.springidol.AudienceAdvice">
<property name="audience" ref="audience" />
</bean>
AudienceAdvice is a single class that implements three different types of AOP advice. Let’s break it down one advice type at a time, starting with before advice.
Before advice
We want our audience to take their seats and turn off their cell phones prior to a performance. Therefore, AudienceAdvice provides before advice by implementing the MethodBeforeAdvice interface. This interface requires that a before() method be implemented:
public void before(Method method, Object[] args, Object target)
throws Throwable {
audience.takeSeats();
audience.turnOffCellPhones();
}
The before() method takes three parameters. The first parameter is a java.lang.reflect.Method object that represents the method to which the advice is being applied. The second parameter is an array of Objects that are the arguments that were passed to the method when the method was called. The final parameter is the target of the method invocation (i.e., the object on which the method was called).
If you’re familiar with Java’s dynamic proxy support that was introduced in Java 1.3, these parameters may seem familiar. They are nearly the same parameters that are given to the invoke() method of java.lang.reflect.InvocationHandler.
These parameters are available to you if your advice needs them. In this case, however, they’re ignored, as their values have no bearing on the functionality of the audience.
After returning advice
If a performance goes well (i.e., if no exceptions are thrown), we’d like the audience to graciously applaud. We know that a performance is successful if the perform() method returns. Therefore, AudienceAdvice implements After-
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
audience.applaud();
}
You’ll notice that the parameters to the afterReturning() method aren’t much different than the parameters to the before() method of MethodBeforeAdvice. The only difference is that an additional parameter has been added as the first parameter. This parameter holds the value that was returned from the invoked method.
Again, as with before(), the parameters are irrelevant to the audience and are thus ignored.
After throwing advice
People paid good money to be in the audience to see these performances. If anything goes wrong, they’re going to want their money back. Therefore, if the perform() method fails for any reason—that is, if the method throws an exception— the audience will demand their money back. To accommodate after throwing advice, the AudienceAdvice class implements the ThrowsAdvice interface.
Unlike MethodBeforeAdvice and AfterReturningAdvice, however, ThrowsAdvice doesn’t require that any method be implemented. ThrowsAdvice is only a marker interface that tells Spring that the advice may wish to handle a thrown exception.
An implementation of ThrowsAdvice may implement one or more afterThrowing() methods whose signatures take the following form:
public void afterThrowing([method], [args], [target], throwable);
All of the parameters of afterThrowing() are optional except for the one that is a Throwable type. It is this parameter that tells Spring which exceptions should be handled by the advice. For example, suppose we want to write a log entry every time that a NullPointerException is thrown. The following afterThrowing() method handles that task:
public void afterThrowing(Method method, Object[] args,
Object target, NullPointerException e) {
LOGGER.error("NPE thrown from " + method.getName());
}
In the case of AudienceAdvice, only one afterThrowing() method is defined:
public void afterThrowing(Throwable throwable) {
audience.demandRefund();
}
This indicates that we want the audience to demand a refund if any Exception is thrown from the perform() method. Furthermore, since the invocation target, method, and arguments are unimportant to the audience, the parameters are left out of the method signature.
With the audience advice class defined, we’re ready to associate the advice with a pointcut to create a complete aspect. But first, let’s look at how the same advice could have been implemented as an around advice.
Around advice
Around advice is effectively before, after-returning, and after-throwing advice all rolled into one. In Spring, around advice is defined by the AOP Alliance’s MethodInterceptor interface. In our example, the AudienceAdvice class could be rewritten as around advice, as shown in listing 4.4. This new AudienceAroundAdvice class is equivalent to AudienceAdvice, but is implemented as around advice.
Listing 4.4 Defining audience advice as around advice
package com.springinaction.springidol;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class AudienceAroundAdvice
implements MethodInterceptor {
// Implements MethodInterceptor
public Object invoke(MethodInvocation invocation)
throws Throwable {
try {
audience.takeSeats(); // Executes before
audience.turnOffCellPhones(); // method call
// Calls target method
Object returnValue = invocation.proceed();
audience.applaud(); // Executes after successful return
return returnValue;
} catch (PerformanceException throwable) {
audience.demandRefund(); // Executes after exception thrown
throw throwable;
}
}
// injected
private Audience audience;
public void setAudience(Audience audience) {
this.audience = audience;
}
}
The MethodInterceptor interface requires that only an invoke() method be implemented. In the case of AudienceAroundAdvice, the invoke() method instructs the audience to take their seats and turn off their cell phones. Next it calls proceed() on the method invocation to cause the advised method to be invoked. If a PerformanceException is caught from calling invocation.proceed(), the audience will demand a refund. Otherwise, the audience will applaud.
The nice thing about writing around advice is that you can succinctly define before and after advice in one method. If you have advice that will be applied both before and after a method, you may find around advice preferable to implementing the individual interface for each advice type. It’s less beneficial, however, if you only need before advice or after advice (but not both).
Around advice also offers you the opportunity to inspect and alter the value returned from the advised method. This makes it possible to write advice that performs some postprocessing on a method’s return value before returning the value to the caller. AfterReturningAdvice only allows you to inspect the returned value—you can’t change it.
But around advice has one minor gotcha: you must remember to call proceed(). Failure to call proceed() will result in the advice being applied but the target method never being executed. But then again, that may be what you want. Perhaps you’d like to prevent execution of a method under certain conditions. Compare that to MethodBeforeAdvice, where you can inspect the method and its parameters prior to invocation but you can’t stop the method from being invoked (short of throwing an exception, breaking the execution chain).
At this point, we’ve seen several ways to create advice that defines both the what and the when of aspects. But if you take a close look at either AudienceAdvice or AudienceAroundAdvice, you won’t find any clues as to what methods those advices will be applied to. That brings us to the topic of pointcuts to define the where characteristic of aspects.
Defining pointcuts and advisors
So far we have only discussed how to create AOP advice. This is not very useful if we cannot expressively define where this advice should be applied in our application.
This is where pointcuts come in. Recall that joinpoints are the points within application code where aspect advice could be woven in. Pointcuts are a way of selecting a subset of all possible joinpoints where advice should be woven, as illustrated in figure 4.4.
Spring comes with several different types of pointcuts to choose from. Two of the most useful pointcuts are regular expression pointcuts and AspectJ expression pointcuts. Let’s look at regular expression pointcuts first.
Declaring a regular expression pointcut
The main purpose of a pointcut is to choose which method(s) that advice will be applied to, usually by matching a method signature against some pattern. If you’re a fan of regular expressions, you may want to use a regular expression pointcut to match the method signature.
Spring comes with two classes that implement regular expression pointcuts:
- org.springframework.aop.support.Perl5RegexpMethodPointcut—Useful when an application will be running in a pre-Java 1.4 environment. Requires Jakarta ORO.
- org.springframework.aop.support.JdkRegexpMethodPointcut—Best choice when running in Java 1.4 or higher. Does not require Jakarta ORO.
Since we’ll be targeting a Java 1.5 runtime, we’re going to define the pointcut using JdkRegexpMethodPointcut as follows:
<bean id="performancePointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*perform" />
</bean>
The pattern property is used to specify the pointcut pattern used in method matching. Here the pattern property has been set to a regular expression that should match any method called perform() on any class.
Once you have defined a pointcut, you’ll need to associate it with advice. The following <bean> associates the regular expression pointcut we just defined with the audience advice defined in the previous section:
<bean id="audienceAdvisor"
class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="audienceAdvice" />
<property name="pointcut" ref="performancePointcut" />
</bean>
DefaultPointcutAdvisor is an advisor class that simply associates advice with a pointcut. Here the advice property has been set to reference the audienceAdvice bean from the previous section. Meanwhile, the pointcut property references the performancePointcut bean, which is our pointcut that matches the perform() method.
Combining a pointcut with an advisor
Although the audienceAdvisor bean completely defines an aspect by associating a pointcut with advice, there’s a slightly terser way to define an advisor with a regular expression pointcut.
RegexpMethodPointcutAdvisor is a special advisor class that lets you define both a pointcut and an advisor in a single bean. To illustrate, consider the following <bean> declaration:
<bean id="audienceAdvisor"
class="org.springframework.aop.support. RegexpMethodPointcutAdvisor">
<property name="advice" ref="audienceAdvice" />
<property name="pattern" value=".*perform" />
</bean>
This single <bean> does the work of two beans. It is effectively equivalent to both the performancePointcut bean and the previously defined audienceAdvisor bean.
Defining AspectJ pointcuts
Although regular expressions work fine as a pointcut definition language, their purpose is for general-purpose text parsing—they weren’t created with pointcuts in mind. Contrast them with how pointcuts are defined in AspectJ and you’ll find that AspectJ’s pointcut language is a true pointcut expression language.
If you’d prefer to use AspectJ-style expressions when defining your Spring pointcuts, you’ll want to use AspectJExpressionPointcut instead of JdkRegexpMethodPointcut. The following <bean> declares the performance pointcut using an AspectJ pointcut expression:
<bean id="performancePointcut"
class="org.springframework.aop.aspectj.AspectJExpressionPointcut">
<property name="expression" value="execution(* Performer+.perform(..))" />
</bean>
The pointcut expression is defined as a value of the expression property. In this case, we’re indicating that the pointcut should trigger advice when any perform() method taking any arguments is executed on a Performer, returning any type. Figure 4.5 summarizes the AspectJ expression used.
To associate the AspectJ expression pointcut with the audience advice, you could use DefaultPointcutAdvisor, just as with regular expression pointcut. But just as with regular expression pointcuts, you can also simplify how pointcuts and advice are tied together by using a special advisor that lets you define the pointcut expression as a property of the advisor. For AspectJ pointcut expressions, the advisor class to use is AspectJExpressionPointcutAdvisor. The following <bean> applies the audience advice to the perform() method using an AspectJ pointcut expression:
<bean id="audienceAdvisor"
class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="advice" ref="audienceAdvice" />
<property name="expression" value="execution(* *.perform(..))" />
</bean>
The advice property references the advice being applied—here it’s the audienceAdvice bean from earlier. The expression property is where the AspectJ pointcut expression is set.
In Spring AOP, advisors completely define an aspect by associating advice with a pointcut. But aspects in Spring are proxied. Whether you use regular expression pointcuts or AspectJ pointcuts, you’ll still need to proxy your target beans for the advisors to take effect. For that, you’ll need to declare one or more ProxyFactoryBeans.
Using ProxyFactoryBean
As you may recall from chapter 2, one of the performers in the Spring Idol competition is the juggling poet named Duke. As a quick reminder, here’s how Duke is declared as a <bean> in Spring:
<bean id="dukeTarget"
class="com.springinaction.springidol.PoeticJuggler"
autowire="constructor">
<constructor-arg ref="sonnet29" />
</bean>
If you’re paying close attention, you’ve probably noticed one small change that was made to this <bean> declaration. The id attribute has changed from duke to dukeTarget. We’ll explain why this has been done in a moment. But for now we wanted to draw your attention to this new id.
For a bean to be advised by an advisor, it must be proxied. Spring’s ProxyFactoryBean is a factory bean that produces a proxy that applies one or more interceptors (and advisors) to a bean. The following <bean> definition creates a proxy for the duke bean:
<bean id="duke"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="dukeTarget" />
<property name="interceptorNames" value="audienceAdvisor" />
<property name="proxyInterfaces"
value="com.springinaction.springidol.Performer" />
</bean>
The most notable thing about this bean is that its id is duke. But hold on—won’t that mean that when the Spring container is asked for a bean named duke it will be the proxy and not the PoeticJuggler that is returned? That’s absolutely right. In fact, that’s elemental to how Spring AOP works. As depicted in figure 4.6, when you invoke a method on an advised bean, you are actually invoking a method on the proxy. The proxy will use the pointcut to decide whether advice should be applied (or not), and then it invokes the advised bean itself.
Because the ProxyFactoryBean has been given the id of the advised bean (duke), the advised bean will need to be given a new id. That’s why we renamed the actual PoeticJuggler bean as dukeTarget. And it’s the dukeTarget bean that is referenced by the target property of ProxyFactoryBean. Put simply, this property tells ProxyFactoryBean which bean it will be proxying.
The interceptorNames property tells ProxyFactoryBean which advisors to apply to the proxied bean. This property takes an array of Strings, of which each member is the name of an interceptor/advisor bean in the Spring context. In our case, we only want to apply a single advisor, so we provide a single value of audienceAdvisor (don’t worry; Spring will automatically turn that value into a single member array). However, we could have just as easily set that property explicitly as an array using the following XML:
<property name="interceptorNames">
<list>
<value>audienceAdvisor</value>
</list>
</property>
The final property set on ProxyFactoryBean is proxyInterfaces. ProxyFactoryBean produces a Java dynamic proxy that advises the target bean, but you’ll still need a way to invoke methods on that proxy. The proxyInterfaces property tellsProxyFactoryBean which interface(s) the proxy should implement. As with the interceptorNames property, this property is an array property—actually, an array of java.lang.Class. But we specified the value as a single String value. Fortunately, Spring is smart enough (using the ClassEditor property editor from table 3.1) to translate that single String value into a single-member Class array.
Abstracting ProxyFactoryBean
So far we’ve only proxied Duke. This means that the audience will only attend Duke’s performance. If we want the audience to take their seats, turn off their cell phones, and applaud for our other performers, then we’ll need to proxy the other performers as well.
To that end, here’s Stevie, proxied with the audience advisor:
<bean id="stevie"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="stevieTarget" />
<property name="proxyInterfaces"
value="com.springinaction.springidol.Performer" />
<property name="interceptorNames" value="audienceAdvisor" />
</bean>
Now the audience will watch Stevie’s performance as well as Duke’s. But wait a minute—do we have to write all of this XML for each and every bean that we want to proxy? The proxyInterfaces property will be the same for all performers. And the interceptorNames property will be the same. It seems a bit too much to have to repeat this information for all of our performers when the only thing that will be different will be the target property.
It’s often the case that an aspect will be applied to multiple beans in your application. In fact, that’s why aspects are said to handle cross-cutting concerns— because an aspect’s concern cuts across multiple objects. Although you could write the same ProxyFactoryBean declaration for all of the beans to be advised, there’s a better way that cuts down on the amount of redundant XML.
The trick is to declare a single ProxyFactoryBean as an abstract bean, then reuse that declaration as a parent for each of the advised beans. For example, audienceProxyBase declares an abstract bean with the common proxyInterfaces and interceptorNames properties set:
<bean id="audienceProxyBase"
class="org.springframework.aop.framework.ProxyFactoryBean"
abstract="true">
<property name="proxyInterfaces"
value="com.springinaction.springidol.Performer" />
<property name="interceptorNames" value="audienceAdvisor" />
</bean>
The audienceProxyBase bean has its abstract attribute set to true, indicating that it is an abstract bean and that Spring shouldn’t try to instantiate it directly. Instead, this bean will serve as the basis for the other performer beans. Here are the new, terser, declarations of stevie and duke, which use the parent attribute to extend the audienceProxyBase bean:
<bean id="stevie" parent="audienceProxyBase">
<property name="target" ref="stevieTarget" />
</bean>
<bean id="duke" parent="audienceProxyBase">
<property name="target" ref="dukeTarget" />
</bean>
That’s much more succinct, isn’t it? In this form, only the variant target property is declared. The common properties are inherited from the parent bean.
Using abstract beans to define a parent for all of your advised beans is a great way to cut down on the amount of XML in your Spring configuration. However, there’s still more that you can do to reduce the amount of XML required to proxy beans with advisors. Coming up next, you’ll learn how to eliminate the need for ProxyFactoryBean and have Spring automatically proxy beans to be advised.
Autoproxying
One thing that may have struck you as odd from the previous section is that we had to rename our bean to dukeTarget and then give the ProxyFactoryBean an id of duke. This left us with a strange arrangement of beans: the bean that actually represents Duke is named dukeTarget, while the bean named duke is really a ProxyFactoryBean with the purpose of proxying the real Duke with an audience.
If you found that unclear, don’t feel too bad. It’s a confusing concept that baffles most programmers who are just getting their feet wet with Spring AOP.
In addition to confusion, ProxyFactoryBean also lends to the verbosity in the Spring configuration file. Even if you define an abstract ProxyFactoryBean, you will still need declare two beans for each bean that is advised: the target bean and the proxy bean. It would be so much nicer if we could simply declare the advisor once and let Spring automatically create proxies for beans whose methods match the advisor’s pointcut.
Good news! Spring provides support for automatic proxying of beans. Autoproxying provides a more complete AOP implementation by letting an aspect’s pointcut definition decide which beans need to be proxied, rather than requiring you to explicitly create proxies for specific beans.
Actually, there are two ways to autoproxy beans:
- Basic autoproxying of beans based on advisor beans declared in the Spring context— The advisor’s pointcut expression is used to determine which beans and which methods will be proxied.
- Autoproxying based on @AspectJ annotation-driven aspects—The pointcut specified on the advice contained within the aspect will be used to choose which beans and methods will be proxied.
Using either of these autoproxying strategies can eliminate ProxyFactoryBean from your Spring context XML file. The former approach to autoproxying uses the advisors we’ve already created up to this point in Spring. Let’s start by looking at this basic autoproxy mechanism.
Creating autoproxies for Spring aspects
If you take a look at the audienceAdvisor bean declared in section 4.2.2, you’ll see that it has all of the information needed to advise our performer beans:
<bean id="audienceAdvisor"
class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="advice" ref="audienceAdvice" />
<property name="expression" value="execution(* *.perform(..))" />
</bean>
The advice property tells it what advice to apply and the expression property tells it where to apply that advice. Despite that wealth of information, we still have to explicitly declare a ProxyFactoryBean for Spring to proxy our performers.
However, Spring comes with a handy implementation of BeanPostProcessor (see chapter 3) called DefaultAdvisorAutoProxyCreator, which automatically checks to see whether an advisor’s pointcut matches a bean’s methods and replaces that bean’s definition with a proxy that applies the advice. In a nutshell, it automatically proxies beans with matching advisors.
To use DefaultAdvisorAutoProxyCreator, all you have to do is declare the following <bean> in your Spring context:
<bean class="org.springframework.aop.framework.autoproxy.
DefaultAdvisorAutoProxyCreator" />
Notice that this bean doesn’t have an id. That’s because we’ll never refer to it directly. Instead, the Spring container will recognize it as a BeanPostProcessor and put it to work creating proxies.
With DefaultAdvisorAutoProxyCreator declared, we no longer need to declare ProxyFactoryBeans in the Spring context. What’s more, we no longer have to give our beans weird names that end with target. We can now give them appropriate names like steve or duke:
<bean id="duke"
class="com.springinaction.springidol.PoeticJuggler"
autowire="constructor">
<constructor-arg ref="sonnet29" />
</bean>
In this way, we are able to keep both bean declarations and bean code free from the aspect-related details.
Spring’s basic autoproxy facility is fine for working with simple advice or when in a pre–Java 5 environment. But if you’re targeting Java 5, you may want to consider Spring’s support for AspectJ’s annotation-based aspects. Let’s see how to create aspects in Spring that are annotation based.
Autoproxying @AspectJ aspects
A major new feature of AspectJ 5 is the ability to annotate POJO classes to be aspects. This new feature is commonly referred to as @AspectJ Prior to AspectJ 5, writing AspectJ aspects involved learning a Java language extension. But AspectJ’s new aspect annotations make it simple to turn any class into an aspect just by sprinkling a few annotations around.
Looking back at our Audience class, we see that Audience contained all of the functionality needed for an audience, but none of the details to make it an aspect. That left us having to create advice, pointcuts, and advisors—AOP plumbing—to define an audience aspect.
But with @AspectJ annotations, we can revisit our Audience class and turn it into an aspect without the need for any additional classes or bean declarations. Listing 4.5 shows the new Audience class, now annotated to be an aspect.
Listing 4.5 Annotating Audience to be an aspect
package com.springinaction.springidol;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class Audience
public Audience() {}
@Pointcut("execution(* *.perform(..))")
public void performance() {}
@Before("performance()")
public void takeSeats() {
System.out.println("The audience is taking their seats.");
}
@Before("performance()")
public void turnOffCellPhones() {
System.out.println("The audience is turning off their cellphones");
}
@AfterReturning("performance()")
public void applaud() {
System.out.println("CLAP CLAP CLAP CLAP CLAP");
}
@AfterThrowing("performance()")
public void demandRefund() {
System.out.println("Boo! We want our money back!");
}
}
The new Audience class is now annotated with @Aspect This annotation indicates that Audience is not just any old POJO but that it is an aspect.
The @Pointcut annotation is used to define a reusable pointcut within an [@AspectJ aspect. The value given to the @Pointcut annotation is an AspectJ pointcut expression—here indicating that the pointcut should match the perform() method of any class. The name of the pointcut is derived from the name of the method to which the annotation is applied. Therefore, the name of this pointcut is performance(). The actual body of the performance() method is irrelevant and, in fact, should be empty. The method itself is just a marker, giving the @Pointcutannotation something to attach itself to.
Each of the audience’s methods has been annotated with advice annotations. The @Before annotation has been applied to both takeSeats() and turnOffCellPhones() to indicate that these two methods are before advice. The @AfterReturning annotation indicates that the applaud() method is an after-returning advice method. And the @AfterReturning annotation is placed on demandRefund() so that it will be called if any exceptions are thrown during the performance.
The name of the performance() pointcut is given as the value parameter to all of the advice annotations. This tells each advice method where it should be applied.Notice that aside from the annotations and the no-op performance() method, the Audience class is functionally unchanged. This means that it’s still a simple Java object and can be used as such. It can also still be wired in Spring as follows:
<bean id="audience"
class="com.springinaction.springidol.Audience" />
Because the Audience class contains everything that’s needed to define its own pointcuts and advice, there’s no more need for a class that explicitly implements one of Spring’s advice interfaces. There’s also no further need to declare an advisor bean in Spring. Everything needed to use Audience as advice is now contained in the Audience class itself.
There’s just one last thing to do to make Spring apply Audience as an aspect.
You must declare an autoproxy bean in the Spring context that knows how to turn @AspectJ-annotated beans into proxy advice.
For that purpose, Spring comes with an autoproxy creator class called AnnotationAwareAspectJAutoProxyCreator. You could register an AnnotationAwareAspectJAutoProxyCreator as a <bean> in the Spring context, but that would require a lot of typing (believe me… I’ve typed it twice so far). Instead, to simplify that rather long name, Spring also provides a custom configuration element in the aop namespace that’s much easier to remember:
<aop:aspectj-autoproxy />
<aop:aspectj-autoproxy/> will create an AnnotationAwareAspectJAutoProxyCreator in the Spring context and will automatically proxy beans whose methods match the pointcuts defined with @Pointcutannotations in @Aspect-annotated beans.
To use the <aop:aspectj-autoproxy> configuration element, you’ll need to remember to include the aop namespace in your Spring configuration file:
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="https://www.springframework.org/schema/aop"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
https://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
…
</beans>
You should be aware that AnnotationAwareAspectJAutoProxyCreator only uses @AspectJ’s annotations as a guide for creating proxy-based aspects. Under the covers, it’s still Spring-style aspects. This is significant because it means that although you are using @AspectJ’ annotations, you are still limited to proxying method invocations.
You may also be interested to know that AnnotationAwareAspectJAutoProxyCreator also creates proxies based on classic Spring advisors. That is, it also does the same job that DefaultAdvisorAutoProxyCreator does. So, if you have any advisor beans declared in your Spring context, those will also automatically be used to advise proxied beans.
Annotating around advice
Just as with classic Spring advice, you are not limited to before and after advice types when using @AspectJ annotations. You may also choose to create around advice. For that, you must use the @Around annotation, as in the following example:
@Around("performance()")
public void watchPerformance (ProceedingJoinPoint joinpoint) {
System.out.println("The audience is taking their seats.");
System.out.println("The audience is turning off " +
"their cellphones");
try {
joinpoint.proceed();
System.out.println("CLAP CLAP CLAP CLAP CLAP");
} catch (PerformanceException throwable) {
System.out.println("Boo! We want our money back!");
}
}
Here the @Around annotation indicates that the watchPerformance() method is to be applied as around advice to the performance() pointcut.
As you may recall from section 4.2.1, around advice methods must remember to explicitly invoke proceed() so that the proxied method will be invoked. But simply annotating a method with @Around isn’t enough to provide a proceed() method to call. Therefore, methods that are to be around advice must take a ProceedingJoinPoint object as an argument and then call the proceed() method on that object.
Autoproxying of aspects sure makes configuring Spring aspects a lot simpler and makes the application of aspects transparent. But in its transparency, autoproxying obscures many details of the aspects. With autoproxying it is less apparent as to which beans are aspects and which beans are being proxied. In the next section, we’ll see how some new features in Spring 2.0 achieve a middle ground where aspects are explicitly defined but without all of the XML verbosity of using ProxyFactoryBean.
Declaring pure-POJO aspects
The Spring development team recognized that using ProxyFactoryBean is somewhat clumsy. So, they set out to provide a better way of declaring aspects in Spring. The outcome of this effort is found in the new XML configuration elements that come with Spring 2.0.
You’ve already seen one of the new elements in the aop namespace— <aop:aspectj-autoproxy>. But Spring 2.0 comes with several more configuration elements in the aop namespace that make it simple to turn any class into an aspect. The new AOP configuration elements are summarized in table 4.2.
Revisiting our audience example one last time, you’ll recall that the Audience class has all of the methods that define an audience’s functionality. We only need to turn that Audience class into an aspect with pointcuts that tell it when to perform each of its actions. In the previous section we did that with @AspectJ annotations, but this time we’ll do it using Spring’s AOP configuration elements.
The great thing about Spring’s AOP configuration elements is that they can be used to turn any class into an aspect. The original Audience class from listing 4.1, for instance, is just a plain Java class—no special interfaces or annotations. Using Spring’s AOP configuration elements, as shown in listing 4.6, we can turn the audience bean into an aspect.
Table 4.2 Spring 2.0’s AOP configuration elements simplify declaration of POJO-based aspects.
AOP configuration element | |
---|---|
<aop:advisor> | Defines an AOP advisor. |
<aop:after> | Defines an AOP after advice (regardless of whether the advised method returns successfully). |
<aop:after-returning> | Defines an AOP after-returning advice. |
<aop:after-throwing> | Defines an AOP after-throwing advice. |
<aop:around> | Defines an AOP around advice. |
<aop:aspect> | Defines an aspect. |
<aop:before> | Defines an AOP before advice. |
<aop:config> | The top-level AOP element. Most <aop:*> elements must be contained within <aop:config>. |
<aop:pointcut> | Defines a pointcut. |
Listing 4.6 Defining an audience aspect using Spring’s AOP configuration elements
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="https://www.springframework.org/schema/aop"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
https://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="audience"
class="com.springinaction.springidol.Audience" />
<!--References audience bean as aspect -->
<aop:config>
<aop:aspect ref="audience">
<!--Executes before performance -->
<aop:before
method="takeSeats"
pointcut="execution(* *.perform(..))" />
<!--Executes before performance -->
<aop:before
method="turnOffCellPhones"
pointcut="execution(* *.perform(..))" />
<!--Executes after performance -->
<aop:after-returning
method="applaud"
pointcut="execution(* *.perform(..))" />
<!--Executes after bad performance -->
<aop:after-throwing
method="demandRefund"
pointcut="execution(* *.perform(..))" />
</aop:aspect>
</aop:config>
</beans>
The first thing to notice about the Spring AOP configuration elements is that most of them must be used within the context of the <aop:config> element. There are a few exceptions to this rule, but none of those exceptions appear in this section. When we encounter such an exception elsewhere in this book, I’ll be sure to point it out.
Within <aop:config> you may declare one or more advisors, aspects, or pointcuts. In listing 4.6, we’ve declared a single aspect using the <aop:aspect> element. The ref attribute references the POJO bean that will be used to supply the functionality of the aspect—in this case, Audience. The bean that isreferenced by the ref attribute will supply the methods called by any advice in the aspect.
The aspect has four different bits of advice. The two <aop:before> elements define method before advice that will call the takeSeats() and turnOffCellPhones() methods (declared by the method attribute) of the Audience bean before any methods matching the pointcut are executed. The <aop:afterreturning> element defines an after-returning advice to call the applaud() method after the pointcut. Meanwhile, the <aop:after-throwing> element defines an after-throwing advice to call the demandRefund() method if any exceptions are thrown. Figure 4.7 shows how the advice logic is woven into the business logic.
In all advice elements, the pointcut attribute defines the pointcut where the advice will be applied. The value given to the pointcut attribute is a pointcut defined in AspectJ’s pointcut expression syntax.
You’ll notice that the value of the pointcut attribute is the same for all of the advice elements. That’s because all of the advice is being applied to the same pointcut. This, however, presents a DRY (don’t repeat yourself) principle violation. If you decide later to change the pointcut, you must change it in four different places.
To avoid duplication of the pointcut definition, you may choose to define a named pointcut using the <aop:pointcut> element. The XML in listing 4.7 shows how the <aop:pointcut> element is used within the <aop:aspect> element to define a named pointcut that can be used by all of the advice elements.
Listing 4.7 Defining a named pointcut to eliminate redundant pointcut definitions
<aop:config>
<aop:aspect ref="audience">
<!--Defines performance pointcut-->
<aop:pointcut
id="performance"
expression="execution(* *.perform(..))" />
<aop:before
method="takeSeats"
pointcut-ref="performance" /> <!--References pointcut-->
<aop:before
method="turnOffCellPhones"
pointcut-ref="performance" /> <!--References pointcut-->
<aop:after-returning
method="applaud"
pointcut-ref="performance" /> <!--References pointcut-->
<aop:after-throwing
method="demandRefund"
pointcut-ref="performance" /> <!--References pointcut-->
</aop:aspect>
</aop:config>
Now the pointcut is defined in a single location and is referenced across multiple advice elements. The <aop:pointcut> element defines the pointcut to have an id of performance. Meanwhile, all of the advice elements have been changed to reference the named pointcut with the pointcut-ref attribute.
As used in listing 4.7, the <aop:pointcut> element defines a pointcut that can be referenced by all advices within the same <aop:aspect> element. But you can also define pointcuts that can be used across multiple aspects by placing the <aop:pointcut> elements within the scope of the <aop:config> element.
It’s worth mentioning at this point that both the <aop:aspect> element and the @AspectJ annotations are effective ways to turn a POJO into an aspect. But <aop:aspect> has one distinct advantage over @AspectJ in that you do not need the source code of the class that is to provide the aspect’s functionality. With @AspectJ you must annotate the class and methods, which requires having the source code. But <aop:aspect> can reference any bean.Spring AOP enables separation of cross-cutting concerns from an application’s business logic. But as we’ve mentioned, Spring aspects are still proxy based and are limited to advising method invocations. If you need more than just method proxy support, you’ll want to consider using AspectJ. In the next section, you’ll see how AspectJ aspects can be used within a Spring application.
Injecting AspectJ aspects
Although Spring AOP is sufficient for many applications of aspects, it is a weak AOP solution when contrasted with AspectJ. AspectJ offers many types of pointcuts that are simply not possible with Spring AOP.
Constructor pointcuts, for example, are convenient when you need to apply advice upon the creation of an object. Unlike constructors in some other object-oriented languages, Java constructors are different from normal methods. This makes Spring’s proxy-based AOP woefully inadequate for advising creation of an object.
For the most part, AspectJ aspects are independent of Spring. Although they can certainly be woven into any Java-based application, including Spring applications, there’s little involvement on Spring’s part in applying AspectJ aspects.
However, any well-designed and meaningful aspect will likely depend on other classes to assist in its work. If an aspect depends on one or more classes when executing its advice, you can instantiate those collaborating objects with the aspect itself. Or, better yet, you can use Spring’s dependency injection to inject beans into AspectJ aspects.
To illustrate, let’s create a new aspect for the Spring Idol competition. A talent competition needs a judge. So, let’s create a judge aspect in AspectJ. JudgeAspect (listing 4.8) is such an aspect.
Listing 4.8 An AspectJ implementation of a talent competition judge
package com.springinaction.springidol;
public aspect JudgeAspect {
public JudgeAspect() {}
pointcut performance() : execution(* perform(..));
after() returning() : performance() {
System.out.println(criticismEngine.getCriticism());
}
// injected
private CriticismEngine criticismEngine;
public void setCriticismEngine(CriticismEngine criticismEngine) {
this.criticismEngine = criticismEngine;
}
}
The chief responsibility for JudgeAspect is to make commentary on a performance after the performance has completed. The performance() pointcut in listing 4.8 matches the perform() method. When it’s married with the after() returning() advice, you get an aspect that reacts to the completion of a performance.
What makes listing 4.8 interesting is that the judge doesn’t make simple commentary on its own. Instead, JudgeAspect collaborates with a CriticismEngine object, calling its getCriticism() method, to produce critical commentary after a performance. To avoid unnecessary coupling between JudgeAspect and the CriticismEngine, the JudgeAspect is given a reference to a CriticismEngine through setter injection. This relationship is illustrated in figure 4.8.
CriticismEngine itself is an interface that declares a simple getCriticism() method. An implementation of CriticismEngine is found in listing 4.9.
Listing 4.9 An implementation of the CriticismEngine used by JudgeAspect
package com.springinaction.springidol;
public class CriticismEngineImpl implements CriticismEngine {
public CriticismEngineImpl() {}
public String getCriticism() {
int i = (int) (Math.random() * criticismPool.length);
return criticismPool[i];
}
// injected
private String[] criticismPool;
public void setCriticismPool(String[] criticismPool) {
this.criticismPool = criticismPool;
}
}
CriticismEngineImpl implements the CriticismEngine interface by randomly choosing a critical comment from a pool of injected criticisms. This class can be declared as a Spring <bean> using the following XML:
<bean id="criticismEngine"
class="com.springinaction.springidol.CriticismEngineImpl">
<property name="criticisms">
<list>
<value>I'm not being rude, but that was appalling.</value>
<value>You may be the least talented
person in this show.</value>
<value>Do everyone a favor and keep your day job.</value>
</list>
</property>
</bean>
So far, so good. We now have a CriticismEngine implementation to give to JudgeAspect. All that’s left is to wire CriticismEngineImpl into JudgeAspect.
Before we show you how to do the injection, you should know that AspectJ aspects can be woven into your application without involving Spring at all. But if you want to use Spring’s dependency injection to inject collaborators into an AspectJ aspect, you’ll need to declare the aspect as a <bean> in Spring’s configuration. The following <bean> declaration injects the criticismEngine bean into JudgeAspect:
<bean class="com.springinaction.springidol.JudgeAspect"
factory-method="aspectOf">
<property name="criticismEngine" ref="criticismEngine" />
</bean>
For the most part, this <bean> declaration isn’t much different from any other <bean>you may find in Spring. But the big difference is the use of the factorymethod attribute. Normally Spring beans are instantiated by the Spring container—but AspectJ aspects are created by the AspectJ runtime. By the time Spring gets a chance to inject the CriticismEngine into JudgeAspect, JudgeAspect has already been instantiated.
Since Spring isn’t responsible for the creation of JudgeAspect, it isn’t possible to simply declare JudgeAspect as a bean in Spring. Instead, we need a way for Spring to get a handle to the JudgeAspect instance that has already been created by AspectJ so that we can inject it with a CriticismEngine. Conveniently, all AspectJ aspects provide a static aspectOf() method that returns the singleton instance of the aspect. So to get an instance of the aspect, you must use factory-method to invoke the aspectOf() method instead of trying to call JudgeAspect’s constructor.
In short, Spring doesn’t use the <bean> declaration from earlier to create an instance of the JudgeAspect—it has already been created by the AspectJ runtime. Instead, Spring retrieves a reference to the aspect through the aspectOf() factory method and then performs dependency injection on it as prescribed by the
<bean> element.
Summary
AOP is a powerful complement to object-oriented programming. With aspects, you can now group application behavior that was once spread throughout your applications into reusable modules. You can then declaratively or programmatically define exactly where and how this behavior is applied. This reduces code duplication and lets your classes focus on their main functionality.
Spring provides an AOP framework that lets you insert aspects around method executions. You have learned how you can weave advice before, after, and around a method invocation, as well as add custom behavior for handling exceptions.
You have several choices in how you can use aspects in your Spring applications. Wiring advice and pointcuts in Spring is much easier in Spring 2.0 with the addition of @AspectJ annotation support and a simplified configuration schema.
Finally, there are times when Spring AOP isn’t powerful enough and you must turn to AspectJ for more powerful aspects. For those situations, we looked at how to use Spring to inject dependencies into AspectJ aspects.
At this point, we’ve covered the basics of the Spring Framework. You’ve seen how to configure the Spring container and how to apply aspects to Springmanaged objects. These core Spring techniques will be foundational throughout the rest of the book. In the coming chapters, we’ll begin applying what we’ve learned as we develop enterprise capabilities into our applications. We’ll start in the next chapter by looking at how to persist and retrieve data using Spring’s JDBC and ORM abstractions.
------------------------
ТРИО теплый пол отзыв
Заработок на сокращении ссылок
Earnings on reducing links
Код PHP на HTML сайты
Категория: Книги по Java
Комментарии |