Skip to main content

skip to main content

developerWorks  >  Java technology  >

AOP@Work: Dependency injection with AspectJ and Spring

Advanced techniques for aspect-oriented developers

developerWorks
Document options

Document options requiring JavaScript are not displayed

Sample code


Rate this page

Help us improve this content


Level: Intermediate

Adrian Colyer (adrian.colyer@interface21.com), Chief scientist, Interface21

13 Dec 2005

Dependency injection and aspect-oriented programming are complementary techniques, so it's natural to want to use them together. Follow along as Adrian Colyer explores the relationship between the two and shows you how you can combine them to facilitate advanced dependency injection scenarios.

Dependency injection and aspect-oriented programming (AOP) are two key technologies that help to simplify and purify domain models and application layers in enterprise applications. Dependency injection encapsulates the details of resource and collaborator discovery, and aspects can (among other things) encapsulate the details of middleware service invocations -- for example, to provide transaction and security management. Because both dependency injection and AOP lead to simpler, more easily tested object-based applications, it is natural to want to use them together. Aspects can help to bring the power of dependency injection to a wider range of objects and services, and you can use dependency injection to configure the aspects themselves.

In this article, I show you how to combine the dependency injection of the Spring framework effectively with aspects written using AspectJ 5. I assume you have a basic knowledge of AOP (although if you don't, you'll find some good starting points in the Resources section), so I'll begin my discussion by analyzing the key roles and responsibilities involved in a dependency injection-based solution. From there, I'll show you how to configure singleton aspects via dependency injection. Because configuring non-singleton aspects shares much in common with configuring domain objects, I'll then look at a simple solution that applies to them both. I conclude the article by showing you how to use aspects for several advanced dependency injection scenarios, including interface-based injection and repeated injection.

About this series

The AOP@Work series is intended for developers who have some background in aspect-oriented programming and want to expand or deepen what they know. As with most developerWorks articles, the series is highly practical: you can expect to come away from every article with new knowledge that you can put immediately to use.

Each of the authors contributing to the series has been selected for his leadership or expertise in aspect-oriented programming. Many of the authors are contributors to the projects or tools covered in the series. Each article is subjected to a peer review to ensure the fairness and accuracy of the views expressed.

Please contact the authors individually with comments or questions about their articles. To comment on the series as a whole, you may contact series lead Nicholas Lesiecki. See Resources for more background on AOP.

See Download for the article source and Resources to download AspectJ or the Spring framework, which you will need to follow the examples.

What is dependency injection?

In the book Domain-Driven Design, Eric Evans argues persuasively for hiding objects from the details of their configuration and association establishment:

Much of the power of objects rests in the intricate configuration of their internals and associations. An object should be distilled until nothing remains that does not relate to its meaning or support its role in interactions. This mid-life cycle responsibility is plenty. Problems arise from overloading a complex object with responsibility for its own creation.

Evans goes on to give an example of a car engine whose dozens of parts collaborate to perform the engine's responsibility. While you might be able to imagine an engine block capable of grabbing hold of a set of pistons and inserting them into its cylinders, such a design would significantly complicate the engine. Instead, a human mechanic or a robot assembles the engine, and the engine itself is concerned only with its operation.

While I took this example from a section of the book introducing the concept of factories for complex object creation, we can also apply it to explain the motivation for dependency injection techniques.

From collaboration to contract

Further reading
For the classic introduction to dependency injection, see Martin Fowler's “Inversion of Control Containers and the Dependency Injection Pattern." For more about dependency injection using Spring, see Professional Java Development with the Spring Framework. Links for both are in Resources.

For the purposes of this article, you can think of dependency injection as a contract between an object and the environment in which it executes. An object (playing any or all of the roles of ResourceConsumer, Collaborator, and ServiceClient) agrees not to go out searching for the resources it needs, partners it collaborates with, or services it uses. Instead, the object provides a means for these dependencies to be provided to it. In turn, the execution environment agrees to provide the object with all its dependencies before they are needed.

