Suppose I have some Message
class like the following. (This is a made-up class for simplicity.)
public class Message {
private String text;
public Message(String text) {
this.text = text;
}
public void send(Person recipient) {
// I think I should be Guice-injecting the sender.
MessageSender sender = new EmailBasedMessageSender();
sender.send(recipient, this.text);
}
}
Since I have different MessageSender
implementations, and might want to unit test this sending ability, I think I should be injecting the MessageSender
in Message
's send()
method. But how do I do this?
All the Guice examples I've seen and that I understand seem to do the injection in the constructor:
public class Message {
private String text;
private MessageSender sender;
// ??? I don't know what to do here, since the `text` argument shouldn't be injected.
@Inject
public Message(String text, MessageSender sender) {
this.text = text;
this.sender = sender;
}
public void send(Person recipient) {
this.sender.send(recipient, this.text);
}
}
public class MessageSenderModule extends AbstractModule {
@Override
protected void configure() {
bind(MessageSender.class).to(EmailBasedMessageSender.class);
}
}
But my Message
class takes in a text
argument in its constructor, which I don't want to inject. So what am I supposed to do instead?
(Note: I'm a complete Google Guice noob. I think I understand dependency injection, but I don't understand how to actually implement it with Guice.)
Injectable constructors are annotated with @Inject and accept zero or more dependencies as arguments. @Inject can apply to at most one constructor per class. @Inject is optional for public, no-argument constructors when no other constructors are present. This enables injectors to invoke default constructors.
Annotation Type Inject. @Target(value={METHOD,CONSTRUCTOR,FIELD}) @Retention(value=RUNTIME) @Documented public @interface Inject. Annotates members of your implementation class (constructors, methods and fields) into which the Injector should inject values.
The implementation is very easy to understand. We need to create Injector object using Guice class createInjector() method where we pass our injector class implementation object. Then we use injector to initialize our consumer class. If we run above class, it will produce following output.
Overview of bindings in Guice. A binding is an object that corresponds to an entry in the Guice map. You add new entries into the Guice map by creating bindings.
You could use assisted injection to provide the text through a factory, along with the message sender instantiated by Guice:
public class Message {
private String text;
private MessageSender sender;
@Inject
public Message(@Assisted String text, MessageSender sender) {
this.text = text;
this.sender = sender;
}
public void send(Person recipient) {
this.sender.send(recipient, this.text);
}
}
Factory:
public interface MessageFactory{
Message buildMessage(String text);
}
Module:
public class MessageSenderModule extends AbstractModule {
@Override
protected void configure() {
bind(MessageSender.class).to(EmailBasedMessageSender.class);
FactoryModuleBuilder factoryModuleBuilder = new FactoryModuleBuilder();
install(factoryModuleBuilder.build(MessageFactory.class));
}
}
usage:
@Inject MessageFactory messageFactory;
void test(Recipient recipient){
Message message = messageFactory.buildMessage("hey there");
message.send(recipient);
}
Assisted Injection Wiki
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With