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.

The module class is a plain Java class that you create to inform Tapestry about your services and contributions.

A system of annotations and naming conventions allow Tapestry to determine what services are provided by the module.

...

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

...

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:

...

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.

...

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:

{scrollbar}
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;
  }
}
Wiki Markup

 

Scrollbar