我想注入一列豆子。我在网上搜索,但发现的不是很多。我尝试过:https://onlysoftware.wordpress.com/2011/07/10/injecting-lists-cdi-jsf/,但有豆子。
查看:
@UIScoped
public class DemoView extends VerticalLayout {
@Inject
private MessageBean messageBean;
private Button button;
public DemoView() {
getStyle().set("border", "1px solid");
button = new Button("Click me", event -> Notification.show(messageBean.getMessage()));
}
public void init() {
removeAll();
add(new Label("oh no!"));
add(button);
}
}
注释:
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface ViewList {
}
InitBean:
@ApplicationScoped
public class AppInitBean implements Serializable {
@Produces
@Named(value = "viewNamedList")
@ViewList
public List<DemoView> getViews() {
return this.generateViews();
}
private List<DemoView> generateViews() {
List<DemoView> views = new ArrayList<DemoView>(5);
for (int i = 1; i <= 5; i++) {
DemoView emp = new DemoView();
views.add(emp);
}
return views;
}
}
MainClass:
@Route("")
public class MainView extends VerticalLayout implements BeforeEnterObserver {
@Inject
@ViewList
private List<DemoView> viewList;
@Override
public void beforeEnter(BeforeEnterEvent event) {
removeAll();
add(new Label("whatever"));
for (DemoView demoView : viewList) {
demoView.init();
add(demoView);
}
}
}
列表已生成,将按预期显示。但是,如果按下按钮,我会得到一个NPE->未注入messageBean。
所以我的问题是:是否有可能注入一系列豆子? 我想如果可能的话,也应该可以向列表中添加元素。但是一步一步来。
答案 0 :(得分:1)
首先,使用new
创建一个bean会绕过CDI,自然不会发生注入。这就是NPE的原因。
您可以通过两种方式解决该问题:(1)正如您所说,列出CDI管理的bean列表并将其注入要求的内容,或者(2)使DemoView
成为普通Java对象,由您管理,而不是由CDI管理,并且仍然由CDI生成并注入此类事物的列表。
解决方案(2)比较简单,我认为在某些情况下更合适。我不知道引发问题的用例的确切细节,但是显然DemoView
是某种UI组件。当尝试将UI组件(如JSF,JavaFX)用作CDI bean时,存在严重冲突:哪个框架将创建对象?无论如何,请按以下方式更改DemoView
:
// No scope annotation!
public class DemoView extends VerticalLayout {
// No inject!
private MessageBean messageBean;
public DemoView(MessageBean messageBean) {
this.messageBean = messageBean;
}
...
}
制作人变成:
@ApplicationScoped
public class AppInitBean implements Serializable {
// Inject the collaborators required by DemoView here
@Inject
private MessageBean messageBean;
@Produces
@Named(value = "viewNamedList")
@ViewList
// I would argue you should define a scope here - @UIScoped maybe?
// This would be the scope of the produced list (and it seems appropriate)
public List<DemoView> getViews() {
return this.generateViews();
}
private List<DemoView> generateViews() {
List<DemoView> views = new ArrayList<DemoView>(5);
for (int i = 1; i <= 5; i++) {
DemoView emp = new DemoView(messageBean); // pass the messageBean
views.add(emp);
}
return views;
}
// You may also consider adding a disposer method,
// if the list of DemoViews needs special cleanup logic.
}
如果不为生成的视图提供范围,则List
隐式地@Dependent
范围。 非常小心,如果将其注入寿命长的组件(例如,@ApplicationScoped
),则可能需要手动销毁它。
如果您真的想要解决方案(1),即生成受管理bean的列表,则必须考虑到对于正常范围的bean,CDI在活动范围内仅保留1个bean,并且不能管理更多(至少没有限定符来区分实例)。我猜测,@UIScoped
是正常范围。如果要自己管理Bean的创建,则还必须(a)使它成为@Dependent
范围的,并且(b)自己管理其生命周期。为了创建@Dependent
范围的bean的许多实例,请注入Instance<DemoView>
。
因此,DemoView
将更改为:
@Dependent
public class DemoView extends VerticalLayout {
...
}
和生产者(一般概念和未经测试的代码,这可能会有细微的陷阱):
@ApplicationScoped
public class AppInitBean implements Serializable {
// Inject the collaborators required by DemoView here
@Inject
private Instance<DemoView> demoViewInstance;
@Produces
@Named(value = "viewNamedList")
@ViewList
@UIScoped // Do give the list a scope!
public List<DemoView> getViews() {
return this.generateViews();
}
private List<DemoView> generateViews() {
List<DemoView> views = new ArrayList<DemoView>(5);
for (int i = 1; i <= 5; i++) {
DemoView emp = demoViewInstance.get(); // creates new instance for @Dependent beans
views.add(emp);
}
return views;
}
// Definitely add a disposer method
void disposeViews(@Disposes @ViewList List<DemoView> views) {
views.forEach(demoViewInstance::destroy);
}
}
警告:Instance
和@Dependent
Bean如果使用不当,则容易发生内存泄漏-请确保正确使用它们!