Jsf 应用程序范围的ManagedBean方法的可伸缩性和线程安全性

Jsf 应用程序范围的ManagedBean方法的可伸缩性和线程安全性,jsf,Jsf,在测试过程中,我们的应用程序构建f:selectItems列表的方式暴露了一个弱点,特别是在一些实体上输入很长的名称,通过进行很宽的选择来调整页面对齐 许多selectItem列表在多个视图和支持bean中重复,因此我想合并它们的创建 我们已经有了一个应用程序范围的bean,它为枚举提供列表,我最初的想法是将它们放在那个里 不过,我有一些问题。我们正在使用JSF1.2(如果这很重要的话) 1) 我的理解是,应用程序范围的bean是单实例的,因为单个实例被实例化并放置在会话上下文中。它们不同于EJ

在测试过程中,我们的应用程序构建
f:selectItems
列表的方式暴露了一个弱点,特别是在一些实体上输入很长的名称,通过进行很宽的选择来调整页面对齐

许多selectItem列表在多个视图和支持bean中重复,因此我想合并它们的创建

我们已经有了一个应用程序范围的bean,它为枚举提供列表,我最初的想法是将它们放在那个里

不过,我有一些问题。我们正在使用JSF1.2(如果这很重要的话)

1) 我的理解是,应用程序范围的bean是单实例的,因为单个实例被实例化并放置在会话上下文中。它们不同于EJB3单例,因为只有一个线程可以访问任何方法,所以多个请求不会阻止尝试访问不同的方法。对吗

2) 我怀疑每个方法都必须同步,以防止调用同一方法的多个线程相互碰撞。即使方法中访问的唯一类成员是threadsafe无状态@EJB,情况也是如此吗

下面是其中一个的实现,将在20个视图中使用。其他10个实体的实现也类似。此外,还注册了相应的转换器

public synchronized List<SelectItem> getAccountSelect(){
    List<Account> list = new ArrayList<Account>(pemEJB.list(Account.class));
    Collections.sort(list, new AccountByActiveByName());
    List<SelectItem> result=new ArrayList<SelectItem>(list.size());
    for(Account row : list){
        result.add(new SelectItem(row, 
                StringUtil.prefixTruncate(row.getName(), MAX_ACCT_LENGTH, row.isActive())));
    }
    return result;
}
公共同步列表getAccountSelect(){
List List=newarraylist(pemEJB.List(Account.class));
排序(list,newaccountbyactivebyname());
列表结果=新的ArrayList(List.size());
对于(科目行:列表){
结果。添加(新选择项)(第行,
StringUtil.prefixTruncate(row.getName(),MAX_ACCT_LENGTH,row.isActive());
}
返回结果;
}

如果必须在getter中而不是构造函数/后构造函数中加载数据,那么将其作为应用程序范围的bean是毫无意义的。只需将其设置为请求范围,您就可以在构造函数/postconstruct中执行数据加载工作。

在我处理的jsf应用程序中,我们在应用范围bean中加载几乎所有的参考数据(主要是selectOneMenues的值),并在这些bean的构造函数中设置值。然后,通过getter将数据提供给其他托管bean和视图,但是对于应用程序来说,这些数据是全球化和集中化的。由于这些值仅通过getter读取,因此不需要同步

然后,我们通过一个重载方法通过jmx将bean公开为MBean,以便可以根据需要进行更新。重新加载方法是同步的,以便在短时间重新加载期间阻塞

在上面的示例中,您似乎可以返回selectItems的集合,因此,只要事先设置了值,您就可以使用此方法,并且仍然可以为多个线程提供良好的服务:

public List<SelectItem> getAccountSelectItems() {
    return this.accountSelectItems;
}
public List getAccountSelectItems(){
返回此帐户。accountSelectItems;
}
只需将此私有成员添加到您的bean中:

private List<SelectItem> accountSelectItems;
private List accountSelectItems;
并在构造函数中设置它:

public AccountBean() {
    List<Account> list = new ArrayList<Account>(pemEJB.list(Account.class));
    Collections.sort(list, new AccountByActiveByName());
    this.accountSelectItems = new ArrayList<SelectItem>(list.size());

    for(Account row : list) {
        this.accountSelectItems.add(new SelectItem(row, StringUtil.prefixTruncate(row.getName(), MAX_ACCT_LENGTH, row.isActive())));
    }
}
publiccountbean(){
List List=newarraylist(pemEJB.List(Account.class));
排序(list,newaccountbyactivebyname());
this.accountSelectItems=newarraylist(list.size());
对于(科目行:列表){
this.accountSelectItems.add(新的SelectItem(行,StringUtil.prefixTruncate(行,getName(),最大帐户长度,行,isActive()));
}
}

另一方面,如果这是不断变化且需要更新的数据,则最好只在每个会话或每个请求中加载它,尽管您可以在应用程序范围内使用Quartz或其他计时器定期重新加载它,以降低从数据源读取的数据(如果实时性不是应用程序中此数据的基本要求)。如果正在重新加载数据,则如果正在使用application scope,则需要同步这些操作。

谢谢,灯泡刚刚熄灭。我陷入了beans back views的思维模式,从未考虑过简单地从所有视图中使用一个单独的请求bean。幸运的是,这是基于请求构建的要求。我认为一个单独的请求范围bean是最好的选择。