In order to bring the dependency injection alive,
the code classes should be marked properly with at least the @Inject
annotation.
The @Inject
annotation can be placed at three different statements:
- Constructor
- Field
- Setter method
When the factory is requested to get the instance, the implementation is
instantiated using the constructor marked with the @Inject
annotation.
public class ServiceImpl implements Service {
private final User mUser;
@Inject
public Service(final User user)
{
mUser = user;
}
}
All the eventual parameters of the constructor are automatically provided by the same factory.
Sometimes, it can be useful to ask factory for an instance with the constructor parameters already specified.
To achieve this, simply pass one or more arguments to the Factory#getInstance()
or Factory#get()
method.
Factory will then try to look for the matching constructor according to the specified
parameters (ignoring the @Inject
annotation at all). If no such constructor located
then the runtime exception will be thrown (clear programming error).
After instantiating the object, the factory scans the fields of the object and performs
the automatic injection of all of those marked with the @Inject
annotation.
public class ServiceImpl implements Service {
@Inject
private User mUser;
}
This brings a few benefits over the constructor:
- It is briefer (less code)
- The injection is done in second loop, so it allows the cyclic injection (not very good programming practice)
- This injection can be done also on foreign objects
After instantiating the object, the factory scans the methods of the object and performs
the automatic injection of all of those marked with the @Inject
annotation.
public class ServiceImpl implements Service {
private String mUserName;
private String mPassword;
@Inject
public void setup(final String userName, final String passWord passWord) {
mUserName = userName;
mPassword = passWord;
}
}
Note: The example above intentionally does not use simple setter (which would take one parameter only). This is to demonstrate that the injection works on multiple parameters too.
Factory supports also injection on objects created with traditional new
operator.
public class Main {
public static void main(String [] args) {
Factory.registerModule(new MyModule());
final Service service = new ServiceImpl();
Factory.injectInstance(service);
}
}
The method injection example above (setup()
) apparently contains a problem:
- The injection cannot distinguish which value to inject into which parameter
- As both of them are of the same type -
String
This can be solved by one of the ways demonstrated below.
The solution is particularly useful for injecting the configuration parameters (like URLs for instance).
The @Named
annotation can be used to be more specific with the injection when type is ambiguous.
public class ServiceImpl implements Service {
private String mUserName;
private String mPassword;
@Inject
public void setup(@Named('userName) final String userName, @Named('password') final String passWord passWord) {
mUserName = userName;
mPassword = passWord;
}
}
class MyModule extends Module
{
@Override
protected void defineBindings() {
whenRequestedInstanceOf(String.class).ifNamed('userName').thenReturn('John');
whenRequestedInstanceOf(String.class).ifNamed('password').thenReturn('1234');
}
}
Note: The named annotations can also be placed with the fields and providers.
The @Named
annotation is not exactly structured so the application specific
annotations can be used instead to achieve the same effect.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
public @interface UserName {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
public @interface Password {}
public class ServiceImpl implements Service {
private String mUserName;
private String mPassword;
@Inject
public void setup(@UserName final String userName, @Password final String passWord passWord) {
mUserName = userName;
mPassword = passWord;
}
}
class MyModule extends Module
{
@Override
protected void defineBindings() {
whenRequestedInstanceOf(String.class).ifAnnotatedWith(UserName.class).thenReturn('John');
whenRequestedInstanceOf(String.class).ifAnnotatedWith(Password.class).thenReturn('1234');
}
}
Note: The other annotations can also be placed with the fields and providers.