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 ?
Related
I'm just getting started with Dagger & Dependency Injection and wondering about configuration at runtime for some of the lower-level dependencies. Is there a way to provide a low-level injected Singleton with a configuration object at runtime?
Basic idea of what I'm after:
#Singleton
class DatabaseService {
#Inject
public DatabaseService(DatabaseConnectionConfig config) { // how can this arg be passed in at runtime?
// make the connection
}
}
#Singleton
class HighLevelService {
#Inject
public HighLevelService(DatabaseService db) {
}
}
#Module
class Module {
#Binds
abstract HighLevelService bindHighLevelService(HighLevelService svc);
#Binds
abstract DatabaseService bindDatabaseService(DatabaseService svc);
}
#Singleton
#Component(modules = {
Module.class
})
interface Factory {
HighLevelService highLevelService();
static Factory create() {
return DaggerFactory.create();
}
}
public class App {
public static void main(String[] args) {
// get the config details from the arguments
DatabaseConnectionConfig config = parseDBConfigFromArgs(args);
// is there a way to configure the DatabaseConnectionConfig from here?
HighLevelService svc = Factory.create().highLevelService();
}
}
You can use a #Component.Factory (or #Component.Builder) with #BindsInstance.
#Singleton
#Component
interface Factory {
HighLevelService highLevelService();
// This nested interface is typically called "Factory", but I
// don't want to look up how to access Factory from Factory.Factory
#Component.Factory
interface MyFactory {
Factory create(#BindsInstance DatabaseConnectionConfig config);
}
static Factory create(DatabaseConnectionConfig config) {
return DaggerFactory.factory().create(config);
}
}
public class App {
public static void main(String[] args) {
DatabaseConnectionConfig config = parseDBConfigFromArgs(args);
HighLevelService svc = Factory.create(config).highLevelService();
}
}
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;
}
}
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");
}
});
I'm using a FileHandler class that gets a File as a ctor parameter.
I want to use it with 2 different Files.
How can configure the injector to inject a different File each time?
Like getInstance with annoation that will return the desired concrete instance?
Annotation wouldn't help here as If I put it in the ctor, there will still be one annotation for all the usages of this ctor.
public class StringFileWriter implements IResponsesStorage {
File file;
#Inject
#Singleton
public StringFileWriter(#myAnnotation File file) {
this.file = file;
}
and
public class MainModule extends AbstractModule {
#Override
protected void configure() {
File resultBaselineFile = new File(Constants.RESULTS_BASELINE_FILE);
bind(File.class).annotatedWith(ResultBaselineFile.class).toInstance(resultBaselineFile);
File logLatencyFile = new File(Constants.LATENCY_FILE);
bind(File.class).annotatedWith(LatencyLogFile.class).toInstance(logLatencyFile);
edit
the main code calls 2 repositories: one for log files, second for result files. Each repository has it's own file handler. That's the problem
This is a variation on the so-called "robot legs" problem. As the comment above asks, the right solution really depends on where the decision is made.
Here's how I would solve this problem in this case:
public final class StringFileWriter implements IResponsesStorage {
private final File file;
// Note: constructor is not annotated with #Inject
StringFileWriter(File file) {
this.file = file;
}
// ...
}
public final class StringFileWriterModule extends AbstractModule {
#Override protected void configure() {}
#Provides
#Singleton
#ResultBaselineFile
StringFileWriter provideResultBaselineFileWriter(
#ResultBaselineFile File resultBaselineFile) {
return new StringFileWriter(resultBaselineFile);
}
#Provides
#Singleton
#LatencyLogFile
StringFileWriter provideLatencyLogFileWriter(
#LatencyLogFile File latencyLogFile) {
return new StringFileWriter(latencyLogFile);
}
}
And then at the point of injection you would choose which StringFileWriter you wanted:
final class MyWorkerClass {
private final StringFileWriter resultBaselineFileWriter;
#Inject MyWorkerClass(#ResultBaselineFile StringFileWriter writer) {
resultBaselineFileWriter = writer;
}
// ...
}
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;
}
}