Customizing Service Types
When a component is included in an injector, it is published with zero or more "services". A service consists
of a java type and an optional qualifier. By default, Sting publishes a single service with the java type set
to the type that is annotated by the @Injectable
annotation or the return type
of the provider method. Sting makes it possible to customize the service types published by using the
@Typed
annotation.
The @Typed
annotation can be applied to either the injectable type or the provider method.
Zero or more types can be specified and thus a single component can publish multiple services or no services.
It should be noted that if a component does not publish any services then it must be annotated with
@Eager
. If a component publishes zero services that it will never be a dependency of any
other component and if the component is not annotated with the @Eager
annotation, it would
would never be instantiated.
The easiest way to illustrate how this would work is to present some basic examples.
@Typed on @Injectable types
Consider a scenario where you have a single component that provides multiple services. The following
example demonstrates the MessageService
that publishes two types. The MessageSender
type is published
to enable some components in the application to send messages while the MessageBroker
type is published
so that other components can receive messages.
@Injectable
@Typed( { MessageBroker.class, MessageSender.class } )
public class MessageService
implements MessageSender, MessageBroker
{
...
}
It is easy to imagine that we would have another component LoginService
that performs the asynchronous
login action and sends events as it progresses through each step of the process. The LoginService
component
would depend upon the MessageSender
type. For example:
@Injectable
public class LoginService
{
private final MessageSender _sender;
...
LoginService( MessageSender sender )
{
_sender = sender;
...
}
public void requestLogin( String username, String secret )
{
_sender.sendMessage( new UserLoginStartedEvent( username ) );
_remoteLoginService.login( username, secret, new AsyncCallback<Integer>()
{
@Override
public void onFailure( final Throwable caught )
{
_sender.sendMessage( new UserLoginFailedEvent( username, caught ) );
}
@Override
public void onSuccess( final Integer userId )
{
_sender.sendMessage( new UserLoginCompletedEvent( username, userId ) );
}
} );
}
...
}
It also easy to imagine that there are multiple components within the application that will add listeners without ever needing to send messages. For example:
@Injectable
public class UserHeaderItem
{
UserHeaderItem( MessageBroker broker )
{
broker.addMessageListener( UserLoginStartedEvent.class,
e -> setText( "Authenticating " + e.getUsername() + " ..." ) );
broker.addMessageListener( UserLoginCompletedEvent.class,
e -> setText( "User: " + e.getUsername() ) );
broker.addMessageListener( UserLoginFailedEvent.class,
e -> setText( "Failed to authenticate " + e.getUsername() ) );
}
...
}
@Typed on provider methods
The @Typed
annotation can be applied to provider methods with the same impacts as when
it is applied to an injectable type. We could re-implement the above example but instead of using types
annotated by the @Injectable
annotation we could use use provider methods. Such an
example would look like:
@Fragment
public interface ApplicationFragment
{
@Typed( { MessageBroker.class, MessageSender.class } )
default MessageService provideMessageService()
{
return new MessageService();
}
default LoginService provideLoginService( MessageSender sender )
{
return new LoginService( sender );
}
default UserHeaderItem provideUserHeaderItem( MessageBroker broker )
{
return new UserHeaderItem( broker );
}
...
}
Combining @Typed and @Named
The @Typed
annotation can be combined with the @Named
annotation.
The qualifier specified by the @Named
annotation is applied to all the types published
by the component.
The example below publishes the component of type MessageService
with two service interfaces; a service
of type MessageBroker
with the qualifier system
and a service of type MessageSender
with the qualifier
system
.
@Fragment
public interface NamedApplicationFragment
{
@Named( "system" )
@Typed( { MessageBroker.class, MessageSender.class } )
default MessageService provideMessageService()
{
return new MessageService();
}
default LoginService provideLoginService( @Named( "system" ) MessageSender sender )
{
return new LoginService( sender );
}
default UserHeaderItem provideUserHeaderItem( @Named( "system" ) MessageBroker broker )
{
return new UserHeaderItem( broker );
}
...
}