Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Fixed bad links due to copy-paste from cwiki-test

...

Service builder methods are public methods. They are often static. Here's a trivial example:

Code Block
java
languagejava
package org.example.myapp.services;

public class MyAppModule
{
  public static Indexer build()
  {
    return new IndexerImpl();
  }
}

...

You can give a service an explicit id by adding it to the method name: buildIndexer(). This is useful when you do not want the service id to match the service interface name (for example, when you have different services that implement the same interface), or when you need to avoid name collisions on the method name (Java allows only a single method with a given name and set of parameters, even if the return types are different, so if you have two different service builder methods that take the same parameters, you should give them explicit service ids in the method name).

Tapestry IoC is case insensitive; later we can refer to this service as "indexer" or "INDEXER" or any variation thereof, and connect to this service.

...

We could extend this example by adding additional service builder methods, or by showing how to inject dependencies. See the service documentation for more details.

Autobuilding Services

Main article: Defining Tapestry IoC ModulesIOC Services

An alternate, and usually preferred, way to define a service is via a module's bind() method. The previous example can be rewritten as:

Code Block
java
languagejava
package org.example.myapp.services;

import org.apache.tapestry5.ioc.ServiceBinder;

public class MyAppModule
{
  public static void bind(ServiceBinder binder)
  {
     binder.bind(Indexer.class, IndexerImpl.class);
  }
}

...

Generally speaking, you should always bind and autobuild your services. The only exceptions are when:

...

You will occasionally find yourself in the position of injecting the same services into your service builder or service decorator methods repeatedly (this occurs much less often since the introduction of service autobuilding). This can result in quite a bit of redundant typing. Less code is better code, so as an alternative, you may define a constructor for your module that accepts annotated parameters (as with service builder injection).

This gives you a chance to store common services in instance variables for later use inside service builder methods.

Code Block
java
languagejava
public class MyModule
{   
  private final JobScheduler scheduler;
  private final FileSystem fileSystem;
  
  public MyModule(JobScheduler scheduler, FileSystem fileSystem)
  {
    this.scheduler = scheduler;
    this.fileSystem = fileSystem;
  }
  
  public Indexer build()
  {
    IndexerImpl indexer = new IndexerImpl(fileSystem);
      
    scheduler.scheduleDailyJob(indexer);
      
    return indexer;
  }
}

...

Note that the fields are final: this is important. Tapestry IoC is thread-safe and you largely never have to think about concurrency issues. But in a busy application, different services may be built by different threads simultaneously. Each module class is a singleton, instantiated at most once, and making these fields final ensures that the values are available across multiple threads. Refer to Brian Goetz's Java Concurrency in Practice for a more complete explanation of the relationship between final fields, constructors, and threads ... or just trust us!

Care should be taken with this approach: in some circumstances, you may force a situation in which the module constructor is dependent on itself. For example, if you invoke a method on any injected services defined within the same module from the module class' constructor, then the service implementation will be needed. Creating service implementations requires the module builder instance ... that's a recursive reference.

...

Again, keep the methods very simple. Use parameter injection to gain access to the dependencies you need.

...

Often, all services in a module should share a marker, this can be specified with a @Marker annotation on the module class. For example, the TapestryIOCModule:

Code Block
java
languagejava
@Marker(Builtin.class)
public final class TapestryIOCModule
{
  . . .

This references a particular annotation class, Builtin:

Code Block
java
languagejava
@Target(
{ PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Builtin
{

}

...

Using this style, the previous example of a module class may be rewritten:

Code Block
java
languagejava
public class MyModule
{
  @Inject
  private JobScheduler scheduler;

  @Inject
  private FileSystem fileSystem;

  public Indexer build()
  {
    IndexerImpl indexer = new IndexerImpl(fileSystem);
      
    scheduler.scheduleDailyJob(indexer);

    return indexer;
  }
}

...