2

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

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

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) 编辑:

报告工厂类:

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 查找。

4

3 回答 3

12

为了动态创建正确的报告类,对已接受的答案进行小的更改即可解决问题。解决方案是 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();
    }
 }
于 2011-09-13T10:54:34.267 回答
8

为了管理依赖关系,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;
...
}

有关更多信息,请参见此处此处

如果我没有误解您的问题,这应该会让您前进 - 请随时发表进一步的问题/评论。

更新

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

@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;
...
}
于 2011-07-08T08:37:48.880 回答
1

好的,所以如果我没有错,基于文档(这是正确的框架吗?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。现在这可能总是同一个实例;我不知道这是不是你需要的,对不起。

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

于 2011-07-08T08:32:51.203 回答