ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Lazy Loading with Aspects
Pages: 1, 2

Introducing the Aspects

Finally, four aspects are declared to encapsulate lazy loading in the example application. The ProxyPattern, DelegatingProxyPattern, LazyLoading, and TraceLazyInitialization aspects.



The ProxyPattern aspect and its derived DelegatingProxyPattern aspect are not shown here, but can be found in the full source for this example. Both of these aspects provide a hierarchy that supports a generic reusable abstract foundation that encapsulates the GoF Proxy Design Pattern, in particular the delegation characteristics of the pattern.

The Proxy Pattern, particularly in its delegation mode as the DelegatingProxyPattern that specializes the pattern to, is an ideal base for a lazy-loading system, as it provides a means by which control can be gained to any and all calls to an object. The original Proxy Pattern AspectJ implementation adapted for this example came from the research completed by Gregor Kiczales and Jan Hannemann.

The LazyLoading aspect as shown in the following code snippet encapsulates the specific application of the proxy pattern to the lazy-loading policies. It defines the logic to capture messages for the concrete target objects so that they can be initialized if necessary and then the message passed on. If the object already exists then the message is simply passed on without any additional instantiation.

public aspect LazyLoading 
   extends DelegatingProxyPattern
{
	
   /** 
    * Assigns the Subject role to Feature.
    */
   declare parents : Feature implements Subject;
   
   /** 
    * Provides a default implementation of the 
    * doSomething(String) method to 
    * stop implementations within this application 
    * from having to implement this method. This 
    * will deliberately not include features 
    * developed by third parties that must still 
    * implement the Feature interface fully).
    */
   public void Feature.doSomething(
      String message)
   {
   }
   
   /** 
    * Provides a means by which Features can 
    * be made available.
    */
   public Feature initializeFeature(
                      String featureClass)
   {
      Feature feature =
         new LazyFeatureProxy(featureClass);
      return feature;
   }
   
   /**
    * Captures all accesses to the subject that 
    * should be protected by this pattern 
    * instance. In this case all calls to 
    * RealSubject.write(..).
    */
   protected pointcut requestTriggered() : 
      call(* features.Feature.* (..)) &&
      !within(feature_management.LazyLoading +);
   
   /**
    * Delegates the call to a newly initialised 
    * object if not previously initialised.
    */
   protected Object delegateRequest(
      Object caller,
      Subject subject,
      JoinPoint joinPoint)
   {
      if (subject instanceof LazyFeatureProxy)
      {
         LazyFeatureProxy feature =
            (LazyFeatureProxy) subject;
         try
         {
            Feature implementedFeature =
               feature.getFeature();

			implementedFeature.doSomething(
               (String) joinPoint.getArgs()[0]);
         }
         catch (Exception e)
         {
            System.out.println(
               "Exception when attempting to "
                  + "lazy load"
                  + " a particular class,"
                  + " aborting the call");
         }
      }
      else
      {
         System.out.println(
            "Feature already loaded so "
               + "passing the "
               + "request on to it");

         /* Call doSomething on the feature, 
          * examining the args first to know 
          * how to pass it on
          */
         ((Feature) subject).doSomething(
            (String) joinPoint.getArgs()[0]);
      }
      return null;
   }
}

Finally, the TraceLazyInitialization aspect, as shown in the final code snippet below, merely provides a testing capability for the application to produce the output analyzed for the Summary and Conclusion to this article. This aspect merely examines each of the applications join points to provide an effective trace of when and where particular loading and initializations occurred to provide proof that lazy loading is indeed happening.

public aspect TraceLazyInstantiation 
{
   public pointcut lazyClassInitialized() : 
      initialization(
         features.FeatureA.new (..))
         || initialization(
               features.FeatureB.new (..));

   public pointcut cflowLazyInitialization() : 
      cflow(execution(MainApplication.new (..)))
      && !within(TraceLazyInstantiation);

   after() : lazyClassInitialized()
   {
      System.out.println(
         "*** Implementation class initialised: "
            + thisJoinPoint
               .getStaticPart()
               .getSignature()
            + " ***");
   }

   before() : cflowLazyInitialization()
   {
      System.out.println(thisJoinPoint);
   }
}

Summary and Conclusion

The example has shown that although the AspectJ version is certainly more complicated, it does have some significant advantages over the more traditional OO implementation of lazy loading. The most powerful advantage is that the lazy-loading logic is modularized and encapsulated within the LazyLoading aspect, rather than spread throughout the business logic classes. Therefore it can be amended and managed in a central location rather than the maintenance nightmare that could occur if changes had to be made to the mechanism in more traditional implementations.

An additional benefit is that to remove lazy loading would simply involve changing the initialization calls in the main method to initialize the concrete class instances directly. This would effectively remove the lazy-loading behavior should it not be needed with only a very simple change, rather than the fundamental architecture change that would be necessary in an OO-only system.

It is worth pointing out that there are several interesting features of AspectJ that have been included in this example that may confuse the beginner. AspectJ allows you to define default behavior on interfaces and this has been done in this case as part of the LazyLoading aspect affecting the Feature interface.

The default behavior leaves the interface as still being an interface, but also provides default behavior for specific areas of the interface to give the implementing classes the option of implementing that behavior or not.

In this example the LazyFeature did not need to implement the behavior for the doSomething(String) method since the message was only being used for interception by the lazy-loading aspect and so a default empty implementation was provided. However, the external feature developers would still be forced to implement the method since they would not be applying the LazyLoading aspect as part of their development, and therefore would only see the interface definition.

The apparent disparity between the default implementation of the doSomething(String) method being declared in the LazyLoading aspect could have been alleviated to some degree by declaring a static inner aspect in the Feature interface. However, this would have forced the implementers of the concrete features using the source code for the interface to use AspectJ to compile against the Feature interface. This constraint was avoided by placing the lazy-loading aspect specific extensions to the Feature interface in the aspect only.

In conclusion, although lazy loading can certainly be implemented effectively using traditional Object-Oriented methods only, the crosscutting nature of lazy loading is an ideal candidate for using an Aspect-Oriented approach, improving modularization in various fundamental ways.

Russell Miles is a senior consultant for SpringSource in the UK and contributes to various open source projects while working on books for O'Reilly.


Return to ONJava.com.