1

My current situation:

I want to inject the following class into my application:

public interface IConfigAccessor<T extends IConfig> { ... } 

ConfigAccessors are a proxy-objects, created dynamically at runtime. The creation of these object works as follows:

public class ConfigFactory implements IConfigFactory { private final IConfigUpdater updater; @Inject public ConfigFactory(IConfigUpdater updater) { this.updater = updater; } @Override public <T extends IConfig> IConfigAccessor<T> register(final String configKey, final Class<T> configClass) { ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass); updater.register(configCache); return new ConfigAccessor<>(configCache, configKey, configClass); } } 

As you can see, to create these objects, I need to inject the ConfigUpdater and other depdencies. This means, that guice needs to be fully configured already.

To get the instance out of Guice, I use the following code:

IConfigFactory configClient = injector.getInstance(IConfigFactory.class); IConfigAccessor<ConcreteConfig> accessor = configClient.register("key", ConcreteConfig.class) 

How I want to inject them via Guice:

Currently, I can get the requried objects, but I have to manually pass them around in my application.

Instead, what I want to have is the following:

public class SomeClass { @Inject public SomeClass(@Config(configKey="key") IConfigAccessor<ConcreteConfig> accessor) { // hurray! } } 

What's the correct approach/technology to get this working?

After a lot of research, I'm feeling a bit lost on how to approach this topic. There are a lot of different things Guice offers, including simple Providers, custom Listeners which scan classes and identify custom annotations, FactoryModuleBuilders and more.

My problem is quite specific, and I'm not sure which of these things to use and how to get it working. I'm not even sure if this is even possible with Guice?


Edit: What I have so far

I have the following annotation which I want to use inside constructor paramters:

@Target({ ElementType.FIELD, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) public @interface InjectConfig { String configKey(); } 

Inside the module, I can bind a provider to IConfigAccessor (with the above annotation) as such:

bind(IConfigAccessor.class).annotatedWith(InjectConfig.class) .toProvider(new ConfigProvider<>()); 

However, there are two problems whith this:

  1. The provider cannot provide IConfigAccessor. To create such an instance, the provider would need an IConfigUpdater, but since I use 'new' for the provider, I can't inject it.
  2. Inside the provider, there is no way to find out about the configKey used in the Annotation.

Second approach:

Let's assume that I already know all configurations and configKeys I want to inject during startup. In this case, I could loop over all possible configKeys and have the following binding:

String configKey = "some key"; final Class<? extends IConfig> configClass =...; bind(IConfigAccessor.class).annotatedWith(Names.named(configKey)) .toProvider(new ConfigProvider<>(configKey, configClass)); 

However, problem (1) still resides: The provider cannot get an IConfigUpdater instance.

9
  • The duplicates have examples of all the features that will get you there. Commented Feb 11, 2018 at 16:08
  • @JarrodRoberson Thank you. If I understood the other questions (in particular stackoverflow.com/a/28575445/2224996) correctly, this is not possible with Guice. When I want to inject these objects inside a constructor, I need to implement a Provider. However, this is not possible because I wouldn't be able to get the 'configKey' from the annotation into the provider. When using Listeners, I effectively manage injection myself, but subsequently cannot use it within the constructor. Am I correct? Commented Feb 11, 2018 at 16:30
  • @JarrodRoberson However, I cannot use a custom annotation either, because I wouldn't be able to construct the object-to-inject (ConfigAccessor) in the first place. Within the TypeListener, I cannot inject other objects from Guice (IConfigUpdater ) Commented Feb 11, 2018 at 16:36
  • @JarrodRoberson So in the end, it all boils down to "I cannot create the object-to-inject ConfigAccessor inside modules, because I would need the injector for that. And if I create the objects at runtime (after I have the injector), I cannot inject them themself anymore. Commented Feb 11, 2018 at 16:42
  • You are hugely over complicating this. Provider is for injecting things that are not available inside the module, more specifically that require the injector to already be created. That covers every reasonable case I have ever come across since Guice first came out, 10 years ago! If it does not then you need to re-evaluate what you are doing. Commented Feb 11, 2018 at 16:57

1 Answer 1

1

The main problem here is that you cannot use the value of the annotation in the injection. There is another question which covers this part: Guice inject based on annotation value

Instead of binding a provider instance, you should bind the provider class, and get the class by injecting a typeliteral.

That way, your config factory can look like that:

public class ConfigFactory<T extends IConfig> implements IConfigFactory { @Inject private final IConfigUpdater updater; @Inject private TypeLiteral<T> type; @Override public IConfigAccessor<T> register(final String configKey) { Class<T> configClass = (Class<T>)type.getRawType(); ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass); updater.register(configCache); return new ConfigAccessor<>(configCache, configKey, configClass); } } 

And then SomeClass:

public class SomeClass { @Inject public SomeClass(ConfigFactory<ConcreteConfig> accessor) { ConcreteConfig config = accessor.register("key"); } } 

Since SomeClass needs to know "key" anyway, this is not too much a change information-wise. The downside is that the SomeClass API now gets a factory instead of the concrete config.

[EDIT]

And here is someone who actually did inject annotated values using custom injection.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.