Guice inject an object to a class constructor - java

I just started looking at Guice for a new project. I have something like this
the ConfigImpl class ans Config interface
interface Config{...}
class ConfigImpl implements Config {
private static final Map<> propMap;
public ConfigImpl(Map<> propMap) {
this.propMap = someProps;
}
}
Guice injection I came up with
public class MyInjector extends AbstractModule {
protected void configure() {
bind(Config.class).to(ConfigImpl.class)
}
}
and finally
public SomeClass {
Config someConfig;
Injector injector = Guice.createInjector(new MyInjector());
someConfig = injector.getInstance(Config.class);
}
Now I am very confused as I can't find a way to pass propMap into ConfigImpl class. I'd like to know the proper way of doing it in Guice. Thanks!

You should inject propMaps from your module:
public class MyInjector extends AbstractModule {
private final Map<String,String> mapProps;
public MyInjector(Map<String,String> mapProps) {
this.mapProps = mapProps;
}
protected void configure() {
bind(Config.class).to(ConfigImpl.class).in(Scope.SINGLETON); // You most than likely want this
bind(new TypeLiteral<Map<String,String>>() {}).toInstance(mapProps); // binding for the map.
}
}
And use it like this:
public class SomeClass {
void doSomething() {
Map<String,String> mapProps = ... ;
Injector injector = Guice.createInjector(new MyInjector(mapProps));
Config someConfig = injector.getInstance(Config.class);
}
}
Also, you should fix your ConfigImpl class:
class ConfigImpl implements Config {
private final Map<String,String> propMap;
#Inject // mandatory since you use a non-default constructor
public ConfigImpl(Map<String,String> propMap) { // add the generic type of the map
this.propMap = propMap;
}
}

Related

Guice volatile one of the injected property

