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

advertisement

AddThis Social Bookmark Button

Lazy Loading with Aspects

by Russell Miles
03/17/2004

In this article you will discover how Aspect Oriented Programming with AspectJ can apply Lazy Loading to areas within an application. By using the simple example provided here to control when an object is loaded, you can apply lazy loading to your own applications wherever you feel it is required.

So What Is Lazy Loading?

Lazy loading is a characteristic of an application when the actual loading and instantiation of a class is delayed until the point just before the instance is actually used. The goal is to only dedicate memory resources when necessary by only loading and instantiating an object at the point when it is absolutely needed.

Tools such as Eclipse have popularized the lazy-loading approach as they use the facility to control the load and initialization of heavyweight plug-ins. This gives the double bonus of speeding up the initial load time for the application, as not all plug-ins are loaded straightaway; ensuring efficiency as only the plug-ins that are used are loaded at all. This article will demonstrate how lazy loading can be achieved in your application where certain features of the design do not need to be loaded up front but rather on demand.

Lazy Loading with Traditional Object Orientation

Traditional Object Oriented (OO) methods for providing lazy loading usually lead to business logic being interleaved with the lazy loading code. Figure 1 shows a traditional object interaction sequence where lazy loading is applied.

Figure 1
Figure 1. Lazy loading using traditional Object Oriented techniques.

The main class in this simple example invokes the proxy objects directly. The proxy objects (of class LazyFeature) each encapsulate how the lazy loading takes place and load and initialize the implementation objects (of class FeatureA) that they are providing a proxy for.

The main problem with this approach is that the lazy-loading architecture is interleaved with the business logic encapsulated within the individual implementation classes and the main class. Although the architecture is simple, the lazy-loading capability is closely coupled to the classes involved and it could prove difficult to remove the lazy loading or even to apply new lazy-loading techniques, at a later point in time.

Lazy Loading with Aspects

Lazy loading is really an overall policy that should be adopted for the loading and initialization of declared types of objects, and therefore it is not directly related to the business logic of the application. For this reason lazy loading is actually a crosscutting concern. Aspect Orientation (AO) is an ideal candidate for improving the modularity of the implementation by de-coupling the crosscutting concern, in this case lazy loading, from the business logic. Figure 2 shows how an AO approach could modularize lazy loading in the same example application.

Figure 2
Figure 2. Lazy loading using Aspect Oriented techniques.

In the AO example the main program is not closely coupled to the lazy-loading classes. In fact, it has no knowledge of the lazy loading taking place beyond the initializeFeature(String) call that is used to create a new Feature object. The aspect itself controls all lazy-loading policies by capturing join points on calls to the Feature objects and delegating those calls where necessary to newly created or existing implementation class instances.

All lazy loading is controlled by the Aspect, modularizing the concern and providing a single place for lazy-loading policy enforcement. This is achieved without the main program, the proxy, or individual implementation classes having any knowledge of the lazy-loading policy.

A Practical Example

The following example gives a flavor of how lazy loading could be applied using Java and AspectJ. The full code for this example is available as a compressed .zip file and it is highly recommend that you download and compile the example in Eclipse with the AspectJ Development Tool plug-in installed. As well, any AspectJ development environment should work with some tweaking.

First a simple application is declared as shown in the following code sample. The MainApplication class simply loads the features according to their configurations (in this case String representations of the class names) and then sends example calls to the various features that it holds. The lazy-loading policy is simply that the classes that implement the Feature interface will not be loaded and initialized until the first calls to the methods on the feature, but this is not of concern to the main class.

public class MainApplication
{
   private Feature[] features;

   /**
    * Overriding the default constructor to 
    * show an example usage of the Lazy Loading 
    * capability.
    */
   public MainApplication()
   {
      features = new Feature[2];

      features[0] =
         LazyLoading
            .aspectOf()
            .initializeFeature(
            "features.FeatureA");

      features[1] =
         LazyLoading
            .aspectOf()
            .initializeFeature(
            "features.FeatureB");

      features[0].doSomething("Hello there");
      features[0].doSomething("Hello again");

      features[1].doSomething("Hi to you too");
      features[1].doSomething("Hi again");
   }

   /**
    * The main program that runs this example.
    * @param args The arguments passed from the 
    *             command line
    */
   public static void main(String[] args)
   {
      MainApplication mainApplication =
         new MainApplication();
   }
}

The rest of the example architecture includes the Feature interface shown in the next code snippet. The Feature interface declares the methods that any feature, lazy loaded or not, must fulfill. Also an example FeatureA class is provided that implements the Feature interface and could be provided by a third party. Once again, neither the interface nor the specialized implementation classes need to have any knowledge of the proposed lazy-loading policy.

public interface Feature
{
   /**
    * A simple example method on the feature.
    * @param message Some data being sent to 
    *                the feature.
    */
   public void doSomething(String message);
}

public class FeatureA implements Feature
{
   public void doSomething(String message)
   {
      System.out.println(
         "doSomething called in "
            + this
            + ", with: "
            + message);
   }
}

A proxy, the LazyFeatureProxy class, is declared to encapsulate the creation calls to the distinct feature implementation classes as shown in the next code snippet. This simple proxy also adds the behavior necessary for the lazy-loading policy to complete its function.

public class LazyFeatureProxy implements Feature
{
   private String featureClass;
   private Feature delegate;

   /**
    * The constructor for the LazyFeatureProxy 
    * class. This example simply sets up the 
    * proxy with the class that is to be used 
    * when instantiating objects as needed. 
    * This could also be configured in a more 
    * application specific way, such as using XML 
    * configuration information for example.
    * @param featureClass The string class name 
    *                     of the class being 
    *                     proxied.
    */
   public LazyFeatureProxy(String featureClass)
   {
      this.featureClass = featureClass;
   }

   /**
    * This method initializes the feature 
    * when the implementation is actually 
    * needed.
    * @return Feature the initialized concrete 
    *         feature.
    * @throws IllegalAccessException
    * @throws InstantiationException
    */
   public Feature getFeature()
      throws
         IllegalAccessException,
         InstantiationException,
         ClassNotFoundException
   {
      if (this.delegate == null)
      {
         return this.delegate =
            (Feature) Class
               .forName(this.featureClass)
               .newInstance();
      }
      else
      {
         return this.delegate;
      }
   }
}

Pages: 1, 2

Next Pagearrow