The way that the dependencies are resolved differs in different scenarios. In a unit test case, for example, the execution environment for the object is the test case itself, and the test setup code is tasked with satisfying the dependencies directly. In an integration test or when the application is in production, a broker is responsible for finding the resources that satisfy the object's dependencies and passing them to it. The broker role is often played by a lightweight container such as the Spring framework. Regardless of how dependencies are resolved, the object being configured is typically unaware of such details. In the second example, it would likely be unaware of the presence of the broker.

A broker such as the Spring framework has four key responsibilities, which I return to throughout the article:

  • Determining that an object needs configuring (typically because it has just been created)
  • Determining the dependencies of the object
  • Finding the objects that satisfy those dependencies
  • Configuring the object with its dependencies

Multiple strategies are possible for discharging these responsibilities, as you'll see from the variety of dependency injection solutions that follow.



Back to top


Dependency injection with Spring

In a standard Spring deployment, the Spring container is responsible for both creating and configuring the core application objects (known as beans). Because the container both creates beans and plays the role of broker, it is trivial for the Spring container to determine that a bean has been created and needs configuring. The dependencies of the bean are determined by querying the application metamodel, which is typically expressed in XML as a Spring configuration file.

The objects that satisfy the bean's dependencies are other beans managed by the container. The container acts as a repository for these beans so that they can be looked up by name (or created if need be). Finally, the container configures the new bean with its dependencies. This is usually done via setter injection (calling setter methods on the new bean passing in the dependencies as arguments), though Spring supports other forms of injection such as constructor injection and lookup-method injection. (See Resources to learn more about dependency injection using Spring.)



Back to top


Dependency injection for aspects

Like any other object, aspects can benefit from configuration through dependency injection. In many cases, it's good practice to implement an aspect as a lightweight controller. In this case, the aspect determines when some behavior should be executed but delegates to a collaborator to perform the actual work. For example, you could configure an exception-handling aspect with an exception-handling strategy object. The aspect would detect when exceptions were thrown and delegate to the handler to handle them. Listing 1 shows a basic RemoteException handling aspect:


Listing 1. RemoteException handling aspect
    

public aspect RemoteExceptionHandling {
      private RemoteExceptionHandler exceptionHandler;
    
      public void setExceptionHandler(RemoteExceptionHandler aHandler) {
        this.exceptionHandler = aHandler;
      }
    
      pointcut remoteCall() : call(* *(..) throws RemoteException+);
    
      /**
       * Route exception to handler. RemoteException will still 
       * propagate to caller unless handler throws an alternate 
       * exception.
       */
      after() throwing(RemoteException ex) : remoteCall() {
        if (exceptionHandler != null)  
          exceptionHandler.onRemoteException(ex);
      }  
    }
  

Go to the source!

If you want to see the RemoteExceptionHandling aspect configuration in action, download the article source and run the RemoteExceptionHandlingTest test case from the testsrc folder.

Now I'd like to configure my aspect with a particular exception handling strategy using dependency injection. For this, I can use the standard Spring approach, but with one caveat. Normally Spring is responsible for both creating and configuring beans. AspectJ aspects, however, are created by the AspectJ runtime. I need Spring to configure the aspect that AspectJ has created. For the most common case of singleton aspects such as the RemoteExceptionHandling aspect above, AspectJ defines an aspectOf() method that returns the aspect instance. I can tell Spring to use the aspectOf() method as a factory method for obtaining the aspect instance. Listing 2 shows the Spring configuration for the aspect:


Listing 2. Spring configuration for an aspect
    

    
  <beans>      
      <bean name="RemoteExceptionHandlingAspect"
        class="org.aspectprogrammer.dw.RemoteExceptionHandling"
        factory-method="aspectOf">
        <property name="exceptionHandler">
          <ref bean="RemoteExceptionHandler"/>
        </property>
      </bean>
      
      <bean name="RemoteExceptionHandler" 
        class="org.aspectprogrammer.dw.DefaultRemoteExceptionHandler">
      </bean>
  </beans>
   

I want to be sure my aspect is configured before any remote exceptions are thrown. In the sample code, I'm using a Spring ApplicationContext that ensures this is the case, because it automatically pre-instantiates all singleton beans. If I were using a plain BeanFactory, then calling preInstantiateSingletons would achieve the same result.