Im trying to build some ConfigurationService which will supply configuration with some injections.I want to make one of the property to be volatile while running.
Id like to achive something like this but I don 't know how to do this in Guice.
Guice module:
public class ConfModule extends AbstractModule {
protected void configure() {
// how to bind class to itself, without any specific implementation
bind(Property1.class).to(Property1.class);
bind(IProperty2.class).to(Iproperty2DefaultImplementation1.class);
bind(IProperty2.class).annotatedWith(Names.named("setterOfproperty2").to( ? ? ? );
}
}
Conf class:
public class Conf {
#Inject // just consatant injected property1.class
private Property1 property1;
// variable property impl of interface
private IProperty2 property2;
// default impl of interface from module binding
#Inject
public Conf(IProperty2 property2) {
this.property2 = property2;
}
// method for changing implementation
#Inject
public setIProperty2(#Named("setterOfproperty2") IProperty2 property2) {
this.property2 = property2;
}
}
Conf service class:
public class ConfService {
#Inject
Conf configuration;
public Conf getConf() {
return configuration;
}
}
Usage:
public static main() {
// return Conf with default IProperty2 impl
Injector injector = Guice.createInjector(new ConfModule());
ConfService c = injector.getInstance(ConfService.class);
// change to other Iproperty2Implementation
c.getConf().setIProperty2(new Iproperty2Implementation2());
}
Would anyone want to help me ?

Set an attribute in Guice injected class

I'm using Google Guice, to bind a class with a mock class for some unit test.
This is my current code:
#Override
protected void configure() {
bind(ProductCaller.class).to(MockProductCaller.class);
}
In my test:
Injector injector = Guice.createInjector(new JUnitMockBootstrapBinder());
#Before
public void init() {
injector.getBinding(ProductCaller.class);
injector.getInstance(ProductCaller.class);
}
But in my mock class I have an attribute that I would like to set before the mock is instantiated.
public class MockProductCaller extends ProductCaller {
private String jsonValue; // <---This value
}
Any idea how to accomplish this?
You can inject maybe inject a #Named property :
public class MockProductCaller extends ProductCaller {
#Named("jsonValue") #Inject
private String jsonValue; // <---This value
}
and then :
Injector injector = Guice.createInjector(new JUnitMockBootstrapBinder(),
new AbstractModule() {
public void configure() {
bind(String.class).annotatedWith(Names.named("jsonValue"))
.toInstance("someValue");
}
});

Why can't Guice bind for an intermediate dependency?

Here's my code:
// Groovy
interface MyMapper {
Buzz toBuzz(Fizz fizz);
}
class MyMapperImpl implements MyMapper {
#Named("SIMPLE_FOOBAR")
Foobar foobar;
MyMapperImpl(Foobar foobar) {
super();
this.foobar = foobar;
}
#Override
Buzz toBuzz(Fizz fizz) {
// ...etc.
}
}
class Whistlefeather {
MyMapper mapper;
Whistlefeather(MyMapper mapper) {
super();
this.mapper = mapper;
}
void doSomething(Fink fink) {
Fizz fizz = getSomehow(fink);
Buzz buzz = mapper.toBuzz(fizz);
// Do something with 'buzz'...
}
}
class ApplicationMain {
Whistlefeather whistlefeather;
#Inject
ApplicationMain(Whistlefeather whistlefeather) {
super();
this.whistlefeather = whistlefeather;
}
static void main(String[] args) {
Injector injector = Guice.createInjector(new ApplicationModule());
ApplicationMain appMain = injector.getInstance(ApplicationMain);
appMain.run();
}
void run() {
whistlefeather.doSomething(new Fink());
}
}
Here's my Guice module:
class ApplicationModule extends AbstractModule {
#Override
protected void configure() {
// I have to name the Foobars because in reality there will be
// *many* of them, each configured slightly different.
bind(Foobar.class).annotatedWith(Names.named("SIMPLE_FOOBAR"))
.toInstance(new Foobar(true, true, false, 103, "yee haw"));
bind(MyMapper.class).to(MyMapperImpl);
}
}
Here's my exception:
Could not find a suitable constructor in com.me.myapp.MyMapperImpl.
Classes must have either one (and only one) constructor annotated
with #Inject or a zero-argument constructor that is not private.
My understanding was that I only need to annotate constructors with #Inject if I would be directly calling them through the Injector#getInstance(...) method. Since I do this with ApplicationMain, which contains a reference to Whistlefeather, which contains a reference to MyMapper, I didn't think I would have to annotate the MyMapperImpl constructor.
Any ideas as to where I'm going awry here?
In order for Guice to create any object, it has to know which constructor to use. This is true all the way down the Object Graph.
Consider the following code:
public interface Something { }
public class SomethingImpl implements Something {
private final String data;
public SomethingImpl(String data) {
this.data = data;
}
public SomethingImpl(Integer data) {
this.data = data.toString();
}
}
public class AnotherClass {
private final Something something;
#Inject
public AnotherClass(Something something) {
this.something = something;
}
}
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(Something.class).to(SomethingImpl.class);
bind(String.class).toInstance("Hello!");
bind(Integer.class).toInstance(50);
}
}
In this scenario, how is Guice supposed to know which constructor to use in SomethingImpl? If you were the author of Guice, how would you write it?
Obviously, you can't answer, because it's impossible. There has to be some sort of mechanism to tell Guice which constructor to use, regardless of whether or not it's called by Injector.getInstance() or not; that's why you have to annotate at least one constructor. Guice will use a no-argument constructor by default if one is specified, but if there isn't one, Guice doesn't know what to do.

how to implement a global state with guice?

