How to use Guice Assisted Inject for this factory pattern? - java

The following is a factory pattern that I wrote. But, in order to change it into a Provider, Guice documentation is not quite helping.
class ClientA extends AbstractClient {...}
class ClientB extends AbstractClient {...}
class ClientUtil {
private static AbstractClient client;
public static AbstractClient getClient(String key) {
ClientType clientType = ....
switch(clientType) {
case ".." : client = new ClientA.Builder()....build();
break;
case "..." :
default : client= new ClientB.Builder()....build();
}
return client;
}
}
class Application {
AbstractClient client = ClientUtil.getClient(key); // here, key is a string which is dynamic
}
Please provide some suggestions on how this can be written in a Provider format with Guice AssistedInject.

Did you try to write your Factory manually? The manual you linked has a nice example, and your code will translate easily to Guice.
public interface ClientFactory {
AbstractClient create(String key);
}
public class ClientFactoryImpl implements ClientFactory {
#Override
public AbstractClient create(String key) {
if ("A".equals(key)) {
return new ClientA();
} else {
return new ClientB();
}
}
}
and bind factory to implenetation
bind(ClientFactory.class).to(ClientFactoryImpl.class);
AssistedInject is detrimental in your case. All it offers is building ClientFactoryImpl automatically. The implementation only passes injected and assisted arguments to the constructor. But you have some non-trivial logic in the create method. In this case, I suggest you create factory implementation yourself.

First off, I would definitely agree with #Lesiak. The code client= new ClientB.Builder()....build(); isn't clear as the ellipsis could be any number of fields you're setting on ClientA/B.
But to give you an example of how to use AssistedInject for your particular instance:
class ClientA extends AbstractClient {
#Inject
public ClientA(ServiceOne serviceOne,
ServiceTwo serviceTwo,
#Assisted MyObject myObject) {
...
}
}
class ClientB extends AbstractClient {
// Same constructor as ClientA
}
Your factory would then look something like:
interface ClientFactory {
#Named("ClientA") public AbstractClient getClientA(...);
#Named("ClientB") public AbstractClient getClientB(...);
}
Your parameters can be different objects, or whatever you want, but they essentially have to matchup with the constructor #Assisted annotation. You can see now why #Lesiak provided the answer he did, if your builder is setting 10 fields on ClientA, then your factory method will need to have 10 method parameters, and is very unruly.
You'd then use this with:
#Inject ClientFactory clientFactory;
...
AbstractClient client = clientFactory.getClientA(something, something1, ...);
...

Assuming you want to create the Object directly and thus need to use #Assisted you can let Guice create named factories:
import javax.inject.Inject;
// com.google.inject:guice:4.2.2
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.name.Names;
// com.google.inject.extensions:guice-assistedinject:4.2.2
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryModuleBuilder;
public class StackOverflow {
public static void main(String[] args) {
final Injector injector = Guice.createInjector(new GuiceModule());
Key<ClientFactory> key = Key.get(ClientFactory.class, Names.named(args[0]));
System.out.println("Client: " + injector.getInstance(key).create("xxx"));
}
}
class GuiceModule extends AbstractModule {
#Override
protected void configure() {
install(new FactoryModuleBuilder().implement(AbstractClient.class, ClientA.class)
.build(Key.get(ClientFactory.class, Names.named("ClientA"))));
install(new FactoryModuleBuilder().implement(AbstractClient.class, ClientB.class)
.build(Key.get(ClientFactory.class, Names.named("ClientB"))));
}
}
abstract class AbstractClient {
}
class ClientA extends AbstractClient {
private String key;
#Inject
public ClientA(#Assisted String key) {
this.key = key;
}
#Override
public String toString() {
return "ClientA [key=" + key + "]";
}
}
class ClientB extends AbstractClient {
private String key;
private Injector injector; // just an example for additional injections
#Inject
public ClientB(#Assisted String key, Injector injector) {
this.key = key;
this.injector = injector;
}
#Override
public String toString() {
return "ClientB [key=" + key + "]";
}
}
interface ClientFactory {
AbstractClient create(String key);
}
The downside to this approach is that in order to use a dynamic input you need a reference to the Injector and Guice complains that this is very slow - that may not be an issue for you tough. If somebody knows how to replace the direct injector.getInstance call with something better please let me know!

Related

