【问题标题】:Replace factory-based object creation with CDI mechanism用 CDI 机制替换基于工厂的对象创建
【发布时间】:2011-07-08 07:38:18
【问题描述】:

我想在我们的项目中引入 CDI(焊接),但现在手动构建的对象遇到了一些问题。

所以我们有一些实现IReport 接口的类,它们有一个应该被注入的字段。这在运行时为 null,因为所有这些类都是由类 ReportController 中的 ReportFactory 生成的。

private Map<String,Object> generateReport(ReportInfo ri, ...) {
// some input validation
    IReport report = ReportControllerFactory.getReportInstance( ri.getClassName() );
// ...
}

我知道我可以将@Produces 注释与ReportControllerFactory 中的另一个自定义注释一起使用,但是在完成一些验证后,如何将@Inject 用于只能创建的变量, 里面一个方法?我将如何提交参数ri.getClassName()?对象ri 在构造ReportController 时是未知的。

非常感谢!

亲切的问候, 塞巴斯蒂安

2011 年 7 月 8 日 (10:00) 编辑:

ReportFactory 类:

public static IReport getReportInstance( String className ) throws ReportException {

    IReport report = null;

    try {
        Class<?> clazz = Class.forName( className );
        report = (IReport) clazz.newInstance();
    }
    catch ( Exception e ) { … }        

    return report;
}

编辑 2(选择正确的报告实施)

报表实例由一些从 JSF 前端到 ReportController 的路径选择。 ManagedBean 调用一个会话 bean,该会话 bean 有多种方法,具体取决于在何处按下了哪个按钮。所有这些方法都设置报告名称并调用更通用的方法sendOrGetReport。此方法从数据库中选择指定报告的唯一键,并决定是发送电子邮件还是立即发送报告。让我们假设它应该交付。

然后ReportController 开始发挥作用。他根据上述方法提供的唯一键和其他信息获取ReportInfo 对象,并调用ReportFactory 创建ri.getClassName() 类型的报告。

花哨,对吧?我认为整个部分可能需要一些重构。如果您没有看到任何容易的地方,我会跳过报告实现中的 @Inject 并对该资源进行 JDNI 查找。

【问题讨论】:

  • 您是否尝试注入“报告”(这似乎很奇怪,因为您指出它只能在运行时构建)?或者你需要的是注入一个工厂(带有注入的依赖项)而不是使用静态方法?
  • 是的,“报告”有一个带有@Inject 注释的字段。所以我必须以某种方式告诉 CDI 解决该注入。我编辑了我的问题并添加了工厂代码。
  • 好的,但在这种情况下,由于您使用 clazz.newInstance() 创建 IReport bean,它无法获得注入的依赖项,对吗?我不知道你的 DI 框架,但你可能必须使用框架中的某种“injector”/“beanRepository”/“beanFactory”来创建注入依赖项的 bean,不是吗?
  • 你能否用一些更明确的信息来更新你的问题,这些信息是关于你需要在运行时决定需要哪个 IReport 实现的标准?
  • 谢谢,我做到了。相当复杂,但它可能会有所帮助。

标签: java factory factory-pattern cdi jboss-weld


【解决方案1】:

为了动态创建正确的报告类,对已接受的答案稍作改动即可解决问题。解决方案是 Instance<...> 为您提供特定类型的所有 bean 的列表。不需要生成注释。

创建一个可以在运行时在注入的实例之间进行选择的工厂类

public class ReportFactory {

@Inject Instance<IReport> availableReports;

public IReport createReport(String type) {

   for (IReport report: availableReports) {
      if (report.getType().equals(type)) { //or whatever test you need
         return report;
      }
   }
   return null;
}

现在需要动态选择报表的类,可以使用这个工厂了。

public class ReportCreator {

    @Inject
    private ReportFactory reportFactory;

    public void createReport(String type) {
        IReport report = reportFactory.createReport(type);
        report.execute();
    }
 }

【讨论】:

  • 抱歉回复晚了。切换到 JBoss 7 已停止。这听起来像是一个很有前途的解决方案,我要试试这个。
  • 感谢您的回答!它有很大帮助,尤其是 Instance&lt;...&gt; 构造的这个很好的提示!
  • 优秀。这对我来说非常方便。
  • 比最好的更好。
【解决方案2】:

为了管理依赖关系,CDI(和其他 DI 框架)背后的想法是接管对托管 bean 生命周期的控制。这意味着 - 除其他外 - 如果您希望托管 bean 由容器管理,则不能干预它的创建。

当然,这并不意味着您的方案无法解决,您只需要稍微改变您的观点;-)

这个想法是使用托管 bean(显然),但让您自己的逻辑决定所有可用实例中的哪个实例是正确的。

...
@Inject Instance<IReport> availableReports;
...
@Produces
public IReport createReport() {
   IReport result;
   for (IReport report: availableReports) {
      // choose correct instance, you might want to query the injection
      // point or any attached qualifier a bit more in order to 
      // determine which is the correct instance
      if ...
         result = report;
      ...
   }
   return result;
}
...

使用尽可能多的 beantype IReport bean

public class AReport implements IReport {
...
@Inject
...
}

public class BReport implements IReport {
...
@Inject
...
}

还有这样的自动魔术用法

public class MyStuff {
...
@Inject
IReport myReport;
...
}

请参阅herehere 了解更多信息。

如果我没有误解您的问题,这应该会让您向前 - 随时发布更多问题/cmets。

更新

如果这样的东西符合您的要求,一切都可能非常简单:

@AReport
public class AReport implements IReport {
...
@Inject
...
}

@BReport
public class BReport implements IReport {
...
@Inject
...
}

这样的用法

public class MyStuff {
...
@Inject
@AReport
IReport myAReport;
...
@Inject
@BReport
IReport myBReport;
...
}

【讨论】:

  • 谢谢!我在您的第一个代码示例中实现了 ReportFactory,以及注释 @Report("")。根据该值,返回正确的实例。但正如我所说,在构建时,我不知道我需要什么 IReport 实现。所以我需要在方法中动态注入正确的实现。
【解决方案3】:

好的,所以如果我没记错的话,根据文档(这是正确的框架吗?http://docs.jboss.org/seam/3/latest/reference/en-US/html/solder-beanmanagerprovider.html),你的工厂需要获取 BeanManager 单例的句柄(要么注入它,要么调用框架中的一些访问器),并按照

的方式做一些事情
Class<?> clazz = Class.forName( className );
report = beanManager.getBean(clazz);

假设您的 CDI 已配置为处理每个可能的类名,您应该得到正确的 bean。现在这可能总是同一个实例;不知道是不是你需要的,抱歉。

对不起,如果我弄错了;希望有帮助。

【讨论】:

  • 由于 ReportFactory 是使用静态的,我不能以非静态的方式使用 BeanManager。 :( 此外,IReport 的实现不是托管 bean,除此之外,Weld BeanManager 只有 getBeans() 方法,它返回一组奇怪的多个 bean。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-08
  • 2012-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多