经典Java EE企业应用实战
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.3 使用转换器完成类型转换

虽然JSF框架希望模拟传统C/S架构应用的开发模式,但它本质上依然是基于请求-响应架构的Web框架,因此所有请求参数必然是字符串类型。但JSF将这些请求参数绑定到托管Bean的属性时,托管Bean属性的类型是多种多样的,因此必然涉及类型转换。

3.3.1 转换器概述、用途

所有的MVC框架,都需要负责解析HTTP请求参数,并将请求参数传给控制器组件。此时问题出现了:HTTP请求参数都是字符串类型,但Java是强类型的语言,因此MVC框架必须将这些字符串参数转换成相应的数据类型——这个工作是所有的MVC框架都应该提供的功能。

表现层数据的流向以及所需的类型转换如图3.16所示。

图3.16 表现层数据的流向和类型转换

当某个组件绑定到一个托管Bean的属性时,该组件的值通常有两个数据类型:

字符串类型:当该组件在页面上呈现时,该组件的值只能是字符串类型,用户可以读取或修改该组件的值。

真实类型:当把该组件的值绑定到托管Bean的属性时,该组件的值就会转换为与托管Bean的属性相同的类型。

JSF提供了非常强大的类型转换机制,当开发者将页面上UI组件的值绑定到托管Bean的属性时,JSF框架将会在后台完成类型转换。正如前面示例程序中看到的,当托管Bean中的price属性是Double类型时,JSF将会自动把绑定到price属性的UI组件的值转换为Double类型。上面这个转换过程对于普通开发者来说完全透明,实际上这得益于JSF内置的各种转换器。

除此之外,JSF还提供了很好的扩展性,开发者可以非常简单地开发出自己的类型转换器,完成字符串和自定义复合类型之间的转换(例如,完成字符串到Person实例的转换)。

正如大家都知道的,Java的类型转换完全可能出现错误,如果类型转换出现错误,JSF的消息机制就会起作用,JSF的消息机制会自动处理该异常,并且在页面上生成提示信息。

表现层的另一个数据处理是输入校验,输入校验可分为客户端校验和服务器端校验两种。客户端校验和服务器端校验都是必不可少的,二者分别完成不同的过滤。

客户端校验进行基本校验,如检验非空字段是否为空,数字格式是否正确等。客户端校验主要用来过滤用户的误操作。客户端校验的作用是:拒绝误操作输入提交到服务器处理,降低服务器端负担。

服务器端校验也必不可少,服务器端校验防止非法数据进入程序,导致程序异常、底层数据库异常。服务器端校验是保证程序有效运行及数据完整的手段。

下一节将会详细介绍输入校验的知识。

3.3.2 JSF内建转换器

通常来说,当把组件的值或组件本身绑定到托管Bean的属性时,JSF会自动完成两种类型之间的转换,无须开发者关心。例如,我们把UISelectBoolean组件绑定到boolean类型的Bean属性,JSF就可以自动完成所需的类型转换。除此之外,JSF中的一些特殊组件必须绑定到特定类型的属性,就拿UISelectBoolean组件来说,它只能被绑定到java.lang.Boolean类型或boolean类型的属性。

在默认情况下,JSF依靠内建转换器来完成自动类型转换,JSF内建支持的转换器位于javax.faces.convert包下,该包提供了如下类型转换器:

BigDecimalConverter

BigIntegerConverter

BooleanConverter

ByteConverter

CharacterConverter

DateTimeConverter

DoubleConverter

EnumConverter

FloatConverter

IntegerConverter

LongConverter

NumberConverter

ShortConverter

上面每个内置转换器都关联一个标准的错误消息,如果用户输入的数据进行类型转换失败时,该错误消息可提示用户重新输入。关于内置转换器的错误消息后面会有更详细的介绍。

对于上面绝大部分转换器,开发者无须显式地调用它们。但由于DateTimeConverter、NumberConverter两个转换器可能需要重复显式使用,因此JSF为它们提供了两个专门的标签:

<f:convertDateTime…/>:为DateTimeConverter转换器提供的标签。

<f:convertNumber…/>:为NumberConverter转换器提供的标签。

除此之外,JSF还提供了一个<f:converter…/>标签,该标签通常用于使用自定义转换器,后面会有更详细的解释。

3.3.3 使用转换器

JSF使用内建转换器有两种情形:

自动使用:当开发者将UI组件的值绑定到Bean属性时,系统会自动进行类型转换。

通过专用标签引用:通过<f:convertDateTime…/>、<f:convertNumber…/>两个标签进行显式转换。

对于自动使用JSF内建转换器的情形,本书前面已有不少示例,此处不再赘述。下面介绍通过<f:convertDateTime…/>、<f:convertNumber…/>两个标签使用转换器的情形。

使用<f:convertDateTime…/>标签可以完成日期时间和字符串之间的转换。使用该标签时可以指定如下属性:

dateStyle:指定日期风格,该属性值与java.text.DateFormat中定义的日期风格对应。该属性的属性值支持short、medium、long和full等值。如果没有指定该属性值,则默认使用default。需要指出的是,只有当该标签的type属性为date且未指定pattern属性时,该属性才会起作用。

locale:指定格式化该日期、时间所使用的Locale,如果没有指定该属性,则使用由FacesContext.getLocale()方法返回的Locale作为默认值。

pattern:指定对日期格式进行格式化的模式字符串。该参数的作用和SimpleDateFormat提供的format方法中pattern参数的作用一样。

timeStyle:指定时间风格,该属性值与java.text.DateFormat中定义的时间风格对应。该属性的属性值支持short、medium、long和full等值。如果没有指定该属性值,则默认使用default。需要指出的是,只有当该标签的type属性为time且未指定pattern属性时,该属性才会起作用。

timeZone:该属性指定该日期、时间对应的市区信息。

type:指定该字符串是包含日期、时间还是同时包含。该属性的有效值为"date"、"time"和"both,默认值是"date"。

binding:该属性用于将转换器本身绑定到托管Bean的属性。

提示

JSF的DateTimeConverter转换器主要依赖于Java的DateFormat、SimpleDateFormat两个工具类,因此它的很多属性都与它们方法的参数相对应,读者可以参考疯狂Java体系的《疯狂Java讲义》来了解关于这些参数的更多知识。

例如,有如下示例:

      <h:outputText value="user. birthday ">
          <f:convertDateTime dateStyle="full"/>
      </h:outputText>

上面代码的作用就相当于在页面上输出如下Java代码:

      DateFormat.getDateInstance(DateFormat.FULL).format(user.birthday)

再看下面示例:

      <h:outputText value="user.birthday">
          <f:convertDateTime timeStyle="short" type="time"/>
      </h:outputText>

上面代码的作用就相当于在页面上输出如下Java代码:

      DateFormat.getTimeInstance(TimeFormat.SHORT).format(user.birthday)

大部分时候,我们可以为<f:convertDatetime…/>标签指定pattern属性,通过该属性既可完成日期、时间到字符串的转换,也可轻松完成字符串到日期、时间的转换。

下面示例既使用<f:convertDateTime…/>将输入组件中的字符串输入转换为日期,也使用<f:convertDateTime…/>对输出组件的日期、时间进行格式化。首先看如下页面代码。

程序清单:codes\03\3.3\DatetimeConverter\add.jsp

      <h:form>
      用户名:<h:inputText value="#{userBean.name}"/><br/>
      生日:<h:inputText value="#{userBean.birthday}">
          <!-- 指定用户按指定格式输入日期、时间 -->
          <f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss"/>
      </h:inputText>(请按yyyy-MM-dd HH:mm:ss格式输入)<br/>
      <h:commandButton value="添加" action="#{userBean.add}"/>
      </h:form>

