Нет объекта, доступного для ошибки внедрения при использовании bindFactory

У меня есть следующая реализация для приложения Джерси (2.18):

public class RootApplication extends ResourceConfig {

    public RootApplication() {
        packages("com.foo.bar");

        register(new AbstractBinder() {

            @Override
            protected void configure() {       
                bindFactory(RepositoryFactory.class).to(Repository.class);

                // if I use following line instead of bindFactory it works
                // bind(OracleRepository.class).to(Repository.class);
            }
        });
    }

    public class RepositoryFactory implements Factory<Repository> {

        private final Repository repo;

        public RepositoryFactory() {
            this.repo = new OracleRepository();
        }

        @Override
        public Repository provide() {
            return repo;
        }

        @Override
        public void dispose(Repository repo) {
        }
    }
}

и получите исключение ниже при попадании в службу, которая внедряет репозиторий

javax.servlet.ServletException: A MultiException has 3 exceptions.  They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=Repository,parent=MeasureService,qualifiers={},position=-1,optional=false,self=false,unqualified=null,56464420)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.fidelity.pi.dashboard.rest.MeasureService errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.fidelity.pi.dashboard.rest.MeasureService

Все это работает, если я закомментирую bindFactory и использую закомментированную привязку. Я что-то упустил в плане реализации Factory? Исключение, похоже, происходит еще до того, как будет сработан конструктор RepositoryFactory. Мне нужна фабрика, так как у меня есть другая инициализация для экземпляра OracleRepository.


person Jim O'Neil    schedule 12.06.2015    source источник


Ответы (1)


Единственный способ, которым я смог воспроизвести проблему (с вашей неполной информацией, то есть отсутствующей точкой впрыска), состоял в том, чтобы попытаться ввести OracleRepository вместо Repository. У меня нет точной причины, по которой инъекция не удалась, но я думаю, это потому, что вы привязываете Repository, а не OracleRepository. Если это проблема, самым простым решением будет привязать фабрику к OracleRepository или вместо этого просто ввести Repository.

Для внедрения Repository, если вы хотите квалифицировать различные реализации, вы можете сделать это, связав named или qaulifiedBy с привязкой, как в приведенном ниже примере (где я использовал named и аннотировал точку внедрения с помощью @Named).

В примере я использовал Jersey Test Framework.

<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-inmemory</artifactId>
    <version>${jersey2.version}</version>
    <scope>test</scope>
</dependency>

Вот полный тест. Вы можете изменить @Named между "Sql" и "Oracle", чтобы увидеть разницу.

import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Assert;
import org.junit.Test;

public class InjectionTest extends JerseyTest {

    @Path("test")
    public static class SimpleResource {

        @Inject
        @Named("Oracle")
        private Repository repo;

        @GET
        public String getRepoName() {
            return repo.getClass().getSimpleName();
        }
    }

    @Override
    public Application configure() {
        ResourceConfig config = new ResourceConfig();
        config.register(SimpleResource.class);
        config.register(new AbstractBinder(){
            @Override
            protected void configure() {
                bindFactory(SqlFactory.class)
                        .to(Repository.class).named("Sql");
                bindFactory(OracleFactory.class)
                        .to(Repository.class).named("Oracle");
            }
        });
        return config;       
    }


    public static interface Repository {}

    public static class OracleRepository implements Repository {}

    public static class SqlRepository implements Repository {}

    public static class SqlFactory implements Factory<Repository> {

        @Override
        public Repository provide() {
            return new SqlRepository();
        }

        @Override
        public void dispose(Repository t) {}
    }

    public static class OracleFactory implements Factory<Repository> {

        @Override
        public Repository provide() {
            return new OracleRepository();
        }

        @Override
        public void dispose(Repository t) {}
    }

    /**
     * Change the `Assert` from "OracleRepository" to "SqlRepository"
     * when switching the `@Named` on the injection point.
     */
    @Test
    public void testInjectOk() {
        Response response = target("test").request().get();
        String respString = response.readEntity(String.class);
        Assert.assertEquals("OracleRepository", respString);
        System.out.println(respString);
    }
}

Если у вас все еще есть проблема, опубликуйте полный тестовый пример для одного класса, как показано выше, который демонстрирует проблему.

person Paul Samsotha    schedule 12.06.2015
comment
Спасибо за рабочий образец... немного покопавшись, я обнаружил причину ошибки, хотя и не совсем понял, почему. Обратите внимание, что мой класс Factory не был статичным, и это, похоже, проблема. Аналогичным образом произойдет сбой с образцом, который у вас есть выше, если фабрика не статична. - person Jim O'Neil; 13.06.2015
comment
Ааа. Я совершенно не уловил этого из вашего кода. Я думал это два разных файла. Да, для внутренних классов они должны быть статическими. Я предполагаю, что класс не может быть создан фреймворком, если это не так. См. здесь некоторые интересные материалы по этому вопросу. - person Paul Samsotha; 13.06.2015