CHAPTER 6 Services and AOP
Most kayakers that I know are demented and proud of it. We tend to look for those places on a river that are dangerous enough to scare but tame enough to play. While I was learning to surf hydraulics on Barton Creek at high water, I slipped into a hydraulic to play, but couldnt get out. I flipped, upstream, and the hydraulic quickly flipped me 360 degrees, so that I was upright again. I frantically surfed a little while longer, then flipped again, and then all of my control deserted me. The hydraulic then took over, and spun me around like a window shade. When my friends laughed and grabbed their cameras instead of their safety ropes, I bailed, coughing and gagging my way to the bank to nurse my battered ego. I mumbled something about my boat being the source of the problem. Predictably, one of my friends got into my boat, and then toyed with my nemesis, the killer hydraulic, for a while. To make things worse, he then threw his paddle onto the bank, and played some more. He!
said, "Bruce, its not the boat. Its how you use it." Im still fuming today.
Im starting to recognize that the same is true of many Java services. Its not the service; its how you apply it. You can get a whole lot more leverage out of certain services like transactions, security, and logging by packaging them and using them in a way that simplifies them and eliminates duplication in the rest of the code. Spring uses aspect-oriented programming (AOP) to manage these services, called crosscutting concerns. In this chapter, youll learn to build and configure such a service using an interceptor.
Building a Service
The core functions of your application are roughly complete, but its going to be hard to trace what changes a user made, and when. In this example, youll build an audit trail that records a log record whenever someone takes an action that changes the database. Rather than add the same code in many different places, youll build a simple Spring service to do the job. Youll focus on the Hibernate implementation that you built earlier.
Object-oriented languages handle some problems pretty well, but they dont handle crosscutting concerns, common in enterprise developments, very well. Figure 6-1 shows a method with some common enterprise services that crosscut many methods in DAO and façade layers.
To handle crosscutting concerns, you might decide to use a container. Most containers accept only components that match their specification, and provide services to components once theyre in the container. Heavyweight container architectures, like EJB 1.x and 2.x, handle crosscutting concerns, but they force you to write code that depends on your given container. You cant take the code out of the container and expect it to run, and since the container takes too long to start, the result is difficult to test.
Lightweight containers that use AOP technologies accept components too, but the component is a simple POJO. You can then use AOP to attach services to the POJO. Well get into how this works later in the chapter, but for now, think of this feature as the ability to add declarative services to POJO. In Spring, you implement declarative services with configuration rather than code, so you can change services on the fly through changes in the context.
How do I do that?
The interceptor strategy uses three objects: the target (our façade), a proxy object that Spring creates for you, and an interceptor, which youll build. Essentially, the proxy imitates the targetwherever you would use an instance of the target object, Spring will use the proxy instead. The proxy will pass all calls to the object through a series of interceptors that youll configure in the context. Depending on the type of interceptors, the call might pass through the interceptor before reaching the target, on the way back from the target, or both.
Youve got to do three things:
1. Create your service, which well call an interceptor. Another word for interceptors is advice.
2. Configure the beans: the interceptor and the target object.
3. Configure the proxyidentify the proxy, the target object, and the methods that will use our service.
To build advice, youll simply implement an interface. Think of advice as something that will happen at some event in a programs execution. Spring has four types of advice:
Before advice
Spring fires this type of advice before it invokes a method that you specify, called the target method. This type of advice implements the MethodBeforeAdvice interface.
After Returning advice
Spring fires this type of advice after returning from your target method. After Returning advice implements the AfterReturningAdvice interface.
Throws advice
Spring fires this type of advice when your target method throws an exception. This type of advice implements the ThrowsAdvice interface.
Around advice
Spring fires this type of advice before a method, and allows you to choose whether to invoke the target method. After the target method returns, you can add additional code. This is also called an interceptor.
In general, you should use the simplest type of advice that will solve your problem. For this example, youll use before advice, and youll build on the Hibernate interface. The interceptor will implement the MethodBeforeAdvice interface. It will do the logging. Dont worry about how it gets invoked just yet. Example 6-1 gives the advice.
Example 6-1. LoggingBefore.java
public class LoggingBefore implements MethodBeforeAdvice {
private SessionFactory factory;
public SessionFactory getFactory( ) {
return factory;
}
public void setFactory(SessionFactory factory) {
this.factory = factory;
}
public void before(Method method, Object[] objects, Object o)
throws Throwable {
Session s = null;
LogEvent le = new LogEvent(method.getName(), new Date( ));
try {
s = factory.openSession( );
s.save(le);
} catch (Exception ex) {
//log the exception
} finally {
s.close( );
}
}
}