上面页面代码中粗体字代码的转换器标签指定了pattern参数,这意味着只要用户输入的字符串能匹配该模式字符串,该转换器就可将用户输入转换成日期、时间。当然,由于此处我们显式使用DateTimeConverter对userBean.birthday进行转换,这就要求userBean托管Bean的birthday属性必须是java.util.Date类型。

处理完成后,另一个页面上输出userBean托管Bean的birthday属性,如果输出时只要保留日期部分,无须时间部分,我们也可使用如下转换器对该属性进行格式化输出。

程序清单:codes\03\3.3\DatetimeConverter\show.jsp

      <h1>添加成功</h1>
      <h:outputText value="#{userBean.name}"/><br/>
      <h:outputText value="#{userBean.birthday}">
          <!-- 转换出来的日期、时间将只包括日期部分 -->
          <f:convertDateTime dateStyle="full"
              type="date"/>
      </h:outputText>

上面粗体字代码指定type="date",这意味格式化输出该日期、时间时将只保留日期部分,且采用DateFormat.FULL的日期风格进行格式化。

如果在第一个页面的生日框中按yyyy-MM-dd HH:mm:ss格式输入2000-12-12 20:12:22字符串,JSF应用转换成功后将看到如图3.17所示的界面。

图3.17 使用DateTimeConverter转换器

从上面示例可以看出,使用DateTimeConverter可以完成两方面工作:

字符串到日期的转换。

日期到字符串的转换,该转换实质上就是一种格式化输出。

与DateTimeConverter转换器类似的是,NumberConverter转换器也有类似的功能,只是该转换器是完成数值和字符串之间的转换。JSF为该转换器提供了<f:convertNumber…/>标签,该标签支持如下属性:

currencyCode:该属性值必须为ISO 4217货币代码之一,仅当格式化货币时该属性才有效。

currencySymbol:该属性指定货币符号,仅当格式化货币时该属性才有效。

groupingUsed:指定格式化数值时是否使用分组符。该属性值只能是true或false,默认是true。

integerOnly:该属性指定是否只解析处理数值的整数部分。

locale:指定解析或格式化数值所使用的Locale。

maxFractionDigits:指定小数部分最多保留多少位。

maxIntegerDigits:指定整数部分最多保留多少位。

minFractionDigits:指定小数部分最少保留多少位。

minIntegerDigits:指定整数部分最少保留多少位。

pattern:该属性用于指定一个模式字符串,指定转换器将根据自定义模式字符串进行格式化。

type:指定解析和格式化该数值的风格。该属性支持number、currency或percentage三个值之一,它们与NumberFormat提供的getNumberInstance、getCurrencyInstance、getPercentInstance三个方法相对应。

binding:该属性用于将转换器本身绑定到托管Bean的属性。

提示

JSF的NumberConverter转换器主要依赖于Java的NumberFormat及其子类DecimalFormat,因此它的很多属性都与它们方法的参数相对应,读者可以参考疯狂Java体系的《疯狂Java讲义》中关于NumberFormat的知识。

例如,如下代码使用<f:convertNumber…/>标签来执行转换。

      <h:outputText value="bookBean.price">
          <f:convertNumber type="currency"/>
      </outputText>

下面示例示范使用了pattern模式字符串对数值进行转换的情形。我们首先提供如下托管Bean类。

程序清单:codes\03\3.3\NumberConverter\WEB-INF\src\org\crazyit\jsf\BookBean.java

      public class BookBean
      {
          private String name;
          private Double price;
          //无参数的构造器
          public BookBean()
          {
          }
          //初始化全部属性的构造器
          public BookBean(String name , Double price)
          {
              this.name = name;
              this.price = price;
          }
          //省略name属性的setter和getter方法
          …
          //省略price属性的setter和getter方法
          …
      }

上面托管Bean中的price属性是Double类型,我们在faces-config.xml文件中配置该托管Bean,并为它的name、price属性指定初始值。下面是该托管Bean的配置代码。