Back to top


Dependency injection for domain objects

Configuring singleton aspects is as easy as configuring any other bean inside a Spring container, but what about aspects that have other lifecyles such as perthis, pertarget, or even percflow aspects? Aspect instances that have a lifecycle other than singleton cannot be pre-instantiated by the Spring container; instead, they are created by the AspectJ runtime in accordance with the aspect declaration. So far, the broker (Spring) has known that an object needed configuring because it created that object. If I want to perform dependency injection of non-singleton aspects, I need to use a different strategy to determine that an object has been created that needs configuration.

Non-singleton aspects aren’t the only kinds of objects created outside the control of the Spring container that would benefit from externalized configuration. For example, domain entities that need access to repositories, services, and factories (see Resources) would reap the same benefits from dependency injection as do container-managed beans. Recall the four responsibilities of a broker:

  • Determining that an object needs configuring (typically because it has just been created)
  • Determining the dependencies of the object
  • Finding the objects that satisfy those dependencies
  • Configuring the object with its dependencies

I still want to use Spring to determine the dependencies of an object, to find the objects that satisfy those dependencies, and to configure the object with its dependencies. However, I need another way to determine that an object needs configuring. In particular, I need a solution for objects that can be created at arbitrary points in the execution of the application, outside of the Spring container's control.

The SpringConfiguredObjectBroker

I call an object to be configured by Spring a SpringConfigured object. The requirement follows that after creating a new SpringConfigured object, I should ask Spring to configure it. A SpringConfiguredObjectBroker backed by a Spring ApplicationContext should do the job, as shown in Listing 3:


Listing 3. @SpringConfigured object broker

public aspect SpringConfiguredObjectBroker 
    implements ApplicationContextAware {
    
      private ConfigurableListableBeanFactory beanFactory;
    
      /**
       * This broker is itself configured by Spring DI, which will
       * pass it a reference to the ApplicationContext
       */
      public void setApplicationContext(ApplicationContext aContext) {
        if (!(aContext instanceof ConfigurableApplicationContext)) {
          throw new SpringConfiguredObjectBrokerException(
            "ApplicationContext [" + aContext +
            "] does not implement ConfigurableApplicationContext"
          );
        }
        this.beanFactory = 
          ((ConfigurableApplicationContext)aContext).getBeanFactory();
      }
    
      /**
       * creation of any object that we want to be configured by Spring
       */
      pointcut springConfiguredObjectCreation(
                  Object newInstance,
                  SpringConfigured scAnnotation
                  ) 
        : initialization((@SpringConfigured *).new(..)) &&
          this(newInstance) &&
          @this(scAnnotation);
    
      /**
       * ask Spring to configure the newly created instance
       */
      after(Object newInstance, SpringConfigured scAnn) returning 
       : springConfiguredObjectCreation(newInstance,scAnn)
      {
        String beanName = getBeanName(newInstance, scAnn);
        beanFactory.applyBeanPropertyValues(newInstance,beanName);
      }
    
      /**
       * Determine the bean name to use - if one was provided in
       * the annotation then use that, otherwise use the class name.
       */
      private String getBeanName(Object obj, SpringConfigured ann) {
        String beanName = ann.value();
        if (beanName.equals(“”)) {
          beanName = obj.getClass().getName();
        }
        return beanName;
      }
    }
  

Inside the SpringConfiguredObjectBroker

I'll walk through the parts of the SpringConfiguredObjectBroker aspect in turn. First, the aspect implements the Spring ApplicationContextAware interface. The broker aspect is itself configured by Spring (that's how it obtains the reference it needs to the application context). Having the aspect implement the ApplicationContextAware interface ensures that Spring knows to pass it a reference to the current ApplicationContext during configuration.

The pointcut springConfiguredObjectCreation() matches the initialization join point of any object with the @SpringConfigured annotation. Both the annotation and the newly created instance are captured as context at the join point. Finally, the after returning advice asks Spring to configure the newly created instance. A bean name is used to look up the configuration information for the instance. I could provide this as the value of the @SpringConfigured annotation, or the default of using the class name can be used.

