...
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 | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
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);
}
} |
For more details, see see Defining Tapestry IOC Services. In most cases, autobuilding is the preferred approach.
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 | ||
---|---|---|
| ||
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;
}
} |
...
- org.slf4j.Logger: logger for the module (derived from the module's class name)
- ObjectLocator: access to other services
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 | ||
---|---|---|
| ||
@Marker(Builtin.class)
public final class TapestryIOCModule
{
. . . |
This references a particular annotation class, Builtin:
Code Block | ||
---|---|---|
| ||
@Target(
{ PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Builtin
{
} |
...
Using this style, the previous example of a module class may be rewritten:
Code Block | ||
---|---|---|
| ||
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 |
---|