﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-javaGrowing-文章分类-java学习</title><link>http://www.blogjava.net/juhongtao/category/15778.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 24 Oct 2009 23:32:50 GMT</lastBuildDate><pubDate>Sat, 24 Oct 2009 23:32:50 GMT</pubDate><ttl>60</ttl><item><title>hibernate 显示sql解决方案(内容转载)</title><link>http://www.blogjava.net/juhongtao/articles/298852.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Mon, 19 Oct 2009 07:01:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/articles/298852.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/298852.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/articles/298852.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/298852.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/298852.html</trackback:ping><description><![CDATA[利用P6SPY ＋SQL Profiler记录、统计web app对数据库的操作。 
弄hibernate时，想显示sql语句，可以设置show_sql为true来达到这个目的，但是参数值全是像PreparedStatement一样，用?来代替的。
用p6spy可以达到显示的那些参数原值的目的，但可读性差。可以利用SQL Profiler来处理这个事情。
p6spy: http://www.p6spy.com
SQL Profile: http://www.jahia.net/jahia/page597.html

p6spy安装：
* 将p6spy.jar放到WEB-INF/lib目录下，将spy.properties放到WEB-INF/classes目录下。
* 修改你 原有 JDBC Driver为：com.p6spy.engine.spy.P6SpyDriver
* 修改 spy.properties 中的 realdriver 值为 原有 的JDBC Driver，比如我的是：com.mysql.jdbc.Driver
* 完成，运行web server。

我的日志记录产生在 %TOMCAT_HOME%\bin下，此log位置可以能过修改 logfile  = x:\x_dir\spy.log 来控制
打开看看，看里面的日志是不是看起来比较不爽？下面我们安装SQL Profiler来让自已的视线爽一点。

SQL Profiler安装：（须p6spy成功安装）
* 将SQL Profiler自带的spy.properties覆盖原来的classes目录下文件
* 修改现在spy.properties中realdriver 值为 原有 的JDBC Driver
看后看看readme注意这几句 ^__^ ：
1. Start the GUI
2. Start the webapp, in starts doing some JDBC requests we will ignore
3. Press the "reset" button on the GUI
4. Make a request to the webapp
5. Press the "pause" button after the request has finished executing
6. Press the "report" button to save profiling results as a CSV file

* 我们先用java -jar sqlprofiler.jar 运行 sql profiler
* 然后启动web server :-)

一切尽在眼前了吧？ 
当然，p6spy 和 sql profiler 能做的不止这些，sql profiler还能根据你的query来帮你生成建立合适的index功能等等。
<img src ="http://www.blogjava.net/juhongtao/aggbug/298852.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2009-10-19 15:01 <a href="http://www.blogjava.net/juhongtao/articles/298852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中ThreadLocal的设计与使用(转贴)</title><link>http://www.blogjava.net/juhongtao/articles/135537.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Thu, 09 Aug 2007 08:28:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/articles/135537.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/135537.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/articles/135537.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/135537.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/135537.html</trackback:ping><description><![CDATA[早在Java 1.2推出之时，Java平台中就引入了一个新的支持：java.lang.ThreadLocal，给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序，虽然ThreadLocal非常有用，但是似乎现在了解它、使用它的朋友还不多。 <br /><br />　　 <strong>ThreadLocal是什么<br /></strong><br />　　 ThreadLocal是什么呢？其实ThreadLocal并非是一个线程的本地实现版本，它并不是一个Thread，而是thread local variable（线程局部变量）。也许把它命名为ThreadLocalVar更加合适。线程局部变量（ThreadLocal）其实的功用非常简单，就是为每一个使用该变量的线程都提供一个变量值的副本，是每一个线程都可以独立地改变自己的副本，而不会和其它线程的副本冲突。从线程的角度看，就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明，在其它的一些语言编译器实现（如IBM XL FORTRAN）中，它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持，而是提供了一个ThreadLocal的类来提供支持，所以，在Java中编写线程局部变量的代码相对比较笨拙，这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。<br /><br />　　 <strong>ThreadLocal的设计<br /></strong><br />　　 首先看看ThreadLocal的接口：<br /><br />　　 　Object get() ; // 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值<br />　　　 void set(Object value); // 设置当前线程的线程局部变量副本的值<br /><br />　　 ThreadLocal有3个方法，其中值得注意的是initialValue()，该方法是一个protected的方法，显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值，这个方法是一个延迟调用方法，在一个线程第1次调用get()或者set(Object)时才执行，并且仅执行1次。ThreadLocal中的确实实现直接返回一个null：<br /><br /><table bordercolor="#ffcc66" width="100%" bgcolor="#b3b3b3" border="1"><tbody><tr><td>protected Object initialValue() { return null; }</td></tr></tbody></table><br />　　ThreadLocal是如何做到为每一个线程维护变量的副本的呢？其实实现的思路很简单，在ThreadLocal类中有一个Map，用于存储每一个线程的变量的副本。比如下面的示例实现：<br /><br /><table bordercolor="#ffcc66" width="100%" bgcolor="#b3b3b3" border="1"><tbody><tr><td>public class ThreadLocal<br />{<br />　 private Map values = Collections.synchronizedMap(new HashMap());<br />　 public Object get()<br />　 {<br />　　 Thread curThread = Thread.currentThread(); <br />　　 Object o = values.get(curThread); <br />　　 if (o == null &amp;&amp; !values.containsKey(curThread))<br />　　 {<br />　　　 o = initialValue();<br />　　　 values.put(curThread, o); <br />　　 }<br />　　 return o; <br />　 }<br /><br />　 public void set(Object newValue)<br />　 {<br />　　 values.put(Thread.currentThread(), newValue);<br />　 }<br /><br />　 public Object initialValue()<br />　 {<br />　　 return null; <br />　 }<br />}</td></tr></tbody></table><br />　　当然，这并不是一个工业强度的实现，但JDK中的ThreadLocal的实现总体思路也类似于此。<br /><br />　　<strong> ThreadLocal的使用<br /></strong><br />　　 如果希望线程局部变量初始化其它值，那么需要自己实现ThreadLocal的子类并重写该方法，通常使用一个内部匿名类对ThreadLocal进行子类化，比如下面的例子，SerialNum类为每一个类分配一个序号：<br /><br /><table bordercolor="#ffcc66" width="100%" bgcolor="#b3b3b3" border="1"><tbody><tr><td>public class SerialNum <br />{<br />　 // The next serial number to be assigned<br /><br />　 private static int nextSerialNum = 0; <br />　 private static ThreadLocal serialNum = new ThreadLocal() <br />　 {<br />　　 protected synchronized Object initialValue() <br />　　 {<br />　　　 return new Integer(nextSerialNum++);<br />　　 }<br />　 };<br /><br />　 public static int get() <br />　 {<br />　　 return ((Integer) (serialNum.get())).intValue(); <br />　 }<br />}</td></tr></tbody></table><br />　　SerialNum类的使用将非常地简单，因为get()方法是static的，所以在需要获取当前线程的序号时，简单地调用：<br /><br /><table bordercolor="#ffcc66" width="100%" bgcolor="#b3b3b3" border="1"><tbody><tr><td>int serial = SerialNum.get();</td></tr></tbody></table><br />　　即可。<br /><br />　　 在线程是活动的并且ThreadLocal对象是可访问的时，该线程就持有一个到该线程局部变量副本的隐含引用，当该线程运行结束后，该线程拥有的所以线程局部变量的副本都将失效，并等待垃圾收集器收集。<br /><br />　　 ThreadLocal与其它同步机制的比较<br /><br />　　 ThreadLocal和其它同步机制相比有什么优势呢？ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突，在普通的同步机制中，是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的，使用这种同步机制需要很细致地分析在什么时候对变量进行读写，什么时候需要锁定某个对象，什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问，ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本，从而隔离了多个线程的数据，每一个线程都拥有自己的变量副本，从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象，在编写多线程代码时，可以把不安全的整个变量封装进ThreadLocal，或者把该对象的特定于线程的状态封装进ThreadLocal。<br /><br />　　 由于ThreadLocal中可以持有任何类型的对象，所以使用ThreadLocal get当前线程的值是需要进行强制类型转换。但随着新的Java版本（1.5）将模版的引入，新的支持模版参数的ThreadLocal&lt;T&gt;类将从中受益。也可以减少强制类型转换，并将一些错误检查提前到了编译期，将一定程度地简化ThreadLocal的使用。<br /><br />　　 <strong>总结<br /></strong><br />　　 当然ThreadLocal并不能替代同步机制，两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问，是为了多个线程之间进行通信的有效方式；而ThreadLocal是隔离多个线程的数据共享，从根本上就不在多个线程之间共享资源（变量），这样当然不需要对多个线程进行同步了。所以，如果你需要进行多个线程之间进行通信，则使用同步机制；如果需要隔离多个线程之间的共享冲突，可以使用ThreadLocal，这将极大地简化你的程序，使程序更加易读、简洁。<br /><img src ="http://www.blogjava.net/juhongtao/aggbug/135537.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2007-08-09 16:28 <a href="http://www.blogjava.net/juhongtao/articles/135537.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中关于时间日期操作的常用函数 </title><link>http://www.blogjava.net/juhongtao/articles/129053.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Mon, 09 Jul 2007 07:37:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/articles/129053.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/129053.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/articles/129053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/129053.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/129053.html</trackback:ping><description><![CDATA[1.计算某一月份的最大天数<br />Calendar time=Calendar.getInstance(); <br />time.clear(); <br />time.set(Calendar.YEAR,year); <br />time.set(Calendar.MONTH,i-1);//注意,Calendar对象默认一月为0             <br /><font color="#ff3300">int day=time.getActualMaximum(Calendar.DAY_OF_MONTH)</font>;//本月份的天数<br />2.Calendar和Date的转化<br />(1) Calendar转化为Date<br />Calendar cal=Calendar.getInstance();<br />Date date=cal.getTime();<br />(2) Date转化为Calendar<br />Date date=new Date();<br />Calendar cal=Calendar.getInstance();<br />cal.setTime(date);<br />3.格式化输出日期时间<br />Date date=new Date();<br />SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");<br />System.out.println(df.format(date));<br />4.计算一年中的第几星期<br />(1)计算某一天是一年中的第几星期<br />Calendar cal=Calendar.getInstance();<br />cal.set(Calendar.YEAR, 2006);<br />cal.set(Calendar.MONTH, 8);<br />cal.set(Calendar.DAY_OF_MONTH, 3);<br /><font color="#ff3300">int weekno=cal.get(Calendar.WEEK_OF_YEAR);</font><br />(2)计算一年中的第几星期是几号<br />SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");<br />Calendar cal=Calendar.getInstance();<br />cal.set(Calendar.YEAR, 2006);<br />cal.set(Calendar.WEEK_OF_YEAR, 1);<br />cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);<br />System.out.println(df.format(cal.getTime()));<br />输出:<br />2006-01-02<br />5.add()和roll()的用法<br />(1)add()方法<br />SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");<br />Calendar cal=Calendar.getInstance();<br />cal.set(Calendar.YEAR, 2006);<br />cal.set(Calendar.MONTH, 8);<br />cal.set(Calendar.DAY_OF_MONTH, 3);<br />cal.add(Calendar.DATE, -4);<br />Date date=cal.getTime();<br />System.out.println(df.format(date));<br />cal.add(Calendar.DATE, 4);<br />date=cal.getTime();<br />System.out.println(df.format(date));<br />输出：<br />    2006-08-30<br />    2006-09-03<br />(2)roll方法<br />cal.set(Calendar.YEAR, 2006);<br />cal.set(Calendar.MONTH, 8);<br />cal.set(Calendar.DAY_OF_MONTH, 3);<br />cal.roll(Calendar.DATE, -4);<br />date=cal.getTime();<br />System.out.println(df.format(date));<br />cal.roll(Calendar.DATE, 4);<br />date=cal.getTime();<br />System.out.println(df.format(date));<br />输出：<br />    2006-09-29<br />    2006-09-03<br />可见，<font color="#ff3300">roll()方法在本月内循环，一般使用add()方法；</font><img src ="http://www.blogjava.net/juhongtao/aggbug/129053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2007-07-09 15:37 <a href="http://www.blogjava.net/juhongtao/articles/129053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>格式化输出数字 </title><link>http://www.blogjava.net/juhongtao/articles/129051.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Mon, 09 Jul 2007 07:36:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/articles/129051.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/129051.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/articles/129051.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/129051.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/129051.html</trackback:ping><description><![CDATA[
		<p>在实际工作中，常常需要设定数字的输出格式，如以百分比的形式输出，或者设定小数位数等，先稍微总结如下。<br />主要使用的类：java.text.DecimalFormat<br />1。实例化对象，可以用如下两种方法：<br />DecimalFormat df=(DecimalFormat)NumberFormat.getInstance();<br />DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();<br />因为DecimalFormat继承自NumberFormat。<br />2。设定小数位数<br />系统默认小数位数为3，如：<br />  DecimalFormat df=(DecimalFormat)NumberFormat.getInstance();<br />  System.out.println(df.format(12.3456789));<br />输出：12.346<br />现在可以通过如下方法把小数为设为两位：<br />  <font color="#ff0000">df.setMaximumFractionDigits(2);<br /></font>  System.out.println(df.format(12.3456789));<br />则输出为：12.35<br />3。将数字转化为百分比输出，有如下两种方法：<br />(1) <br />  <font color="#ff0000">df.applyPattern("##.##%");<br /></font>  System.out.println(df.format(12.3456789));<br />  System.out.println(df.format(1));<br />  System.out.println(df.format(0.015));<br />输出分别为：1234.57%  100%    1.5%<br />(2)<br />  df.setMaximumFractionDigits(2);<br />  System.out.println(df.format(12.3456789*100)+"%");<br />  System.out.println(df.format(1*100)+"%");<br />  System.out.println(df.format(0.015*100)+"%");<br />输出分别为：<br />1,234.57%   100%   1.5%<br />4。设置分组大小<br />   DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();<br /> <font color="#ff0000"> df1.setGroupingSize(2);<br /></font>  System.out.println(df1.format(123456789));<br />输出：1,23,45,67,89<br />还可以通过df1.setGroupingUsed(false);来禁用分组设置，如：<br />   DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();<br />  df1.setGroupingSize(2);<br /> <font color="#ff0000"> df1.setGroupingUsed(false);<br /></font>  System.out.println(df1.format(123456789));<br />输出：123456789<br />5。设置小数为必须为2位<br />  DecimalFormat df2=(DecimalFormat) DecimalFormat.getInstance();<br />  <font color="#ff0000">df2.applyPattern("0.00");<br /></font>  System.out.println(df2.format(1.2));<br />输出：1.20</p>