The aspect implementation itself could be part of a standard library (an aspect like this will in fact be shipped with future releases of Spring), in which case all I need to do is annotate types whose instances are to be configured by Spring, as shown here:

    @SpringConfigured("AccountBean")
    public class Account {
      ...
    }

You can create instances of such types under program control (for example, as the result of a query to the database), and they will have all of their Spring-configured dependencies managed for them automatically. See the article source for examples of the @SpringConfigured annotation being used. Note that while I chose to use an annotation for this example (because it is a very natural way to provide the bean name), a marker interface would make it possible to use the approach with Java™ 1.4 and below.

As I discussed at the start of this section, the SpringConfigured technique works not only for domain entities, but for any object created outside the control of the Spring container (for objects created by Spring itself there is no need to add the extra complication). You can configure any aspect, regardless of its lifecycle, this way. For example, if you define a percflow aspect, then AspectJ creates a new aspect instance each time the associated control flow is entered, and Spring configures each of the aspects as it is created.



Back to top


Interface-based injection

So far, I've used the bean definition read by the Spring container to determine the dependencies of an object. A variation on this theme uses contract interfaces for a client to declare its requirements. Suppose the Account entity from the previous section required access to the AccountOperationValidationService. I could declare an interface as shown in Listing 4:


Listing 4. Client interface

public interface AccountOperationValidationClient {
    
   public void setAccountOperationValidationService(
      AccountOperationValidationService aValidationService);
        
}  

Now any object needing access to the AccountOperationValidationService simply has to implement this interface and declare itself as a client. Using an aspect similar to the one I developed in the previous section, I can match all initialization join points for client objects implementing this interface. That takes care of the first broker responsibility: determining when an object needs to be configured. The second responsibility is made explicit in the interface: the dependency that must be satisfied is the validation service dependency. I'm going to use an aspect to dependency inject all clients of the validation service. The easiest way for the aspect to get a hold of the appropriate service to inject is to inject that service into the aspect itself! Listing 5 shows an example:


Listing 5. Service injector aspect

   /** 
     * ensure that all clients of the account validation service
     * have access to it 
     */
    public aspect AccountOperationValidationServiceInjector {
    
      private AccountOperationValidationService service;
    
      /**
       * the aspect itself is configured via Spring DI
       */
      public void setService(AccountOperationValidationService aService){
        this.service = aService;
      }
    
      /**
       * the creation of any object that is a client of the 
       * validation service
       */
      pointcut clientCreation(AccountOperationValidationClient aClient) :
        initialization(AccountOperationValidationClient+.new(..)) &&
        this(aClient);
    
      /**
       * inject clients when they are created
       */
      after(AccountOperationValidationClient aClient) returning :
        clientCreation(aClient) {
        aClient.setAccountOperationValidationService(this.service);
      }
    
    }

This solution gives me two levels of control. The actual definition of the service itself is provided in the Spring configuration file, as for example in the XML fragment of Listing 6:


Listing 6. Service injector configuration
    

   
    <beans>  
      <bean name="AccountOperationValidationServiceInjector"
        class="org.aspectprogrammer.dw.
                  AccountOperationValidationServiceInjector"
        factory-method="aspectOf">
        <property name="service">
        <ref bean="AccountOperationValidationService"/>
          </property>
      </bean>
      
      <bean name="AccountOperationValidationService" 
        class="org.aspectprogrammer.dw.
                 DefaultAccountOperationValidationService">
      </bean>
    </beans> 
  

Clients of the service only have to implement the AccountOperationValidationClient interface and they are automatically configured with the current instantiation of the service as defined by Spring.



Back to top


Repeated injection

Lookup method injection in Spring

Lookup method injection is an advanced feature supported by the Spring container whereby the container overrides abstract or concrete methods on managed beans, to return the result of looking up another named bean in the container. The lookup will typically be of a non-singleton bean. The bean that looks up the dependency defines a lookup method with a declared return type of the type of the bean to be looked up. The Spring configuration file uses the <lookup-method> element inside a bean definition to tell Spring what bean instance to return when the lookup method is called. See Resources to learn more about this technique. A Spring AOP proxy with a HotSwappable target source offers another approach.

