使用注入器从名称动态实例化 class

Dynamicaly instanciate class from name with injector

上下文

我为我的公司开发了一款软件,该软件通过多种特征提取算法对包含网站的网络钓鱼和恶意软件进行分类。

提取特征后,我们将使用一组经验和机器学习分类器。我们在其中进行选择得益于我们自己的选举功能。

代码

基本上我们有实现 AnalysisFunction 契约的分类器 类。

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

我们的分类器池包含在实现 AnalysisFunction.

的 "pool" 中
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,但我没有找到这样做的文档,一位同事说这不好。

他告诉我要记录有关代理的信息,但这对我来说似乎不是一件好事,或者我可能错过了什么。

您可以在活页夹中配置所有这些。这样您就不必担心尝试自己实例化所有内容。让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 class,而不需要使用工厂。

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

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

注意 IterableProvider class。这是一个 HK2 class,用于注入绑定到同一个合约的多个服务。

或者,如果您想使用工厂,您可以将函数注入工厂。这样你就可以使 PoolAnalysisFunction class 独立于 HK2 classes(即 InjectableProvider)。