Внедрение зависимостей в Jersey 2.x

Я определил AbstractBinder и зарегистрировал его в своем приложении JAX-RS. Связующее указывает, как инъекции зависимостей должны создавать мои классы.

public class AppBinder extends AbstractBinder {
@Override
protected void configure() {
     bind(TranslationImportServiceImpl.class).to(TranslationImportService.class);
    bindAsContract(ImportCandidateBuilder.class);
    bind(DomainsBusinessServices.class)
            .to(IDomainsBusinessServices.class).in(Singleton.class);
    bind(CodeBusinessService.class)
            .to(ICodeBusinessService.class).in(Singleton.class);
    bind(LangBusinessService.class)
            .to(ILangBusinessService.class).in(Singleton.class);
    bind(TranslationBusinessService.class)
            .to(ITranslationBusinessService.class).in(Singleton.class);
    bind(SessionImpl.class)
            .to(Session.class).in(Singleton.class);
    bind(SessionFactoryImpl.class)
            .to(SessionFactory.class).in(Singleton.class);
    bind(CriteriaImpl.class)
            .to(Criteria.class).in(Singleton.class);
    bindAsContract(DefaultBusinessService.class);
}

Я реализовал класс Application (указанный в параметре init файла web.xml).

public class Application extends ResourceConfig {
public Application() {
    register(new AppBinder());
    packages(true, "web.rest");
}

web.xml

<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>web.rest</param-value>
    </init-param>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>web.rest.Application</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Jersey Web Application</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

Затем мой RestService, где инъекция работает нормально

@Path("/call")
public class RestService {

@Inject
private TranslationImportService translationImportService;

@POST
@Path("/post")
@Consumes({MediaType.MULTIPART_FORM_DATA})
   public Response upload(@FormDataParam("file") InputStream fileInputStream,
                          @FormDataParam("file") FormDataContentDisposition fileMetaData) throws Exception {

        Map<String, TranslationImportResult> importResultMap = new HashMap<String, TranslationImportResult>();

        try {
            Map<String, InputStream> extractedFiles = extractFiles(fileMetaData.getFileName(), fileInputStream);

            Set<Entry<String, InputStream>> entrySet = extractedFiles.entrySet();
            TranslationImportResult importResult;
            for (Entry<String, InputStream> entry : entrySet) {
                importResult = null;

                if (entry.getKey().endsWith(".xml") || entry.getKey().endsWith(".xlf")) {

                    importResult = translationImportService.importTranslations(entry.getKey(), entry.getValue(), 1320L, "admin", true);
                } else {
                    logger.warn("Cannot process file (wrong extension): ", entry.getKey());
                    importResult = new TranslationImportResult(TranslationImportStatus.FAILURE, -1, -1);
                }
                importResultMap.put(entry.getKey(), importResult);
            }
        } catch (Exception e) {
            logger.error("A problem with the file extention occured! \n", e);
        }
        return Response.ok("File uploaded successfully").build();
    }

Я аннотировал интерфейс TranslationImportService как @Contract

@Contract
public interface TranslationImportService

и реализация как @Service

@Service
public class TranslationImportServiceImpl implements TranslationImportService{

@Inject
private ImportCandidateBuilder importCandidateBuilder;

@Override
public TranslationImportResult importTranslations(String inputName, InputStream inputStream, Long domainId,
                                                  String username, boolean allOrNothing) throws Exception {
    TranslationImportStatus ret;
    InputDeserializer inputDeserializer = InputDeserializerFactory.newInputDeserializer(inputName);
    List<InputTranslationBean> translationBeans = inputDeserializer.deserialize(inputStream);
    List<ImportCandidate> candidates = importCandidateBuilder.buildCandidates(domainId, translationBeans);
    insertStartImportEvent(candidates, inputName);
    translationImportOrchestrator.performTranslationsImport(candidates, username, allOrNothing);
    TranslationImportResult importResult = buildImportResult(translationImportOrchestrator);
    insertStopImportEvent(candidates, inputName);
    return importResult;
}

Проблема в инъекции importCandidateBuilder. Хотя я привязал его, он выдает исключение:

org.glassfish.hk2.api.UnsatisfiedDependencyException: в Injectee не было объекта, доступного для инъекции (requiredType = ImportCandidateBuilder, parent = TranslationImportServiceImpl ...)

ImportCandidateBuilderClass:

@Service
public class ImportCandidateBuilder {

