underflow
underflow

Reputation: 103

How to do dependency injection for objects created with TypeListener in Guice?

Assuming such module configure method:

    @Override
    protected void configure() {
        bindListener(Matchers.any(), new TranslationKeyListener());
    }

    class TranslationKeyListener implements TypeListener {
        public <T> void hear(TypeLiteral<T> typeLiteral, TypeEncounter<T> typeEncounter) {
            Class<?> clazz = typeLiteral.getRawType();

            while (clazz != null) {
                for (Field field : clazz.getDeclaredFields()) {
                    Class<?> fieldType = field.getType();
                    if (field.isAnnotationPresent(TranslationKey.class) &&
                            (fieldType == Translation.class)) {
                        TranslationKey key = field.getAnnotation(TranslationKey.class);

                        String theKey = key.value();
                        typeEncounter.register(new TranslationInjector<T>(field, theKey));
                    }
                }
                clazz = clazz.getSuperclass();
            }
        }
    }

    class TranslationInjector<T> implements MembersInjector<T> {
        private final Field field;
        private final TranslationImpl translation;

        TranslationInjector(Field field, String key) {
            this.field = field;
            this.translation = new TranslationImpl(key, true);
            field.setAccessible(true);
        }

        @SneakyThrows
        public void injectMembers(T t) {
            field.set(t, translation);
        }
   }

and usage in another class like

@TranslationKey("someKey")
private Translation translation; // implemented by TranslationImpl

how can I make sure that dependcies - class fields - using @Inject inside TranslationImpl will be injected as well? in the same way that requestInjection(this) works within a module, but this is not supported when called outside configure()...

Upvotes: 1

Views: 182

Answers (1)

underflow
underflow

Reputation: 103

Looks like I found the trick - Guice is rejecting any other attempts to use MembersInjector if it's not used exactly this way.
The resulting errors of all other attempts has varied from "recursive load", "can't use before Injector is created" and others...
Attempting to call getMembersInjector before creating TranslationImpl also triggers "recursive load" error for a reason I don't yet understand, but moving it past that makes it work.

    class TranslationKeyListener implements TypeListener {
        public <T> void hear(TypeLiteral<T> typeLiteral, TypeEncounter<T> typeEncounter) {
            Class<?> clazz = typeLiteral.getRawType();

            while (clazz != null) {
                for (Field field : clazz.getDeclaredFields()) {
                    Class<?> fieldType = field.getType();
                    if (field.isAnnotationPresent(TranslationKey.class) &&
                            (fieldType == Translation.class)) {
                        TranslationKey key = field.getAnnotation(TranslationKey.class);

                        String theKey = key.value()
                        TranslationImpl impl = new TranslationImpl(theKey, true);
                        MembersInjector injector =
                                typeEncounter.getMembersInjector(TranslationImpl.class);

                        typeEncounter.register(new TranslationInjector(field, impl));
                        typeEncounter.register(new MembersInjector<T>() {
                            @Override
                            public void injectMembers(T instance) {
                                injector.injectMembers(impl);
                            }
                        });
                    }
                }
                clazz = clazz.getSuperclass();
            }
        }
    }

Upvotes: 1

Related Questions