tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Tapestry > IoC cookbook - basics
Date Fri, 26 Nov 2010 04:39:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/1810/9/12/_/styles/combined.css?spaceKey=TAPESTRY&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/TAPESTRY/IoC+cookbook+-+basics">IoC
cookbook - basics</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~bobharner">Bob
Harner</a>
    </h4>
        <div id="versionComment">
        <b>Comment:</b>
        fixed {code} syntax errors and &lt;/&gt; in code<br />
    </div>
        <br/>
                         <h4>Changes (12)</h4>
                                 
    
<div id="page-diffs">
            <table class="diff" cellpadding="0" cellspacing="0">
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >The PropertyAccess service is defined
inside TapestryIOCModule&amp;apos;s bind() method: <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">{code|borderStyle=solid}
<br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">{code}
<br></td></tr>
            <tr><td class="diff-unchanged" >  public static void bind(ServiceBinder
binder) <br>  { <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >This example includes [ExceptionAnalyzer|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/ExceptionAnalyzer.html],
because it has a dependency on PropertyAccess: <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">{code|borderStyle=solid}
<br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">{code}
<br></td></tr>
            <tr><td class="diff-unchanged" >public class ExceptionAnalyzerImpl
implements ExceptionAnalyzer <br>{ <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >In the previous example, we relied
on the fact that only a single service implements the PropertyAccess interface. Had more than
one done so, Tapestry would have thrown an exception when the ExceptionAnalyzer service was
realized (it isn&amp;apos;t until a service is realized that dependencies are resolved).
<br> <br></td></tr>
            <tr><td class="diff-changed-lines" >That&amp;apos;s normally <span
class="diff-changed-words">ok<span class="diff-added-chars"style="background-color:
#dfd;">ay</span>;</span> in many situations, it makes sense that only a single
service implement a particular interface. <br></td></tr>
            <tr><td class="diff-unchanged" > <br>But there are often exceptions
to the rule, and in those cases, we must provide more information to Tapestry when a service
is defined, and when it is injected, in order to disambiguate -- to inform Tapestry which
particular version of service to inject. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Tapestry defines two such services,
in the [TapestryModule|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/TapestryModule.html].
<br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">{code|borderStyle=solid}
<br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">{code}
<br></td></tr>
            <tr><td class="diff-unchanged" >  @Marker(ClasspathProvider.class)
<br>  public AssetFactory buildClasspathAssetFactory(ResourceCache resourceCache, <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Here&amp;apos;s an example. Again,
we&amp;apos;ve jumped the gun with this _service contributor method_ (we&amp;apos;ll
get into the why and how of these later), but you can see how Tapestry is figuring out which
service to inject based on the presence of those annotations: <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">{code|borderStyle=solid}
<br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">{code}
<br></td></tr>
            <tr><td class="diff-changed-lines" >public void <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">contributeAssetSource(MappedConfiguration&amp;lt;String,
AssetFactory&amp;gt;</span> <span class="diff-added-words"style="background-color:
#dfd;">contributeAssetSource(MappedConfiguration&lt;String, AssetFactory&gt;</span>
configuration, <br></td></tr>
            <tr><td class="diff-unchanged" >      @ContextProvider <br>
     AssetFactory contextAssetFactory, <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >      AssetFactory classpathAssetFactory)
<br>  { <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">configuration.add(&amp;quot;context&amp;quot;,</span>
<span class="diff-added-words"style="background-color: #dfd;">configuration.add(&quot;context&quot;,</span>
contextAssetFactory); <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">configuration.add(&amp;quot;classpath&amp;quot;,</span>
<span class="diff-added-words"style="background-color: #dfd;">configuration.add(&quot;classpath&quot;,</span>
classpathAssetFactory); <br></td></tr>
            <tr><td class="diff-unchanged" >  }{code} <br> <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
        </table>
</div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h1><a name="IoCcookbook-basics-BasicServicesandInjection"></a>Basic
Services and Injection</h1>

<p>A good part of the basics is convention: what to name your classes, what packages
to put them in and so forth.</p>

<p>In many cases, these conventions are just a little stronger: you may have to do some
amount of extra configuration if you choose to go your own way.</p>

<h1><a name="IoCcookbook-basics-GettingStarted"></a>Getting Started</h1>

<p>Like anything else, you want to choose a package for your application, such as org.example.myapp.</p>

<p>By convention, services go in a package named &quot;services&quot;.</p>

<p>Module classes are suffixed with &quot;Module&quot;.</p>

<p>Thus, you might start with a module class org.example.myapp.services.MyAppModule.</p>

<h1><a name="IoCcookbook-basics-SimpleServices"></a>Simple Services</h1>

<p>The simplest services don&apos;t have any special configuration or dependencies.
They are defined as services so that they can be shared.</p>

<p>For example, the <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/PropertyAccess.html"
class="external-link" rel="nofollow">PropertyAccess</a> service is used in multiple
places around the framework to access properties of objects (its a wrapper around the Java
Beans Introspector and a bit of reflection). This is defined in the <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/TapestryIOCModule.html"
class="external-link" rel="nofollow">TapestryIOCModule</a>.</p>

<p>It&apos;s useful to share PropertyAccess, because it does a lot of useful caching
internally.</p>

<p>The PropertyAccess service is defined inside TapestryIOCModule&apos;s bind()
method:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  <span class="code-keyword">public</span> <span class="code-keyword">static</span>
void bind(ServiceBinder binder)
  {
    . . .
    binder.bind(PropertyAccess.class, PropertyAccessImpl.class);
    binder.bind(ExceptionAnalyzer.class, ExceptionAnalyzerImpl.class);
    . . .
  }</pre>
</div></div>

<p>This example includes <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/ExceptionAnalyzer.html"
class="external-link" rel="nofollow">ExceptionAnalyzer</a>, because it has a dependency
on PropertyAccess:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class ExceptionAnalyzerImpl <span
class="code-keyword">implements</span> ExceptionAnalyzer
{
    <span class="code-keyword">private</span> <span class="code-keyword">final</span>
PropertyAccess propertyAccess;

    <span class="code-keyword">public</span> ExceptionAnalyzerImpl(PropertyAccess
propertyAccess)
    {
        <span class="code-keyword">this</span>.propertyAccess = propertyAccess;
    }

    . . .
}</pre>
</div></div>

<p>And that&apos;s the essence of Tapestry IoC right there; the bind() plus the
constructor is <em>all</em> that&apos;s necessary.</p>

<h1><a name="IoCcookbook-basics-ServiceDisambiguation"></a>Service Disambiguation</h1>

<p>In the previous example, we relied on the fact that only a single service implements
the PropertyAccess interface. Had more than one done so, Tapestry would have thrown an exception
when the ExceptionAnalyzer service was realized (it isn&apos;t until a service is realized
that dependencies are resolved).</p>

<p>That&apos;s normally okay; in many situations, it makes sense that only a single
service implement a particular interface.</p>

<p>But there are often exceptions to the rule, and in those cases, we must provide more
information to Tapestry when a service is defined, and when it is injected, in order to disambiguate
&#8211; to inform Tapestry which particular version of service to inject.</p>

<p>This example demonstrates a number of ideas that we haven&apos;t discussed so
far, so try not to get too distracted by some of the details. One of the main concepts introduced
here is <em>service builder methods</em>. These are methods, of a Tapestry IoC
Module class, that act as an alternate way to define a service. You often used a service builder
method if you are doing more than simply instantiating a class.</p>

<p>A service builder method is a method of a Module, prefixed with the word &quot;build&quot;.
This defines a service, and dependency injection occurs on the parameters of the service builder
method.</p>

<p>The Tapestry web framework includes the concept of an &quot;asset&quot;:
a resource that may be inside a web application, or packaged inside a JAR. Assets are represented
as the type <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/Asset.html"
class="external-link" rel="nofollow">Asset</a>.</p>

<p>In fact, there are different implementations of this class: one for context resources
(part of the web application), the other for classpath resources (packaged inside a JAR).
The Asset instances are created via <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapesty/services/AssetFactory.html"
class="external-link" rel="nofollow">AssetFactory</a> services.</p>

<p>Tapestry defines two such services, in the <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/TapestryModule.html"
class="external-link" rel="nofollow">TapestryModule</a>.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  @Marker(ClasspathProvider.class)
  <span class="code-keyword">public</span> AssetFactory buildClasspathAssetFactory(ResourceCache
resourceCache,

  ClasspathAssetAliasManager aliasManager)
  {
    ClasspathAssetFactory factory = <span class="code-keyword">new</span> ClasspathAssetFactory(resourceCache,
aliasManager);

    resourceCache.addInvalidationListener(factory);

    <span class="code-keyword">return</span> factory;
  }

  @Marker(ContextProvider.class)
  <span class="code-keyword">public</span> AssetFactory buildContextAssetFactory(ApplicationGlobals
globals)
  {
    <span class="code-keyword">return</span> <span class="code-keyword">new</span>
ContextAssetFactory(request, globals.getContext());
  }</pre>
</div></div>

<p>Service builder methods are used here for two purposes: For the ClasspathAssetFactory,
we are registering the new service as a listener of events from another service. For the ContextAssetFactory,
we are extracting a value from an injected service and passing <em>that</em> to
the constructor.</p>

<p>What&apos;s important is that the services are differentiated not just in terms
of their id (which is defined by the name of the method, after stripping off &quot;build&quot;),
but in terms of their <em>marker annotation</em>.</p>

<p>The <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Marker.html"
class="external-link" rel="nofollow">Marker</a> annotation provides the discriminator.
When the service type is supplemented with the ClasspathProvider annotation, the ClasspathAssetFactory
is injected. When the service type is supplemented with the ContextProvider annotation, the
ContextAssetFactory is injected.</p>

<p>Here&apos;s an example. Again, we&apos;ve jumped the gun with this <em>service
contributor method</em> (we&apos;ll get into the why and how of these later), but
you can see how Tapestry is figuring out which service to inject based on the presence of
those annotations:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  <span class="code-keyword">public</span> void contributeAssetSource(MappedConfiguration&lt;<span
class="code-object">String</span>, AssetFactory&gt; configuration,
      @ContextProvider
      AssetFactory contextAssetFactory,

      @ClasspathProvider
      AssetFactory classpathAssetFactory)
  {
    configuration.add(<span class="code-quote">"context"</span>, contextAssetFactory);
    configuration.add(<span class="code-quote">"classpath"</span>, classpathAssetFactory);
  }</pre>
</div></div>

<p>This is far from the final word on injection and disambiguation; we&apos;ll be
coming back to this concept repeatedly. And in later chapters of the cookbook, we&apos;ll
also go into more detail about the many other concepts present in this example. The important
part is that Tapestry <em>primarily</em> works off the parameter type (at the
point of injection), but when that is insufficient (you&apos;ll know ... there will be
an error) you can provide additional information, in the form of annotations, to straighten
things out.</p>

    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;">
            <a href="https://cwiki.apache.org/confluence/users/viewnotifications.action"
class="grey">Change Notification Preferences</a>
        </div>
        <a href="https://cwiki.apache.org/confluence/display/TAPESTRY/IoC+cookbook+-+basics">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=23338497&revisedVersion=3&originalVersion=2">View
Changes</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message