What's JAVA ?

发现在我走近java之后,感觉自己什么都不会了!
posts - 13, comments - 7, trackbacks - 0, articles - 1
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

2005年12月16日

线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类。

Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap

Collection接口
  Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
  所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
  如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
    Iterator it = collection.iterator(); // 获得一个迭代子
    while(it.hasNext()) {
      Object obj = it.next(); // 得到下一个元素
    }
  由Collection接口派生的两个接口是List和Set。

List接口
  List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
  除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
  实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

LinkedList类
  LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
  注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
    List list = Collections.synchronizedList(new LinkedList(...));

ArrayList类
  ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
  每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
  和LinkedList一样,ArrayList也是非同步的(unsynchronized)。

Vector类
  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。

Stack 类
  Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Set接口
  Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
  很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
  请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

Map接口
  请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。

Hashtable类
  Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
  添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
    Hashtable numbers = new Hashtable();
    numbers.put(“one”, new Integer(1));
    numbers.put(“two”, new Integer(2));
    numbers.put(“three”, new Integer(3));
  要取出一个数,比如2,用相应的key:
    Integer n = (Integer)numbers.get(“two”);
    System.out.println(“two = ” + n);
  由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。
  如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
  Hashtable是同步的。

HashMap类
  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。

WeakHashMap类
  WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。

总结
  如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
  如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
  要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
  尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。


posted @ 2006-02-16 09:54 Eddie Lee 阅读(710) | 评论 (0)编辑 收藏

tree2是Myfaces的tomahawk控件中的一个,在使用tomahawk控件之前一定先确定已经按照myfaces的要求配置好web.xml文件。
Myfaces网站tomahawk栏目的Extensions Filter 子栏目中有如下一段话:
If you just use standard JSF component, but don't use any MyFaces' extended component (beginning with t:),
then you don't need the Extensions Filter.
However, if you use some of the MyFaces' extended components like t:inputFileUpload, t:inputHTtml, t:inputCalendar, ...
then you most likely need to have this filter configured in your webapp.
大概意思是,如果你在项目中没有使用到Myfaces的扩展组件(t:开头的),则你不需要配置这个Filter.
可是,如果你用了Myfaces的扩展组件,你必须为你的web程序配置这个Filter.
配置如下:
<filter>
    
<filter-name>MyFacesExtensionsFilter</filter-name>
    
<filter-class>org.apache.myfaces.component.html.util.ExtensionsFilter</filter-class>
    
<init-param>
        
<param-name>maxFileSize</param-name>
        
<param-value>20m</param-value>
        
<description>Set the size limit for uploaded files.
            Format: 10 - 10 bytes
                    10k - 10 KB
                    10m - 10 MB
                    1g - 1 GB
        
</description>
    
</init-param>
</filter>

<!-- extension mapping for adding <script/>, <link/>, and other resource tags to JSF-pages  -->
<filter-mapping>
    
<filter-name>MyFacesExtensionsFilter</filter-name>
    
<!-- servlet-name must match the name of your javax.faces.webapp.FacesServlet entry -->
    
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>

<!-- extension mapping for serving page-independent resources (javascript, stylesheets, images, etc.)  -->
<filter-mapping>
    
<filter-name>MyFacesExtensionsFilter</filter-name>
    
