【问题标题】:Dynamicaly instanciate class from name with injector使用注入器从名称动态实例化类
【发布时间】:2017-04-28 13:32:21
【问题描述】:

上下文

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

提取特征后,我们会使用经验分类器和机器学习分类器池。我们通过自己的选举功能在其中进行选择。

代码

基本上,我们的分类器类实现了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,但我没有找到这样做的文档,一位同事说这不好。

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

【问题讨论】:

  • (注意:您可以使用{} 图标作为代码块或选择代码,然后按CTRL+K 或command-K)
  • 感谢您的编辑和提示 :)
  • 所以如果我理解得很好,您想在构造函数“working”上使用 @Inject 实例化 SVMF2AnalysisFunction
  • 就是这样,当我在我的工厂动态实例化它时,我希望它工作
  • 我会使用注入来获取AnalysisFunction 中的SVMContainer 并将其作为参数传递给newInstance()(假设它始终是相同的参数类型)。或者为您的“功能”使用某种“原型”bean

标签: java jersey-2.0 hk2


【解决方案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)。

【讨论】:

  • 感谢stackoverflow.com/questions/29283176/… 和您的回答,我尝试了一些非常相似的方法。由于模型构建很长,我稍后会放上我的结果。
  • 顺便说一句,如果你不想在那里有hk2特定的API,你也可以只使用Iterable
  • 此外,您可能希望在绑定中添加 .named("something-from-the-file") 这可能有助于稍后进行诊断分析或能够专门选择特定功能跨度>
  • 您好!我将您的答案标记为对我有帮助的答案,因为我得到了我想要的。我只是保留我的工厂,以便我可以真正配置我包含的功能。
猜你喜欢
  • 2021-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-09
  • 1970-01-01
  • 1970-01-01
  • 2013-10-04
  • 2014-01-26
相关资源
最近更新 更多