Динамически создать экземпляр класса из имени с помощью инжектора

Контекст

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

После извлечения функций мы используем пул классификаторов эмпирического и машинного обучения. Мы выбираем среди них благодаря собственной избирательной функции.

код

В основном у нас есть классы классификаторов, которые реализуют контракт AnalysisFunction.

public abstract class AnalysisFunction {
    abstract public StatusType analyze(List<TokenEntity> tokens);
    abstract public double getPhishingProbability(List<TokenEntity> tokens);
}

Наш пул классификатора содержится в «пуле», который реализует AnalysisFunction.

public class PoolAnalysisFunction extends AnalysisFunction{
    private final List<AnalysisFunction> candidates;
    private final ChoiceFunction choice;
    private static final Logger LOG = LogManager.getLogger(PoolAnalysisFunction.class);

    public PoolAnalysisFunction(List<AnalysisFunction> candidates, ChoiceFunction choice) {
        this.candidates = candidates;
        this.choice = choice;
    }

    @Override
    public StatusType analyze(List<TokenEntity> tokens) {
        try {
            return choice.chooseAmong(candidates, tokens).analyze(tokens);
        } catch (ImpossibleChoiceException e){
            LOG.fatal("Not enough analysis function.", e);
            return StatusType.CLEAN;
        }
    }

    @Override
    public double getPhishingProbability(List<TokenEntity> tokens) {
        try {
            return choice.chooseAmong(candidates, tokens).getPhishingProbability(tokens);
        } catch (ImpossibleChoiceException e){
            LOG.fatal("Not enough analysis function.", e);
            return 0;
        }
    }
}

Чтобы упростить развертывание и тестирование новой функции, мы хотим сделать наш пул полностью настраиваемым и создавать экземпляры каждой функции по ее имени. Для достижения этой цели у нас есть ключ в нашем файле свойств, похожий на analysis.pool.functions=com.vadesecure.analysis.empirical.Function1,com.vadesecure.analysis.machine.AutomaticClassifier1.

Благодаря этому я хочу создать экземпляры своих функций. Моя проблема в том, что эти классификаторы зависят от разных вещей, таких как настраиваемый объект конфигурации и модель машинного обучения. Я хотел бы ввести те зависимости, которые уже привязаны к моему инжектору hk2.

import org.glassfish.hk2.api.Factory;
public class PoolFunctionFactory implements Factory<AnalysisFunction> {
    private final PoolAnalysisParameters parameters;
    private static final Logger LOG = LogManager.getLogger(PoolAnalysisFunction.class);
    @Inject
    public PoolFunctionFactory(PoolAnalysisParameters parameters) {
        this.parameters = parameters;
    }

    @Override
    public AnalysisFunction provide() {
        try {

            Class<?> choice = Class.forName(parameters.getChoiceFunctionFQDN());
            ChoiceFunction choiceFunction = new PhishingPriorityChoiceFunction(); // default choice
            if(choice.getSuperclass().isInstance(ChoiceFunction.class)){
                choiceFunction = (ChoiceFunction) choice.newInstance();
            }
            List<AnalysisFunction> analysisFunctions = new LinkedList<>();
            // I want to instantiate here
            }
            return new PoolAnalysisFunction(analysisFunctions, choiceFunction);
        } catch (ClassNotFoundException|IllegalAccessException|InstantiationException e){
            LOG.fatal(e, e);
        }

        return null;
    }

    @Override
    public void dispose(AnalysisFunction analysisFunction) {
        LOG.trace(String.format("%s end of life", analysisFunction));
    }
}

На примере модельно-зависимого классификатора:

public class SVMF2AnalysisFunction extends AnalysisFunction {
    private final SVMContainer modelContainer;
    private double probability = 0.0;
    private double threshold = 0.9;
    @Inject // i build this model in a parallel thread
    public SVMF2AnalysisFunction(SVMContainer modelContainer) {
        this.modelContainer = modelContainer;
    }