<img src ="http://www.blogjava.net/juhongtao/aggbug/129051.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2007-07-09 15:36 <a href="http://www.blogjava.net/juhongtao/articles/129051.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Servlet教程</title><link>http://www.blogjava.net/juhongtao/articles/125250.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Wed, 20 Jun 2007 02:27:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/articles/125250.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/125250.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/articles/125250.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/125250.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/125250.html</trackback:ping><description><![CDATA[
		<div>
				<b>一、        </b>
				<b>Servlet</b>
				<b>简介</b>
				<b>
				</b>
		</div>
		<div>Servlet是对支持Java的服务器的一般扩充。它最常见的用途是扩展Web服务器，提供非常安全的、可移植的、易于使用的CGI替代品。它是一种动态加载的模块，为来自Web服务器的请求提供服务。它完全运行在Java虚拟机上。由于它在服务器端运行，因此它不依赖于浏览器的兼容性。<br />servlet容器：<br />负责处理客户请求、把请求传送给servlet并把结果返回给客户。不同程序的容器实际实现可能有所变化，但容器与servlet之间的接口是由servlet API定义好的，这个接口定义了servlet容器在servlet上要调用的方法及传递给servlet的对象类。<br />servlet的生命周期：<br />1、servlet容器创建servlet的一个实例<br />2、容器调用该实例的init()方法<br />3、如果容器对该servlet有请求，则调用此实例的service()方法<br />4、容器在销毁本实例前调用它的destroy()方法<br />5、销毁并标记该实例以供作为垃圾收集<br />一旦请求了一个servlet，就没有办法阻止容器执行一个完整的生命周期。<br />容器在servlet首次被调用时创建它的一个实例，并保持该实例在内存中，让它对所有的请求进行处理。容器可以决定在任何时候把这个实例从内存中移走。在典型的模型中，容器为每个servlet创建一个单独的实例，容器并不会每接到一个请求就创建一个新线程，而是使用一个线程池来动态的将线程分配给到来的请求，但是这从servlet的观点来看，效果和为每个请求创建一个新线程的效果相同。<br />servlet API<br />servlet接口：<br />public interface Servlet<br />它的生命周期由javax.servlet.servlet接口定义。当你在写servlet的时候必须直接或间接的实现这个接口。一般趋向于间接实现：通过从javax.servlet.GenericServlet或javax.servlet.http.HttpServlet派生。在实现servlet接口时必须实现它的五个方法：<br />init():<br />public void init(ServletConfig config) throws ServletException<br />一旦对servlet实例化后，容器就调用此方法。容器把一个ServletConfig对象传统给此方法，这样servlet的实例就可以把与容器相关的配置数据保存起来供以后使用。如果此方法没有正常结束就会抛出一个ServletException。一旦抛出该异常，servlet就不再执行，而随后对它的调用会导致容器对它重新载入并再次运行此方法。接口规定对任何servlet实例，此方法只能被调用一次，在任何请求传递给servlet之前，此方法可以在不抛出异常的情况下运行完毕。<br />service():<br />public void service(ServletRequest req,ServletResponse res) throws ServletException,IOException<br />只有成功初始化后此方法才能被调用处理用户请求。前一个参数提供访问初始请求数据的方法和字段，后一个提供servlet构造响应的方法。<br />destroy():<br />public void destroy()<br />容器可以在任何时候终止servlet服务。容器调用此方法前必须给service()线程足够时间来结束执行，因此接口规定当service()正在执行时destroy()不被执行。<br />getServletConfig():<br />public ServletConfig getServletConfig()<br />在servlet初始化时，容器传递进来一个ServletConfig对象并保存在servlet实例中，该对象允许访问两项内容：初始化参数和ServletContext对象，前者通常由容器在文件中指定，允许在运行时向sevrlet传递有关调度信息，后者为servlet提供有关容器的信息。此方法可以让servlet在任何时候获得该对象及配置信息。<br />getServletInfo():<br />public String getServletInfo()<br />此方法返回一个String对象，该对象包含servlet的信息，例如开发者、创建日期、描述信息等。该方法也可用于容器。<br />GenericServlet类<br />Public abstract class GenericServlet implants Servlet,ServletConfig,Serializable<br />此类提供了servlet接口的基本实现部分，其service()方法被申明为abstract，因此需要被派生。init(ServletConfig conf)方法把servletConfig对象存储在一个private transient（私有临时）实例变量里，getServletConfig()方法返回指向本对象的指针，如果你重载此方法，将不能使用getServletConfig来获得ServletConfig对象，如果确实想重载，记住要包含对super.config的调用。2.1版的API提供一个重载的没有参数的init()方法。现在在init(ServletConfig)方法结束时有一个对init()的调用，尽管目前它是空的。2.1版API里面，此类实现了ServletConfig接口，这使得开发者不用获得ServletConfig对象情况下直接调用ServletConfig的方法，这些方法是：getInitParameter(),getInitParameterNames(),getServletContext。此类还包含两个写日志的方法，它们实际上调用的是ServletContext上的对应方法。log(String msg)方法将servlet的名称和msg参数写到容器的日志中，log(String msg,Throwable cause)除了包含servlet外还包含一个异常。<br />HttpServlet类<br />该类扩展了GenericServlet类并对servlet接口提供了与HTTP更相关的实现。<br />service():<br />protected void service(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException<br />public void service(HttpServletRequest req,HttpServletResponse res)throws ServletException,IOException<br />该方法作为HTTP请求的分发器，这个方法在任何时候都不能被重载。当请求到来时，service()方法决定请求的类型（GET,POST,HEAD,OPTIONS,DELETE,PUT,TRACE），并把请求分发给相应的处理方法（doGet(),doPost(),doHead(),doOptions(),doDelete(),doPut(),doTrace()）每个do方法具有和第一个service()相同的形式。为了响应特定类型的HTTP请求，我们必须重载相应的do方法。如果servlet收到一个HTTP请求而你没有重载相应的do方法，它就返回一个说明此方法对本资源不可用的标准HTTP错误。<br />getLatModified():<br />protected long getLastModified(HttpServletRequest req)<br />该方法返回以毫秒为单位的的自GMT时间1970年1月1日0时0分0秒依赖的最近一次修改servlet的时间，缺省是返回一个负数表示时间未知。当处理GET请求时，调用此方法可以知道servlet的最近修改时间，服务器就可决定是否把结果从缓存中去掉。<br />HttpServletRequest接口<br />public interface HttpServletRequest extends ServletRequest<br />所有实现此接口的对象（例如从servlet容器传递的HTTP请求对象）都能让servlet通过自己的方法访问所有请求的数据。下面是一些用来获取表单数据的基本方法。<br />getParameter()<br />public String getParameter(String key)<br />此方法试图将根据查询串中的关键字定位对应的参数并返回其值。如果有多个值则返回列表中的第一个值。如果请求信息中没有指定参数，则返回null。<br />getParameterValues():<br />public String[] getParameterValues(String key)<br />如果一个参数可以返回多个值，比如复选框集合，则可以用此方法获得对应参数的所有值。如果请求信息中没有指定参数，则返回null。<br />GetParameterNames():<br />Public Enumeration getParameterNames()<br />此方法返回一个Enumeration对象，包含对应请求的所有参数名字列表。<br />HttpServletResponse接口<br />public interface HttpServletResponse extends servletResponse<br />servlet容器提供一个实现该接口的对象并通过service()方法将它传递给servlet。通过此对象及其方法，servlet可以修改响应头并返回结果。<br />setContentType():<br />public void setContentType(String type)<br />在给调用者发回响应前，必须用此方法来设置HTTP响应的MIME类型。可以是任何有效的MIME类型，当给浏览器返回HTML是就是”text/html”类型。<br />getWriter():<br />public PrintWriter getWriter()throws IOException<br />此方法将返回PrintWriter对象，把servlet的结果作为文本返回给调用者。PrintWriter对象自动把Java内部的UniCode编码字符转换成正确的编码以使客户端能够阅读。<br />getOutputStream():<br />public ServletOutputStream getOutputStream() throws IOException<br />此方法返回ServletOutputStream对象，它是java.io.OutputStream的一个子类。此对象向客户发送二进制数据。<br />setHeader():<br />public void setHeader(String name,String value)<br />此方法用来设置送回给客户的HTTP响应头。有一些快捷的方法用来改变某些常用的响应头，但有时也需要直接调用此方法。<br />编译条件<br />需要从http://java.sun.com/products/servlet/ 获得一份JSDK的拷贝，并把servlet.jar移动到JDK安装目录下的\jre\lib\ext目录下。如果是JDK1.1，则移动到\lib下，并在CLASSPATH中加入servlet.jar的绝对路径。<br />运行条件<br />需要Apache Jserv,Jrun Servlet Exec,Java Web Server,Weblogic,WebSphere,Tomcat,Resin等servlet服务器端程序。<br />简单范例<br /><br /><b>import</b> java.io.*;<br /><b>import</b> javax.servlet.*;<br /><b>import</b> javax.servlet.http.*;<br /><br /><b>public</b> <b>class</b> HelloWorld <b>extends</b> HttpServlet {<br /><br />    <b>public</b> <b>void</b> doGet(HttpServletRequest request, HttpServletResponse response)<br />    <b>throws</b> IOException, ServletException<br />    {<br />        response.setContentType("text/html");<br />        PrintWriter out = response.getWriter();<br />        out.println("&lt;html&gt;");<br />        out.println("&lt;body&gt;");<br />        out.println("&lt;head&gt;");<br />        out.println("&lt;title&gt;Hello World!&lt;/title&gt;");<br />        out.println("&lt;/head&gt;");<br />        out.println("&lt;body&gt;");<br />        out.println("&lt;h1&gt;Hello World!&lt;/h1&gt;");<br />        out.println("&lt;/body&gt;");<br />        out.println("&lt;/html&gt;");<br />    }<br />}<br /><br />servlet的性能和效率<br />一个servlet仅被初始化一次而执行多次，因此极小的低效性也会随着时间的增加而产生很很大的影响。在代码中需要考虑String对象的使用，如果产生HTML响应需要用到很多字符串时，不应该为每一个字符串生成一个String对象，因为这会产生大量的String和StringBuffer对象，造成大量的对象构造消耗和垃圾收集负担，解决的办法是一行一行的把所有需要写入的直接写入PrintWriter中，或者创建一个StringBuffer对象，并使用append()方法将文本加入。<br />及时回送<br />有时，程序需要花费很长时间执行，在这种情况下应该回送给客户端一些信息，而不是长时间显示白屏，这可以在执行到一定程度就回送一些东西，可以使用PrintWriter的flush()方法强制将现有的内容回送给浏览器。<br /><br />Servlet会话<br />由于Web服务器使用的协议HTTP是一种无状态的协议，因此要维护单个客户机一系列请求的当前状态就需要使用其它的附加手段，在以前，一般的方法是使用：<br />l    隐藏的表格字段：在浏览器中，这种类型的字段是不可见的，然而，它在请求中被传送，服务器端程序可以读取它的值。它的优点是实现容易，且大多浏览器都支持；缺点是字段必须按照特定的顺序建立，客户端可以通过查看源代码得到其值，在浏览器中单击“后退”按钮会丢失加到当前页中的附加字段，同时也限制了用户动态的生成文档。<br />l    Cookie：是一些关键的数据片断，由服务器建立并由客户机的浏览器存放。浏览器维护一个它自己的Cookie表，这使得它可以作为一种会话跟踪的解决方案。使用Cookie的好处是它比在URL或表单中储存数据更直观。它的缺点是它可以用于在比一次短会话更长时间内跟踪用户，甚至可以用来跟踪某个用户向站点发送的每一个请求，因此有人担心自己的隐私问题而关闭了Cookie，一些老的浏览器也不支持cookie。Servlet API提供一个Cookie类支持cookie，使用HttpServletResponse.addCookie()和HttpServletResponse.getCookies()方法添加和读取cookie。<br />l    URL重写：修改请求的url，使之包含会话ID。这种方法的缺点是：对于大量的数据，URL会变得很长而失去控制；在某些环境下，URL的字符串长度有一定的限制；数据保密问题，你可能不想让旁边的人或者可以使用同一个计算机的看到你的会话数据。Servlet提供HttpServletRequest类可以获得参数。<br />Servlet API有自己内置的会话跟踪支持，使用HttpSession对象既可。它的setAttribute()方法绑定一对名字/值数据，把它存到当前会话中，如果会话中已经存在该名字责替换它，语法为：public void setAttribute(String name,Object value)。getAttribute()方法读取存储在会话中的对象，语法为：public Object getAttribute(String name)。getAttributeNames()方法返回存储在会话中的所有名字，语法为：public String[] getAttributeNames()。最后一个方法是removeAttribute()方法，它从会话中删除指定的信息，语法为：public void removeAttribute(String name)。HttpSession对象可以使用HttpServletRequest对象request的getSession(true)方法获得。参数为true意味着如果不存在该对象则创建一个。</div>
		<div>
				<b>
				</b> </div>
		<div>
				<b>二、    </b>
				<b>servlet</b>
				<b>规范定义的</b>
				<b>Servlet </b>
				<b>生命周期</b>
				<b>
				</b>
		</div>
		<div>servlet有良好的生存期的定义，包括如何加载、实例化、初始化、处理客户端请求以及如何被移除。这个生存期由javax.servlet.Servlet接口的init,service和destroy方法表达。<br />1、加载和实例化<br />容器负责加载和实例化一个servlet。实例化和加载可以发生在引擎启动的时候，也可以推迟到容器需要该servlet为客户请求服务的时候。<br />首先容器必须先定位servlet类，在必要的情况下，容器使用通常的Java类加载工具加载该servlet，可能是从本机文件系统，也可以是从远程文件系统甚至其它的网络服务。容器加载servlet类以后，它会实例化该类的一个实例。需要注意的是可能会实例化多个实例，例如一个servlet类因为有不同的初始参数而有多个定义，或者servlet实现SingleThreadModel而导致容器为之生成一个实例池。<br /><br />2、初始化<br />servlet加载并实例化后，容器必须在它能够处理客户端请求前初始化它。初始化的过程主要是读取永久的配置信息，昂贵资源（例如JDBC连接）以及其它仅仅需要执行一次的任务。通过调用它的init方法并给它传递唯一的一个（每个servlet定义一个）ServletConfig对象完成这个过程。给它传递的这个配置对象允许servlet访问容器的配置信息中的名称－值对（name-value）初始化参数。这个配置对象同时给servlet提供了访问实现了ServletContext接口的具体对象的方法，该对象描述了servlet的运行环境。<br />    2.1初始化的错误处理<br />    在初始化期间，servlet实例可能通过抛出UnavailableException 或者 ServletException异常表明它不能进行有效服务。如果一个servlet抛出一个这样的异常，它将不会被置入有效服务并且应该被容器立即释放。在此情况下destroy方法不会被调用因为初始化没有成功完成。在失败的实例被释放后，容器可能在任何时候实例化一个新的实例，对这个规则的唯一例外是如果失败的servlet抛出的异常是UnavailableException并且该异常指出了最小的无效时间，那么容器就会至少等待该时间指明的时限才会重新试图创建一个新的实例。<br />    2.2、工具因素<br />    当工具（注：根据笔者的理解，这个工具可能是应用服务器的某些检查工具，通常是验证应用的合法性和完整性）加载和内省（introspect）一个web应用时，它可能加载和内省该应用中的类，这个行为将触发那些类的静态初始方法被执行，因此，开发者不能假定只要当servlet的init方法被调用后它才处于活动容器运行状态（active container runtime）。作为一个例子，这意味着servlet不能在它的静态（类）初始化方法被调用时试图建立数据库连接或者连接EJB容器。<br /><br />3、处理请求<br />在servlet被适当地初始化后，容器就可以使用它去处理请求了。每一个请求由ServletRequest类型的对象代表，而servlet使用ServletResponse回应该请求。这些对象被作为service方法的参数传递给servlet。在HTTP请求的情况下，容器必须提供代表请求和回应的HttpServletRequest和HttpServletResponse的具体实现。需要注意的是容器可能会创建一个servlet实例并将之放入等待服务的状态，但是这个实例在它的生存期中可能根本没有处理过任何请求。<br />    3.1、多线程问题<br />    容器可能同时将多个客户端的请求发送给一个实例的service方法，这也就意味着开发者必须确保编写的servlet可以处理并发问题。如果开发者想防止这种缺省的行为，那么他可以让他编写的servlet实现SingleThreadModel。实现这个类可以保证一次只会有一个线程在执行service方法并且一次性执行完。容器可以通过将请求排队或者维护一个servlet实例池满足这一点。如果servlet是分布式应用的一部分，那么，那么容器可能在该应用分布的每个JVM中都维护一个实例池。如果开发者使用synchronized关键字定义service方法(或者是doGet和doPost)，容器将排队处理请求，这是由底层的java运行时系统要求的。我们强烈推荐开发者不要同步service方法或者HTTPServlet的诸如doGet和doPost这样的服务方法。<br />    3.2、处理请求中的异常<br />    servlet在对请求进行服务的时候有可能抛出ServletException或者UnavailableException异常。ServletException表明在处理请求的过程中发生了错误容器应该使用合适的方法清除该请求。UnavailableException表明servlet不能对请求进行处理，可能是暂时的，也可能是永久的。如果UnavailableException指明是永久性的，那么容器必须将servlet从服务中移除，调用它的destroy方法并释放它的实例。如果指明是暂时的，那么容器可以选择在异常信息里面指明的这个暂时无法服务的时间段里面不向它发送任何请求。在这个时间段里面被被拒绝的请求必须使用SERVICE_UNAVAILABLE (503)返回状态进行响应并且应该携带稍后重试（Retry-After）的响应头表明不能服务只是暂时的。容器也可以选择不对暂时性和永久性的不可用进行区分而全部当作永久性的并移除抛出异常的servlet。<br />    3.3线程安全<br />    开发者应该注意容器实现的请求和响应对象（注：即容器实现的HttpServletRequest和HttpServletResponese）没有被保证是线程安全的，这就意味着他们只能在请求处理线程的范围内被使用，这些对象不能被其它执行线程所引用，因为引用的行为是不确定的。<br /><br />4、服务结束<br />容器没有被要求将一个加载的servlet保存多长时间，因此一个servlet实例可能只在容器中存活了几毫秒，当然也可能是其它更长的任意时间（但是肯定会短于容器的生存期）<br />当容器决定将之移除时（原因可能是保存内存资源或者自己被关闭），那么它必须允许servlet释放它正在使用的任何资源并保存任何永久状态（这个过程通过调用destroy方法达到）。容器在能够调用destroy方法前，它必须允许那些正在service方法中执行的线程执行完或者在服务器定义的一段时间内执行（这个时间段在容器调用destroy之前）。一旦destroy方法被调用，容器就不会再向该实例发送任何请求。如果容器需要再使用该servlet，它必须创建新的实例。destroy方法完成后，容器必须释放servlet实例以便它能够被垃圾回收。</div>
		<div>
				<b>
				</b> </div>
		<div>
				<b>三、    </b>
				<b>serlvet</b>
				<b>为什么只需要实现</b>
				<b>doGet</b>
				<b>和</b>
				<b>doPost</b>
				<b>
				</b>
		</div>
		<div>Serlvet接口只定义了一个服务方法就是service，而HttpServlet类实现了该方法并且要求调用下列的方法之一：<br />doGet：处理GET请求<br />doPost：处理POST请求<br />doPut：处理PUT请求<br />doDelete：处理DELETE请求<br />doHead：处理HEAD请求<br />doOptions：处理OPTIONS请求<br />doTrace：处理TRACE请求<br />通常情况下，在开发基于HTTP的servlet时，开发者只需要关心doGet和doPost方法，其它的方法需要开发者非常的熟悉HTTP编程，因此这些方法被认为是高级方法。<br />而通常情况下，我们实现的servlet都是从HttpServlet扩展而来。<br /><br />doPut和doDelete方法允许开发者支持HTTP/1.1的对应特性；<br />doHead是一个已经实现的方法，它将执行doGet但是仅仅向客户端返回doGet应该向客户端返回的头部的内容；<br />doOptions方法自动的返回servlet所直接支持的HTTP方法信息；<br />doTrace方法返回TRACE请求中的所有头部信息。<br /><br />对于那些仅仅支持HTTP/1.0的容器而言，只有doGet, doHead 和 doPost方法被使用，因为HTTP/1.0协议没有定义PUT, DELETE, OPTIONS,或者TRACE请求。<br /><br />另外，HttpServlet定义了getLastModified方法以支持有条件的（conditional）get操作。有条件的get操作是指使用GET方式请求资源并且在头部指定只有在资源内容在指定时间后被修改的情况下服务器才有必要回应请求并发送请求的内容。对于那些实现doGet方法并且在不同请求之间内容相同的servlet而言，它应该实现这个方法以提高网络资源的利用率。<br /><br />另外要提及的是，按照规范的要求，servlet容器至少要实现HTTP/1.0协议规范，推荐实现HTTP/1.1规范，在此基础上可以实现其它的基于请求回应模式（based request response model）的协议（例如HTTPS）。</div>
		<div>
				<b>
				</b> </div>
		<div>
				<b>四、    </b>
				<b>servlet</b>
				<b>实例的个数及因此引发的问题</b>
				<b>
				</b>
		</div>
		<div>在缺省情况下，一个容器中只为每个servlet定义生成一个servlet类实例。在servlet实现SingleThreadModel接口的情况下，容器可以生成多个实例以应付沉重的请求，也可以将请求排队发送给同一个实例（对于一个高性能的容器，也可能是这两种方式的结合，因为实例的个数是有限制的，因此在线程安全方式下一个实例会有多个请求排队等待服务同时容器中多个实例可以对请求进行服务）。对于为可分布式（distributable）应用开发的servlet而言，在每个JVM中对每个SERVLET定义都会有一个实例，如果在这样的应用中servlet也实现了SingleThreadModel接口，那么在每个JVM中每个servlet定义也可能有多个实例。<br /><br />使用SingleThreadModel接口可以保证一个线程一次性执行完给定实例的service方法，需要注意的是这个保证只能应用于servlet实例，那些可以被多个servlet实例访问的对象（例如HttpSession实例）依然对多个servlet有效，即使他们实现了SingleThreadModel。<br /><br />根据规范中的这些说明，我们在实现自己的serlvet时需要考虑多线程的问题，一般而言，不要在servlet中定义可变的成员，只能定义一些常量（使用final定义，如果没有使用，应该注意在程序中不应该修改其值），笔者见过一个定义很差的servlet:<br />public class SomeHttpServlet extends HttpServlet {<br /><br />    HttpSession session;<br />    ...<br />}<br /><br />这样的servlet在使用中一定会出现问题，所有的用户都会共用一个session（这样很节约系统资源，不是吗？:)），因此一个用户请求的信息突然跑到另一个用户的ie窗口豪不奇怪。<br />而且，即使你的servlet实现了SingleThreadModel接口也不要定义可变的成员，因为该成员的信息会保留下来，而这对于其它的用户而言在绝大部分情况下是毫无意义的。（你确定会有意义的情况例外，例如某种计数）<br /><br />另外需要说明的是上面说明中都是针对servlet定义而言的，而servlet定义定义不等价servlet类定义，即一个servlet类可能会有多个servlet定义，但是笔者还没有找到“servlet定义”的定义，规范中提到实例化一个servlet时可能会有不同的初始参数，但是这个也不同于带参数的多个构造方法。一般情况下我们可以认为一个servlet类对应一个servlet定义。</div>
		<div>
				<b>
				</b> </div>
		<div>
				<b>五、    </b>
				<b>servlet</b>
				<b>会话</b>
				<b>
				</b>
		</div>
		<div>HTTP协议是一种无状态的协议，而对于现在的web应用而言，我们往往需要记录从特定客户端的一系列请求间的联系。现在已经有很多会话跟踪的技术，但是对于程序员而言都不是很方便直接使用。servlet规范定义了一个简单的HttpSession接口以方便servlet容器进行会话跟踪而不需要开发者注意实现的细节。<br /><br />一般而言，有两种最常用的会话跟踪机制，一种就是URL重写。在客户端不接受cookie的情况下可以使用URL重写进行会话跟踪。URL重写包括向URL路径添加一些容器可以解释的数据。规范要求会话ID必须编码在URL路径中，参数名称必须是jsessionid，例如:<br />http://www.myserver.com/catalog/index.html;jsessionid=1234<br /><br />另一种就是现在最常用的cookie了，规范要求所有的servlet都必须支持cookie。容器向客户端发送一个cookie，客户端在后续的处于同一个会话的请求中向服务器返回该cookie。会话跟踪cookie的名字必须是JSESSIONID。<br /><br />新出现的一种会话功能是SSL会话，SSL（Secure Sockets Layer，安全套接字层）是HTTPS协议使用的一种加密技术，内建了会话跟踪功能，servlet容器可以非常容易的使用这些数据建立会话跟踪。（但是HTTPS不是规范要求servlet必须支持的协议） <br /><br />因为HTTP是一种基于请求响应的协议，因此会话只有在客户端加入它以后才被新建立。当会话跟踪信息被成功的返回给服务器以指示会话给建立时客户端才算加入了一个会话。如果客户端没有加入会话，那么下一次请求不会被认为是会话的一部分。如何客户端还不知道会话或者客户端选择不加入一个会话，那么会话被认为是新的。开发者必须自己设计自己的应用中的会话处理状态，在什么地方没有加入会话，什么地方不能加入会话以及什么地方不需要加入会话。<br />规范要求HttpSession在应用或者servlet上下文级别有效，诸如cookie这样的建立会话的底层机制可以在上下文中共享，但是对于那些外露的对象，以及更重要的是对象的那些属性是不能在上下文中共享的。<br /><br />对于会话的属性的绑定而言，任何对象都可以绑定到某个命名属性。被绑定的属性对象对于其它处于相同ServletContext并且处于同一个会话处理中的其它servlet也是可见的。<br />某些对象在被加入会话或者被从会话中移除时要求得到通知，这样的信息可以通过让该对象实现HttpSessionBindingListener接口得到。该接口定义了两个方法用以标记被绑定到会话或者从会话中被移除。<br />valueBound方法在对象通过getAttribute之前就被调用，而valueUnbound方法在对象已经不能通过getAttribute得到后才被调用。<br /><br />由于HTTP是无状态协议，因此客户端不再活动时没有什么明显的信号，这也就意味着只有一种机制可以用于表明客户端不再活动：超时。会话的缺省的时限由servlet容器定义并且可以通过HttpSession的getMaxInactiveInterval得到，开发者也可以通过使用setMaxInactiveInterval方法进行设置，这些方法返回的单位是秒，如果时限被设置为－1，那么意味着永远不会超时。<br /><br />通过调用HttpSession的getLastAccessedTime方法，我们可以得到在当前请求之前的访问时间。当会话中的一个请求被servlet上下文处理时会话就被认为被访问了。<br /><br />另外需要注意的就是一些很重要的会话的语义问题。<br />多线程问题：多个请求线程可能会同时访问同一个会话，开发者有责任以适当的方式同步访问会话中的资源。<br />分布式环境：对于被标记为可分布的应用而言，同一会话中的所有请求只能被单一的VM处理。同时，放入HttpSession中的所有对象都必须实现Serializable接口，否则容器可能会抛出IllegalArgumentException（在jboss_tomcat下没有抛出这个异常，但是如果在关闭服务器时还有未完成的会话，那么服务器在试图存储会话时会出现串行化异常，在重新启动的时候会试图回复会话，也会出现异常）。这个限制意味着开发者不会遇到非可分布容器中的那些并发问题。另外容器提供者可以通过将一个会话对象以及它的内容从分布式系统的一个活动节点移动到系统的其它不同节点的能力来保证可伸缩性。<br />客户端的语义：基于cookie或者SSL证书通常是被web浏览器控制并且不联系到特定浏览器窗口的事实，从客户端应用的所有窗口发送到容器的请求都可能是同一个会话。为了达到最大的可移植性，开发者不能总假设特定客户端的所有窗口的请求都处于同一个会话中。<b></b></div>
		<div>
				<b>六、    </b>
				<b>Bean</b>
				<b>和</b>
				<b>Servlet</b>
				<b>的企业应用</b>
				<b>
				</b>
		</div>
		<div>J2EE是一个企业应用程序的开发平台，包括了对EJB、Servlet、JavaServer Page、JNDI、XML等的支持。在这个平台上可以开发瘦客户端的多层体系结构的企业应用程序。<br /><br />　　Enterprise JavaBean技术是J2EE的主要基础。EJB技术对在分布式的计算环境中执行应用逻辑提供了一个可伸缩的框架结构。J2EE通过将EJB组件结构和其它的企业技术相结合，解决了在Java平台上进行开发和配置服务端应用程序的无缝结合。<br /><br />　　要使用J2EE开发您的企业应用，您必须要在您的机器上安装一个Web服务器，还要支持XML。为了在浏览器中运行Java 2的API，还要给您的浏览器安装一个支持Java2的插件。<br /><br />　　下面就介绍怎样用J2EE SDK写一个包括了HTML页面，Servlet和Session Bean的一个简单的瘦客户端的多层体系结构的企业应用程序。听起来是不是心动了呢？下面就开始吧。<br /><br />还要提醒一点的就是：在编程的时候，适当的增加catch子句是一个很好编程风格。如果例子代码抛出了一个正确的异常，代码就被 try/catch这样的程序结构包围。Catch子句应该中应该加入处理异常的代码，千万不要留成空白。至少，应该加入语句：e.printStackTrace()来在控制台显示异常信息。<br /><br />　　J2EE SDK是一个J2EE平台上用于演示、教育等用途的非商业的东东。可以从javasoft的网站上免费下载。很适合用来学习。如果你没有出国权限，还可以从国内各高校的FTP服务器上去下载，速度比较快，但可能版本不是最新的。<br /><br /><br />瘦客户端的多层体系结构的应用程序的例子：<br /><br />　　本例子通过一个HTML页面的输入来调用一个Servlet，Servlet再用Java的名字目录服务接口（JNDI）APIs来寻找一个会话Session Bean，用这个Session Bean来执行一个计算。当Servlet得到了计算的结果的之后，Servlet把计算结果返回给HTML页面的用户。<br /><br />　　之所以说这是一个瘦客户端的应用程序，是因为Servlet本身并没有执行任何的应用逻辑。这个简单的计算是由一个Session Bean在J2EE的应用服务器上执行的。客户没有参与过程的任何操作，所有的计算都是由Session Bean完成的。<br /><br />　　所谓的多层体系结果实际上是由三或者四层组成的。我们的例子实际上是四层的一个结构。三层的体系结构是在标准的两层的客户/服务器结构基础上，将一个多线程的应用服务器加到了非浏览器的客户端和后台数据库之间。而四层的体系结构是通过Servlet和JavaServer Pages技术将客户端的应用程序由浏览器和HTML页面来取代。这个例子我们暂时只用其中的三层，在下一个例子中。我们再去访问数据库。这样，就扩展到四层了。再以后，我们会涉及到JavaServer Pages技术和XML技术。<br /><br /><br />J2EE软件的安装：<br /><br />　　为了使我们的例子能够运行起来，首先要下载一个Java2 SDK Enterprise Edition（J2EE）的1.2.1的版本和一个J2SE（Java 2 Standard Edition）的1.2以上的版本。在Windows 2000系统中，假设我们把J2EE和J2SE都装到了C:\J2EE目录下。安装详细目录如下：<br /><br />J2EE：C:\J2EE\j2sdkee1.2.1<br /><br />J2SE：C:\J2EE\jdk1.2.2<br /><br /><br />Path和ClassPath的设置：<br /><br />　　下载的东西包括了J2EE的应用服务器、Cloudscape数据库、使用了加密套接字协议层的Web服务器、开发和配置的工具、企业级的Java APIs。其Path和ClassPath的设置如下：<br /><br />Path的设置：在Windows系统中，需要把Path的目录包含下面两个目录：<br /><br />C:\J2EE\j2sdkee1.2.1\bin<br /><br />C:\J2EE\jdk1.2.2\bin<br /><br />Classpath的设置：在Windows系统中，需要把Classpath参数包含下面的文件：<br /><br />C:\J2EE\j2sdkee.1.2.1\lib\j2ee.jar<br /><br />另外，还要配置环境变量：<br /><br />J2EE_HOME=C:\J2EE\j2sdkee1.2.1<br /><br />JAVA_HOME=C:\J2EE\jdk1.2.2<br /><br />　　这样，就可以执行C:\J2EE\j2sdkee1.2.1\bin目录下面的批处理命令了。仔细看看里面的批处理，你会发现不少的东西的。<br /><br /><br />J2EE应用程序组件：<br /><br />　　J2EE程序员编写J2EE组件。J2EE组件是一个功能齐全的软件单元。将其它的应用程序组件组装到J2EE的应用程序和接口中。J2EE规范中定义如下的应用程序组件：<br /><br /><br />应用程序客户组件<br /><br /><br />Enterprise JavaBean组件<br /><br /><br />Servlet和JavaServer Pages组件（也叫做Web组件）<br /><br /><br />Applet<br /><br />　　在本例子中，我们创建了一个J2EE的应用程序和两个J2EE的组件：一个Servlet和一个Session Bean。Servlet和HTML文件是捆绑在一个WAR（WEB Archive）文件中。Session Bean的类和接口捆绑到了一个JAR文件中。然后再把WAR文件和JAR文件加到J2EE的应用程序，捆绑到一个EAR（Enterprise Archive）文件中。并验证测试产品环境的配置。<br /><br />　　在这所有的步骤中。实际上执行了很多的不用的角色的功能。编写Session Bean和Servlet是开发工作。而创建一个J2EE的应用程序，将J2EE组件组装到应用程序中是应用程序的组装工作。实际上，这些工作可以在不同的地方由不用的人员来做。<br /><br />创建一个HTML页面：<br /><br />这个页面名字为bonus.html。HTML代码如下：<br /><br />　　代码中，让人感兴趣的是用别名来调用BonusServlet.class。因为在后面提到的应用程序的组装的时候，将它映射到了这个别名BonusServlet上<br /><br />&lt;HTML&gt;<br /><br />&lt;BODY BGCOLOR = "WHITE"&gt;<br /><br />&lt;BLOCKQUOTE&gt;<br /><br />&lt;H3&gt;Bonus Calculation&lt;/H3&gt;<br /><br />&lt;FORM METHOD="GET" ACTION="BonusAlias"&gt;<br /><br />&lt;P&gt;<br /><br />Enter social security Number:<br /><br />&lt;P&gt;<br /><br />&lt;INPUT TYPE="TEXT" NAME="SOCSEC"&gt;&lt;/INPUT&gt;<br /><br />&lt;P&gt;<br /><br />Enter Multiplier:<br /><br />&lt;P&gt;<br /><br />&lt;INPUT TYPE="TEXT" NAME="MULTIPLIER"&gt;&lt;/INPUT&gt;<br /><br />&lt;P&gt;<br /><br />&lt;INPUT TYPE="SUBMIT" VALUE="Submit"&gt;<br /><br />&lt;INPUT TYPE="RESET"&gt;<br /><br />&lt;/FORM&gt;<br /><br />&lt;/BLOCKQUOTE&gt;<br /><br />&lt;/BODY&gt;<br /><br />&lt;/HTML&gt;<br /><br />　　这个HTML文件有两个数据域，用户可以输入社会保险号和一个乘数。当用户单击了Submit按纽。BonusServlet就得到了终端用户的数据。然后寻找Session Bean。将用户数据传递给Session Bean。Session Bean计算出奖金，把结果返回给Servlet。Servlet再通过另一个HTML页面将奖金结果返回给用户。<br /><br /><br /><br />创建Servlet：<br /><br />例子假定BonusServlet.java文件是在C:\J2EE\Client-Code目录下面。在运行的时候，Servlet代码执行如下操作：<br /><br /><br />获得用户数据<br /><br /><br />查找Session Bean<br /><br /><br />将用户数据传递给Session Bean<br /><br /><br />在得到Session Bean的返回结果以后，创建一个HTML页面将结果返回给客户。<br /><br /><br />Servlet代码如下：<br /><br />import javax.servlet.*;<br /><br />import javax.servlet.http.*;<br /><br />import java.io.*;<br /><br />import javax.naming.*;<br /><br />import javax.rmi.PortableRemoteObject;<br /><br />import Beans.*;<br /><br />public class BonusServlet extends HttpServlet {<br /><br />CalcHome homecalc;<br /><br />public void init(ServletConfig config)<br /><br />throws ServletException{<br /><br />//Look up home interface<br /><br />try{<br /><br />InitialContext ctx = new InitialContext();<br /><br />Object objref = ctx.lookup("calcs");<br /><br />homecalc =<br /><br />(CalcHome)PortableRemoteObject.narrow(<br /><br />objref,<br /><br />CalcHome.class);<br /><br />} catch (Exception NamingException) {<br /><br />NamingException.printStackTrace();<br /><br />} <br /><br />}<br /><br />public void doGet (HttpServletRequest request,<br /><br />HttpServletResponse response)<br /><br />throws ServletException, IOException {<br /><br />String socsec = null;<br /><br />int multiplier = 0;<br /><br />double calc = 0.0;<br /><br />PrintWriter out;<br /><br />response.setContentType("text/html");<br /><br />String title = "EJB Example";<br /><br />out = response.getWriter();<br /><br />out.println("&lt;HTML&gt;&lt;HEAD&gt;&lt;TITLE&gt;");<br /><br />out.println(title);<br /><br />out.println("&lt;/TITLE&gt;&lt;/HEAD&gt;&lt;BODY&gt;");<br /><br />try{<br /><br />Calc theCalculation;<br /><br />//Get Multiplier and Social Security Information<br /><br />String strMult =<br /><br />request.getParameter("MULTIPLIER");<br /><br />Integer integerMult = new Integer(strMult);<br /><br />multiplier = integerMult.intValue();<br /><br />socsec = request.getParameter("SOCSEC");<br /><br />//Calculate bonus.10 AUGUST 28, 2000<br /><br />double bonus = 100.00;<br /><br />theCalculation = homecalc.create();<br /><br />calc =<br /><br />theCalculation.calcBonus(multiplier, bonus);<br /><br />} catch(Exception CreateException){<br /><br />CreateException.printStackTrace();<br /><br />}<br /><br />//Display Data<br /><br />out.println("&lt;H1&gt;Bonus Calculation&lt;/H1&gt;");<br /><br />out.println("&lt;P&gt;Soc Sec: " + socsec + "&lt;P&gt;");<br /><br />out.println("&lt;P&gt;Multiplier: " +<br /><br />multiplier + "&lt;P&gt;");<br /><br />out.println("&lt;P&gt;Bonus Amount: " + calc + "&lt;P&gt;");<br /><br />out.println("&lt;/BODY&gt;&lt;/HTML&gt;");<br /><br />out.close();<br /><br />}<br /><br />public void destroy() {<br /><br />System.out.println("Destroy");<br /><br />}<br /><br />}<br /><br /><br />　　在import子句中，javax.servlet包括了Servlet Class的协议。Java.io是系统输入输出包。Javax.naming里面包含了Java名字目录服务APIs。Javax.rmi是用来Session Bean的home接口和Remote对象的通信使用的。<br /><br />　　在BonusServlet.init方法中，查找Session Bean的home接口。并且产生它的实例。方法使用了JNDI在组件的组装中的指定的名字calcs。用它来得到home接口的reference。然后就把这个reference和home接口类传递给PortableRemoteObject.narrow方法。来保证把reference转化为CalcHome类型。<br /><br />　　DoGet()方法有两个参数。一个是request对象，另一个是reponse对象。浏览器发送一个request对象给Servlet。而Servlet返回一个response对象给浏览器。方法访问request对象里面的信息，可以发现是谁在发出的请求、请求的数据在什么表单里面、是哪个HTTP头被发送。并使用reponse对象产生一个HTML页面来响应浏览器的请求。<br /><br />　　当方法处理请求的时候，如果产生输入输出错误，就抛出一个IOException异常。如果不能处理请求，就会抛出一个ServletException异常。为了计算奖金值，doGet()创建了一个home接口，调用它的calcBonus。<br /><br /><br />创建Session Bean：<br /><br />　　Session Bean代表了与客户的一个短暂的会话。如果服务或者客户有一方崩溃了。数据就消失了。相反，Entity Bean代表了数据库中一段持久的数据。如果服务或者客户又一方崩溃了，底层的服务保证数据能被保存下来。<br /><br />　　因为这个Enterprise Bean只是应BonusServlet的请求，执行了一个简单的计算。如果发生崩溃，可以重新初始化计算。这样，我们在本例子中就选择Session Bean来实现这个计算。<br /><br />　在组装配置好以后，Servlet组件和Session Bean组件如何在一个J2EE应用程序中协同工作。容器是Session Bean和支持Session Bean的底层平台之间的接口。容器是在配置期间产生的。<br /><br />　　本例子假定CalcBean.java、Calc.java和CalcHome.java文件都放在C:\J2EE\Beans目录下面。CalcHome.java文件前面的Package名字 Beans和目录Beans的名字应该是一样的。当这些文件被编译的时候，是从Beans目录中编译，其名字是包的名字后面加一个斜线在加上类或者接口的名字。<br /><br /><br />　<br /><br />CalcHome.java文件：<br /><br />package Beans;<br /><br />import java.rmi.RemoteException;<br /><br />import javax.ejb.CreateException;<br /><br />import javax.ejb.EJBHome;<br /><br />public interface CalcHome extends EJBHome {<br /><br />Calc create() throws CreateException, RemoteException;<br /><br />}<br /><br />　　BonusServlet并不直接同Session Bean通信。而是通过产生一个CalcHome的实例。这个Home接口扩展了EJBHome接口。有一个Create()方法，用来在容器中产生一个Session Bean。如果无法产生Session Bean，将会抛出一个CreateException异常。如果不能与Session Bean的远程方法通信，就会抛出一个RemoteException异常。<br /><br /><br />Calc.java文件：<br /><br />package Beans;<br /><br />import javax.ejb.EJBObject;<br /><br />import java.rmi.RemoteException;<br /><br />public interface Calc extends EJBObject {<br /><br />public double calcBonus(int multiplier,<br /><br />double bonus)<br /><br />throws RemoteException;<br /><br />}<br /><br />　　产生一个Home接口以后,J2EE应用程序就创建一个Remote接口和一个Session Bean。Remote接口扩展了EJBObject接口。并且声明了一个calcBonus()方法来计算奖金值。方法需要抛出javax.rmi.RemoteException异常。方法的实现在CalcBean类里面。<br /><br /><br />CalcBean.java文件：<br /><br />package Beans;<br /><br />import java.rmi.RemoteException;<br /><br />import javax.ejb.SessionBean;<br /><br />import javax.ejb.SessionContext;<br /><br />public class CalcBean implements SessionBean { <br /><br />public double calcBonus(int multiplier,<br /><br />double bonus) {<br /><br />double calc = (multiplier*bonus);<br /><br />return calc;<br /><br />}<br /><br />public void ejbCreate() { }<br /><br />public void setSessionContext(<br /><br />SessionContext ctx) { }<br /><br />public void ejbRemove() { }<br /><br />public void ejbActivate() { }<br /><br />public void ejbPassivate() { }<br /><br />public void ejbLoad() { }<br /><br />public void ejbStore() { }<br /><br />}<br /><br />　　本Session Bean类实现了SessionBean接口，提供了CalcBonus()方法的行为。在BonusServlet调用CalcHome的Create()方法以后，依次调用setSessionContext()方法和ejbCreate()方法。<br /><br />　　这些空的方法是从SessionBean中来的。由容器负责调用。除非在Bean的创建或者删除里面，你需要附加一些你自己的操作。否者，你并不需要提供这些方法的行为。<b></b></div>
		<div>
				<b>七、</b>
		</div>
<img src ="http://www.blogjava.net/juhongtao/aggbug/125250.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2007-06-20 10:27 <a href="http://www.blogjava.net/juhongtao/articles/125250.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Struts 1.1</title><link>http://www.blogjava.net/juhongtao/articles/74572.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Wed, 11 Oct 2006 07:08:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/articles/74572.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/74572.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/articles/74572.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/74572.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/74572.html</trackback:ping><description><![CDATA[
		<p>级别: 初级</p>
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#author">王和全</a>, <br /></p>
		<p>2003 年 8 月 02 日</p>
		<blockquote>作为基于MVC模式的Web应用最经典框架，Struts已经正式推出了1.1版本，该版本在以往版本的基础上，提供了许多激动人心的新功能。本文就将带你走进Struts 1.1去深入地了解这些功能。</blockquote>
		<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
		<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
		<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
		<p>
				<b>说明：</b>希望本文的读者能有一定的Struts使用基础。 </p>
		<p>
				<a name="1">
						<span class="atitle">Model 2</span>
				</a>
		</p>
		<p>
		</p>
		<p>Struts 是基于Model 2之上的，而Model 2是经典的MVC（模型－视图－控制器）模型的Web应用变体，这个改变主要是由于网络应用的特性--HTTP协议的无状态性引起的。Model 2的目的和MVC一样，也是利用控制器来分离模型和视图，达到一种层间松散耦合的效果，提高系统灵活性、复用性和可维护性。在多数情况下，你可以将 Model 2与MVC等同起来。</p>
		<p>下图表示一个基于Java技术的典型网络应用，从中可以看出Model 2中的各个部分是如何对应于Java中各种现有技术的。</p>
		<br />
		<img alt="" src="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/image001.gif" />
		<br />
		<p>在利用Model 2之前，我们是把所有的表示逻辑和业务逻辑都集中在一起（比如大杂烩似的JSP），有时也称这种应用模式为Model 1，Model 1的主要缺点就是紧耦合，复用性差以及维护成本高。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#main">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="2">
						<span class="atitle">Struts 1.1 和Model 2</span>
				</a>
		</p>
		<p>
		</p>
		<p>既然Struts 1.1是基于Model 2之上，那它的底层机制也就是MVC，下面是Struts 1.1中的MVC实现示意图：</p>
		<br />
		<img alt="" src="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/image002.jpg" />
		<br />
		<i>图解说明：其中不同颜色代表MVC的不同部分：红色（控制器）、紫色（模型）和绿色（视图） </i>
		<p>首先，控制器（ActionServlet）进行初始化工作，读取配置文件（struts- config.xml），为不同的Struts模块初始化相应的ModuleConfig对象。比如配置文件中的Action映射定义都保存在 ActionConfig集合中。相应地有ControlConfig集合、FormBeanConfig集合、ForwardConfig集合和 MessageResourcesConfig集合等。</p>
		<p>
				<i>
						<b>提示：</b>模块是在Struts 1.1中新提出的概念，在稍后的内容中我们将详细介绍，你现在可以简单地把模块看作是一个子系统，它们共同组成整个应用，同时又各自独立。Struts 1.1中所有的处理都是在特定模块环境中进行的。模块的提出主要是为了解决Struts 1.0中单配置文件的问题。 </i>
		</p>
		<p>控制器接收HTTP请求，并从ActionConfig中找出对应于该请求的Action子类，如果没有对应的Action，控制器直接将请求转发给JSP或者静态页面。否则控制器将请求分发至具体Action类进行处理。</p>
		<p>在控制器调用具体Action的execute方法之前，ActionForm对象将利用HTTP请求中的参数来填充自己（可选步骤，需要在配置文件中指定）。具体的ActionForm对象应该是ActionForm的子类对象，它其实就是一个JavaBean。此外，还可以在ActionForm类中调用validate方法来检查请求参数的合法性，并且可以返回一个包含所有错误信息的ActionErrors对象。如果执行成功， ActionForm自动将这些参数信息以JavaBean（一般称之为form bean）的方式保存在Servlet Context中，这样它们就可以被其它Action对象或者JSP调用。</p>
		<p>Struts将这些ActionForm的配置信息都放在FormBeanConfig集合中，通过它们Struts能够知道针对某个客户请求是否需要创建相应的ActionForm实例。</p>
		<p>Action 很简单，一般只包含一个execute方法，它负责执行相应的业务逻辑，如果需要，它也进行相应的数据检查。执行完成之后，返回一个 ActionForward对象，控制器通过该ActionForward对象来进行转发工作。我们主张将获取数据和执行业务逻辑的功能放到具体的 JavaBean当中，而Action只负责完成与控制有关的功能。遵循该原则，所以在上图中我将Action对象归为控制器部分。</p>
		<p>
				<i>
						<b>提示：</b>其实在Struts 1.1中，ActionMapping的作用完全可以由ActionConfig来替代，只不过由于它是公共API的一部分以及兼容性的问题得以保留。 ActionMapping通过继承ActionConfig来获得与其一致的功能，你可以等同地看待它们。同理，其它例如ActionForward与 ForwardConfig的关系也是如此。 </i>
		</p>
		<p>下图给出了客户端从发出请求到获得响应整个过程的图解说明。</p>
		<br />
		<img alt="" src="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/image003.jpg" />
		<br />
		<p>下面我们就来详细地讨论一下其中的每个部分，在这之前，先来了解一下模块的概念。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#main">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="3">
						<span class="atitle">模块</span>
				</a>
		</p>
		<p>
		</p>
		<p>我们知道，在Struts 1.0中，我们只能在web.xml中为ActionServlet指定一个配置文件，这对于我们这些网上的教学例子来说当然没什么问题，但是在实际的应用开发过程中，可能会有些麻烦。因为许多开发人员都可能同时需要修改配置文件，但是配置文件只能同时被一个人修改，这样肯定会造成一定程度上的资源争夺，势必会影响开发效率和引起开发人员的抱怨。</p>
		<p>在Struts 1.1中，为了解决这个并行开发的问题，提出了两种解决方案： </p>
		<ol>
				<li>多个配置文件的支持</li>
				<li>模块的支持</li>
		</ol>
		<p>支持多个配置文件，是指你能够为ActionServlet同时指定多个xml配置文件，文件之间以逗号分隔，比如Struts提供的MailReader演示例子中就采用该种方法。</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
  &lt;!-- Action Servlet Configuration --&gt;
  &lt;servlet&gt;
	&lt;servlet-name&gt;action&lt;/servlet-name&gt;
	&lt;servlet-class&gt;org.apache.struts.action.ActionServlet&lt;/servlet-class&gt;
	&lt;init-param&gt;
		&lt;param-name&gt;config&lt;/param-name&gt;
		&lt;param-value&gt;/WEB-INF/struts-config.xml, /WEB-INF/struts-config-registration.xml&lt;/param-value&gt;
	&lt;/init-param&gt; 
	&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
  &lt;/servlet&gt;

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这种方法可以很好地解决修改冲突的问题，不同的开发人员可以在不同的配置文件中设置自己的Action、ActionForm等等（当然不是说每个开发人员都需要自己的配置文件，可以按照系统的功能模块进行划分）。但是，这里还是存在一个潜在的问题，就是可能不同的配置文件之间会产生冲突，因为在ActionServlet初始化的时候这几个文件最终还是需要合并到一起的。比如，在struts-config.xml中配置了一个名为success的&lt; forward&gt;，而在struts-config-registration.xml中也配置了一个同样的&lt;forward&gt;，那么执行起来就会产生冲突。</p>
		<p>为了彻底解决这种冲突，Struts 1.1中引进了模块（Module）的概念。一个模块就是一个独立的子系统，你可以在其中进行任意所需的配置，同时又不必担心和其它的配置文件产生冲突。因为前面我们讲过，ActionServlet是将不同的模块信息保存在不同的ModuleConfig对象中的。要使用模块的功能，需要进行以下的准备工作：</p>
		<p>1、为每个模块准备一个配置文件</p>
		<p>2、配置web.xml文件，通知控制器</p>
		<p>决定采用多个模块以后，你需要将这些信息告诉控制器，这需要在web.xml文件进行配置。下面是一个典型的多模块配置：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
&lt;init-param&gt;
	&lt;param-name&gt;config&lt;/param-name&gt;
	&lt;param-value&gt;/WEB-INF/struts-config.xml&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;init-param&gt;
	&lt;param-name&gt;config/customer&lt;/param-name&gt; 
	&lt;param-value&gt;/WEB-INF/struts-config-customer.xml&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;init-param&gt; 
	&lt;param-name&gt;config/order&lt;/param-name&gt;
	&lt;param-value&gt;/WEB-INF/struts-config-order.xml&lt;/param-value&gt;
&lt;/init-param&gt;

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>要配置多个模块，你需要在原有的一个&lt;init-param&gt;（在Struts 1.1中将其对应的模块称为缺省模块）的基础之上，增加模块对应的&lt;init-param&gt;。其中&lt;param-name&gt;表示为config/XXX的形式，其中XXX为对应的模块名，&lt;param-value&gt;中还是指定模块对应的配置文件。上面这个例子说明该应用有三个模块，分别是缺省模块、customer和order，它们分别对应不同的配置文件。</p>
		<p>3、准备各个模块所需的ActionForm、Action和JSP等资源</p>
		<p>但是要注意的是，模块的出现也同时带来了一个问题，即如何在不同模块间进行转发？有两种方法可以实现模块间的转发，一种就是在&lt; forward&gt;（全局或者本地）中定义，另外一种就是利用org.apache.struts.actions.SwitchAction。</p>
		<p>下面就是一个全局的例子：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
    ... 
    &lt;struts-config&gt;
	... 
	&lt;global-forwards&gt;
		&lt;forward name="toModuleB"
			contextRelative="true"  
			path="/moduleB/index.do" 
		redirect="true"/&gt;   
	... 
	&lt;/global-forwards&gt;  
	...   
    &lt;/struts-config&gt;
</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>可以看出，只需要在原有的path属性前加上模块名，同时将contextRelative属性置为true即可。此外，你也可以在&lt;action&gt;中定义一个类似的本地&lt;forward&gt;。</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
  &lt;action-mappings&gt;
	&lt;!-- Action mapping for profile form --&gt;
	&lt;action path="/login" 
	type="com.ncu.test.LoginAction"  
	name="loginForm"     
	scope="request"      
	input="tile.userLogin"
	validate="true"&gt;     
	&lt;forward name="success" contextRelative="true" path="/moduleA/login.do"/&gt; 
	&lt;/action&gt; 
  &lt;/action-mappings&gt;
</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>如果你已经处在其他模块，需要转回到缺省模块，那应该类似下面这样定义，即模块名为空。</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
&lt;forward name="success" contextRelative="true" path="/login.do"/&gt;

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>此外，你也可以使用org.apache.struts.actions.SwitchAction，例如：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
    ...
    &lt;action-mappings&gt; 
	&lt;action path="/toModule" 
	type="org.apache.struts.actions.SwitchAction"/&gt;  
	...    
    &lt;/action-mappings&gt;  
    ...
</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#main">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="4">
						<span class="atitle">ActionServlet</span>
				</a>
		</p>
		<p>
		</p>
		<p>我们首先来了解MVC中的控制器。在Struts 1.1中缺省采用ActionServlet类来充当控制器。当然如果ActionServlet不能满足你的需求，你也可以通过继承它来实现自己的类。这可以在/WEB-INF/web.xml中来具体指定。</p>
		<p>要掌握ActionServlet，就必须了解它所扮演的角色。首先，ActionServlet表示MVC结构中的控制器部分，它需要完成控制器所需的前端控制及转发请求等职责。其次，ActionServlet被实现为一个专门处理HTTP请求的Servlet，它同时具有servlet的特点。在 Struts 1.1中它主要完成以下功能： </p>
		<ul>
				<li>接收客户端请求 </li>
				<li>根据客户端的URI将请求映射到一个相应的Action类 </li>
				<li>从请求中获取数据填充Form Bean（如果需要） </li>
				<li>调用Action类的execute()方法获取数据或者执行业务逻辑 </li>
				<li>选择正确的视图响应客户</li>
		</ul>
		<p>此外，ActionServlet还负责初始化和清除应用配置信息的任务。ActionServlet的初始化工作在init方法中完成，它可以分为两个部分：初始化ActionServlet自身的一些信息以及每个模块的配置信息。前者主要通过initInternal、initOther和 initServlet三个方法来完成。</p>
		<p>我们可以在/WEB-INF/web.xml中指定具体的控制器以及初始参数，由于版本的变化以及Struts 1.1中模块概念的引进，一些初始参数被废弃或者移入到/WEB-INF/struts-config.xml中定义。下面列出所有被废弃的参数，相应地在web.xml文件中也不鼓励再使用。</p>
		<ul>
				<li>application </li>
				<li>bufferSize </li>
				<li>content </li>
				<li>debug </li>
				<li>factory </li>
				<li>formBean </li>
				<li>forward </li>
				<li>locale </li>
				<li>mapping </li>
				<li>maxFileSize </li>
				<li>multipartClass </li>
				<li>nocache </li>
				<li>null </li>
				<li>tempDir</li>
		</ul>
		<p>ActionServlet根据不同的模块来初始化ModuleConfig类，并在其中以XXXconfig集合的方式保存该模块的各种配置信息，比如ActionConfig，FormBeanConfig等。</p>
		<p>初始化工作完成之后，ActionServlet准备接收客户请求。针对每个请求，方法process(HttpServletRequest request, HttpServletResponse response)将被调用。该方法指定具体的模块，然后调用该模块的RequestProcessor的process方法。</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
protected void process(HttpServletRequest request, 
		HttpServletResponse response) 
		throws IOException, ServletException {

	RequestUtils.selectModule(request, getServletContext());        
	getRequestProcessor(getModuleConfig(request)).process(request, response);
}
</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>RequestProcessor包含了Struts控制器的所有处理逻辑，它调用不同的processXXX方法来完成不同的处理。下表列出其中几个主要的方法：</p>
		<p>
		</p>
		<table border="1">
				<tbody>
						<tr>
								<td>方法</td>
								<td>功能</td>
						</tr>
						<tr>
								<td>processPath</td>
								<td>获取客户端的请求路径</td>
						</tr>
						<tr>
								<td>processMapping</td>
								<td>利用路径来获得相应的ActionMapping</td>
						</tr>
						<tr>
								<td>processActionForm</td>
								<td>初始化ActionForm（如果需要）并存入正确的scope中</td>
						</tr>
						<tr>
								<td>processActionCreate</td>
								<td>初始化Action</td>
						</tr>
						<tr>
								<td>processActionPerform</td>
								<td>调用Action的execute方法</td>
						</tr>
						<tr>
								<td>processForwardConfig</td>
								<td>处理Action返回的ActionForward</td>
						</tr>
				</tbody>
		</table>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#main">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="5">
						<span class="atitle">ActionForm</span>
				</a>
		</p>
		<p>
		</p>
		<p>对于ActionForm你可以从以下几个方面来理解它： </p>
		<ol>
				<li>ActionForm 表示HTTP窗体中的数据，可以将其看作是模型和视图的中介，它负责保存视图中的数据供模型或者视图使用。Struts 1.1文档中把它比作HTTP和Action之间的防火墙，这体现了ActionForm具有的过滤保护的作用，只有通过ActionForm验证的数据才能够发送到Action处理。</li>
				<li>ActionForm是与一个或多个ActionConfig关联的JavaBean，在相应的action的execute方法被调用之前，ActionForm会自动利用请求参数来填充自己（初始化属性）。</li>
				<li>ActionForm是一个抽象类，你必须通过继承来实现自己的类。</li>
		</ol>
		<p>ActionForm 首先利用属性的getter和setter方法来实现初始化，初始化完毕后，ActionForm的validate方法被调用，你可以在其中来检查请求参数的正确性和有效性，并且可以将错误信息以ActionErrors的形式返回到输入窗体。否则，ActionForm将被作为参数传给action的 execute方法以供使用。</p>
		<p>ActionForm bean的生命周期可以设置为session（缺省）和request，当设置为session时，记得在reset方法中将所有的属性重新设置为初始值。</p>
		<p>由于ActionForm对应于HTTP窗体，所以随着页面的增多，你的ActionForm将会急速增加。而且可能同一类型页面字段将会在不同的 ActionForm中出现，并且在每个ActionForm中都存在相同的验证代码。为了解决这个问题，你可以为整个应用实现一个ActionForm 或者至少一个模块对应于一个ActionForm。</p>
		<p>但是，聚合的代价就是复用性很差，而且难维护。针对这个问题，在Struts 1.1中提出了DynaActionForm的概念。</p>
		<p>
				<b>DynaActionForm类</b>
		</p>
		<p>DynaActionForm 的目的就是减少ActionForm的数目，利用它你不必创建一个个具体的ActionForm类，而是在配置文件中配置出所需的虚拟 ActionForm。例如，在下表中通过指定&lt;form-bean&gt;的type为 "org.apache.struts.action.DynaActionForm"来创建一个动态的ActionForm--loginForm。</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
&lt;form-beans&gt;
	&lt;form-bean name="loginForm" type="org.apache.struts.action.DynaActionForm"&gt;  
		&lt;form-property name="actionClass" type="java.lang.String"/&gt;
		&lt;form-property name="username" type="java.lang.String"/&gt;
		&lt;form-property name="password" type="java.lang.String"/&gt; 
	&lt;/form-bean&gt; 
&lt;/form-beans&gt;

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>动态的ActionForm 的使用方法跟普通的ActionForm相同，但是要注意一点。普通的ActionForm对象需要为每个属性提供getter和setter方法，以上面的例子而言，我们需要提供getUsername() 和 setUsername()方法取得和设置username属性，同样地有一对方法用于取得和设置password属性和actionClass属性。</p>
		<p>如果使用DynaActionForm，它将属性保存在一个HashMap类对象中，同时提供相应的get(name) 和 set(name)方法，其中参数name是要访问的属性名。例如要访问DynaActionForm中username的值，可以采用类似的代码：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
String username = (String)form.get("username")；

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>由于值存放于一个HashMap对象，所以要记得对get()方法返回的Object对象做强制性类型转换。正是由于这点区别，如果你在Action中非常频繁地使用ActionForm对象，建议还是使用普通的ActionForm对象。</p>
		<p>在Struts 1.1中，除了DynaActionForm以外，还提供了表单输入自动验证的功能，在包org.apache.struts.validator中提供了许多有用的类，其中最常见的就是DynaValidatorForm类。</p>
		<p>
				<b>DynaValidatorForm类</b>
		</p>
		<p>DynaValidatorForm是DynaActionForm的子类，它能够提供动态ActionForm和自动表单输入验证的功能。和使用DynaActionForm类似，你必须首先在配置文件中进行配置：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
&lt;form-beans&gt;
	&lt;form-bean name="loginForm" type="org.apache.struts.validator.DynaValidatorForm"&gt; 
		&lt;form-property name="actionClass" type="java.lang.String"/&gt;     
		&lt;form-property name="username" type="java.lang.String"/&gt; 
		&lt;form-property name="password" type="java.lang.String"/&gt;  
	&lt;/form-bean&gt;
&lt;/form-beans&gt;

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>同时要定义验证的插件：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
  &lt;plug-in className="org.apache.struts.validator.ValidatorPlugIn"&gt;
	&lt;set-property property="pathnames"  
	value="/WEB-INF/validator-rules.xml,  
	/WEB-INF/validation.xml"/&gt;
  &lt;/plug-in&gt;

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>其中的validator.xml和validator-rules.xml分别表示验证定义和验证规则的内容（可以合并在一起），比如针对上例中的DynaValidatorForm，我们有如下验证定义（validator.xml）：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
&lt;!DOCTYPE form-validation PUBLIC  
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN"  
"http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"&gt;
&lt;!--    Validation Rules    $Id: validation.xml--&gt;

&lt;form-validation&gt;  
&lt;!-- ========== Default Language Form Definitions ===================== --&gt;
&lt;formset&gt;  
	&lt;form name="loginForm"&gt;     
		&lt;field property="username" depends="required, minlength,maxlength"&gt; 
			&lt;arg0   key="prompt.username"/&gt;          
			&lt;arg1   key="${var:minlength}" name="minlength" resource="false"/&gt;       
			&lt;arg2   key="${var:maxlength}" name="maxlength" resource="false"/&gt;              
			&lt;var&gt;                
				&lt;var-name&gt;maxlength&lt;/var-name&gt;    
				&lt;var-value&gt;16&lt;/var-value&gt;         
			&lt;/var&gt;          
			&lt;var&gt;      
				&lt;var-name&gt;minlength&lt;/var-name&gt;     
				&lt;var-value&gt;3&lt;/var-value&gt;         
			&lt;/var&gt;       
		&lt;/field&gt;     
		&lt;field property="password" depends="required, minlength,maxlength" bundle="alternate"&gt;          
			&lt;arg0   key="prompt.password"/&gt;   
			&lt;arg1   key="${var:minlength}" name="minlength" resource="false"/&gt;          
			&lt;arg2   key="${var:maxlength}" name="maxlength" resource="false"/&gt;  
			&lt;var&gt;              
				&lt;var-name&gt;maxlength&lt;/var-name&gt;     
				&lt;var-value&gt;16&lt;/var-value&gt;        
			&lt;/var&gt;          
			&lt;var&gt;      
				&lt;var-name&gt;minlength&lt;/var-name&gt; 
				&lt;var-value&gt;3&lt;/var-value&gt;       
			&lt;/var&gt;        
		&lt;/field&gt;    
	&lt;/form&gt;   
&lt;/formset&gt;
&lt;/form-validation&gt;
</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>从上述定义中，我们可以看到对于字段username有三项验证：required, minlength, maxlength，意思是该字段不能为空，而且长度在3和16之间。而validator-rules.xml文件则可以采用Struts提供的缺省文件。注意在&lt;form-bean&gt;中定义的form是如何与validation.xml中的form关联起来的。最后，要启动自动验证功能，还需要将Action配置的validate属性设置为true。</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">

&lt;action path="/login"  
type="com.ncu.test.LoginAction"
name="loginForm"          
scope="request"         
input="tile.userLogin"validate="true"&gt;

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>此时，Struts将根据xml配置文件中的定义来检验表单输入，并将不符合要求的错误信息输出到页面。但是你可能会想：这个功能虽然好，可是什么检验都跑到服务器端执行，效率方面和用户易用性方面是不是有些问题？你可能会怀念起那简单的JavaScript客户端验证。</p>
		<p>不用担心，在Struts 1.1中也支持JavaScript客户端验证。如果你选择了客户端验证，当某个表单被提交以后，Struts 1.1启动客户端验证，如果浏览器不支持JavaScript验证，则服务器端验证被启动，这种双重验证机制能够最大限度地满足各种开发者的需要。 JavaScript验证代码也是在validator-rules.xml文件中定义的。要启动客户端验证，你必须在相应的JSP文件中做如下设置： </p>
		<ol>
				<li>为&lt;html:form&gt;增加onsubmit属性</li>
				<li>设置Javascript支持</li>
		</ol>
		<p>下表中列出了一JSP文件的示例代码，红字部分为Javascript验证所需代码。</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
&lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt;
&lt;table bgcolor="#9AFF9A" cellspacing="0" cellpadding="10" border="1" width="100%"&gt;
	&lt;tr&gt;
	&lt;td&gt; 
	&lt;table cellspacing="0" cellpadding="0" border="0" width="100%"&gt; 
	&lt;tr bgcolor="#696969"&gt; 
		&lt;td align="center"&gt;     
		&lt;font color="#FFFFFF"&gt;Panel 3: Profile&lt;/font&gt;  
		&lt;/td&gt;
		&lt;/tr&gt; 
	&lt;tr&gt;  
		&lt;td&gt;&lt;br&gt; 
		&lt;html:errors/&gt;  
		&lt;html:form action="/login.do" focus="username"  onsubmit="return validateLoginForm(this);"&gt;  
		&lt;html:hidden property="actionClass"/&gt;   
		&lt;center&gt;      
		&lt;table&gt;      
			&lt;tr&gt;        
			&lt;td&gt;UserName:&lt;/td&gt;   
			&lt;td&gt;&lt;html:text property="username" size="20"/&gt;&lt;/td&gt; 
			&lt;/tr&gt; 
			&lt;tr&gt;  
			&lt;td&gt;Password:&lt;/td&gt;   
			&lt;td&gt;&lt;html:password property="password" size="20"/&gt;&lt;/td&gt;    
			&lt;/tr&gt;  
			&lt;tr&gt;  
			&lt;td colspan=2&gt;&lt;html:submit property="submitProperty" value="Submit"/&gt;&lt;/td&gt;     
		&lt;/table&gt;   
		&lt;/center&gt;  
		&lt;/html:form&gt; 
		&lt;html:javascript formName="loginForm" dynamicJavascript="true" staticJavascript="false"/&gt;  
	
	&lt;script language="Javascript1.1" src="staticJavascript.jsp"&gt;&lt;/script&gt;  
	&lt;/td&gt; 
	&lt;/tr&gt; 
	&lt;/table&gt;
	&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;
</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>其中onsubmit的值为"return validateLoginForm(this);"，它的语法为：</p>
		<p>return validate + struts-config.xml中定义的form-bean名称 + (this);</p>
		<p>staticJavascript.jsp的内容为：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
&lt;%@ page language="java" %&gt;
&lt;%-- set document type to Javascript (addresses a bug in Netscape according to a web resource --%&gt;
&lt;%@ page contentType="application/x-javascript" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt;
&lt;html:javascript dynamicJavascript="false" staticJavascript="true"/&gt;


</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>如果validator-rules.xml中定义的基本验证功能不能满足你的需求，你可以自己添加所需的验证类型。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#main">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="6">
						<span class="atitle">Action</span>
				</a>
		</p>
		<p>
		</p>
		<p>我们通过继承Action类来实现具体的执行类。具体Action类的功能一般都在execute（以前是perform方法）方法中完成，其中主要涉及到以下几个方面： </p>
		<ol>
				<li>辅助ActionForm进行一些表单数据的检查。</li>
				<li>执行必要的业务逻辑，比如存取数据库，调用实体bean等。</li>
				<li>更新服务器端的bean数据，后续对象中可能会用到这些数据，比如在JSP中利用bean:write来获得这些数据。</li>
				<li>根据处理结果决定程序的去处，并以ActionForward对象的形式返回给ActionServlet。</li>
		</ol>
		<p>
				<i>
						<b>提示：</b>由于在Action和ActionForm中都可以实现验证方法，那么如何来安排它们之间的分工呢？一般来说，我们秉着MVC分离的原则，也就是视图级的验证工作放在ActionForm来完成，比如输入不能为空，email格式是否正确，利用ValidatorForm可以很轻松地完成这些工作。而与具体业务相关的验证则放入Action中，这样就可以获得最大ActionForm重用性的可能。 </i>
		</p>
		<p>前面我们提到过，我们主张将业务逻辑执行分离到单独的 JavaBean中，而Action只负责错误处理和流程控制。而且考虑到重用性的原因，在执行业务逻辑的JavaBean中不要引用任何与Web应用相关的对象，比如HttpServletRequest，HttpServletResponse等对象，而应该将其转化为普通的Java对象。关于这一点，可以参考Petstore中WAF框架的实现思路。</p>
		<p>此外，你可能还注意到execute与perform的一个区别：execute方法简单地掷出Exception异常，而perform方法则掷出ServletException和IOException 异常。这不是说Struts 1.1在异常处理功能方面弱化了，而是为了配合Struts 1.1中一个很好的功能--宣称式异常处理机制。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#main">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="7">
						<span class="atitle">宣称式异常处理</span>
				</a>
		</p>
		<p>
		</p>
		<p>和EJB中的宣称式事务处理概念类似，宣称式异常处理其实就是可配置的异常处理，你可以在配置文件中指定由谁来处理Action类中掷出的某种异常。你可以按照以下步骤来完成该功能： </p>
		<ol>
				<li>实现org.apache.struts.action.ExceptionHandler的子类，覆盖execute方法，在该方法中处理异常并且返回一个ActionForward对象</li>
				<li>在配置文件中配置异常处理对象，你可以配置一个全局的处理类或者单独为每个Action配置处理类</li>
		</ol>
		<p>下表就定义了一个全局的处理类CustomizedExceptionHandler，它被用来处理所有的异常。</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
&lt;global-exceptions&gt; 
&lt;exception 
	handler="com.yourcorp.CustomizedExceptionHandler" 
	key="global.error.message" 
	path="/error.jsp"    
	scope="request"    
	type="java.lang.Exception"/&gt;
&lt;/global-exceptions&gt;

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>其中具体的参数含义，可以参考ExceptionHandler.java源文件。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#main">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="8">
						<span class="atitle">taglib</span>
				</a>
		</p>
		<p>
		</p>
		<p>讲完了模型和控制器，接下来我们要涉及的是视图。视图的角色主要是由JSP来完成，从JSP的规范中可以看出，在视图层可以"折腾"的技术不是很多，主要的就是自定义标记库的应用。Struts 1.1在原有的四个标记库的基础上新增了两个标记库--Tiles和Nested。</p>
		<p>其中Tiles除了替代Template的基本模板功能外，还增加了布局定义、虚拟页面定义和动态页面生成等功能。Tiles强大的模板功能能够使页面获得最大的重用性和灵活性，此外可以结合Tiles配置文件中的页面定义和Action的转发逻辑，即你可以将一个Action转发到一个在Tiles配置文件中定义的虚拟页面，从而减少页面的数量。比如，下表中的Action定义了一个转发路径，它的终点是tile.userMain，而后者是你在 Tiles配置文件中定义的一个页面。</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
&lt;!-- ========== Action Mapping Definitions ============================== --&gt;
&lt;action-mappings&gt;  
&lt;!-- Action mapping for profile form --&gt; 
	&lt;action path="/login"   
		type="com.ncu.test.LoginAction"      
		name="loginForm"    
		scope="request"     
		input="tile.userLogin"
		validate="true"&gt;     
		&lt;forward name="success" path="tile.userMain"/&gt;   
	&lt;/action&gt; 
&lt;/action-mappings&gt;
</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>Tiles配置文件：tiles-defs.xml</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
&lt;!DOCTYPE tiles-definitions PUBLIC 
"-//Apache Software Foundation//DTD Tiles Configuration//EN"       "http://jakarta.apache.org/struts/dtds/tiles-config.dtd"&gt;
&lt;tiles-definitions&gt;  
&lt;!-- =======================================================  --&gt; 
&lt;!-- Master definitions                                       --&gt;
&lt;!-- =======================================================  --&gt; 
&lt;!-- Page layout used as root for all pages. --&gt; 

&lt;definition name="rootLayout" path="/tiles-layouts/rootLayout.jsp"&gt; 
	&lt;put name="titleString" value="CHANGE-ME"/&gt;   
	&lt;put name="topMenu" value="/tiles-components/topMenu.jsp"/&gt; 
	&lt;put name="leftMenu" value="/tiles-components/panel1.jsp"/&gt;  
	&lt;put name="body" value="CHANGE-ME"/&gt;   
	&lt;put name="footer" value="/tiles-components/footer.jsp"/&gt; 
&lt;/definition&gt; 

&lt;!-- =======================================================  --&gt; 
&lt;!-- Page definitions 					--&gt;  
&lt;!-- =======================================================  --&gt; 

&lt;!-- User Login page --&gt; 
&lt;definition name="tile.userLogin" extends="rootLayout"&gt; 
	&lt;put name="titleString" value="User Login"/&gt;  
	&lt;put name="body" value="/src/userLogin.jsp"/&gt; 
&lt;/definition&gt;  
&lt;!-- User Main page --&gt; 
&lt;definition name="tile.userMain" extends="rootLayout"&gt; 
	&lt;put name="titleString" value="User Main"/&gt;  
	&lt;put name="body" value="/src/userMain.jsp"/&gt; 
&lt;/definition&gt;
&lt;/tiles-definitions&gt;
</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>而Nested标记库的作用是让以上这些基本标记库能够嵌套使用，发挥更大的作用。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#main">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="9">
						<span class="atitle">Commons Logging 接口</span>
				</a>
		</p>
		<p>
		</p>
		<p>所谓的Commons Logging接口，是指将日志功能的使用与日志具体实现分开，通过配置文件来指定具体使用的日志实现。这样你就可以在Struts 1.1中通过统一的接口来使用日志功能，而不去管具体是利用的哪种日志实现，有点于类似JDBC的功能。Struts 1.1中支持的日志实现包括：Log4J，JDK Logging API， LogKit，NoOpLog和SimpleLog。</p>
		<p>你可以按照如下的方式来使用Commons Logging接口（可以参照Struts源文中的许多类实现）：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
package com.foo;
// ...
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
//...
	public class Foo {    
	// ...    
	private static Log log = LogFactory.getLog(Foo.class);
	// ...    
	public void setBar(Bar bar) {       
		if (log.isTraceEnabled()) {         
			log.trace("Setting bar to " + bar);   
		}      
	this.bar = bar;   
	}
// ...
}
</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>而开启日志功能最简单的办法就是在WEB-INF/classes目录下添加以下两个文件：</p>
		<p>commons-logging.properties文件：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
# Note: The Tiles framework now uses the commons-logging package to output different information or debug statements. 
Please refer to this package documentation to enable it. The simplest way to enable logging is to create two files in 
WEB-INF/classes:
# commons-logging.properties
# org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
# simplelog.properties
# # Logging detail level,
# # Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
#org.apache.commons.logging.simplelog.defaultlog=trace
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>simplelog.properties文件：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
# Logging detail level,
# Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
org.apache.commons.logging.simplelog.defaultlog=fatal

</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这里我们采用的日志实现是 SimpleLog，你可以在simplelog.properties文件指定日志明细的级别：trace，debug，info，warn， error和fatal，从trace到fatal错误级别越来越高，同时输出的日志信息也越来越少。而这些级别是和 org.apache.commons.logging.log接口中的方法一一对应的。这些级别是向后包含的，也就是前面的级别包含后面级别的信息。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#main">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/#main">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="author">
						<span class="atitle">关于作者</span>
				</a>
		</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td colspan="3">
										<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
								</td>
						</tr>
						<tr valign="top" align="left">
								<td>
										<p>
										</p>
								</td>
								<td>
										<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" />
								</td>
								<td width="100%">
										<p>王和全,邮件地址： <a href="mailto:ok_winnerboy@sina.com">ok_winnerboy@sina.com</a></p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/juhongtao/aggbug/74572.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-10-11 15:08 <a href="http://www.blogjava.net/juhongtao/articles/74572.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> Java中Set的深入研究</title><link>http://www.blogjava.net/juhongtao/articles/73861.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Sun, 08 Oct 2006 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/articles/73861.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/73861.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/articles/73861.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/73861.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/73861.html</trackback:ping><description><![CDATA[
		<p>作者：jjp</p>
		<p>Set和数学中的集合是同一个概念，就是没有重复元素的集合。</p>
		<p>这篇文章主要论述了Set是如何实现"没有重复元素"（no duplicate elements）的，以及阐述了什么是“重复”（duplicate），是相同的地址空间？是equals的返回值为true？是compareTo的返回值为0 ？还是有相同的hashCode？本文还给出了在什么情况下使用什么样的Set的建议。</p>
		<p>注：本文不涉及范型。</p>
		<p>1、树形结构：<br /> public interface Set&lt;E&gt; extends Collection&lt;E&gt;{}<br /> public abstract class AbstractSet&lt;E&gt; extends AbstractCollection&lt;E&gt; implements Set&lt;E&gt;{}<br /> public class CopyOnWriteArraySet&lt;E&gt;extends AbstractSet&lt;E&gt;implements Serializable{}<br /> public abstract class EnumSet&lt;E extends Enum&lt;E&gt;&gt;extends AbstractSet&lt;E&gt;implements Cloneable, Serializable{}<br /> public class HashSet&lt;E&gt;extends AbstractSet&lt;E&gt;implements Set&lt;E&gt;, Cloneable, Serializable{}<br /> public final class JobStateReasonsextends HashSet&lt;JobStateReason&gt;implements PrintJobAttribute{}<br /> public class LinkedHashSet&lt;E&gt;extends HashSet&lt;E&gt;implements Set&lt;E&gt;, Cloneable, Serializable{}<br /> public class TreeSet&lt;E&gt;extends AbstractSet&lt;E&gt;implements SortedSet&lt;E&gt;, Cloneable, Serializable{}<br />   可以看出，可以实例化的类为：CopyOnWriteArraySet，HashSet，LinkedHashSet，TreeSet。<br />2、Set是如何实现元素唯一性的<br />   javadoc中对Set的描述第一段如下：“A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 <br />   and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.”<br />   这段话是对是错，请看下面分析。<br />   要进行下面的论述，我们先了解一下Map。Map中的元素是“键－值”对，其中“键”必须是唯一的。TreeSet和HashSet就是利用这个特性实现“no duplicate    elements”。它把set中的元素作为Map中的“键”，从而保持元素的唯一性。这些键在Map中又是如何区分的呢？不同的Map有不同的做法，而且区别很大。<br />   下面我们分别就TreeSet、HashSet和CopyOnWriteArraySet进行论述：<br />2.1、TreeSet部分：<br />   以下以TreeSet为例进行分析。<br />   请看TreeSet的部分实体：<br /> public class TreeSet&lt;E&gt; extends AbstractSet&lt;E&gt;<br />      implements SortedSet&lt;E&gt;, Cloneable, java.io.Serializable<br /> {<br />  // The backing Map<br />      private transient SortedMap&lt;E,Object&gt; m; <br />      // The keySet view of the backing Map<br />      private transient Set&lt;E&gt; keySet; <br />      // Dummy value to associate with an Object in the backing Map<br />      //这是每个键所指的对像<br />      private static final Object PRESENT = new Object();<br />      //constructor<br />      private TreeSet(SortedMap&lt;E,Object&gt; m) {<br />          this.m = m;<br />           keySet = m.keySet();<br />      }<br />      public TreeSet() {<br />   this(new TreeMap&lt;E,Object&gt;());<br />      }<br />      //以下省略..........<br /> }<br />    可以看到TreeSet使用了SortedMap作为其Map保存“键－值”对，而这个SortedMap的真正实体是TreeMap。<br />    <br />    请看示例程序1：<br /> import java.util.*;<br /> public class SetTest1 {<br />  public static void main(String[] args){<br />   Set set = new TreeSet();<br />   set.add(new SetElement1("aa"));<br />   set.add(new SetElement1("bb"));<br />  }<br />  static class SetElement1{<br />   String s;<br />   public SetElement1(String s){<br />    this.s =  s;<br />   }<br />   public String toString(){<br />    return s;<br />   }<br />   public boolean equals(Object obj) {<br />    return s.equals(((SetElement1)obj).s);<br />   }<br />  }<br /> }<br />    该程序能够正常编译，但是运行时会抛出异常java.lang.ClassCastException。为什么？<br />    <br />    请看示例程序2：<br /> import java.util.*;<br /> public class SetTest2 {<br />  public static void main(String[] args){<br />   Set set = new TreeSet();<br />   set.add(new SetElement2("aa"));<br />   set.add(new SetElement2("aa"));<br />   set.add(new SetElement2("bb"));<br />   System.out.println(set);<br />  }<br />  static class SetElement2 implements Comparable{<br />   String s;<br />   public SetElement2(String s){<br />    this.s =  s;<br />   }<br />   public String toString(){<br />    return s;<br />   }<br />   public int compareTo(Object o){<br />    return s.compareTo(((SetElement2)o).s);<br />   }<br />   public boolean equals(Object obj) {<br />    return s.equals(((SetElement2)obj).s);<br />   }<br />  }<br /> }<br />   运行结果：<br />   [aa, bb]<br />   这正是我们所期望的结果。那“示例程序1”和“示例程序2”有什么区别？<br />   是因为SetElement2实现了Comparable接口，而SetElement1没有。SetElement2实现Comparable接口有什么用呢？因为在TreeSet的add方法中需要比较两个    元素的“值”。请看TreeMap中的compare方法：<br />   private int compare(K k1, K k2) {<br />        return (comparator==null ? ((Comparable&lt;/*-*/K&gt;)k1).compareTo(k2)<br />                                 : comparator.compare((K)k1, (K)k2));<br />   }<br />   可见这个方法先把要比较的元素down cast成Comparable类型。这里就可以解释“示例程序1”中为什么会抛出异常java.lang.ClassCastException，因SetElement1没有实现Comparable接口，当然就不能down cast成Comparable。可见，要用TreeSet来做为你的Set，那么Set中所装的元素都必须实现了Comparable接口。<br />   说到这里，你是不是想到了TreeSet中是采用Comparable接口中的compareTo方法来判断元素是否相同（duplicate），而不是采用其他类似equals之类的东东来判断。<br />   <br />   请看示例程序3：<br />    import java.util.Set;<br /> import java.util.*;<br /> public class SetTest3 {<br />  public static void main(String[] args){<br />   Set set = new HashSet();<br />   set.add(new SetElement3("aa"));<br />   set.add(new SetElement3("aa"));<br />   set.add(new SetElement3("bb"));<br />   System.out.println(set);<br />  }<br />  static class SetElement3 implements Comparable{<br />   String s;<br />   public SetElement3(String s){<br />    this.s =  s;<br />   }<br />   public String toString(){<br />    return s;<br />   }<br />   public int compareTo(Object o){<br />    //return s.compareTo(((SetElement3)o).s);<br />    return -1;<br />   }<br />   public boolean equals(Object obj) {<br />    return s.equals(((SetElement3)obj).s);<br />   }<br />  }<br /> }<br />   运行结果：<br />   [bb, aa, aa]<br />   看到没有，有两个“aa”！！这是因为compareTo返回值始终是"-1"，也就是说“把任何元素都看成不同”。<br />   <br />   综上所述，你是否对javadoc中对Set功能的描述有了怀疑？！<br />2.2、HashSet部分：<br />   以下以HashSet为例进行分析。<br />   从Hashset类的主体部分：<br /> public class HashSet&lt;E&gt; extends AbstractSet&lt;E&gt;<br />     implements Set&lt;E&gt;, Cloneable, java.io.Serializable<br /> {<br />  static final long serialVersionUID = -5024744406713321676L;<br />  private transient HashMap&lt;E,Object&gt; map;<br />  // Dummy value to associate with an Object in the backing Map<br />  //这是每个键所指的对像<br />  private static final Object PRESENT = new Object();</p>
		<p>     public HashSet() {<br />   map = new HashMap&lt;E,Object&gt;();<br />      }<br />     public boolean add(E o) {<br />   return map.put(o, PRESENT)==null;<br />      }<br />    //以下省略..........<br />    }<br /> <br />        public HashSet() {<br /> <br />  map = new HashMap&lt;E,Object&gt;();<br />    <br /> }<br />   可以看到HashSet使用了HashMap作为其Map保存“键－值”对。<br />   <br />   请看示例程序4：<br /> import java.util.*;</p>
		<p> public class SetTest4 {<br /> public static void main(String[] args){<br />  Set set = new HashSet();<br />  set.add(new SetElement4("aa"));<br />  set.add(new SetElement4("aa"));<br />  set.add(new SetElement4("bb"));<br />  System.out.println(set);<br /> }<br /> static class SetElement4{<br />  String s;<br />  public SetElement4(String s){<br />   this.s =  s;<br />  }<br />  public String toString(){<br />   return s;<br />  }<br />  public boolean equals(Object obj) {<br />   return s.equals(((SetElement4)obj).s);<br />  }<br /> }<br />}</p>
		<p>   运行结果：<br />   [bb, aa, aa]<br />   没有“示例程序1”中的java.lang.ClassCastException，但是运行结果似乎不对，因为有两个“aa”。<br />   <br />   请看示例程序5：<br /> import java.util.*;<br /> public class SetTest5 {<br />  public static void main(String[] args){<br />   Set set = new HashSet();<br />   set.add(new SetElement5("aa"));<br />   set.add(new SetElement5("aa"));<br />   set.add(new SetElement5("bb"));<br />   System.out.println(set);<br />  }<br />  static class SetElement5{<br />   String s;<br />   public SetElement5(String s){<br />    this.s =  s;<br />   }<br />   public String toString(){<br />    return s;<br />   }<br />   public boolean equals(Object obj) {<br />    return s.equals(((SetElement5)obj).s);<br />   }<br />   public int hashCode() {<br />    //return super.hashCode();<br />    return s.hashCode();<br />   }<br />  }<br /> }<br />    运行结果：<br />    [bb, aa]<br />    这就对了。“示例程序4”和“示例程序5”有什么区别？是SetElement5重写了hashCode方法。<br />    <br />    可见HashSet中是采用了比较元素hashCode的方法来判断元素是否相同（duplicate），而不是采用其他类似equals之类的东东来判断。<br />    <br />    说了这么多，那java类库中到底有没有根据equals来判断元素是否相同（duplicate）的Set呢？请看下文。<br />2.2、CopyOnWriteArraySet部分：<br />   类CopyOnWriteArraySet是java.util.concurrent包中的一个类，所以它是线程安全的。<br />   CopyOnWriteArraySet是使用CopyOnWriteArrayList作为其盛放元素的容器。当往CopyOnWriteArrayList添加新元素，它都要遍历整个List，并且用equals来    比较两个元素是否相同。</p>
		<p>   请看示例程序6：<br /> import java.util.*;<br /> import java.util.concurrent.*;<br /> public class SetTest6 {<br />  public static void main(String[] args){<br />   Set set = new CopyOnWriteArraySet();<br />   set.add(new SetElement6("aa"));<br />   set.add(new SetElement6("aa"));<br />   set.add(new SetElement6("bb"));<br />   System.out.println(set);<br />  }<br />  static class SetElement6{<br />   String s;<br />   public SetElement6(String s){<br />    this.s =  s;<br />   }<br />   public String toString(){<br />    return s;<br />   }<br />   public boolean equals(Object obj) {<br />    return s.equals(((SetElement6)obj).s);<br />   }<br />  }<br /> }<br />   运行结果：<br />   [aa, bb]<br />   好了，一切搞定！！</p>
		<p>3、总结：<br />   Javadoc中的一些描述可能是不准确的，大家要当心了！<br />   <br />   Set中实现元素互异的各种方法差异很大，大致可以分为三种：使用equals，使用hashCode，使用compareTo。但是我还没有发现采用“判断地址空间是否相同”来判断元素是否相同的类，当然我们可以用现有的三种方法来实现“判断地址空间是否相同”。<br />   <br />   综上所述，我们可以总结出使用Set的三种不同的情形：（以下假设元素类为Element）<br />   A、如果想使用Element的equals方法来判断元素是否相同，那么可以使用CopyOnWriteArraySet来构造类的实体。<br />   B、如果Element实现了Comparable接口，而且想使用compareTo方法来判断元素是否相同，那么可以使用TreeSet来构造类的实体。<br />   C、如果想使用判断hashCode是否相同的方法来判断元素是否相同，那么可以使用HashSet来构造类的实体。</p>
<img src ="http://www.blogjava.net/juhongtao/aggbug/73861.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-10-08 17:01 <a href="http://www.blogjava.net/juhongtao/articles/73861.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的事件处理</title><link>http://www.blogjava.net/juhongtao/articles/72498.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Thu, 28 Sep 2006 02:45:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/articles/72498.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/72498.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/articles/72498.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/72498.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/72498.html</trackback:ping><description><![CDATA[　【导读】在JAVA程序设计中，事件的处理是非常重要的，尤其是在需要自定义事件和设计JavaBean时. 本文用一个演示性的例子来说明事件及其处理过程。<br /><br />　　在JAVA程序设计中，事件的处理是非常重要的，尤其是在需要自定义事件和设计JavaBean时.对事件的处理过程有一个完整的认识对于编程是很有帮助的。<br /><br />　　下面用一个演示性的例子来说明事件及其处理过程<br /><br />　　一.事件的组成<br /><br />　　如果想要自定义一个事件，则必须提供一个事件的监听接口以及一个事件类。在JAVA中监听接口继承java.util.EventListener,事件类继承java.util.EventObject.很多基本的事件在编程环境中都已经提供可以很方便使用，但是在自定义事件中必须要要了解这些。<br /><br />　　下面是一个事件类的代码,事件类可以向用户处理程序提供被监听类的信息<br /><br />　　import java.util.*;<br /><br />　　public class PropertyEvent extends EventObject {<br /><br />　　public PropertyEvent(){}<br /><br />　　}<br /><br />　　下面是监听接口的代码<br /><br />　　import java.util.*;<br /><br />　　public interface PropertyListener extends EventListener {<br /><br />　　public void propertyChanged(PropertyEvent propertyEvent);<br /><br />　　}<br /><br />　　二.事件的处理机制<br /><br /><br /><br />　　下面是一段简要的被监听类代码，通过代码分析事件处理过程<br /><br />　　import java.util.*;<br /><br />　　public class Exam {<br /><br />　　private int property;<br /><br />　　//listeners用来存放已注册的监听对象<br /><br />　　private Set listeners= new HashSet();<br /><br />　　.....<br /><br />　　public void addListener(PropertyListener propertyListener){<br /><br />　　//listeners必须保证只能被一个线程访问<br /><br />　　synchronized(listeners){<br /><br />　　listeners.add(propertyListener);<br /><br />　　}<br /><br />　　}<br /><br />　　public void firePropertyChange(){<br /><br />　　Iterator iterator;<br /><br />　　synchronized(listeners){<br /><br />　　//将listeners中的类名放到iterator<br /><br />　　iterator = new HashSet(listeners).iterator();<br /><br />　　}<br /><br />　　//创建事件类<br /><br />　　PropertyEvent propertyEvent = new PropertyEvent();<br /><br />　　while(iterator.hasNext()){<br /><br />　　PropertyListener propertyListener = (propertyListener) iterator.next();<br /><br />　　//调用用户的事件处理程序<br /><br />　　propertyListener.propertyChanged(propertyEvent);<br /><br />　　}<br /><br />　　}<br /><br />　　}当属性值发生变化时，首先进行内部处理调用firePropertyChange方法，产生一个事件对象然后用事件对象为参数来调用用户的事件处理程序。<br /><br />　　三.事件处理的使用<br /><br />　　1.基本用法<br /><br />　　public Exam exam;<br /><br />　　exam.addListener(this);<br /><br />　　public void propertyChange(PropertyEvent event){...}<br /><br />　　注：exam是被监听对象，this为监听对象，是已经实现了接口方法的当前类，addListener 将当前类注册到listeners.<br /><br />　　2.一个被监听对象可以有多个监听对象<br /><br />　　exam.addListener(listener1);<br /><br />　　exam.addListener(listener2);<br /><br />　　这样当exam的property发生变化时，actionListener1和actionListener2的处理程序都会被调用。当然listener1和listener2必须都是已实现接口方法的类。<br /><br />　　3.被监听的对象也可以是实现了方法的接口<br /><br />　　exam.addListener(<br /><br />　　new PropertyListener(){<br /><br />　　//用户定义事件处理过程<br /><br />　　public void propertyChange(PropertyEvent event){<br /><br />　　...<br /><br />　　}<br /><br />　　);<br /><br />　　这种方法在实际编程中非常方便<img src ="http://www.blogjava.net/juhongtao/aggbug/72498.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-09-28 10:45 <a href="http://www.blogjava.net/juhongtao/articles/72498.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈Java与C#的事件处理机制（2）</title><link>http://www.blogjava.net/juhongtao/articles/72494.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Thu, 28 Sep 2006 02:35:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/articles/72494.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/72494.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/articles/72494.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/72494.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/72494.html</trackback:ping><description><![CDATA[
		<span class="txt">
				<center>
						<font color="#000099">
								<strong>适配类</strong>
						</font>
				</center>
				<br />
				<br />适配类是Java事件模型中极其重要的一部分。在一些应用场合，事件从源到监听者之间的传递要通过适配类来“转发”。例如：当事件源发出一个事件，而有几个事件监听者对象都可接收该事件，但只有指定对象做出反应时，就要在事件源与事件监听者之间插入一个事件适配器类，由适配器类来指定事件应该是由哪些监听者来响应。适配类成为了事件监听者，事件源实际是把适配类作为监听者注册入监听者队列中，而真正的事件响应者并未在监听者队列中，事件响应者应做的动作由适配类决定。目前绝大多数的开发工具在生成代码时，事件处理都是通过适配类来进行的。<br /><br /><center><font color="#000099"><strong>C#事件处理</strong></font></center><br /><br />在. NET应用程序开发中，不管是WEB Forms（ASP.NET）还是Windows Forms，都涉及到大量对象的事件响应及处理，比如客户在线提交一份订单、或是在Windows窗口上移动鼠标等都将有事件发生。那么在C#中，是怎样声明事件并为事件添加响应方法的呢？<br /><br />在C#中，事件(Events)成员就是用来声明一个类事件的。在类中声明一个事件成员一般采用如下的语法形式：<br /><table width="90%" bgcolor="#f8f8f8" border="1"><tbody><tr><td>public event 代表名 事件名。</td></tr></tbody></table><p> </p><p>如在Control类中声明了一个Click事件成员，其语法如下：<br /><br /></p><ccid_nobr></ccid_nobr><p></p><table bordercolor="#000000" width="90%" bgcolor="#f8f8f8" border="1"><tbody><tr><td>public event EventHandler Click;</td></tr></tbody></table><p>在C#中，增加了一个新的数据类型delegate（代表）来解决事件处理问题。代表数据类型非常类似于C语言中的指针，其与指针不同的是，其是代码是安全的，可管理的。由于C#本身的简易性，对于没有使用过Ｃ及指针的程序来说，理解delegate也是非常容易的。<br /><br />在C#中，通过使用delegate，你可以通过"+="（加等于）操作符非常容易地为.Net对象中的一个事件添加一个甚至多个响应方法；还可以通过非常简单的"-="（减等于）操作符取消这些响应方法。如下面为temp按钮添加Click事件的语句：<br /></p><p></p><table bordercolor="#000000" width="90%" bgcolor="#f8f8f8" border="1"><tbody><tr><td>temp.Click+=new System.EventHandler(this.Test);//为test添加事件处理方法</td></tr></tbody></table><p>在上面声明事件的语句中，Eventhandler是一个delegate(代表)类型，其在.Net类库中如下声明的： </p><p></p><table bordercolor="#000000" cellspacing="1" cellpadding="2" width="90%" bgcolor="#000000" border="0"><tbody><tr bgcolor="#f8f8f8"><td>public delegate void EventHandler(object sender,EventArgs e);</td></tr></tbody></table><p>这样，所有形如:void 函娄名(object 参数名,EventArgs 参数名);的函数都可以作为Control类的Click事件响应方法了。如下面所定义的一个事件响应方法：<br /></p><p></p><table bordercolor="#000000" cellspacing="1" cellpadding="2" width="90%" bgcolor="#f8f8f8" border="1"><tbody><tr><td>private void button1_Click(object sender, System.EventArgs e)</td></tr></tbody></table><p>由于是通过delegate（代表类型）来处理事件，因此，可能通过累加使一个事件具有多个响应方法；与此同时，还可以使一个方法作为多个事件的响应方法。（注意：在C#语言类中的event成员后面只能出现"+="与"-="两个表示添加与取消事件响应函数的操作符。）<br /><br />不管是ASP.Net还是一般的Windows Forms 编程，在C#中，基本上我们遇到的事件响应方法都是说明成如下的形式：<br /></p><p></p><table bordercolor="#000000" cellspacing="1" cellpadding="2" width="90%" bgcolor="#f8f8f8" border="1"><tbody><tr><td>private void button1_Click(object sender, System.EventArgs e)</td></tr></tbody></table><p>那么，一个事件响应方法的存取权限、返回值类型、参数及类型甚至方法名称等是否都必须固定不变呢？答案是：不是！<br /><br />一般情况下，事件的响应方法中都有两个参数，其中一个代表引发事件的对象即sender，由于引发事件的对象不可预知的，因此我们把其声明成为object类型，所有的对象都适用。第二个参数代表引发事件的具体信息，各种类型的事件中可能不同，这要根据类中事件成员的说明决定。<br />delegate int MyEventHandler(object sender, ToolBarButtonClickEventArgs e);</p><p>private int MyTest(object sender,ToolBarButtonClickEventArgs e) {}</p><p>在给对象添加事件响应方法时就可以用如下的代码实现：</p><p>Control.Event+=new MyEventHandler(MyTest);<br />总的来说,Java事件处理更直接,简单.而C#事件处理由于引用代理,使得程序更灵活,更体<br /><br />现程序之间的松藕合性.美国神鸟（Stryon http://www.stryon.com.cn）公司宣布在Java<br /><br />开发平台上实现微软的.NET,命名为iNET.并于近期推出iNET的Beta3版本,其中就包括用<br /><br />Java实现了C#的三级事件处理机制。</p></span>
<img src ="http://www.blogjava.net/juhongtao/aggbug/72494.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-09-28 10:35 <a href="http://www.blogjava.net/juhongtao/articles/72494.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈Java与C#的事件处理机制（1）</title><link>http://www.blogjava.net/juhongtao/articles/72486.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Thu, 28 Sep 2006 02:22:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/articles/72486.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/72486.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/articles/72486.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/72486.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/72486.html</trackback:ping><description><![CDATA[
		<span class="txt">
				<p>
						<span class="p11b">Java与C#的事件处理都是实现了事件源-事件响应者机制，但又不完全相同。Java实现的是一种事件源与事件响应者两级实体对象方式，这里的事件响应者也是事件监听者，而C#实现的是一种事件源-代理-事件响应者三级实体对象方式。下面就这两种方式来具体说明。</span>
				</p>
				<span class="p11b">
						<center>
								<font color="#000099">
										<strong>Java事件处理</strong>
								</font>
						</center>
						<center>
								<strong>
										<font color="#000099">
										</font>
								</strong> </center>
						<center>
								<font color="#000099">
										<font color="#000000">
										</font>
								</font> </center>
						<div align="left">
								<font color="#000099">
										<font color="#000000">从概念上讲，事件是一种在“源对象”和“监听者对象”之间，某种状态发生变化的传递机制。事件有许多不同的用途，例如在Windows系统中常要处理的鼠标事件、窗口边界改变事件、键盘事件等。在Java中则是定义了一个一般的、可扩充的事件机制，这种机制能够：<br /><br />· 对事件类型和传递的模型的定义和扩充提供一个公共框架，并适合于广泛的应用。<br /><br />· 与Java语言和环境有较高的集成度。<br /><br />· 事件能被描述环境捕获和触发。<br /><br />· 能使其它构造工具采取某种技术在设计时直接控制事件，以及事件源和事件监听者之间的联系。<br /><br />· 事件机制本身不依赖于复杂的开发工具。<br /><br />· 事件从事件源到监听者的传递是通过对目标监听者对象的Java方法调用进行的。对每个明确的事件的发生，都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者（EventListener）接口中，这个接口要继承java.util.EventListener。实现了事件监听者接口中一些或全部方法的类就是事件监听者。伴随着事件的发生，相应的状态通常都封装在事件状态对象中，该对象必须继承自 java.util.EventObject。事件状态对象作为单参传递给应响应该事件的监听者方法中。发出某种特定事件的事件源的标识是：遵从规定的设计格式为事件监听者定义注册方法，并接受对指定事件监听者接口实例的引用。<br /><br />有时，事件监听者不能直接实现事件监听者接口，或者还有其它的额外动作时，就要在一个源与其它一个或多个监听者之间插入一个事件适配器类的实例，来建立它们之间的联系。<br /><br /></font>
								</font>
						</div>
						<center>
								<font color="#000099">
										<font color="#000099">
												<strong>事件状态对象（Event State Object）</strong>
										</font>
								</font>
						</center>
						<center>
								<font color="#000099">
										<br />
										<br />
										<strong>与事件发生有关的状态信息一般都封装在一个事件状态对象中，这种对象是java.util.EventObject的子类。按设计习惯，这种事件状态对象类的名应以Event结尾。例如：</strong>
								</font>
						</center>
						<p>
								<font color="#000099">
										<strong>
										</strong>
								</font> </p>
						<p align="left">
								<font color="#000099">
										<strong>
										</strong>
										<table width="90%" align="center" bgcolor="#f8f8f8" border="1">
												<tbody>
														<tr>
																<td>public class MouseMovedExampleEvent extends java.util.EventObject<br />{ protected int x, y；<br />/*　创建一个鼠标移动事件MouseMovedExampleEvent */<br />　MouseMovedExampleEvent(java.awt.Component source, Point location) { <br />super(source);<br />x = location.x;<br />y = location.y;<br />}<br />/* 获取鼠标位置*/<br />public Point getLocation() { <br />return new Point(x, y);<br />}}<br /></td>
														</tr>
												</tbody>
										</table>
								</font>
						</p>
						<center>
								<font color="#000099">
										<strong>
												<br />
										</strong>
								</font>
						</center>
				</span>
				<center>
						<font color="#000099">
								<strong>事件监听者接口（EventListener Interface）与事件监听者</strong>
						</font>
				</center>
				<p>
						<br />
						<br />由于Java事件模型是基于方法调用，因而需要一个定义并组织事件操纵方法的方式。事件操纵方法都被定义在继承了 java.util.EventListener类的EventListener接口中，按规定，EventListener接口的命名要以 Listener结尾。任何一个类如果想操纵在EventListener接口中定义的方法都必须以实现这个接口方式进行。这个类也就是事件监听者。例如：<br />/*先定义了一个鼠标移动事件对象*/<br />　　public class MouseMovedExampleEvent extends java.util.EventObject {<br />　　 // 在此类中包含了与鼠标移动事件有关的状态信息<br />　　　　 ... <br />　　}<br />　　/*定义了鼠标移动事件的监听者接口*/<br />　　interface MouseMovedExampleListener extends java.util.EventListener { <br />/*在这个接口中定义了鼠标移动事件监听者所应支持的方法*/<br />void mouseMoved(MouseMovedExampleEvent mme);<br />}<br /></p>
				<p>class ArbitraryObject implements MouseMovedExampleListener { <br />　　　public void mouseMoved(MouseMovedExampleEvent mme) <br />　 { ... } <br />｝<br /></p>
				<p>ArbitraryObject就是MouseMovedExampleEvent事件的监听者。<br /><br /></p>
				<center>
						<font color="#000099">
								<strong>事件监听者的注册与注销</strong>
						</font>
				</center>
				<p>
						<br />
						<br />为了各种可能的事件监听者把自己注册入合适的事件源中，建立源与事件监听者间的事件流，事件源必须为事件监听者提供注册和注销的方法。在前面的bound属性介绍中已看到了这种使用过程，在实际中，事件监听者的注册和注销要使用标准的设计格式：<br />public void add&lt; ListenerType&gt;(&lt; ListenerType&gt; listener)；<br />public void remove&lt; ListenerType&gt;(&lt; ListenerType&gt; listener)；</p>
				<p>例如，首先定义了一个事件监听者接口：<br /><br /><ccid_nobr>public interface ModelChangedListener extends java.util.EventListener { <br />void modelChanged(EventObject e); <br />}</ccid_nobr></p>
				<p>接着定义事件源类</p>
				<p>public abstract class Model { <br />private Vector listeners = new Vector(); // 定义了一个储存事件监听者的数组<br />/*上面设计格式中的&lt; ListenerType&gt;在此处即是下面的ModelChangedListener*/<br />public synchronized void addModelChangedListener(ModelChangedListener mcl)<br />　　{ listeners.addElement(mcl); }//把监听者注册入listeners数组中<br />public synchronized void removeModelChangedListener(ModelChangedListener mcl)<br />　　　　{ listeners.removeElement(mcl); //把监听者从listeners中注销<br />　　　　｝<br />/*以上两个方法的前面均冠以synchronized，是因为运行在多线程环境时，可能同时有几个对象同时要进行<br />注册和注销操作，使用synchronized来确保它们之间的同步。开发工具或程序员使用这两个方法建立源与监<br />听者之间的事件流*/ <br />　protected void notifyModelChanged() <br />{/**事件源使用本方法通知监听者发生了modelChanged事件*/<br />　　　 Vector l; <br />　　　　EventObject e = new EventObject(this);<br />　　　　/*首先要把监听者拷贝到l数组中，冻结EventListeners的状态以传递事件。这样来确保在事件<br />传递到所有监听者之前，已接收了事件的目标监听者的对应方法暂不生效。*/ <br />　　　　synchronized(this) { <br />　　　　　　l = (Vector)listeners.clone(); <br />　　　　}<br />　　　　for (int i = 0; i &lt; l.size(); i++) {<br />　　　　 /* 依次通知注册在监听者队列中的每个监听者发生了modelChanged事件，<br />　　　　 并把事件状态对象e作为参数传递给监听者队列中的每个监听者*/<br />((ModelChangedListener)l.elementAt(i)).modelChanged(e); <br />　　　　}<br />　　　 } <br />　　　｝</p>
				<p>在程序中可见事件源Model类显式地调用了接口中的modelChanged方法，实际是把事件状态对象e作为参数，传递给了监听者类中的modelChanged方法。</p>
		</span>
<img src ="http://www.blogjava.net/juhongtao/aggbug/72486.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-09-28 10:22 <a href="http://www.blogjava.net/juhongtao/articles/72486.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>