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

Scrollbar

You inform Tapestry about your services and contributions by providing a module class.

...

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

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

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

Any public method (static or instance) whose name starts with "build" is a service builder method, implicitly defining a service within the module.

...

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
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:

...

The bind() method must be static; an exception is thrown if the bind() method exists but is an instance method.

Anchor
Cacheing_Services
Cacheing_Services
Caching Services

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
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;
  }
}

Notice that we've switched from static methods to instance methods. Since the builder methods are not static, the MyModule class will be instantiated so that the methods may be invoked. The constructor receives two common dependencies, which are stored into instance fields that may later be used inside service builder methods such as buildIndexer().

...

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.

...

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
languagejava
@Marker(Builtin.class)
public final class TapestryIOCModule
{
  . . .

This references a particular annotation class, Builtin:

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

}

The annotation can be applied to method and constructor parameters, for use within the IoC container. It can also be applied to fields, though this is specific to the Tapestry web framework.

...

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

Code Block
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;
  }
}

 

Scrollbar