    private static Logger logger = LoggerFactory.getLogger(ImportCandidateBuilder.class);

    private IDomainsBusinessServices domainService;
    private ICodeBusinessService codeService;
    private ILangBusinessService vlService;
    private ITranslationBusinessService translationService;

    public ImportCandidateBuilder(IDomainsBusinessServices domainService, ICodeBusinessService codeService,
                                  ILangBusinessService vlService, ITranslationBusinessService translationService) {
        this.domainService = domainService;
        this.codeService = codeService;
        this.vlService = vlService;
        this.translationService = translationService;
    }

    public List<ImportCandidate> buildCandidates(Long domainId, List<InputTranslationBean> inputBeans) {
        Validate.notNull(domainId, "arg [domainId] is null");
        Validate.notEmpty(inputBeans, "arg [translations] is null or empty");

        List<ImportCandidate> ret = new ArrayList<>();

        Domains domain = fetchDomain(domainId);

        for (InputTranslationBean inputBean : inputBeans) {
            Tables table = fetchTable(domain, inputBean.getTableName());
            Codes code = (table == null) ? null : fetchCode(table, inputBean.getCodeName());
            Vl lang = fetchVl(inputBean.getLang());
            Trad translation = (code == null) ? null : fetchTranslation(code, lang);
            String transText = inputBean.getTranslation();
            String instructions = inputBean.getInstructions();
            ImportCandidate candidate = new ImportCandidate(domain, table, code, lang, translation, transText, instructions, inputBean);

            logger.debug("New import candidate built: [{}]", candidate);
            ret.add(candidate);
        }
        return ret;
    }
}

Когда я вызываю метод getCurrentSession (), выдает исключение, которое не может быть введено SessionFactory, и когда я пытался создать и ввести конструктор для DefaultBusinessService, sessionfactory возвращала значение null. Я также связала уроки.

@Service
public class DomainsBusinessServices extends DefaultBusinessService 
                                     implements IDomainsBusinessServices
{
public Domains getDomainById(Long domainId, boolean fetchLangs,
            boolean fetchTables, boolean fetchCodes) {
        Criteria domainCriteria = getCurrentSession().createCriteria(Domains.class);
        domainCriteria.add(Restrictions.idEq(domainId));
        if (fetchLangs){
            domainCriteria.setFetchMode("Vls", FetchMode.JOIN);
        }
        if (fetchTables){
            domainCriteria.setFetchMode("Tableses", FetchMode.JOIN);            
        }
        if (fetchCodes){
            domainCriteria.setFetchMode("Codeses", FetchMode.JOIN);         
        }
        Domains domainVL = (Domains)domainCriteria.uniqueResult();
        if (domainVL != null) {
            Hibernate.initialize(domainVL.getVls());
        }
        return domainVL;
    }
}


@Service
public class DefaultBusinessService {

