-
Notifications
You must be signed in to change notification settings - Fork 2
Lookup Method Injection
Lookup Method Injection is a very interesting implementation of Dependency Injection pattern. It allows to inject dependencies by overwriting the virtual methods of class. With this feature it is possible to configure and instantiate objects of abstract class type without writing explicit implementation.
Spring.FluentContext gives possibility to overwrite non-void parameterless methods:
public abstract class HouseFactory
{
public abstract Worker GetWorker();
public void BuildHouse()
{
Worker worker = GetWorker();
worker.Build(/* ... */);
}
}
// Configuration
ctx.RegisterDefault<Worker>()
.AsPrototype();
ctx.RegisterDefault<HouseFactory>()
.BindLookupMethod(f => f.GetWorker()).ToRegisteredDefault();
// Instantiation
HouseFactory factory = ctx.GetObject<HouseFactory>();
factory.BuildHouse();
The example presented above, shows usage of lookup method injection. The HouseFactory is an abstract class with abstract GetWorker() method, which is used in BuildHouse() method to instantiate worker that is used later to build house.
The configuration part of code presents usage of BindLookupMethod() method that binds GetWorker() method to a registered definition of Worker class, so every call of GetWorker() method will result with a return of new Worker instance as Worker object is defined as prototype.
The biggest advantage of this dependency injection method is that it is possible to configure object to use new instances of dependent object, every time when dependent object is requested. Constructor Injection and Setter Injection mechanisms does not allow to do that, because in their cases, all dependencies are resolved only once during object instantiation. Please note that it is also possible to configure object to use always the same instance of dependent object - it can be achieved by registering Worker class as singleton.
Other advantage is that it is not needed to define explicit implementation of abstract class to instantiate it.
It may happen that abstract method returns an interface rather than class instance. In such case, it is possible to bind method to object of derived type using ToRegisteredDefaultOf() method:
public abstract class HouseFactory
{
public abstract IWorker GetWorker();
public void BuildHouse()
{
IWorker worker = GetWorker();
worker.Build(/* ... */);
}
}
// Configuration
ctx.RegisterDefault<Worker>()
.AsPrototype();
ctx.RegisterDefault<HouseFactory>()
.BindLookupMethod(f => f.GetWorker()).ToRegisteredDefaultOf<Worker>();
// Instantiation
HouseFactory factory = ctx.GetObject<HouseFactory>();
factory.BuildHouse();
Analogically, it is possible to bind method to registered definition using its ID or reference.
There are also few limitations that are worth to be mentioned:
- If abstract class does not have parameterless constructor or requires additional configuration using constructor injection, it is not possible to use UseConstructor() method to select proper constructor in explicit way, however it is still possible to configure object using BindConstructorArg() methods directly.
- The abstract class has to be accessible (it has to have a public modifier - it also applies to all outer classes if it is defined as inner class), otherwise Spring will not be able to create it's implementation.
- If abstract method is not public, it will be not possible to select it using BindLookupMethod() method - in this case BindLookupMethodNamed() method can be used.
- Method Lookup Injection works only with objects instantiated by using constructor. It does not work with other instantiation mechanisms.
Here is an example of code using described workarounds:
public abstract class HouseFactory
{
private string _name;
public HouseFactory(string name)
{
_name = name;
}
protected abstract Worker GetWorker();
public void BuildHouse()
{
Console.WriteLine("Factory {0} is building a house.", _name);
Worker worker = GetWorker();
worker.Build(/* ... */);
}
}
// Configuration
ctx.RegisterDefault<Worker>()
.AsPrototype();
ctx.RegisterDefault<HouseFactory>()
.BindConstructorArg<string>().ToValue("Dream Houses Factory")
.BindLookupMethodNamed<Worker>("GetWorker").ToRegisteredDefaultOf<Worker>();
// Instantiation
HouseFactory factory = ctx.GetObject<HouseFactory>();
factory.BuildHouse();
Continue reading: 8. Object instantiation