程序清单:codes\03\3.3\NumberConverter\WEB-INF\faces-config.xml

      <!-- 配置托管Bean -->
      <managed-bean>
          <!-- 设置托管Bean的名字 -->
          <managed-bean-name>bookBean</managed-bean-name>
          <!-- 设置托管Bean的实现类 -->
          <managed-bean-class>org.crazyit.jsf.BookBean</managed-bean-class>
          <!-- 设置托管Bean实例的有效范围 -->
          <managed-bean-scope>request</managed-bean-scope>
          <managed-property>
              <property-name>name</property-name>
              <value>疯狂Java讲义</value>
          </managed-property>
          <managed-property>
              <property-name>price</property-name>
              <value>99</value>
          </managed-property>
      </managed-bean>

上面配置文件中粗体字代码指定了price属性的初始值是99,如果我们希望对price属性进行格式化输出,格式化后整数部分占4位、小数部分占2位,则可以使用如下JSP页面代码。

程序清单:codes\03\3.3\NumberConverter\show.jsp

      <h1>查看图书</h1>
      <h:outputText value="#{bookBean.name}"/><br/>
      <h:outputText value="#{bookBean.price}">
          <!-- 转换出来的日期、时间将只包括日期部分 -->
          <f:convertNumber pattern="0000.00"/>
      </h:outputText>

上面页面中为<f:convertNumber…/>指定了pattern="¥0000.00",这就可以将price属性格式化成整数部分占4位、小数部分占2位的字符串。在浏览器中浏览该页面会看到如图3.18所示的效果。

图3.18 使用NumberConverter执行格式化输出

3.3.4 转换失败后的错误消息

当JSF转换器执行类型转换时不可避免地会遇到转换失败的情形,当类型转换失败时JSF会将转换失败后的错误显示在页面上。

为了在页面上显示错误消息,可以借助于JSF提供的如下两个标签:

<h:message…/>:显示指定UI组件类型转换失败、输入校验失败后的错误消息。

<h:messages…/>:显示某个页面中所有组件转换失败、输入校验失败后的错误消息。

使用<h:message…/>标签时可指定如下属性:

for:该属性是必填属性,该属性指定某个UI组件的id属性值,该属性指定该标签用于输出哪个UI组件转换失败、校验失败后的提示信息。

rendered:该属性指定该标签显示的错误消息是否输出。

binding:该属性用于将该标签本身绑定到托管Bean的属性。

使用<h:messages…/>标签时可指定如下属性:

globalOnly:指定是否仅仅显示全局消息。

rendered:该属性指定该标签显示的错误消息是否输出。

layout:指定采用何种方式来对多个组件类型转换失败、输入校验失败后的错误消息进行布局,该属性支持table、list两个属性值。如果指定为table,则意味着使用HTML表格来进行布局;如果指定为list,则意味着使用HTML列表方式来进行布局。

binding:该属性用于将该标签本身绑定到托管Bean的属性。

例如,前面示例中添加用户时如果输入的用户生日不满足要求的格式,那么JSF进行类型转换时将会失败,只要使用<h:message…/>标签输出该UI组件类型转换失败的提示信息即可。

修改后的add.jsp页面代码如下:

程序清单:codes\03\3.3\ErrorMessage\add.jsp

      <h1>添加用户</h1>
      <h:form>
      用户名:<h:inputText value="#{userBean.name}"/><br/>
      生日:<h:inputText value="#{userBean.birthday}" id="birthday">
          <!-- 指定用户按指定格式输入日期、时间 -->
          <f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss"/>
      </h:inputText>(请按yyyy-MM-dd HH:mm:ss格式输入)
      <!-- 指定输出birthday组件的值类型转换失败、校验失败的错误信息 -->
      <h:message for="birthday"/><br/>
      <h:commandButton value="添加" action="#{userBean.add}"/>
      </h:form>

