Bindings¶
Both the IntelliJ IDEA plugin and the JPS plugin use Guice bindings for the dependency injection.
Injecting Dependencies¶
To inject dependencies in a class that is created by Guice, simply annotate
the constructor with @Inject
and add the dependencies as parameters.
public final class MyClass implements IMyInterface {
private final MyDependency dependency;
@Inject
public MyClass(final MyDependency dependency) {
this.dependency = dependency;
}
}
Register the binding in the applicable Guice module.
bind(IMyInterface.class).to(MyClass.class).in(Singleton.class);
In some cases the class is not created by Guice but by IntelliJ IDEA’s extension system or by the Java service loader. In that case you have to inject your dependencies like this:
public final class MyClass implements IMyInterface {
private MyDependency dependency;
/**
* This instance is created by IntelliJ's plugin system.
* Do not call this constructor manually.
*/
public MetaborgReferenceContributor() {
SpoofaxIdeaPlugin.injector().injectMembers(this);
}
@SuppressWarnings("unused")
@Inject
private void inject(final MyDependency dependency) {
this.dependency = dependency;
}
}
Note that SpoofaxIdeaPlugin
in this example can also be SpoofaxJpsPlugin
if you’re writing a JPS plugin class.
Since the class is not created by Guice, you don’t need to register a binding if you’re not depending on it. However, if you do depend on such a class, register the appropriate kind of binding that depends on who created the class and where it was registered.
For an IntelliJ component registered in META-INF/plugin.xml
under the
<application-components />
tag:
install(new IntelliJComponentProviderFactory().provide(IdeaApplicationComponent.class));
Note
Binding module-level or project-level components is not currently implemented.
For an IntelliJ service registered in META-INF/plugin.xml
under the
<applicationService />
extension:
install(new IntelliJServiceProviderFactory().provide(IMetaborgModuleConfig.class));
Note
Binding module-level or project-level services is not currently implemented.
For any other IntelliJ extension registered under the <extensions>
tag
in META-INF/plugin.xml
(e.g. the MetaborgFacetType
class registered for the
<facetType />
extension):
install(new IntelliJExtensionProviderFactory().provide(
MetaborgFacetType.class, "com.intellij.facetType"));
For a class registered using the Java service provider (mostly used in the JPS plugin):
install(new JavaServiceProviderFactory().provide(BuilderService.class));
Injecting Arguments¶
Sometimes you have some arguments that you want to use in the constructor,
but the constructor is also used for dependency injection. In that case,
annotate those arguments with @Assisted
and create a factory interface
for the class.
public final class MyClass {
private final String name;
private final MyDependency dependency;
@Inject
public MyClass(@Assisted final String name,
final MyDependency dependency) {
this.name = name;
this.dependency = dependency;
}
}
public interface MyClassFactory {
MyClass create(String name);
}
And register the factory instead of the binding:
install(new FactoryModuleBuilder()
.implement(MyClass.class, MyClass.class)
.build(MyClassFactory.class));
You can use it by depending on the factory instead of the class itself.
Injecting the Logger¶
In most classes you need the logger. Guice will automatically inject it if you add the following field:
@InjectLogger
private ILogger logger;
This also works with classes that are not created by Guice and have to use
injectMembers()
, as explained above. Injection is handled by the
MetaborgLoggerTypeListener
and Slf4JLoggerTypeListener
classes.
In unit tests where Guice is not available, you can inject a logger in a class
using the LoggerUtils#injectLogger()
function.
Singleton Classes¶
Mark any classes that must only be used as singletons with the @Singleton
attribute. This prevents issues where the registered binding is incomplete,
and makes the intent of the class clear to future maintainers.
@Singleton
public final class MyClass implements IMyInterface {
}