我想向我们的项目介绍CDI(Weld),现在手动构建的对象遇到了一些麻烦。
所以我们有一些实现IReport
接口的类,它们有一个应该注入的字段。这在运行时为null,因为所有这些类都是由类ReportFactory
中的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)编辑:
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,它有几种方法,具体取决于按下哪个按钮。所有这些方法都设置报告名称并调用更通用的方法sendOrGetReport
。此方法从数据库中选择指定报告的唯一键,并决定是发送电子邮件还是立即发送报告。我们假设应该交付它。
然后ReportController
发挥作用。他根据上述方法提供的唯一键和其他信息提取ReportInfo
个对象,并调用ReportFactory
创建类型为ri.getClassName()
的报告。
想象,是吧?我认为整个部分可能需要一些重构。如果您没有看到任何简单的位置,我会跳过报告实现中的@Inject
并对该资源执行JDNI查找。
答案 0 :(得分:12)
为了动态创建正确的报告类,对接受的答案进行小的更改将解决问题。解决方案是Instance&lt; ...&gt;为您提供特定类型的所有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();
}
}
答案 1 :(得分:8)
为了管理依赖关系,CDI(和其他DI框架)背后的想法是接管对托管bean生命周期的控制。这意味着 - 除其他外 - 如果您希望托管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;
...
}
答案 2 :(得分:1)
好的,如果我没有错,基于doc(这是正确的框架吗?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。现在这可能永远是同一个实例;我不知道这是否是你需要的,对不起。
抱歉,如果我弄错了;希望它有所帮助。