user779860
user779860

Reputation: 775

Java: Use a Spring Autowired library with Guice program

I have a server that I've been developing. It uses Guice for it DI. There is a team library that I need to use. It uses @Autowired for its DI.

My code

import team.requiredlibrary.NeededClass

public class MyClass extends NeededClass {
  @Inject
  public MyClass() {

  }
}

Imported library

public class NeededClass {
  @Autowired
  private NestedClass1 nestedClass1;

  @Autowired
  private NestedClass2 nestedClass2;
}

When this runs, nestedClass1 and nestedClass2 are null.

What are my options here. Is there a way to get Guice to recognize @Autowired? If it was an option to update the team library replacing @Autowired with @Inject, would the work be that simple and be worth the effort? Assuming the worst case, would I be stuck replacing Guice with Spring in my project for DI?

Upvotes: 2

Views: 1195

Answers (1)

chris922
chris922

Reputation: 366

Spring supports @Inject, so if it is possible to replace @Autowired by @Inject in your team library everything should be fine.

Otherwise you can create a custom injector for guice, see this link: https://github.com/google/guice/wiki/CustomInjections

You can create a custom injector for @Autowired and use the method getProvider(Class) of type TypeEncounter of your TypeListener to get a provider that you can use to retrieve the required type.

Of course you have to configure Guice correctly to be able to inject those types.

The issue with this approach: You maybe will not cover everything that Spring supports, e. g. @Qualifier annotations.

Here is a working example:

public class AutowiredTypeListener implements TypeListener {
    public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
        Class<?> clazz = typeLiteral.getRawType();
        while (clazz != null) {
            for (Field field : clazz.getDeclaredFields()) {
                Autowired annotation = field.getAnnotation(Autowired.class);
                if (annotation != null) {
                    typeEncounter.register(new AutowiredMembersInjector<I>(field,
                            typeEncounter.getProvider(field.getType())));
                }
            }
            clazz = clazz.getSuperclass();
        }
    }
}

public class AutowiredMembersInjector<T> implements MembersInjector<T> {
    private final Field field;
    private final Provider<?> instanceProvider;

    public AutowiredMembersInjector(Field field, Provider<?> instanceProvider) {
        this.field = field;
        this.instanceProvider = instanceProvider;

        field.setAccessible(true);
    }

    public void injectMembers(T t) {
        try {
            field.set(t, instanceProvider.get());
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

And then just add bindListener(Matchers.any(), new AutowiredTypeListener()); inside your Guice module. What I directly noticed is that @Autowired supports optional injections, this seems to be impossible, because as soon as a provider was created using the typeEncounter it is required to have a binding of the class registered.

Upvotes: 3

Related Questions