JSF 自定义日期转换器 - 线程安全吗?
我在 JSF 1.2 中创建了一个自定义 Converter
来转换 Date
对象。日期具有非常特殊的格式。我已经使用核心 Java SimpleDateFormat
类实现了我的转换器来进行转换,并使用下面代码注释中显示的格式化程序字符串。这一切都很好。
我的问题是关于线程安全的。 SimpleDateFormat
API 文档声明它不是线程安全的。因此,我为转换器对象的每个实例创建了一个单独的日期格式对象实例。但是,我不确定这是否足够。我的 DateFormat
对象存储为 DTGDateConverter
的成员。
问题:两个线程是否会同时访问 JSF 中 Converter 对象的同一个实例?
如果答案是肯定的,那么我的 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();
}
}
}
I have created a custom Converter
in JSF 1.2 to convert Date
objects. The dates have a very particular format. I have implemented my converter using the core Java SimpleDateFormat
class to do the conversion, using the formatter string shown in my code comments below. This all works fine.
My question is about thread safety. The SimpleDateFormat
API docs state that it is not thread safe. For that reason I have created a separate instance of the date format object for each instance of my converter object. However, I'm not sure if this is enough. My DateFormat
object is stored as a member of the DTGDateConverter
.
QUESTION: Will two threads every simultaneously access the same instance of a Converter object in JSF?
If the answer is yes, then my Converter is probably at risk.
/**
* <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();
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
取决于您如何使用转换器。如果您使用
,那么将为视图中的每个输入元素创建一个新实例,这是线程安全的(除了非常罕见的边缘情况外,最终用户在同一会话的两个浏览器选项卡中拥有两个相同的视图,并同时在两种观点)。
但是,如果您使用
,则同一实例将在整个应用程序的所有视图之间共享,因此不是线程安全的。
然而,每次创建转换器时,您都会克隆一个静态
DataFormat
实例。该部分已经不是线程安全的。您可能会面临克隆实例的风险,而其内部状态会因为已在其他地方使用而发生更改。此外,克隆现有实例并不一定比创建新实例便宜。我建议将其声明为线程本地(即在方法块内),无论您如何使用转换器。如果每次创建
DateFormat
的成本是一个主要问题(您是否分析过它?),那么请考虑将其替换为 JodaTime。Depends on how you use the converter. If you use
then a new instance will be created for every input element in view, which is threadsafe (expect of the very rare edge case that the enduser has two identical views in two browser tabs in the same session and simultaneously issues a postback on the both views).
If you however use
then the same instance will be shared across all views of the entire application, which is thus not threadsafe.
You're however cloning a static
DataFormat
instance everytime you create the converter. That part is already not threadsafe. You may risk that you're cloning an instance while its internal state is been changed because it's been used somewhere else. Also, cloning an existing instance isn't necessarily cheaper than creating a new instance.I would recommend to just declare it threadlocal (i.e. inside the method block), regardless of how you use the converter. If the expensiveness of creating the
DateFormat
everytime is a major concern (did you profile it?), then consider replacing it by JodaTime.是的,这里不是线程安全的。
将其放在方法本地并为每个线程创建实例
Yes it is not thread safe here.
Put it local to method and create instance per thread