上面页面代码中粗体字代码就用于输出birthday的类型转换失败、校验失败的错误提示信息,如果输入的字符串不能成功转换,将看到如图3.19所示的页面。

图3.19 转换失败后的错误提示信息

从图3.19可以看出,<h:message.../>输出了birthday组件的值转换失败的提示信息,不过该提示信息全部是英文,这对简体中文应用来说并不适合。

JSF应用UI组件的值类型转换失败、输入校验失败后的错误消息可来自3个方面:

来自JSF为内置转换器、校验器提供的默认消息。

来自开发者通过自定义消息资源文件提供的错误消息。

来自各UI组件标签的converterMessage属性指定的错误消息。

正如前面指出的,JSF所有内置转换器都有默认的错误提示消息,当转换失败时,<h:message…/>将会输出这些默认的错误提示消息。关于JSF内置转换器的标准错误消息可以参考JSF规范的2.5.2.4节。下面列出JSF所有内置转换器、校验器的标准错误消息。

javax.faces.component.UIInput.CONVERSION={0}: Conversion error occurred

javax.faces.component.UIInput.REQUIRED={0}: Validation Error: Value is required

javax.faces.component.UIInput.UPDATE={0}: An error occurred when processing your submitted information

javax.faces.component.UISelectOne.INVALID={0}: Validation Error: Value is not valid

javax.faces.component.UISelectMany.INVALID={0}: Validation Error: Value is not valid

javax.faces.converter.BigDecimalConverter.DECIMAL={2}: ''{0}'' must be a signed decimal number.

javax.faces.converter.BigDecimalConverter.DECIMAL_detail={2}: ''{0}'' must be a signed decimal number consisting of zero or more digits, that may be followed by a decimal point and fraction. Example: {1}

javax.faces.converter.BigIntegerConverter.BIGINTEGER={2}: ''{0}'' must be a number consisting of one or more digits.

javax.faces.converter.BigIntegerConverter.BIGINTEGER_detail={2}: ''{0}'' must be a number consisting of one or more digits. Example: {1}

javax.faces.converter.BooleanConverter.BOOLEAN={1}: ''{0}'' must be 'true' or 'false'.

javax.faces.converter.BooleanConverter.BOOLEAN_detail={1}: ''{0}'' must be 'true' or 'false'. Any value other than 'true' will evaluate to 'false'.

javax.faces.converter.ByteConverter.BYTE={2}: ''{0}'' must be a number between 0 and 255.

javax.faces.converter.ByteConverter.BYTE_detail={2}: ''{0}'' must be a number between 0 and 255. Example: {1}

javax.faces.converter.CharacterConverter.CHARACTER={1}: ''{0}'' must be a valid character.

javax.faces.converter.CharacterConverter.CHARACTER_detail={1}: ''{0}'' must be a valid ASCII character.

javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}'' could not be understood as a date.

javax.faces.converter.DateTimeConverter.DATE_detail={2}: ''{0}'' could not be understood as a date. Example: {1}

javax.faces.converter.DateTimeConverter.TIME={2}: ''{0}'' could not be understood as a time.

javax.faces.converter.DateTimeConverter.TIME_detail={2}: ''{0}'' could not be understood as a time. Example: {1}

javax.faces.converter.DateTimeConverter.DATETIME={2}: ''{0}'' could not be understood as a date and time.

javax.faces.converter.DateTimeConverter.DATETIME_detail={2}: ''{0}'' could not be understood as a date and time. Example: {1}

javax.faces.converter.DateTimeConverter.PATTERN_TYPE={1}: A 'pattern' or 'type' attribute must be specified to convert the value ''{0}''.

javax.faces.converter.DoubleConverter.DOUBLE={2}: ''{0}'' must be a number consisting of one or more digits.

javax.faces.converter.DoubleConverter.DOUBLE_detail={2}: ''{0}'' must be a number between 4.9E-324 and 1.7976931348623157E308 Example: {1}