    @Inject
    private SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    protected Session getCurrentSession(){
        return sessionFactory.getCurrentSession();
    }
}

Вы используете Джерси v2.27 или v2.26? Если да, то я знаю решение

vikarjramun 18.07.2018 21:09

Я использую Jersey v2.6, потому что в моем проекте используется Java 7. Буду рад, если вы предложите какое-нибудь решение.

kbou 19.07.2018 10:20

вы можете показать мне импорт для AbstractBinder?

vikarjramun 19.07.2018 15:56

Можете ли вы опубликовать трассировку стека полный и класс построителя.

Paul Samsotha 19.07.2018 18:13

да конечно @vikarjramun, import org.glassfish.hk2.utilities.binding.AbstractBinder;

kbou 20.07.2018 09:09

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

kbou 20.07.2018 09:41

А класс строителя?

Paul Samsotha 20.07.2018 09:49

я также редактировал @paul

kbou 20.07.2018 13:46

@kbou Я не думаю, что это связано с bindToContract(), как я сначала подумал, или с использованием неправильного импорта, поскольку вы используете тот же импорт, который я использовал до v2.25. Я считаю, что это может быть связано с ImportCandidateBuilder. Вы упомянули код публикации для этого ранее, но я его не вижу. Я считаю, что нам понадобится этот код, чтобы понять это ...

vikarjramun 20.07.2018 18:05

@kbou ... Вы обнаружили, что одно из моих самых больших разочарований в JAX-RS: конфигурация. При правильной настройке он работает очень хорошо - REST API - это просто аннотирование, а внедрение зависимостей работает хорошо. Тем не менее, я потратил так много времени, соединяя вместе внедрение зависимостей и заставляя все работать вместе. Надеюсь, вы скоро решите проблему :)

vikarjramun 20.07.2018 18:13

Единственное, о чем я могу думать, - это проблема с созданием класса ImportCandidateBuilder. В остальном все выглядит хорошо. Если не можете понять и хотите собрать минимальный репозиторий GitHub, я могу взглянуть на него. Но из предоставленной вами информации (за вычетом запрошенного класса построителя, который вы не предоставили), все выглядит хорошо.

Paul Samsotha 21.07.2018 07:19

извините, я забыл добавить это @ paul .. взгляните также на класс Builder

kbou 23.07.2018 16:37
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
12
1 669
1

Ответы 1

Класс ImportCandidateBuilder не может быть создан, потому что нет конструктора по умолчанию. У вас есть конструктор, но откуда все эти аргументы должны исходить

public ImportCandidateBuilder(IDomainsBusinessServices domainService,
                              ICodeBusinessService codeService,
                              ILangBusinessService vlService, 
                              ITranslationBusinessService translationService) {
}

Если вы хотите, чтобы инфраструктура внедрения создавала службу, тогда должен быть либо вызываемый конструктор по умолчанию, либо конструктор с аргументами, который необходимо аннотировать с помощью @Inject, и все аргументы также должны быть привязаны к контейнеру DI.

@Inject
public ImportCandidateBuilder(IDomainsBusinessServices domainService,
                              ICodeBusinessService codeService,
                              ILangBusinessService vlService, 
                              ITranslationBusinessService translationService) {
}

register(new AbstractBinder() {
    @Override
    public void configure() {
        bind(DomainsBusinessServices.class)
                .to(IDomainsBusinessServices.class).in(Singleton.class);
        //.. bind others
    }
});

Ты определенно прав, @Paul. Я думал, что в этом случае достаточно служебной аннотации. Я попробую дальше, потому что у меня сложная логика в коде с большим количеством DI.

kbou 25.07.2018 10:53

Аннотации @Service и @Contract ничего не делают, если вы не используете автоматическое заполнение службы. И даже при этом то же правило по-прежнему применяется в отношении того, как мы обрабатываем конструкторы.

Paul Samsotha 25.07.2018 11:03

Однако это случай, когда я не могу ввести класс репозитория (SessionFactory.class). Как справиться с этим @paul?

kbou 25.07.2018 11:56

Ну, вам нужно привязать ServiceFactory к системе DI. Вы можете использовать Factory для его создания и использовать bindFactory. Взгляните на это для некоторой идеи

Paul Samsotha 25.07.2018 12:50

Можно ли @paul написать код для этого случая? Я попытался использовать Factory и bindfactory, но снова моя инъекция sessionFactory вернулась как null.

kbou 26.07.2018 09:07

Другие вопросы по теме