JEE - 注入 bean 列表
JEE - inject a list of beans
我想注入一个 beans 列表。我在网上搜索过,但发现的并不多。我试过这个: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;
}
}
主类:
@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 没有被注入。
所以我的问题是:是否可以注入一个 beans 列表?
我想如果这可能的话,也应该可以向列表中添加一个元素。但是一步一个脚印。
首先 使用 new
创建 bean 会绕过 CDI,自然不会发生注入 。这就是 NPE 的原因。
您可以通过两种方式解决该问题:(1) 正如您所说,列出 CDI 管理的 bean 并将其注入到任何需要的地方,或者 (2) 制作 DemoView
一个普通 Java 对象,由您管理,而不是 CDI,并且仍然有 CDI 生成并注入此类内容的列表。
解决方案 2
解决方案(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
如果您真的想要解决方案 (1),即生成一个托管 bean 列表,您必须考虑到对于正常作用域的 bean,CDI 只保留 1 个 bean活动范围并且无法管理更多(至少没有限定符来区分实例)。我 猜测 @UIScoped
是一个正常范围。如果你想自己管理 bean 的创建,你还必须 (a) 使它成为 @Dependent
-scoped 和 (b) 自己管理它的生命周期。为了创建一个 @Dependent
-scoped 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
beans 容易出现内存泄漏,如果使用不当 - 请确保正确使用它们!!!
我想注入一个 beans 列表。我在网上搜索过,但发现的并不多。我试过这个: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;
}
}
主类:
@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 没有被注入。
所以我的问题是:是否可以注入一个 beans 列表? 我想如果这可能的话,也应该可以向列表中添加一个元素。但是一步一个脚印。
首先 使用 new
创建 bean 会绕过 CDI,自然不会发生注入 。这就是 NPE 的原因。
您可以通过两种方式解决该问题:(1) 正如您所说,列出 CDI 管理的 bean 并将其注入到任何需要的地方,或者 (2) 制作 DemoView
一个普通 Java 对象,由您管理,而不是 CDI,并且仍然有 CDI 生成并注入此类内容的列表。
解决方案 2
解决方案(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
如果您真的想要解决方案 (1),即生成一个托管 bean 列表,您必须考虑到对于正常作用域的 bean,CDI 只保留 1 个 bean活动范围并且无法管理更多(至少没有限定符来区分实例)。我 猜测 @UIScoped
是一个正常范围。如果你想自己管理 bean 的创建,你还必须 (a) 使它成为 @Dependent
-scoped 和 (b) 自己管理它的生命周期。为了创建一个 @Dependent
-scoped 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
beans 容易出现内存泄漏,如果使用不当 - 请确保正确使用它们!!!