JSF(1)

1.1简介JSF

Web应用程序的开发与传统的单机程序开发在本质上存在着太多的差异,Web应用程序开发人员至今不可避免的必须处理HTTP的细节,而HTTP无状态的(statrless)本质,与传统应用程序必须维持程序运行过程中的信息有明显的违背,再则Web应用程序面对网站上不同的使用者同时的存取,其执行线程安全问题以及数据验证,转换处理等问题,又是复杂而且难以解决的。
另一方面,本质上是静态的HTML与本质上是动态的应用程序又是一项违背,这造成不可避免的处理网页设计的美术人员与程序设计人员必须被彼此加入至视图组件中的逻辑互相干扰,即便一些试图呈现逻辑以标签的方式呈现,试图展现对网页设计美术人员的亲切,但它终究必须牵涉到相关的流程逻辑。
有很多方案试着解决种种困境,而各自的着眼点各不相同,有的从程序设计人员的角度来解决,有的从网页设计人员的角度来解决,各种框架被提出,所造成的是各种不统一的标签与框架,为了促进产能的集成开发环境(IDE)难以整合这些标签与框架,另一方面,开发人员的学习负担也不断的加重,他们必须一人了解多个角色的工作。
JavaServer Faces的提出在试图解决这个问题,它试图在不同的角度上提供网页设计人员,应用程序设计人员,组件开发人员解决方案,让不同技术的人员可以彼此合作又不互相干扰,它综合了各家厂商现有的技术特点,由Java Community Process(JCP)团队研拟出来的一套标准,并在2004年三月发表了JavaServer Faces 1.0的实现成果。
从网页设计人员的角度来看,JavaServer Faces提供了一套像是新版本的HTML标签,它不是静态的,而是动态的,可以与后端的动态程序结合,但网页设计人员不需要理会后端的动态部分,网页设计人员甚至不太需要接触JSTL这类的标签,也可以动态的展现数据(就像是动态的查询表格的内容),JavaServer Faces 提供标准的标签,这也可以与网页编辑程序结合在一起,另一方面,JavaServer Faces也允许您自定义标签。
从应用程序设计人员的角度来看,JavaServer Faces 提供了一个与传统应用程序开发相类似的模型(当然因某些本质上的差异,模型稍有不同),他们可以基于时间驱动开发程序,不必关切HTTP的处理细节,如果必须处理一些视觉组件的 属性的话,他们也可以直接在整合开发环境上拖拉这些组件,点选设定组件属性,JavaServer Faces 甚至还在为应用程序设计人员处理了对象与字符串(HTTP传送本质上就是字符串)间不匹配的转换问题。
从UI组件开发人员的角度来看,他们可以设计通用的UI组件,让应用程序的开发产能提高,就如同在设计Swing组件等,UI开发人员可以独立开发,只要定义好相关的属性选项来调整细节,而不用受到网页设计人员或应用程序设计人员的干扰。
三个角色的知识领域和原则上可以互不干扰,根据自身的角色,只要了解其中一个知识领域就可以运用JavaServer Faces ,其他角色的知识领域您可以不用了解太多细节。
当然,就其中一个角色单独来看,JavaServer Faces隐藏了许多细节,若要全盘了解,其实JavaServer Faces 是复杂的,每一个处理的环境都值得深入探讨,所以学习JavaServer Faces,您要选择的是通盘了解还是从使用的角度来了解,就决定了学习时所要花费的心理。
要使用JSF,首先要取得JavaServer Faces参考实现(JavaServer Faces Reference Implementation),在将来,JSF会与Container整合在一起,届时您只要下载支持的Container,就可以使用JSF的功能。
请至JSF官网的下载区下载参考实现,在下载压缩文件并解压缩之后,将其lib目录下的jar文件复制到Web应用程序/WEB-INF/lib目录下,另外还需要jstl.jar与standard.jar 文件,这些文件您可以在sample目录下,解压缩当中一个范例,在它的/WEB-INF/lib目录下找到,将之一并复制至您的Web应用陈鼓的/WEB-INF/lib目录下,总共需要以下文件:

  • jsf-impl.jar
  • jsf-api.jar
  • commons-digester,jar
  • commons-collections.jar
  • commons-beanutils.jar
  • jstl.jar
  • standard.jar

接下来配置Web应用程序的web.xml,使用JSF时,所有的请求都通过FacesServlet来处理,可以如下定义:

  • web.xml
<?xml version = "1.0" encoding = "ISO-8859-1"?>
	<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation = "http://java.sun.com/xml/ns/j2ee 
		http://java.sun.com/xml/ns/j2ss/web-app_2_4.xsd"
		version="2.4">
		<description>JSF Demo</description>
		<display-name>JSF Demo</display-name>
		<servlet>
			<servlet-name>Faces Servlet</servlet-name>
			<servlet-class>javax.faces.webapp.FaceServlet</servlet-class>
			<load-on-startup>1</load-on-startup>
		</servlet>
		<servlet-mapping>
			<serlvet-name>Faces Servlet</servlet-name>
			<url-pattern>*.faces</url-pattern>
		</servlet-mapping>
		<welcome-file-list>
			<welcome-file>index.html</welcome-file>
		</welcome-file-list>
	</web-app>

在上面定义中,我们将所有.faces的请求交给FaceServlet来处理,FaceServlet会唤起相对的.jsp网页,例如请求是/index.faces的话,实际是会唤起/index.jsp网页,完成以上配置,就可以使用JSF了。

1.2 第一个JSF程序

现在可以开发一个简单的程序了,我们将设计一个简单的登入程序,使用者提交名称,之后由程序显示使用者名称及欢迎信息。
程序开发人员
先看看应用程序开发人员要作些什么事,我们编写一个简单的JavaBean;

  • UserBean.java
package onlyfun.caterpillar;
	public class UserBean{
		private String name;
		public void setName(String name){
			this.name=name;
		}
		public String getName(){
			return name;
		}
	}

这个Bean将存储使用者的 名称,编译好之后放置在/WEB-INF/classes下。接下来设计页面流程,我们将先显示一个登入网页/pages/index.jsp,使用者填入名称并提交表单,之后在/pages/welcome.jsp中显示Bean中的使用者名称与欢迎信息。
为了让JSF知道我们所设计的Bean以及页面流程,我们定义一个/WEB-INF/faces-config.xml:

  • faces-fonfig.xml
<?xml version="1.0"?>
	<!DOCTYPE face-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
	"http://java sun.com/dtd/web-facesconfig_1_0.dtd">
	<faces-config>
		<navigation-rule>
			<from-view-id>/pages/index.jsp</from>
   		<navigation-case>
   			<from-outcome>login</from-outcome>
   			<to-view-id>/pages/welcome.jsp</to-view-id>
   		</navigation-case>
   	</navigation-rule>
   	<managed-bean>
   		<manage-bean-name>user</managed-bean-name>
   		<managed-bean-class>
   			onlyfun.caterpillar.UserBean
   		</managed-bean-class>
   		<managed-bean-scope>session</managed-bean-scope>
   	</managed-bean>
   </faces-config>

在中,我们定义了页面流程,当请求来自中指定的页面,并且指定了中的为login时,则会将请求导向至所指定的页面。
在中我们可以统一管理我们的Bean,我们设定Bean对象的存活范围是session,也就是使用者开始浏览器与程序互动过程中都存活。
接下来要告诉网页设计人员的信息是,他们可以使用的Bean名称,即中设定的名称,以及上面所定义的页面流程。
网页设计人员
首先网页设计人员编写index.jsp网页:

  • index.jsp
<%@taglib uri = "http://java.sun.com/jsf/core" prefix = "f" %>
<%@taglib uri = "http://java.sun.com/jsf/html" prefix = "h" %>
<%@page contentType = "text/html;charset = GB2312" %>
<head>
<title>第一个JSF程序</title>
</head>
<body>
	<f:view>
		<h:form> 
			<h3>请输入您的名称<h3>
			名称:<h:commandButton value = "送出" action = "login" />
		<h:form>
	</f:view>
</body>
</html>

我们使用了JSF的core与html标签库,core是有关于UI组件的处理,而html则是有关HTML的进阶标签,
<f:view>与有类似的作用,当您要开始使用JSF组件时,这些组件一定要在<f:view>与</f:view>之间,就如同使用 HTML时,所有的标签一定要在与之间。
html标签库中几乎都是与html标签相关的进阶标签,<h:form>会产生一个表单,我们使用<h:inputText>来显示user这个Bean对象的name属性,而<h:commandButton>会产生一个提交按钮,我们在action属性中指定将根据之前定义的login页面流程中前往welcome.jsp页面。
网页设计人员不必理会表单传送注资后要作些什么,他只要设计好欢迎页面就好了:

  • welcome.jsp
<%@taglib uri = "http://java.sun.com/jsf/core" prefix ="f" %>
<%@taglib uri = "http://java.sun.com/jsf/html" prefix ="h" %>
<%@page contentType="text/html;charset = "GB2312"%>
<html>
<head>
<title>第一个JSF程序</title>
</head>
<body>
	<f:view>
		<h:outputText value = "#{user.name}" />您好!
		<h3>欢迎使用JavaServer Faces!</h3>
	</f:view>
</body>
</html>

这个网页没什么需要解释的了,如您所看到的,在网页上没有程序逻辑,网页设计人员所作的就是遵照页面流程,使用相关名称取出数据,而不用担心实际上程序是如何运行的。
接下来启动Container,连接上应用程序网址,例如:http://localhost:8080/jsfDemo/pages/index.faces,填入名称并提交表单,您的欢迎页面就会显示了。

1.3简单的导航 Navigation

在第一个JSF程序中,我们简单定义了页面的流程由index.jsp到welcome.jsp,接下来我们扩充程序,让它可以根据使用者输入的名称与密码是否正确,决定要显示欢迎信息或是将使用者送回原页面进行重新录入。

  • UserBean.java
package onlyfun.caterpillar;

public class UserBean{
   private String name;
   private String password;
   private String errMessage;
   
   public void setName (String name) {
   	this.name = name;
   }
   
   public String getName () {
   	return name; 
   }
   
   public void setPassword (String password) {
   	this.password = password;
   }
   
   public String getPassword () {
   	return password;
   }
   
   public void setErrMessage (String errMessage) {
   	this.errMessage = errMessage;
   }
   
   public String getErrMessage () {
   	return errMessage;
   }
   
   public String verify () {
   	if (!name.equals("justin"))||
   		!password.equals(“123456”)) {
   			errMessage = "名称或密码错误";
   			return "failure" ;
   		}
   	else {
   			return "success";
   	}
   }
}

在UserBean中,我们增加了密码与错误信息属性,在verify()方法中,我们就按察使用者名称与密码,它传回一个字符串,“failure”表示登入错误, 并会设定错误信息,而“sucess” 表示登入正确,这个传回的字符串将决定页面的流程。
接下来我们修改一下faces-config.xml 中的页面流程定义:

  • faces-config.xml
<?xml version = "1.0" ?>
	<!DOCTYPE faces-config PUBLIC 
	“-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0 //EN”
	"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
	<faces-config>
		<navigation-rule>
			<from-view-id>/pages/index.jsp</from-view-id> 
			<navigation-case>
				<from-outcome>success</from-outcome>	
				<to-view-id>/pages/welcome.jsp</to-view-id>
			</navigation-case>
			<navigation-case>
				<from-outcome>failure</from-outcome>	
				<to-view-id>/pages/index.jsp</to-view-id>
			</navigation-case>
		</navigation-rule>
		<managed-bean>
			<managed-bean-name>user</managed-bean-name>
			<managed-bean-class>onlyfun.caterpillar.UserBean</managed-bean-class>
			<managed-bean-scope>session</managed-bean-scope>
		</managed-bean>
	</faces-config>

根据上面的定义,当传回的字符串是“success”时,将前往welcome.jsp,如果时“failure”的话,将送回index.jsp。
接下来告诉网页设计人员Bean名称与相关属性,以及决定页面流程的verify名称,我们修改index.jsp如下:

  • index.jsp
<%@taglib uri = "http://java.sun.com/jsf/core" prefix ="f" %>
<%@taglib uri = "http://java.sun.com/jsf/html" prefix ="h" %>
<%@page contentType="text/html;charset = "GB2312"%>
<html>
<head>
<title>第一个JSF程序</title>
</head>
<body>
	<f:view>
		<h:form>
			<h3>请输入您的名称</h3>
			<h:outputText value="#{user.errMessage}"><p>
			名称:<h:inputText value="#{user.name}"><p>
			密码:<h:inputSecret value="#{user.password}"><p>
			<h:commandButton value="送出" action = "#{user.verify}" />
		</h:form>
	</f:view>
</body>
</html>

当要根据verify运行结果来决定页面流程时,action属性中使用JSF Expression Language “#{user.verify}”, 如此JSF就知道必须根据verify传回的结果来导航页面。
<h:outputText>可以去除指定的Bean之属性值,当使用者因验证错误而被送回原页面时,这个错误信息就可以显示在页面上。

1.4导航规则设置

在JSF中时根据faces-config.xml中设定,以决定在符合的条件成立时,该连接至拿一个页面,一个基本的设定如下:

...
	<navigation-rule>
		<from-view-id>/pages/index.jsp</from-view-id>
		<navigation-case>
			<from-outcome>success</from-outcome>
			<to-view-id>/pages/welcome.jsp</to-view-id>	
		</navigation-case>
		<navigation-case>
			<from-outcome>failure</from-outcome>
			<to-view-id>/pages/index.jsp</to-view-id>	
		</navigation-case>
	</navigation-rule>

对于JSF,每一个视图(View)都有一个独特(indentifier),称之为View ID,在JSF中的View ID是从Web应用程序的环境相对路径开始计算,设定时都是以/作为开头,如果您请求时的路径时/pages/index.faces,则JSF会将副文件名改为/pages/index.jsp,以此作为view-id。
在中的是个选择性的定义,它规定了来源页面的条件,中定义各种导览条件, 定义当表单结果符合条件时,各自改导向哪个目的页面,目的页面是在中定义。
您还可以在中加入,进一步规范表单结果必须根据哪一个动作方法(action method),当中是使用JSF Expression Language 来设定,例如:

...
	<navigetion-rule>
		<from-view-id>/pages/index.jsp</from-view-id>
		<navigation-case>
			<from-action>#{user,verify}</from-action>
			<from-outcome>success</from-outcome>
			<to-view-id>/pages/welcome.jsp</to-view-id>
		</navigation-case>
		...
	</navigation-rule>
	...

您的来源网页可能是某个特定的模组,例如在/admin/下的页面,您可以在中使用wildcards(通配符),也就是使用*字符,例如:

...
	<navigation-rule>
		<from-view-id>/admin/*</from-view-id>
		<navigtion-case>
			<from-action>#{user.verify}</from-action>
			<from-outcome>success</from-outcome>
			<to-view-id>/pages/welcome.jsp</to-view-id>
		</navigation-case>
		...
		</navigation-rule>

在上面的设定中,只要来源网页是从/admin来的,都可以开始测试接下来的
如果没有设定,表示来源网页不做限制,您也可以使用*显式的在定义文件中表明,例如:

	<navigation-rule>
		<from-view-id>/*</from-view-id>
		<navigation-case>
		...
	</navigation-rule>

或者是 这样:

<navigation-rule>
		<from-view-id>*</from-view-id>
		<navigation-case>
		...
	</navigation-rule>

1.5 JSF Expression Language

JSF Expression Language 搭配JSF 标签来使用,是用来存取世俗据对象的简易语言。
JSF EL是以#开始,将变量或运算式防止在 { 与 } 之间,例如:#{someBeanName}
变量名称可以是faces-config.xml中定义的名称,如果是Bean的话,可以通过使用"."运算符来存取它的属性,例如:

	<f:view>
		<h:outputText value="#{userBean.name}" />
	</f:view>

在JSF标签的属性上," 与 " (或 '与 ')之间如果含有EL,则会加以运算,您也可可以这么使用它:

<f:view>
	名称,年龄:<h:outputText value="#{userBean.name} , #{userBean.age}"/>
</f:view>

一个执行的结果可能是这样显示的:

	名称,年龄:Justin,29

EL的变量名也可以程序执行过程中所声明的名称,或是JSF EL预定义的隐含对象,例如下面的程序使用param隐含对象来取得使用者输入的参数:

<%@taglib uri = "http://java.sun.com/jsf/core" prefix = "f" %>
<%@taglib uri = "http://java.sun.com/jsf/html" prefix = "h" %>
<%@page contentType = "text/html:charset=GB2312" %>

<html>
<head>
<title></title>
</head>
<body>
	<f:view>
		<b>您好,<h:outputText value = "#{param.name}" / > </b>
	<f:view>
</body>
</html>

param是JSF EL预定义的隐含对象变量,它代表reques所有参数的集合,实际上是一个java.util.Map类型对象,JSF所提供的隐含对象,大致上应用于JSF隐含对象,不过JSF隐含对象一处了pageScope与pageContext,而增加了facesContext与view,他们分别应用于javax.faces.context,FacesContext与javax.faces.component.UIViewRoot。
对于Map类型对象,我们可以使用“.”运算符指定key值取出对应的value,也可以使用 [ 与 ] 来指定,例如:

	<f:view>
		<b>您好,<h:outputText value = "#{param['name']}" / > </b>
	</f:view>

在 [ 与 ] 之间,也可以放置其它的变量值, 例如:

	<f:view>
		<h:outputText value = "#{someBean.someMap[user.name]}">
	</f:view>

如果变量是List类型或者阵列的话,则可以在[]中指定索引,例如:

<f:view>
	<h:outputText value = "#{someBean.someList[0]}" />
	<h:outputText value = "#{someBean.someArray[1]}" />
	<h:outputText value = " #{someBean.someListOrArray[user.age]}"/>
</f:view>

您也可以指定字面常数,对于true,false,字符串,数字,JSF EL会尝试进行转换,例如:

...
 <h:outputText value = "#{true}" />
 ...
 <h:outputText value = "#{'This is a test'} " />
 ...

如果要输出字符串,必须以单引号’ 或双引号 " 括住,如此才不会被认为是变量名称。
在声明变量名称时,要留意不可与JSF的保留字或关键字同名,例如不可取以下名称:

true false null div mod and or not eq ne It gt ge instancef empty

使用EL,您可以直接实行一些算术运算,逻辑运算与关系运算,其使用就如同在一般常见的程序语言中之运算。
算术运算符:加(+),减(-),乘(*),除( / or div ) 余除 ( % or mod)

运算式结果
#{ 1 }1
#{ 1 + 2 }3
#{ 1.2 + 2.3 }3.5
#{ 1.2E4 +1.4 }12001.4
#{ -4 - 2}-6
#{ 21 * 2 }42
#{ 3 / 4 }0.75
#{ 3 div 4 }0.75,除法
#{ 3 / 0 }Infinity
#{ 10 % 4 }2
#{ 10 mod 4}2 余除
#{ (1==2)?3:4}4

如同在Java语法一样(expression ? resultl : result2 )是个三元运算, expression 为true 显示 resultl , false 显示 result2。
逻辑运算有:and (或&&),or(或!!!), not(或!)。一些例子为:

运算式结果
#{ true and false}false
#{ true or false}true
#{ not true}false

关系运算有: 小于Less-than( < or It) ,大于Greater-than( > or gt) ,小于或等于Less-than-or-equal(<= or le), 大于或等于Greater-than-or-equal (>=or ge) ,等于Equal(=or eq),不等于Not Equal(!= or ne), 由英文名称可以得到lt,gt 等运算符之缩写词,以下是tomcat中的列子:

运算式结果
#{ 1 < 2}true
#{ 1 lt 2}true
#{ 1 > (4/2) }false
#{ 1 < (4/2) }false
#{ 4.0 >= 3 }true
#{ 4.0 ge 3 }true
#{ 4 <= 3 }false
#{ 4 le 3 }false
#{ 100.0 == 100 }true
#{ 100.0 eq 100 }true
#{ (10*10) != 100 }false
#{ (10*10) ne 100 }false

左边是运算符的使用方式,右边的是运算结果,关系运算也可以用来比较字符或字符串,按字典顺序来决定比较结果,例如:

运算式结果
#{ ‘a’ < ‘b’ }true
#{ ‘hip’ > ‘hit’}false
#{ ‘4’ > ‘3’ }true

EL运算符的执行优先顺序与Java运算符对应,如果有疑虑的话,也可以使用括号()来自行决定先后顺序

1.6国际化信息

JSF的国际化(Internnationnalization)信息处理是基于Java对国际化的支持,您可以在一个信息资源文件中统一管理信息资源,资源文件的名称是.properties,而内容是名称与值配对,例如:

  • messages.properties
titleText  = JSF Demo
	hinText = Please input your name and password
	nameText = name 
	passText = password
	commandText = Submit

资源文件名称由basename加上语言与地区来组成,例如:

*basename.properties
*basename_en.properties
*basename_zh_CN.properties

没有指定语言与地区的basename是预定义的资源文件名称,JSF会根据浏览器送来的Accept-Language header 中的内容来决定该使用哪一个资源文件名称,例如:

Accept-Language:zh_CN,en-US,en

如果浏览器送来这些header,则预定义会使用简体中文,接着美式英文,再来是英文语系,如果找不到对应的信息资源文件,则会使用预定义的信息资源文件。
由于信息资源文件必须先是ISO-8859-1编码,所以对于非西方语系的处理,必须先将之转换为Java Unicode Escape格式,例如您可以先在信息资源文件中写一下内容:

  • messages_zh_CN.txt
titleText = JSF示范
	hitText = 请输入名称与密码
	nameText = 名称
	passText = 密码
	commandText = 送出

然后再JDK的工具程序native2ascii来转换,例如:

native2ascii -encoding GB2312 message_zh_CN.txt messages_zh_CN.propertise

转换后的内容如下:

titleText = JSF\u793a\u8303
hintText = \u8bf7\u8f93\u5165\u540d\u79f0\u4e03\u5bc6\u7801
nameText = \u540d\u79f0
passText = \u5bc6\u7801
commandText = \u9001\u51fa

接下来您可以使用<f:loadBundle>标签来指定载入信息资源,一个例子如下:

  • index.jsp
<%@taglib uri = "http://java.sun.com/jsf/core" prefix = "f" %>
<%@taglib uri = "http://java.sun.com/jsf/html" prefix = "h" %>
<5@page contenType = "text/html:charset = UTF8">
<f:view>
<f:loadBundle basename = "messages" var = "msgs" />
<html>
<head>
<title><h:outputText value = "#{msgs.titleText}"/></title>
</head>
<body>
   <h:form>
   	<h3><h:outputText value = "#{msgs.hinText}" / ></h3>
   	<h:outputText value = "#{msgs.nameText}" />
   		<h:inputText value = "#{user.name}" />
   	<h:outputText value = "#{msgs.passText}" />
   		<h:inputSecret value = "#{user.password}" />
   	<h:commandButton value = "#{msgs.commandText}"
   		actionListener = "#{user.verify}"
   		action = "#{user.outcome}"/>
   </h:form>
</body>
</html>	
<f:view>

如此一来,如果您的浏览器预定义接受zh_CN语系的话,则页面上就可以显示中文,否则预定义将以英文显示,也就是messages.properties的内容,为了能显示多普通话语系,我们设定网页编码为UTF8。
<f:view>可以设定locale属性,直接指定所要使用的语系,例如:

<f:view locale = "zh_CN">
	<f:loadBundle basename = "message" var = "msgs"/>

直接指定以上的话,则会使用简体中文来显示,JSF会根据<f:loadBundle>的basename属性加上<f:view>的locale属性来决定要使用哪一个信息资源文件,就上例而言,就是使用message_zh_CN.properties,如果设定为一下的话久会使用message_en.propertise:

<f:view locale = "en">
	<f:loadBundle basename = "messages" var = "msgs" />

您也可以在faces-config.xml中设定语系,例如:

<faces-config>
	<application>
		<local-config>
			<default-locale>en</default-locale>
			<supported-locale>zh_CN</supported-locale>
		</local-config>
	</application>
	......
</faces-config>

在一定有一个,而可以有好几个,这告诉JSF您的应用程序支持哪些语系。
当然,如果您可以提供一个选项让使用者选择自己的语系会是更好的方式,例如根据user这个Bean的locale属性来决定页面语系:

<f:view locale = "#{user.locale}">
	<f:loadBundle basename = "message" var =”msgs“ />

在页面中设定一个表单,可以让使用者选择语系,例如设定单选按钮:

<h:selectOneRadio value = "#{user.locale}">
	<f:selectItem itemValue = "zh_CN"
		itemLabel = "#{msgs.zh_CNText}"/>
	<f:selectItem itemValue = "en"
		itemLabel = "#{msgs.enText}"/>
</h:selectOneRadio>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值