any hints creating a patch for adding construction listeners for issues 62/78/203?

48 views
Skip to first unread message

James Strachan

unread,
Oct 2, 2008, 12:01:42 PM10/2/08
to google...@googlegroups.com
Before I start rambling - has anyone attempted to patch Guice to add
this feature yet?

Basically I thought I'd take a stab at trying to create a patch for
construction listeners. My initial idea was to add a method
postConstructionHook(InternalContext context, Object value) method to
InjectorImpl - then figure out later on how to register listeners to
the injector.

I've not really noodled the internals much so did a bit of
IDEA-walking through the code and started down the path of adding the
hook to all invocations of the InternalFactory; which certainly
catches all the points at which an object is constructed; though you
end up with duplicate events as many of these call sites are nested.
Being a total newbie at Guice implementation code - I had a quick look
in the debugger and didn't see any obvious solution to the right call
sites to include the event notification to catch all object
constructions without duplicates - so figured I'd shoot a mail to this
list to see if I was totally off base or if there was some other
cunning way to do it (maybe at the binding layer is cleaner?).

Any thoughts? If someone's working on it already I'll happily work on
something else :)

--
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com

limpb...@gmail.com

unread,
Oct 3, 2008, 4:15:10 AM10/3/08
to google-guice

I'm intending on refactoring a lot of
the implementation of the InternalFactory
stuff in order to better support proxyless
circular dependencies. So if you were to
prepare a patch, it might not work as
desired.

What I am interested in is what the API
will look like -- how do you setup the
construction listeners in your module?
And what does the interface for handling
them look like?

James Strachan

unread,
Oct 3, 2008, 7:48:31 AM10/3/08
to google...@googlegroups.com
2008/10/3 je...@swank.ca <limpb...@gmail.com>:

> I'm intending on refactoring a lot of
> the implementation of the InternalFactory
> stuff in order to better support proxyless
> circular dependencies. So if you were to
> prepare a patch, it might not work as
> desired.

OK no worries; I'll hold off a little while.


> What I am interested in is what the API
> will look like -- how do you setup the
> construction listeners in your module?
> And what does the interface for handling
> them look like?

BTW I wonder should we allow the post construction listener to return
a new instance (as it could return a proxy?) or should we restrict it
to being able to just perform some additional injection or calling of
lifecycle methods? The former might be more useful for optionally
applying interceptors and returning some kinda proxy.

How about something like this as a strawman API

/** invoked after Guice has constructed the object T */
public interface PostConstructionHandler<T> {
T postConstruct(T value);
}

(We could pass in some kinda context as a parameter to postConstruct()
to indicate where the value is being injected but am not sure how
useful that really is right now)


Then for example, to support Spring lifecycles we could write a class like this

public class SpringLifecyle implements
PostConstructionHandler<InitializingBean> {
public InitializingBean postConstruct(InitializingBean value) {
try {
value.afterPropertiesSet();
return value;
}
catch (Exception e) {
throw new PostConstructionRuntimeException(e);
}
}
}

Then to register the post construction handler we can use a similar
mechanism to the interceptor approach; so that we only cause the
overhead of the PostConstructHandler invocations when they are
actually used (on certain types or types with certain annotations and
so forth).

void bindPostConstructionHandler(Matcher<? super Class<?>> classMatcher,
PostConstructionHandler<?>... handlers);


Incidentally I did wonder about some kinda Module method a-la @Provides. e.g.

public class SpringModule extends AbstractModule {
...

@PostConstructHandler
public InitializingBean postConstruct(InitializingBean value) {
...
}
}

Though to be honest there's not gonna be that many implementations of
PostConstructionHandler<?> around the place so I don't think we need
to go too far in making it super easy to write them (only a few
frameworks will implement them typically). Whats most important I
think is avoiding their cost from most Guice users unless folks
actually use them - so we should encourage framework folks to use a
good Matcher to minimise PostConstructionHandler overhead.

After writing this mail I'm wondering if a more natural place to add
support for the PostConstructionHandler code is to mirror the
Interceptor code for this rather than using the InternalFactory code.

tzwoenn

unread,
Oct 3, 2008, 8:14:32 AM10/3/08
to google-guice
When I needed lifecycle support some months ago, I had implemented
this using a custom scope, which optionally wrappes the 'real' scope.

It looks like

Scope LifeCycleScope.listenFor(Scope enclosingScope,
LifeCycleListener... lifeCycleListeners);

public interface LifeCycleListener {
void postConstruct(Object instance);
void preDestroy(Object instance);
}

with postConstruct gets called right after enclosingScope.scope(key,
unscoped).get() and preDestroy when the object is not references any
more and before GC runs.

Based on this mechanism I have implemented an extension that uses
javax.annotation lifecycle annotations. As far as I see this would
fulfill your needs, would not it?

Regards, Sven


On Oct 3, 1:48 pm, "James Strachan" <james.strac...@gmail.com> wrote:
> 2008/10/3 je...@swank.ca <limpbiz...@gmail.com>:

Dhanji R. Prasanna

