java 用 CDI 机制代替基于工厂的对象创建
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6621403/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Replace factory-based object creation with CDI mechanism
提问by Sebastian Wramba
I wanted to introduce CDI (Weld) to our project and now having some trouble with objects that are manually constructed.
我想在我们的项目中引入 CDI(焊接),现在手动构建的对象有一些问题。
So we have some classes implementing the IReport
interface, which have a field that should be injected. This is null at runtime because all of those classes are being generated by the ReportFactory
in a class ReportController
.
所以我们有一些实现IReport
接口的类,它们有一个应该注入的字段。因为所有这些类是由产生这是在运行时零ReportFactory
一类ReportController
。
private Map<String,Object> generateReport(ReportInfo ri, ...) {
// some input validation
IReport report = ReportControllerFactory.getReportInstance( ri.getClassName() );
// ...
}
I am aware that I can use the @Produces
annotation together with another custom annotation in the ReportControllerFactory
, but how do I use the @Inject
for a variable that can only be created, after some validation was done, insidea method? And how would I submit the parameter ri.getClassName()
? The object ri
is not known when the ReportController
is constructed.
我知道我可以将@Produces
注释与 中的另一个自定义注释一起使用ReportControllerFactory
,但是如何使用@Inject
for 只能在完成某些验证后在方法中创建的变量?我将如何提交参数ri.getClassName()
?对象ri
在ReportController
构造时未知。
Thank you very much!
非常感谢你!
Kind regards, Sebastian
亲切的问候,塞巴斯蒂安
Edit at Jul 08, 2011 (10:00):
2011 年 7 月 8 日 (10:00) 编辑:
ReportFactory class:
报表工厂类:
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;
}
Edit 2 (Selection of the right report implementation)
编辑2(选择正确的报表实现)
The report instance is selected by some paths that go from the JSF frontend to the ReportController. The ManagedBean calls a session bean, which has several methods, depending on which button was pressed where. All those methods set the report name and call the more generic method sendOrGetReport
. This method selects the unique key for the specified report from the database and decides whether to send an e-mailor immediately deliverthe report. Let's assume it should be delivered.
报表实例由一些从 JSF 前端到 ReportController 的路径选择。ManagedBean 调用会话 bean,该会话 bean 有多种方法,具体取决于按下哪个按钮的位置。所有这些方法都会设置报告名称并调用更通用的方法sendOrGetReport
。此方法从数据库中选择指定报告的唯一键,并决定是发送电子邮件还是立即发送报告。让我们假设它应该被交付。
Then the ReportController
comes into play. He fetches a ReportInfo
object based upon the unique key and other information provided by the methods above and calls the ReportFactory
to create a report of type ri.getClassName()
.
然后ReportController
开始发挥作用。他ReportInfo
根据上述方法提供的唯一键和其他信息获取对象,并调用ReportFactory
以创建类型为 的报告ri.getClassName()
。
Fancy, huh? I think this whole section might need some refactoring. If you don't see any easy spot, I skip the @Inject
in the report implementation and do a JDNI lookup for that resource.
花哨吧?我认为整个部分可能需要一些重构。如果您没有看到任何简单的地方,我会跳过@Inject
报告实现中的 并对该资源进行 JDNI 查找。
回答by yoda
In order to dynamically create the right report class a small change to the accepted answer will solve the problem. The solution is the fact that Instance<...> gives you the list of all beans that are of a certain type. A produces annotation is not needed.
为了动态创建正确的报告类,对已接受的答案进行小幅更改即可解决问题。解决方案是 Instance<...> 为您提供某种类型的所有 bean 的列表。不需要生产注释。
Make a factory class that can select between the injected instances at runtime
创建一个可以在运行时在注入的实例之间进行选择的工厂类
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;
}
Now the class that needs a dynamically selected report, can use this factory.
现在需要动态选择报表的类,可以使用这个工厂。
public class ReportCreator {
@Inject
private ReportFactory reportFactory;
public void createReport(String type) {
IReport report = reportFactory.createReport(type);
report.execute();
}
}
回答by jan groth
The idea behind CDI (and other DI-frameworks) in order to manage dependencies is to take over control of the managed beans lifecycle. This implies - among other things - that you cannot interfere with the creation of managed beans if you want it to be managed by the container.
为了管理依赖关系,CDI(和其他 DI 框架)背后的想法是接管对托管 bean 生命周期的控制。这意味着 - 除其他外 - 如果您希望它由容器管理,则不能干扰托管 bean 的创建。
Certainly this does not mean that your scenario is unsolvable, you just have to change your perspective a bit ;-)
当然,这并不意味着您的场景无法解决,您只需要稍微改变一下观点;-)
The idea is to work with managed beans (obviously), but let your own logic decide which instance of all available instances is the correct.
这个想法是使用托管 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;
}
...
With as many beans of beantype IReport as you like
使用任意数量的 beantype IReport bean
public class AReport implements IReport {
...
@Inject
...
}
public class BReport implements IReport {
...
@Inject
...
}
And an automagical usage like this
像这样的自动用法
public class MyStuff {
...
@Inject
IReport myReport;
...
}
See hereand herefor more information.
If I did not misunderstand your problem, this should bring you forward - feel free to post further questions / comments.
如果我没有误解你的问题,这应该会让你前进 - 随时发布进一步的问题/评论。
UPDATE:
更新:
Everything might be just dead simple if something like this fits your requirements:
如果这样的事情符合您的要求,一切都可能非常简单:
@AReport
public class AReport implements IReport {
...
@Inject
...
}
@BReport
public class BReport implements IReport {
...
@Inject
...
}
With the usage like this
像这样的用法
public class MyStuff {
...
@Inject
@AReport
IReport myAReport;
...
@Inject
@BReport
IReport myBReport;
...
}
回答by phtrivier
Ok, so if I'm not too wrong, based on the doc ( Is this the proper framework ? http://docs.jboss.org/seam/3/latest/reference/en-US/html/solder-beanmanagerprovider.html), your factory would need to get a handle to the BeanManager singleton (either get it injected, or call some accessor from the framework) , and do something along the lines of
好的,所以如果我没有错的话,根据文档(这是合适的框架吗?http://docs.jboss.org/seam/3/latest/reference/en-US/html/solder-beanmanagerprovider。 html),您的工厂需要获得 BeanManager 单例的句柄(要么注入它,要么从框架调用一些访问器),并按照以下方式做一些事情
Class<?> clazz = Class.forName( className );
report = beanManager.getBean(clazz);
Assuming your CDI has been configured to handle each of the possible class name, you should get the correct bean. Now this might always be the same instance ; I don't know if this is what you need, sorry.
假设您的 CDI 已配置为处理每个可能的类名,您应该获得正确的 bean。现在这可能总是同一个实例;我不知道这是否是您需要的,抱歉。
Sorry If I'm mistaken ; hoping it helps.
对不起,如果我弄错了;希望它有帮助。