JBoss.orgCommunity Documentation
The core Errai IOC module implements the JSR-330 Dependency Injection specification for in-client component wiring.
Dependency injection (DI) allows for cleaner and more modular code, by permitting the implementation of decoupled and type-safe components. By using DI, components do not need to be aware of the implementation of provided services. Instead, they merely declare a contract with the container, which in turn provides instances of the services that component depends on.
A simple example:
public class MyLittleClass {
private final TimeService timeService;
@Inject
public MyLittleClass(TimeService timeService) {
this.timeService = timeService;
}
public void printTime() {
System.out.println(this.timeService.getTime());
}
}
In this example, we create a simple class which declares a dependency using
@Inject
for the interface
TimeService
. In this particular case, we use constructor injection to establish the contract between the container and the component. We can similarly use field injection to the same effect:
public class MyLittleClass {
@Inject
private TimeService timeService;
public void printTime() {
System.out.println(this.timeService.getTime());
}
}
In order to inject
TimeService
, you must annotate it with
@ApplicationScoped
or the Errai DI container will not acknowledge the type as a bean.
@ApplicationScoped
public class TimeService {
}
Although field injection results in less code, a major disadvantage is that you cannot create immutable classes using the pattern, since the container must first call the default, no argument constructor, and then iterate through its injection tasks, which leaves the potential – albeit remote – that the object could be left in an partially or improperly initialized state. The advantage of constructor injection is that fields can be immutable (final), and invariance rules applied at construction time, leading to earlier failures, and the guarantee of consistent state.
In contrast to
Gin
, the Errai IOC container does not provide a programmatic way of creating and configuring injectors. Instead, container-level binding rules are defined by implementing a
Provider
, which is scanned for an auto-discovered by the container.
A
Provider
is essentially a factory which produces dependent types in the container, which defers instantiation responsibility for the provided type to the provider implementation. Top-level providers use the standard
javax.inject.Provider<T>
interface.
Types made available as top-level providers will be available for injection in any managed component within the container.
Out of the box, Errai IOC implements three default top-level providers:
org.jboss.errai.ioc.client.api.builtin.MessageBusProvider :
Makes an instance of
MessageBus
available for injection.
org.jboss.errai.ioc.client.api.builtin.RequestDispatchProvider :
Makes an instance of the
RequestDispatcher
available for injection.
org.jboss.errai.ioc.client.api.builtin.ConsumerProvider :
Makes event
Consumer<?>
objects available for injection.
Implementing a
Provider
is relatively straight-forward. Consider the following two classes:
TimeService.java
public interface TimeService {
public String getTime();
}
TimeServiceProvider.java
@IOCProvider
@Singleton
public class TimeServiceProvider implements Provider<TimeService> {
@Override
public TimeService get() {
return new TimeService() {
public String getTime() {
return "It's midnight somewhere!";
}
};
}
}
If you are familiar with Guice, this is semantically identical to configuring an injector like so:
Guice.createInjector(new AbstractModule() {
public void configure() {
bind(TimeService.class).toProvider(TimeServiceProvider.class);
}
}).getInstance(MyApp.class);
As shown in the above example code, the annotation
@IOCProvider
is used to denote top-level providers.
The classpath will be searched for all annotated providers at compile time.
Top-level providers are treated as regular beans. And as such may inject dependencies – particularly from other top-level providers – as necessary.
By default, Errai uses Google Guice to wire components. When deploying services on the server-side, it is currently possible to obtain references to the
MessageBus
,
RequestDispatcher
, the
ErraiServiceConfigurator
, and
ErraiService
by declaring them as injection dependencies in Service classes, extension components, and session providers.
Alternatively, supports CDI based wiring of server-side components. See the chapter on Errai CDI for more information.
Out of the box, the IOC container supports three bean scopes,
@Dependent
,
@Singleton
and
@EntryPoint
. The singleton and entry-point scopes are roughly the same semantics.
In Errai CDI, all client types are valid bean types if they are default constructable or can have construction dependencies satisfied. These unqualified beans belong the dependent pseudo-scope. See: Dependent Psuedo-Scope from CDI Documentation
Additionally, beans may be qualified as
@ApplicationScoped
,
@Singleton
or
@EntryPoint
. Although these three scopes are supported for completeness and conformance to the specification, within the client they effectively result in behavior that is identical.
Example 3.1. Example dependent scoped bean
public void MyDependentScopedBean {
private final Date createdDate;
public MyDependentScopedBean {
createdDate = new Date();
}
}
Example 3.2. Example ApplicationScoped bean
@ApplicationScoped
public void MyClientBean {
@Inject MyDependentScopedBean bean;
// ... //
}
As is mentioned in the
bean manager documentation
, only beans that are
explicitly
scoped will be made available to the bean manager for lookup. So while it is not necessary for regular injection, you must annotate your dependent scoped beans with
@Dependent
if you wish to dynamically lookup these beans at runtime.
As Errai IOC provides a container-based approach to client development, support for Errai services are exposed to the container so they may be injected and used throughout your application where appropriate. This section covers those services.
The
org.jboss.errai.bus.server.annotations.Service
annotation is used for binding service endpoints to the bus. Within the Errai IOC container you can annotate services and have them published to the bus on the client (or on the server) in a very straight-forward manner:
Example 3.3. A simple message receiving service
@Service
public class MyService implements MessageCallback {
public void callback(Message message) {
// ... //
}
}
Or like so ...
Example 3.4. Mapping a callback from a field of a bean
@Singleton
public class MyAppBean {
@Service("MyService")
private final MessageCallback myService = new MesageCallback() {
public void callback(Message message) {
// ... //
}
}
}
As with server-side use of the annotation, if a service name is not explicitly specified, the underlying class name or field name being annotated will be used as the service name.
The
org.jboss.errai.bus.server.api.Local
annotation is used in conjunction with the
@Service
annotation to advertise a service only for visibility on the local bus and thus, cannot receive messages across the wire for the service.
Example 3.5. A local only service
@Service @Local
public class MyLocalService implements MessageCallback {
public void callback(Message message) {
// ... //
}
}
Services which are registered with ErraiBus via the bean manager through use of the
@Service
annotation, have de-registration hooks tied implicitly to the destruction of the bean. Thus,
destruction of the bean
implies that these associated services are to be dereferenced.
The IOC container, by default, provides a set of default injectable bean types. They range from basic services, to injectable proxies for RPC. This section covers the facilities available out-of-the-box.
The type
org.jboss.errai.bus.client.framework.MessageBus
is globally injectable into any bean. Injecting this type will provide the instance of the active message bus running in the client.
The type
org.jboss.errai.bus.client.framework.RequestDispatcher
is globally injectable into any bean. Injecting this type will provide a
RequestDispatcher
instance capable of delivering any messages provided to it, to the the
MessageBus
.
The type
org.jboss.errai.ioc.client.api.Caller<?>
is a globally injectable RPC proxy. RPC proxies may be provided by various components. For example, JAX-RS or Errai RPC. The proxy itself is agnostic to the underlying RPC mechanism and is qualified by it's type parameterization.
For example:
Example 3.8. An example Caller<?> proxy
public void MyClientBean {
@Inject
private Caller<MyRpcInterface> rpcCaller;
// ... ///
@UiHandler("button")
public void onButtonClick(ClickHandler handler) {
rpcCaller.call(new RemoteCallback<Void>() {
public void callback(Void void) {
}
).callSomeMethod();
}
}
The above code shows the injection of a proxy for the RPC remote interface,
MyRpcInterface
. For more information on defining RPC proxies see
Chapter 5, Remote Procedure Calls (RPC)
and
Section 7.1, “Creating Requests”
in Errai JAX-RS.
A problem commonly associated with building large applications in the browser is ensuring that things happen in the proper order when code starts executing. Errai IOC provides you tools which permit you to ensure things happen before initialization, and forcing things to happen after initialization of all of the Errai services.
In order to prevent initialization of the the bus and it's services so that you can do necessary configuration, especially if you are writing extensions to the Errai framework itself, you can create an implicit startup dependency on your bean by injecting an
org.jboss.errai.ioc.client.api.InitBallot<?>
.
Example 3.9. Using an InitBallot to Control Startup
@Singleton
public class MyClientBean {
@Inject InitBallot<MyClientBean> ballot;
@PostConstruct
public void doStuff() {
// ... do some work ...
ballot.voteForInit();
}
}
Sending RPC calls to the server from inside constructors and
@PostConstruct
methods in Errai is not always reliable due to the fact that the bus and RPC proxies initialize asynchronously with the rest of the application. Therefore it is often desirable to have such things happen in a post-initialization task, which is exposed in the
ClientMessageBus
API. However, it is much cleaner to use the
@AfterInitialization
annotation on one of your bean methods.
Example 3.10. Using @AfterInitialization to do something after startup
@Singleton
public class MyClientBean {
@AfterInitialization
public void doStuffAfterInit() {
// ... do some work ...
}
}
It may be necessary at times to obtain instances of beans managed by Errai IOC from outside the container managed scope or creating a hard dependency from your bean. Errai IOC provides a simple client-side bean manager for handling these scenarios:
org.jboss.errai.ioc.client.container.IOCBeanManager
.
As you might expect, you can inject the bean manager into any of your managed beans.
Example 3.11. Injecting the client-side bean manager
public MyManagedBean {
@Inject IOCBeanManager manager;
// class body
}
If you need to access the bean manager outside a managed bean, such as in a unit test, you can access it by calling
org.jboss.errai.ioc.client.container.IOC.getBeanManager()
Looking up beans can be done through the use of the
lookupBean()
method in
IOCBeanManager
. Here's a basic example:
Example 3.12. Example lookup of a bean
public MyManagedBean {
@Inject IOCBeanManager manager;
public void lookupBean() {
IOCBean<SimpleBean> bean = manager.lookupBean(SimpleBean.class);
// check to see if the bean exists
if (bean != null) {
// get the instance of the bean
SimpleBean inst = bean.getInstance();
}
}
}
In this example we lookup a bean class named
SimpleBean
. This example will succeed assuming that
SimpleBean
is unambiguous. If the bean is ambiguous and requires qualification, you can do a qualified lookup like so:
Example 3.13. Looking up beans with qualifiers
MyQualifier qual = new MyQualifier() {
public annotationType() {
return MyQualifier.class;
}
}
MyOtherQualifier qual2 = new MyOtherQualifier() {
public annotationType() {
return MyOtherQualifier.class;
}
}
// pass qualifiers to IOCBeanManager.lookupBean
IOCBean<SimpleInterface> bean = beanManager.lookupBean(SimpleBean.class, qual, qual2);
In this example we manually construct instances of qualifier annotations in order to pass it to the bean manager for lookup. This is a necessary step since there's currently no support for annotation literals in Errai client code.
It may be desirable to have multiple matching dependencies for a given injection point with the ability to specify which implementation to use at runtime. For instance, you may have different versions of your application which target different browsers or capabilities of the browser. Using alternatives allows you to share common interfaces among your beans, while still using dependency injection, by exporting consideration of what implementation to use to the container's configuration.
Consider the following example:
@Singleton @Alternative
public class MobileView implements View {
// ... //
}
and
@Singleton @Alternative
public class DesktopView implements View {
// ... //
In our controller logic we in turn inject the
View
interface:
@EntryPoint
public class MyApp {
@Inject
View view;
// ... //
}
This code is unaware of the implementation of
View
, which maintains good separation of concerns. However, this of course creates an ambiguous dependency on the
View
interface as it has two matching subtypes in this case. Thus, we must configure the container to specify which alternative to use. Also note, that the beans in both cases have been annotated with
javax.enterprise.inject.Alternative
.
In your
ErraiApp.properties
for the module, you can simply specify which active alternative should be used:
errai.ioc.enabled.alternatives=org.foo.MobileView
You can specify multiple alternative classes by white space separating them:
errai.ioc.enabled.alternatives=org.foo.MobileView \ org.foo.HTML5Orientation \ org.foo.MobileStorage
You can only have one enabled alternative for matching set of alternatives, otherwise you will get ambiguous resolution errors from the container.
Similar to alternatives, but specifically designed for testing scenarios, you can replace beans with mocks at runtime for the purposes of running unit tests. This is accomplished simply by annotating a bean with the
org.jboss.errai.ioc.client.api.TestMock
annotation. Doing so will prioritize consideration of the bean over any other matching beans while running unit tests.
Consider the following:
@ApplicationScoped
public class UserManagementImpl implements UserManagement {
public List<User> listUsers() {
// do user listy things!
}
}
You can specify a mock implementation of this class by implementing its common parent type (
UserManagement
) and annotating that class with the
@TestMock
annotation inside your test package like so:
@TestMock @ApplicationScoped
public class MockUserManagementImpl implements UserManagement {
public List<User> listUsers() {
// return only a test user.
return Collections.singletonList(TestUser.INSTANCE);
}
}
In this case, the container will replace the
UserManagementImpl
with the
MockUserManagementImpl
automatically when running the unit tests.
The
@TestMock
annotation can also be used to specify alternative providers during test execution. For example, it can be used to mock a
Caller<T>
.
Callers
are used to invoke RPC or JAX-RS endpoints. During tests you might want to replace theses callers with mock implementations. For details on providers see
Section 3.1, “Container Wiring”
.
@TestMock @IOCProvider
public class MockedHappyServiceCallerProvider implements ContextualTypeProvider<Caller<HappyService>> {
@Override
public Caller<HappyService> provide(Class<?>[] typeargs, Annotation[] qualifiers) {
return new Caller<HappyService>() {
...
}
}
All beans managed by the Errai IOC container support the
@PostConstruct
and
@PreDestroy
annotations.
Beans which have methods annotated with
@PostConstruct
are guaranteed to have those methods called before the bean is put into service, and only after all dependencies within its graph has been satisfied.
Beans are also guaranteed to have their
@PreDestroy
annotated methods called before they are destroyed by the bean manager.
This cannot be guaranteed when the browser DOM is destroyed prematurely due to: closing the browser window; closing a tab; refreshing the page, etc.
Beans under management of Errai IOC, of any scope, can be explicitly destroyed through the client bean manager. Destruction of a managed bean is accomplished by passing a reference to the
destroyBean()
method of the bean manager.
Example 3.14. Destruction of bean
public MyManagedBean {
@Inject IOCBeanManager manager;
public void createABeanThenDestroyIt() {
// get a new bean.
SimpleBean bean = manager.lookupBean(SimpleBean.class).getInstance();
bean.sendMessage("Sorry, I need to dispose of you now");
// destroy the bean!
manager.destroyBean(bean);
}
}
When the bean manager "destroys" the bean, any pre-destroy methods the bean declares are called, it is taken out of service and no longer tracked by the bean manager. If there are references on the bean by other objects, the bean will continue to be accessible to those objects.
Container managed resources that are dependent on the bean such as bus service endpoints or CDI event observers will also be automatically destroyed when the bean is destroyed.
Another important consideration is the rule, "all beans created together are destroyed together." Consider the following example:
Example 3.15. SimpleBean.class
@Dependent
public class SimpleBean {
@Inject @New AnotherBean anotherBean;
public AnotherBean getAnotherBean() {
return anotherBean;
}
@PreDestroy
private void cleanUp() {
// do some cleanup tasks
}
}
Example 3.16. Destroying bean from subgraph
public MyManagedBean {
@Inject IOCBeanManager manager;
public void createABeanThenDestroyIt() {
// get a new bean.
SimpleBean bean = manager.lookupBean(SimpleBean.class).getInstance();
// destroy the AnotherBean reference from inside the bean
manager.destroyBean(bean.getAnotherBean());
}
}
In this example we pass the instance of
AnotherBean,
created as a dependency of
SimpleBean,
to the bean manager for destruction. Because this bean was created at the same time as its parent, its destruction will also result in the destruction of
SimpleBean
; thus, this action will result in the
@PreDestroy
cleanUp()
method of
SimpleBean
being invoked.
Another way which beans can be destroyed is through the use of the injectable
org.jboss.errai.ioc.client.api.Disposer<T>
class. The class provides a straight forward way of disposing of bean type.
For instance:
Example 3.17. Destroying bean with disposer
public MyManagedBean {
@Inject @New SimpleBean myNewSimpleBean;
@Inject Disposer<SimpleBean> simpleBeanDisposer;
public void destroyMyBean() {
simpleBeanDisposer.dispose(myNewSimpleBean);
}
}