unread,
Oct 3, 2008, 8:15:42 AM10/3/08
to google...@googlegroups.com
Have you considered using the AopAlliance ConstructorInterceptors as the interface? This will match nicely with the MethodInterceptors.

Dhanji.

James Strachan

unread,
Oct 3, 2008, 9:50:46 AM10/3/08
to google...@googlegroups.com
2008/10/3 tzwoenn <sven.li...@googlemail.com>:

> When I needed lifecycle support some months ago, I had implemented
> this using a custom scope, which optionally wrappes the 'real' scope.
>
> It looks like
>
> Scope LifeCycleScope.listenFor(Scope enclosingScope,
> LifeCycleListener... lifeCycleListeners);
>
> public interface LifeCycleListener {
> void postConstruct(Object instance);
> void preDestroy(Object instance);
> }
>
> with postConstruct gets called right after enclosingScope.scope(key,
> unscoped).get() and preDestroy when the object is not references any
> more and before GC runs.
>
> Based on this mechanism I have implemented an extension that uses
> javax.annotation lifecycle annotations. As far as I see this would
> fulfill your needs, would not it?

But that would prevent users from using any of the standard Guice
scopes right? So no SINGLETON/REQUEST/SESSION scopes? Or do you have a
cunning way of wrapping then under the covers?

James Strachan

unread,
Oct 3, 2008, 9:55:22 AM10/3/08
to google...@googlegroups.com
2008/10/3 Dhanji R. Prasanna <dha...@gmail.com>:

> Have you considered using the AopAlliance ConstructorInterceptors as the
> interface? This will match nicely with the MethodInterceptors.

Ah great idea! I guess the only downside with that approach is it
introduces another object construction (the ConstructorInvocation) for
each object being constructed that has a ConstructorInterceptor; plus
from an implementation perspective it might sometimes be a bit tricky
grabbing the Constructor reference (but thats less of an issue). On
the plus side though it'd certainly make the API more consistent with
the aopalliance APIs

James Strachan

unread,
Oct 3, 2008, 11:56:33 AM10/3/08
to google...@googlegroups.com
2008/10/3 James Strachan <james.s...@gmail.com>:

> 2008/10/3 Dhanji R. Prasanna <dha...@gmail.com>:
>> Have you considered using the AopAlliance ConstructorInterceptors as the
>> interface? This will match nicely with the MethodInterceptors.
>
> Ah great idea!

It turned out - now you suggested the idea - it was super easy to implement! :)

I've a first spike of an implementation working; most of the code is
just boilerplate code; I just followed the way MethodInterceptors
worked and it all seemed super easy. Many thanks Dhanji!

The patch is attached here...
http://code.google.com/p/google-guice/issues/detail?id=78

I'll try tidy up the generics code in
ProxyFactory.wrapConstructorProxy() some more (my generics ninja was
letting me down) and add a few more unit tests, but so far its looking
good!

So you can now just do

bindConstructorInterceptor(matcher, interceptors);

just like with method interceptors and it seems to just work. e.g. see
the IntegrationTest.testConstructorInterceptor()

limpb...@gmail.com

unread,
Oct 4, 2008, 1:47:20 AM10/4/08
to google-guice
On Oct 3, 8:56 am, "James Strachan" <james.strac...@gmail.com> wrote:
> It turned out - now you suggested the idea - it was super easy to implement! :)

Fantastic. I'll review the code early next week!

Thanks!

James Strachan

unread,
Oct 4, 2008, 10:54:47 AM10/4/08
to google...@googlegroups.com
2008/10/4 je...@swank.ca <limpb...@gmail.com>:

>
> On Oct 3, 8:56 am, "James Strachan" <james.strac...@gmail.com> wrote:
>> It turned out - now you suggested the idea - it was super easy to implement! :)
>
> Fantastic. I'll review the code early next week!

Many thanks!

BTW I've just attached an updated patch which includes some
ConstructorInterceptor implementations - one for Spring's
InitializingBean along with a jsr250 module with support for
@PostConstruct with some tests. Both interceptors come with a handy
static bind(Binder) method to make them real easy to bind without
needing to grok the right Matchers etc

Dhanji R. Prasanna

unread,
Oct 4, 2008, 9:13:12 PM10/4/08
to google...@googlegroups.com
On Sat, Oct 4, 2008 at 2:56 AM, James Strachan <james.s...@gmail.com> wrote:
>
> 2008/10/3 James Strachan <james.s...@gmail.com>:
>> 2008/10/3 Dhanji R. Prasanna <dha...@gmail.com>:
>>> Have you considered using the AopAlliance ConstructorInterceptors as the
>>> interface? This will match nicely with the MethodInterceptors.
>>
>> Ah great idea!
>
> It turned out - now you suggested the idea - it was super easy to implement! :)
>
> I've a first spike of an implementation working; most of the code is
> just boilerplate code; I just followed the way MethodInterceptors
> worked and it all seemed super easy. Many thanks Dhanji!

Very cool! It looks quite simple.

Dhanji.

Reply all
Reply to author
Forward
0 new messages