So far, I've looked at solutions that involve configuring objects immediately after their instantiation. In some cases, however, the objects that a client needs to collaborate with will change at run time. For example, by interacting with the system, a sales team might be able to dynamically change the pricing strategy and seat allocation strategy for an online booking application. The booking service interacting with the pricing strategy and seat allocation strategy would need the implementations of those strategies that were current at the time of making a booking, not the versions that were current when the booking service itself was first instantiated. In such cases, it is possible to defer injection of a dependency until a client first needs it, and to re-inject the client with the most up-to-date version of the dependency each time it is referenced.

The basic techniques for this scenario involve either field-level injection or getter-method overrides. Before getting into the example, let me stress once more that I'm looking at injection techniques for objects created outside of the Spring container's control. For objects created by Spring, the Spring container already offers simple mechanisms to address these needs.

Field-level injection

In the example below, you can see how to apply a field-level injection for deferred or repeated injection. A get join point of a field lets me determine when to do the injection and the type of the field determines the dependency to inject. So, for example, if a client declares a field like this

private PricingStrategy pricingStrategy;

and somewhere in a client method I find the code

this.pricingStrategy.price(.....);

then the execution of that code at run time leads to a get() join point for the pricingStrategy field that I can use to inject the current pricing strategy implementation, as shown in Listing 7:


Listing 7. Field-level injection example
    

    public aspect PricingStrategyInjector {
    
      private PricingStrategy currentPricingStrategy;
    
      public void setCurrentPricingStrategy(PricingStrategy aStrategy) {
        this.currentPricingStrategy = aStrategy;
      }
    
      /**
       * a client is trying to access the current pricing strategy
       */
      pointcut pricingStrategyAccess() : 
        get(PricingStrategy *) &&
        !within(PricingStrategyInjector);  // don’t advise ourselves!
    
      /**
       * whenever a client accesses a pricing strategy field, ensure they
       * get the latest...
       */
      PricingStrategy around() : pricingStrategyAccess() {
        return this.currentPricingStrategy;
      }
    }

See the article source to observe this technique in action.

The service-location strategy

An alternative to repeated injection is to instead use a more conventional technique to inject a client with a service-location strategy implementation. For example:

public interface PricingStrategyLocator {
      PricingStrategy getCurrentPricingStrategy();
}    

At the expense of defining an additional interface and making the client code a little more verbose, this approach certainly has the edge for me when it comes to code clarity.



Back to top


In conclusion

In this article, I've looked at dependency injection as a contract between an object and the environment in which it executes. The object agrees not to go out searching for the resources it needs, partners it collaborates with, or services it uses. Instead, the object provides a means for these dependencies to be provided to it. In turn, the execution environment agrees to provide the object with all of the dependencies it needs, before the object needs them.

I discussed the four key components of a dependency injection solution, which must be addressed by the broker acquiring dependencies on the object's behalf. Finally, I looked at a number of different approaches to satisfying these requirements. It should be clear that if you can use the Spring container to both instantiate and configure your objects, you should do so. For objects created outside of the control of the Spring container, such as some domain objects or aspects with a non-singleton instantiation model, I recommend using the @SpringConfigured annotation or similar. This technique lets you fully externalize all configuration information into a Spring configuration file.

I wrote the examples for this article using the latest milestone build of AspectJ 5 (as of October 2005) and Spring 1.2.4. Download the complete working examples to begin experimenting with the ideas I've discussed. The test cases under the testsrc directory are a good starting point.




Back to top


Download

DescriptionNameSizeDownload method
Source codej-aopwork13.zip26 KBHTTP
Information about download methods


Resources

Learn

Get products and technologies

Discuss


About the author

Adrian Colyer is the leader of the AspectJ project at Eclipse.org, and the founder of the AJDT project providing IDE support for AspectJ in Eclipse. Prior to taking up the post of chief scientist at Interface21 in October 2005, Adrian led the aspect-oriented software development team at IBM's software development lab in Hursley, England. He is co-author of the book Eclipse AspectJ: Aspect-Oriented Programming with AspectJ and the Eclipse AspectJ Development Tools, and a frequent speaker on the topic of aspect-oriented programming.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top