Using Guice for dynamic objects

3,713 views
Skip to first unread message

dhoffer

unread,
Apr 11, 2011, 10:45:08 AM4/11/11
to google-guice
I'm taking a look at Guice to see how it would work for us, currently
we code the IoC manually (giant main that wires everything up) and use
factories to create dynamic objects so all methods are mock testable,
i.e. no 'new' in the code (just in the main and factories).

I get how Guice solves the IoC part with @Inject & modules, however
how does it help with the factories that generate dynamic objects?
I.e. a method that calculates values, creates an object with those
values, and does some work on and/or returns the object. How does
Guice stop us from needing to write the class factory that generates
the object? It looks like I could inject a typed Provider but that
doesn't let me create the object with the values just created
dynamically. (Also it would tie my code to the Guice APIs.)

So it seems Guice solves the static part of the app but not the
dynamic part or am I missing something?

Fred Faber

unread,
Apr 11, 2011, 11:00:45 AM4/11/11
to google...@googlegroups.com, dhoffer
This is similar to a question from earlier this week...

Guice will help you remove invocations of "new" on service objects. 

It won't be a replacement for your domain-level factory objects i.e., objects that do more work on an object than simply instantiate it.

Such a factory might look like:

class YourFactory {
  private final Provider<Foo> fooProvider;

  @Inject YourFactory(Provider<Foo> fooProvider) {
     ...
   }

   Foo createFoo(ArgType1 arg1, ArgType2 arg2, ...) {
       Foo foo = fooProvider.get();
       ... manipulate foo, probably using the args given above
   }
}

Note that AssistedInject can remove the boilerplate if all the factory is doing is to use the argument it gets as constructor arguments to the thing it is creating.  But it sounds like your requirements are more in-depth in that the Factory seems to be doing more work than simply instantiating an object.

Fred


--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To post to this group, send email to google...@googlegroups.com.
To unsubscribe from this group, send email to google-guice...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-guice?hl=en.


David Hoffer

unread,
Apr 11, 2011, 11:07:16 AM4/11/11
to ffa...@faiser.com, google...@googlegroups.com
Nope, my factories are as plain as they get, i.e.

public class MyClassFactory implements IMyClassFactory {

    public MyClass createMyClass(String param1, String param2, String param3, int param4, double param5) {
        return new MyClass(param1, param2, param3, param4, param5);
    }
}

The whole purpose of this factory is so I can inject IMyClassFactory into other classes that need to 'new' MyClass so that class is mock testable.

I'd love to use Guice so I don't need to write these factories but how do I pass param1, param2, param3, param4 & param5 into MyClass's constructor?

-Dave

Sam Berlin

unread,
Apr 11, 2011, 11:17:46 AM4/11/11
to google...@googlegroups.com, ffa...@faiser.com
Take a look at the AssistedInject extension @ http://code.google.com/p/google-guice/wiki/AssistedInject .

sam

dhoffer

unread,
Apr 12, 2011, 2:28:44 AM4/12/11
to google-guice
Yeah, that's what I was missing. This should do exactly what I need
but I can't get it to work yet. It compiles but at runtime it fails.
I find the documentation on this feature too sparse. Instead of
giving one full example they jump between two separate partial
examples.

In particular the docs don't make clear which module it is referring
to. In my case the class to be dynamically created has no non-
assisted parameters. I.e. the constructor of that class has 5
parameters and they all have the @Assisted annotation.

So then question 1 is...should this class have the @Inject annotation
or the @AssistededInject annotation? The docs aren't real clear on
this, I think the later is needed only if I have overloaded
constructors which I do not. Question 2 is...do I even need a module
for this class? Since it has no injected parameters it seems it has
nothing to do.

Now for the class(s) that need to inject the factory interface I
assume these are the modules that need this:
install(new FactoryModuleBuilder()
.implement(IMyClass.class, MyClass.class)
.build(IMyClassFactory.class));

And this would be positioned in the module to line up with the
IMyClassFactory injected parameter (which in my case is after the bind
methods).

However at runtime I get this error:

com.google.inject.CreationException: Guice creation errors:

1) A binding to com.app.MyParam annotated with
@com.google.inject.assistedinject.Assisted(value=) was already
configured at com.app.IMyClassFactory.create().
at com.app.IMyClassFactory.create(IMyClassFactory.java:1)
at
com.google.inject.assistedinject.FactoryProvider2.initialize(FactoryProvider2.java:
539)
at com.google.inject.assistedinject.FactoryModuleBuilder
$1.configure(FactoryModuleBuilder.java:335)

Where MyParam is one of the 5 parameters in the IMyClassFactory create
method (i.e. MyClass constructor).

Any idea what is wrong? Is there a full example of this someplace?

-Dave





On Apr 11, 6:17 pm, Sam Berlin <sber...@gmail.com> wrote:
> Take a look at the AssistedInject extension @http://code.google.com/p/google-guice/wiki/AssistedInject.
>
> sam
>

Fred Faber

