技术开发 频道

借助 Ajax 自动保存 JSF 表单: 第 2 部分

  在多线程环境中工作

  之前介绍过的 DataMapRepository 类及其基类 LinkedHashMap 都不是线程安全的。解决这个问题的第一种方法是使用 java.util.Collections 的 synchronizedMap() 方法,它返回线程安全的包装器地图。对表单数据存储库的情况而言,除了确保它在多线程的服务器环境中被安全使用之外,最好是构建一个定制的包装器类,由它控制如何访问存储库。

  为数据存储库使用线程安全的包装器

  清单 9 给出了这个包装器类,在示例应用程序中称为 RepositoryWrapper。它具有一个名为 repository 的字段,类型为 DataMapRepository。getRepository() 方法返回专用 repository 的副本,而 setRepository() 也会创建一个新副本。这些副本都包含与原始存储库相同的数据地图对象,这一点没错,因为数据地图在用表单数据创建和填充之后不会被修改。

  清单 9. 数据存储库的包装器类

package autosave;
...
public class RepositoryWrapper implements java.io.Serializable {
    
private DataMapRepository repository;
    
    
public RepositoryWrapper() {
        repository
= new DataMapRepository();
    }
    
    
public synchronized DataMapRepository getRepository() {
        return
new DataMapRepository(repository);
    }

    
public synchronized void setRepository(
            DataMapRepository repository) {
        
if (repository != null)
            this.repository
= new DataMapRepository(repository);
        
else
            this.repository.clear();
    }
    ...
}

  RepositoryWrapper 类包含线程安全的方法以便访问数据地图和包装了的存储库的 maxDataMaps 属性(参见 清单 10)。getDataMap() 和 setDataMap() 方法可以为给定的 FacesContext 检索和存储数据地图。若针对 ctx 参数的数据地图已经存在,hasDataMap() 方法返回 true,而且 clearDataMap() 方法会从存储库删除数据地图。

  清单 10. 用于访问数据存储库的线程安全的方法

package autosave;
...
import javax.faces.context.FacesContext;
...
import java.util.Map;

public class RepositoryWrapper implements java.io.Serializable {
    ...
    
public synchronized Map<String, Object> getDataMap(
            FacesContext ctx) {
        return repository.getDataMap(ctx);
    }
    
    
public synchronized void setDataMap(FacesContext ctx,
            Map
<String, Object> dataMap) {
        repository.setDataMap(ctx, dataMap);
    }
    
    
public synchronized boolean hasDataMap(FacesContext ctx) {
        return getDataMap(ctx) !
= null;
    }
    
    
public synchronized void clearDataMap(FacesContext ctx) {
        setDataMap(ctx,
null);
    }
    
    
public synchronized int getMaxDataMaps() {
        return repository.getMaxDataMaps();
    }

    
public synchronized void setMaxDataMaps(int maxDataMaps) {
        repository.setMaxDataMaps(maxDataMaps);
    }
    ...
}

  将包装器配置成 JSF 受管 bean

  所有包含表单数据的地图对象都保存在存储库实例中,该实例的包装器被配置成 faces-config.xml 文件中的一个 JSF 受管 bean(参见 清单 11)。指定的 bean 的名称为 repositoryWrapper,作用域为 application。JSF 配置文件也可用于为数据存储库的 maxDataMaps 属性提供值。

  清单 11. 将存储库包装器配置成 JSF 受管 bean

<faces-config>
    ...
    
<managed-bean>
        
<managed-bean-name>repositoryWrapper</managed-bean-name>
        
<managed-bean-class>autosave.RepositoryWrapper</managed-bean-class>
        
<managed-bean-scope>application</managed-bean-scope>
        ...
        
<managed-property>
            
<property-name>maxDataMaps</property-name>
            
<value>100</value>
        
</managed-property>
    
</managed-bean>
    ...
</faces-config>

  RepositoryWrapper 类有两个静态方法,可以返回受管 bean 实例。这些方法(如 清单 12 所示)可以使用 FacesContext 或 ServletContext 从 application 作用域检索包装器 bean。

  清单 12. 获得受管 bean 实例

package autosave;

import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;

import javax.servlet.ServletContext;
...
public class RepositoryWrapper implements java.io.Serializable {
    ...
    
public static RepositoryWrapper getManagedBean(FacesContext ctx) {
        Application app
= ctx.getApplication();
        ValueBinding vb
= app.createValueBinding("#{repositoryWrapper}");
        return (RepositoryWrapper) vb.getValue(ctx);
    }

    
public static RepositoryWrapper getManagedBean(ServletContext ctx) {
        return (RepositoryWrapper) ctx.getAttribute(
"repositoryWrapper");
    }

}
0
相关文章