javax.faces.converter.EnumConverter.ENUM={2}: ''{0}'' must be convertible to an enum.

javax.faces.converter.EnumConverter.ENUM_detail={2}: ''{0}'' must be convertible to an enum from the enum that contains the constant ''{1}''.

javax.faces.converter.EnumConverter.ENUM_NO_CLASS={1}: ''{0}'' must be convertible to an enum from the enum, but no enum class provided.

javax.faces.converter.EnumConverter.ENUM_NO_CLASS_detail={1}: ''{0}'' must be convertible to an enum from the enum, but no enum class provided.

javax.faces.converter.FloatConverter.FLOAT={2}: ''{0}'' must be a number consisting of one or more digits.

javax.faces.converter.FloatConverter.FLOAT_detail={2}: ''{0}'' must be a number between 1.4E-45 and 3.4028235E38 Example: {1}

javax.faces.converter.IntegerConverter.INTEGER={2}: ''{0}'' must be a number consisting of one or more digits.

javax.faces.converter.IntegerConverter.INTEGER_detail={2}: ''{0}'' must be a number between-2147483648 and 2147483647 Example: {1}

javax.faces.converter.LongConverter.LONG={2}: ''{0}'' must be a number consisting of one or more digits.

javax.faces.converter.LongConverter.LONG_detail={2}: ''{0}'' must be a number between -9223372036854775808 to 9223372036854775807 Example: {1}

javax.faces.converter.NumberConverter.CURRENCY={2}: ''{0}'' could not be understood as a currency value.

javax.faces.converter.NumberConverter.CURRENCY_detail={2}: ''{0}'' could not be understood as a currency value. Example: {1}

javax.faces.converter.NumberConverter.PERCENT={2}: ''{0}'' could not be understood as a percentage.

javax.faces.converter.NumberConverter.PERCENT_detail={2}: ''{0}'' could not be understood as a percentage. Example: {1}

javax.faces.converter.NumberConverter.NUMBER={2}: ''{0}'' is not a number.

javax.faces.converter.NumberConverter.NUMBER_detail={2}: ''{0}'' is not a number. Example:{1}

javax.faces.converter.NumberConverter.PATTERN={2}: ''{0}'' is not a number pattern.

javax.faces.converter.NumberConverter.PATTERN_detail={2}: ''{0}'' is not a number. pattern. Example: {1}

javax.faces.converter.ShortConverter.SHORT={2}: ''{0}'' must be a number consisting of one or more digits.

javax.faces.converter.ShortConverter.SHORT_detail={2}: ''{0}'' must be a number between -32768 and 32767 Example: {1}

javax.faces.converter.STRING={1}: Could not convert ''{0}'' to a string.

javax.faces.validator.DoubleRangeValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of "{0}"

javax.faces.validator.DoubleRangeValidator.MINIMUM={1}: Validation Error: Value is less than allowable minimum of "{0}"

javax.faces.validator.DoubleRangeValidator.NOT_IN_RANGE={2}: Validation Error:Specified attribute is not between the expected values of {0} and {1}.

javax.faces.validator.DoubleRangeValidator.TYPE={0}: Validation Error: Value is not of the correct type

javax.faces.validator.LengthValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of "{0}"

javax.faces.validator.LengthValidator.MINIMUM={1}: Validation Error: Value is less than allowable minimum of "{0}"

javax.faces.validator.LongRangeValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of "{0}"

javax.faces.validator.LongRangeValidator.MINIMUM={1}: Validation Error Value is less than allowable minimum of "{0}"

javax.faces.validator.LongRangeValidator.NOT_IN_RANGE={2}: Validation Error: Specified attribute is not between the expected values of {0} and {1}.

javax.faces.validator.LongRangeValidator.TYPE={0}: Validation Error: Value is not of the correct type The following message keys are deprecated:

javax.faces.validator.NOT_IN_RANGE=Specified attribute is not between the expected values of{0} and {1}

