JEE-注入豆列表

时间:2018-11-29 13:13:25

标签: java-ee cdi

我想注入一列豆子。我在网上搜索,但发现的不是很多。我尝试过: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。

所以我的问题是:是否有可能注入一系列豆子? 我想如果可能的话,也应该可以向列表中添加元素。但是一步一步来。

1 个答案:

答案 0 :(得分:1)

首先,使用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范围的,并且(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如果使用不当,则容易发生内存泄漏-请确保正确使用它们!