We try to refactoring a project with Guice. The idea is to bind all the Language interface to a concreate object like French or Polish.
We have a module for binding:
public class StandardModule extends AbstractModule {
@Override
protected void configure() {
bind(Language.class).to(Polish.class);
}
}
And a classe (AboutDialog.java) that use this injected object :
@Inject Language language;
public AboutDialog(JFrame parent) {
super(parent, "", true);
this.language=language;
this.setTitle(language.getLanguageInUse().getString("AboutDialog.title"));
this.parent = parent;
try {
jbInit();
} catch (Exception e) {
e.printStackTrace();
}
pack();
}
And we have as result:
java.lang.NullPointerException at net.sf.jmoney.gui.AboutDialog.<init>(AboutDialog.java:67)
Line 67 is:
this.setTitle(language.getLanguageInUse().getString("AboutDialog.title"));
Our interface is:
public interface Language {
public ResourceBundle getLanguageInUse();
}
And the Polish class is:
public class Polish implements Language {
private ResourceBundle languageInUse;
public Polish() {
languageInUse = ResourceBundle.getBundle(Constants.LANGUAGE_PL);
}
public ResourceBundle getLanguageInUse() {
return languageInUse;
}
}
We are lost...
Guice forbids null by default So if something tries to supply null for an object, Guice will refuse to inject it and throw a NULL_INJECTED_INTO_NON_NULLABLE ProvisionException error instead. If null is permissible by your class, you can annotate the field or parameter with @Nullable .
Note that the only Guice-specific code in the above is the @Inject annotation. This annotation marks an injection point. Guice will attempt to reconcile the dependencies implied by the annotated constructor, method, or field.
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.
Client Application 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.
You're using "field injection". This will make it hard to use your injected values in a constructor; even if Guice were to create the object (which is not happening now) or you were to use injector.injectMembers(aboutDialog)
, the constructor would run before the injector has a chance to inject the field you want.
It's a little more tricky to create a class that takes a varying parameter as well as an injected parameter. This leaves you with a few options:
Inject the JFrame. If you know what JFrame you're going to use when the constructor is being created, then just use bind(JFrame.class).toInstance(myJFrame);
in your Module. Then Guice can create the AboutDialog entirely.
Create a Factory manually. That way you can inject AboutDialog.Factory
and just call create
to get your AboutDialog
. It'll look something like this:
public class AboutDialog extends JDialog {
/** Injectable factory. */
public static class Factory {
@Inject private Language language;
public AboutDialog create(JFrame parent) {
return new AboutDialog(parent, language);
}
}
// no @Inject parameter; you're calling "new" yourself above!
public AboutDialog(JFrame parent, Language language) {
super(parent, "", true);
this.language = language;
// ... other initialization
}
}
Create a Factory and let Guice wire it up for you via assisted injection.
public class AboutDialog extends JDialog {
public interface Factory {
public AboutDialog create(JFrame parent);
}
// you need the @Inject, and also the @Assisted to tell Guice to
// use the parameter instead of Guice bindings
@Inject
public AboutDialog(@Assisted JFrame parent, Language language) {
super(parent, "", true);
this.language = language;
// ... other initialization
}
}
public class StandardModule extends AbstractModule {
@Override protected void configure() {
bind(Language.class).to(Polish.class);
// here every method in AboutDialog.Factory will be implemented
// to create the method's return type [AboutDialog] based on
// the parameters (like JFrame) and the bindings (like Language)
install(new FactoryModuleBuilder().build(AboutDialog.Factory.class));
}
}
As noted in the question comments, make sure you're getting your AboutDialog
(or AboutDialog.Factory
via an @Inject
ed constructor/field or from the Injector
itself, or else Guice will not know to inject the parameters.
I assume that your are not creating your AboutDialog
with the help of Guice.
What you could do is use injector.injectMembers(this)
where this
is the AboutDialog
.
The best way would be that the AboutDialog
will be created by Guice, so all members will be injected.
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