I want to create a global state (data object, not a service object).
I have created class MyDataObject.
I want to avoid regular global state,
but prefer using Guice dependency injection.
However all the tutorials show how to set a DI for service object with registration to interface.
How can I use Guice injection for my need?
Edit
I have tried:
public class AppInjector extends AbstractModule {
#Override
protected void configure() {
bind(E2eResult.class).toInstance(new E2eResult());
}
}
with:
#Test
public void sendSearchRequest() throws Exception {
...
e2eResult = injector.getInstance(E2eResult.class);
timerUtils.setTimeOut(criticalBlockTimeOutMilli);
timerUtils.startStopWatch();
...
long timeElapsed = timerUtils.stopStopWatch();
e2eResult.runTime = timeElapsed;
...
}
and:
public static void main(String... args) throws ClassNotFoundException, IOException {
Injector injector = Guice.createInjector(new AppInjector());
Result result = runTest(classAndMethod);
E2eResult e2eResult = injector.getInstance(E2eResult.class);
}
and yet I saw the in the main was without the new long value.
To inject GlobalState class you should first a create an instance of it(set it as you like) and then bind class to instance:
bind(GlobalState.class)
.toInstance(globalState);
GlobalState can be created and configured in your "module", you can read about it more here:
https://github.com/google/guice/wiki/GettingStarted
So you have a plain old java object GlobalState:
public class GlobalState {
// whatever...
}
You can use the singleton mechanism provided by guice:
bind(GlobalState.class).in(Singleton.class);
Or use the instance binding:
bind(GlobalState.class).toInstance(new GlobalState());
In this way, you will be able to inject an unique instance of GlobalState in your application.
I eventually create an "old" bad singleton
No need for special binding
because i didn't have any pre-loaded object.
#Override
protected void configure() {
}
just to carry a one and only guice injector
public class InjectorSingleton {
public Injector guiceInjector;
private static InjectorSingleton singleton;
private InjectorSingleton() {
guiceInjector = Guice.createInjector(new AppInjector());
}
public static InjectorSingleton getInstance() {
if (singleton == null) {
singleton = new InjectorSingleton();
}
return singleton;
}
}
and I call this from my main class and from my test class
InjectorSingleton.getInstance().guiceInjector.getInstance(MyDataObject.class);
Fashionably late to the party. I just wanted to share this pattern.
package modules;
public class MetricsModule extends AbstractModule {
#Override
protected void configure() {
// The initializer is an eager singleton
bind(modules.MetricsModule.MeterRegistryInitializer.class).asEagerSingleton();
}
private static class MeterRegistryInitializer {
#Inject
// When initialized, Guice will handle the injection as usual
public MeterRegistryInitializer(Config config, MeterRegistry registry) {
var instance = config.getString("instance.id");
registry.config().commonTags(List.of(
Tag.of("instance", instance)
));
// This is global state
Metrics.addRegistry(registry);
}
}
#Provides
#Singleton
MeterRegistry provideMeterRegistry(
#MetricsDriver String driver,
PrometheusMeterRegistry prometheusRegistry
) {
MeterRegistry registry;
switch (driver) {
case "none":
registry = new CompositeMeterRegistry();
break;
case "prometheus":
registry = prometheusRegistry;
break;
default:
throw new UnsupportedOperationException();
}
return registry;
}
}

Simple usage of private modules and/or providers

New to Guice, so I'm looking into its expressive power. Suppose I have classes as follows:
public class Data {
#Inject
public Data(#Named("First") String first, #Named("Second") String second) { ... }
}
public class DataUser1 {
#Inject
public DataUser1(Data data) { ... }
}
public class DataUser2 {
#Inject
public DataUser2(Data data) { ... }
}
How do I create a module such that when I call injector.getInstance(DataUser1.class) I get something equivalent to new DataUser1(new Data("foo", "bar")) while having injector.getInstance(DataUser2.class) I get something equivalent to new DataUser2(new Data("foo2", "bar2"))?
Also related, how do I create a module for which I may need to get two instances of DataUser1, each of which using different Data values?
You use private modules for creating graphs of objects which are almost the same but differ in particular details.
public class DataUser1Module extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo");
bindConstant().annotatedWith(Names.named("Second")).to("bar");
bind(Data.class);
bind(DataUser1.class);
expose(DataUser1.class);
}
}
public class DataUser2Module extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo2");
bindConstant().annotatedWith(Names.named("Second")).to("bar2");
bind(Data.class);
bind(DataUser2.class);
expose(DataUser2.class);
}
}
Injector injector = Guice.createInjector(new DataUser1Module(), new DataUser2Module());
DataUser1 dataUser1 = injector.getInstance(DataUser1.class);
DataUser2 dataUser2 = injector.getInstance(DataUser2.class);
You do the same thing if you need two instances of DataUser1 with different Datas, but you use annotations to differentiate between them:
public class DataUser1Module1 extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo");
bindConstant().annotatedWith(Names.named("Second")).to("bar");
bind(Data.class);
bind(DataUser1.class).annotatedWith(Names.named("1")).to(DataUser1.class);
expose(DataUser1.class).annotatedWith(Names.named("1"));
}
}
public class DataUser1Module2 extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo2");
bindConstant().annotatedWith(Names.named("Second")).to("bar2");
bind(Data.class);
bind(DataUser1.class).annotatedWith(Names.named("2")).to(DataUser1.class);
expose(DataUser1.class).annotatedWith(Names.named("2"));
}
}
Injector injector = Guice.createInjector(new DataUser1Module1(), new DataUser1Module2());
DataUser1 dataUser11 = injector.getInstance(Key.get(DataUser1.class, Names.named("1"));
DataUser1 dataUser12 = injector.getInstance(Key.get(DataUser1.class, Names.named("2"));
This pattern is described in Guice FAQ.
See also these questions:
Binding a constructor argument based on the Annotation of the class
How do I bind Different Interfaces using Google Guice?

Categories