Ошибка графического интерфейса Java Guice: UnsatisfiedDependencyException: не было объекта, доступного для внедрения в SystemInjecteeImpl - PullRequest
0 голосов
/ 28 сентября 2018

У меня есть простой проект REST API с использованием Jersey 2.x.Я пытался использовать Google Guice для ввода своих зависимостей, но, похоже, это не работает.Я получаю эту ошибку:

org.glassfish.hk2.api.UnsatisfiedDependencyException: не было объекта, доступного для инъекции в SystemInjecteeImpl (requiredType = AccountService, parent = AccountsResource, qualifiers = {}, position = 0), необязательно = false, self = false, unqualified = null, 1658198405)

У меня есть этот простой класс ресурсов

@Path("/accounts")
@Produces(MediaType.APPLICATION_JSON)

public class AccountsResource {

    private final AccountService accountService;

    @Inject
    public AccountsResource(AccountService accountService) {
        this.accountService = accountService;
    }

  @GET
  @Path("test")
  public String test() {
    return this.accountService.test();
  }

Я хочу внедрить этот сервис в свой класс ресурсов

public class AccountService {

    public AccountService() {}

    public String test() {
        return "test";
    }
}

Итак, следуя руководству Guice, я создал этот модуль класса

import com.google.inject.*;

public class AccountsResourceModule extends AbstractModule  {

@Override
protected void configure() {
    bind(AccountService.class);
}
}

Наконец, я добавил инжектор в свой основной метод

public class TradingServer implements Runnable {
private static final int PORT = 8181;

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new AccountsResourceModule());
    AccountsResource accountsResource = injector.getInstance(AccountsResource.class);
    new TradingServer().run();
}

public void run() {
    Server server = new Server(PORT);
    ServletContextHandler contextHandler = new ServletContextHandler(server, "/");
    ResourceConfig packageConfig = new ResourceConfig().packages("ca.ulaval.glo4002.trading");
    ServletContainer container = new ServletContainer(packageConfig);
    ServletHolder servletHolder = new ServletHolder(container);

    contextHandler.addServlet(servletHolder, "/*");

    try {
        server.start();
        server.join();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        server.destroy();
    }
}

}

Когда я звоню на свой сервер, я получаю ошибку, упомянутую выше.Кажется, что внедрение зависимости не сработало.Пожалуйста, помогите

1 Ответ

0 голосов
/ 29 сентября 2018

Итак, Джерси ничего не знает о Гисе.Он уже использует свою собственную структуру DI, HK2.Есть пара вещей, которые вы можете сделать.Вы можете либо связать Guice с HK2, чтобы HK2 мог находить службы, которые связаны внутри Guice, либо можно просто связать свои классы ресурсов внутри Guice и зарегистрировать экземпляры этих ресурсов с помощью Jersey.

Tie Guiceс HK2

Чтобы связать Guice с HK2, вам нужно использовать Guice HK2 Bridge .Сначала вам нужно добавить следующую зависимость

<dependency>
    <groupId>org.glassfish.hk2</groupId>
    <artifactId>guice-bridge</artifactId>
    <version>${hk2.version}</version>
</dependency>

Чтобы hk2.version взглянул на ваши зависимости в Джерси (вы можете запустить mvn dependency:tree и посмотреть, какая версия HK2 Jersey используется).Вы хотите убедиться, что используете точно такую ​​же версию.

Следующее, что вам нужно сделать, это программно связать две системы.Один из способов сделать это - внутри Feature.

public class GuiceFeature implements Feature {

    @Override
    public boolean configure(FeatureContext context) {
        // This is the way in Jersey 2.26+ to get the ServiceLocator.
        // In earlier versions, use
        // ServiceLocatorProvider.getServiceLocator(context);
        ServiceLocator locator = InjectionManagerProvider.getInjectionManager(context)
                .getInstance(ServiceLocator.class);

        Injector injector = Guice.createInjector(new AccountResourceModule());
        GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
        GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
        guiceBridge.bridgeGuiceInjector(injector);
        return true;
    }
}

Тогда просто зарегистрируйте функцию на Джерси.

ResourceConfig packageConfig = new ResourceConfig()
        .packages("ca.ulaval.glo4002.trading")
        .register(GuiceFeature.class);

И все.Он должен работать, как я уже тестировал.

Связывать ресурсы с Guice

При указанной конфигурации Джерси будет создавать экземпляры ваших классов ресурсов (@Path аннотированныйклассы).Причина, по которой нам нужен мост, заключается в том, что Jersey тесно связан с HK2, поэтому, когда мы внедряем наши классы ресурсов, при создании экземпляра Jersey вызывает HK2, чтобы попытаться найти все зависимости для ресурса.

В этом случае мы не будем полагаться на Джерси для создания экземпляра ресурса.Мы свяжем ресурс с Guice и позволим Guice создать экземпляр, когда мы его запросим.Мы будем использовать этот экземпляр для регистрации на Джерси.

Сначала свяжите ресурс

public class AccountResourceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(AccountService.class);
        bind(AccountResource.class);
    }
}

Также убедитесь, что аннотация @Inject в классе ресурсов com.google.inject.Inject.

Получить экземпляр ресурса и зарегистрировать его

Injector injector = Guice.createInjector(new AccountResourceModule());
AccountResource accountResource = injector.getInstance(AccountResource.class);

ResourceConfig config = new ResourceConfig()
        .register(accountResource);

Возможно, вам придется найти более чистый способ сделать это, так как вы этого не хотитенужно сделать это для каждого ресурса, который у вас есть.Но в этом суть того, что вам нужно сделать.

Обновление

Итак, вот быстрая реализация, чтобы очистить второе решение.Мы можем рекурсивно сканировать пакет, чтобы получить все аннотированные классы @Path, а затем связать их в Guice и зарегистрировать их на Джерси.

С этой SO-записи , которую мы можем использоватьбиблиотека Reflections позволяет легко получить все классы.Просто добавьте следующую зависимость

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.11</version>
</dependency>

Затем создайте немного вспомогательных классов

import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Path;
import org.reflections.Reflections;

public class ResourceClassHelper {

    private static Set<Class<?>> resourceClasses;

    public static Set<Class<?>> getResourceClasses() {
        if (resourceClasses != null) {
            return resourceClasses;
        }

        // the package to scan for @Path classes "com.example"
        Reflections reflections = new Reflections("com.example");

        resourceClasses = reflections.getTypesAnnotatedWith(Path.class);
        resourceClasses = Collections.unmodifiableSet(resourceClasses);
        return resourceClasses;
    }
}

Затем в вашем модуле Guice

public class AccountResourceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(AccountService.class);

        ResourceClassHelper.getResourceClasses().forEach(this::bind);
    }
}

И ваш ресурс регистрации

Injector injector = Guice.createInjector(new AccountResourceModule());

ResourceConfig config = new ResourceConfig();
ResourceClassHelper.getResourceClasses()
            .forEach(cls -> config.register(injector.getInstance(cls)));
...