上面这些错误消息的key的含义都非常直观,相信读者可以很容易理解它们。如果要覆盖标准的错误消息,可以通过提供自己的错误消息资源文件来实现。例如,提供如下错误消息资源文件。

程序清单:codes\03\3.3\CustomErrorMessage\WEB-INF\src\crazyitMessages.properties

      javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}''不是一个有效的日期!
      javax.faces.converter.DateTimeConverter.DATE_detail={2}: ''{0}'' 不是一个有效的日期!应按如下格式输入: {1}
      javax.faces.converter.DateTimeConverter.TIME={2}: ''{0}'' 不是一个有效的时间!
      javax.faces.converter.DateTimeConverter.TIME_detail={2}: ''{0}'' 不是一个有效的时间!应按如下格式输入: {1}
      javax.faces.converter.DateTimeConverter.DATETIME={2}: ''{0}'' 不是一个有效的日期时间!
      javax.faces.converter.DateTimeConverter.DATETIME_detail={2}:''{0}'' 不是一个有效的日期时间!应按如下格式输入: {1}

使用native2ascii.exe工具处理上面消息资源文件,处理后生成的新文件名为“crazyitMessages_zh_CN.properties”。提供了上面消息资源文件之后,还需要在faces-config.xml文件中配置加载它,并配置系统支持的语言、国家Locale。下面是我们在faces-config.xml文件中增加的配置片段。

程序清单:codes\03\3.3\CustomErrorMessage\WEB-INF\struts-config.xml

      <application>
          <!-- 配置自定义错误消息资源 -->
          <message-bundle>crazyitMessages</message-bundle>
          <!-- 配置该应用所支持的语言、国家Locale -->
          <locale-config>
              <default-locale>en_US</default-locale>
              <supported-locale>zh_CN</supported-locale>
          </locale-config>
      </application>

上面配置片段中粗体字代码指定系统将使用baseName为crazyitMessages的资源文件作为错误消息,因此如果需要系统支持多个国家、语言系统,那就应该为多个国家提供相应的资源文件。例如,本应用提供了crazyitMessages_zh_CN.properties(简体中文)、crazyitMessages_en_US.properties(美式英语)两份文件。

经过上面配置之后,如果用户添加用户时输入的生日不符合规则,将看到如图3.20所示的页面。

图3.20 自定义错误消息

除此之外,还可以为<h:inputText…/>标签添加converterMessage属性来指定类型转换失败后的错误消息。converterMessage属性支持使用表达式语言,因此它既可以直接指定固定的字符串,也可以通过表达式语言来指定国际化消息。

例如,我们把该页面代码改为如下形式。

程序清单:codes\03\3.3\AssignErrorMessage\WEB-INF\struts-config.xml

      <h:form prependId="false">
      用户名:<h:inputText value="#{userBean.name}"/><br/>
      生日:<h:inputText value="#{userBean.birthday}" id="birthday"
          converterMessage="您输入的生日格式无效!">
          <!-- 指定用户按指定格式输入日期、时间 -->
          <f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss"/>
      </h:inputText>(请按yyyy-MM-dd HH:mm:ss格式输入)
      <!-- 指定输出birthday组件的值类型转换失败、校验失败的错误信息 -->
      <h:message for="birthday" style="color:red;font-weight:bold"/><br/>
      <h:commandButton value="添加" action="#{userBean.add}"/>
      </h:form>

上面页面代码中通过converterMessage属性临时指定了转换失败的错误提示信息——这条错误消息的等级最高,它会覆盖其他错误消息。除此之外,我们还为<h:message…/>标签指定了CSS样式用于重定义错误消息的外观。

如果用户输入的生日转换失败,将会看到如图3.21所示的页面。

图3.21 临时指定的错误消息

从上面介绍可以看出,JSF应用中通过converterMessage属性临时指定错误消息的覆盖等级最高,该属性指定的错误消息会覆盖其他所有的错误消息。