Skip to content

Instantly share code, notes, and snippets.

@diversit
Created December 8, 2017 13:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save diversit/04dc5da5f76d51a23f91eaab81806386 to your computer and use it in GitHub Desktop.
Save diversit/04dc5da5f76d51a23f91eaab81806386 to your computer and use it in GitHub Desktop.
Guice PostConstructModule
package flexc.graph.guice.module;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.Matchers;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.ProvisionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* Module to allow methods annotated with @{@link javax.annotation.PostConstruct} to be executed
* after Guice context has been setup.
*
* Guice by default has no support for @{@link javax.annotation.PostConstruct}.
* The <a href="http://code.mycila.com/guice/">MycilaGuice library</a> has several Guice extentions among which
* also support for JSR-250 (Object lifecycle, like @PostConstruct annotation).
* But since just @{@link javax.annotation.PostConstruct} is needed and do not want to include yet another dependency, this module was created.
*
* Cannot be implemented in Groovy since Groovy has a problem with generic inner types
* and therefore give a compiler error on the ```new InjectionListener<I> { .. }``` in the ```hear``` function.
*/
public final class PostConstructModule extends AbstractModule {
@Override
protected void configure() {
// listen to classes since interested in classes which has annotated methods
// binder().bindListener(Matchers.any(), new PostConstructTypeListener());
binder().bindListener(Matchers.any(), new PostConstructProvisionListener());
}
/**
* Utility class to execute methods annotated with @PostConstruct on an instance.
*/
private static class PostConstructProcessor {
/**
* Filter to get only the {@link Method}s annotated with {@link PostConstruct} annotation.
*/
private final Predicate<Method> filterPostConstructMethods = m -> {
PostConstruct annotation = m.getAnnotation(PostConstruct.class);
return annotation != null;
};
/**
* Invoke a method on an instance by consuming {@link Method}s.
*
* @param instance Instance of T on which to invoke the method.
* @param <T> Instance type.
* @return {@link Consumer} of {@link Method}s which invokes the methods on behalf of the #instance.
*/
private final <T> Consumer<Method> invokeMethodOnInstance(final T instance) { return method -> {
try {
method.invoke(instance);
} catch (final Exception e) {
throw new RuntimeException(String.format("@PostConstruct error: %s", e.getMessage()), e);
}
};}
/**
* Invoke all methods annotated with @PostConstruct on given instance.
* @param instance The instance on which to execute @PostConstruct methods
* @param <T> Type of instance.
*/
final <T> void invokePostConstructMethodsOn(final T instance) {
Arrays.asList(instance.getClass().getMethods())
.stream()
.filter(filterPostConstructMethods)
.forEach(invokeMethodOnInstance(instance));
}
}
/**
* {@link ProvisionListener} implementation which executes the @{@link PostConstruct} annotated
* methods on instances created via a @{@link com.google.inject.Provides} annotation.
*/
final static class PostConstructProvisionListener extends PostConstructProcessor implements ProvisionListener {
@Override
public <T> void onProvision(final ProvisionInvocation<T> provision) {
invokePostConstructMethodsOn(provision.provision());
}
}
/**
* {@link TypeListener} implementation which executes the @{@link PostConstruct} annotated
* methods on instances created by Guice (not via @Provides annotation).
*/
final static class PostConstructTypeListener extends PostConstructProcessor implements TypeListener {
@Override
public <I> void hear(final TypeLiteral<I> type, final TypeEncounter<I> encounter) {
// register listener on any injected type
encounter.register(new InjectionListener<I>() {
@Override
public void afterInjection(final I injectee) {
invokePostConstructMethodsOn(injectee);
}
});
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment