2015年10月7日星期三

4. Spring in Action -- Aspect-oriented Spring

1.  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.

2.  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’re applying the new feature. Cross-cutting concerns can now be modularized into special classes 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 business modules are now cleaner since they only contain code for their primary concern (or core functionality) and secondary concerns have been moved to aspects.

3.  Aspects have a purpose—a job they’re meant to do. In AOP terms, the job of an aspect is called advice. An aspect’s functionality (advice) is woven into a program’s execution at one or more join points:

4.  In addition to describing the job that an aspect will perform, advice addresses the question of when to perform the job. Spring aspects can work with five kinds of advice:
  • Before—The advice functionality takes place before the advised method is invoked.
  • After—The advice functionality takes place after the advised method completes, regardless of the outcome.
  • After-returning—The advice functionality takes place after the advised method successfully completes.
  • After-throwing—The advice functionality takes place after the advised method throws an exception.
  • Around—The advice wraps the advised method, providing some functionality before and after the advised method is invoked.

5.  A join point 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.

6.  An aspect doesn’t necessarily advise all join points in an application. Pointcuts help narrow down the join points 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 join points 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.

7.  The join points are all the points within the execution flow of the application that are candidates to have advice applied. The pointcut defines where (at what join points) that advice is applied.

8.  An aspect is the merger of advice and pointcuts— what it does and where and when it does it.

9.  An introduction allows you to add new methods or attributes to existing classes. 

10.  Weaving is the process of applying aspects to a target object to create a new proxied object. 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.
  • Class load 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 byte-code 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.

11.  AOP frameworks may differ in how rich their join point models are. Some allow you to apply advice at the field-modification level, whereas others only expose the join points 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 join points at which aspects should be woven is what makes it an AOP framework.

12.  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 (http://eclipse.org/aspectj)
  • JBoss AOP (http://www.jboss.org/jbossaop)
  • Spring AOP (http://www.springframework.org)

13.  Spring’s support for AOP comes in four flavors:
  • Classic Spring proxy-based AOP
  • @AspectJ annotation-driven aspects
  • Pure-POJO aspects
  • 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 values into AspectJ-driven aspects.

14.  In Spring, aspects are woven into Spring-managed beans at runtime by wrapping them with a proxy class. The proxy class poses as the target bean, intercepting advised method calls and forwarding those calls to the target bean. Between the time when the proxy intercepts the method call and the time when it invokes the target bean’s method, the proxy performs the aspect logic.

15.  Spring doesn’t create a proxied object until that proxied bean is needed by the application. If you’re using an ApplicationContext, the proxied objects will be created when it loads all of the beans from the BeanFactory.

16.  Because it’s based on dynamic proxies, Spring only supports method join points. This is in contrast to some other AOP frameworks, such as AspectJ and JBoss, which provide field and constructor join points in addition to method pointcuts.

17.  In Spring AOP, pointcuts are defined using AspectJ’s pointcut expression language. For a more detailed discussion of AspectJ and AspectJ’s pointcut expression language, Please refer Ramnivas Laddad’s AspectJ in Action, Second Edition (Manning, 2009, www.manning.com/laddad2/).

18.  Spring only supports a subset of the pointcut designators available in AspectJ:
  • args(): Limits join-point matches to the execution of methods whose arguments are instances of the given types
  • @args(): Limits join-point matches to the execution of methods whose arguments’ runtime type are annotated with the given annotation types
  • execution(): Matches join points that are method executions, this is the primary pointcut designator you will use when working with Spring AOP
  • this(): Limits join-point matches to those where the bean reference of the AOP proxy is of a given type
  • target(): Limits join-point matches to those where the target object is of a given type
  • @target : Limits matching to join points where the class of the executing object has an annotation of the given type
  • within() : limits matching to join points within certain types (simply the execution of a method is invoked within a matching type when using Spring AOP)
  • @within() : limits matching to join points within types that have the given annotation (the execution of methods invoked in types with the given annotation when using Spring AOP)
  • @annotation - limits matching to join points where the subject of the join point (method being executed in Spring AOP) has the given annotation

19.  Attempting to use any of AspectJ’s other designators will result in an IllegalArgumentException being thrown. execution designator is the only one that actually performs matches. The other designators are used to limit those matches. This means that execution is the primary designator you’ll use in every pointcut definition you write. You’ll use the other designators to constrain the pointcut’s reach.

20.  The pointcut expression shown below can be used to apply advice whenever an Instrument's play() method is executed:


You can confine the reach of that pointcut to only the com.springinaction.springidol package:


You’re free to use and in place of && when specifying pointcuts in a Spring XML-based configuration. Likewise, or and not can be used in place of || and ! .

21.  Spring 2.5 introduced a new bean() designator that lets you identify beans by their ID within a pointcut expression. bean() takes a bean ID or name as an argument and limits the pointcut’s effect to that specific bean:

execution(* com.springinaction.springidol.Instrument.play()) and bean(‘eddie’)

Here we’re saying that we want to apply aspect advice to the execution of an Instrument’s play() method, but limited to the bean whose ID is eddie.

22.  A key feature introduced in AspectJ 5 is the ability to use annotations to create aspects. Prior to AspectJ 5, writing AspectJ aspects involved learning a Java language extension. This new feature is commonly referred to as @AspectJ.

23.  The following shows the Audience class now annotated to be an aspect:
@Aspect

public class Audience {

  @Pointcut (“execution(* com.springinaction.springidol.Performer.perform(..))”)

  public void performance() {

  }

  @Before(“performance()”)

  public void takeSeats() {

    System.out.println(“The audience is taking their seats.”);

  }

  @Before(“performance()”)

  public void turnOffCellPhone() {

    System.out.println(“The audience is turning off their cellphones”);

  }

  @AfterReturning(“performance()”)

  public void applaud() {

    System.out.println(“CLAP CLAP CLAP CLAP”);

  }

  @AfterThrowing(“performance()”)

  public void demandRefund() {

    System.out.println(“Boo! We want our money back!”);
  } }
@Aspect annotation indicates that Audience isn’t just any POJO but is an aspect. The name of the pointcut is derived from the name of the method to which the @Pointcut annotation is applied. The actual body of the performance() method is irrelevant and in fact should be empty. You can also make the pointcut inline within the annotation:

@Before(“execution(* com.springinaction.springidol.Performer.perform(..))”)

24.  AspectJ provides five annotations for defining advice:

  • @After: The advice method is called after the advised method returns or throws an exception.
  • @AfterReturning: The advice method is called after the advised method returns.
  • @AfterThrowing: The advice method is called after the advised method throws an exception.
  • @Around: The advice method wraps the advised method.
  • @Before: The advice method is called before the advised method is called.

25.  You must declare an autoproxy bean in the Spring context that knows how to turn @AspectJ-annotated beans into proxy advice. <aop:aspectj-autoproxy/> will create an AnnotationAwareAspectJAutoProxyCreator bean in the Spring context and will automatically proxy beans whose methods match the pointcuts. If you’re using JavaConfig, you can turn on auto-proxying by applying the @EnableAspectJAutoProxy annotation at the class level of the configuration class.

26.  It’s tricky to share information between before advice and after advice without resorting to storing that information in member variables. Around advice has an advantage over before and after advice in this regard. With around advice, you can accomplish the same thing you could with distinct before and after advice, but you can do it in a single method.

27.  Methods annotated with @Around are to be around advice. They must take a ProceedingJoinPoint object as an argument and then call the proceed() method on that object :


What’s also interesting is that just as you can omit a call to the proceed() method to block access to the advised method, you can also invoke it multiple times from within the advice. One reason for doing this may be to implement retry logic to perform repeated attempts on the advised method should it fail.

28.  You can supply parameters to advice using @AspectJ:
@Aspect
public class TrackCounter {

    private Map<Integer, Integer> trackCounts = new HashMap<Integer, Integer>();

    @Pointcut(“execution(* soundsystem.CompactDisc.playTrack(int)) && args(trackNumber)”)

public void trackPlayed(int trackNumber) {

}

    @Before(“trackPlayed(trackNumber)”)

    public void countTrack(int trackNumber) {

        int currentCount = trackCounts.contains(trackNumber) ? trackCounts.get(trackNumber) : 0;

        trackCounts.put(trackNumber, currentCount + 1);

    }

}

the pointcut also declares parameters to be supplied to the advice method:

The args(trackNumber) qualifier indicates that any int argument that is passed into the execution of playTrack() should also be passed into the advice. The parameter name, trackNumber, also matches the parameter in the pointcut method signature.

29.  Using an AOP concept known as introduction, aspects can attach all new methods to Spring beans. In Spring, aspects are just proxies that implement the same interface(s) as the beans that they wrap. What if, in addition to implementing those interfaces, the proxy were to also be exposed through some new interface? Then any bean that’s advised by the aspect will appear to implement the new interface, even if its underlying implementation class doesn’t:


30.  To introduces the Encoreable interface to all the existing Performance implementations:

@Aspect

public class EncoreableIntroducer {

  @DeclareParents(value="concert.Performance+", defaultImpl=DefaultEncoreable.class)

  public static Encoreable encoreable;

}

   The @DeclareParents annotation is made up of three parts:

  • The value attribute identifies the kinds of beans that should be introduced with the interface. (The plus sign at the end specifies any subtype of Performance, as opposed to Performance itself.)
  • The defaultImpl attribute identifies the class that will provide the implementation for the introduction.
  • The static property that is annotated by @DeclareParents specifies the interface that’s to be introduced.


31.  To use Spring AOP configuration elements you need aop namespace :

    <beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="

        http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">


32.  The Spring AOP configuration elements are :

  • <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:aspectj-autoproxy> : Enables annotation-driven aspects using @AspectJ.
  • <aop:before> : Defines an AOP before advice.
  • <aop:config> : The top-level AOP element. Most <aop:*> elements must be contained within <aop:config>.
  • <aop:declare-parents> : Introduces additional interfaces to advised objects that are transparently implemented.
  • <aop:pointcut> : Defines a pointcut.

33.  <aop:aspect> has one distinct advantage over @AspectJ in that you don’t need the source code of the class that’s 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.

34.  Using Spring’s AOP configuration elements below, you can turn the audience bean into an aspect:
<aop:config>

    <aop:aspect ref=”audience”>

        <aop:before pointcut=”execution(* com.springinaction.springidol.Performer.perform(..))” method=”takeSeats” />

        <aop:before pointcut=”execution(* com.springinaction.springidol.Performer.perform(..))” method=”turnOffCellPhone” />

        <aop:after-returning pointcut=”execution(* com.springinaction.springidol.Performer.perform(..))” method=”applaud” />

        <aop:after-throwing pointcut=”execution(* com.springinaction.springidol.Performer.perform(..))” method=”demandRefund” />

    </aop:aspect>

</aop:config>

The following figure shows how the advice logic is woven into the business logic:

35.  The following XML 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:

<aop:config>

    <aop:aspect ref=”audience”>

        <aop:pointcut id=”performance” expression=”execution(* com.springinaction.springidol.Performer.perform(..))” />

        <aop:before pointcut-ref=”performance” method=”takeSeats” />

        <aop:before pointcut-ref=”performance” method=”turnOffCellPhone” />

        <aop:after-returning pointcut-ref=”performance” method=”applaud” />

        <aop:after-throwing pointcut-ref=”performance” method=”demandRefund” />

    </aop:aspect>

</aop:config>

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.

36.  The following configuration will pass the advised method’s parameters to the advice:


Here, args is used in a binding form. If a parameter name is used in place of a type name in an args expression, then the value of the corresponding argument will be passed as the parameter value when the advice is invoked. Or you can use arg-names attribute to specify the arguments you want to pass the value to.

37.  The following configurations make the beans whose type matches the Performer interface (per the types-matching attribute) should have implement Contestant interface (per the implement-interface attribute).
<aop:aspect>

  <aop:declare-parents

    types-matching="com.springinaction.springidol.Performer+"

    implement-interface="com.springinaction.springidol.Contestant"

    default-impl="com.springinaction.springidol.GraciousContestant"

    />
</aop:aspect>

In this case, we’re using the default-impl attribute to explicitly identify the implementation by its fully-qualified class name. Alternatively, we could’ve identified it using the delegate-ref attribute  refers to a Spring bean as the introduction delegate. @DeclareParents is an @AspectJ annotation. @AspectJ is a project that’s separate from Spring and thus its annotations aren’t bean-aware.

38.  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. Normally, Spring beans are instantiated by the Spring container, but AspectJ aspects are created by the AspectJ runtime. You need a way for Spring to get a handle to the AspectJ aspect instance that has already been created by AspectJ so that you can inject it with collaborators. 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 the aspect’s constructor.