<url-pattern>/faces/myFacesExtensionResource/*</ url-pattern>
</filter-mapping>

要使用MyFaces的tomahawk控件,要在页面引用<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>。
在页面中添加tree2的标签:

<t:tree2 id="sortTree" var="node" varNodeToggler="t" showRootNode="false" value="#{tree.treeData}">

</t:tree2>
建立托管Bean,用于填充tree。
public class TreeBacker implements Serializable
{
    
public TreeNode getTreeData ( )
    
{
        TreeNode treeData 
= new TreeNodeBase ( "foo-folder" , "Inbox" , false ) ;

        TreeNodeBase personNode 
= new TreeNodeBase("person""Eddie L"false);
        
//personNode.getChildren().add(new TreeNodeBase("document","Eddie doc one", true));
        TreeNodeBase doc = new TreeNodeBase("document","Eddie doc one"true);
        doc.setIdentifier(
"No.1");
        personNode.getChildren().add(doc);
        treeData.getChildren().add(personNode);
        
        System.out.println ( 
"Create Tree ..ok" ) ;
        
return treeData ;
    }

}

其中TreeNodeBase personNode = new TreeNodeBase("person", "Eddie L", false);中的"person"的作用是表示渲染得时候显示什么样式,
跟页面中<f:facet name="person"></f:facet>相对应。
在页面中

<f:facet name="person">
    
<h:panelGroup>
        
<f:facet name="expand">
            
<t:graphicImage value="../images/person.png" rendered="#{t.nodeExpanded}" border="0" />
        
</f:facet>
        
<f:facet name="collapse">
            
<t:graphicImage value="../images/person.png" rendered="#{!t.nodeExpanded}" border="0" />
        
</f:facet>
        
<h:outputText value="#{node.description}" styleClass="nodeFolder" />
    
</h:panelGroup>
</f:facet>

<t:graphicImage value="../images/person.png" rendered="#{t.nodeExpanded}" border="0" />显示Tree每一个节点的图片。rendered属性的意思是是否渲染的意思,
jsf中渲染的意思是把jsf组件树当前的状态转换成html。在jsf的生命周期里,渲染响应在最后一个周期,rendered值为false在渲染相应这个周期就不运行。
在处理叶子节点上,需要特殊的注意。因为对叶子节点是需要操作的。可能需要点击叶子节点连接到一个页面,或者执行某些action等。
<f:facet name="document">
    
<h:panelGroup>
        
<h:commandLink immediate="true" styleClass="#{t.nodeSelected ? 'documentSelected':'document'}" actionListener="#{t.setNodeSelected}" action="#{tree.okListener}" id="clickbutton">
            
<t:graphicImage value="../images/document.png" border="0" />
            
<h:outputText value="#{node.description}" />
            
<f:param name="docNum" value="#{node.identifier}" />
        
</h:commandLink>
    
</h:panelGroup>
</f:facet>
commandLink标记可以有n个参数,参数分为参数名和参数值。在程序里可以读取。
编写action事件:okListener
FacesContext context = FacesContext.getCurrentInstance ( ) ;//取得上下文

String a 
= context.getExternalContext ( ).getRequestParameterMap ( )
                .get ( 
"docNum" ).toString ( ) ;

a的值就是docNum的值。
identifier的值在创建树的时候一起加进去的:
TreeNodeBase doc = new TreeNodeBase("document","Eddie doc one", true);
doc.setIdentifier("No.1");

这样。一个tree2的应用基本就算ok了。

posted @ 2006-01-05 21:34 Eddie Lee 阅读(10975) | 评论 (1)编辑 收藏

FacesContext保存了组件书的一个引用,由它为请求进行渲染;
贯穿整个请求处理生命周期,你可以通过FacesContext访问组件树;
而且贯穿整个生命周期,你可以更改或替换他的当前组件树。

posted @ 2005-12-31 11:31 Eddie Lee 阅读(866) | 评论 (0)编辑 收藏

把这个属性设置成True,他就会直接执行Action的方法,不执行一些get方法,不会读取一些必要的信息。

posted @ 2005-12-29 21:56 Eddie Lee 阅读(2572) | 评论 (0)编辑 收藏

jsf提供了<h:selectOneMenu>标签,来定义单选下拉框,直接写在jsp的文件里的做法,很简单,不写了。

在托管Bean里初始化这个标签的时候,可以使用SelectItem[] 数组类型,也可以使用List 、Map等类型。

具体做法如下:
private List<SelectItem> okItems = null;
    
/**
     * 
@return Returns the okItems.
     
*/

public List < SelectItem > getOkItems ( )
    
{
        
this.okItems = new LinkedList< SelectItem >();
        
this.okItems.add(new SelectItem("a","aaa"));
        
this.okItems.add(new SelectItem("b","bbb"));
        
return okItems ;
    }

