在Facelet模板中动态加载JSF bean

时间:2010-12-29 11:15:33

标签: jsf facelets javabeans

我在程序中动态加载bean时遇到问题。我有这样的facelet模板:

<ui:define name="content">
        <f:loadBundle var="Application" basename="start" />
        <f:loadBundle var="Messages" basename="#{ApplicationController.bundleName}" />
        <h:panelGroup rendered="#{ApplicationController.chosenBean.hasAccess}">
            <h:form>
                <h:outputText value="Error: #{ApplicationController.chosenBean.errorMessage}" id="outputErrorMessage2" /><br />
                  <h:outputText value="Report running: #{ApplicationController.chosenBean.runningReport}" /><br />
                  <ui:insert name="application-content" />
                  <h:commandButton action="#{ApplicationController.chosenBean.actionRunReport}" value="Create report"/>
               </h:form>
            </h:panelGroup>
            <h:panelGroup rendered="#{!ApplicationController.chosenBean.hasAccess}">
            <h:outputText value="Err" />
        </h:panelGroup>
</ui:define>

如您所见,我希望ApplicationController决定使用哪个Javabean。这是通过以下代码完成的:

 ApplicationController.java
public ReportWeb getChosenBean() {
    String reportName = getReportNameFromServletPath();
    //Make first character upper case
    String beanName = reportName.substring(0, 1).toUpperCase() + reportName.substring(1);
    logger.log(Level.INFO, "Loading bean with name : {0}", BEAN_PACKET_NAME + beanName);
    try {
        if (Class.forName(BEAN_PACKET_NAME + beanName).newInstance() instanceof ReportWeb) {
            chosenBean = (ReportWeb) Class.forName(BEAN_PACKET_NAME + beanName).newInstance();
        }
    } catch (ClassNotFoundException ex) {
        Logger.getLogger(ApplicationController.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        Logger.getLogger(ApplicationController.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        Logger.getLogger(ApplicationController.class.getName()).log(Level.SEVERE, null, ex);
    }
    return chosenBean;
}

我的所有Javabeans都是ReportWeb类的子类,它是一个包含访问逻辑和其他便捷方法的抽象类。

是最重要的方法
public abstract String actionRunReport();

我的所有报告都实现了这一点。但是在我的模板中,我无法通过这样做来调用此方法:

<h:commandButton action="#{ApplicationController.chosenBean.actionRunReport}" value="Create report"/>

我收到没有错误消息,但它不起作用。我必须像这样硬编码Javabean名称:

<h:commandButton action="#{AntallHenvendelser.actionRunReport}" value="Create report"/>

anoyne知道为什么我的技术不起作用吗?

修改 永远不会从动态加载bean的动作按钮调用该方法。

1 个答案:

答案 0 :(得分:1)

根本原因可能是因为在表单提交请求期间,操作组件(或至少其中一个父组件)的rendered属性已评估false。这样就不会调用该动作。在处理表单提交请求时,您需要确保rendered属性未评估false

另请参阅this answer,了解未调用操作组件的可能原因。


无论如何,你所展现的方法效率很低。我将ApplicationController请求作为范围并加载所选的bean,如下所示:

public class ApplicationController {

    private ReportWeb chosenBean;

    public ApplicationController() {
        chosenBean = findBean(getReportNameFromServletPath(), ReportWeb.class);
    }

    public static <T> T findBean(String managedBeanName, Class<T> beanClass) {
        FacesContext context = FacesContext.getCurrentInstance();
        return beanClass.cast(context.getApplication().evaluateExpressionGet(context, "#{" + managedBeanName + "}", beanClass);
    }

    // ...
}

这样每个请求只加载一次,而不是至少一次 EL表达式调用getter(在rendered属性中使用时至少两倍)。

这样你就可以抓住由JSF管理的实际的bean,而不是在getter中手动创建它(两次!)。如果JSF已经创建了一个实例,那么将返回这个实例。如果JSF尚未创建它,则会在返回之前创建一个新的并相应地放入范围。