Dependency injection and inversion of control
Magnolia uses dependency injection. This means that object dependencies are injected automatically at the time the object is instantiated. The benefit of injecting dependencies in this way is that you can change the implementation of the object which is depended on without having to change the way dependencies are retrieved. A further benefit is testability.
This type of dependency is different from module dependencies. It is a code/object level dependency that defines the relationship between objects, not a dependency between modules.
The term dependency injection (DI) is often used together with inversion of control (IoC). IoC is the principle and DI is the implementation. The concepts are related but at Magnolia we talk only about DI. To learn how IoC was implemented at Magnolia, see Concept IoC in Magnolia.
Defining object components
Components in Magnolia are defined in the module descriptor. Modules can define components of the same type. A definition in a module takes precedence over definitions in modules that it depends on. This allows modules to override and customize components in other modules.
For example, suppose you want to implement a MailSender
component. If
another Magnolia module already implements such a component, you can
override it by declaring a module dependency on the module that
provides the original MailSender
component and by making sure that
your component definition uses the same type.
The module descriptor can specify components for different
containers.
This is done using the id
element. You will rarely use a container
other than main
.
This example module descriptor defines a component of type GeoService
which is an interface. The actual implementation is GeoServiceImpl
.
<!DOCTYPE module SYSTEM "module.dtd">
<module>
<name>my-module</name>
<version>1.0</version>
<components>
<id>main</id> <!-- Container ID -->
<component>
<type>com.acme.geo.GeoService</type>
<implementation>com.acme.geo.GeoServiceImpl</implementation>
</component>
</components>
</module>
Injecting component dependencies
To retrieve a component, declare a dependency on it. Here is an example
declaring a dependency on a component that needs to be injected
(GeoService
). The example uses the constructor
injection
type. Field and setter injection are also supported.
public class SomeGeoAwareModel extends RenderingModelImpl {
private final GeoService geoService;
@Inject
public SomeGeoAwareModel(GeoService geoService) {
this.geoService = geoService;
}
public String execute() {
}
}
<component>
<type>com.acme.geo.GeoService</type>
<implementation>com.acme.geo.GeoServiceImpl</implementation>
</component>