然后在jsp页面中:
<h:selectOneMenu id="sortpid" value="#{booksort.sortPid}">
    
<f:selectItems value="#{booksort.okItems}" />
</h:selectOneMenu>

selectItems他拥有一个value这个属性,赋值为托管Bean里的初始化下拉框的那个属性,也就是okItems。

posted @ 2005-12-29 00:09 Eddie Lee 阅读(3107) | 评论 (2)编辑 收藏

      用MyEclipse创建一个Web Project的项目,然后让这个项目支持MyFaces。运行,结果不报错而且只显示出一个空白的页面,右键查看网页的源代码,里面只显示出了网页的一些基本html代码,没有出现任何有关jsf的一些内容。然而,我只让他支持JSF,却一切正常,什么东西都显示出来了。

      发现这个问题以后,去csdn和QQ的群里面问了很多人,都没有出现过这些问题。我都开始纳闷了。。。。。

      然后我做了一个实验,我把MyFaces官方的blank代码直接复制到tomcat的webapp目录却正常显示了。我把这个blank的代码全部复制到MyEclipse的项目里,然后同步服务器,同样的问题就又出现了,还是出现了一个大白页面。。这个问题整整让我郁闷了好几天,到底是我配置的问题呢,还是MyEclipse插件的问题呢?因为我把这个blank的代码直接放到webapp目录里,却一切正常,我想这说明我的配置应该是没有错误的。那问题就出在MyEclipse上了。

      然后我在Eclipse的选项里面查看MyEclipse的选项,找到了Project Capabilities里有对jsf项目的一些jar的设置,在这个选项里面我发现在下方有一个copy jsf jar and package Tlds to /WEB-INF/lib, if not present 的复选框,把这个勾打掉,然后再用MyEclipse建立web Project项目,然后支持MyFaces,自己手工的把blank的所有jar都复制到/WEB-INF/lib里,同步服务器。哇靠了! 亲爱的MyFaces组件都出来了!

      到目前为止,我还没有弄明白,为什么用MyEclipse带的所有jar就会出现这些问题,我也仔细察看了,新建的MyFaces项目里的classpath都没有jsf-api.jar,最后还使用了最土的办法,自己手工copy那些jar。

哪位大兄弟也遇到过这样的问题,还有更好的解决方法,一定要告诉我啊。还有就是,错误的根在哪里呢?我还没弄明白。我觉得MyEclipse他自带的那些jar按道理来说不应该有问题的。可能原因还在其它的地方。

posted @ 2005-12-18 15:39 Eddie Lee 阅读(5811) | 评论 (3)编辑 收藏

JavaServer Pages(JSF) 在 Java 的 WEB 编程中已经被认为是下一个重大的事件。通过 JSF ,你可以在网页上使用 WEB 组件,来捕获用户行为产生的事件。不远的将来,Java 工具将支持这个技术,开发 WEB 应用程序将与我们现在开发 SWING 程序类似:拖放控件、写事件侦听器。本文是一个 JSF 的简要介绍,并且提供一个 JSF 的例子,用来展示 JSF 的事件驱动特性。要理解本文,您需要对 servlets, JSP, JavaBeans, 与标签库有一定的理解。

  首先,一个 JSF 应用就是一个 servlet/JSP 应用。它有一个配置描述符,有 JSP 页面、客户定制标签、静态资源等等。不同的是,JSF 应用是事件驱动的。你通过写一个事件侦听类来决定应用程序的行为。以下建立一个 JSF 应用所需要的几个步骤:
1、建立 JSP 页面,用 JSF 组件包装 HTML 元素。
2、写一个 JavaBean 用来保持用户输入与组件数据的状态。
3、写一个事件侦听器来决定当某事件发生时应该有什么反映,比如用户点击了一个按钮或者提交了表单。JSF 支持两个事件:ActionEvent 与 ValueChangeEvent 。ActionEvent 是针对用户提交表单与点击按钮的,而 ValueChangeEvent 是当一个 JSF 组件改变了时触发。

  现在,让我们来看一下 JSF 动作的细节。