Resolve named dependency with Dagger 2 after the class is initialized

I am using Dagger 2 for the dependency management of my Java application.
I have the following structure:
public interface SecondaryService
{
void doSomethingElse(String data);
}
public class SecondaryServiceFirstImpl implements SecondaryService
{
public void doSomethingElse(String data)
{
// Do something else
}
}
public class SecondaryServiceSecondImpl implements SecondaryService
{
public void doSomethingElse(String data)
{
// Do something else
}
}
public interface MainInterface
{
void doSomething(String data);
}
public class MainService implements MainInterface
{
private SecondaryService secondaryService;
private DatabaseService databaseService;
public MainService(SecondaryService secondaryService, DatabaseService databaseService)
{
this.secondaryService = secondaryService;
this.databaseService = databaseService;
}
public void doSomething(String data)
{
String name = databaseService.getName(data);
// Resolve the NAMED SecondaryService based on the name property and
// use the implementation.
}
}
And here is the Dagger Module code:
#Module
public class DependencyRegisterModule
{
#Provides #Named('first')
SecondaryService provideSecondaryServiceFirstImpl ()
{
return new SecondaryServiceFirstImpl ();
}
#Provides #Named('second')
SecondaryService provideSecondaryServiceSecondImpl ()
{
return new SecondaryServiceSecondImpl ();
}
#Provides
DatabaseService provideDatabaseService ()
{
return new DatabaseServiceImpl();
}
#Provides
MainInterface provideMainInterface(SecondaryService secondaryService, DatabaseService databaseService)
{
return new MainService (secondaryService, );
}
}
As you can see I have a SecondaryService interface that is implemented by two classes. I want to resolve the named dependency for the SecondaryService based on a parameter, that I get from the database inside a method in the MainService.
Is there a way to do this? If this does not work with a Named dependencies, is there another way to do this?
So far I have used a factory pattern, but it is very hard to manage, as I have to pass the dependencies of the classes inside their constructor.
#Named—or #Qualifiers in general—are intended to be used if you need 2 objects of the same kind, e.g. injecting a String username and String email. In this case a qualifier could be used to distinguish between those objects.
In your case it seems that you want either one object or the other. In your case you should use different modules if you want to take a clean approach, or add an if/else to your module in the constructor.
Use a base class
#Module
public abstract class ServiceModule {
#Provides
public abstract SecondaryService providesServcie();
}
Then just create 2 sub classes of this module and provide the needed service respectively.
When creating your component just supply the right implementation of your module
if(1 == 1)
new MyComponent.Builder().add(new OneServiceModule()).build();
else
new MyComponent.Builder().add(new OtherServiceModule()).build();
And remember, you don't have to duplicate your other #Provides methods, since you can just use multiple modules with the component.
Use an if/else construct
#Module
public class DependencyRegisterModule
{
private int whichModule;
public DependencyRegisterModule(int whichModule) {
this.whichModule = whichModule;
}
#Provides
SecondaryService provideSecondaryServiceSecondImpl ()
{
return whichModule == 1 ? new SecondaryServiceSecondImpl() : new SecondaryServiceFirstImpl();
}
}
I hope I don't have to explain how if/else works ;)

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.

Constructor parameter injection using guice

