1. opensymphony
  2. xwork


xwork / docs / components.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<html xmlns="http://www.w3.org/1999/xhtml" lang="en_US" xml:lang="en_US">
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <title>XWork Documentation</title>
  <link type="text/css" href="main.css" rel="STYLESHEET"/>
  <div id="page-logo">
    <a href="index.html"><img src="logo.png" border="0"/></a>
    <div class="snip-title">
	  <h1 class="snip-name">XWork Components
<div id="snip-content" class="snip-content">

 <div class="snip-attachments"></div>
 <h3 class="heading-1">Overview
</h3><p class="paragraph"/><a href="index.html">XWork</a> provides the ComponentManager interface (and a corresponding implementation in the DefaultComponentManager class) to allow a design pattern known as <b class="bold">Inversion of Control</b> (or <b class="bold">IoC</b> for short) to be applied to your actions or other arbitrary objects. In a nutshell, the IoC pattern allows a parent object (in this case XWork's ComponentManager instance) to control a client object (usually an action, but it could be any object that implements the appropriate <b class="bold">enabler</b>).<p class="paragraph"/>You may also want to look at <a href="http://wiki.opensymphony.com/space/WebWork+2+Components">WebWork 2 Components</a> to see how WW2 uses XWork's IoC architecture.
<h3 class="heading-1">Why IoC?
</h3><p class="paragraph"/>So why is IoC useful? It means that you can develop components (generally services of some sort) in a top-down fashion, without the need to build a registry class that the client must then call to obtain the component instance.<p class="paragraph"/>Traditionally when implementing services you are probably used to following steps similar to these:
<li>Write the component (eg an ExchangeRateService)</li>
<li>Write the client class (eg an XWork action)</li>
<li>Write a registry class that holds the component object (eg Registry)</li>
<li>Write code that gives the component object to the registry (eg Registry.registerService(new MyExchangeRateService());)</li>
<li>Use the registry to obtain the service from your client class (eg ExchangeRateService ers = Registry.getExchangeRateService();)</li>
<li>Make calls to the component from the client class (eg String baseCurrencyCode = ers.getBaseCurrency();)</li>
</ol><p class="paragraph"/>Using IoC, the process is reduced to the following:
<li>Write the component class (eg an ExchangeRateService)</li>
<li>Register the component class with XWork (eg componentManager.addEnabler(MyExchangeRateService, ExchangeRateAware);)</li>
<li>Write the client class, making sure it implements the enabling interface (eg an XWork action that implements ExchangeRateAware)</li>
<li>Access the component instance directly from your client action (eg String baseCurencyCode = ers.getBaseCurrency();)</li>
</ol><p class="paragraph"/>XWork takes care of passing components through to enabled action classes or other components.<p class="paragraph"/>Some other benefits that IoC can provide include:
<ul class="minus">
<li>A component describes itself. When you instantiate a component, you can easily determine what dependencies it requires without looking at the source or using trial and error.</li>
<li>Dependencies can be discovered easily using reflection. This has many benefits ranging from diagram generation to runtime optimization (by determining in advance which components will be needed to fufill a request and preparing them asyncronously, for example).</li>
<li>Avoids the super-uber-mega-factory pattern where all the components of the app are held together by a single class that is directly tied back to other domain specific classes, making it hard to 'just use that one class'.</li>
<li>Adheres to Law of Demeter. Some people think this is silly, but in practise I've found it works much better. Each class is coupled to only what it actually uses (and it should never use too much) and no more. This encourages smaller responsibility specific classes which leads to cleaner design.</li>
<li>Allows context to be isolated and explicitly passed around. ThreadLocals may be ok in a web-app, but they aren't well suited for high concurrency async applications (such as message driven applications).</li>
<h3 class="heading-1">Configuration - xwork.xml
</h3><p class="paragraph"/>The ComponentInterceptor class is used to apply the IoC pattern to XWork actions (ie, to supply components to actions). The ComponentInterceptor should be declared in the &#60;interceptors&#62; block of xwork.xml as follows:<p class="paragraph"/><div class="code"><pre>&#60;interceptor name=<span class="xml&#45;quote">"component"</class>
        class=<span class="xml&#45;quote">"com.opensymphony.xwork.interceptor.component.ComponentInterceptor"</class>/&#62;</pre></div><p class="paragraph"/>You should ensure that any actions that are to be supplied with components have this interceptor applied. (See <a href="interceptors.html">XWork Interceptors</a> for information on how to apply interceptors to actions.)<p class="paragraph"/>If you want to apply IoC to objects other than actions or other components, you will need to use the ComponentManager object directly.
<h3 class="heading-1">Writing Component Classes
</h3><p class="paragraph"/>The actual component class can be virtually anything you like. The only constraints on it are that it must be a concrete class with a default constructor so that XWork can create instances of it as required. Optionally, a component may implement the Initializable and/or Disposable interfaces so it will receive lifecycle events just after it is created or before it is destroyed.
<h3 class="heading-1">Component Dependencies
</h3><p class="paragraph"/>One feature that is not immediately obvious is that it is possible for components to depend on other components. For example if the ExchangeRateService described above depended on a Configuration component, XWork will pass the Configuration component through to the ExchangeRateService instance after ExchangeRateService is instantiated. Note that XWork automatically takes care of initializing the components in the correct order, so if A depends on B and C, and B depends on C, the ComponentManager will load C first, then B then A.
<h3 class="heading-1">Writing Enablers
</h3><p class="paragraph"/>An enabler should consist of just a single method that accepts a single parameter. The parameter class should either be the component class that is to be enabled, or one of the component's superclasses. XWork does not care what the name of the enabler's method is.<p class="paragraph"/>Here is an example of what the ExchangeRateAware enabler might look like:<p class="paragraph"/><div class="code"><pre><span class="java&#45;keyword">public</span> <span class="java&#45;keyword">interface</span> ExchangeRateAware &#123;
    <span class="java&#45;keyword">public</span> void setExchangeRateService(ExchangeRateService exchangeRateService);
&#125;</pre></div><p class="paragraph"/>Note that typically an enabler would be an interface, however there is nothing to prevent you from using a class instead if you so choose.
<h3 class="heading-1">Writing "Enabler-aware" Actions
</h3><p class="paragraph"/>All an action needs to do is implement the relevant enabler interface. XWork will then call the action's enabler method just prior to the action's execution. As a simple example:<p class="paragraph"/><div class="code"><pre><span class="java&#45;keyword">public</span> class MyAction <span class="java&#45;keyword">extends</span> ActionSupport <span class="java&#45;keyword">implements</span> ExchangeRateAware &#123;
    ExchangeRateService ers;<p class="paragraph"/>    <span class="java&#45;keyword">public</span> void setExchangeRateService(ExchangeRateService exchangeRateService) &#123;
        ers = exchangeRateService;
    &#125;<p class="paragraph"/>    <span class="java&#45;keyword">public</span> <span class="java&#45;object">String</span> execute() <span class="java&#45;keyword">throws</span> Exception &#123;
        <span class="java&#45;object">System</span>.out.println(<span class="java&#45;quote">"The base currency is "</span> + ers.getBaseCurrency());
&#125;</pre></div><p class="paragraph"/>If you have an object that is not an action or another component, you must explictly tell XWork to supply any enabled components to your object by calling componentManager.initializeObject(enabledObject);<p class="paragraph"/>-Chris