unread,
Apr 12, 2011, 8:08:15 AM4/12/11
to google...@googlegroups.com, dhoffer
On Tue, Apr 12, 2011 at 2:28 AM, dhoffer <dhof...@gmail.com> wrote:
Yeah, that's what I was missing.  This should do exactly what I need
but I can't get it to work yet.  It compiles but at runtime it fails.
I find the documentation on this feature too sparse.  Instead of
giving one full example they jump between two separate partial
examples.

In particular the docs don't make clear which module it is referring
to.  In my case the class to be dynamically created has no non-
assisted parameters.  I.e. the constructor of that class has 5
parameters and they all have the @Assisted annotation.

 
So then question 1 is...should this class have the @Inject annotation
or the @AssistededInject annotation?

It should use @Inject....@AssistedInject was used in the initial version of the library, which uses reflection and not a child injector to create the factory-created objects. 

 
 The docs aren't real clear on
this, I think the later is needed only if I have overloaded
constructors which I do not.  Question 2 is...do I even need a module
for this class?  Since it has no injected parameters it seems it has
nothing to do.

You'll need to bind the Factory as you show below (I'm unclear what "class" you mean in this question).  You won't bind the class being created by the factory, if that's the question.
 

Now for the class(s) that need to inject the factory interface I
assume these are the modules that need this:
install(new FactoryModuleBuilder()
               .implement(IMyClass.class, MyClass.class)
               .build(IMyClassFactory.class));

And this would be positioned in the module to line up with the
IMyClassFactory injected parameter (which in my case is after the bind
methods).

Ok.
 

However at runtime I get this error:

com.google.inject.CreationException: Guice creation errors:

1) A binding to com.app.MyParam annotated with
@com.google.inject.assistedinject.Assisted(value=) was already
configured at com.app.IMyClassFactory.create().
 at com.app.IMyClassFactory.create(IMyClassFactory.java:1)
 at
com.google.inject.assistedinject.FactoryProvider2.initialize(FactoryProvider2.java:
539)
 at com.google.inject.assistedinject.FactoryModuleBuilder
$1.configure(FactoryModuleBuilder.java:335)

Where MyParam is one of the 5 parameters in the IMyClassFactory create
method (i.e. MyClass constructor).

I am guessing that more than one parameter has type "MyParam."

If this is the case, then you need to distinguish param of the same type by "naming" them, e.g.,

@Inject ClassCreatedByFactory(
   @Assisted("usedAsUsername") String username,
   @Assisted("usedAsPassword") String password)  { ... }


 

Any idea what is wrong?  Is there a full example of this someplace?

dhoffer

unread,
Apr 12, 2011, 8:53:51 AM4/12/11
to google-guice
I had got it working with I guess it's the 2.0 way of doing
things...with this style binding:

bind(IMyClassFactory.class).toProvider(
FactoryProvider.newFactory(IMyClassFactory.class,
MyClass.class));

but FactoryProvider is deprecated.

So now that I see your post saying that I need to used 'named'
annotations I changed it to use this approach.

install(new FactoryModuleBuilder()
.implement(IMyClass.class, MyClass.class)
.build(IMyClassFactory.class));

however with this approach I have to used named Assisted annotations
in MyClass and I have to add named Assisted annotations to
IMyClassFactory where none was needed with the 2.0 approach. (Seems
like order of parameters could be used.)

Both seem to work but the 2.0 approach as less intrusive to the code
(interfaces). ...just my two-cents.

Thanks much!
-Dave

On Apr 12, 3:08 pm, Fred Faber <ffa...@faiser.com> wrote:
> http://code.google.com/p/google-guice/source/browse/trunk/extensions/...

Sam Berlin

unread,
Apr 12, 2011, 10:08:04 AM4/12/11
to google...@googlegroups.com
Yup, AssistedInject in Guice 3.0 requires a little more coordination between the implementation & the factory, especially when more than one parameter is of the same type.  The benefits are large, though:

From the Guice 3.0 Wiki, these are the changes since 2.0:

 *  New FactoryModuleBuilder for building assisted factories
 *  Injection is done through child injectors and can participate in AOP
 *  Performance improvements
 *  Multiple different implementations can be created from a single factory
 *  Incorrect implementations or factories are detected in Stage.TOOL, and more error-checking with better error messages
 *  Work better with parameterized types
 *  Expose details through new extensions SPI

I don't think any of those are applicable if you continue to use FactoryProvider and leave the implementation annotated with @AssistedInject.  (However, if you change the implementation to @Inject, you'll see the new behavior regardless of whether or not you use FactoryProvider of FactoryModuleBuilder.  Also, if you use FactoryModuleBuilder, you'll always get the new behavior regardless of using @Inject or @AssistedInject).

Long story short... you're really better off using FactoryModuleBuilder.

sam

David Hoffer

unread,
Apr 12, 2011, 10:40:38 AM4/12/11
to google...@googlegroups.com, Sam Berlin
Thanks for the summary, that's very good.  I'll stay with FactoryModuleBuilder and see how it goes...

-Dave

Jan Torben Heuer

unread,
Apr 11, 2011, 2:33:53 PM4/11/11
to google...@googlegroups.com
Usually, I don't use the injection for junit tests, I (manually) set up all
fields with easymock mocks and then test the service class.

If you want to test the injection, refer to the other replys.

Jan

Reply all
Reply to author
Forward
0 new messages