I have a standard CDI bean named NamedService;
#Named
public class NamedService {
#com.google.inject.Inject
private HazelcastService hazelcastService;
public void writeSomething() {
if (hazelcastService != null) {
System.out.println("injected properly.");
} else {
System.out.println("hazelcastService null");
}
}
}
which injects HazelcastService;
#Named
public class HazelcastService {
#Inject
#HazelcastMap(name = CacheConstants.CONFIGURATION_MAP)
protected IMap<String, String> configurationMap;
}
They are binded in the specified Module class:
public class NamedServiceModule extends AbstractModule {
#Override
public void configure() {
bind(NamedService.class);
bind(HazelcastService.class);
}
}
When I try to see whether hazelcastMap injected properly, It throws an exception;
public class TestGuice {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new NamedServiceModule());
NamedService namedService = injector.getInstance(NamedService.class);
namedService.writeSomething();
}
}
Exception in thread "main" com.google.inject.CreationException: Unable
to create injector, see the following errors:
1) No implementation for com.hazelcast.core.IMap was bound. while locating
com.hazelcast.core.IMap
for field at com.sqills.s3_ticket.service.util.HazelcastService.configurationMap(HazelcastService.java:11)
at
com.sqills.s3_ticket.service.util.NamedServiceModule.configure(NamedServiceModule.java:10)
When I remove the hazelcastMap injection then the injection is successful. How can I properly inject that hazelcast map by using Guice?
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;
}
}
Target.java:
package me;
#Component
public class Target implements Runnable {
#Autowired
private Properties properties;
public Target(){
properties.getProperty('my.property');
}
public void run() {
//do stuff
}
}
Config.java:
#Configuration
#ComponentScan(basePackages = {"me"})
public class Config {
#Bean(name="properties")
public PropertiesFactoryBean properties() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new FileSystemResource("src/my.properties"));
return bean;
}
public static void main(String[] argv) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Target t = context.getBean(Target.class);
t.run();
}
}
With the above code. Bottom of stack:
Caused by: java.lang.NullPointerException
at me.Target.<init>(Target.java:9)
....
You are doing field injection. You're referencing "to-be-autowired" instance variable inside constructor. In constructor, properties field is null because it has not been autowired yet. You can use constructor injection.
package me;
#Component
public class Target implements Runnable {
private final Properties properties;
#Autowired
public Target(Properties properties){
this.properties = properties;
properties.getProperty('my.property');
}
public void run() {
//do stuff
}
}
The constructor is called before properties is set. You are calling a method on properties in constructor before spring gets to set it. Use something like PostConstruct:
package me;
#Component
public class Target implements Runnable {
#Autowired
private Properties properties;
public Target(){}
#PostConstruct
public void init() {
properties.getProperty('my.property');
}
public void run() {
//do stuff
}
}
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 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;
}
}