JSF学习笔记
JSF事件驱动型的MVC框架,与流行的struts比较学习,易于理解。jsf component event事件是指从浏览器由用户操作触发的事件,Struts application event 是用Action来接受浏览器表单提交的事件,一个表单只能对应一个事件,application event和component event相比是一种粗粒度的事件。优点:事件粒度细化,方便实现。
JSF配置文件
一 web.xml
所有的请求都透过FacesServlet来处理,通过web.xml启用jsf服务
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>
javax.faces.webapp.FacesServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
</servlet>
JSF预设会读取faces-config.xml的定义,如果想要自行设置定义档的名称,我们是在web.xml中提供javax.faces.CONFIG_FILES参数,例如:
<web-app>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/beans.xml</param-value>
</context-param>
</web-app>
定义档可以有多个,中间以 "," 区隔,例如:
/WEB-INF/navigation.xml,/WEB-INF/beans.xml
二 faces-config.xml配置bean定义
<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>
<managed-bean-name>供JSF页面JSF表示语言使用
<managed-bean-class>对应java类
<managed-bean-scope>设定Bean的存活范围
类中获取Bean对象
如果要在其它类别中取得Bean对象,则可以先取得javax.faces.context.FacesContext,它代表了JSF目前的执行环境对象,接着尝试取得javax.faces.el.ValueBinding对象,从中取得指定的Bean对象,例如:
FacesContext context = FacesContext.getCurrentInstance();
ValueBinding binding =
context.getApplication().createValueBinding("#{user}");
UserBean user = (UserBean) binding.getValue(context);
如果只是要尝试取得Bean的某个属性,则可以如下:
FacesContext context = FacesContext.getCurrentInstance();
ValueBinding binding =
context.getApplication().createValueBinding("#{user.name}");
String name = (String) binding.getValue(context);
设置属性的初始值
如果有必要在启始Bean时,自动设置属性的初始值,则可以如下设定:
<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-property>
<property-name>name</property-name>
<value>caterpillar</value>
</managed-property>
<managed-property>
<property-name>password</property-name>
<value>123456</value>
</managed-property>
</managed-bean>
如果要设定属性为 null 值,则可以使用<null-value/>标签,例如:
<managed-property>
<property-name>name</property-name>
<null-value/>
</managed-property>
List或Map型态的属性
如果您的Bean上有接受List或Map型态的属性,则您也可以在组态档案中直接设定这些属性的值,一个例子如下:
<managed-bean>
<managed-bean-name>someBean</managed-bean-name>
<managed-bean-class>
onlyfun.caterpillar.SomeBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>someProperty</property-name>
<list-entries>
<value-class>java.lang.Integer</value-class>
<value>1</value>
<value>2</value>
<value>3</value>
</list-entries>
</managed-property>
</managed-bean>
这是一个设定接受List型态的属性,我们使用<list-entries>卷标指定将设定一个List对象,其中<value-class>指定将存入List的型态,而<value>指定其值,如果是基本型态,则会尝试使用指定的 <value-class>来作Wrapper类别。
设定Map的话,则是使用<map-entries>标签,例如:
<managed-bean>
<managed-bean-name>someBean</managed-bean-name>
<managed-bean-class>
onlyfun.caterpillar.SomeBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>someProperty</property-name>
<map-entries>
<value-class>java.lang.Integer</value-class>
<map-entry>
<key>someKey1</key>
<value>100</value>
</map-entry>
<map-entry>
<key>someKey2</key>
<value>200</value>
</map-entry>
</map-entries>
</managed-property>
</managed-bean>
由于Map对象是以key-value对的方式来存入,所以我们在每一个<map-entry>中使用<key>与<value>标签来分别指定。
您也可以直接像设定Bean一样,设定一个List或Map对象,例如在JSF附的范例中,有这样的设定:
<managed-bean>
<description>
Special expense item types
</description>
<managed-bean-name>specialTypes</managed-bean-name>
<managed-bean-class>
java.util.TreeMap
</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
<map-entries>
<value-class>java.lang.Integer</value-class>
<map-entry>
<key>Presentation Material</key>
<value>100</value>
</map-entry>
<map-entry>
<key>Software</key>
<value>101</value>
</map-entry>
<map-entry>
<key>Balloons</key>
<value>102</value>
</map-entry>
</map-entries>
三 faces-config.xml配置页面流转
<navigation-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>
JSF每一个视图(View)都有一个独特的识别(identifier),称之为View ID,在JSF中的View ID是从Web应用程序的环境相对路径开始计算,设定时都是以/作为开头,如果您请求时的路径是/pages/index.faces,则JSF会将扩展名改为/pages/index.jsp,以此作为view-id。
<from-view-id>是个选择性的定义,它规定了来源页面的条件,<navigation-case>中定义各种导览条件,<from-outcome>定义当窗体结果符合的条件时,各自改导向哪一个目的页面,目的页面是在<to-view-id>中定义。
可以在<navigation-case>中加入<from-action>,进一步规范窗体结果必须根据哪一个动作方法(action method),当中是使用 JSF Expression Language 来设定
<from-view-id>可以没有设定,表示来源网页不作限制,您也可以使用 * 显式的在定义档中表明
四 faces-config.xml配置自定义组件
<component>
<component-type>MyComponentType</component-type>
<component-class>customcomponent1.StrRepeat</component-class>
</component>
1. 创建组件类
a) 创建一个Java类,命名为StrRepeat,放在customcomponent1包中,修改其声明,使继承UIComponentBase类;
代码如程序清单所示
b) 定义text和number属性及其getter和setter方法;
c) 覆盖父类的encodeEnd()方法;[encodeBegin()、encodeChildren()和encodeEnd()]
public class StrRepeat extends UIComponentBase{
@Override
public String getFamily() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void encodeEnd(javax.faces.context.FacesContext context)
throws IOException {
ResponseWriter writer = context.getResponseWriter();
for(int i = 0; i < this.getNumber(); i++) {
writer.writeText(this.getText(), "text");
}
}
private String text;
public String getText() { return text; }
public void setText(String text) { this.text = text; }
private int number;
public int getNumber() { return number; }
public void setNumber(int number) { this.number = number; }
}
2. 创建标签处理器类
a) 创建一个Java类,命名为StrRepeatTag,放在customcomponent1包中,修改其声明,使继承UIComponentELTag类;
b) 创建text属性和number属性,以及它们的getter和setter方法;
c) 实现getComponentType()方法,使返回字符串“MyComponentType”;
d) 实现getRendererType()方法,使返回null;
e) 覆盖setProperties()方法。
代码如程序清单所示。
public class StrRepeatTag extends UIComponentELTag{
@Override
public String getComponentType() {
return "MyComponentType";
}
@Override
public String getRendererType() {
return null;
//throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected void setProperties(UIComponent component) {
super.setProperties(component);
component.getAttributes().put("text", this.getText());
component.getAttributes().put("number", this.getNumber());
}
private String text;
public String getText() { return text; }
public void setText(String text) { this.text = text; }
private int number;
public int getNumber() { return number; }
public void setNumber(int number) { this.number = number; }
}
4. 创建标签库描述符
a) 在NetBeans中新建一个文件,“类别”选择“Web”,“文件类型”选择“标记库描述符”;
b) NetBeans打开“新建标记库描述符”对话框,在其中输入文件名如mytld;
c) 在根元素<taglib>内添加一个<tag>元素;
d) 在<tag>元素内,用<name>元素定义标签名,用<tag-class>元素定义标签处理器类,用<attribute>元素定义标签属性。
代码如图所示
<tag>
<name>repeatText</name>
<tag-class>customcomponent1.StrRepeatTag</tag-class>
<attribute>
<name>text</name>
</attribute>
<attribute>
<name>number</name>
</attribute>
</tag>
5. 配置自定义UI组件
a) 打开faces-config.xml,并且切换到XML显示格式;
b) 将程序清单 10所示的代码添加到faces-config.xml中,置于根元素<faces-config>之下;
faces-config.xml的完整代码如图 13所示(折叠了managed-bean的定义),其中高亮部分为自定义UI组件的定义。
<component>
<component-type>MyComponentType</component-type>
<component-class>customcomponent1.StrRepeat</component-class>
</component>
6. 编写JSF页面
a) 打开NetBeans自动创建Page1页面,并切换到JSP显示方式;
b) 在<jsp:root>标签内加入前缀x的定义xmlns:x="/WEB-INF/tlds/mytld"
c) 在<webuijsf:form>标签之下加入<x:repeatText text="asdadf" number="3"/>
五 JSF的国际化
资源文件的名称是.properties,而内容是名称与值的配对,资源文件名称由basename加上语言与地区来组成,例如:
basename.properties、basename_en.properties、basename_zh_TW.properties
没有指定语言与地区的basename是预设的资源档名称,JSF会根据浏览器送来的Accept-Language header中的内容来决定该使用哪一个资源档名称,例如:
Accept-Language: zh_TW, en-US, en
如果找不到对应的讯息资源文件,则会使用预设的讯息资源文件。
使用<f:loadBundle>卷标来指定加载讯息资源,一个例子如下:
<f:loadBundle basename="messages" var="msgs"/>
<h:outputText value="#{msgs.titleText}"/>
如果您的浏览器预设接受zh_TW语系的话,则页面上就可以显示中文,否则预设将以英文显示,也就是messages.properties的内容,为了能显示多国语系,我们设定网页编码为UTF8。
<f:view>可以设定locale属性,直接指定所要使用的语系,例如:
<f:view locale="zh_TW">
<f:loadBundle basename="messages" var="msgs"/>
直接指定以上的话,则会使用繁体中文来显示,JSF会根据<f:loadBundle>的basename属性加上
JSF标签
一 JSF核心标签
二 JSF表单标签
详细资料http://www.web-tag.net
JSF生命周期(FacesServlet代码阅读)
private FacesContextFactory facesContextFactory = null;
private Lifecycle lifecycle = null;
Init方法:
facesContextFactory =
(FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
LifecycleFactory lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
String lifecycleId ;
// First look in the servlet init-param set
if (null == (lifecycleId = servletConfig.getInitParameter(LIFECYCLE_ID_ATTR))) {
// If not found, look in the context-param set
lifecycleId = servletConfig.getServletContext().getInitParameter (LIFECYCLE_ID_ATTR);
}
if (lifecycleId == null) {
lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
}
lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
service方法:
FacesContext context = facesContextFactory.getFacesContext (servletConfig.getServletContext(), request, response, lifecycle);
lifecycle.execute(context);
lifecycle.render(context);
Lifecycle类负责JSF请求处理的全过程,主要是通过执行其中的execute方法和render方法实现的
lifecycle.execute方法
private Phase[] phases = {
null, // ANY_PHASE placeholder, not a real Phase
new RestoreViewPhase(),
new ApplyRequestValuesPhase(),
new ProcessValidationsPhase(),
new UpdateModelValuesPhase(),
new InvokeApplicationPhase(),
response
};
for (int i = 1, len = phases.length -1 ; i < len; i++) {
if (context.getRenderResponse() ||
context.getResponseComplete()) {
break;
}
phases[i].doPhase(context, this, listeners.listIterator());
}
而在LifeCycle的execute方法中,是用一个for循环顺序执行几个Phase。在每一个Phase执行完之后,都会检查FaceContext对象中是否设置了停止后续处理直接呈现响应的标志(renderResponse)或者已经完成了响应无需后续处理也不需要经过呈现响应阶段了(responseComplete),如果标志为true,那么就不再执行后续Phase。
lifecycle.render方法
if (!context.getResponseComplete()) {
response.doPhase(context, this,listeners.listIterator());
}
在LifeCycle的render方法中,也会检查FacesContext的responseComplete状态,如果为true,那么就不再执行render Phase。于是我们此刻知道了在我们自己所写的一些代码或者JSF库里面的一些代码中,调用FacesContext的responseComplete方法和renderResponse得作用原理。
最后,可以看到对于每一个phase都调用了doPhase方法,同时把LifeCycle和FacesContext当做参数传入了。值得注意的是,所谓的phaseListener,也传入了phase的doPhase方法中,由此大约能够想明白这个“阶段监听器”的道理了。
JSF事件
一 分类
1 动作事件Action Event普通动作响应
command组件通过注册actionListener均可出发此事件侦听响应
2 即时事件Immediate Event立即处理,
不验证/转换/更新模型值(即bean不会保存属性)立即触发,需要一个为被注册的UI组件binding到后台bean中,常用来做bean层面即时服务,以执行action为主要目的。 input与command都有一个immediate属性,只要将其设定为true,就可以直接响应actionListener事件。
3 值改变事件Value Change Event
直接设定JSF输入元件的valueChangeListener属性
4 阶段事件Phase Event监听响应的JSF生命周期
JSF的运行大致分为6个阶段,每个阶段会触发该事件
二 编码
1 属性方式
<h:commandLink
actionListener="#{bean.linkActivated}"
actionListener="#{bean.linkActivated}">
</h:commandLink>
对应类的处理方法
public void listen(ActionEvent e) {}
2 tag方式
<h:commandButton image="mountrushmore.jpg" action="#{rushmore.act}">
<f:actionListener type="com.corejsf.RushmoreListener"/>
</h:commandButton>
对应的处理类
public class ChangeLocaleBean implements ActionListener {
public void processAction(ActionEvent e) {
FacesContext context = FacesContext.getCurrentInstance();
Map requestParams = context.getExternalContext().getRequestParameterMap();
String locale = (String) requestParams.get("locale");
if ("english".equals(locale))
context.getViewRoot().setLocale(Locale.UK);
else if("german".equals(locale))
context.getViewRoot().setLocale(Locale.GERMANY);
}
}
3 phase事件
<faces-config>
<lifecycle>
<phase-listener>com.corejsf.PhaseTracker</phase-listener>
</lifecycle>
</faces-config>
对应的处理类
public class PhaseTracker implements PhaseListener {
public PhaseId getPhaseId() {}
public void beforePhase(PhaseEvent e) {}
public void afterPhase(PhaseEvent e) {}
}