JSF 怎样工作

  JSP 页面是 JSF 应用的用户接口。每个页面包括一些 JSF 组件用来描述 WEB 控件,如表单、输入框、按钮等等。组件可以嵌入另一个组件中,正如输入框可以在表单中。每个 JSP 页面就这样表示为组件树。JaveBeans 从用户的请求中获取数据并存储。

  这是有意思的部分:每当用户做任何事情,如点击按钮或者提交表单,都有事件产生。然后事件消息通过 HTTP 传到服务器。在服务器端,是一个配置了叫做 Faces servlet 的特殊 servlet 的 WEB 容器。Faces servlet(javax.faces.webapp.FacesServlet)是所有 JSF 应用的引擎。每个 JSF 应用在 WEB 容器中都有独立的 Faces servlet 。另一个重要的对象是 javax.faces.context.FacesContext , 它包括了所有关于当前用户请求的必要信息。

  Faces servlet 的后台处理是相当复杂的。然而你没有必要了解这些细节,只需要记住:Faces servlet 为 JSP 页面创建了组件树,对组件树的控制又对应着事件。Faces servlet 知道怎么去创建组件树,因为它已经访问了当前应用中所有的 JSP 页面。Faces servlet 还会创建一个 Event 对象,并把它传递给所有注册过的侦听器。你可以通过与当前请求相对应的 FacesContext 得到这个页面的组件树。

  客户端浏览器上 WEB 控件产生的事件,被包含在一个 HTTP 请求中,放在一起还有如浏览器类型、请求地址等其它信息。因此,所有需要 Faces servlet 处理的请求必须指向这个 servlet 。那你怎样通过调用 Faces servelt 来处理每个 HTTP 请求呢?很容易,只需要在配置描述符里用一个 servlet-mapping 元素把一个特殊的 URL 式样映射到 Faces servlet。通常,你会用到 /faces/* 样式,如下所示:
<!-- Faces Servlet -->
<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<!-- Faces Servlet Mapping -->
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
</servlet-mapping>
  请求地址必须包含有在 <url-pattern> 元素中描述的样式。这个要求不容易达到。另外也需要注意的是 <servlet> 元素,它包含 Faces servlet ,有一个 <load-on-startup> 元素,用来确是否应用程序第一次启动时 servlet 是否加载。
  为了捕获组件产生的事件,你需要为这个组件写一个侦听器,并把它注册给这个组件。通过在表示组件的客户端标签中嵌入 <action_listener> 元素能做到这一点。例如,为了让一个名叫 jsfApp.MyActionListener 的事件侦听器,来捕获一个名叫 submitButton 的命令按钮产生的事件,在你的 JSP 页面中写如下的代码即可:
<h:command_button id="submitButton" label="Add" commandName="submit" >
  <f:action_listener type="jsfApp.MyActionListener" />
</h:command_button>
  一个 action listener 必须实现 javax.faces.event.ActionListener 接口,而一个 value-changed listener 必须实现 java.faces.event.ValueChangedLister 接口。下面让我们来创建一个简单的 JSF 应用,以展现 JSF 是怎么样事件驱动的。


一个简单的 JSF 应用

  我们将创建一个简单的应用,它可以实现对二个数字相加。为了运行这个应用,你需要准备 TOMCAT5 与 JSF v1.0 EA4(包含在 Java Web Services Developer Pack (JWSDP) 1.2中)。这个应用程序包括:
adder.jsp  JSP 页面。
NumberBean 存放用户数据的 JavaBean
MyActionListener 事件侦听器
web.xml 配置描述文件
  为了使这个应用能正常工作,还需要几个 jar 文件,包括 JSF 标准实现与其它类库。如果你安装了 JWSDP 1.2,你就可以在 jsflib 目录下找到所需要的这些文件。把这些 .jar 文件拷贝到 WEB-INF/lib 目录下。下面是整个的 .jar 与 .tld 文件列表:
jsf-api.jar 包含有 Faces servlet 与其它相关 javax.faces 包下面的类
jfs-ri.jar 是 JSF 的参考实现
jstl_el.jar
standard.jar
  此外,一个 JSF 的应用还需要如下的类库,它们是 Apache Jakarta 项目的一部分:
commons-beanutils.jar
commons-digester.jar
commons-logging.jar is


以下的几小段讨论这个 JSF 示例的每个部分。最后的一小段,“编译与运行”,解释 JSF 应用怎么样运行。

创建目录结构

  首先为你的 JSF 应用创建一个目录结构。在 TOMCAT 中,它在 webapps 目录下。“图1”描述了叫做 myJSFApp 的应用程序的目录结构。


写配置描述符

  与其它的 servlet/JSP 应用一样,这个应用程序也需要一个配置描述文件。如“清单1”表示。

Listing 1. The deployment descriptor (the web.xml file)

<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <!-- Faces Servlet -->
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup> 1 </load-on-startup>
    </servlet>

    <!-- Faces Servlet Mapping -->
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
</web-app>

  在这个配置描述文件中有二个部分。 <servlet> 元素注册 Faces servlet , <servlet-mapping> 元素声明任何包含有 /faces/ 式样的请求地址,必须传递给 Faces servlet 。

创建 JSP 页面

  一个叫做 adder.jsp 的 JSP 页面提供用户接口,如“清单2”所示:

Listing 2. The adder.jsp page

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
<head>
<title>Add 2 numbers</title>
</head>
<body>
<jsp:useBean id="NumberBean" class="jsfApp.NumberBean" scope="session" />
<f:use_faces><br />
    <h:form id="addForm" formName="addForm" ><br />
        First Number:<br />
        <h:input_number id="firstNumber" valueRef="NumberBean.firstNumber" /><br />
        Second Number:
        <h:input_number id="secondNumber" valueRef="NumberBean.secondNumber" /><br />
        Result:
        <h:output_number id="output" valueRef="NumberBean.result"/><br>
        <h:command_button id="submitButton" label="Add" commandName="submit" >
            <f:action_listener type="jsfApp.MyActionListener" />
        </h:command_button>
    </h:form>
</f:use_faces>
</body>
</html>

  我们首先定义了俩个标签,它用到 JSF 的两个标签库:html 与 core 。这俩个标签库的定义可以在 jsf-ri.jar 文件中找到,所以你不用为它担心。它们的前缀分别是 h / f 。
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
   <jsp:useBean> 这个动作元素定义 NumberBean JavaBean 为 session scope 。
   <jsp:useBean id="NumberBean" class="jsfApp.NumberBean" scope="session" />
  接着是 JSF 控件了。注意 JSF 控件需要嵌入到 <f:use_faces> 标签中:
<f:use_faces>
...
</f:use_faces>
  在这里面,有一个表单。
<h:form id="addForm" formName="addForm">
...
</h:form>
  内嵌在这个表单里的是二个 input_numbers, 一个 output_number, 与一个 command_button 。
第一个数字:
<h:input_number id="firstNumber" valueRef="NumberBean.firstNumber" /><br />
第二个数字:
<h:input_number id="secondNumber" valueRef="NumberBean.secondNumber" /><br />
结果:
<h:output_number id="output" valueRef="NumberBean.result" /><br />
<h:command_button id="submitButton" label="Add" commandName="submit">
    <f:action_listener type="jsfApp.MyActionListener" />
</h:command_button>
  注意命令按钮的事件侦听器。“图2”描述了这个 JSP 页面的组件树(树根省略)。

  主组件是表单,它有四个子组件。

写对象模型

  在这个应用中,你需要用一个 JavaBean 来存二个数字与相加的结果。“清单3”是这个 JavaBean 的内容:NumberBean

Listing 3. The NumberBean JavaBean

package jsfApp;
public class NumberBean {
    int firstNumber  = 0;
    int secondNumber = 0;

    public NumberBean () {
        System.out.println("Creating model object");
    }

    public void setFirstNumber(int number) {
        firstNumber = number;
        System.out.println("Set firstNumber " + number);
    }

    public int getFirstNumber() {
        System.out.println("get firstNumber " + firstNumber);
        return firstNumber;
    }

    public void setSecondNumber(int number) {
        secondNumber = number;
        System.out.println("Set secondNumber " + number);
    }

    public int getSecondNumber() {
        System.out.println("get secondNumber " + secondNumber);
        return secondNumber;
    }

    public int getResult() {
        System.out.println("get result " + (firstNumber + secondNumber));
        return firstNumber + secondNumber;
    }
}


写事件侦听器

  命令按钮的事件侦听器是这个 JSF 应用的最有趣的部分。它表述一个事件怎么样引起一个侦听器去侦听。侦听器简单地只是输出信息到控制台。然而,它显示了重要的信息,如 JSP 页面组件树的层次结构,正是这些组件触发了事件。“清单4”展示事件侦听器:

Listing 4. The action listener for the command button (MyActionListener.java)

package jsfApp;

import java.util.Iterator;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import javax.faces.event.PhaseId;
import javax.faces.tree.Tree;

public class MyActionListener implements ActionListener {

    public PhaseId getPhaseId() {
        System.out.println("getPhaseId called");
        return PhaseId.APPLY_REQUEST_VALUES;
    }
  
    public void processAction(ActionEvent event) {
        System.out.println("processAction called");

        // the component that triggered the action event
        UIComponent component = event.getComponent();
        System.out.println("The id of the component that fired the action event: "
            + component.getComponentId());

        // the action command
        String actionCommand = event.getActionCommand();
        System.out.println("Action command: " + actionCommand);
  
        FacesContext facesContext = FacesContext.getCurrentInstance();
        Tree tree                 = facesContext.getTree();
        UIComponent root          = tree.getRoot();

        System.out.println("----------- Component Tree -------------");
        navigateComponentTree(root, 0);
        System.out.println("----------------------------------------");
    }
  
    private void navigateComponentTree(UIComponent component, int level) {
        Iterator children = component.getChildren();

        // indent
        for (int i=0; i<level; i++)
            System.out.print("  ");

        // print component id
        System.out.println(component.getComponentId());

        // navigate children
        while (children.hasNext()) {
            UIComponent child = (UIComponent) children.next();
            navigateComponentTree(child, level + 1);
        }
    }
}

编译与运行

  为了编译这个应用,我们转到 myJSFApp/WEB-INF/classes 这个目录。如果你用的是 windows 系统,打出如下命令:
javac -classpath ../lib/jsf-api.jar;../lib/jsf-ri.jar;../../../../common/lib/servlet.jar jsfApp/*.java
  注意你必须用到 lib 目录下的类库与 servlet.jar 库。

  然后运行 Tomcat ,在地址栏输入如下地址:
http://localhost:8080/myJSFApp/faces/adder.jsp
  注意你在 JPS 页面文件名前用了 /faces/ 式样。然后可以在浏览器中看到”如图3“所示:
image
  在控制台,你可以看到如下信息:
Model Object Created
get firstNumber 0
get secondNumber 0
get result 0
getPhaseId called
  现在在二个输入框中分别输入二个数字,然后点击 ADD 按钮。浏览器将显示计算结果“如图4”:
image
  更重要的,再检查一下控制台,看到如下信息:
get firstNumber 0
get secondNumber 0
processAction called
The id of the component that fired the action event: submitButton
Action command: submit
----------- Component Tree -------------
null
    addForm
        firstNumber
        secondNumber
        output
        submitButton
----------------------------------------
Set firstNumber 10
Set secondNumber 20
get firstNumber 10
get secondNumber 20
get result 30


总结
  在这篇文章中,你学到了 JSF 区别于其它 servlet/JSP 应用的最重要的特点:事件驱动。你也创建了一个包含一个 JSP 页面的简单应用。更重要的,你写了能响应事件的侦听器。
  实际应用中的 JSF 应用复杂得多,通常是很多 JSP 页面。这样情况下,你需要从一个页面导航到另一个页面。然而这应是另一篇文章的主题。

posted @ 2005-12-16 09:12 Eddie Lee 阅读(1177) | 评论 (1)编辑 收藏