Introduction
The IoC Module brings the Inversion of Control principle to the world of GWT. IoC and dependency injection has proven itself as a great pattern which helps reducing the clutter in server side code base. It does this by centralizing the wiring of the different components and modules of the application, leaving the code of the components themselves clean, simple, and testable. Applying this pattern within a GWT code base has similar results and this modules helps you to accomplish that.
Design Principles and Goals
Goals
- As unintrusive as possible
- Leverage existing frameworks were possible yet minimize dependencies on them
- Leverage Java 1.5 features
- Convention over configuration where possible
- Use pre/well-defined default behaviors
Design and Architecture
IoC and Dependency Injection
TODO
Components & Java Beans
The terminology of IoC and DI containers can sometime confuse. Many framework refer to injected object in different names. The most common name though is "Component". Other popular name include "Service" and "Bean". We'll use the "Component" term to refer to those object we'd like to register with the container.
Another term you need to be familar with is "Java Bean". When DI container just started to pop out, their developers realized that there needs to be some sort of a standard way of not only defining the dependencies but also injecting them. Since Java Beans were already a well known standard it was only natural to use standard Java Bean properties as means of injecting dependencies. A Java Bean property is defined by defining at least one method which is commonly known as the "setter" method. The goal of the method is to set the property value and it has a well defined naming convention where it has to start with "set" and follow with the capitalized property name. For example, if a Person class has a property named "firstName" then it needs to have at least a "void setFirstName(String)" method which accepts the value to be set. You can also define a "getter" method which will be used to retrieve the property value. In the Person example it would be String getFirstName()" (if the property is of a boolean type, the "is" prefix can be used instead of the "get" one, for example, "boolean isVisible()").
According to the Java Bean specifications, a Java Bean must have an empty constructor. Some IoC containers enable you to overlook this restriction and support parameterized constructor. At the current version of this module though we don't support it yet so for this framework the restriction still holds).
Constants
Sometimes you don't want to inject components but instead use some constants which you define somewhere in your application. For example, a fixed URL or configuration which should never change. Later on you will see how you can define constants in several ways and then refer to them by names for injection.
The Component
Here are a few characteristics of a component:
- A component is associated with a unique name id (a.k.a name) within the context of its container.
- A component can be of 2 scopes: a singleton - there will be only one instance in the container of this component, prototype - each time the component is requested a new one will be created based on a prototype compoenent.
- A component has a lifecycle which is controlled by the container (more on this later).
The ComponentContainer
The ComponentContainer is the main registry where all components in the application are held. The container can also be seen as a factory where you can retrieve components by their ids. Of course, if the requested bean is defined as a prototype then each request will instantiate a new one and initialize it with all configured dependencies and return it. On the other hand, if the component is configured as a singleton, each request will eventually return the same instance of the component. As mentioned above, beyond just holding the component, the container also manages their lifecycle. In addition, the container has a notion of an event multicaster which can be used to fire events to any registered listener. The container is represented by the "ComponentContainer" class which is shown below:
public interface ComponentContainer {
Object getComponent(String id);
boolean containsComponent(String id);
ApplicationEventMulticaster getApplicationEventMulticaster();
void close();
}Definting the Component Container
To define the component container you fist need to create an abstract class or an interface to represent it. This class must extends the ComponentContainer interface as this is the only way the GWT compiler will be able to pick it up. Once the class is defined, the compiler will use a custom Generator to look load this it, read its annotations and generate the appropriate code for the container. Here is an example of a component container class:
public abstract class TheComponentContainer implements ComponentContainer {
}The Application EntryPoint
Every GWT application must define an entry point which can be seen as the "main" class of the application. The same holds here of course, and in theory, the only thing that you need to do in order to load the component container is the following:
public TheEntryPoint implements EntryPoint {
public void onModuleLoad() {
ComponentContainer container = GWT.create(TheContainer.class);
// by now, the container is initialized along with all configured components. You can now use it to retreive components.
}
}That said, there is an easier and better way to do this. Instead of implementing EntryPoint directly, you should extends the AbstractComponentContainerEntryPoint class. Beyond just initializing the container, this entry point class also hooks up as a listener to the Window.close() event to properly close the application context. It also enables more advanced initialization options.
Configuring Components
@Component
Description
TODO
Attributes
| Name | Type | Optional/Required | Default Value | Description |
| name | String | optional | the class name with the first letter lower cased | defines the id/name of the component. By default the id is the name of the class where the first letter is lower cased (e.g. if the class name is "Person", the default id will be "person") |
| lazyInit | boolean | optional | false | indicates whether the component should be initialized lazily, that is, will only be constructed when it is accessed for the first time (only makes sense when the scope is a singleton) |
| scope | Scope | optional | Scope.SINGLETON | defines the scope of the component. Can have one of two values: Scope.SINGLETON or Scope.PROTOTYPE |
| initMethodName | String | optional | "" | the name of the method that should be called right after all the dependencies of the component were injected |
| disposeMethodName | String | optional | "" | the name of the method that should be called when the container shuts down |
Example
Simple greeter component
@Component
public class Greeter {
public void greet(String name) {
Window.alert("Hello " + name);
}
}A greeter component with a custom id
@Component("helloGreeter")
public class Greeter {
public void greet(String name) {
Window.alert("Hello " + name);
}
}@Inject
Description
There are two flavors of dependency injection this modules supports:
Injection by name - The component that needs to be injected is identified by its unique id/name in the container. Injection by type - The component that needs to be injecded is selected based on the type of the required dependency.
Experience shows that most of the time you can get away with injection by type. This is also the default behavior when the injection type is not explicitly specified. But sometimes there is advantages in using injection by id. On such typical scenario is when there are multiple instances of the same type in the container.
You can also inject List
<t>
and Set
<t>
with this annotation. Note that in such case, the injection type
must be by TYPE. In this scenario, the generic type of the collection will be picked up and all instances of that type will be injected as a collection. The order in which all the components are put in the collection is determined by their
@Order annotation if they have one.
Attributes
| Name | Type | Optional/Required | Default Value | Description |
| by | InjectionType | optional | InjectionType.TYPE | Indicates how should the container figure out which component should be injected |
| name | String | optional | The property name | The id/name of the component that should be injected (only applies when the injection type is InjectionType.NAME) |
| required | boolean | optional | true | Indicates whether this dependency is required or optional. For now, although this annotation available, the container doesn't actually support it, thus even when defined all dependencies are considered to be required |
Example
@Component
public class Alerter {
public void alert(String message) {
Window.alert(message);
}
}Setter Injection:
@Component
public class Greeter {
private Alerter alerter;
public void greet(String name) {
alerter.alert("Hello " + name);
}
@Inject
public void setAlerter(Alerter alerter) {
this.alerter = alerter;
}
}Constructor Injection:
@Component
public class Greeter {
private Alerter alerter;
@Inject
public Greeter(Alerter alerter) {
this.alerter = alerter;
}
public void greet(String name) {
alerter.alert("Hello " + name);
}
}As you can see above, there is support for both setter and constructor injection. in the case of setter injection, make sure the class has an empty default constructor (whether implicit or explicit). For the constructor injection, there must be only one constructor which is annotated with @Inject. That will be the constructor that the container will inject.
When injecting constructors, each constructor parameter can have its own @Inject annotation which will override the one defined on the constructor itself. This provides fine grain control on how the different dependencies should be injected. For example, since the default injection type is set to TYPE, it could be that one of the parameters of the constructor is required to be injected by name. In this case, adding the @Inject annotation before the parameter can do the trick.
@Initializer
Description
Can be put on any empty argument public method of the class and can be used instead of the initMethodName attribute of the @Component annotation. When put on the method, the container will call this method right after all the component dependecies where injected. This can be very useful to perform extra initialization logic which depends on the injected dependencies (specially when only using setter injection).
Attributes
No attributes
Example
@Component
public class Greeter {
private Alerter alerter;
public void greet(String name) {
alerter.alert("Hello " + name);
}
@Initializer
public void init() {
assert alerter != null; // will not be null as it's called after alerter was injected
}
@Inject
public void setAlerter(Alerter alerter) {
this.alerter = alerter;
}
}
@Disposer
Description
Can be put on any empty argument public method of the class and can be used instead of the desposeMethodName attribute of the @Component annotation. When put on the method, the container will call this method when it closes. This can be very useful to perform any cleanup logic.
Attributes
No attributes
Example
@ConfiguredProperty
Description
Sometimes you don't want to inject other components but instead just simple values. Of course you can hardcode these values in the class itself, but sometime it's easier to manage when these values are configured in an external properties file (it keeps the code cleaner and it's easier to reconfigure these values in one centeral location). You can put this annotation on a setter to indicate that the property of the class should be injected with a value that is taken from an exteranl resource bundle. This annotation accepts only one value which is the name of the property as defined in the resource bundle. Note that this will only work when the container actually knows about the configured property. One way to let the container know about resource bundles is by using the @ResourceBundle annotation on the container class (more on this below).
Attributes
| Name | Type | Optional/Required | Default Value | Description |
| name | String | required | - | The name of the property to inject |
Example
@Component
public class Greater {
private String greating;
private Alerter alerter;
public void great(String name) {
alerter.alert(greating + " " + name);
}
@Injectr
public void setAlerter(Alerter alerter) {
this.alerter = alerter;
}
@ConfiguredProperty("greating")
public void setGreating(String greating) {
this.greating = greating;
}
}@MetaProperty
Description
The @ConfiguredProperty provides you compile time property initialization. But what if you want a more dynamic way of configuring your properties? @MetaProperty to the rescue. The @MetaProperty will bind properties that are defined in
<meta>
tags in the host html file. This enables you to use a JSP file as the host html, and have it dynamically "pass" in parameter to the GWT application (without the need to modify the url query parameters). The "name" attribute of the
<meta>
tag will be resolved as the name of the property you want to bind, and the "content" attribute of the tag will serve as the property value.
Attributes
| Name | Type | Optional/Required | Default Value | Description |
| name | String | required | - | The name of the property to inject (relates to the "name" attribute of a <meta> tag) |
Example
@Component
public class Greeter {
private String greeting;
private Alerter alerter;
public void greet(String name) {
alerter.alert(greeting + " " + name);
}
@Inject
public void setAlerter(Alerter alerter) {
this.alerter = alerter;
}
@MetaProperty("greeting")
public void setGreeting(String greeting) {
this.greeting = greeting;
}
}@Generated
If you wish to use deferred binding for your components, the container needs to know that the instances of the component need to be constructed using GWT.create(Class) method. This is where the @Generated annotation becomes useful. When a component is annotated with @Generated, the container will construct it using GWT.create(...).
An example of a use case for this annotation can be GWT i18n support. You can create a Constants interface and annotated it with @Component and @Generated. This will construct the constants class using deferred binding and add the constructed object to the component container. This will enable you to define dependencies and inject Constants classes just like any other component in the container.
Example
@Component @Generated
public interface Labels extends Constants {
String name();
}
@Component
public class Greeter {
private Labels labels;
public String greet() {
return "Hello " + labels.name();
}
@Inject
public void setLabels(Labels labels) {
this.labels = labels;
}
}
GWT RemoteService Support
The container automatically picks up all remote services which are picked up during the package scanning. One a remote service is picked up, its async class is instantiated and is then added to the container as a component. This enables to easily inject remote services to components. You just need to treat it as a normal component in the container... nothing else.
Configurating the Container
In frameworks like Spring, one can extend the behavior of the container (ApplicationContext in spring) in many ways. One very powerful way is by using ApplicationContextPostProcessors. With the GWT IoC modules however this is not possible. The reason for that lies in the fact that the container code (which also does the wiring) is generated at compile time and not at runtime as happens with Spring. For this reason a different extension mechanism was required. The ComponentContainerProcessor is an interface which enables you to process (modify) the container configuration just before it is used to generate the actual code. It enables to create new component definitions, edit existing ones or even remove definitions. To define a processor, all that is needed is to add its associated annotation to the container class and then you're done. Plugging in your own custom written processors is also very easy and described in the following section. Here is the general interface of a processor:
public interface ComponentContainerProcessor<T extends Annotation> {
public static final int DEFAULT_ORDER = Integer.MIN_VALUE;
int getOrder();
void init(T t, JClassType containerType, GeneratorContext context) throws ProcessorInitializationException;
void process(MutableComponentContainerOracle oracle, GeneratorContext context) throws Exception;
}Note: this is pretty a low level interface, but the good thing is that for 99% of the time you don't really need to use it or even know about it as the already predefined processor will answer most if not all of your needs.
There are several processors defined by the framework which enable you to work with the container. We will list them below based on their associated annotaion which you can place on the container class.
@ComponentScan Processor
Description
The component scan annotation/processor is probably the most important one. It enables you to define one or more package where you put your components. The processor will then search these packages for all available components and configure them in the container. How does it know whether a class is a component or not? This is where the @Component annotation kicks in. When writing a class you can annotate it with the @Component annotation which will indicate to the processor that this class is a component. There are a few other annotations that you can also add to the component class which will indicate to the processor how this component needs to be configured:
Attributes
| Name | Type | Optional/Required | Default Value | Description |
| packages | java.lang.String | required | {} | A list of all the packages that should be scanned for components |
Example
// the entry point of the application.
public class TheEntryPoint extends AbstractComponentContainerEntryPoint {
protected ComponentContainer createComponentContainer() {
return GWT.create(TheComponentContainer.class);
}
}// the component container
@ComponentScan("org.gwtoolbox.ioc.sample")
public abstract class TheComponentContainer implements ComponentContainer {
}In the example above, when the application starts, the two component scan processor will scan the "org.gwtoolbox.ioc.sample" package and will pick up the two components. The first component is just a simple Person class with a predefined default name, the second component is a Greater class which greats a person. The preson of the greater class is configured as dependency using the @Inject annotation. After the container injects this dependency, the init() method will be called and a javscript alert will show with a greating message.
@ResourceBundle Processor
Description
As metioned above, you can define a resource bundle in which properties can be defined and used in the container. This annotation accepts a list of string representing the names of the property files that should serve as resource bundles. By default, these files are looked for in the same package as the application context (which is usually in the "client" package in the standard GWT directory structure). Here is how the Person component of the example above could be configured using a resource bundle.
Attributes
| Name | Type | Optional/Required | Default Value | Description |
| value | String | required | {} | A list of all properties files that should serve as resource bundles for the container |
Example
TODO
@RootPanelBindingScan Processor
This processor will look for a component in the container that is annotated with @RootPanelBinding annotation. When found, it will assume this component is a Widget and will bind it to the RoolLayoutPanel.
The @RootPanelBinding annotation which you put on the widget you want to bind to the root panel supports the following attributes:
Attributes
| Name | Type | Optional/Required | Default Value | Description |
| elementId | String | false | "" | The id of the html element where you want to bind the widget (this configuration is ignored if "useRootLayoutPanel" is set to "true" |
| useRootLayoutPanel | boolean | false | true | Indicate whether to bind the widget to RootLayoutPanel. When set to false, the widget will be bound to the RootPanel instead |
@Logging Processor
The @Logging annotation can be quite useful during development mode. What it does, is basically dump information over the runtime status of the component container. It supports several levels of output that can be configured using the "value" attribute:
The finer the log level the more information over the container is logged during the GWT compilation phase.
Intializable & Disposable
A component can implement the Initializable and the Disposable interfaces. This has the same goal as the methods that are annotated with @Initializer and @Disposer annotations - The first enables to perform extra initialization on the component after all its dependencies were set. The later enables to perform proper cleanup of component before the application exists.
ComponentContainerAware
If for some reason a component would need to interact directly with the component container, it can implement the ComponentContanerAware interface, in which case, the container will inject itself into the component as part of the initialization.
Event Driver Development
One of the most powerful features o the component container relates to its embedded even multicaster. The idea here is quite simple - in every application of whatever kind, it is always for the best if good separation of concerns and proper modularization is applied. Decoupling is one of the key aspect of such modular architecture. Unfortunately, when developing UI in GWT/Swing module, it is very easy to break encapsulation and module boundries. Not only is it easy, but without proper infrastructure, it is unavoidable. An ideal situation would be, that different parts of the application will be able to communicate in one way or another without even knowing about one another. This is usually where some sort of an event bus functionality kicks in, where all the components are aware of this bus and only this bus, and they use it to send messages/event to notify state changes. Every component that is interested in specific events, can then register and listen to the events and act on it (again... without needing to know where and why this even was fired).
Since the container is the backbone of the whole application and it know about all the components, it can serve as a good candidate to act as or hold an event bus. And indeed this is what the component container does. The good news, that not only it provides this functionality, but also provide a certain development model which makes it very easy to use events and multicast them.
ApplicationEvent
To start of, you first need to have a better understand of what is an event. In its most basic form, an event is a simple object... any object and it can hold any data. The container does however put one restriction which is that the event object must implement the ApplicationEvent interface. This interface has two main method. One to get a description of the event, and the other to get the source of the event (an object representing the source from which the even was fired).
The Multicaster
The container defines an interface called Multicaster which it exposes to components so they can use it to multicast their events. If a component needs the multicaster, all the it has to do is to define it as a configured property (just like any other dependency). Once the component is initialized, it can use the multicaster freely to publish its events.
When the container starts, it instantiate its own internal multicaster and uses that. It is however possible to define your own multicaster. Just create your own implementation of this interface and annotate it as a @Component. The container will then pick it up and replace its own with your custom multicaster. Why would you want to do that? One reason my be performance. Since each event is unit in terms of the data it carries, whenever an event needs to be fired, a new instance of the event is created, populated with the data, and then multicasted. In some browsers however, creating a lot of objects in javascript may become too expensive. So one solution would be to create a custom multicaster that caches event objects internally and reuses the same object over and over without the need to create new ones all the time.
@EventHanlder
Ok, so now we know how to create an event and how to fire it. But how do you listen to these events. It is actually very simple - each component can define a method which accepts an event. The even can be of any type as long as it implements the ApplicationEvent interface. The only thing that is left then, is to annotate this method with @EventHandler annotation. The container will pick up all these methods and automatically register them as listeners in the multicaster. When an event is fired, each listener filters all the event that are not assignable to the one defined in the method signature.
Java Configuration
TODO
I'm new to this project an considering using it but all the TODOs on this page are setting the alarm bells ringing. Why isn't the documentation complete?