Wiki Markup |
---|
{scrollbar} |
Overriding Tapestry IoC Services
Tapestry is designed to be easy to customize, and the IoC container is the key to that customizability.
...
The strategy used to determine what object gets injected is defined inside Tapestry IoC itself; thus we can take advantage of several features of the Tapestry IoC container in order to take control over specific injections.
Contributing a Service Override
In most cases, services are injected by matching just the type; there is no @InjectService annotation, just a method or constructor parameter whose type matches the service's interface.
In this case, it is very easy to supply your own alternate implementation of a service, by contributing a Service Override in your module class (usually AppModule.java), like this:
Code Block | ||||
---|---|---|---|---|
| ||||
@Contribute(ServiceOverride.class)
public static void setupApplicationServiceOverrides(MappedConfiguration<Class,Object> configuration)
{
configuration.addInstance(SomeServiceType.class, SomeServiceTypeOverrideImpl.class);
}
|
...
In this example, we are using addInstance()
which will instantiate the indicated class and handle dependency resolution . (Be careful with this, because in some cases, resolving dependencies of the override class can require checking against the ServiceOverrides service, and you'll get a runtime exception about ServiceOverrides requiring itself!). Footnote
Sometimes you'll want to define the override as a service of its own: this . This is useful if you want to inject a Logger specific to the service, or if the overriding implementation needs a service configuration:
Code Block | ||||
---|---|---|---|---|
| ||||
public static void bind(ServiceBinder binder)
{
binder.bind(SomeServiceType.class, SomeServiceTypeOverrideImpl.class).withId("SomeServiceTypeOverride");
}
@Contribute(ServiceOverride.class)
public static void setupApplicationServiceOverrides(MappedConfiguration<Class,Object> configuration, @Local SomeServiceType override)
{
configuration.add(SomeServiceType.class, override);
}
|
...
We can inject our overriding implementation of SomeServiceType using the special @Local annotation, which indicates that a service within the same module only should be injected (that is, services of the indicated type in other modules are ignored). Without @Local, there would be a problem because the override parameter would need to be resolved using the MasterObjectProvider and, ultimately, the ServiceOverride service; this would cause Tapestry to throw an exception indicating that ServiceOverride depends on itself. We defuse that situation by using @Local, which prevents the MasterObjectProvider service from being used to resolve the override parameter.
Decorating Services
Another option is to decorate the existing service. Perhaps you want to extend some of the behavior of the service but keep the rest.
...
Code Block | ||||
---|---|---|---|---|
| ||||
public SomeServiceType decorateSomeServiceType(final SomeServiceType delegate)
{
return new SomeServiceType() { . . . };
}
|
...
Note that the object passed in as delegate
may be the core service implementation, or it may be some other interceptor from some other decorator for the same service.
...
display
-footnotes
Wiki Markup |
---|
{scrollbar} |