    @Override
    public StatusType analyze(List<TokenEntity> tokens) {
        if (modelContainer.getModel() == null) {
            return null;
        }
        probability = modelContainer.getModel().analyse(tokens.stream());
        return probability >= threshold ? StatusType.PHISHING : StatusType.CLEAN;
    }

    @Override
    public double getPhishingProbability(List<TokenEntity> tokens) {
        return probability;
    }
}

Как я могу добиться этих экземпляров.

Мой первый подход заключался в том, чтобы внедрить serviceLocator, но я не нашел документации для этого, и мой коллега сказал мне, что это нехорошо.

Он сказал, чтобы я задокументировал себя про прокси, но мне это не кажется хорошим, или, возможно, я что-то пропустил.


person artragis    schedule 28.04.2017    source источник
comment
(NB: вы можете использовать значок {} для блоков кода или выбрать код и его CTRL + K или команду-K)   -  person    schedule 28.04.2017
comment
спасибо за редактирование и совет :)   -  person artragis    schedule 28.04.2017
comment
Итак, если я хорошо понимаю, вы хотите создать экземпляр, например, SVMF2AnalysisFunction с @Inject на рабочем конструкторе?   -  person    schedule 28.04.2017
comment
вот и все, я хочу, чтобы он работал, когда я динамически создаю его на своей фабрике   -  person artragis    schedule 28.04.2017
comment
Я бы использовал инъекцию, чтобы получить SVMContainer в AnalysisFunction и передать его в качестве аргумента newInstance() (при условии, что это всегда один и тот же тип аргумента). ИЛИ используйте какой-то компонент Prototype для ваших функций   -  person    schedule 28.04.2017
comment
Что ты имеешь в виду? Это примерно никогда не бывает одним и тем же аргументом. Каждый классификатор можно настроить по-своему (но с ним легко работать), но все модели совершенно разные.   -  person artragis    schedule 28.04.2017
comment
Позвольте нам продолжить это обсуждение в чате.   -  person artragis    schedule 28.04.2017


Ответы (1)


Вы можете просто настроить все это в своей папке. Таким образом, вам не нужно беспокоиться о попытках создать все самостоятельно. Просто позвольте HK2 сделать всю работу

@Override
protected void configure() {
   bindAsContract(PoolAnalysisFunction.class).in(Singleton.class);

   bind(choiceFnClass).to(ChoiceFunction.class);

   for (Class<AnalysisFunction> analysisFnClass: analyisFnClasses) {
       bind(analysisFnClass).to(AnalysisFunction.class).in(Singleton.class);
   }
}

Затем вы можете просто внедрить все в класс PoolAnalysisFunction без необходимости использования фабрики.

@Inject
public PoolAnalysisFunction(IterableProvider<AnalysisFunction> candidates,
                            ChoiceFunction choice) {
    this.choice = choice;

    this.candidates = new ArrayList<>();
    candidates.forEach(this.candidates::add);
}

Обратите внимание на класс IterableProvider. Это класс HK2 для внедрения нескольких сервисов, связанных с одним и тем же контрактом.

Или, если вы хотите использовать фабрику, вы можете просто внедрить функции в фабрику. Таким образом вы можете сделать класс PoolAnalysisFunction независимым от классов HK2 (то есть InjectableProvider).

person Paul Samsotha    schedule 28.04.2017
comment
Благодаря stackoverflow .com / questions / 29283176 / и ваш ответ. Я пробовал нечто очень похожее. Так как построение модели очень долгое, я опубликую свои результаты позже. - person artragis; 28.04.2017
comment
Кстати, вы также можете просто использовать Iterable ‹AnalysisFunction›, если вы не хотите, чтобы там был специфичный для hk2 API. - person jwells131313; 01.05.2017
comment
Кроме того, в привязке вы можете добавить. Named (что-то из файла), что может помочь с диагностическим анализом позже или возможность специально выбрать конкретную функцию. - person jwells131313; 01.05.2017
comment
Привет ! Я отмечаю ваш ответ как тот, который помог мне, потому что я получил то, что хотел. Я просто сохраняю свою фабрику, чтобы действительно настроить, какую функцию я включаю. - person artragis; 02.05.2017