This is a slightly different question from the other answered questions about constructor parameter (or at least that's what I think so, course I may be wrong). So am using a MapBinder to store a bunch of implementations and then pick one during runtime based of some criteria. Here is some code:
public interface MessageService {
void send();
}
public class FacebookMessageService implements MessageService {
private final String name;
#Inject
public FacebookMessageService(String name) {
this.name = name;
}
public void send() {
System.out.println("Sending message via facebook service to " + name);
}
}
public class MessageModule extends AbstractModule {
#Override
protected void configure() {
MapBinder<String, MessageService> mapBinder = MapBinder.newMapBinder<.....>
mapBinder.addBinding("facebook").to(FacebookMessageService.class);
}
}
public class MessageClient {
#Inject
Map<String, MessageService> map; //Mapbinder being injected
public void callSender() {
Injector injector = Guice.createInjector(new MessageModule());
injector.injectMembers(this);
MessageService service = map.get("facebook");
service.send();
}
}
I am unable to figure out how to get the FacebookMessageService with the name parameter? If I use AssistedInject with a Factory then I am not able to figure out how to inject the implementation into the MapBinder.
You can inject the 'name' parameter.
public class FacebookMessageService implements MessageService {
private final String name;
#Inject
public FacebookMessageService(#Named("facebookServiceName") String name) {
this.name = name;
}
}
public class MessageModule extends AbstractModule {
#Override
protected void configure() {
// bind the "facebookServiceName"
// I think this binding should exist before the map binding
bindConstant().annotatedWith(Names.named("facebookServiceName"))
.to("insert your argument here");
MapBinder<String, MessageService> mapBinder = MapBinder.newMapBinder<.....>
mapBinder.addBinding("facebook").to(FacebookMessageService.class);
}
}
Put a debug point in the FacebookMessageService constructor to see whether this works.

Inject bean into enum

I have the DataPrepareService that prepare data for reports and I have an Enum with report types, and I need to inject ReportService into Enum or have access to ReportService from enum.
my service:
#Service
public class DataPrepareService {
// my service
}
my enum:
public enum ReportType {
REPORT_1("name", "filename"),
REPORT_2("name", "filename"),
REPORT_3("name", "filename")
public abstract Map<String, Object> getSpecificParams();
public Map<String, Object> getCommonParams(){
// some code that requires service
}
}
I tried to use
#Autowired
DataPrepareService dataPrepareService;
, but it didn't work
How can I inject my service into enum?
public enum ReportType {
REPORT_1("name", "filename"),
REPORT_2("name", "filename");
#Component
public static class ReportTypeServiceInjector {
#Autowired
private DataPrepareService dataPrepareService;
#PostConstruct
public void postConstruct() {
for (ReportType rt : EnumSet.allOf(ReportType.class))
rt.setDataPrepareService(dataPrepareService);
}
}
[...]
}
weekens' answer works if you change inner class to static so spring can see it
Maybe something like this:
public enum ReportType {
#Component
public class ReportTypeServiceInjector {
#Autowired
private DataPrepareService dataPrepareService;
#PostConstruct
public void postConstruct() {
for (ReportType rt : EnumSet.allOf(ReportType.class))
rt.setDataPrepareService(dataPrepareService);
}
}
REPORT_1("name", "filename"),
REPORT_2("name", "filename"),
...
}
There is one another approach you may like to explore. However instead of injecting a bean into enum it associates a bean with an enum
Say you have an enum WidgetType and Widget class
public enum WidgetType {
FOO, BAR;
}
public class Widget {
WidgetType widgetType;
String message;
public Widget(WidgetType widgetType, String message) {
this.widgetType = widgetType;
this.message = message;
}
}
And you want to create Widgets of this type using a Factory BarFactory or FooFactory
public interface AbstractWidgetFactory {
Widget createWidget();
WidgetType factoryFor();
}
#Component
public class BarFactory implements AbstractWidgetFactory {
#Override
public Widget createWidget() {
return new Widget(BAR, "A Foo Widget");
}
#Override
public WidgetType factoryFor() {
return BAR;
}
}
#Component
public class FooFactory implements AbstractWidgetFactory {
#Override
public Widget createWidget() {
return new Widget(FOO, "A Foo Widget");
}
#Override
public WidgetType factoryFor() {
return FOO;
}
}
The WidgetService is where most of the work happens. Here I have a simple AutoWired field which keeps tracks of all the registered WidgetFactories. As a postConstruct operation we create a map of the enum and the associated factory.
Now clients could inject the WidgetService class and get the factory for the given enum type
#Service
public class WidgetService {
#Autowired
List<AbstractWidgetFactory> widgetFactories;
Map<WidgetType, AbstractWidgetFactory> factoryMap = new HashMap<>();
#PostConstruct
public void init() {
widgetFactories.forEach(w -> {
factoryMap.put(w.factoryFor(), w);
});
}
public Widget getWidgetOfType(WidgetType widgetType) {
return factoryMap.get(widgetType).createWidget();
}
}
Enums are static, so you have to figure out a way to access to the beans from a static context.
You can create a class named ApplicationContextProvider that implements the ApplicationContextAware interface.
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class ApplicationContextProvider implements ApplicationContextAware{
private static ApplicationContext appContext = null;
public static ApplicationContext getApplicationContext() {
return appContext;
}
public void setApplicationContext(ApplicationContext appContext) throws BeansException {
this.appContext = appContext;
}
}
then add this your application context file:
<bean id="applicationContextProvider" class="xxx.xxx.ApplicationContextProvider"></bean>
after that you could access to the application context in a static way like this:
ApplicationContext appContext = ApplicationContextProvider.getApplicationContext();
it will be hard to control that the spring container is already up and running at the time the enum is instantiated (if you had a variable with this type in a test-case, your container will usually not be there, even aspectj autowiring won't help there). i would recommend to just let the dataprepare-service or something give you the specific-params with a lookup-method with the enum-parameter.
I think this what you need
public enum MyEnum {
ONE,TWO,THREE;
}
Autowire the enum as per usual
#Configurable
public class MySpringConfiguredClass {
#Autowired
#Qualifier("mine")
private MyEnum myEnum;
}
Here is the trick, use the factory-method="valueOf" and also make sure
lazy-init="false"
so the container creates the bean upfront
<bean id="mine" class="foo.bar.MyEnum" factory-method="valueOf" lazy-init="false">
<constructor-arg value="ONE" />
</bean>
and you are done!
Just pass it to the method manually
public enum ReportType {
REPORT_1("name", "filename"),
REPORT_2("name", "filename"),
REPORT_3("name", "filename")
public abstract Map<String, Object> getSpecificParams();
public Map<String, Object> getCommonParams(DataPrepareService dataPrepareService){
// some code that requires service
}
}
As long as you call the method only from managed beans, you can inject it in these beans and pass the reference to the enum on each call.
Maybe you can use this solution ;
public enum ChartTypes {
AREA_CHART("Area Chart", XYAreaChart.class),
BAR_CHART("Bar Chart", XYBarChart.class),
private String name;
private String serviceName;
ChartTypes(String name, Class clazz) {
this.name = name;
this.serviceName = clazz.getSimpleName();
}
public String getServiceName() {
return serviceName;
}
#Override
public String toString() {
return name;
}
}
And in another class which you need the bean of the Enum :
ChartTypes plotType = ChartTypes.AreaChart
Object areaChartService = applicationContext.getBean(chartType.getServiceName());

Guice: Forcing a binding to be injected as a provider

I'm looking for a way to force certain Guice bindings to be injected as providers only. For example, when there is a configuration like
interface ResultLogger {
void log(String resultAsString);
}
class ResultLoggerProvider implements Provider<ResultLogger> {
// ...
}
class ResultDisplayModule extends AbstractModule {
#Override
protected void configure() {
bind(ResultLogger.class).toProvider(ResultLoggerProvider.class);
}
}
I would like to have way to configure my module so that a class like
#Singleton
class ResultParser {
private final Provider<ResultLogger> loggerProvider;
#Inject
public ResultParser(Provider<ResultLogger> loggerProvider) {
this.loggerProvider = loggerProvider;
}
}
can be injected just fine, but an implementation like
#Singleton
class ResultParser {
private final ResultLogger resultLogger;
#Inject
public ResultParser(ResultLogger resultLogger) {
this.resultLogger = resultLogger;
}
}
should throw a RuntimeException which notifies the developer that ResultLogger is only available via a provider. The exception would ideally be thrown as soon as possible, e.g. during construction of the injector. I'm looking for an easy way to achieve this using the existing API in Guice 3.0.
Maybe you should not implement Provider at all and just have a
#Singleton
public class ResultLoggerProvider {
public ResultLogger get() {...}
// ...
}
#Singleton
class ResultParser {
private final ResultLoggerProvider loggerProvider;
#Inject
public ResultParser(ResultLoggerProvider loggerProvider) {
this.loggerProvider = loggerProvider;
}
}
and remove the other bindings.
I think that it isn't right way. I guess you need smt like
interface ResultLogger {
void log(String resultAsString);
}
class ResultLoggerWrapper implements ResultLogger {
#Inject #Named("day") ResultLogger dayLogger;
#Inject #Named("night") ResultLogger nightLogger;
public void log(String resultAsString){
if(isDay()) {
dayLogger.log(resultAsString)
} else {
nightLogger.log(resultAsString)
}
}
}
bind(ResultLogger.class).to(ResultLoggerWrapper.class);
It should work to bind Provider instead of ResultLogger. That is in your module
bind(new TypeLiteral<Provider<ResultLogger>>(){}).to(ResultLoggerProvider.class);

Categories