JSF自定义日期转换器 - 是否是线程安全的?

时间:2010-12-13 15:39:46

标签: java jsf thread-safety converter simpledateformat

我在JSF 1.2中创建了一个自定义Converter来转换Date个对象。日期有一个非常特殊的格式。我使用核心Java SimpleDateFormat类实现了我的转换器,使用下面的代码注释中显示的格式化程序字符串进行转换。一切正常。

我的问题是线程安全。 SimpleDateFormat API文档声明它不是线程安全的。出于这个原因,我为我的转换器对象的每个实例创建了一个单独的日期格式对象实例。但是,我不确定这是否足够。我的DateFormat对象存储为DTGDateConverter的成员。

问题:两个线程是否会同时访问JSF中的Converter对象的同一个实例?

如果答案是肯定的,那么我的转换器可能存在风险。

/**
 * <p>JSF Converter used to convert from java.util.Date to a string.
 * The SimpleDateFormat format used is: ddHHmm'Z'MMMyy.</p>
 * 
 * <p>Example: October 31st 2010 at 23:59 formats to 312359ZOCT10</p>
 * 
 * @author JTOUGH
 */
public class DTGDateConverter implements Converter {

    private static final Logger logger = 
        LoggerFactory.getLogger(DTGDateConverter.class);

    private static final String EMPTY_STRING = "";

    private static final DateFormat DTG_DATE_FORMAT = 
        MyFormatterUtilities.createDTGInstance();

    // The 'format' family of core Java classes are NOT thread-safe.
    // Each instance of this class needs its own DateFormat object or
    // runs the risk of two request threads accessing it at the same time.
    private final DateFormat df = (DateFormat)DTG_DATE_FORMAT.clone();

    @Override
    public Object getAsObject(
            FacesContext context, 
            UIComponent component, 
            String stringValue)
            throws ConverterException {
        Date date = null;
        // Prevent ParseException when an empty form field is submitted
        // for conversion
        if (stringValue == null || stringValue.equals(EMPTY_STRING)) {
            date = null;
        } else {
            try {
                date = df.parse(stringValue);
            } catch (ParseException e) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Unable to convert string to Date object", e);
                }
                date = null;
            }
        }
        return date;
    }

    @Override
    public String getAsString(
            FacesContext context, 
            UIComponent component, 
            Object objectValue)
            throws ConverterException {
        if (objectValue == null) {
            return null;
        } else if (!(objectValue instanceof Date)) {
            throw new IllegalArgumentException(
                "objectValue is not a Date object");
        } else {
            // Use 'toUpperCase()' to fix mixed case string returned
            // from 'MMM' portion of date format
            return df.format(objectValue).toUpperCase();
        }
    }

}

2 个答案:

答案 0 :(得分:7)

  

两个线程是否会同时访问JSF中的Converter对象的同一个实例?

取决于您如何使用转换器。如果你使用

<h:inputWhatever>
    <f:converter converterId="converterId" />
</h:inputWhatever>

然后将为视图中的每个输入元素创建一个新实例,这是线程安全的(期望极端罕见的边缘情况,即最终用户在同一会话中的两个浏览器选项卡中有两个相同的视图,同时在两种观点)。

但是如果你使用

<h:inputWhatever converter="#{applicationBean.converter}" />

然后将在整个应用程序的所有视图中共享相同的实例,因此这不是线程安全的。

然而,每次创建转换器时,您都会克隆静态DataFormat实例。那部分已经不是线程安全的。在内部状态发生变化时,您可能会冒着克隆实例的风险,因为它已在其他地方使用过。此外,克隆现有实例并不一定比创建新实例便宜。

我建议只是将它声明为threadlocal(即在方法块内),无论你如何使用转换器。如果每次创建DateFormat的代价是一个主要问题(您是否对其进行了分析?),请考虑将其替换为JodaTime

答案 1 :(得分:2)

  

日期格式未同步。它   建议单独创建   每个线程的格式实例。如果   多个线程访问一种格式   同时,它必须同步   外部。

是的,这里不是线程安全的。

将它放在方法本地并为每个线程创建实例