﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-jinfeng_wang-随笔分类-java</title><link>http://www.blogjava.net/jinfeng_wang/category/484.html</link><description>G-G-S,D-D-U!</description><language>zh-cn</language><lastBuildDate>Fri, 30 Apr 2010 18:55:40 GMT</lastBuildDate><pubDate>Fri, 30 Apr 2010 18:55:40 GMT</pubDate><ttl>60</ttl><item><title>log4jdbc and hibernate</title><link>http://www.blogjava.net/jinfeng_wang/archive/2010/04/30/319762.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 30 Apr 2010 02:53:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2010/04/30/319762.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/319762.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2010/04/30/319762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/319762.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/319762.html</trackback:ping><description><![CDATA[1)&nbsp; SLF4J user manual<br />
<a href="http://www.slf4j.org/manual.html">http://www.slf4j.org/manual.html</a><br />
<br />
source:<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #0000ff">package</span><span style="color: #000000">&nbsp;test;<br />
</span><span style="color: #008080">&nbsp;2</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">&nbsp;3</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;org.slf4j.Logger;<br />
</span><span style="color: #008080">&nbsp;4</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;org.slf4j.LoggerFactory;<br />
</span><span style="color: #008080">&nbsp;5</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">&nbsp;6</span><span style="color: #000000"><img id="Codehighlighter1_91_232_Open_Image" onclick="this.style.display='none'; Codehighlighter1_91_232_Open_Text.style.display='none'; Codehighlighter1_91_232_Closed_Image.style.display='inline'; Codehighlighter1_91_232_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_91_232_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_91_232_Closed_Text.style.display='none'; Codehighlighter1_91_232_Open_Image.style.display='inline'; Codehighlighter1_91_232_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top"  alt="" /></span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Test&nbsp;</span><span id="Codehighlighter1_91_232_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_91_232_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">&nbsp;7</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">final</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;Logger&nbsp;logger&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;LoggerFactory.getLogger(Test.</span><span style="color: #0000ff">class</span><span style="color: #000000">);<br />
</span><span style="color: #008080">&nbsp;8</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">&nbsp;9</span><span style="color: #000000"><img id="Codehighlighter1_202_229_Open_Image" onclick="this.style.display='none'; Codehighlighter1_202_229_Open_Text.style.display='none'; Codehighlighter1_202_229_Closed_Image.style.display='inline'; Codehighlighter1_202_229_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_202_229_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_202_229_Closed_Text.style.display='none'; Codehighlighter1_202_229_Open_Image.style.display='inline'; Codehighlighter1_202_229_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span id="Codehighlighter1_202_229_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_202_229_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">10</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.error(</span><span style="color: #000000">"</span><span style="color: #000000">test</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">11</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">12</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">13</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"  alt="" />}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">14</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span></div>
<br />
jar file:<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #000000">slf4j</span><span style="color: #000000">-</span><span style="color: #000000">log4j12</span><span style="color: #000000">-</span><span style="color: #000000">1.5</span><span style="color: #000000">.</span><span style="color: #000000">8</span><span style="color: #000000">.jar<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" />slf4j</span><span style="color: #000000">-</span><span style="color: #000000">api</span><span style="color: #000000">-</span><span style="color: #000000">1.5</span><span style="color: #000000">.</span><span style="color: #000000">8</span><span style="color: #000000">.jar<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" />log4j</span><span style="color: #000000">-</span><span style="color: #000000">1.2</span><span style="color: #000000">.</span><span style="color: #000000">15</span><span style="color: #000000">.jar</span></div>
<br />
2) Analyzing JDBC Logs with LOG4JDBC<br />
http://www.cubrid.org/analyzing_jdbc_logs#_Toc234038379<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #0000ff">package</span><span style="color: #000000">&nbsp;test;<br />
</span><span style="color: #008080">&nbsp;2</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">&nbsp;3</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;java.sql.Connection;<br />
</span><span style="color: #008080">&nbsp;4</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;java.sql.DriverManager;<br />
</span><span style="color: #008080">&nbsp;5</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;java.sql.ResultSet;<br />
</span><span style="color: #008080">&nbsp;6</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;java.sql.Statement;<br />
</span><span style="color: #008080">&nbsp;7</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">&nbsp;8</span><span style="color: #000000"><img id="Codehighlighter1_153_647_Open_Image" onclick="this.style.display='none'; Codehighlighter1_153_647_Open_Text.style.display='none'; Codehighlighter1_153_647_Closed_Image.style.display='inline'; Codehighlighter1_153_647_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_153_647_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_153_647_Closed_Text.style.display='none'; Codehighlighter1_153_647_Open_Image.style.display='inline'; Codehighlighter1_153_647_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top"  alt="" /></span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;jdbcsample&nbsp;</span><span id="Codehighlighter1_153_647_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_153_647_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">&nbsp;9</span><span style="color: #000000"><img id="Codehighlighter1_212_645_Open_Image" onclick="this.style.display='none'; Codehighlighter1_212_645_Open_Text.style.display='none'; Codehighlighter1_212_645_Closed_Image.style.display='inline'; Codehighlighter1_212_645_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_212_645_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_212_645_Closed_Text.style.display='none'; Codehighlighter1_212_645_Open_Image.style.display='inline'; Codehighlighter1_212_645_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main(String[]&nbsp;argc)&nbsp;</span><span style="color: #0000ff">throws</span><span style="color: #000000">&nbsp;Exception&nbsp;</span><span id="Codehighlighter1_212_645_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_212_645_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">10</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;connectionURL&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">jdbc:log4jdbc:mysql://127.0.0.1:3306/sampledb</span><span style="color: #000000">"</span><span style="color: #000000">;<br />
</span><span style="color: #008080">11</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">12</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class.forName(</span><span style="color: #000000">"</span><span style="color: #000000">net.sf.log4jdbc.DriverSpy</span><span style="color: #000000">"</span><span style="color: #000000">).newInstance();<br />
</span><span style="color: #008080">13</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connection&nbsp;connection&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;DriverManager.getConnection(connectionURL,<br />
</span><span style="color: #008080">14</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">root</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">1234</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">15</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">16</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Statement&nbsp;statement&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;connection.createStatement();<br />
</span><span style="color: #008080">17</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;statement.executeQuery(</span><span style="color: #000000">"</span><span style="color: #000000">SELECT&nbsp;*&nbsp;FROM&nbsp;customers</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">18</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">19</span><span style="color: #000000"><img id="Codehighlighter1_583_627_Open_Image" onclick="this.style.display='none'; Codehighlighter1_583_627_Open_Text.style.display='none'; Codehighlighter1_583_627_Closed_Image.style.display='inline'; Codehighlighter1_583_627_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_583_627_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_583_627_Closed_Text.style.display='none'; Codehighlighter1_583_627_Open_Image.style.display='inline'; Codehighlighter1_583_627_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">while</span><span style="color: #000000">&nbsp;(rs.next())&nbsp;</span><span id="Codehighlighter1_583_627_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_583_627_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">20</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(rs.getString(</span><span style="color: #000000">1</span><span style="color: #000000">));<br />
</span><span style="color: #008080">21</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">22</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rs.close();<br />
</span><span style="color: #008080">23</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">24</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">25</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"  alt="" />}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">26</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span></div>
<br />
jar file:<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #000000">add&nbsp;the&nbsp;jar&nbsp;file:<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" />log4jdbc3</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">.2beta1.jar<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" />mysql</span><span style="color: #000000">-</span><span style="color: #000000">connector</span><span style="color: #000000">-</span><span style="color: #000000">java</span><span style="color: #000000">-</span><span style="color: #000000">5.1</span><span style="color: #000000">.</span><span style="color: #000000">12</span><span style="color: #000000">-</span><span style="color: #000000">bin.jar</span></div>
<br />
3)Hibernate SQL Logging: log4jdbc / Haciendo log de SQL con Hibernate: log4jdbc<br />
<a href="http://softdevbuilttolast.wordpress.com/2010/02/22/hibernate-sql-logging-log4jdbc-haciendo-log-de-sql-con-hibernate-log4jdbc/">http://softdevbuilttolast.wordpress.com/2010/02/22/hibernate-sql-logging-log4jdbc-haciendo-log-de-sql-con-hibernate-log4jdbc/<br />
</a><br />
//confgi the hibernate with the proxy connection<br />
<p>hibernate.dialect=net.sf.hibernate.dialect.MySQLDialect<br />
hibernate.connection.driver_class=net.sf.log4jdbc.DriverSpy<br />
hibernate.connection.url=jdbc:log4jdbc:mysql://localhost:3306/SAMPLEDB<br />
hibernate.connection.username=root<br />
hibernate.connection.password=1234<br />
hibernate.show_sql=false</p>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/319762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2010-04-30 10:53 <a href="http://www.blogjava.net/jinfeng_wang/archive/2010/04/30/319762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>pure java call jolt services</title><link>http://www.blogjava.net/jinfeng_wang/archive/2010/02/26/313964.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 26 Feb 2010 01:17:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2010/02/26/313964.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/313964.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2010/02/26/313964.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/313964.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/313964.html</trackback:ping><description><![CDATA[<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #0000ff">package</span><span style="color: #000000">&nbsp;IVR.client.ui;<br />
</span><span style="color: #008080">&nbsp;2</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">&nbsp;3</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;bea.jolt.JoltDefinition;<br />
</span><span style="color: #008080">&nbsp;4</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;bea.jolt.JoltMessage;<br />
</span><span style="color: #008080">&nbsp;5</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;bea.jolt.JoltRemoteService;<br />
</span><span style="color: #008080">&nbsp;6</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;bea.jolt.JoltSession;<br />
</span><span style="color: #008080">&nbsp;7</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;bea.jolt.JoltSessionAttributes;<br />
</span><span style="color: #008080">&nbsp;8</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;bea.jolt.JoltTransaction;<br />
</span><span style="color: #008080">&nbsp;9</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">10</span><span style="color: #000000"><img id="Codehighlighter1_240_1895_Open_Image" onclick="this.style.display='none'; Codehighlighter1_240_1895_Open_Text.style.display='none'; Codehighlighter1_240_1895_Closed_Image.style.display='inline'; Codehighlighter1_240_1895_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_240_1895_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_240_1895_Closed_Text.style.display='none'; Codehighlighter1_240_1895_Open_Image.style.display='inline'; Codehighlighter1_240_1895_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top"  alt="" /></span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;test&nbsp;</span><span id="Codehighlighter1_240_1895_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_240_1895_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">11</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">12</span><span style="color: #000000"><img id="Codehighlighter1_247_276_Open_Image" onclick="this.style.display='none'; Codehighlighter1_247_276_Open_Text.style.display='none'; Codehighlighter1_247_276_Closed_Image.style.display='inline'; Codehighlighter1_247_276_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_247_276_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_247_276_Closed_Text.style.display='none'; Codehighlighter1_247_276_Open_Image.style.display='inline'; Codehighlighter1_247_276_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span id="Codehighlighter1_247_276_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**&nbsp;*/</span><span id="Codehighlighter1_247_276_Open_Text"><span style="color: #008000">/**</span><span style="color: #008000"><br />
</span><span style="color: #008080">13</span><span style="color: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080">@param</span><span style="color: #008000">&nbsp;args<br />
</span><span style="color: #008080">14</span><span style="color: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">*/</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">15</span><span style="color: #000000"><img id="Codehighlighter1_321_1892_Open_Image" onclick="this.style.display='none'; Codehighlighter1_321_1892_Open_Text.style.display='none'; Codehighlighter1_321_1892_Closed_Image.style.display='inline'; Codehighlighter1_321_1892_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_321_1892_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_321_1892_Closed_Text.style.display='none'; Codehighlighter1_321_1892_Open_Image.style.display='inline'; Codehighlighter1_321_1892_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span id="Codehighlighter1_321_1892_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_321_1892_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">16</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JoltSessionAttributes&nbsp;sattr&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">null</span><span style="color: #000000">;<br />
</span><span style="color: #008080">17</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JoltRemoteService&nbsp;joltServices&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">null</span><span style="color: #000000">;<br />
</span><span style="color: #008080">18</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JoltTransaction&nbsp;trans&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">null</span><span style="color: #000000">;<br />
</span><span style="color: #008080">19</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;userName&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">""</span><span style="color: #000000">;<br />
</span><span style="color: #008080">20</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;userPassword&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">""</span><span style="color: #000000">;<br />
</span><span style="color: #008080">21</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;appPassword&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">""</span><span style="color: #000000">;<br />
</span><span style="color: #008080">22</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;userRole&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">""</span><span style="color: #000000">;<br />
</span><span style="color: #008080">23</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;outstr;<br />
</span><span style="color: #008080">24</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sattr&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;JoltSessionAttributes();<br />
</span><span style="color: #008080">25</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">26</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sattr.setString(sattr.APPADDRESS,&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">//16.162.93.226:8327</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">27</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">28</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sattr.setInt(sattr.IDLETIMEOUT,&nbsp;</span><span style="color: #000000">300</span><span style="color: #000000">);<br />
</span><span style="color: #008080">29</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">30</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bea.jolt.JoltSession&nbsp;session&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;JoltSession(sattr,&nbsp;userName,<br />
</span><span style="color: #008080">31</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;userRole,&nbsp;userPassword,&nbsp;appPassword);<br />
</span><span style="color: #008080">32</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">33</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joltServices&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;JoltRemoteService(</span><span style="color: #000000">"</span><span style="color: #000000">SwtNnsy</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;session);<br />
</span><span style="color: #008080">34</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">35</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">36</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">37</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joltServices.setStringItem(</span><span style="color: #000000">"</span><span style="color: #000000">sw01_autelno</span><span style="color: #000000">"</span><span style="color: #000000">,</span><span style="color: #000000">0</span><span style="color: #000000">,&nbsp;&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">autelno</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">38</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joltServices.setStringItem(</span><span style="color: #000000">"</span><span style="color: #000000">sw01_custcdv</span><span style="color: #000000">"</span><span style="color: #000000">,</span><span style="color: #000000">0</span><span style="color: #000000">,&nbsp;&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">custcdv</span><span style="color: #000000">"</span><span style="color: #000000">);&nbsp;<br />
</span><span style="color: #008080">39</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joltServices.setStringItem(</span><span style="color: #000000">"</span><span style="color: #000000">sw01_codenov</span><span style="color: #000000">"</span><span style="color: #000000">,</span><span style="color: #000000">0</span><span style="color: #000000">,&nbsp;&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">nov</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">40</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joltServices.setStringItem(</span><span style="color: #000000">"</span><span style="color: #000000">sw01_certsvckbn</span><span style="color: #000000">"</span><span style="color: #000000">,</span><span style="color: #000000">0</span><span style="color: #000000">,&nbsp;&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">svc</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">41</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joltServices.setStringItem(</span><span style="color: #000000">"</span><span style="color: #000000">sw01_ngcount</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">,&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">1</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">42</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joltServices.setStringItem(</span><span style="color: #000000">"</span><span style="color: #000000">sw01_ngpassword1</span><span style="color: #000000">"</span><span style="color: #000000">,</span><span style="color: #000000">0</span><span style="color: #000000">,&nbsp;&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">ngpassword1</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">43</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joltServices.setStringItem(</span><span style="color: #000000">"</span><span style="color: #000000">sw01_ngpassword2</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">,&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">ngpassword2</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">44</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joltServices.setStringItem(</span><span style="color: #000000">"</span><span style="color: #000000">sw01_ngpassword3</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">,&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">ngpassword3</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">45</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">46</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">47</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">48</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">49</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joltServices.call(</span><span style="color: #0000ff">null</span><span style="color: #000000">);<br />
</span><span style="color: #008080">50</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">51</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JoltDefinition&nbsp;jd&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;joltServices.getDefinition();<br />
</span><span style="color: #008080">52</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JoltMessage&nbsp;jm&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;joltServices.getOutputs();<br />
</span><span style="color: #008080">53</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String[]&nbsp;strPar&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;jm.toString().split(</span><span style="color: #000000">"</span><span style="color: #000000">\</span><span style="color: #000000">"</span><span style="color: #000000">:</span><span style="color: #000000">"</span><span style="color: #000000">);</span><span style="color: #000000"><br />
</span><span style="color: #008080">54</span><span style="color: #000000"><img id="Codehighlighter1_1791_1854_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1791_1854_Open_Text.style.display='none'; Codehighlighter1_1791_1854_Closed_Image.style.display='inline'; Codehighlighter1_1791_1854_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_1791_1854_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_1791_1854_Closed_Text.style.display='none'; Codehighlighter1_1791_1854_Open_Image.style.display='inline'; Codehighlighter1_1791_1854_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">for</span><span style="color: #000000">&nbsp;(</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;&nbsp;i&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;strPar.length;&nbsp;i</span><span style="color: #000000">++</span><span style="color: #000000">)&nbsp;</span><span id="Codehighlighter1_1791_1854_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_1791_1854_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">55</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">item:</span><span style="color: #000000">"</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">+</span><span style="color: #000000">&nbsp;strPar[i]);<br />
</span><span style="color: #008080">56</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">57</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">58</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;session.endSession();<br />
</span><span style="color: #008080">59</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">60</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">61</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">62</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"  alt="" />}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">63</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span></div>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/313964.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2010-02-26 09:17 <a href="http://www.blogjava.net/jinfeng_wang/archive/2010/02/26/313964.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>文字コードについて（シフトJISの問題） zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/11/27/303975.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 27 Nov 2009 12:51:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/11/27/303975.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/303975.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/11/27/303975.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/303975.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/303975.html</trackback:ping><description><![CDATA[<table cellspacing="5" cellpadding="0" width="100%" bgcolor="#ddffff" border="0">
    <tbody>
        <tr>
            <td width="100%">
            <p>&nbsp;</p>
            <p>http://park3.wakwak.com/~ozashin/sw_tips/webapp_tips/sjis_charset.html<br />
            <br />
            文字コードについて（シフトJISの問題）</p>
            </td>
        </tr>
    </tbody>
</table>
<p>文字コードをシフトJISで開発し、Windowsのサーバで動かす場合の文字コード問題について示します。</p>
<p>厳密にいうとWindowsが扱う文字コードは、シフトＪＩＳでは、ありません。MS932です。または、コードページ CP932ともいいます。MS932は、マ社がシフトＪＩＳを拡張して定義したコード体系です。</p>
<p>で、ここで何が問題になるかというと、クライアントへの出力をcharset="Shift_JIS"とか定義して、アプリケーションサーバーなど が一生懸命シフトＪＩＳで出力しようとすると、ある文字列が文字化けするのです。ようは、MS932にある文字コードはシフトＪＩＳには、ないので、'? 'で出力されるということです。なお、OC4JやJBuilderで利用するtomcatは、MS932で出力しようとするので、あんまり文字化けしませ ん。</p>
<p>Java内部で扱う文字コードは、Unicodeです。ここでシフトＪＩＳとUnicodeのマッピングとMS932とUnicodeのマッピング の違いによって、思わぬ、文字化けが発生するのです。ちなみに、JSPファイルにじか書きしてあるもじは、そのまま出力されるので問題はありません。<br />
Javaプログラムを通して、Unicodeからcharsetで定義された文字が出力されるとき文字化けの対象となります。</p>
<p>具体的には、'～'、'∥'、'－'、'￠'、'￡'、'￢'などの一部の記号類です。<br />
日本語のUnicodeベンダ依存文字表 <a href="http://www.ingrid.org/java/i18n/unicode.html" target="_blank">http://www.ingrid.org/java/i18n/unicode.html</a> を参照してください。</p>
<p>というわけで、Windowsで動かすのであれば、charsetを"Windows-31J"にしましょう！そうすれば、一部の文字コードを除い て解決です。<br />
JSPの場合は、<strong>&lt;%@ page contentType="text/html; charset=Windows-31J" %&gt;</strong>と定義します。<br />
Servletでは、</p>
<pre>	private static final String CONTENT_TYPE = "text/html; charset=Windows-31J";
....
response.setContentType(CONTENT_TYPE);
</pre>
<p>のように実装します。charset="MS932"でもＯＫですが、<a href="http://www.ingrid.org/java/i18n/encoding/shift_jis.html" target="_blank">http://www.ingrid.org/java/i18n/encoding/shift_jis.html</a>に あるとおり、これからは、Windows-31Jでいくようです。</p>
<hr />
<strong>WAVE DASH問題（TILDE問題）</strong>
<p>シフトＪＩＳがらみでもうひとつWAVE DASH問題（TILDE問題）というのがあります。これは、JDBCがらみというか、データベースの文字セットの問題というか、誰が根本的な原因かわか らないところがあります。Unicodeの実装が厳密に定義されていないので、ベンダごとで実装の仕方が違っているそうです。</p>
<p>Oracleを例にとって文字の流れを以下に示します。テスト環境は以下のとおり。</p>
<blockquote>
<table cellspacing="0" cellpadding="3" width="80%" border="1">
    <tbody>
        <tr>
            <td width="21%">OS</td>
            <td width="79%">Windows2000</td>
        </tr>
        <tr>
            <td width="21%">データベースサーバ</td>
            <td width="79%">Oracle8i R8.1.7</td>
        </tr>
        <tr>
            <td width="21%">ＤＢ内部コード</td>
            <td width="79%">JA16SJIS</td>
        </tr>
    </tbody>
</table>
</blockquote>
<p>ある入力フィールドに '～' を含んだ文字列を設定し、それをデータベースへ格納し、参照する場合、文字変換の流れは以下のようになります。</p>
<ol>
    <li>ブラウザ上では、Windows-31J(<strong>MS932</strong>)なので、'～'=<strong>0x8160</strong>
    <li>Javaプログラム内部では、<strong>Unicode</strong>なので、JisAutoDetectなどで変換して、'～'=<strong>0xFF5E (FULL WIDTH TILDE)</strong>
    <li>データベースに格納された結果は、<strong>JA16SJIS</strong>なので、'～'=<strong>0x8160</strong>
    <li>Javaプログラムがデータベースから文字をGETしたとき、'～'=<strong>0x301C (WAVE DASH)</strong>
    <li>0x301Cを応答として出力すると、0x8160にマッピングできないので <strong>'?'</strong>となる。 </li>
</ol>
<p>この問題は、上記のようにcharsetをWindows31Jにしても回避できません。</p>
<p>ちなみに、Unicodeでいうところの '～' は、0x301C (WAVE DASH)になるのが、Unicodeコンソーシアムでは正しい実装方法だそうです。</p>
<p>(4)で、0x301Cに変換されているんだからＯＫじゃん、と思いますが、0x8160を0xFF5Eに変換するんだから、0x8160にするに は、0xFF5Eにしてあげなければならないのです。</p>
<p>この問題を回避するには、２通りあります。</p>
<ol>
    <li>(4)で0x301Cで変換された文字を0xFF5Eに再変換する。
    <li>Oracle9i R9.2以降でMS932がサポートされたデータベース内部コード "JA16SJISTILDE"を利用する。 </li>
</ol>
<p>しかし、(1)では、データベースをアクセスするたび、毎回再変換するので、パフォーマンスが落ちます。(2)では、いまさらOracle9に移行 できる予算がないし、検証しているひまもないという問題があります。<br />
したがって、第３の回避方法として、運用で逃げる（'～'は、システム上扱えないコードとしてユーザに我慢してもらう）という手もあります。</p>
<p>ちなみに、私は、多少パフォーマンスが落ちてもＯＫということで、(1)で再変換の道を選びました。</p>
<p>まぁ、最初からOracle9iR9.2を選んでいれば、ＯＫですけどね。また、検証はしていませんが、EUC-JPでも同様の問題があって、 Oracle9iR9.2からJA16EUCTILDEがサポートされているようです。</p>
<hr />
<strong>参考（Javaのデフォルトコンバータの推移）</strong>
<p>Windowsで動かし、JISAutoで変換した場合のデフォルトコンバータは、誰の都合か知りませんが、以下のように変化しました。<br />
まぁ、Windowsで動かす限り、Windows-31Jをcharsetにしておけば、JDK1.4.1以降でもＯＫのようです。</p>
<table cellspacing="0" cellpadding="3" width="80%" border="1">
    <tbody>
        <tr>
            <td align="center" width="50%" bgcolor="#e6e6e6"><strong>JDKのバージョン</strong></td>
            <td align="center" width="50%" bgcolor="#e6e6e6"><strong>デフォルトコンバータ</strong></td>
            <td align="center" width="50%" bgcolor="#e6e6e6"><strong>実装されたコンバータ</strong></td>
        </tr>
        <tr>
            <td width="50%">JDK 1.1.7以前</td>
            <td width="50%">SJISコンバータ</td>
            <td width="50%">　</td>
        </tr>
        <tr>
            <td width="50%">JDK 1.1.8</td>
            <td width="50%">SJISコンバータ</td>
            <td width="50%">MS932コンバータが実装される</td>
        </tr>
        <tr>
            <td width="50%">JDK 1.2 ～ 1.4.0</td>
            <td width="50%">MS932コンバータ</td>
            <td width="50%">　</td>
        </tr>
        <tr>
            <td width="50%">JDK 1.4.1</td>
            <td width="50%">SJISコンバータ</td>
            <td width="50%">　</td>
        </tr>
        <tr>
            <td width="50%">それ以降</td>
            <td width="50%">保証なし？</td>
            <td width="50%">　</td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/303975.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-11-27 20:51 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/11/27/303975.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JMX を使用する監視と管理 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/11/12/240000.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Wed, 12 Nov 2008 01:53:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/11/12/240000.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/240000.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/11/12/240000.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/240000.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/240000.html</trackback:ping><description><![CDATA[http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#remote<br />
<br />
<br />
<h1>JMX を使用する監視と管理</h1>
<p>Java 仮想マシン (JVM) には、JMX を使って JVM の監視と管理を可能にする機能が組み込まれています。また、実装されたアプリケーションを JMX で監視することもできます。</p>
<ul>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#sysprops">JMX 用のシステムプロパティの設定</a>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#jmxagent">JMX エージェントの有効化</a>
    <ul>
        <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#local">ローカルの監視と管理</a>
        <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#remote">リモートの監視と管理</a> </li>
    </ul>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#connecting">プログラムによる JMX エージェントへの接続</a>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#PasswordAccessFiles">パスワードおよびアクセスファイルの使用</a>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#properties">JMX 管理および監視プロパティ</a> </li>
</ul>
<h2><a id="sysprops" name="sysprops"></a>システムプロパティの設定</h2>
<p>JMX エージェントを有効にして、動作を設定するには、JVM を起動するときに特定のシステムプロパティを設定する必要があります。コマンド行上で次のようにシステムプロパティを設定します。</p>
<pre>java -D<em>property</em>=<em>value</em> ...<br />
</pre>
<p>この方法で任意の数のシステムプロパティを設定できます。値を指定しない場合、デフォルト値で設定されます。JMX 管理プロパティのフルセットは、<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#mmprops_table">表 1</a> に記載されています。<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#properties">JMX 管理および監視プロパティ</a>に記載したように設定ファイルでシステムプロパティを設定することもできます。</p>
<p><strong>注</strong>:<code>java</code> (Java VM) をコマンド行から実行するには、<code><em>JRE_HOME</em>/bin</code> をパスに追加する必要があります。ここで <em><code>JRE_HOME</code></em> は、JRE (Java Runtime Environment) を含むディレクトリです。代わりにコマンドを入力するときにフルパスを入力することもできます。</p>
<p>以下のドキュメントは、Java HotSpot VM によってサポートされている構文とコマンド行オプションのフルセットについて説明しています。</p>
<ul>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/tooldocs/windows/java.html">Microsoft Windows 用 Java アプリケーション起動ツール</a>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/tooldocs/solaris/java.html">Solaris オペレーティング環境用 Java アプリケーション起動ツール</a>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/tooldocs/linux/java.html">Linux 用 Java アプリケーション起動ツール</a> </li>
</ul>
<h2><a id="jmxremote" name="jmxagent"></a>JMX エージェントの有効化</h2>
<p>JMX によって Java プラットフォームを監視するには、以下の手順に従ってください。</p>
<ol>
    <li>JVM を起動するときに JMX エージェント (MBean サーバ) を有効にします。以下に対して JMX エージェントを有効にすることができます。
    <ul>
        <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#local">ローカル監視</a> - ローカルシステムで動作するクライアント管理アプリケーション用。
        <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#remote">リモート監視</a> - リモートシステムで動作するクライアント管理アプリケーション用。 </li>
    </ul>
    <li>JVM を <code>jconsole</code> などの JMX 準拠のツールで監視します。詳細は、<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/jconsole.html">「jconsole の使用」</a>を参照してください。 </li>
</ol>
<h3><a id="enablingjmxagent" name="local"></a>ローカルの監視と管理</h3>
<p>ローカルアクセスに対して JMX エージェントを有効にするには、JVM または Java アプリケーションを起動するときにこのシステムプロパティを設定します。</p>
<pre>com.sun.management.jmxremote</pre>
<p>このプロパティを設定することにより、JVM 実装 MBean を登録し、RMI コネクタを専用インタフェース経由で公開して、JMX クライアントアプリケーションがローカルの Java プラットフォーム、すなわち、同一マシン上で動作する JVM を監視できるようにします。</p>
<p>たとえば、サンプルアプリケーションの <em>Notepad</em> 用の JMX エージェントは、以下のようにして有効にします。</p>
<pre>cd <em>JDK_HOME</em>/demo/jfc/Notepad<br />
java -Dcom.sun.management.jmxremote -jar Notepad.jar</pre>
<p>ここで、<em>JDK_HOME</em> は、JDK がインストールされているディレクトリです。</p>
<h4>jconsole の使用</h4>
<p><code>jconsole</code> によるローカルの監視は、開発およびプロトタイプ作成に便利です。<code>jconsole</code> 自体がかなりのシステムリソースを消費するため、<code>jconsole</code> をローカルで実稼働環境に使用することはお勧めしません。それよりは、jconsole をリモートシステムで使用して、監視されるプラットフォームから分離してください。</p>
<p>コマンドシェルで <code>jconsole</code> と入力して、jconsole を起動します。引数なしで jconsole を起動する場合、jconsole は自動的にすべてのローカル Java アプリケーションを検出し、監視したいアプリケーションを選択できるダイアログボックスを表示します。システムの監視には、オペレーティングシステムのファイルアクセス権が必要であるため、<code>jconsole</code> とアプリケーションは両方とも、同じユーザ名で実行する必要があります。</p>
<p><strong>注</strong>:<code>jconsole</code> をコマンド行から実行するには、<em><code>JAVA_HOME</code></em> が JDK を含むディレクトリになっているパスに、<code><em>JAVA_HOME</em>/bin</code> を追加する必要があります。あるいは、コマンドを入力するときにフルパスを入力することもできます。</p>
<p>jconsole の使用についての詳細は、<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/jconsole.html">「jconsole の使用」</a>を参照してください。</p>
<h3><a id="remote" name="remote"></a>リモートの監視と管理</h3>
<p>リモートシステムからの監視と管理を可能にするには、JVM を起動するときにこのシステムプロパティを設定します。 </p>
<pre>com.sun.management.jmxremote.port=<em>portNum</em></pre>
<p>ここで、<em>portNum </em>は、JMX/RMI 接続を有効にしたいポート番号です。必ず未使用のポート番号を指定してください。ローカルアクセスに対して RMI コネクタを公開するのに加えて、このプロパティを設定することにより、標準名「jmxrmi」を使用して、特定のポートのプライベートな読み取り専用レジストリで追加の RMI コネクタを公開します。 </p>
<p><strong>注</strong>:以下に説明されているように、セキュリティ用に設定するプロパティの他に上記のシステムプロパティを設定する必要があります。 </p>
<p>リモートの監視と管理には、権限のないユーザがアプリケーションを制御または監視できないようにするためにセキュリティが必要です。デフォルトで、 SSL (secure sockets layer) 経由のパスワード認証が有効になっています。以下のセクションで説明されているように、パスワード認証と SSL を個別に無効にすることができます。 </p>
<ul>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#auth">パスワード認証の使用</a>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#auth_disabled">パスワード認証の無効化</a>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#SSL_enabled">SSL の使用</a>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#client_ssl">クライアント SSL 認証の使用</a>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#SSL_disabled">SSL の無効化</a>
    <li><a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#no_security">セキュリティの無効化</a> </li>
</ul>
<p>JMX エージェントをリモートで使用できるようにしたら、<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#jconsole_remote">「jconsole によるリモート監視」</a>で説明されているように jconsole を使ってアプリケーションを監視できます。</p>
<h4><a id="auth" name="auth"></a>パスワード認証の使用</h4>
<p>デフォルトでは、リモート監視で JMX エージェントを有効にすると、JMX エージェントはパスワード認証を使用します。ただし、パスワードを設定する方法は、シングルユーザ環境にいるか、マルチユーザ環境にいるかによって異なります。 </p>
<p>パスワードはパスワードファイルにクリアテキストで格納されるため、監視用の通常のユーザ名とパスワードを使用することはお勧めできません。代わりに、<code>monitorRole</code> および <code>controlRole</code> などのパスワードファイルで指定したユーザ名を使用します。詳細は、<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#PasswordAccessFiles">「パスワードおよびアクセスファイルの使用」</a>を参照してください。</p>
<p><strong>シングルユーザ環境</strong>: <code><em>JRE_HOME</em>/lib/management</code> ディレクトリで以下の手順でパスワードファイルを設定します。</p>
<ol>
    <li>パスワードのテンプレートファイル、<code>jmxremote.password.template</code> を <code>management.jmxremote.password</code> にコピーします。
    <li>ファイルアクセス権を設定して、自分だけがパスワードファイルを読み取りおよび書き込みできるようにします。
    <li><code>monitorRole</code> や <code>controlRole</code> などのロールに対するパスワードを追加します。 </li>
</ol>
<p><strong>マルチユーザ環境</strong>: <code><em>JRE_HOME</em>/lib/</code><code>management</code>ディレクトリで以下の手順でパスワードファイルを設定します。</p>
<ol>
    <li>パスワードのテンプレートファイル、<code>jmxremote.password.template</code> をホームディレクトリにコピーします。
    <li>ファイルアクセス権を設定して、所有者だけがパスワードファイルを読み取りおよび書き込みできるようにします。
    <li><code>monitorRole</code> や <code>controlRole</code> などのロールに対するパスワードを追加します。
    <li>JVM を起動するときにシステムプロパティを設定します。
    <pre>com.sun.management.jmxremote.password.file=<em>pwFilePath</em></pre>
    ここで、<em>pwFilePath</em> はパスワードファイルへのパスです。 </li>
</ol>
<a name="security_warning"></a>
<table height="32" cellspacing="2" cellpadding="4" width="605" align="center" bgcolor="#dddddd" border="0">
    <tbody>
        <tr>
            <td valign="top"><strong>警告:</strong>クライアントがセキュリティ保護されていない RMI レジストリ (デフォルト) からリモートコネクタを取得すると、JMX リモートコネクタからのパスワード認証でセキュリティの問題が起こる可能性があります。攻撃者がターゲットサーバ上で正当な RMI レジストリが開始される前に偽の RMI レジストリを開始すると、攻撃者はクライアントのパスワードを盗むことができます。このシナリオは、システムプロパティ <code>com.sun.management.jmxremote.port=<em>portNum</em></code> でリモート管理を有効にして JVM を起動する場合も同じです。SSL が有効になっていても同じです。このような攻撃者は発見されることが多いものの、脆弱性があることは確かです。 <br />
            <br />
            この問題を避けるため、認証にはパスワードの代わりに SSL クライアント認証を使用してください。または、クライアントがリモートコネクタオブジェクトを安全に (セキュリティ保護された LDAP 経由で、または共有のセキュリティ保護されたファイルシステムにあるファイルを経由してなど) 取得するようにしてください。 <br />
            <br />
            将来のリリースで、この問題は修正される予定です。 </td>
        </tr>
    </tbody>
</table>
<h4><a id="auth_disabled" name="auth_disabled"></a>パスワード認証の無効化</h4>
<p>リモート監視のパスワード認証は、デフォルトで有効になっています。パスワード認証を無効にするには、JVM を起動するときに以下のシステムプロパティを設定します。 </p>
<pre>com.sun.management.jmxremote.authenticate=false</pre>
<p>ここで、<em>portNum</em> は、使用するポート番号です。必ず未使用のポート番号を指定してください。 </p>
<p>パスワード認証を無効にする場合、<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#no_security">「セキュリティの無効化」</a>で説明しているように SSL を無効にすることもできます。「クライアントの SSL 認証の有効化」で説明しているようにパスワードを無効にして、SSL クライアント認証を有効にしたい場合もあります。 </p>
<p><strong>警告</strong>: この構成は安全ではありません。JMX ポート番号およびホスト名を知っている (または推測する) リモートユーザが Java アプリケーションおよびプラットフォームを監視および制御できます。開発用のシステムなら許容されるかもしれませんが、実稼働システムにはお勧めしません。 </p>
<h4><a id="SSL_enabled" name="SSL_enabled"></a>SSL の使用</h4>
<p>SSL (Secure sockets layer) は、リモート監視および管理を有効にするときにデフォルトで有効になっています。SSL を使用するには、JMX エージェント (MBean サーバ) が動作するシステム上でデジタル証明書を設定して、次に SSL を正しく設定します。コマンド行ユーティリティ <code>keytool</code> を使用して、証明書を操作します。通常の手順は以下のとおりです。 </p>
<ol>
    <li>サーバ上でまだ鍵ペアと証明書を設定していない場合は以下の手順に従います。
    <ol>
        <li type="a"><code>keytool -genkey</code> コマンドで鍵ペアを生成します。
        <li type="a"><code>keytool -certreq</code> コマンドで認証局 (CA) に署名付き証明書を要求します。
        <li type="a"><code>keytool -import</code> コマンドで証明書をキーストアにインポートします。<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/tooldocs/solaris/keytool.html#ImportCertificate">「証明書のインポート」</a>を参照してください。<br />
        詳細および例については、「keytool - 鍵および証明書管理ツール」<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/tooldocs/solaris/keytool.html#EXAMPLES">(Solaris および Linux)</a> <a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/tooldocs/windows/keytool.html#EXAMPLES">(Windows)</a>を参照してください。 </li>
    </ol>
    <li>サーバシステム上で SSL を設定します。このドキュメントでは、SSL の設定とカスタマイズについて詳細には説明しませんが、通常、次の表に記載されている<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#sysprops">システムプロパティ</a>を設定する必要があります。詳細は、JSSE ガイドの<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/security/jsse/JSSERefGuide.html#CustomizingStores">「デフォルト鍵およびトラストストア、ストアタイプ、およびストアパスワードのカスタマイズ」</a>を参照してください。 </li>
</ol>
<p>&nbsp;</p>
<table height="212" cellspacing="2" cellpadding="2" width="70%" align="center" border="0">
    <caption>SSL 構成のシステムプロパティ</caption>
    <tbody>
        <tr>
            <th>システムプロパティ</th>
            <th>説明</th>
        </tr>
        <tr>
            <td><code>javax.net.ssl.keyStore</code> </td>
            <td>キーストアの場所</td>
        </tr>
        <tr>
            <td><code>javax.net.ssl.trustStore</code> </td>
            <td>トラストストアの場所</td>
        </tr>
        <tr>
            <td><code>javax.net.ssl.keyStoreType</code> </td>
            <td>デフォルトのキーストア型 <code></code></td>
        </tr>
        <tr>
            <td><code>javax.net.ssl.keyStorePassword</code> </td>
            <td>デフォルトのキーストアパスワード</td>
        </tr>
        <tr>
            <td><code>javax.net.ssl.trustStoreType</code> </td>
            <td>デフォルトのトラストストア型</td>
        </tr>
        <tr>
            <td><code>javax.net.ssl.trustStorePassword</code> </td>
            <td>デフォルトのトラストストアパスワード</td>
        </tr>
    </tbody>
</table>
<p>詳細は、「keytool - 鍵および証明書管理ツール<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/tooldocs/solaris/keytool.html#EXAMPLES">(Solaris および Linux)</a> <a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/tooldocs/windows/keytool.html#EXAMPLES">(Windows)</a>」および<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/security/jsse/JSSERefGuide.html">「JSSE ガイド」</a>を参照してください。 </p>
<h4><a name="client_ssl"></a>クライアント SSL 認証の有効化</h4>
<p>クライアント SSL 認証を有効にするには、JVM を起動するときにこのシステムプロパティを設定します。 </p>
<pre>com.sun.management.jmxremote.ssl.need.client.auth=true</pre>
<p>クライアント SSL 認証を使用するには、SSL を有効 (デフォルト) にしておく必要があります。 </p>
<p>この構成では、クライアントシステムが有効なデジタル証明書を持つ必要があります。<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/agent.html#SSL_enabled">「SSL の使用」</a>で説明したとおりに証明書をインストールして、SSL を設定する必要があります。 </p>
<h4><a id="SSL_disabled" name="SSL_disabled"></a>SSL の無効化</h4>
<p>リモート監視で SSL を無効にするには、JVM を起動するときにこのシステムプロパティを設定します。</p>
<pre>com.sun.management.jmxremote.ssl=false</pre>
<p>パスワード認証は、<a href="" auth_disabled="no_security" name="no_security"></a>セキュリティの無効化</h4>
<p>パスワード認証と SSL の両方を無効 (セキュリティなし) にするには、JVM を起動するときにこれらのシステムプロパティを設定します。</p>
<pre>com.sun.management.jmxremote.authenticate=false<br />
com.sun.management.jmxremote.ssl=false</pre>
<p><strong>警告</strong>:この構成は安全ではありません。JMX ポート番号およびホスト名を知っている (または推測する) リモートユーザが Java アプリケーションおよびプラットフォームを監視および制御できます。開発用のシステムなら許容されるかもしれませんが、実稼働システムにはお勧めしません。</p>
<h4><a id="jconsole_remote" name="jconsole_remote"></a>jconsole によるリモート監視</h4>
<p>リモートアプリケーションを監視するには、次のように <code>jconsole</code> を起動します。</p>
<pre>jconsole <em>hostName</em>:<em>portNum</em></pre>
<p>ここで、<em>hostName</em> は、アプリケーションを実行するシステム名で、<em>portNum</em> は、JVM を起動するときに指定したポート番号です。ホスト名とポート番号を省略して、<code>jconsole </code>が提供するダイアログボックスに入力することもできます。 </p>
<p><strong>注</strong>: <code>jconsole</code> をコマンド行から実行するには、<em><code>JAVA_HOME</code></em> が JDK を含むディレクトリになっているパスに、<code><em>JAVA_HOME</em>/bin</code> を追加する必要があります。あるいは、コマンドを入力するときにフルパスを入力することもできます。</p>
<p>jconsole の使用についての詳細は、<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/jconsole.html">「jconsole の使用」</a>を参照してください。</p>
<h2><a id="connecting" name="connecting"></a>プログラムによる JMX エージェントへの接続</h2>
<p>JMX エージェントを有効にしたら、以下の URL を使ってサービスにアクセスできます。</p>
<pre>service:jmx:rmi:///jndi/rmi://<em>hostName</em>:<em>portNum</em>/jmxrmi</pre>
<p>ここで、<em>hostName</em> はホスト名で <em>portNum</em> は、JMX エージェントを有効にしたときに指定したポート番号です。</p>
<p>次のように、URL を使って、<code>javax.management.remote.JMXServiceURL</code> オブジェクトのインスタンスを生成し、次に <code>JMXConnectorFactory.connect</code> メソッドを使用して接続を作成することにより、クライアントはエージェントへのコネクタを作成できます。</p>
<pre>JMXServiceURL u = new JMXServiceURL(<br />
"service:jmx:rmi:///jndi/rmi:// &#8220; + hostName + ":"+ portNum +  "/jmxrmi");<br />
JMXConnector c = JMXConnectorFactory.connect(u); </pre>
<h2><a id="PasswordAccessFiles" name="PasswordAccessFiles"></a>パスワードおよびアクセスファイルの使用</h2>
<p>パスワードおよびアクセスファイルは、リモート監視および管理のセキュリティを制御します。これらのファイルは、デフォルトでは、<code><em>JRE_HOME</em>/lib/management</code> にあり、標準の Java プロパティファイルフォーマットです。フォーマットに関する詳細は、<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/Properties.html">「java.util.properties」</a>を参照してください。</p>
<h3>パスワードファイル</h3>
<p>パスワードファイルは、さまざまなロールとそのパスワードを定義します。アクセス制御ファイル (デフォルトでは、jmxremote.access) は、それぞれのロールに許可されるアクセス権を定義します。ロールを機能させるには、パスワードとアクセスファイルの両方にエントリを持つ必要があります。</p>
<p>JRE には、<code>jmxremote.password.template</code> という名前のパスワードファイルテンプレートがあります。このファイルを <code><em>JRE_HOME</em>/lib/management/jmxremote.password</code> またはホームディレクトリにコピーして、アクセスファイルで定義したロールのパスワードを追加します。</p>
<p>パスワードファイルにはパスワードがクリアテキストで含まれるため、必ず所有者だけがこのファイルへの読み取りおよび書き込みアクセス権を持つようにしてください。セキュリティ上の理由から、システムは所有者だけがファイルを読み取りまたは書き込み可能であることを確認し、そうでない場合は終了します。このため、マルチユーザ環境では、パスワードファイルをホームディレクトリなどの非公開の場所に置きます。</p>
<p>プロパティ名はロールで、関連付けられた値はロールのパスワードです。</p>
<p>たとえば、パスワードファイルのエントリの例は次のようになります。</p>
<pre># The "monitorRole" role has password "QED".<br />
# The "controlRole" role has password "R&amp;D".<br />
monitorRole QED<br />
controlRole R&amp;D</pre>
<h3>アクセスファイル</h3>
<p>デフォルトでは、アクセスファイルは、<code>jmxremote.access</code> という名前です。プロパティ名はパスワードファイルと同じ領域からの ID です。関連する値は「readonly」または「readwrite」のいずれかにする必要があります。</p>
<p>アクセスファイルはロールとアクセスレベルを定義します。デフォルトでは、アクセスファイルは次の 2 つの主要なロールを定義します。</p>
<ul>
    <li><code>monitorRole</code> - 監視のための読み取り専用アクセスを許可します。
    <li><code>controlRole</code> - 監視および管理のために読み取り/書き込みアクセス権を許可します。 </li>
</ul>
<p>アクセス制御エントリは、ロール名および関連するアクセスレベルで構成されています。ロール名には、スペースやタブを含めることはできず、パスワードファイル内のエントリに対応している必要があります。アクセスレベルは次のいずれかです。</p>
<ul>
    <li><strong>readonly</strong>: MBean の属性に読み取りアクセスを許可します。監視の場合、これは、このロールのリモートクライアントが測定を読み取ることができるが、実行プログラムの環境を変更するアクションは実行できないことを意味します。
    <li><strong>readwrite</strong>: MBean の属性への読み取りおよび書き込みアクセス、属性への操作の呼び出し、これらの属性の作成または削除を許可します。アプリケーションの操作を妨害できるのは、信頼できるクライアントだけであるため、このアクセス権は信頼できるクライアントにだけ許可する必要があります。 </li>
</ul>
<p>ロールは、アクセスファイル内で 1 つのエントリだけを持つ必要があります。ロールにエントリがない場合、アクセス権はありません。ロールに複数のエントリがある場合、最後のエントリが優先されます。</p>
<p>アクセスファイルの通常の事前定義のロールは次のとおりです。</p>
<pre># The "monitorRole" role has readonly access.<br />
# The "controlRole" role has readwrite access.<br />
monitorRole readonly<br />
controlRole readwrite</pre>
<h2><a id="properties" name="properties"></a>JMX 管理および監視プロパティ</h2>
<p>構成ファイルまたはコマンド行で、管理と監視のプロパティを設定できます。コマンド行で指定したプロパティは、構成ファイル内のプロパティを無効にします。構成ファイルのデフォルトの場所は、<code><em>JRE_HOME</em>/lib/management/management.properties</code> です。ここで <em>JRE_HOME</em> は、Java 実行時環境がインストールされているディレクトリです。<code>com.sun.management.jmxremote</code> または <code>com.sun.management.jmxremote.port</code> のいずれかのコマンド行プロパティが設定されている場合、JVM はこのファイルを読み取ります。SNMP 管理は同じ構成ファイルを使用します。詳細は、<a href="http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/management/SNMP.html">「SNMP 監視および管理」</a>を参照してください。 </p>
<p>次のコマンド行オプションでこの構成ファイルに別の場所を指定することもできます。</p>
<pre>com.sun.management.config.file=<em>ConfigFilePath</em></pre>
<p>ここで、<em>ConfigFilePath</em> は、この構成ファイルへのパスです。</p>
<p>次の表は、すべての JMX 監視および管理プロパティを示しています。</p>
<table cellspacing="2" cellpadding="2" summary="JMX Management and Monitoring Properties" border="1">
    <caption><a id="mmprops_table" name="mmprops_table"></a>表 1. JMX 管理および監視プロパティ</caption>
    <tbody>
        <tr>
            <th>プロパティ名</th>
            <th>説明</th>
            <th>値</th>
        </tr>
        <tr>
            <td><code>com.sun.management.jmxremote</code></td>
            <td><code>jconsole</code> で使用される専用インタフェース上に公開された JMX コネクタ経由で JMX リモートエージェントおよびローカルの監視を有効にします。jconsole ツールは、エージェントを開始したユーザ ID と同じユーザ ID で実行した場合、このコネクタを使用できます。このコネクタ経由の要求については、パスワードやアクセスファイルはチェックされません。</td>
            <td valign="top">true / false - デフォルトは、true です。</td>
        </tr>
        <tr>
            <td><code>com.sun.management.jmxremote. port</code></td>
            <td>JMX リモートエージェントを有効にして、指定したポート経由で待機するためにリモート JMX コネクタを作成します。デフォルトでは、SSL、パスワード、およびアクセスファイルプロパティがこのコネクタに使用されます。また、<code>com.sun.management.jmxremote</code> プロパティで説明したローカルの監視も有効にします。</td>
            <td valign="top">ポート番号 - デフォルトはありません。</td>
        </tr>
        <tr>
            <td><code>com.sun.management.jmxremote.<br />
            ssl</code></td>
            <td>SSL 経由で安全に監視できるようにします。false の場合、SSL は使用されません。</td>
            <td>true / false - デフォルトは、true です。</td>
        </tr>
        <tr>
            <td><code>com.sun.management.jmxremote.<br />
            ssl.enabled.protocols</code></td>
            <td>SSL/TLS プロトコルバージョンを有効にするカンマ区切りのリスト。<code>com.sun.management.jmxremote.ssl</code> と組み合わせて使用されます。</td>
            <td valign="top">デフォルトの SSL/TLS プロトコルバージョン</td>
        </tr>
        <tr>
            <td><code>com.sun.management.jmxremote.<br />
            ssl.enabled.cipher.suites</code></td>
            <td>SSL/TLS 暗号群を有効にするカンマ区切りのリスト。<code>com.sun.management.jmxremote.ssl</code> と組み合わせて使用されます。</td>
            <td valign="top">デフォルトの SSL/TLS 暗号群</td>
        </tr>
        <tr>
            <td><code>com.sun.management.jmxremote.<br />
            ssl.need.client.auth</code></td>
            <td>このプロパティが true でプロパティ <code>com.sun.management.jmxremote.ssl</code> が true の場合、クライアント認証が実行されます。</td>
            <td valign="top">true / false - デフォルトは、false です。</td>
        </tr>
        <tr>
            <td><code>com.sun.management.jmxremote.<br />
            authenticate</code></td>
            <td>このプロパティが false の場合、JMX はパスワードまたはアクセスファイルを使用しません。すべてのユーザがアクセスを許可されます。</td>
            <td valign="top">true / false - <br />
            デフォルトは、true です。</td>
        </tr>
        <tr>
            <td><code>com.sun.management.jmxremote.<br />
            password.file</code></td>
            <td>パスワードファイルの場所を指定します。<code>com.sun.management.jmxremote.password</code> が false の場合、このプロパティとパスワードおよびアクセスファイルは無視されます。それ以外の場合は、パスワードファイルが存在し、有効なフォーマットである必要があります。パスワードファイルが空であるか存在しない場合、アクセスは許可されません。</td>
            <td valign="top"><code><em>JRE_HOME</em>/lib/management/<br />
            jmxremote.password</code></td>
        </tr>
        <tr>
            <td><code>com.sun.management.jmxremote.<br />
            access.file</code></td>
            <td>アクセスファイルの場所を指定します。<code>com.sun.management.jmxremote.password</code> が false の場合、このプロパティとパスワードおよびアクセスファイルは無視されます。それ以外の場合は、アクセスファイルが存在し、有効なフォーマットである必要があります。アクセスファイルが空であるか存在しない場合、アクセスは許可されません。</td>
            <td valign="top"><code><em>JRE_HOME</em>/lib/management/<br />
            jmxremote.access</code></td>
        </tr>
        <tr>
            <td style="vertical-align: top"><code>com.sun.management.jmxremote.<br />
            login.config</code></td>
            <td style="vertical-align: top">RMI 監視のユーザを認証する場合に使用する JAAS ログイン設定エントリの名前を指定します。このプロパティをデフォルトのログイン設定をオーバーライドするために使用する場合は、JAAS でロードされたファイルに指定された設定エントリが存在する必要があります。また、設定で指定されたログインモジュールはユーザの資格を取得するために名前とパスワードのコールバックを使用する必要があります。詳細は <a href="file:///import/near/TechPub/Japanese/JDK1.5/doc/1.5.0/docs/api/javax/security/auth/callback/NameCallback.html"><code>javax.security.auth.callback.NameCallback</code></a> および <a href="file:///import/near/TechPub/Japanese/JDK1.5/doc/1.5.0/docs/api/javax/security/auth/callback/PasswordCallback.html"><code>javax.security.auth.callback.PasswordCallback</code></a> を参照してください。<br />
            <br />
            <code>com.sun.management.jmxremote.authenticate</code> が false の場合、このプロパティおよびパスワード、アクセスファイルは無視されます。 </td>
            <td style="vertical-align: top">デフォルトのログイン設定はファイルベースのパスワード認証です。 </td>
        </tr>
    </tbody>
</table>
<h3>構成エラー</h3>
<p>MBean サーバ、RMI レジストリ、またはコネクタの起動中にエラーが発生した場合、JVM は例外をスローして終了します。構成エラーには以下のものがあります。</p>
<ul>
    <li>ポート番号へのバインドの失敗
    <li>無効なパスワードファイル
    <li>無効なアクセスファイル
    <li>パスワードファイルが所有者以外に読み取り可能になっている </li>
</ul>
<p>アプリケーションでセキュリティマネージャを実行している場合は、セキュリティ権限ファイルに追加の権限が必要です。</p>
<script language="JavaScript" src="/js/omi/jsc/s_code_remote.js"></script><script language="JavaScript" src="http://www-cdn.sun.com/share/metrics/metrics_group1.js"></script>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/240000.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-11-12 09:53 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/11/12/240000.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM参数调优实践 xx</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/11/11/239879.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 11 Nov 2008 06:44:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/11/11/239879.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/239879.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/11/11/239879.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/239879.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/239879.html</trackback:ping><description><![CDATA[http://www.cjsdn.net/post/print?bid=62&amp;id=196304<br />
<br />
<br />
<span class="javascript" id="text196304">JVM参数调优是一个很头痛的问题，可能和应用有关系，下面是本人一些调优的实践经验，希望对读者能有帮助，环境LinuxAS4,resin2.1.17,JDK6.0,2CPU,4G内存,dell2950服务器，网站是http://shedewang.com<br />
<br />
一：串行垃圾回收，也就是默认配置，完成10万request用时153秒，JVM参数配置如下<br />
$JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps ";<br />
这种配置一般在resin启动24小时内似乎没有大问题，网站可以正常访问，但查看日志发现，在接近24小时时，Full GC执行越来越频繁，大约每隔3分钟就有一次Full GC，每次Full GC系统会停顿6秒左右，作为一个网站来说，用户等待6秒恐怕太长了，所以这种方式有待改善。MaxTenuringThreshold=7表示一个对象如果在救助空间移动7次还没有被回收就放入年老代，GCTimeRatio=19表示java可以用5%的时间来做垃圾回收，1/(1+19)=1 /20=5%。<br />
<br />
二：并行回收，完成10万request用时117秒，配置如下：<br />
$JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xmx2048M -Xms2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=500 -XX:+UseAdaptiveSizePolicy -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 ";<br />
并行回收我尝试过多种组合配置，似乎都没什么用，resin启动3小时左右就会停顿，时间超过10 秒。也有可能是参数设置不够好的原因，MaxGCPauseMillis表示GC最大停顿时间，在resin刚启动还没有执行Full GC时系统是正常的，但一旦执行Full GC，MaxGCPauseMillis根本没有用，停顿时间可能超过20秒，之后会发生什么我也不再关心了，赶紧重启resin，尝试其他回收策略。<br />
<br />
三：并发回收，完成10万request用时60秒，比并行回收差不多快一倍，是默认回收策略性能的2.5倍，配置如下：<br />
$JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 ";<br />
这个配置虽然不会出现10秒连不上的情况，但系统重启3个小时左右，每隔几分钟就会有5秒连不上的情况，查看gc.log，发现在执行ParNewGC时有个promotion failed错误，从而转向执行Full GC，造成系统停顿，而且会很频繁，每隔几分钟就有一次，所以还得改善。UseCMSCompactAtFullCollection是表是执行Full GC后对内存进行硌顾酰獾貌诖嫠槠珻MSFullGCsBeforeCompaction=N表示执行N次Full GC后执行内存压缩。<br />
<br />
四：增量回收，完成10万request用时171秒，太慢了，配置如下<br />
$JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xincgc ";<br />
似乎回收得也不太干净，而且也对性能有较大影响，不值得试。<br />
<br />
五：并发回收的I-CMS模式，和增量回收差不多，完成10万request用时170秒。<br />
$JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+CMSIncrementalPacing -XX:CMSIncrementalDutyCycleMin=0 -XX:CMSIncrementalDutyCycle=10 -XX:-TraceClassUnloading ";<br />
采用了sun推荐的参数，回收效果不好，照样有停顿，数小时之内就会频繁出现停顿，什么sun推荐的参数，照样不好使。<br />
<br />
六：递增式低暂停收集器，还叫什么火车式回收，不知道属于哪个系，完成10万request用时153秒<br />
$JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -Xloggc:log/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseTrainGC ";<br />
该配置效果也不好，影响性能，所以没试。<br />
<br />
七：相比之下，还是并发回收比较好，性能比较高，只要能解决ParNewGC（并行回收年轻代）时的promotion failed错误就一切好办了，查了很多文章，发现引起promotion failed错误的原因是CMS来不及回收（CMS默认在年老代占到90%左右才会执行），年老代又没有足够的空间供GC把一些活的对象从年轻代移到年老代，所以执行Full GC。CMSInitiatingOccupancyFraction=70表示年老代占到约70%时就开始执行CMS，这样就不会出现Full GC了。SoftRefLRUPolicyMSPerMB这个参数也是我认为比较有用的，官方解释是softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap，我觉得没必要等1秒，所以设置成0。配置如下<br />
$JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xms2048M -Xmx2048M -Xmn512M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -XX:+DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:-CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime -Xloggc:log/gc.log ";<br />
上面这个配置内存上升的很慢，24小时之内几乎没有停顿现象，最长的只停滞了0.8s，ParNew GC每30秒左右才执行一次，每次回收约0.2秒，看来问题应该暂时解决了。<br />
<br />
参数不明白的可以上网查，本人认为比较重要的几个参数是：-Xms -Xmx -Xmn MaxTenuringThreshold GCTimeRatio UseConcMarkSweepGC CMSInitiatingOccupancyFraction SoftRefLRUPolicyMSPerMB </span>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/239879.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-11-11 14:44 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/11/11/239879.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM调优总结 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/11/11/239875.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 11 Nov 2008 06:35:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/11/11/239875.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/239875.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/11/11/239875.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/239875.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/239875.html</trackback:ping><description><![CDATA[<span id="ctl00_MainContentPlaceholder_ctl01_ctl00_lblEntry">
<p><font size="2">http://pengjiaheng.spaces.live.com/blog/cns!2DAA368B386E6AEA!770.entry?wa=wsignin1.0<br />
<br />
最近总结的一些东西，基本上是网上一些资料的汇总。</font> </p>
<p><strong></strong>&nbsp; </p>
<p><strong><font size="2">一、相关概念</font></strong> </p>
<p><strong><font size="2"></font></strong><br />
<strong><font color="#ff0000">基本回收算法</font></strong> </p>
<ol>
    <li><strong>引用计数（Reference Counting）<br />
    </strong>比较古老的回收算法。原理是此对象有一个引用，即增加一个计数，删除一个引用则减少一个计数。垃圾回收时，只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。 </li>
    <li><strong>标记-清除（Mark-Sweep）<br />
    </strong>此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象，第二阶段遍历整个堆，把未标记的对象清除。此算法需要暂停整个应用，同时，会产生内存碎片。 </li>
    <li><strong>复制（Copying）<br />
    </strong>此算法把内存空间划为两个相等的区域，每次只使用其中一个区域。垃圾回收时，遍历当前使用区域，把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象，因此复制成本比较小，同时复制过去以后还能进行相应的内存整理，不过出现&#8220;碎片&#8221;问题。当然，此算法的缺点也是很明显的，就是需要两倍内存空间。 </li>
    <li><strong>标记-整理（Mark-Compact）<br />
    </strong>此算法结合了&#8220;标记-清除&#8221;和&#8220;复制&#8221;两个算法的优点。也是分两阶段，第一阶段从根节点开始标记所有被引用对象，第二阶段遍历整个堆，把清除未标记对象并且把存活对象&#8220;压缩&#8221;到堆的其中一块，按顺序排放。此算法避免了&#8220;标记-清除&#8221;的碎片问题，同时也避免了&#8220;复制&#8221;算法的空间问题。 </li>
    <li><strong>增量收集（Incremental Collecting）</strong><br />
    实施垃圾回收算法，即：在应用进行的同时进行垃圾回收。不知道什么原因JDK5.0中的收集器没有使用这种算法的。 </li>
    <li><strong>分代（Generational Collecting）<br />
    </strong>基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代，对不同生命周期的对象使用不同的算法（上述方式中的一个）进行回收。现在的垃圾回收器（从J2SE1.2开始）都是使用此算法的。 </li>
</ol>
<br />
<strong><font color="#ff0000">分代垃圾回收详述</font></strong>
<p><img height="425" src="http://lh3.google.com/pengjiaheng/Ron7MqXPDPI/AAAAAAAAAH0/Ulzfr9RZX9o/Java%20SE%206%20HotSpot%5Btm%5D%20Virtual%20Machine%20Garbage%20Collection%20Tuning_1183357573109.JPG" width="654"  alt="" /><br />
如上图所示，为Java堆中的各代分布。 </p>
<ol>
    <li><strong>Young（年轻代）<br />
    </strong>年轻代分三个区。一个Eden区，两个Survivor区。大部分对象在Eden区中生成。当Eden区满时，还存活的对象将被复制到Survivor区（两个中的一个），当这个Survivor区满时，此区的存活对象将被复制到另外一个Survivor区，当这个Survivor去也满了的时候，从第一个Survivor区复制过来的并且此时还存活的对象，将被复制&#8220;年老区(Tenured)&#8221;。需要注意，Survivor的两个区是对称的，没先后关系，所以同一个区中可能同时存在从Eden复制过来对象，和从前一个Survivor复制过来的对象，而复制到年老区的只有从第一个Survivor去过来的对象。而且，Survivor区总有一个是空的。 </li>
    <li><strong>Tenured（年老代）<br />
    </strong>年老代存放从年轻代存活的对象。一般来说年老代存放的都是生命期较长的对象。 </li>
    <li><strong>Perm（持久代）<br />
    </strong>用于存放静态文件，如今Java类、方法等。持久代对垃圾回收没有显著影响，但是有些应用可能动态生成或者调用一些class，例如Hibernate等，在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=&lt;N&gt;进行设置。</li>
</ol>
<br />
<strong><font color="#ff0000">GC类型</font><br />
</strong>GC有两种类型：<strong>Scavenge GC和Full GC</strong>。
<ol>
    <li>Scavenge GC<br />
    一般情况下，当新对象生成，并且在Eden申请空间失败时，就好触发Scavenge GC，堆Eden区域进行GC，清除非存活对象，并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。 </li>
    <li>Full GC<br />
    对整个堆进行整理，包括Young、Tenured和Perm。Full GC比Scavenge GC要慢，因此应该尽可能减少Full GC。有如下原因可能导致Full GC：
    <ul>
        <li>Tenured被写满 </li>
    </ul>
    <ul>
        <li>Perm域被写满 </li>
    </ul>
    <ul>
        <li>System.gc()被显示调用 </li>
    </ul>
    <ul>
        <li>上一次GC之后Heap的各域分配策略动态变化 </li>
    </ul>
    </li>
</ol>
<p><br />
<strong><font color="#ff0000">分代垃圾回收过程演示</font></strong><br />
<img src="http://lh4.google.com/pengjiaheng/RosphaXPDZI/AAAAAAAAAJY/5g7J7TqR2pI/1.JPG?imgmax=576"  alt="" /><br />
<img src="http://lh4.google.com/pengjiaheng/RosphaXPDaI/AAAAAAAAAJg/k1Z_u32zD0Y/2.JPG?imgmax=576"  alt="" /><br />
<img src="http://lh4.google.com/pengjiaheng/RosphaXPDbI/AAAAAAAAAJo/e3bRlUcld3Q/3.JPG?imgmax=576"  alt="" /><br />
<img src="http://lh4.google.com/pengjiaheng/RosphaXPDcI/AAAAAAAAAJw/Q-M__ID-k_0/4.JPG?imgmax=576"  alt="" /> </p>
<p><br />
<strong><font size="2">二、垃圾回收器</font></strong> </p>
<p><strong><font size="2"><br />
</font></strong>目前的收集器主要有三种：<strong>串行收集器、并行收集器、并发收集器</strong>。 </p>
<ol>
    <li><strong>串行收集器</strong><br />
    <img height="390" src="http://lh3.google.com/pengjiaheng/Ron7MqXPDTI/AAAAAAAAAIU/FKFnXwdLz-4/%E4%B8%B2%E8%A1%8C%E6%94%B6%E9%9B%86%E5%99%A8.JPG" width="713"  alt="" /><br />
    使用单线程处理所有垃圾回收工作，因为无需多线程交互，所以效率比较高。但是，也无法使用多处理器的优势，所以此收集器适合单处理器机器。当然，此收集器也可以用在小数据量（<strong>100M</strong>左右）情况下的多处理器机器上。可以使用<strong>-XX:+UseSerialGC</strong>打开。<br />
    </li>
    <li><strong>并行收集器<br />
    </strong><img src="http://lh3.google.com/pengjiaheng/Ron7MqXPDSI/AAAAAAAAAIM/YClzvXUk5A4/%E5%B9%B6%E8%A1%8C%E6%94%B6%E9%9B%86%E5%99%A8.JPG"  alt="" />
    <ol>
        <li>对年轻代进行并行垃圾回收，因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。使用<strong>-XX:+UseParallelGC</strong>.打开。并行收集器在J2SE5.0第六6更新上引入，在Java SE6.0中进行了增强--可以堆年老代进行并行收集。<strong>如果年老代不使用并发收集的话，是使用单线程进行垃圾回收</strong>，因此会制约扩展能力。使用<strong>-XX:+UseParallelOldGC</strong>打开。 </li>
        <li>使用<strong>-XX:ParallelGCThreads=&lt;N&gt;</strong>设置并行垃圾回收的线程数。<strong>此值可以设置与机器处理器数量相等</strong>。 </li>
        <li>此收集器可以进行如下配置：
        <ul>
            <li><strong>最大垃圾回收暂停:</strong>指定垃圾回收时的最长暂停时间，通过<strong>-XX:MaxGCPauseMillis=&lt;N&gt;</strong>指定。&lt;N&gt;为毫秒.如果指定了此值的话，<strong>堆大小和垃圾回收相关参数会进行调整以达到指定值</strong>。设定此值可能会减少应用的吞吐量。 </li>
            <li><strong>吞吐量:</strong>吞吐量为<strong>垃圾回收时间与非垃圾回收时间的比值</strong>，通过<strong>-XX:GCTimeRatio=&lt;N&gt;</strong>来设定，公式为<strong>1/（1+N）</strong>。例如，-XX:GCTimeRatio=19时，表示5%的时间用于垃圾回收。默认情况为99，即1%的时间用于垃圾回收。</li>
        </ul>
        </li>
    </ol>
    </li>
    <li><strong>并发收集器<br />
    </strong>可以保证大部分工作都并发进行（应用不停止），垃圾回收只暂停很少的时间，此收集器适合对响应时间要求比较高的中、大规模应用。使用<strong>-XX:+UseConcMarkSweepGC</strong>打开。<br />
    <img height="413" src="http://lh3.google.com/pengjiaheng/Ron7MqXPDRI/AAAAAAAAAIE/HnA7UnjlqQ4/%E5%B9%B6%E5%8F%91%E6%94%B6%E9%9B%86%E5%99%A8.JPG" width="834"  alt="" />
    <ol>
        <li>并发收集器主要减少年老代的暂停时间，他在应用不停止的情况下使用独立的垃圾回收线程，跟踪可达对象。在每个年老代垃圾回收周期中，在收集初期并发收集器会对整个应用进行简短的暂停，在收集中还会再暂停一次。第二次暂停会比第一次稍长，在此过程中多个线程同时进行垃圾回收工作。 </li>
        <li>并发收集器使用<strong>处理器换来短暂的停顿时间</strong>。在一个N个处理器的系统上，并发收集部分使用<strong>K/N</strong>个可用处理器进行回收，一般情况下<strong>1&lt;=K&lt;=N/4</strong>。 </li>
        <li>在只有<strong>一个处理器的主机上使用并发收集器</strong>，设置为<strong>incremental mode</strong>模式也可获得较短的停顿时间。 </li>
        <li><strong>浮动垃圾</strong>：由于在应用运行的同时进行垃圾回收，所以有些垃圾可能在垃圾回收进行完成时产生，这样就造成了&#8220;Floating Garbage&#8221;，这些垃圾需要在下次垃圾回收周期时才能回收掉。所以，并发收集器一般需要<strong>20%</strong>的预留空间用于这些浮动垃圾。 </li>
        <li><strong>Concurrent Mode Failure</strong>：并发收集器在应用运行时进行收集，所以需要保证堆在垃圾回收的这段时间有足够的空间供程序使用，否则，垃圾回收还未完成，堆空间先满了。这种情况下将会发生&#8220;并发模式失败&#8221;，此时整个应用将会暂停，进行垃圾回收。 </li>
        <li><strong>启动并发收集器</strong>：因为并发收集在应用运行时进行收集，所以必须保证收集完成之前有足够的内存空间供程序使用，否则会出现&#8220;Concurrent Mode Failure&#8221;。通过设置<strong>-XX:CMSInitiatingOccupancyFraction=&lt;N&gt;</strong>指定还有多少剩余堆时开始执行并发收集</li>
    </ol>
    </li>
    <li><strong>小结</strong>
    <ul>
        <li><strong>串行处理器：</strong><br />
        &nbsp;--适用情况：数据量比较小（100M左右）；单处理器下并且对响应时间无要求的应用。<br />
        &nbsp;--缺点：只能用于小型应用 </li>
        <li><strong>并行处理器：</strong><br />
        &nbsp;--适用情况：&#8220;对吞吐量有高要求&#8221;，多CPU、对应用响应时间无要求的中、大型应用。举例：后台处理、科学计算。<br />
        &nbsp;--缺点：应用响应时间可能较长 </li>
        <li><strong>并发处理器：<br />
        </strong>&nbsp;--适用情况：&#8220;对响应时间有高要求&#8221;，多CPU、对应用响应时间有较高要求的中、大型应用。举例：Web服务器/应用服务器、电信交换、集成开发环境。</li>
    </ul>
    </li>
</ol>
<br />
<strong><font size="2">三、常见配置举例</font></strong>
<ol>
    <li><strong>堆大小设置<br />
    </strong>JVM 中最大堆大小有三方面限制：相关操作系统的数据模型（32-bt还是64-bit）限制；系统的可用虚拟内存限制；系统的可用物理内存限制。32位系统下，一般限制在1.5G~2G；64为操作系统对内存无限制。我在Windows Server 2003 系统，3.5G物理内存，JDK5.0下测试，最大可设置为1478m。<br />
    <code><strong>典型设置：</strong></code>
    <ul>
        <li><code>java <strong>-Xmx3550m -Xms3550m -Xmn2g</strong> </code><code><strong>-Xss128k</strong><br />
        </code><code><strong>-</strong></code><code><strong>Xmx3550m</strong>：设置JVM最大可用内存为3550M。<br />
        </code><code><strong>-Xms3550m</strong></code><code>：设置JVM促使内存为3550m。此值可以设置与-Xmx相同，以避免每次垃圾回收完成后JVM重新分配内存。<br />
        </code><code><strong><font color="#ff0000">-Xmn2g</font></strong></code><code>：设置年轻代大小为2G。<strong><font color="#0000ff">整个堆大小=年轻代大小 + 年老代大小 + 持久代大小</font></strong>。持久代一般固定大小为64m，所以增大年轻代后，将会减小年老代大小。此值对系统性能影响较大，Sun官方推荐配置为整个堆的3/8。<br />
        </code><code></code><code><strong>-Xss128k</strong></code><code>：设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M，以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下，减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的，不能无限生成，经验值在3000~5000左右。<br />
        </code></li>
        <li><code></code><code>java -Xmx3550m -Xms3550m </code><code>-Xss128k <strong>-XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0</strong></code><br />
        <code><strong>-XX:NewRatio=4</strong></code><code>:设置年轻代（包括Eden和两个Survivor区）与年老代的比值（除去持久代）。设置为4，则年轻代与年老代所占比值为1：4，年轻代占整个堆栈的1/5<br />
        </code><code><strong>-XX:SurvivorRatio=4</strong></code>：设置年轻代中Eden区与Survivor区的大小比值。设置为4，则两个Survivor区与一个Eden区的比值为2:4，一个Survivor区占整个年轻代的1/6<br />
        <code><strong>-XX:MaxPermSize=16m</strong></code>:设置持久代大小为16m。<br />
        <code></code><code><strong>-XX:MaxTenuringThreshold=0</strong></code><code>：设置垃圾最大年龄。<strong><font color="#0000ff">如果设置为0的话，则年轻代对象不经过Survivor区，直接进入年老代</font></strong>。对于年老代比较多的应用，可以提高效率。<strong><font color="#0000ff">如果将此值设置为一个较大值，则年轻代对象会在Survivor区进行多次复制，这样可以增加对象再年轻代的存活时间</font></strong>，增加在年轻代即被回收的概论。</code></li>
    </ul>
    </li>
    <li><strong>回收器选择<br />
    </strong>JVM给了三种选择：<strong>串行收集器、并行收集器、并发收集器</strong>，但是串行收集器只适用于小数据量的情况，所以这里的选择主要针对并行收集器和并发收集器。默认情况下，JDK5.0以前都是使用串行收集器，如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后，JVM会根据当前<a href="http://java.sun.com/j2se/1.5.0/docs/guide/vm/server-class.html">系统配置</a>进行判断。
    <ol>
        <li><strong>吞吐量优先</strong>的并行收集器<br />
        如上文所述，并行收集器主要以到达一定的吞吐量为目标，适用于科学技术和后台处理等。<br />
        <strong>典型配置</strong>：
        <ul>
            <li><code>java -Xmx3800m -Xms3800m -Xmn2g -Xss128k <strong>-XX:+UseParallelGC -XX:ParallelGCThreads=20</strong><br />
            </code><code><strong>-XX:+UseParallelGC</strong></code><code>：选择垃圾收集器为并行收集器。<strong><font color="#0000ff">此配置仅对年轻代有效。即上述配置下，年轻代使用并发收集，而年老代仍旧使用串行收集。<br />
            </font></strong></code><code><strong>-XX:ParallelGCThreads=20</strong></code><code>：配置并行收集器的线程数，即：同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。<br />
            </code></li>
            <li><code>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 <strong>-XX:+UseParallelOldGC<br />
            </strong></code><code><strong>-XX:+UseParallelOldGC</strong></code><code>：配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。<br />
            </code></li>
            <li><code></code><code>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC&nbsp; <strong>-XX:MaxGCPauseMillis=100<br />
            </strong></code><code><strong>-XX:MaxGCPauseMillis=100</strong></code><code><strong>:</strong>设置每次年轻代垃圾回收的最长时间，如果无法满足此时间，JVM会自动调整年轻代大小，以满足此值。<br />
            </code></li>
            <li><code>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC&nbsp; -XX:MaxGCPauseMillis=100 </code><strong>-XX:+UseAdaptiveSizePolicy<br />
            -XX:+UseAdaptiveSizePolicy</strong>：设置此选项后，并行收集器会自动选择年轻代区大小和相应的Survivor区比例，以达到目标系统规定的最低相应时间或者收集频率等，此值建议使用并行收集器时，一直打开。<br />
            </li>
        </ul>
        </li>
        <li><strong>响应时间优先</strong>的并发收集器<br />
        如上文所述，并发收集器主要是保证系统的响应时间，减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。<br />
        <strong>典型配置</strong>：
        <ul>
            <li><code>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 <strong>-XX:+UseConcMarkSweepGC -XX:+UseParNewGC<br />
            </strong></code><code><strong>-XX:+UseConcMarkSweepGC</strong></code><code>：设置年老代为并发收集。测试中配置这个以后，</code><code>-XX:NewRatio=4的配置失效了，原因不明。所以，此时年轻代大小最好用-Xmn设置。</code><br />
            <code><strong>-XX:+UseParNewGC</strong></code><code>:</code><code></code><code>设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上，JVM会根据系统配置自行设置，所以无需再设置此值。</code> </li>
            <li><code>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC </code><strong><code>-XX:CMSFullGCsBeforeCompaction=5 </code>-XX:+UseCMSCompactAtFullCollection</strong><br />
            <code></code><code><strong>-XX:CMSFullGCsBeforeCompaction</strong>：由于并发收集器不对内存空间进行压缩、整理，所以运行一段时间以后会产生&#8220;碎片&#8221;，使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。<br />
            </code><code></code><strong>-XX:+UseCMSCompactAtFullCollection</strong>：打开对年老代的压缩。可能会影响性能，但是可以消除碎片<br />
            </li>
        </ul>
        </li>
    </ol>
    </li>
    <li><strong>辅助信息<br />
    </strong>JVM提供了大量命令行参数，打印信息，供调试使用。主要有以下一些：
    <ul>
        <li><strong>-XX:+PrintGC<br />
        </strong>输出形式<strong>：[GC 118250K-&gt;113543K(130112K), 0.0094143 secs] </strong>
        <p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Full GC 121376K-&gt;10414K(130112K), 0.0650971 secs]</strong> </p>
        </li>
        <li><strong>-XX:+PrintGCDetails<br />
        </strong>输出形式<strong>：[GC [DefNew: 8614K-&gt;781K(9088K), 0.0123035 secs] 118250K-&gt;113543K(130112K), 0.0124633 secs] </strong>
        <p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [GC [DefNew: 8614K-&gt;8614K(9088K), 0.0000665 secs][Tenured: 112761K-&gt;10414K(121024K), 0.0433488 secs] 121376K-&gt;10414K(130112K), 0.0436268 secs]</strong> </p>
        </li>
        <li><strong>-XX:+PrintGCTimeStamps</strong> -XX:+PrintGC：PrintGCTimeStamps可与上面两个混合使用<br />
        输出形式：<strong>11.851: [GC 98328K-&gt;93620K(130112K), 0.0082960 secs]<br />
        </strong></li>
        <li><strong>-XX:+PrintGCApplicationConcurrentTime:</strong>打印每次垃圾回收前，程序未中断的执行时间。可与上面混合使用<br />
        输出形式：<strong>Application time: 0.5291524 seconds<br />
        </strong></li>
        <li><strong>-XX:+PrintGCApplicationStoppedTime</strong>：打印垃圾回收期间程序暂停的时间。可与上面混合使用<br />
        输出形式：<strong>Total time for which application threads were stopped: 0.0468229 seconds<br />
        </strong></li>
        <li><strong>-XX:PrintHeapAtGC</strong>:打印GC前后的详细堆栈信息<br />
        输出形式：<br />
        34.702: [GC {Heap before gc invocations=7:<br />
        &nbsp;def new generation&nbsp;&nbsp; total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)<br />
        <strong>eden space 49152K,&nbsp; 99% used</strong> [0x1ebd0000, 0x21bce430, 0x21bd0000)<br />
        <strong>from space 6144K,&nbsp; 55% used</strong> [0x221d0000, 0x22527e10, 0x227d0000)<br />
        &nbsp; to&nbsp;&nbsp; space 6144K,&nbsp;&nbsp; 0% used [0x21bd0000, 0x21bd0000, 0x221d0000)<br />
        &nbsp;tenured generation&nbsp;&nbsp; total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)<br />
        <strong>the space 69632K,&nbsp;&nbsp; 3% used</strong> [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)<br />
        &nbsp;compacting perm gen&nbsp; total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)<br />
        &nbsp;&nbsp; the space 8192K,&nbsp; 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)<br />
        &nbsp;&nbsp;&nbsp; ro space 8192K,&nbsp; 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)<br />
        &nbsp;&nbsp;&nbsp; rw space 12288K,&nbsp; 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)<br />
        34.735: [DefNew: 52568K-&gt;3433K(55296K), 0.0072126 secs] 55264K-&gt;6615K(124928K)<strong>Heap after gc invocations=8:<br />
        </strong>&nbsp;def new generation&nbsp;&nbsp; total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)<br />
        <strong>eden space 49152K,&nbsp;&nbsp; 0% used</strong> [0x1ebd0000, 0x1ebd0000, 0x21bd0000)<br />
        &nbsp; from space 6144K,&nbsp; 55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)<br />
        &nbsp; to&nbsp;&nbsp; space 6144K,&nbsp;&nbsp; 0% used [0x221d0000, 0x221d0000, 0x227d0000)<br />
        &nbsp;tenured generation&nbsp;&nbsp; total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)<br />
        <strong>the space 69632K,&nbsp;&nbsp; 4% used </strong>[0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)<br />
        &nbsp;compacting perm gen&nbsp; total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)<br />
        &nbsp;&nbsp; the space 8192K,&nbsp; 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)<br />
        &nbsp;&nbsp;&nbsp; ro space 8192K,&nbsp; 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)<br />
        &nbsp;&nbsp;&nbsp; rw space 12288K,&nbsp; 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)<br />
        }<br />
        , 0.0757599 secs]<br />
        <code></code></li>
        <li><strong>-Xloggc:filename</strong>:与上面几个配合使用，把相关日志信息记录到文件以便分析。</li>
    </ul>
    </li>
    <li><strong>常见配置汇总</strong>
    <ol>
        <li>堆设置
        <ul>
            <li><strong>-Xms</strong>:初始堆大小 </li>
            <li><strong>-Xmx</strong>:最大堆大小 </li>
            <li><strong>-XX:NewSize=n</strong>:设置年轻代大小 </li>
            <li><strong>-XX:NewRatio=n:</strong>设置年轻代和年老代的比值。如:为3，表示年轻代与年老代比值为1：3，年轻代占整个年轻代年老代和的1/4 </li>
            <li><strong>-XX:SurvivorRatio=n</strong>:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如：3，表示Eden：Survivor=3：2，一个Survivor区占整个年轻代的1/5 </li>
            <li><strong>-XX:MaxPermSize=n</strong>:设置持久代大小</li>
        </ul>
        </li>
        <li>收集器设置
        <ul>
            <li><strong>-XX:+UseSerialGC</strong>:设置串行收集器 </li>
            <li><strong>-XX:+UseParallelGC</strong>:设置并行收集器 </li>
            <li><strong>-XX:+UseParalledlOldGC</strong>:设置并行年老代收集器 </li>
            <li><strong>-XX:+UseConcMarkSweepGC</strong>:设置并发收集器</li>
        </ul>
        </li>
        <li>垃圾回收统计信息
        <ul>
            <li><strong>-XX:+PrintGC</strong> </li>
            <li><strong>-XX:+PrintGCDetails</strong> </li>
            <li><strong>-XX:+PrintGCTimeStamps</strong> </li>
            <li><strong>-Xloggc:filename</strong></li>
        </ul>
        </li>
        <li>并行收集器设置
        <ul>
            <li><strong>-XX:ParallelGCThreads=n</strong>:设置并行收集器收集时使用的CPU数。并行收集线程数。 </li>
            <li><strong>-XX:MaxGCPauseMillis=n</strong>:设置并行收集最大暂停时间 </li>
            <li><strong>-XX:GCTimeRatio=n</strong>:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)</li>
        </ul>
        </li>
        <li>并发收集器设置
        <ul>
            <li><strong>-XX:+CMSIncrementalMode</strong>:设置为增量模式。适用于单CPU情况。 </li>
            <li><strong>-XX:ParallelGCThreads=n</strong>:设置并发收集器年轻代收集方式为并行收集时，使用的CPU数。并行收集线程数。</li>
        </ul>
        </li>
    </ol>
    </li>
</ol>
<br />
<strong><font size="2">四、调优总结</font></strong>
<ol>
    <li><strong>年轻代大小选择</strong>
    <ul>
        <li><strong>响应时间优先的应用</strong>：<strong><font color="#0000ff">尽可能设大，直到接近系统的最低响应时间限制</font></strong>（根据实际情况选择）。在此种情况下，年轻代收集发生的频率也是最小的。同时，减少到达年老代的对象。 </li>
        <li><strong>吞吐量优先的应用</strong>：尽可能的设置大，可能到达Gbit的程度。因为对响应时间没有要求，垃圾收集可以并行进行，一般适合8CPU以上的应用。</li>
    </ul>
    </li>
    <li><strong>年老代大小选择</strong>
    <ul>
        <li><strong>响应时间优先的应用</strong>：年老代使用并发收集器，所以其大小需要小心设置，一般要考虑<strong>并发会话率</strong>和<strong>会话持续时间</strong>等一些参数。如果堆设置小了，可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式；如果堆大了，则需要较长的收集时间。最优化的方案，一般需要参考以下数据获得：
        <ul>
            <li>并发垃圾收集信息 </li>
            <li>持久代并发收集次数 </li>
            <li>传统GC信息 </li>
            <li>花在年轻代和年老代回收上的时间比例</li>
        </ul>
        减少年轻代和年老代花费的时间，一般会提高应用的效率<br />
        </li>
        <li><strong>吞吐量优先的应用</strong>：一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是，这样可以尽可能回收掉大部分短期对象，减少中期的对象，而年老代尽存放长期存活对象。</li>
    </ul>
    </li>
    <li><strong>较小堆引起的碎片问题<br />
    </strong>因为年老代的并发收集器使用标记、清除算法，所以不会对堆进行压缩。当收集器回收时，他会把相邻的空间进行合并，这样可以分配给较大的对象。但是，当堆空间较小时，运行一段时间以后，就会出现&#8220;碎片&#8221;，如果并发收集器找不到足够的空间，那么并发收集器将会停止，然后使用传统的标记、清除方式进行回收。如果出现&#8220;碎片&#8221;，可能需要进行如下配置：
    <ul>
        <li><strong>-XX:+UseCMSCompactAtFullCollection</strong>：使用并发收集器时，开启对年老代的压缩。 </li>
        <li><strong>-XX:CMSFullGCsBeforeCompaction=0</strong>：上面配置开启的情况下，这里设置多少次Full GC后，对年老代进行压缩</li>
    </ul>
    </li>
</ol>
<br />
<strong><font size="2">五、参考文献</font></strong>
<ul>
    <li><a href="http://www.ibm.com/developerworks/cn/java/j-jtp10283/">Java 理论与实践: 垃圾收集简史</a> </li>
    <li><a href="http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html#resources">Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning</a> </li>
    <li><a href="http://developers.sun.com/mobility/midp/articles/garbagecollection2/#16.2.6">Improving Java Application Performance and Scalability by Reducing Garbage Collection Times and Sizing Memory Using JDK 1.4.1</a> </li>
    <li><a href="https://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf">Hotspot memory management whitepaper</a> </li>
    <li><a href="http://java.sun.com/performance/reference/whitepapers/tuning.html">Java Tuning White Paper</a> </li>
    <li><a href="http://java.sun.com/docs/hotspot/gc1.4.2/example.html">Diagnosing a Garbage Collection problem</a> </li>
    <li><a href="http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp">Java HotSpot VM Options</a> </li>
    <li><a href="http://blogs.sun.com/watt/resource/jvm-options-list.html">A Collection of JVM Options</a> </li>
    <li><a href="http://java.sun.com/docs/hotspot/gc1.4.2/faq.html">Frequently Asked Questions about Garbage Collection in the HotspotTM JavaTM Virtual Machine</a></li>
</ul>
</span>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/239875.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-11-11 14:35 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/11/11/239875.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>应用服务器内存泄露问题诊断</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/10/28/237122.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 28 Oct 2008 07:05:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/10/28/237122.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/237122.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/10/28/237122.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/237122.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/237122.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp; 在中间件应用服务器的整体调优中，有关于等待队列、执行线程，EJB池以及数据库连接池和Statement Cache方面的调优，这些都属于系统参数方面的调优，本文主要从另外一个角度，也就是从应用的角度来解决中间件应用服务器的内存泄露问题，从这个角度来提高系统的稳定性和性能。 </p>
<p><span><span style="font-size: medium; color: rgb(0,0,0); font-family: Arial">项目背景</span></span> </p>
<p><span><strong><span style="font-size: small; color: rgb(0,0,0); font-family: Arial">问题描述</span></strong></span> </p>
<p>某个大型项目（Use Case用例超过300个），在项目上线后，其Web应用服务器经常宕机。表现为： </p>
<p>1. 应用服务器内存长期不合理占用，内存经常处于高位占用，很难回收到低位； </p>
<p>2. 应用服务器极为不稳定，几乎每两天重新启动一次，有时甚至每天重新启动一次； </p>
<p>3. 应用服务器经常做<span class="hilite3">Full</span> <span class="hilite4">GC</span>(Garbage Collection)，而且时间很长，大约需要30-40秒，应用服务器在做<span class="hilite3">Full</span> <span class="hilite4">GC</span>的时候是不响应客户的交易请求的，非常影响系统性能。 </p>
<p><span><strong><span style="font-size: small; color: rgb(0,0,0); font-family: Arial">Web应用服务器的物理部署</span></strong></span> </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一台Unix服务器（4CPU，8G Memory）来部署本Web应用程序；Web应用程序部署在中间件应用服务器上；部署了一个节点（Node），只配置一个应用服务器实例（Instance），没有做Cluster部署。 </p>
<p><span><strong><span style="font-size: small; color: rgb(0,0,0); font-family: Arial">Web应用服务器启动脚本中的内存参数</span></strong></span> </p>
<table cellpadding="0" width="100%" border="1">
    <tbody>
        <tr>
            <td bgcolor="#f4f4f4">MEM_ARGS="-XX:MaxPermSize=128m -XX:MaxNewSize=512m -Xms3096m <br />
            -Xmx3096m -XX:+Printetails -Xloggc:./inwebapp1/<span class="hilite4">gc</span>.$$" </td>
        </tr>
    </tbody>
</table>
<p>可以看出目前生产系统中Web应用服务器的内存分配为3G Memory。 </p>
<p><span><strong><span style="font-size: small; color: rgb(0,0,0); font-family: Arial">Web应用服务器的重要部署参数</span></strong></span> </p>
<table cellpadding="4" border="1">
    <tbody>
        <tr>
            <td>参数名称 </td>
            <td>参数值 </td>
            <td>参数解释 </td>
        </tr>
        <tr>
            <td>kernel.default(Thread Count) </td>
            <td>120 </td>
            <td>执行线程数目，是并发处理能力的重要参数 </td>
        </tr>
        <tr>
            <td>Session Timeout </td>
            <td>240分钟（4小时） </td>
            <td>HttpSession会话超时</td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span><span style="font-size: medium; color: rgb(0,0,0); font-family: Arial"><strong>分析</strong></span></span> </p>
<p><span><strong><span style="font-size: small; color: rgb(0,0,0); font-family: Arial">分析方法</span></strong></span> </p>
<p>内存长期占用并导致系统不稳定一般有两种可能： </p>
<p>1. 对象被大量创建而且被缓存，在旧的对象释放前又有大量新的对象被创建使得内存长期高位占用。 </p>
<ul>
    <li>表现为：内存不断被消耗、在高位时也很难回归到低位，有大量的对象在不断的创建，经过很长时间后又被回收。例如：在HttpSession中保存了大量的分页查询数据，而HttpSession的会话超时时间设置过长（例如：1天），那么在旧的对象释放前又有大量新的对象在第二天产生。 </li>
    <li>解决办法：对共享的对象可以采用池机制进行缓存，避免各自创建；缓存的临时对象应该及时释放；另一种办法是扩大系统的内存容量。</li>
</ul>
<p>2. 另一种情况就是内存泄漏问题 </p>
<ul>
    <li>表现为：内存回收低位点不断升高（以每次内存回收的最低点连成一条直线，那么它是一条上升线）；内存回收的频率也越来越高，内存占用也越来越高，最终出现"Out of Memory Exception"的系统异常。 </li>
    <li>解决办法：定位那些有内存泄漏的类或对象并修改完善这些类以避免内存泄漏。方法是：经过一段时间的测试、监控，如果某个类的对象数目屡创新高，即使在JVM <span class="hilite3">Full</span> <span class="hilite4">GC</span>后仍然数目降不下来，这些对象基本上是属于内存泄漏的对象了。</li>
</ul>
<p><span><strong><span style="font-size: small; color: rgb(0,0,0); font-family: Arial">问题定位</span></strong></span> </p>
<p>这里请看5月份 Web应用服务器的内存回收图形： </p>
<p>《注意：5月18日早上10点重新启动了Web服务器，5月20日早上又重新启动了Web服务器。》 </p>
<ul>
    <li>在Web应用重要部署参数中，我们知道：Session的超时时间为4个小时，我们在监控平台也观测到：在18日晚上10点左右所有的会话都过期了，从图形一中也能看出18日晚上确实系统的内存有回收到40%（就象股票的高位跳水）； </li>
    <li>从图形一（5月18日）中我们也能看到<span class="hilite3">Full</span> <span class="hilite4">GC</span>回收后的内存占用率走势（红色曲线），上午基本平滑上升到20%（内存占用率），中午开始上升到30%，下午上升到40% </li>
    <li>从图形二（5月19日）中我们也能看到<span class="hilite3">Full</span> <span class="hilite4">GC</span>回收后的内存占用率走势（红色曲线），上午又上升到了60%，到下午上升到了70%。 </li>
    <li>从黄色曲线（<span class="hilite4">GC</span>花费的时间，以秒为单位），<span class="hilite3">Full</span> <span class="hilite4">GC</span>的频率也在增快，时间耗费也越来越长，在图形一中基本高位在20秒左右，到19日基本都是30-40秒之间了。</li>
</ul>
<p>&nbsp;<strong><span style="color: rgb(0,0,0)">图形一 5月18日</span></strong><br />
<img height="318" alt="图形一 5月18日" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image002.gif" width="554" border="0" /><br />
<br />
<strong><span style="color: rgb(0,0,0)">图二</span></strong><br />
<img height="320" alt="图二" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image004.gif" width="554" border="0" /></p>
<p>通过上述分析，我们基本定位到了Web应用服务器的内存在高位长期占用的原因了：是内存泄露！并且正是由于这个原因导致系统不稳定、响应客户请求越来越慢的。 </p>
<p><span><strong><span style="font-size: small; color: rgb(0,0,0); font-family: Arial">解决方法</span></strong></span> </p>
<p>方法如下： </p>
<ul>
    <li>我们从图形二中发现，在8.95(将近9点钟)到9.66（将近9点40）期间有几次<span class="hilite3">Full</span> <span class="hilite4">GC</span>，但是有内存泄漏，从占用率40%上升到50%左右，泄漏了大约10%的内存，约300M； </li>
    <li>我们在自己搭建的Web应用服务器平台（应用软件版本和生产版本一致）做这一阶段相同的查询交易；表明对同一个黑盒（Web应用）施加同样的刺激（相同的操作过程和查询交易）以期重现现象； </li>
    <li>我们使用Jprofiler工具对Web应用服务器的内存进行实时监控； </li>
    <li>做完这些交易后，用户退出系统，并等待Web应用服务器的HttpSession超时（我们这里设置为15分钟）； </li>
    <li>我们对Web应用服务器做了两次强制性的内存回收操作。</li>
</ul>
<p>发现如下： </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图三</span></strong><br />
<img height="392" alt="图三" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image007.gif" width="554" border="0" /></p>
<p>如图三所示，内存经过HttpSession超时后，并强制<span class="hilite4">gc</span>后，仍然有大量的对象没有释放。例如：gov.gdlt.taxcore.comm.security.MenuNode，仍然有807个实例没有释放。 </p>
<p>我们继续追溯发现，这些MenuNode首先存放在一个ArrayList对象中，然后发现这个ArrayList对象又是存放在 WHsessionAttrVO对象的Map中，WHsessionAttrVO 对象又是存放在ExternalSessionManager的staic Map中（名称为sessionMap），如图四所示。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图四</span></strong><br />
<img height="260" alt="图四" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image009.gif" width="554" border="0" /></p>
<p>我们发现gov.gdlt.taxcore.taxevent.xtgl.comm.WHsessionAttrVO中保存了EJBSessionId信息 (登录用户的唯一标志，由用户id+登录时间戳组成，每天都不同)和一个HashMap，这个HashMap中的内容有： </p>
<ul>
    <li>ArrayList: 内有MenuTreeNodes（菜单树节点） </li>
    <li>HashMap: 内有操作人员代码信息 </li>
    <li>CurrentVersion:当前版本号 </li>
    <li>CurrentTime:当前系统时间</li>
</ul>
<p>WHsessionAttrVO 这个对象的最终存放在ExternalSessionManager的static Map sessionMap中，由于ExternalSessionManager是一个全局的单实例，不会释放，所以它的成员变量sessionMap中的数据也不会释放，而Map中的Key值为EJBSessionId，每天登录的用户EJBSessionId都不同，就造成了每天的登录信息（包括菜单信息）都保存在sessionMap中不会被释放，最终造成了内存的泄漏。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图五</span></strong><br />
<img height="151" alt="图五" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image011.jpg" width="502" border="0" /></p>
<p>如上图所示：WHsessionAttrsVO对象中除了有一个String对象（内容是EJBSessionId），还有一个HashMap对象。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图六</span></strong><br />
<img height="264" alt="图六" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image013.gif" width="516" border="0" /></p>
<p>如上图所示，这个HashMap中的内容主要有menuTreeNodes为key，value为ArrayList的对象和以czrydminfo为key，value为HashMap对象的数据。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图七</span></strong><br />
<img height="299" alt="图七" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image015.jpg" width="596" border="0" /></p>
<p>如上图所示：menuTreeNodes为key，value为ArrayList对象中包含的对象有许多的MenuNode对象，封装的都是用户的菜单节点。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图八</span></strong><br />
<img height="116" alt="图八" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image017.jpg" width="384" border="0" /></p>
<p>如上图所示，最顶层（Root）的初始对象为一个ExternalSessionManager对象，其中的一个成员变量为static (静态的)，名称为：sessionMap，这个对象是singleton方式的，全局只有一个。 </p>
<p><span><strong><span style="font-size: small; color: rgb(0,0,0); font-family: Arial">初步估量</span></strong></span> </p>
<p>我们从图形一和图形二中可以看出，每天应用服务器损失大约40%的内存，大约1G左右。 </p>
<p>从图形四可以看出，当前用户（Id=24400001129）有807个菜单项（每个菜单项为一个MenuNode 对象实例，图形四中的这个实例的size为592 Byte），这些菜单数据和用户基本登录信息（czrydmInfo HashMap）也都存放在WHsessionAttrVO对象中，当前这个WHsessionAttrVO对象的size为457K。 </p>
<p>我们做如下估算： </p>
<p>假设平均每天有4千人（估计值，这个数值仅仅是5月19日峰值的1/2左右）登录系统（有重复登录的现象，例如：上午登录一次，中午退出系统，下午登录一次），以平均每人占用200K（估计值，是用户id=24400001129 的Size的1/2左右）来计算，一天泄漏的内存约800M，比较符合目前内存泄漏的情况。当然，这种估计仍然需要经过实践的检验，方法是：当这次发现的内存泄漏问题解决后看系统是否还有其它内存泄漏问题。 </p>
<p><br />
</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img class="magplus" title="点击查看原始大小图片" height="0" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="700" /></td>
        </tr>
    </tbody>
</table>
<span><span style="font-size: medium; color: rgb(0,0,0); font-family: Arial">方案</span></span>
<p>ExternalSessionManager类是当初某某软件商设计的用来解决Web服务器负载均衡的模块，这个类主要用来保存客户的基本登录信息（包括会话的EJBSessionId），以维护多个Web服务器之间的会话信息一致。 </p>
<p>改进方案有两种： </p>
<ul>
    <li>
    <p>从架构设计方面改进 </p>
    <p>实现Web层的负载均衡有很多标准的实现方式。例如：采用负载均衡设备(硬件或软件)来实现。 </p>
    <p>如果采用新的Web层的负载均衡方式，那么就可以去掉ExternalSessionManager这个类了。 </p>
    </li>
    <li>
    <p>从应用实现方面改进 </p>
    <p>保留当前的Web层的负载均衡设计机制，仅仅从应用实现方面解决内存泄漏问题，首先菜单信息不应该保存在ExternalSessionManager中。其次，增加对ExternalSessionManager类中用户会话登录信息的清除，有几种方式可以选择： </p>
    <ul>
        <li>被动方式，当HttpSession会话超时（或过期）被Web应用服务器回收时清除相应的ExternalSessionManager中的过期会话登录信息。 </li>
        <li>主动方式，可以采用任务定时清理每天的过期会话登录信息或线程轮询清理。 </li>
        <li>采用新的会话登录信息存储方式，ExternalSessionManager的sessionMap中的key值不再以EJBSessionId作为键值，而是以用户id（EJBSessionId的前11位）代替。由于用户id每天都是一样的，所以不会造成内存泄漏。保存得登录信息也不再包含菜单节点信息，而只是登录基本信息。最多也只是保存整个系统所有的用户id及其基本登录信息（大约每个用户的登录信息只有1.5K左右，而目前这个系统的营业网点用户为1万左右，所以大约只占用Web服务器15M内存）。</li>
    </ul>
    </li>
</ul>
<p><br />
</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img class="magplus" title="点击查看原始大小图片" height="0" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="700" /></td>
        </tr>
    </tbody>
</table>
<span><span style="font-size: medium; color: rgb(0,0,0); font-family: Arial">实施情况</span></span>
<p>采用的方案：某某软件商采用了新的会话登录信息存贮方案，即:ExternalSessionManager的成员变量sessionMap中不再保存用户菜单信息，只保存基本的登录信息；存储方式采用用户id（11位）作为键值（key）来保留用户基本登录信息。 </p>
<p>基本分析：由于基本登录信息只有1K左右，而目前内网登录的用户总数也只有8887个，所以只保存了大约10M-15M的信息在内存，占用量很小，并且不会有内存泄漏。用户菜单信息保存在session中，如果用户退出时点击logout页面，那么应用服务器可以很快地释放这部分内存；如果用户直接关闭窗口，那么保存在session中的菜单信息只有等会话超时后才会由系统清除并回收内存。 </p>
<p>监控状况： </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图九</span></strong><br />
<img height="378" alt="图九" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image019.jpg" width="622" border="0" /></p>
<p>如图九所示，ExternalSessionManager中只保留了简单的登录信息（Map中保存了WHsessionAttrVO对象），包括：当前版本(currentversion)，操作人员代码基本信息（czrydmInfo），当前时间（currenttime）。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图十</span></strong><br />
<img height="164" alt="图十" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image021.jpg" width="553" border="0" /></p>
<p>如图十所示，这个登录用户的基本信息只有1368 bytes，大约1.3K </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图十一</span></strong><br />
<img height="281" alt="图十一" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image023.jpg" width="658" border="0" /></p>
<p>如图十一所示，一共有两个用户（相同的用户id）登录系统，当一个用户使用logout页面退出时，保留在session中的菜单信息(MenuNode)立刻释放了，所以Difference一栏减少了806个菜单项。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图十二</span></strong><br />
<img height="256" alt="图十二" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image025.jpg" width="604" border="0" /></p>
<p>如图十二所示，当另外一个会话超时后，应用服务器回收了整个会话的菜单信息（MenuNode），图上已经没有MenuNode对象了。并且由于是同一个用户登录，所以保留在ExternalSessionManager成员变量sessionMap中的对象WHsessionAttrVO只有一个 (id=24400001129)，而没有产生多个，没有因为多次登录而产生多个对象的后果，避免了内存泄漏问题的出现，解决了前期定位的内存泄漏问题。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图十三</span></strong><br />
<img height="335" alt="图十三" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image027.jpg" width="451" border="0" /></p>
<p>如图十三所示，经过<span class="hilite4">gc</span>内存回收后，发现内存回收比较稳定，基本都回收到了最低点，也证明了内存没有泄露。 </p>
<p>结论与建议：从测试情况看，解决了前期定位的内存泄漏问题。 </p>
<p><strong>生产系统实施后的监控与分析</strong> </p>
<p>经过调优后，我们发现：在2005年6月2日晚9点40左右重新部署、启动了Web应用服务器（采用了新的调优方案）。经过几天的监控运行，发现Web应用服务器目前运行基本稳定，目前没有出现新的内存泄漏问题，下列图示说明了这一点 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图十四 2005年6月2日</span></strong><br />
<img height="224" alt="图十四 2005年6月2日" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image029.gif" width="554" border="0" /></p>
<p>如图十四所示，6月2日晚21.7（21点42分）重新启动应用服务器，内存占用很少，大约为15%（请看红色曲线），每次<span class="hilite4">GC</span>消耗的时间也很短，大约在5秒以内（请看黄色曲线）。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图十五 2005年6月3日周五</span></strong><br />
<img height="248" alt="图十五 2005年6月3日周五" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image031.gif" width="553" border="0" /></p>
<p>如图十五所示，在6月3日周五的整个工作日内，内存的回收基本到位，回收位置控制在20%-30%之间，也就是在600M-900M之间（请看红色曲线的最低点），始终可以回收2G的内存供应用程序使用，每次<span class="hilite4">GC</span>的时间最高不超过20秒，<span class="hilite3">Full</span> <span class="hilite4">GC</span>平均在10秒左右，时间消耗比较短（请看黄色曲线）。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图十六2005年6月5日周日</span></strong><br />
<img height="198" alt="图十六2005年6月5日周日" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image033.gif" width="554" border="0" /></p>
<p>如图十六所示，在周日休息日期间，Web应用服务器全天只做了大约4次<span class="hilite3">Full</span> <span class="hilite4">GC</span>（黄色曲线中的小山峰），时间都在10秒以内；大的<span class="hilite3">Full</span> <span class="hilite4">GC</span>后，内存只占用10%，内存回收很彻底。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图十七 2005年6月6日周一</span></strong><br />
<img height="249" alt="图十七 2005年6月6日周一" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image035.gif" width="554" border="0" /></p>
<p>如图十七所示，在周一工作日期间，内存回收还是不错的，基本可以回收到30%（见红色曲线的最低点），即：占用900M内存空间，剩余2G的内存空间；<span class="hilite3">Full</span> <span class="hilite4">GC</span>的时间大部分控制在20秒以内，平均15秒（见黄色曲线）。 </p>
<p><br />
<strong><span style="color: rgb(0,0,0)">图十八 2005年6月7日周二</span></strong><br />
<img height="208" alt="图十八 2005年6月7日周二" src="http://www-128.ibm.com/developerworks/cn/java/j-performance/images/image037.gif" width="554" border="0" /></p>
<p>如图十八所示，在6月7日周二早上，大约8:30左右，Web应用服务器作了一次<span class="hilite3">Full</span> <span class="hilite4">GC</span>，用了10秒的时间，把内存回收到了10%的位置，为后续的使用腾出了90%的内存空间。内存回收仍然比较彻底，说明基本没有内存泄漏问题。 </p>
<p>经过这几天的监控分析，我们可以看出： </p>
<ul>
    <li>Web应用服务器的内存使用已经比较合理，内存在工作日的占用在20%至30%之间，约1G的内存占用，有2G的内存空间富裕；而在空闲时间（周日，每天的凌晨等）内存可以回收到10%，有90%的内存空间富裕； </li>
    <li>Web应用服务器的<span class="hilite3">Full</span> <span class="hilite4">GC</span>的次数明显减少了并且每次<span class="hilite3">Full</span> <span class="hilite4">GC</span>占用的时间也很少，基本控制在10-20秒之间，有的甚至在10秒以内，明显改善了内网应用服务器内存的使用； </li>
    <li>从6月2日重新部署之后，Web应用服务器没有出现宕机重启的现象。</li>
</ul>
<p>&nbsp;</p>
<p>&nbsp;</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img class="magplus" title="点击查看原始大小图片" height="0" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="700" /></td>
        </tr>
    </tbody>
</table>
<p><span><span style="font-size: medium; color: rgb(0,0,0); font-family: Arial">总结</span></span></p>
<p>&nbsp;通过本文，我们可以看到，内存的泄露将会导致服务器的宕机，系统性能就更别说了。对于系统内存泄露问题应该从服务器<span class="hilite4">GC</span>日志方面进行早诊断，使用工具早确认并提出解决方案，排除内存泄露问题，提高系统性能，以规避项目风险。 </p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/237122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-10-28 15:05 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/10/28/237122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM GC Parameter</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/10/28/237090.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 28 Oct 2008 06:14:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/10/28/237090.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/237090.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/10/28/237090.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/237090.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/237090.html</trackback:ping><description><![CDATA[ <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/237090.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-10-28 14:14 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/10/28/237090.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Thread.interrupt 害人！ 中断JAVA线程（zz）</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/04/27/196477.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sun, 27 Apr 2008 08:16:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/04/27/196477.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/196477.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/04/27/196477.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/196477.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/196477.html</trackback:ping><description><![CDATA[<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td valign="top" width="344"><span class="text">程序是很简易的。然而，在编程人员面前，多线程呈现出了一组新的难题，如果没有被恰当的解决，将导致意外的行为以及细微的、难以发现的错误。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在本篇文章中，我们针对这些难题之一：<span class="STYLE1">如何中断一个正在运行的线程。&nbsp;</span><br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
            <span class="STYLE1">背景</span><br />
            &nbsp;&nbsp;&nbsp;&nbsp;中断（Interrupt）一个线程意味着在该线程完成任务之前停止其正在进行的一切，有效地中止其当前的操作。线程是死亡、还是等待新的任务或是继续运行至下一步，就取决于这个程序。虽然初次看来它可能显得简单，但是，你必须进行一些预警以实现期望的结果。你最好还是牢记以下的几点告诫。<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;首先，忘掉Thread.stop方法。虽然它确实停止了一个正在运行的线程，然而，这种方法是不安全也是不受提倡的，这意味着，在未来的JAVA版本中，它将不复存在。</span></td>
        </tr>
        <tr>
            <td colspan="2" height="20"><span class="text"><br />
            &nbsp;&nbsp;&nbsp;&nbsp;一些轻率的家伙可能被另一种方法Thread.interrupt所迷惑。尽管，其名称似乎在暗示着什么，然而，这种方法并不会中断一个正在运行的线程（待会将进一步说明），正如Listing&nbsp;A中描述的那样。它创建了一个线程，并且试图使用Thread.interrupt方法停止该线程。Thread.sleep()方法的调用，为线程的初始化和中止提供了充裕的时间。线程本身并不参与任何有用的操作。<br />
            <br />
            <pre>class Example1 extends Thread {
            boolean stop=false;
            public static void main( String args[] ) throws Exception {
            Example1 thread = new Example1();
            System.out.println( "Starting thread..." );
            thread.start();
            Thread.sleep( 3000 );
            System.out.println( "Interrupting thread..." );
            thread.interrupt();
            Thread.sleep( 3000 );
            System.out.println("Stopping application..." );
            //System.exit(0);
            }
            public void run() {
            while(!stop){
            System.out.println( "Thread is running..." );
            long time = System.currentTimeMillis();
            while((System.currentTimeMillis()-time &lt; 1000)) {
            }
            }
            System.out.println("Thread exiting under request..." );
            }
            }</pre>
            </span>
            <p class="text">如果你运行了Listing&nbsp;A中的代码，你将在控制台看到以下输出：<br />
            <br />
            Starting&nbsp;thread...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Interrupting&nbsp;thread...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Stopping&nbsp;application...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            ............................... <br />
            甚至，在Thread.interrupt()被调用后，线程仍然继续运行。<br />
            <br />
            <span class="STYLE1">真正地中断一个线程</span><br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;中断线程最好的，最受推荐的方式是，使用共享变量（shared&nbsp;variable）发出信号，告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量（尤其在冗余操作期间），然后有秩序地中止任务。Listing&nbsp;B描述了这一方式。<br />
            <br />
            Listing&nbsp;B<br />
            class&nbsp;Example2&nbsp;extends&nbsp;Thread&nbsp;{<br />
            <br />
            &nbsp;&nbsp;volatile&nbsp;boolean&nbsp;stop&nbsp;=&nbsp;false;<br />
            <br />
            &nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(&nbsp;String&nbsp;args[]&nbsp;)&nbsp;throws&nbsp;Exception&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;Example2&nbsp;thread&nbsp;=&nbsp;new&nbsp;Example2();<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Starting&nbsp;thread..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;thread.start();<br />
            <br />
            &nbsp;&nbsp;&nbsp;Thread.sleep(&nbsp;3000&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Asking&nbsp;thread&nbsp;to&nbsp;stop..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;thread.stop&nbsp;=&nbsp;true;<br />
            <br />
            &nbsp;&nbsp;&nbsp;Thread.sleep(&nbsp;3000&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Stopping&nbsp;application..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;//System.exit(&nbsp;0&nbsp;);<br />
            <br />
            &nbsp;&nbsp;}<br />
            <br />
            &nbsp;&nbsp;public&nbsp;void&nbsp;run()&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(&nbsp;!stop&nbsp;)&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Thread&nbsp;is&nbsp;running..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;time&nbsp;=&nbsp;System.currentTimeMillis();<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(&nbsp;(System.currentTimeMillis()-time&nbsp;&lt;&nbsp;1000)&nbsp;&amp;&amp;&nbsp;(!stop)&nbsp;)&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;}<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Thread&nbsp;exiting&nbsp;under&nbsp;request..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;}<br />
            <br />
            }<br />
            &nbsp;<br />
            运行Listing&nbsp;B中的代码将产生如下输出（注意线程是如何有秩序的退出的）<br />
            <br />
            Starting&nbsp;thread...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Thread&nbsp;is&nbsp;running...<br />
            <br />
            Asking&nbsp;thread&nbsp;to&nbsp;stop...<br />
            <br />
            Thread&nbsp;exiting&nbsp;under&nbsp;request...<br />
            <br />
            Stopping&nbsp;application...<br />
            <br />
            &nbsp;&nbsp;&nbsp;虽然该方法要求一些编码，但并不难实现。同时，它给予线程机会进行必要的清理工作，这在任何一个多线程应用程序中都是绝对需要的。请确认将共享变量定义成volatile&nbsp;类型或将对它的一切访问封入同步的块/方法（synchronized&nbsp;blocks/methods）中。<br />
            <br />
            到目前为止一切顺利!但是，当线程等待某些事件发生而被阻塞，又会发生什么？当然，如果线程被阻塞，它便不能核查共享变量，也就不能停止。这在许多情况下会发生，例如调用Object.wait()、ServerSocket.accept()和DatagramSocket.receive()时，这里仅举出一些。<br />
            <br />
            他们都可能永久的阻塞线程。即使发生超时，在超时期满之前持续等待也是不可行和不适当的，所以，要使用某种机制使得线程更早地退出被阻塞的状态。<br />
            <br />
            很不幸运，不存在这样一种机制对所有的情况都适用，但是，根据情况不同却可以使用特定的技术。在下面的环节，我将解答一下最普遍的例子。<br />
            <span class="STYLE1"><br />
            使用Thread.interrupt()中断线程</span><br />
            <br />
            &nbsp;&nbsp;正如Listing&nbsp;A中所描述的，Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是，在线程受到阻塞时抛出一个中断信号，这样线程就得以退出阻塞的状态。更确切的说，如果线程被Object.wait,&nbsp;Thread.join和Thread.sleep三种方法之一阻塞，那么，它将接收到一个中断异常（InterruptedException），从而提早地终结被阻塞状态。<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;因此，如果线程被上述几种方法阻塞，正确的停止线程方式是设置共享变量，并调用interrupt()（注意变量应该先设置）。<span class="STYLE1">如果线程没有被阻塞，这时调用interrupt()将不起作用；否则，线程就将得到异常（该线程必须事先预备好处理此状况），接着逃离阻塞状态。</span>在任何一种情况中，最后线程都将检查共享变量然后再停止。Listing&nbsp;C这个示例描述了该技术。<br />
            <br />
            Listing&nbsp;C<br />
            class&nbsp;Example3&nbsp;extends&nbsp;Thread&nbsp;{<br />
            <br />
            &nbsp;&nbsp;volatile&nbsp;boolean&nbsp;stop&nbsp;=&nbsp;false;<br />
            <br />
            &nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(&nbsp;String&nbsp;args[]&nbsp;)&nbsp;throws&nbsp;Exception&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;Example3&nbsp;thread&nbsp;=&nbsp;new&nbsp;Example3();<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Starting&nbsp;thread..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;thread.start();<br />
            <br />
            &nbsp;&nbsp;&nbsp;Thread.sleep(&nbsp;3000&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Asking&nbsp;thread&nbsp;to&nbsp;stop..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;thread.stop&nbsp;=&nbsp;true;//如果线程阻塞，将不会检查此变量<br />
            <br />
            &nbsp;&nbsp;&nbsp;thread.interrupt();<br />
            <br />
            &nbsp;&nbsp;&nbsp;Thread.sleep(&nbsp;3000&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Stopping&nbsp;application..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;//System.exit(&nbsp;0&nbsp;);<br />
            <br />
            &nbsp;&nbsp;}<br />
            <br />
            &nbsp;&nbsp;public&nbsp;void&nbsp;run()&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(&nbsp;!stop&nbsp;)&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Thread&nbsp;running..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(&nbsp;1000&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(&nbsp;InterruptedException&nbsp;e&nbsp;)&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Thread&nbsp;interrupted..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;}<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Thread&nbsp;exiting&nbsp;under&nbsp;request..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;}<br />
            <br />
            }<br />
            <br />
            一旦Listing&nbsp;C中的Thread.interrupt()被调用，线程便收到一个异常，于是逃离了阻塞状态并确定应该停止。运行以上代码将得到下面的输出：<br />
            <br />
            Starting&nbsp;thread...<br />
            <br />
            Thread&nbsp;running...<br />
            <br />
            Thread&nbsp;running...<br />
            <br />
            Thread&nbsp;running...<br />
            <br />
            Asking&nbsp;thread&nbsp;to&nbsp;stop...<br />
            <br />
            Thread&nbsp;interrupted...<br />
            <br />
            Thread&nbsp;exiting&nbsp;under&nbsp;request...<br />
            <br />
            Stopping&nbsp;application...<br />
            <br />
            <br />
            <span class="STYLE1">中断I/O操作</span><br />
            &nbsp;&nbsp;&nbsp;&nbsp;然而，如果线程在I/O操作进行时被阻塞，又会如何？I/O操作可以阻塞线程一段相当长的时间，特别是牵扯到网络应用时。例如，服务器可能需要等待一个请求（request），又或者，一个网络应用程序可能要等待远端主机的响应。<br />
            <br />
            如果你正使用通道（channels）（这是在Java&nbsp;1.4中引入的新的I/O&nbsp;API），那么被阻塞的线程将收到一个ClosedByInterruptException异常。如果情况是这样，其代码的逻辑和第三个例子中的是一样的，只是异常不同而已。<br />
            <br />
            但是，你可能正使用Java1.0之前就存在的传统的I/O，而且要求更多的工作。既然这样，Thread.interrupt()将不起作用，因为线程将不会退出被阻塞状态。Listing&nbsp;D描述了这一行为。尽管interrupt()被调用，线程也不会退出被阻塞状态<br />
            <br />
            Listing&nbsp;D<br />
            import&nbsp;java.io.*;<br />
            <br />
            class&nbsp;Example4&nbsp;extends&nbsp;Thread&nbsp;{<br />
            <br />
            &nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(&nbsp;String&nbsp;args[]&nbsp;)&nbsp;throws&nbsp;Exception&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;Example4&nbsp;thread&nbsp;=&nbsp;new&nbsp;Example4();<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Starting&nbsp;thread..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;thread.start();<br />
            <br />
            &nbsp;&nbsp;&nbsp;Thread.sleep(&nbsp;3000&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Interrupting&nbsp;thread..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;thread.interrupt();<br />
            <br />
            &nbsp;&nbsp;&nbsp;Thread.sleep(&nbsp;3000&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Stopping&nbsp;application..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;//System.exit(&nbsp;0&nbsp;);<br />
            <br />
            &nbsp;&nbsp;}<br />
            <br />
            &nbsp;&nbsp;public&nbsp;void&nbsp;run()&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;ServerSocket&nbsp;socket;<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;socket&nbsp;=&nbsp;new&nbsp;ServerSocket(7856);<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(&nbsp;IOException&nbsp;e&nbsp;)&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Could&nbsp;not&nbsp;create&nbsp;the&nbsp;socket..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;}<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(&nbsp;true&nbsp;)&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Waiting&nbsp;for&nbsp;connection..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Socket&nbsp;sock&nbsp;=&nbsp;socket.accept();<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(&nbsp;IOException&nbsp;e&nbsp;)&nbsp;{<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"accept()&nbsp;failed&nbsp;or&nbsp;interrupted..."&nbsp;);<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;}<br />
            <br />
            &nbsp;&nbsp;}<br />
            <br />
            }<br />
            <br />
            <br />
            &nbsp;&nbsp;&nbsp;很幸运，Java平台为这种情形提供了一项解决方案，即调用阻塞该线程的套接字的close()方法。在这种情形下，如果线程被I/O操作阻塞，该线程将接收到一个SocketException异常，这与使用interrupt()方法引起一个InterruptedException异常被抛出非常相似。<br />
            <br />
            唯一要说明的是，必须存在socket的引用（reference），只有这样close()方法才能被调用。这意味着socket对象必须被共享。Listing&nbsp;E描述了这一情形。运行逻辑和以前的示例是相同的。<br />
            <br />
            Listing&nbsp;E<br />
            import&nbsp;java.net.*;<br />
            import&nbsp;java.io.*;<br />
            class&nbsp;Example5&nbsp;extends&nbsp;Thread&nbsp;{<br />
            &nbsp;&nbsp;volatile&nbsp;boolean&nbsp;stop&nbsp;=&nbsp;false;<br />
            &nbsp;&nbsp;volatile&nbsp;ServerSocket&nbsp;socket;<br />
            &nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(&nbsp;String&nbsp;args[]&nbsp;)&nbsp;throws&nbsp;Exception&nbsp;{<br />
            &nbsp;&nbsp;&nbsp;&nbsp;Example5&nbsp;thread&nbsp;=&nbsp;new&nbsp;Example5();<br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Starting&nbsp;thread..."&nbsp;);<br />
            &nbsp;&nbsp;&nbsp;thread.start();<br />
            &nbsp;&nbsp;&nbsp;Thread.sleep(&nbsp;3000&nbsp;);<br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Asking&nbsp;thread&nbsp;to&nbsp;stop..."&nbsp;);<br />
            &nbsp;&nbsp;&nbsp;thread.stop&nbsp;=&nbsp;true;<br />
            &nbsp;&nbsp;&nbsp;thread.socket.close();<br />
            &nbsp;&nbsp;&nbsp;Thread.sleep(&nbsp;3000&nbsp;);<br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Stopping&nbsp;application..."&nbsp;);<br />
            &nbsp;&nbsp;&nbsp;//System.exit(&nbsp;0&nbsp;);<br />
            &nbsp;&nbsp;}<br />
            &nbsp;&nbsp;public&nbsp;void&nbsp;run()&nbsp;{<br />
            &nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;socket&nbsp;=&nbsp;new&nbsp;ServerSocket(7856);<br />
            &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(&nbsp;IOException&nbsp;e&nbsp;)&nbsp;{<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Could&nbsp;not&nbsp;create&nbsp;the&nbsp;socket..."&nbsp;);<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;}<br />
            &nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(&nbsp;!stop&nbsp;)&nbsp;{<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Waiting&nbsp;for&nbsp;connection..."&nbsp;);<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Socket&nbsp;sock&nbsp;=&nbsp;socket.accept();<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(&nbsp;IOException&nbsp;e&nbsp;)&nbsp;{<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"accept()&nbsp;failed&nbsp;or&nbsp;interrupted..."&nbsp;);<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
            &nbsp;&nbsp;&nbsp;&nbsp;}<br />
            &nbsp;&nbsp;&nbsp;System.out.println(&nbsp;"Thread&nbsp;exiting&nbsp;under&nbsp;request..."&nbsp;);<br />
            &nbsp;&nbsp;}<br />
            }<br />
            以下是运行Listing&nbsp;E中代码后的输出：<br />
            <br />
            Starting&nbsp;thread...<br />
            <br />
            Waiting&nbsp;for&nbsp;connection...<br />
            <br />
            Asking&nbsp;thread&nbsp;to&nbsp;stop...<br />
            <br />
            accept()&nbsp;failed&nbsp;or&nbsp;interrupted...<br />
            <br />
            Thread&nbsp;exiting&nbsp;under&nbsp;request...<br />
            <br />
            Stopping&nbsp;application...<br />
            <br />
            多线程是一个强大的工具，然而它正呈现出一系列难题。其中之一是如何中断一个正在运行的线程。如果恰当地实现，使用上述技术中断线程将比使用Java平台上已经提供的内嵌操作更为简单。 </p>
            </td>
        </tr>
    </tbody>
</table>
============================================<br />
<br />
Writing multithreaded programs in Java, with its built-in support for threads, is fairly straightforward. However, multithreading presents a whole set of new challenges to the programmer that, if not correctly addressed, can lead to unexpected behavior and subtle, hard-to-find errors. In this article, we address one of those challenges: how to interrupt a running thread.<br />
<br />
<span class="subhead1">Background</span><br />
Interrupting a thread means stopping what it is doing before it has completed its task, effectively aborting its current operation. Whether the thread dies, waits for new tasks, or goes on to the next step depends on the application.<br />
<br />
Although it may seem simple at first, you must take some precautions in order to achieve the desired result. There are some caveats you must be aware of as well.<br />
<br />
First of all, forget the <em>Thread.stop</em> method. Although it indeed stops a running thread, the method is unsafe and was <a href="http://java.sun.com/j2se/1.3/docs/guide/misc/threadPrimitiveDeprecation.html" target="_target">deprecated</a>, which means it may not be available in future versions of the Java.<br />
<br />
Another method that can be confusing for the unadvised is <em>Thread.interrupt</em>. Despite what its name may imply, the method does not interrupt a running thread (more on this later), as <a onclick="window.open('http://techrepublic.com.com/html/tr/sidebars/5144546-0.html','','width=680,height=600,top=0,left=0,toolbar=no,location=yes,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes')" href="http://articles.techrepublic.com.com/5100-22-5144546.html#">Listing A</a> demonstrates. It creates a thread and tries to stop it using <em>Thread.interrupt</em>. The calls to <em>Thread.sleep</em><em>(</em><em>)</em> give plenty of time for the thread initialization and termination. The thread itself does not do anything useful.<br />
<br />
If you run the code in Listing A, you should see something like this on your console:<br />
<span class="code">Starting thread...</span><br />
<span class="code">Thread is running...</span><br />
<span class="code">Thread is running...</span><br />
<span class="code">Thread is running...</span><br />
<span class="code">Interrupting thread...</span><br />
<span class="code">Thread is running...</span><br />
<span class="code">Thread is running...</span><br />
<span class="code">Thread is running...</span><br />
<span class="code">Stopping application...</span><br />
<br />
Even after <em>Thread.interrupt</em><em>()</em> is called, the thread continues to run for a while.<br />
<br />
<span class="subhead1">Really interrupting a thread</span><br />
The best, recommended way to interrupt a thread is to use a shared variable to signal that it must stop what it is doing. The thread must check the variable periodically, especially during lengthy operations, and terminate its task in an orderly manner. <a onclick="window.open('http://techrepublic.com.com/html/tr/sidebars/5144546-1.html','','width=680,height=600,top=0,left=0,toolbar=no,location=yes,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes')" href="http://articles.techrepublic.com.com/5100-22-5144546.html#">Listing B</a> demonstrates this technique.<br />
<br />
Running the code in Listing B will generate output like this (notice how the thread exits in an orderly fashion):<br />
<span class="code">Starting thread...</span><br />
<span class="code">Thread is running...</span><br />
<span class="code">Thread is running...</span><br />
<span class="code">Thread is running...</span><br />
<span class="code">Asking thread to stop...</span><br />
<span class="code">Thread exiting under request...</span><br />
<span class="code">Stopping application...</span><br />
<br />
Although this method requires some coding, it is not difficult to implement and give the thread the opportunity to do any cleanup needed, which is an absolute requirement for any multithreaded application. Just be sure to declare the shared variable as <em>volatile</em> or enclose any access to it into <em>synchronized</em> blocks/methods.<br />
<br />
So far, so good! But what happens if the thread is blocked waiting for some event? Of course, if the thread is blocked, it can't check the shared variable and can't stop. There are plenty of situations when that may occur, such as calling <em>Object.wait</em><em>()</em>, <em>ServerSocket.accept</em><em>()</em>, and <em>DatagramSocket.receive</em><em>()</em>, to name a few.<br />
<br />
They all can block the thread forever. Even if a timeout is employed, it may not be feasible or desirable to wait until the timeout expires, so a mechanism to prematurely exit the blocked state must be used.<br />
<br />
Unfortunately there is no such mechanism that works for all cases, but the particular technique to use depends on each situation. In the following sections, I'll give solutions for the most common cases.<br />
<br />
<span class="subhead1">Interrupting a thread with <em>Thread.interrupt</em><em>()</em></span><br />
As demonstrated in Listing A, the method <em>Thread.interrupt</em><em>()</em> does not interrupt a running thread. What the method actually does is to throw an interrupt if the thread is blocked, so that it exits the blocked state. More precisely, if the thread is blocked at one of the methods <em>Object.wait</em>, <em>Thread.join</em>, or <em>Thread.sleep</em>, it receives an <em>InterruptedException</em>, thus terminating the blocking method prematurely.<br />
<br />
So, if a thread blocks in one of the aforementioned methods, the correct way to stop it is to set the shared variable and then call the <em>interrupt()</em> method on it (notice that it is important to set the variable first). If the thread is not blocked, calling <em>interrupt(</em><em>)</em> will not hurt; otherwise, the thread will get an exception (the thread must be prepared to handle this condition) and escape the blocked state. In either case, eventually the thread will test the shared variable and stop. <a onclick="window.open('http://techrepublic.com.com/html/tr/sidebars/5144546-2.html','','width=680,height=600,top=0,left=0,toolbar=no,location=yes,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes')" href="http://articles.techrepublic.com.com/5100-22-5144546.html#">Listing C</a> is a simple example that demonstrates this technique.<br />
<br />
As soon as <em>Thread.interrupt</em><em>()</em> is called in Listing C, the thread gets an exception so that it escapes the blocked state and determines that it should stop. Running this code produces output like this:<br />
<span class="code">Starting thread...</span><br />
<span class="code">Thread running...</span><br />
<span class="code">Thread running...</span><br />
<span class="code">Thread running...</span><br />
<span class="code">Asking thread to stop...</span><br />
<span class="code">Thread interrupted...</span><br />
<span class="code">Thread exiting under request...</span><br />
<span class="code">Stopping application...</span><br />
<br />
<span class="subhead1">Interrupting an I/O operation</span><br />
But what happens if the thread is blocked on an I/O operation? I/O can block a thread for a considerable amount of time, particularly if network communication is involved. For example, a server may be waiting for a request, or a network application may be waiting for an answer from a remote host.<br />
<br />
If you're using channels, available with the new I/O API introduced in Java 1.4, the blocked thread will get a <em>ClosedByInterruptException</em> exception. If that is the case, the logic is the same as that used in the third example—only the exception is different.<br />
<br />
But you might be using the traditional I/O available since Java 1.0, since the new I/O is so recent and requires more work. In this case, <em>Thread.interrupt</em><em>()</em> doesn't help, since the thread will not exit the blocked state. <a onclick="window.open('http://techrepublic.com.com/html/tr/sidebars/5144546-3.html','','width=680,height=600,top=0,left=0,toolbar=no,location=yes,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes')" href="http://articles.techrepublic.com.com/5100-22-5144546.html#">Listing D</a> demonstrates that behavior. Although the <em>interrupt()</em> method is called, the thread does not exit the blocked state.<br />
<br />
Fortunately, the Java Platform provides a solution for that case by calling the <em>close()</em> method of the socket the thread is blocked in. In this case, if the thread is blocked in an I/O operation, the thread will get a <em>SocketException</em> exception, much like the <em>interrupt()</em> method causes an <em>InterruptedException</em> to be thrown.<br />
<br />
The only caveat is that a reference to the socket must be available so that its <em>close()</em> method can be called. That means the socket object must also be shared. <a onclick="window.open('http://techrepublic.com.com/html/tr/sidebars/5144546-4.html','','width=680,height=600,top=0,left=0,toolbar=no,location=yes,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes')" href="http://articles.techrepublic.com.com/5100-22-5144546.html#">Listing E</a> demonstrates this case. The logic is the same as in the examples presented so far.<br />
<br />
And here's the sample output you can expect from running Listing E:<br />
<span class="code">Starting thread...</span><br />
<span class="code">Waiting for connection...</span><br />
<span class="code">Asking thread to stop...</span><br />
<span class="code">accept() failed or interrupted...</span><br />
<span class="code">Thread exiting under request...</span><br />
<span class="code">Stopping application...</span><br />
<br />
Multithreading is a powerful tool, but it presents its own set of challenges. One of these is how to interrupt a running thread. If properly implemented, these techniques make interrupting a thread no more difficult than using the built-in operations already provided by the Java Platform.
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/196477.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-04-27 16:16 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/04/27/196477.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>XDoclet的build.xml写法 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/20/187409.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Thu, 20 Mar 2008 01:52:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/20/187409.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/187409.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/20/187409.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/187409.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/187409.html</trackback:ping><description><![CDATA[http://gocom.primeton.com/showblogarticle.php?cat_id=all&amp;articleId=&amp;blogId=14478&amp;?PHPSESSID=0d485ace30025757c7b9a22da013c1a0&amp;page=4&amp;PHPSESSID=1370043e8b6d902ac0f6fdc3fe7776a8<br />
<br />
<br />
&nbsp;XDoclet的build.xml写法
<p><br />
</p>
<div>
<ol>
    <li>&lt;?xml version="1.0" encoding="utf-8"?&gt; </li>
    <li>&lt;project name="appgen" default="web-demo" basedir="."&gt; </li>
    <li>&nbsp;</li>
    <li>&lt;property file="build.properties" /&gt; </li>
    <li>&nbsp;</li>
    <li>&lt;path id="xdoclet.classpath"&gt; </li>
    <li>&lt;fileset dir="${lib}"&gt; </li>
    <li>&lt;include name="**/*.jar" /&gt; </li>
    <li>&lt;/fileset&gt; </li>
    <li>&lt;path location="${classes}" /&gt; </li>
    <li>&lt;/path&gt; </li>
    <li>&nbsp;</li>
    <li>&lt;target name="clean"&gt; </li>
    <li>&lt;delete&gt; </li>
    <li>&lt;fileset dir="${gen}"&gt; </li>
    <li>&lt;include name="*.xml" /&gt; </li>
    <li>&lt;/fileset&gt; </li>
    <li>&lt;/delete&gt; </li>
    <li>&lt;/target&gt; </li>
    <li>&nbsp;</li>
    <li>&lt;!-- 这个target可以生成，strutsconfigxml , strutsvalidationxml ,web.xml--&gt; </li>
    <li>&nbsp;</li>
    <li>&lt;target name="web-demo" depends="clean,Spring-service-beans,Spring-action-beans,Spring-dao-beans"&gt; </li>
    <li>&lt;taskdef name="webdoclet" classname="xdoclet.modules.web.WebDocletTask" classpathref="xdoclet.classpath" /&gt; </li>
    <li>&lt;webdoclet destDir="${gen}" mergeDir="${merge}" force="false"&gt; </li>
    <li>&lt;fileset dir="${src}"&gt; </li>
    <li>&lt;include name="**/*Action.java" /&gt; </li>
    <li>&lt;include name="**/*Form.java" /&gt; </li>
    <li>&lt;/fileset&gt; </li>
    <li>&nbsp;</li>
    <li>&lt;strutsconfigxml Version="1.2" destDir="${gen}" validateXML="true"/&gt; </li>
    <li>&lt;strutsvalidationxml destDir="${gen}" /&gt; </li>
    <li>&lt;deploymentdescriptor Servletspec="2.4" destDir="${gen}" /&gt; </li>
    <li>&nbsp;</li>
    <li>&lt;/webdoclet&gt; </li>
    <li>&lt;/target&gt; </li>
    <li>&nbsp;</li>
    <li>&lt;target name="ReplaceConfigFile" depends="web-demo"&gt; </li>
    <li>&lt;copy todir="${webinf}"&gt; </li>
    <li>&lt;fileset dir="${gen}"&gt; </li>
    <li>&lt;include name="struts-config.xml"/&gt; </li>
    <li>&lt;include name="validation.xml"/&gt; </li>
    <li>&lt;include name="action-beans.xml"/&gt; </li>
    <li>&lt;include name="dao-beans.xml"/&gt; </li>
    <li>&lt;include name="service-beans.xml"/&gt; </li>
    <li>&lt;/fileset&gt; </li>
    <li>&lt;/copy&gt; </li>
    <li>&lt;/target&gt; </li>
    <li>&lt;!-- 生成spring的xml文件 --&gt; </li>
    <li>&lt;target name="Spring-service-beans" description="Spring-application-beans"&gt; </li>
    <li>&lt;taskdef name="springdoclet" classname="xdoclet.modules.spring.SpringDocletTask" classpathref="xdoclet.classpath" /&gt; </li>
    <li><span>&lt;springdoclet
    excludedTags="@version,@author,@todo" destDir="gen" verbose="true"
    addedTags="@xdoclet-generated at ${TODAY},@copyright The XDoclet
    Team,@author XDoclet,@version ${version}"&gt; </span></li>
    <li>&lt;fileset dir="src" includes="**/*ServiceImpl.java"/&gt; </li>
    <li>&lt;springxml destinationFile="service-beans.xml" destDir="gen"/&gt; </li>
    <li>&lt;/springdoclet&gt; </li>
    <li>&lt;/target&gt; </li>
    <li>&nbsp;</li>
    <li>&nbsp;</li>
    <li>&lt;!-- 生成spring的xml文件 --&gt; </li>
    <li>&lt;target name="Spring-action-beans" description="Spring-servlet-beans"&gt; </li>
    <li>&lt;taskdef name="springdoclet" classname="xdoclet.modules.spring.SpringDocletTask" classpathref="xdoclet.classpath" /&gt; </li>
    <li><span>&lt;springdoclet
    excludedTags="@version,@author,@todo" destDir="gen" verbose="true"
    addedTags="@xdoclet-generated at ${TODAY},@copyright The XDoclet
    Team,@author XDoclet,@version ${version}"&gt; </span></li>
    <li>&lt;springxml destinationFile="action-beans.xml" destDir="gen"/&gt; </li>
    <li>&lt;fileset dir="src" includes="**/*Action.java"/&gt; </li>
    <li>&lt;/springdoclet&gt; </li>
    <li>&lt;/target&gt; </li>
    <li>&nbsp;</li>
    <li>&nbsp;</li>
    <li>&lt;!-- 生成spring的xml文件 --&gt; </li>
    <li>&lt;target name="Spring-dao-beans" description="Spring-servlet-beans"&gt; </li>
    <li>&lt;taskdef name="springdoclet" classname="xdoclet.modules.spring.SpringDocletTask" classpathref="xdoclet.classpath" /&gt; </li>
    <li><span>&lt;springdoclet
    excludedTags="@version,@author,@todo" destDir="gen" verbose="true"
    addedTags="@xdoclet-generated at ${TODAY},@copyright The XDoclet
    Team,@author XDoclet,@version ${version}"&gt; </span></li>
    <li>&lt;springxml destinationFile="dao-beans.xml" destDir="gen"/&gt; </li>
    <li>&lt;fileset dir="src" includes="**/*DAOIbatis.java"/&gt; </li>
    <li>&lt;/springdoclet&gt; </li>
    <li>&lt;/target&gt; </li>
    <li>&nbsp;</li>
    <li>&lt;/project&gt; </li>
    <li>&nbsp;</li>
</ol>
</div>
<p><br />
＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
<p><br />
</p>
<div>
<ol>
    <li>/** </li>
    <li>* @spring.bean id="UserDAO" </li>
    <li>* @spring.property name="dataSource" ref="dataSource" </li>
    <li>* @spring.property name="sqlMapClient" ref="sqlMapClient" </li>
    <li>* </li>
    <li>* @author Conan </li>
    <li>* </li>
    <li>*/ </li>
    <li>public class UserDAOIbatis extends BaseDAOIBatis implements UserDAO {....} </li>
</ol>
</div>
<p><br />
</p>
<p>-------------------------------------------</p>
<p><br />
</p>
<div>
<ol>
    <li>/** </li>
    <li>* @spring.bean id="UserService" </li>
    <li>* @spring.property name="userDao" ref="UserDAO" </li>
    <li>* </li>
    <li>* @author Conan </li>
    <li>* </li>
    <li>*/ </li>
    <li>public class UserServiceImpl extends BaseManager implements UserService {...} </li>
</ol>
</div>
<p><br />
<br />
&nbsp;---------------------------------------------</p>
<p><br />
</p>
<div>
<ol>
    <li>/** </li>
    <li>* </li>
    <li>* @struts.action path="/add" name="UserForm" input="/add.jsp" </li>
    <li>* type="org.springframework.web.struts.DelegatingActionProxy" </li>
    <li>* validate="true" scope="request" </li>
    <li>* </li>
    <li>* @struts.action-forward name="success" path="/success.jsp" redirect="true" </li>
    <li>* @struts.action-forward name="failure" path="/failure.jsp" redirect="true" </li>
    <li>* </li>
    <li>* @struts.action-form name="UserForm" </li>
    <li>* </li>
    <li>* @spring.bean </li>
    <li>* name="/add" </li>
    <li>* @spring.property </li>
    <li>* name="userService" </li>
    <li>* ref="UserService" </li>
    <li>* </li>
    <li>*/ </li>
    <li>public final class AddAction extends BaseAction {...} </li>
</ol>
</div>
<p><br />
</p>
<p>------------------------------------------</p>
<p><br />
</p>
<div>
<ol>
    <li>/** </li>
    <li>* @struts.form name="UserForm" </li>
    <li>*/ </li>
    <li>public class UserForm extends BaseForm{ </li>
    <li>&nbsp;</li>
    <li>/** </li>
    <li>* @struts.validator type="required" </li>
    <li>* @struts.validator type="mask" msgkey="error.age" </li>
    <li>* @struts.validator-var name="mask" value="^[0-9]*$" </li>
    <li>*/ </li>
    <li>public void setAge(Integer age) { </li>
    <li>this.age = age; </li>
    <li>} </li>
    <li>&nbsp;</li>
    <li>} </li>
</ol>
</div>
<p><br />
</p>
<p><br />
</p>
<br />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/187409.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-20 09:52 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/20/187409.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>XDoclet Spring+Struts HowTo zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/20/187406.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Thu, 20 Mar 2008 01:42:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/20/187406.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/187406.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/20/187406.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/187406.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/187406.html</trackback:ping><description><![CDATA[http://www.logemann.org/day/archives/000148.html<br />
<br />
<br />
<p>For all those out there using Spring together with Struts, i created
a small howTo regarding automatic generation of relevant files,
especially the boring action-servlet-xml file which must be in synch
with your struts-config.xml, at least for all Struts actions which
should be injected by Spring.</p>
<p>Lets start with the Action class:</p>
<table border="1">
    <tbody>
        <tr>
            <td>
            <pre>/**<br />
            * Action to delete a Client<br />
            * Date: 09.02.2005<br />
            * Time: 15:29:15<br />
            *<br />
            * @author Logemann - Logentis e.K. (ml@logentis.de)<br />
            * @version $Id$<br />
            * @struts.action path="clientDelete" validate="false"<br />
            *            type="org.springframework.web.struts.DelegatingActionProxy"<br />
            *            name="emptyform"<br />
            * @struts.action-forward name="back" <br />
            *             path="/clientManager.html" redirect="true"<br />
            * @spring.bean name="clientDelete"<br />
            */<br />
            public class ClientDeleteAction extends Action {<br />
            <br />
            <p>    ClientManager clientManagerService;</p>
            <br />
            <br />
            <p>    /**<br />
            <br />
            * Spring injection<br />
            <br />
            *<br />
            <br />
            * @param clientManagerService clientManagerService<br />
            <br />
            * @spring.property ref="clientManagerService"<br />
            <br />
            */<br />
            <br />
            public void setClientManagerService(ClientManager clientManagerService) {<br />
            <br />
            this.clientManagerService = clientManagerService;<br />
            <br />
            }<br />
            <br />
            [..]<br />
            <br />
            </p>
            </pre>
            <br />
            </td>
        </tr>
    </tbody>
</table>
<p>With this tags, the ant build target outlined below will create a
struts-config.xml and the necessary action-servlet.xml needed by
Spring. Lets see how the target looks:</p>
<table border="1">
    <tbody>
        <tr>
            <td>
            &lt;path id="classpath"&gt;
            <p>        &lt;fileset dir="c:/xdoclet-1.2.2" includes="*.jar"/&gt;<br />
            &lt;fileset dir="c:/j2sdkee1.3.1/lib" includes="*.jar"/&gt;<br />
            &lt;fileset dir="c:/struts/lib" includes="struts.jar"/&gt;<br />
            &lt;/path&gt;</p>
            <p>    &lt;taskdef name="springdoclet" <br />
            classname="xdoclet.modules.spring.SpringDocletTask"<br />
            classpathref="classpath"/&gt;<br />
            <br />
            &lt;taskdef name="webdoclet" <br />
            classname="xdoclet.modules.web.WebDocletTask"<br />
            classpathref="classpath"/&gt;</p>
            <p>   &lt;target name="myTest"&gt;<br />
            &lt;webdoclet destDir="c:\" force="true" verbose="true"&gt;<br />
            &lt;fileset dir="${src.dir}"&gt;<br />
            &lt;include name="**/*Form.java" /&gt;<br />
            &lt;include name="**/*Action.java" /&gt;<br />
            &lt;include name="**/*Servlet.java" /&gt;<br />
            &lt;/fileset&gt;<br />
            &lt;strutsconfigxml version="1.1" validateXml="true"/&gt;<br />
            &lt;/webdoclet&gt;</p>
            <p>        &lt;springdoclet destDir="c:\" verbose="true"&gt;<br />
            &lt;fileset dir="${src.dir}"&gt;<br />
            &lt;include name="**/*Action.java" /&gt;<br />
            &lt;/fileset&gt;<br />
            &lt;springxml destinationFile="action-servlet.xml"/&gt;<br />
            &lt;/springdoclet&gt;<br />
            &lt;/target&gt;<br />
            </p>
            </td>
        </tr>
    </tbody>
</table>
<p>Be sure to modify the classpath definition and the destDir values of
springdoclet and webdoclet to suit your needs. Right now the Spring
task only searches for Actions in order to create action-servlet.xml,
if you also want to create your normal service beans like
ClientManagerService, you should create another springdoclet task and
output things to applicationContext.xml if you like. </p>
<p>But this is mostly users choice, as you know, Spring supports from
one to many bean definition files and its up to you how you want to
have your spring xml world.</p>
<p>This will be created when you run the mentioned target:</p>
<p>(action-servlet.xml)<br />
</p>
<table border="1">
    <tbody>
        <tr>
            <td><br />
            &lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />
            &lt;!DOCTYPE beans PUBLIC<br />
            "-//SPRING//DTD BEAN//EN"<br />
            "http://www.springframework.org/dtd/spring-beans.dtd"&gt;
            <p>&lt;beans<br />
            default-autowire="no"<br />
            default-lazy-init="false"<br />
            default-dependency-check="none"<br />
            &gt;</p>
            <p>  &lt;bean<br />
            name="clientDelete"<br />
            class="de.logentis.versysng.action.clientmanager.ClientDeleteAction"<br />
            &gt;</p>
            <p>    &lt;property name="clientManagerService"&gt;</p>
            <p>      &lt;ref bean="clientManagerService"/&gt;<br />
            &lt;/property&gt;</p>
            <p>  &lt;/bean&gt;</p>
            <p>  &lt;!--<br />
            To include additional bean definitions for Spring in the generated<br />
            application context file, add a file to your XDoclet merge directory<br />
            called spring-beans.xml that contains the &lt;bean&gt;&lt;/bean&gt; markup.<br />
            --&gt;<br />
            &lt;/beans&gt;<br />
            </p>
            </td>
        </tr>
    </tbody>
</table>
<p><br />
(struts-config.xml)<br />
</p>
<br />
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
<p>&lt;!DOCTYPE struts-config PUBLIC "-//Apache Software
Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"&gt;</p>
<p>&lt;struts-config&gt;</p>
<p>  &lt;!-- ========== Data Sources Definitions =================================== --&gt;<br />
&lt;!--<br />
Define your Struts data sources in a file called struts-data-sources.xml and place<br />
it in your merge directory.<br />
--&gt;</p>
<p>  &lt;!-- ========== Form Bean Definitions =================================== --&gt;<br />
&lt;form-beans&gt;</p>
<p>    &lt;!--<br />
If you have non XDoclet forms, define them in a file called struts-forms.xml and<br />
place it in your merge directory.<br />
--&gt;<br />
&lt;/form-beans&gt;</p>
<p>  &lt;!-- ========== Global Exceptions Definitions =================================== --&gt;<br />
&lt;!--<br />
Define your exceptions in a file called global-exceptions.xml and place<br />
it in your merge directory.<br />
--&gt;</p>
<p>  &lt;!-- ========== Global Forward Definitions =================================== --&gt;<br />
&lt;!--<br />
Define your forwards in a file called global-forwards.xml and place<br />
it in your merge directory.<br />
--&gt;</p>
<p>  &lt;!-- ========== Action Mapping Definitions =================================== --&gt;<br />
&lt;action-mappings&gt;<br />
&lt;action<br />
path="clientDelete"<br />
type="org.springframework.web.struts.DelegatingActionProxy"<br />
name="emptyform"<br />
scope="request"<br />
unknown="false"<br />
validate="false"<br />
&gt;<br />
&lt;forward<br />
name="back"<br />
path="/clientManager.html"<br />
redirect="true"<br />
/&gt;<br />
&lt;/action&gt;</p>
<p> &lt;!-- If you have non XDoclet actions, define them in a file
called struts-actions.xml and place it in your merge directory. --&gt;<br />
&lt;/action-mappings&gt;</p>
<p>   &lt;!-- Define your Struts controller in a file called struts-controller.xml and place it in your merge directory. --&gt;</p>
<p> &lt;!-- Define your Struts message-resources in a file called
struts-message-resources.xml and place it in your merge directory.
--&gt;</p>
<p>   &lt;!-- Define your Struts plugins in a file called struts-plugins.xml and place it in your merge directory. --&gt;</p>
&lt;/struts-config&gt;<br />
<br />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/187406.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-20 09:42 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/20/187406.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE Web Application Events zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/18/187044.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 18 Mar 2008 09:02:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/18/187044.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/187044.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/18/187044.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/187044.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/187044.html</trackback:ping><description><![CDATA[http://www.stardeveloper.com/articles/display.html?article=2001111901&amp;page=1 <br />
<br />
<br />
<span class="heading">Overview :</span><br />
Web application events are new in Servlet 2.3 specification. They give
you greater degree of control over your web application. In this
article we will study two important application events :
<ul>
    <li>Application startup and shutdown</li>
    <li>Session creation and invalidation</li>
</ul>
<p>As their names suggest, application startup event occurs when your
web application is first loaded and started by the Servlet container
and application shutdown event occurs when the web application is
shutdown.</p>
<p>Session creation event occurs everytime a new session is created on
the server and similarly session invalidation event occurs everytime a
session is invalidated. <span class="highLight">To make use of these
web application events and to do something useful you'll have to create
and make use of special "listener" classes</span>. From here onwards, we'll look at what these listener classes are how you can use them.</p>
<p><span class="smallHeading">Listener Classes :</span><br />
These are simple Java classes which implement one of the two following interfaces :</p>
<ul type="square">
    <li>javax.servlet.ServletContextListener</li>
    <li>javax.servlet.http.HttpSessionListener</li>
</ul>
<p>If you want your class to listen for application startup and
shutdown events then implement ServletContextListener interface. If you
want your class to listen for session creation and invalidation events
then implement HttpSessionListener interface.</p>
<p>Let's see what are the different methods of these interfaces which you'll have to implement.</p>
<p><span class="smallHeading">ServletContextListener :</span><br />
This interface contains two methods :</p>
<ul>
    <li>public void contextInitialized(ServletContextEvent sce);</li>
    <li>public void contextDestroyed(ServletContextEvent sce);</li>
</ul>
<p>Once you implement any interface you have to implement all of it's
methods. So you if you want to make use of application startup and
shutdown events, then create a Java class and implement <code>ServletContextListener</code> interface. An example of such a class is as follows :</p>
<pre>/*<br />
File : ApplicationWatch.java<br />
*/<br />
<br />
import javax.servlet.ServletContextListener;<br />
import javax.servlet.ServletContextEvent;<br />
<br />
public class ApplicationWatch implements ServletContextListener	{<br />
<br />
public static long applicationInitialized =	0L;<br />
<br />
/* Application Startup Event */<br />
public void	contextInitialized(ServletContextEvent ce) {<br />
applicationInitialized = System.currentTimeMillis();<br />
}<br />
<br />
/* Application Shutdown	Event */<br />
public void	contextDestroyed(ServletContextEvent ce) {}<br />
}</pre>
<p>In the code above, a Java class; ApplicationWatch, implements
ServletContextListener interface. It implement both of it's methods but
it really uses only one of them and the second method's body remains
empty. This class notes down the time of application startup in a <code>public static</code> variable which can be called from other application classes to know what was the last time this application was started.</p>
<p>I'll explain how to tell the application server that we have this
listener class and we want to be told of these application events in a
moment, but first let's see what are the different methods of <code>HttpSessionListener</code> interface.</p>
<p><span class="smallHeading">HttpSessionListener :</span><br />
This interface also contains just two methods, for session creation and invalidation events respectively :</p>
<ul>
    <li>public void sessionCreated(HttpSessionEvent se);</li>
    <li>public void sessionDestroyed(HttpSessionEvent se);</li>
</ul>
<p>Like what we did in the case of ApplicationWatch above, we'll have
to create a Java class and implement HttpSessionListener interface. An
example of such a class is as follows :</p>
<pre>/*<br />
File : SessionCounter.java<br />
*/<br />
<br />
import javax.servlet.http.HttpSessionListener;<br />
import javax.servlet.http.HttpSessionEvent;<br />
<br />
public class SessionCounter	implements HttpSessionListener {<br />
<br />
private	static int activeSessions =	0;<br />
<br />
/* Session Creation	Event */<br />
public void	sessionCreated(HttpSessionEvent	se)	{<br />
activeSessions++;<br />
}<br />
<br />
/* Session Invalidation	Event */<br />
public void	sessionDestroyed(HttpSessionEvent se) {<br />
if(activeSessions &gt;	0)<br />
activeSessions--;<br />
}<br />
<br />
public static int getActiveSessions() {<br />
return activeSessions;<br />
}<br />
}</pre>
<p>In the code above, <code>SessionCounter</code> class implements
HttpSessionListener to count the number of active sessions. We will
learn more about counting active users in a separate article in more
detail.</p>
<p>Ok, we have learned what are web application events, what interfaces
are available to us and have also seen examples of classes implementing
those interfaces. Let's see how to tell the application server about
these listener classes.</p>
<p><span class="smallHeading">Web.xml :</span><br />
We do that by putting classpath of these classes in /WEB-INF/web.xml
file under special &lt;listener&gt; tags. An example of such a web.xml
file is given below :</p>
<pre>&lt;!-- Web.xml --&gt;<br />
&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />
<br />
&lt;!DOCTYPE web-app<br />
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br />
"http://java.sun.com/j2ee/dtds/web-app_2.3.dtd"&gt;<br />
<br />
&lt;web-app&gt;<br />
<br />
&lt;!-- Listeners --&gt;<br />
&lt;listener&gt;<br />
&lt;listener-class&gt;<br />
com.stardeveloper.web.listener.SessionCounter<br />
&lt;/listener-class&gt;<br />
&lt;/listener&gt;<br />
&lt;listener&gt;<br />
&lt;listener-class&gt;<br />
com.stardeveloper.web.listener.ApplicationWatch<br />
&lt;/listener-class&gt;<br />
&lt;/listener&gt;<br />
<br />
&lt;/web-app&gt;</pre>
<p>As shown above it is quite easy to declare listener classes in
web.xml files. Now every time the server starts or shutdown, a session
is created or destroyed, your classes will be told as their specific
event methods will be called. It is that simple!</p>
<p><img src="http://www.stardeveloper.com/images/jug.gif" height="28" width="24"  alt="" /><span class="heading">Summary :</span><br />
In this article we learned what are web application events and how we
can make use of these events by creating special "listener" classes. We
then created two classes which implemented ServletContextListener and
HttpSessionListener interfaces respectively. We also learned how to
declare these listener classes in web.xml file by using special
&lt;listener&gt; and &lt;listener-class&gt; tags.</p>
<br />
<br />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/187044.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-18 17:02 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/18/187044.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>web.xml Reference Guide for Tomcat zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/18/187022.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 18 Mar 2008 07:57:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/18/187022.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/187022.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/18/187022.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/187022.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/187022.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: http://wiki.metawerx.net/wiki/Web.xmlIntroductionThe web.xml Deployment Descriptor file describes how to deploy a web application in a servlet container such as Tomcat.This file is require...&nbsp;&nbsp;<a href='http://www.blogjava.net/jinfeng_wang/archive/2008/03/18/187022.html'>阅读全文</a><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/187022.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-18 15:57 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/18/187022.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>EasyMock 使用方法与原理剖析 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/11/185422.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 11 Mar 2008 08:16:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/11/185422.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/185422.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/11/185422.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/185422.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/185422.html</trackback:ping><description><![CDATA[http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/index.html<br />
<br />
<br />
<blockquote>EasyMock 是一套通过简单的方法对于指定的接口或类生成 Mock 对象的类库，它能利用对接口或类的模拟来辅助单元测试。本文将对
EasyMock 的功能和原理进行介绍，并通过示例来说明如何使用 EasyMock 进行单元测试。 </blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p>Mock 方法是单元测试中常见的一种技术，它的主要作用是模拟一些在应用中不容易构造或者比较复杂的对象，从而把测试与测试边界以外的对象隔离开。</p>
<p>编写自定义的 Mock 对象需要额外的编码工作，同时也可能引入错误。EasyMock 提供了根据指定接口动态构建 Mock 对象的方法，避免了手工编写
Mock 对象。本文将向您展示如何使用 EasyMock 进行单元测试，并对 EasyMock 的原理进行分析。</p>
<p><a name="N10086"><span class="atitle">1．Mock 对象与 EasyMock 简介</span></a></p>
<p><a name="N1008C"><span class="smalltitle">单元测试与 Mock 方法</span></a></p>
<p>单元测试是对应用中的某一个模块的功能进行验证。在单元测试中，我们常遇到的问题是应用中其它的协同模块尚未开发完成，或者被测试模块需要和一些不容易构造、比较复杂的对象进行交互。另外，由于不能肯定其它模块的正确性，我们也无法确定测试中发现的问题是由哪个模块引起的。</p>
<p>Mock 对象能够模拟其它协同模块的行为，被测试模块通过与 Mock 对象协作，可以获得一个孤立的测试环境。此外，使用 Mock
对象还可以模拟在应用中不容易构造（如 HttpServletRequest 必须在 Servlet 容器中才能构造出来）和比较复杂的对象（如 JDBC 中的
ResultSet 对象），从而使测试顺利进行。</p>
<p><a name="N10098"><span class="smalltitle">EasyMock 简介</span></a></p>
<p>手动的构造 Mock 对象会给开发人员带来额外的编码量，而且这些为创建 Mock 对象而编写的代码很有可能引入错误。目前，有许多开源项目对动态构建
Mock 对象提供了支持，这些项目能够根据现有的接口或类动态生成，这样不仅能避免额外的编码工作，同时也降低了引入错误的可能。</p>
<p>EasyMock 是一套用于通过简单的方法对于给定的接口生成 Mock
对象的类库。它提供对接口的模拟，能够通过录制、回放、检查三步来完成大体的测试过程，可以验证方法的调用种类、次数、顺序，可以令 Mock
对象返回指定的值或抛出指定异常。通过 EasyMock，我们可以方便的构造 Mock 对象从而使单元测试顺利进行。</p>
<p><a name="N100A4"><span class="smalltitle">安装 EasyMock</span></a></p>
<p>EasyMock 是采用 MIT license 的一个开源项目，您可以在 Sourceforge 上下载到相关的 zip 文件。目前您可以下载的
EasyMock 最新版本是2.3，它需要运行在 Java 5.0 平台上。如果您的应用运行在 Java 1.3 或 1.4 平台上，您可以选择
EasyMock1.2。在解压缩 zip 包后，您可以找到 easymock.jar 这个文件。如果您使用 Eclipse 作为 IDE，把
easymock.jar 添加到项目的 Libraries 里就可以使用了（如下图所示）。此外，由于我们的测试用例运行在 JUnit 环境中，因此您还需要
JUnit.jar（版本3.8.1以上）。</p>
<br />
<a name="classpath.gif"><strong>图1：Eclipse 项目中的
Libraries</strong></a><br />
<img alt="Eclipse 项目中的 Libraries" src="classpath.gif" height="493" width="572" /> <br />
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%" /><br />
            <img alt="" src="//www.ibm.com/i/c.gif" border="0" height="6" width="8" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img alt="" src="//www.ibm.com/i/c.gif" height="4" width="100%" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img alt="" src="//www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16" /><br />
                        </td>
                        <td align="right" valign="top"><a class="fbox" href="#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N100BC"><span class="atitle">2．使用 EasyMock 进行单元测试</span></a></p>
<p>通过 EasyMock，我们可以为指定的接口动态的创建 Mock 对象，并利用 Mock
对象来模拟协同模块或是领域对象，从而使单元测试顺利进行。这个过程大致可以划分为以下几个步骤：</p>
<ul>
    <li>使用 EasyMock 生成 Mock 对象；
    </li>
    <li>设定 Mock 对象的预期行为和输出；
    </li>
    <li>将 Mock 对象切换到 Replay 状态；
    </li>
    <li>调用 Mock 对象方法进行单元测试；
    </li>
    <li>对 Mock 对象的行为进行验证。 </li>
</ul>
<p>接下来，我们将对以上的几个步骤逐一进行说明。除了以上的基本步骤外，EasyMock 还对特殊的 Mock
对象类型、特定的参数匹配方式等功能提供了支持，我们将在之后的章节中进行说明。</p>
<p><a name="N100DA"><span class="smalltitle">使用 EasyMock 生成 Mock 对象</span></a></p>
<p>根据指定的接口或类，EasyMock 能够动态的创建 Mock 对象（EasyMock 默认只支持为接口生成 Mock 对象，如果需要为类生成 Mock
对象，在 EasyMock 的主页上有扩展包可以实现此功能），我们以 <code>ResultSet</code>
接口为例说明EasyMock的功能。<code>java.sql.ResultSet</code> 是每一个 Java
开发人员都非常熟悉的接口：</p>
<br />
<a name="code001"><strong>清单1：ResultSet 接口</strong></a><br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public interface java.sql.ResultSet {<br />
            ......<br />
            public abstract java.lang.String getString(int arg0) throws java.sql.SQLException;<br />
            public abstract double getDouble(int arg0) throws java.sql.SQLException;<br />
            ......<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>通常，构建一个真实的 <code>RecordSet</code> 对象需要经过一个复杂的过程：在开发过程中，开发人员通常会编写一个
<code>DBUtility</code> 类来获取数据库连接 <code>Connection</code>，并利用
<code>Connection</code> 创建一个 <code>Statement</code>。执行一个 <code>Statement</code>
可以获取到一个或多个 <code>ResultSet</code>
对象。这样的构造过程复杂并且依赖于数据库的正确运行。数据库或是数据库交互模块出现问题，都会影响单元测试的结果。</p>
<p>我们可以使用 EasyMock 动态构建 <code>ResultSet</code> 接口的 Mock 对象来解决这个问题。一些简单的测试用例只需要一个
Mock 对象，这时，我们可以用以下的方法来创建 Mock 对象：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">ResultSet mockResultSet = createMock(ResultSet.class);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>其中 <code>createMock</code> 是 <code>org.easymock.EasyMock</code>
类所提供的静态方法，你可以通过 static import 将其引入（注：static import 是 java 5.0 所提供的新特性）。</p>
<p>如果需要在相对复杂的测试用例中使用多个 Mock 对象，EasyMock 提供了另外一种生成和管理 Mock 对象的机制：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">IMocksControl control = EasyMock.createControl();<br />
            java.sql.Connection mockConnection = control.createMock(Connection.class);<br />
            java.sql.Statement mockStatement = control.createMock(Statement.class);<br />
            java.sql.ResultSet mockResultSet = control.createMock(ResultSet.class);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p><code>EasyMock</code> 类的 <code>createControl</code> 方法能创建一个接口
<code>IMocksControl</code> 的对象，该对象能创建并管理多个 Mock 对象。如果需要在测试中使用多个 Mock
对象，我们推荐您使用这一机制，因为它在多个 Mock 对象的管理上提供了相对便捷的方法。</p>
<p>如果您要模拟的是一个具体类而非接口，那么您需要下载扩展包 EasyMock Class Extension 2.2.2。在对具体类进行模拟时，您只要用
<code>org.easymock.classextension.EasyMock</code> 类中的静态方法代替
<code>org.easymock.EasyMock</code> 类中的静态方法即可。</p>
<p><a name="N1014D"><span class="smalltitle">设定 Mock 对象的预期行为和输出</span></a></p>
<p>在一个完整的测试过程中，一个 Mock 对象将会经历两个状态：Record 状态和 Replay 状态。Mock 对象一经创建，它的状态就被置为
Record。在 Record 状态，用户可以设定 Mock 对象的预期行为和输出，这些对象行为被录制下来，保存在 Mock 对象中。</p>
<p>添加 Mock 对象行为的过程通常可以分为以下3步：
</p>
<ul>
    <li>对 Mock 对象的特定方法作出调用；
    </li>
    <li>通过 <code>org.easymock.EasyMock</code> 提供的静态方法 <code>expectLastCall</code>
    获取上一次方法调用所对应的 IExpectationSetters 实例；
    </li>
    <li>通过 <code>IExpectationSetters</code> 实例设定 Mock 对象的预期输出。 </li>
</ul>
<p><strong>设定预期返回值</strong> </p>
<p>Mock 对象的行为可以简单的理解为 Mock 对象方法的调用和方法调用所产生的输出。在 EasyMock 2.3 中，对 Mock
对象行为的添加和设置是通过接口 <code>IExpectationSetters</code> 来实现的。Mock
对象方法的调用可能产生两种类型的输出：（1）产生返回值；（2）抛出异常。接口 <code>IExpectationSetters</code>
提供了多种设定预期输出的方法，其中和设定返回值相对应的是 andReturn 方法：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">IExpectationSetters&lt;T&gt; andReturn(T value);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>我们仍然用 <code>ResultSet</code> 接口的 Mock 对象为例，如果希望方法
<code>mockResult.getString(1)</code> 的返回值为 "My return value"，那么你可以使用以下的语句：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">mockResultSet.getString(1);<br />
            expectLastCall().andReturn("My return value");<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>以上的语句表示 <code>mockResultSet</code> 的 <code>getString</code> 方法被调用一次，这次调用的返回值是
"My return value"。有时，我们希望某个方法的调用总是返回一个相同的值，为了避免每次调用都为 Mock
对象的行为进行一次设定，我们可以用设置默认返回值的方法：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">void andStubReturn(Object value);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>假设我们创建了 <code>Statement</code> 和 <code>ResultSet</code> 接口的 Mock 对象
mockStatement 和 mockResultSet，在测试过程中，我们希望 mockStatement 对象的
<code>executeQuery</code> 方法总是返回 mockResultSet，我们可以使用如下的语句
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">mockStatement.executeQuery("SELECT * FROM sales_order_table");<br />
            expectLastCall().andStubReturn(mockResultSet);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>EasyMock 在对参数值进行匹配时，默认采用 <code>Object.equals()</code> 方法。因此，如果我们以
<code>"select * from sales_order_table"</code> 作为参数，预期方法将不会被调用。如果您希望上例中的 SQL
语句能不区分大小写，可以用特殊的参数匹配器来解决这个问题，我们将在 "在 EasyMock 中使用参数匹配器" 一章对此进行说明。</p>
<p><strong>设定预期异常抛出</strong> </p>
<p>对象行为的预期输出除了可能是返回值外，还有可能是抛出异常。<code>IExpectationSetters</code> 提供了设定预期抛出异常的方法：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">IExpectationSetters&lt;T&gt; andThrow(Throwable throwable);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>和设定默认返回值类似，<code>IExpectationSetters</code> 接口也提供了设定抛出默认异常的函数：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">void andStubThrow(Throwable throwable);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p><strong>设定预期方法调用次数</strong> </p>
<p>通过以上的函数，您可以对 Mock
对象特定行为的预期输出进行设定。除了对预期输出进行设定，<code>IExpectationSetters</code>
接口还允许用户对方法的调用次数作出限制。在 <code>IExpectationSetters</code> 所提供的这一类方法中，常用的一种是
<code>times</code> 方法：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">IExpectationSetters&lt;T&gt;times(int count);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>该方法可以 Mock 对象方法的调用次数进行确切的设定。假设我们希望 mockResultSet 的 <code>getString</code>
方法在测试过程中被调用3次，期间的返回值都是 "My return value"，我们可以用如下语句：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">mockResultSet.getString(1);<br />
            expectLastCall().andReturn("My return value").times(3);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
注意到 <code>andReturn</code> 和
<code>andThrow</code> 方法的返回值依然是一个 <code>IExpectationSetters</code>
实例，因此我们可以在此基础上继续调用 <code>times</code> 方法。</p>
<p>除了设定确定的调用次数，<code>IExpectationSetters</code>
还提供了另外几种设定非准确调用次数的方法：<br />
<code>times(int minTimes, int maxTimes)</code>：该方法最少被调用
minTimes 次，最多被调用 maxTimes
次。<br />
<code>atLeastOnce()</code>：该方法至少被调用一次。<br />
<code>anyTimes()</code>：该方法可以被调用任意次。
</p>
<p>某些方法的返回值类型是 void，对于这一类方法，我们无需设定返回值，只要设置调用次数就可以了。以 <code>ResultSet</code> 接口的
<code>close</code> 方法为例，假设在测试过程中，该方法被调用3至5次：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">mockResultSet.close();<br />
            expectLastCall().times(3, 5);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>为了简化书写，EasyMock 还提供了另一种设定 Mock 对象行为的语句模式。对于上例，您还可以将它写成：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">expect(mockResult.close()).times(3, 5);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
这个语句和上例中的语句功能是完全相同的。 </p>
<p><a name="chap2.3"><span class="smalltitle">将 Mock 对象切换到 Replay 状态</span></a></p>
<p>在生成 Mock 对象和设定 Mock 对象行为两个阶段，Mock 对象的状态都是 Record 。在这个阶段，Mock
对象会记录用户对预期行为和输出的设定。</p>
<p>在使用 Mock 对象进行实际的测试前，我们需要将 Mock 对象的状态切换为 Replay。在 Replay 状态，Mock
对象能够根据设定对特定的方法调用作出预期的响应。将 Mock 对象切换成 Replay 状态有两种方式，您需要根据 Mock 对象的生成方式进行选择。如果
Mock 对象是通过 <code>org.easymock.EasyMock</code> 类提供的静态方法 createMock 生成的（第1节中介绍的第一种
Mock 对象生成方法），那么 <code>EasyMock</code> 类提供了相应的 replay 方法用于将 Mock 对象切换为 Replay 状态：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">replay(mockResultSet);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>如果 Mock 对象是通过 <code>IMocksControl</code> 接口提供的 <code>createMock</code>
方法生成的（第1节中介绍的第二种Mock对象生成方法），那么您依旧可以通过 <code>IMocksControl</code> 接口对它所创建的所有 Mock
对象进行切换：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">control.replay();<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>以上的语句能将在第1节中生成的 mockConnection、mockStatement 和 mockResultSet 等3个 Mock 对象都切换成
Replay 状态。</p>
<p><a name="chap2.4"><span class="smalltitle">调用 Mock 对象方法进行单元测试</span></a></p>
<p>为了更好的说明 EasyMock 的功能，我们引入 src.zip 中的示例来解释 Mock 对象在实际测试阶段的作用。其中所有的示例代码都可以在
src.zip 中找到。如果您使用的 IDE 是 Eclipse，在导入 src.zip 之后您可以看到 Workspace 中增加的
project（如下图所示）。</p>
<br />
<a name="classpath.gif"><strong>图2：导入 src.zip 后的
Workspace</strong></a><br />
<img alt="导入src.zip后的Workspace" src="workspace.gif" height="216" width="281" /> <br />
<p>下面是示例代码中的一个接口 <code>SalesOrder</code>，它的实现类 <code>SalesOrderImpl</code>
的主要功能是从数据库中读取一个 Sales Order 的 Region 和 Total Price，并根据读取的数据计算该 Sales Order 的
Price Level（完整的实现代码都可以在 src.zip 中找到）：</p>
<br />
<a name="code001"><strong>清单2：SalesOrder
接口</strong></a><br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public interface SalesOrder<br />
            {<br />
            &#8230;&#8230;<br />
            public void loadDataFromDB(ResultSet resultSet) throws SQLException;	<br />
            public String getPriceLevel();<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>其实现类 <code>SalesOrderImpl</code> 中对 <code>loadDataFromDB</code>
的实现如下：</p>
<br />
<a name="code001"><strong>清单3：SalesOrderImpl 实现</strong></a><br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public class SalesOrderImpl implements SalesOrder<br />
            {<br />
            ......<br />
            public void loadDataFromDB(ResultSet resultSet) throws SQLException<br />
            {<br />
            orderNumber = resultSet.getString(1);<br />
            region = resultSet.getString(2);<br />
            totalPrice = resultSet.getDouble(3);<br />
            }<br />
            ......<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>方法 <code>loadDataFromDB</code> 读取了 <code>ResultSet</code> 对象包含的数据。当我们将之前定义的
Mock 对象调整为 Replay 状态，并将该对象作为参数传入，那么 Mock 对象的方法将会返回预先定义的预期返回值。完整的 TestCase
如下：</p>
<br />
<a name="code001"><strong>清单4：完整的TestCase</strong></a><br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public class SalesOrderTestCase extends TestCase {<br />
            public void testSalesOrder() {<br />
            IMocksControl control = EasyMock.createControl();<br />
            ......<br />
            ResultSet mockResultSet = control.createMock(ResultSet.class);<br />
            try {<br />
            ......<br />
            mockResultSet.next();<br />
            expectLastCall().andReturn(true).times(3);<br />
            expectLastCall().andReturn(false).times(1);<br />
            mockResultSet.getString(1);<br />
            expectLastCall().andReturn("DEMO_ORDER_001").times(1);<br />
            expectLastCall().andReturn("DEMO_ORDER_002").times(1);<br />
            expectLastCall().andReturn("DEMO_ORDER_003").times(1);<br />
            mockResultSet.getString(2);<br />
            expectLastCall().andReturn("Asia Pacific").times(1);<br />
            expectLastCall().andReturn("Europe").times(1);<br />
            expectLastCall().andReturn("America").times(1);<br />
            mockResultSet.getDouble(3);<br />
            expectLastCall().andReturn(350.0).times(1);<br />
            expectLastCall().andReturn(1350.0).times(1);<br />
            expectLastCall().andReturn(5350.0).times(1);<br />
            control.replay();<br />
            ......<br />
            int i = 0;<br />
            String[] priceLevels = { "Level_A", "Level_C", "Level_E" };<br />
            while (mockResultSet.next()) {<br />
            SalesOrder order = new SalesOrderImpl();<br />
            order.loadDataFromDB(mockResultSet);<br />
            assertEquals(order.getPriceLevel(), priceLevels[i]);<br />
            i++;<br />
            }<br />
            control.verify();<br />
            } catch (Exception e) {<br />
            e.printStackTrace();<br />
            }<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在这个示例中，我们首先创建了 <code>ResultSet</code> 的 Mock 对象 moResultSet，并记录该 Mock
对象的预期行为。之后我们调用了 <code>control.replay()</code>，将 Mock 对象的状态置为 Replay
状态。在实际的测试阶段，Sales Order 对象的 <code>loadDataFromDB</code> 方法调用了 mockResultSet 对象的
<code>getString</code> 和 <code>getDouble</code> 方法读取 mockResultSet 中的数据。Sales
Order 对象根据读取的数据计算出 Price Level，并和预期输出进行比较。 </p>
<p><a name="chap2.5"><span class="smalltitle">对 Mock 对象的行为进行验证</span></a></p>
<p>在利用 Mock 对象进行实际的测试过程之后，我们还有一件事情没有做：对 Mock 对象的方法调用的次数进行验证。</p>
<p>为了验证指定的方法调用真的完成了，我们需要调用 <code>verify</code> 方法进行验证。和 <code>replay</code>
方法类似，您需要根据 Mock 对象的生成方式来选用不同的验证方式。如果 Mock 对象是由
<code>org.easymock.EasyMock</code> 类提供的 <code>createMock</code> 静态方法生成的，那么我们同样采用
<code>EasyMock</code> 类的静态方法 <code>verify</code> 进行验证：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">verify(mockResultSet);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>如果Mock对象是有 <code>IMocksControl</code> 接口所提供的 <code>createMock</code>
方法生成的，那么采用该接口提供的 <code>verify</code> 方法，例如第1节中的 <code>IMocksControl</code> 实例
control：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">control.verify();<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>将对 control 实例所生成的 Mock 对象 mockConnection、mockStatement 和 mockResultSet
等进行验证。如果将上例中 <code>expectLastCall().andReturn(false).times(1)</code> 的预期次数修改为2，在
Eclipse 中将可以看到：</p>
<br />
<a name="asserterror.gif"><strong>图3：Mock对象验证失败</strong></a><br />
<img alt="Mock对象验证失败" src="asserterror.gif" height="286" width="557" /> <br />
<p><a name="chap2.6"><span class="smalltitle">Mock 对象的重用</span></a></p>
<p>为了避免生成过多的 Mock 对象，EasyMock 允许对原有 Mock 对象进行重用。要对 Mock 对象重新初始化，我们可以采用 reset
方法。和 replay 和 verify 方法类似，EasyMock 提供了两种 reset 方式：（1）如果 Mock 对象是由
<code>org.easymock.EasyMock</code> 类中的静态方法 <code>createMock</code> 生成的，那么该 Mock
对象的可以用 <code>EasyMock</code> 类的静态方法 <code>reset</code> 重新初始化；（2）如果 Mock 方法是由
<code>IMocksControl</code> 实例的 <code>createMock</code> 方法生成的，那么该
<code>IMocksControl</code> 实例方法 <code>reset</code> 的调用将会把所有该实例创建的 Mock
对象重新初始化。</p>
<p>在重新初始化之后，Mock 对象的状态将被置为 Record 状态。</p>
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%" /><br />
            <img alt="" src="//www.ibm.com/i/c.gif" border="0" height="6" width="8" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img alt="" src="//www.ibm.com/i/c.gif" height="4" width="100%" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img alt="" src="//www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16" /><br />
                        </td>
                        <td align="right" valign="top"><a class="fbox" href="#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="chap3"><span class="atitle">3．在 EasyMock 中使用参数匹配器</span></a></p>
<p><a name="chap3.1"><span class="smalltitle">EasyMock 预定义的参数匹配器</span></a></p>
<p>在使用 Mock 对象进行实际的测试过程中，EasyMock 会根据方法名和参数来匹配一个预期方法的调用。EasyMock 对参数的匹配默认使用
<code>equals()</code> 方法进行比较。这可能会引起一些问题。例如在上一章节中创建的mockStatement对象：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">mockStatement.executeQuery("SELECT * FROM sales_order_table");<br />
            expectLastCall().andStubReturn(mockResultSet);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>在实际的调用中，我们可能会遇到 SQL 语句中某些关键字大小写的问题，例如将 SELECT 写成 Select，这时在实际的测试中，EasyMock
所采用的默认匹配器将认为这两个参数不匹配，从而造成 Mock 对象的预期方法不被调用。EasyMock 提供了灵活的参数匹配方式来解决这个问题。如果您对
mockStatement 具体执行的语句并不关注，并希望所有输入的字符串都能匹配这一方法调用，您可以用
<code>org.easymock.EasyMock</code> 类所提供的 <code>anyObject</code> 方法来代替参数中的 SQL
语句：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">mockStatement.executeQuery( anyObject() );<br />
            expectLastCall().andStubReturn(mockResultSet);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p><code>anyObject</code> 方法表示任意输入值都与预期值相匹配。除了 <code>anyObject</code>
以外，EasyMock还提供了多个预先定义的参数匹配器，其中比较常用的一些有：</p>
<ul>
    <li><code>aryEq(X value)</code>：通过<code>Arrays.equals()</code>进行匹配，适用于数组对象；
    </li>
    <li><code>isNull()</code>：当输入值为Null时匹配；
    </li>
    <li><code>notNull()</code>：当输入值不为Null时匹配；
    </li>
    <li><code>same(X value)</code>：当输入值和预期值是同一个对象时匹配；
    </li>
    <li><code>lt(X value), leq(X value), geq(X value), gt(X
    value)</code>：当输入值小于、小等于、大等于、大于预期值时匹配，适用于数值类型；
    </li>
    <li><code>startsWith(String prefix), contains(String substring), endsWith(String
    suffix)</code>：当输入值以预期值开头、包含预期值、以预期值结尾时匹配，适用于String类型；
    </li>
    <li><code>matches(String regex)</code>：当输入值与正则表达式匹配时匹配，适用于String类型。 </li>
</ul>
<p><a name="chap3.2"><span class="smalltitle">自定义参数匹配器</span></a></p>
<p>预定义的参数匹配器可能无法满足一些复杂的情况，这时你需要定义自己的参数匹配器。在上一节中，我们希望能有一个匹配器对 SQL 中关键字的大小写不敏感，使用
<code>anyObject</code> 其实并不是一个好的选择。对此，我们可以定义自己的参数匹配器 SQLEquals。</p>
<p>要定义新的参数匹配器，需要实现 <code>org.easymock.IArgumentMatcher</code>
接口。其中，<code>matches(Object actual)</code> 方法应当实现输入值和预期值的匹配逻辑，而在
<code>appendTo(StringBuffer buffer)</code> 方法中，你可以添加当匹配失败时需要显示的信息。以下是 SQLEquals
实现的部分代码（完整的代码可以在 src.zip 中找到）：</p>
<br />
<a name="code001"><strong>清单5：自定义参数匹配器SQLEquals</strong></a><br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public class SQLEquals implements IArgumentMatcher {<br />
            private String expectedSQL = null;<br />
            public SQLEquals(String expectedSQL) {<br />
            this.expectedSQL = expectedSQL;<br />
            }<br />
            ......<br />
            public boolean matches(Object actualSQL) {<br />
            if (actualSQL == null &amp;&amp; expectedSQL == null)<br />
            return true;<br />
            else if (actualSQL instanceof String)<br />
            return expectedSQL.equalsIgnoreCase((String) actualSQL);<br />
            else<br />
            return false;<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在实现了 <code>IArgumentMatcher</code> 接口之后，我们需要写一个静态方法将它包装一下。这个静态方法的实现需要将
SQLEquals 的一个对象通过 <code>reportMatcher</code> 方法报告给EasyMock：</p>
<br />
<a name="code001"><strong>清单6：自定义参数匹配器 SQLEquals 静态方法</strong></a><br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public static String sqlEquals(String in) {<br />
            reportMatcher(new SQLEquals(in));<br />
            return in;<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>这样，我们自定义的 sqlEquals 匹配器就可以使用了。我们可以将上例中的 <code>executeQuery</code> 方法设定修改如下：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">mockStatement.executeQuery(sqlEquals("SELECT * FROM sales_order_table"));<br />
            expectLastCall().andStubReturn(mockResultSet);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
在使用 <code>executeQuery("select * from
sales_order_table")</code> 进行方法调用时，该预期行为将被匹配。 </p>
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%" /><br />
            <img alt="" src="//www.ibm.com/i/c.gif" border="0" height="6" width="8" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img alt="" src="//www.ibm.com/i/c.gif" height="4" width="100%" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img alt="" src="//www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16" /><br />
                        </td>
                        <td align="right" valign="top"><a class="fbox" href="#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="chap4"><span class="atitle">4．特殊的 Mock 对象类型</span></a></p>
<p>到目前为止，我们所创建的 Mock 对象都属于 EasyMock 默认的 Mock 对象类型，它对预期方法的调用次序不敏感，对非预期的方法调用抛出
AssertionError。除了这种默认的 Mock 类型以外，EasyMock 还提供了一些特殊的 Mock 类型用于支持不同的需求。</p>
<p><a name="chap4.1"><span class="smalltitle">Strick Mock 对象</span></a></p>
<p>如果 Mock 对象是通过 <code>EasyMock.createMock()</code> 或是
<code>IMocksControl.createMock()</code> 所创建的，那么在进行 verify
验证时，方法的调用顺序是不进行检查的。如果要创建方法调用的先后次序敏感的 Mock 对象（Strick Mock），应该使用
<code>EasyMock.createStrickMock()</code> 来创建，例如：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">ResultSet strickMockResultSet = createStrickMock(ResultSet.class);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>类似于 createMock，我们同样可以用 <code>IMocksControl</code> 实例来创建一个 Strick Mock 对象：
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">IMocksControl control = EasyMock.createStrictControl();<br />
            ResultSet strickMockResultSet = control.createMock(ResultSet.class);<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p><a name="chap4.2"><span class="smalltitle">Nice Mock 对象</span></a></p>
<p>使用 <code>createMock()</code> 创建的 Mock 对象对非预期的方法调用默认的行为是抛出
AssertionError，如果需要一个默认返回0，null 或 false 等"无效值"的 "Nice Mock" 对象，可以通过
<code>EasyMock</code> 类提供的 <code>createNiceMock()</code> 方法创建。类似的，你也可以用
<code>IMocksControl</code> 实例来创建一个 Nice Mock 对象。</p>
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%" /><br />
            <img alt="" src="//www.ibm.com/i/c.gif" border="0" height="6" width="8" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img alt="" src="//www.ibm.com/i/c.gif" height="4" width="100%" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img alt="" src="//www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16" /><br />
                        </td>
                        <td align="right" valign="top"><a class="fbox" href="#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="chap5"><span class="atitle">5．EasyMock 的工作原理</span></a></p>
<p>EasyMock 是如何为一个特定的接口动态创建 Mock 对象，并记录 Mock 对象预期行为的呢？其实，EasyMock 后台处理的主要原理是利用
<code>java.lang.reflect.Proxy</code> 为指定的接口创建一个动态代理，这个动态代理，就是我们在编码中用到的 Mock
对象。EasyMock 还为这个动态代理提供了一个 <code>InvocationHandler</code>
接口的实现，这个实现类的主要功能就是将动态代理的预期行为记录在某个映射表中和在实际调用时从这个映射表中取出预期输出。下图是 EasyMock
中主要的功能类：</p>
<br />
<a name="easymock_class.gif"><strong>图4：EasyMock主要功能类</strong></a><br />
<img alt="Mock对象验证失败" src="easymock_class.gif" height="377" width="572" /> <br />
<p>和开发人员联系最紧密的是 <code>EasyMock</code> 类，这个类提供了
<code>createMock、replay、verify</code> 等方法以及所有预定义的参数匹配器。</p>
<p>我们知道 Mock 对象有两种创建方式：一种是通过 <code>EasyMock</code> 类提供的 <code>createMock</code>
方法创建，另一种是通过 <code>EasyMock</code> 类的 <code>createControl</code> 方法得到一个
<code>IMocksControl</code> 实例，再由这个 <code>IMocksControl</code> 实例创建 Mock
对象。其实，无论通过哪种方法获得 Mock 对象，EasyMock 都会生成一个 <code>IMocksControl</code>
的实例，只不过第一种方式中的 <code>IMocksControl</code> 的实例对开发人员不可见而已。这个
<code>IMocksControl</code> 的实例，其实就是 <code>MocksControl</code>
类的一个对象。<code>MocksControl</code> 类提供了
<code>andReturn、andThrow、times、createMock</code> 等方法。</p>
<p><code>MocksControl</code> 类中包含了两个重要的成员变量，分别是接口 <code>IMocksBehavior</code> 和
<code>IMocksControlState</code> 的实例。其中，<code>IMocksBehavior</code> 的实现类
<code>MocksBehavior</code> 是 EasyMock 的核心类，它保存着一个
<code>ExpectedInvocationAndResult</code> 对象的一个列表，而
<code>ExpectedInvocationAndResult</code> 对象中包含着 Mock
对象方法调用和预期结果的映射。<code>MocksBehavior</code> 类提供了 <code>addExpected</code> 和
<code>addActual</code> 方法用于添加预期行为和实际调用。</p>
<p><code>MocksControl</code> 类中包含的另一个成员变量是 <code>IMocksControlState</code>
实例。<code>IMocksControlState</code> 拥有两个不同的实现类：<code>RecordState</code> 和
<code>ReplayState</code>。顾名思义，<code>RecordState</code> 是 Mock 对象在 Record
状态时的支持类，它提供了 <code>invoke</code> 方法在 Record 状态下的实现。此外，它还提供了
<code>andReturn、andThrow、times</code> 等方法的实现。<code>ReplayState</code> 是 Mock 对象在
Replay 状态下的支持类，它提供了 <code>invoke</code> 方法在 Replay 状态下的实现。在 ReplayState
中，<code>andReturn、andThrow、times</code> 等方法的实现都是抛出IllegalStateException，因为在
Replay 阶段，开发人员不应该再调用这些方法。</p>
<p>当我们调用 <code>MocksControl</code> 的 <code>createMock</code> 方法时，该方法首先会生成一个
<code>JavaProxyFactory</code> 类的对象。<code>JavaProxyFactory</code> 是接口
<code>IProxyFactory</code> 的实现类，它的主要功能就是通过 <code>java.lang.reflect.Proxy</code>
对指定的接口创建动态代理实例，也就是开发人员在外部看到的 Mock 对象。</p>
<p>在创建动态代理的同时，应当提供 <code>InvocationHandler</code>
的实现类。<code>MockInvocationHandler</code> 实现了这个接口，它的 <code>invoke</code>
方法主要的功能是根据 Mock 对象状态的不同而分别调用 <code>RecordState</code> 的 <code>invoke</code> 实现或是
<code>ReplayState</code> 的 <code>invoke</code> 实现。</p>
<p><a name="chap5.1"><span class="smalltitle">创建 Mock 对象</span></a></p>
<p>下图是创建 Mock 对象的时序图：</p>
<br />
<a name="createmock_seq.gif"><strong>图5：创建 Mock
对象时序图</strong></a><br />
<img alt="创建 Mock 对象时序图" src="createmock_seq.gif" height="234" width="572" /> <br />
<p>当 <code>EasyMock</code> 类的 <code>createMock</code> 方法被调用时，它首先创建一个
<code>MocksControl</code> 对象，并调用该对象的 <code>createMock</code> 方法创建一个
<code>JavaProxyFactory</code> 对象和一个 <code>MockInvocationHandler</code>
对象。<code>JavaProxyFactory</code> 对象将 <code>MockInvocationHandler</code>
对象作为参数，通过 <code>java.lang.reflect.Proxy</code> 类的 <code>newProxyInstance</code>
静态方法创建一个动态代理。</p>
<p><a name="chap5.2"><span class="smalltitle">记录 Mock 对象预期行为</span></a></p>
<p>记录 Mock 的预期行为可以分为两个阶段：预期方法的调用和预期输出的设定。在外部程序中获得的 Mock 对象，其实就是由
<code>JavaProxyFactory</code> 创建的指定接口的动态代理，所有外部程序对接口方法的调用，都会指向
<code>InvocationHandler</code> 实现类的 <code>invoke</code> 方法。在 EasyMock 中，这个实现类是
<code>MockInvocationHandler</code>。下图是调用预期方法的时序图：</p>
<br />
<a name="recordstate_seq1.gif"><strong>图6：调用预期方法时序图</strong></a><br />
<img alt="调用预期方法时序图" src="recordstate_seq1.gif" height="261" width="572" /> <br />
<p>当 <code>MockInvocationHandler</code> 的 <code>invoke</code> 方法被调用时，它首先通过
<code>reportLastControl</code> 静态方法将 Mock 对象对应的 <code>MocksControl</code> 对象报告给
<code>LastControl</code> 类，<code>LastControl</code> 类将该对象保存在一个 ThreadLocal
变量中。接着，<code>MockInvocationHandler</code> 将创建一个 Invocation 对象，这个对象将保存预期调用的 Mock
对象、方法和预期参数。</p>
<p>在记录 Mock 对象预期行为时，Mock 对象的状态是 Record 状态，因此 <code>RecordState</code> 对象的
<code>invoke</code> 方法将被调用。这个方法首先调用 <code>LastControl</code> 的
<code>pullMatchers</code> 方法获取参数匹配器。如果您还记得自定义参数匹配器的过程，应该能想起参数匹配器被调用时会将实现类的实例报告给
EasyMock，而这个实例最终保存在 <code>LastControl</code> 中。如果没有指定参数匹配器，默认的匹配器将会返回给
<code>RecordState</code>。</p>
<p>根据 <code>Invocation</code> 对象和参数匹配器，<code>RecordState</code> 将创建一个
<code>ExpectedInvocation</code> 对象并保存下来。</p>
<p>在对预期方法进行调用之后，我们可以对该方法的预期输出进行设定。我们以
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">expectLastCall().andReturn(X value).times(int times)<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
为例说明。如果 <code>times</code>
方法未被显式的调用，EasyMock 会默认作为 <code>times(1)</code> 处理。下图是设定预期输出的时序图： </p>
<br />
<a name="recordstate_seq2.gif"><strong>图7：设定预期输出时序图</strong></a><br />
<img alt="设定预期输出时序图" src="recordstate_seq2.gif" height="340" width="572" /> <br />
<p>在预期方法被调用时，Mock 对象对应的 <code>MocksControl</code> 对象引用已经记录在
<code>LastControl</code> 中，<code>expectLastCall</code> 方法通过调用
<code>LastControl</code> 的 <code>lastControl</code>
方法可以获得这个引用。<code>MocksControl</code> 对象的 <code>andReturn</code> 方法在 Mock 对象
Record 状态下会调用 <code>RecordState</code> 的 <code>andReturn</code> 方法，将设定的预期输出以
<code>Result</code> 对象的形式记录下来，保存在 <code>RecordState</code> 的 lastResult 变量中。</p>
<p>当 <code>MocksControl</code> 的 <code>times</code> 方法被调用时，它会检查
<code>RecordState</code> 的 lastResult 变量是否为空。如果不为空，则将 lastResult 和预期方法被调用时创建的
<code>ExpectedInvocation</code> 对象一起，作为参数传递给 <code>MocksBehavior</code> 的
<code>addExpected</code> 方法。<code>MocksBehavior</code> 的
<code>addExpected</code> 方法将这些信息保存在数据列表中。</p>
<p><a name="chap5.3"><span class="smalltitle">在 Replay 状态下调用 Mock
对象方法</span></a></p>
<p><code>EasyMock</code> 类的 <code>replay</code> 方法可以将 Mock 对象切换到 Replay 状态。在
Replay 状态下，Mock 对象将根据之前的设定返回预期输出。下图是 Replay 状态下 Mock 对象方法调用的时序图：</p>
<br />
<a name="replaystate_seq.gif"><strong>图8：调用 Mock 对象方法时序图</strong></a><br />
<img alt="调用 Mock 对象方法时序图" src="replaystate_seq.gif" height="244" width="572" /> <br />
<p>在 Replay 状态下，<code>MockInvocationHandler</code> 会调用 <code>ReplayState</code>
的 <code>invoke</code> 方法。该方法会把 Mock 对象通过 <code>MocksBehavior</code> 的
<code>addActual</code> 方法添加到实际调用列表中，该列表在 <code>verify</code>
方法被调用时将被用到。同时，<code>addActual</code> 方法会根据实际方法调用与预期方法调用进行匹配，返回对应的
<code>Result</code> 对象。调用 <code>Result</code> 对象的 <code>answer</code>
方法就可以获取该方法调用的输出。</p>
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%" /><br />
            <img alt="" src="//www.ibm.com/i/c.gif" border="0" height="6" width="8" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img alt="" src="//www.ibm.com/i/c.gif" height="4" width="100%" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img alt="" src="//www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16" /><br />
                        </td>
                        <td align="right" valign="top"><a class="fbox" href="#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="chap6"><span class="atitle">6．使用 EasyMock 进行单元测试小结</span></a></p>
<p>如果您需要在单元测试中构建 Mock 对象来模拟协同模块或一些复杂对象，EasyMock 是一个可以选用的优秀框架。EasyMock 提供了简便的方法创建
Mock 对象：通过定义 Mock 对象的预期行为和输出，你可以设定该 Mock
对象在实际测试中被调用方法的返回值、异常抛出和被调用次数。通过创建一个可以替代现有对象的 Mock 对象，EasyMock 使得开发人员在测试时无需编写自定义的
Mock 对象，从而避免了额外的编码工作和因此引入错误的机会。</p>
<br />
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%" /><br />
            <img alt="" src="//www.ibm.com/i/c.gif" border="0" height="6" width="8" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img alt="" src="//www.ibm.com/i/c.gif" height="4" width="100%" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img alt="" src="//www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16" /><br />
                        </td>
                        <td align="right" valign="top"><a class="fbox" href="#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><span class="atitle"><a name="download">下载</a></span></p>
<table class="data-table-1" border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <th scope="col">描述</th>
            <th scope="col">名字</th>
            <th scope="col">大小</th>
            <th scope="col">下载方法</th>
        </tr>
        <tr>
            <th class="tb-row" scope="row">本文用到的示例代码</th>
            <td nowrap="nowrap">src.zip</td>
            <td nowrap="nowrap">176KB</td>
            <td nowrap="nowrap"><a class="fbox" href="src.zip" cmimpressionsent="1"><strong>HTTP</strong></a></td>
        </tr>
    </tbody>
</table>
<table border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr valign="top">
            <td colspan="5"><img alt="" src="//www.ibm.com/i/c.gif" border="0" height="12" width="12" /></td>
        </tr>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/icons/fw.gif" height="16" width="16" /></td>
            <td><a class="fbox" href="/developerworks/cn/whichmethod.html" cmimpressionsent="1">关于下载方法的信息</a></td>
            <td><img alt="" src="//www.ibm.com/i/c.gif" height="1" width="50" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="resources"><span class="atitle">参考资料 </span></a></p>
<strong>学习</strong><br />
<ul>
    <li>如果您想要获得 EasyMock 完整的文档和 API，您可以访问 EasyMock 的主页：<a href="http://www.easymock.org/" cmimpressionsent="1">http://www.easymock.org/</a>。 <br />
    <br />
    </li>
    <li>您可以在 JUnit 的主页上找到完整的文档和相关下载：<a href="http://www.junit.org/index.htm" cmimpressionsent="1">http://www.junit.org/index.htm</a>。 <br />
    <br />
    </li>
    <li>通过 Developer Works 上的文章，您可以和其它的框架做比较：<a href="http://www.ibm.com/developerworks/cn/opensource/os-eclipse-rmock/" cmimpressionsent="1">利用 Eclipse 进行单元测试</a>。
    <br />
    <br />
    </li>
</ul>
<br />
<strong>获得产品和技术</strong><br />
<ul>
    <li>在Source Forge上，你可以下载到最新的 EasyMock 相关代码：<a href="http://sourceforge.net/project/showfiles.php?group_id=82958" cmimpressionsent="1">http://sourceforge.net/project/showfiles.php?group_id=82958</a>。
    <br />
    <br />
    </li>
    <li>Eclipse 的相关下载可以在 <a href="http://www.eclipse.org/" cmimpressionsent="1">http://www.eclipse.org/</a> 上找到。 </li>
</ul>
<br />
<br />
<br />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/185422.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-11 16:16 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/11/185422.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用poi操作Excel的几点注意事项 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185165.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 10 Mar 2008 10:18:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185165.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/185165.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185165.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/185165.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/185165.html</trackback:ping><description><![CDATA[http://dev.w3pub.com/content/2007-5-1/2437.html<br />
<br />
<br />
上好的人肉包子新鲜出炉啦，各位妖魔鬼怪赶紧来尝尝鲜&#8230;&#8230;<br />
<br />
首先说说现在我所知道的java编辑Excel文件的两大开源工具：jakarta
poi和JavaExcelAPI（简称JXL），这两套工具我都试用了一这段时间，感觉各有优劣吧。poi在某些细节有些小Bug并且不支持写入图片，其他方面都挺不错的；JXL就惨了，除了支持写入图片外，我暂时看不到它比POI好的地方，我碰到的主要的问题就是对公式支持不是很好，很多带有公式的Excel文件用JXL打开后，公式就丢失了（比如now(),today()），在网上看到其他大虾评论说JXL写入公式也有问题，另外，JXL操作Excel文件的效率比POI低一点。经过比较后，我选择了poi开发我的项目。<br />
<br />
现在我要做的东西基本完成啦，我把这段时间使用poi的一些心得总结出来，希望能对和我遇到相同问题的朋友有所帮助，少熬几个夜，多点时间陪MM：），至于poi基本的使用方法，自己去看文档吧。<br />
<br />
1、设置分页符的bug。<br />
<br />
poi里的HSSFSheet类提供了setRowBreak方法可以设置Sheet的分页符。<br />
<br />
Bug：如果你要设置分页符的Sheet是本来就有的，并且你没有在里面插入过分页符，那么调用setRowBreak时POI会抛出空指针的异常。<br />
<br />
解决方法：在Excel里给这个sheet插入一个分页符，用POI打开后再把它删掉，然后你就可以随意插入分页符了。<br />
<br />
如果sheet是由poi生成的则没有这个问题。我跟踪了setRowBreak的源代码，发现是Sheet.java下的PageBreakRecord
rowBreaks这个变量在搞鬼，如果Sheet里原来没有分页符，开发这个模块的那位兄台忘了为这个对象new实例，所以只能我们先手工给Excel插入一个分页符来触发poi为rowBreaks创建实例。<br />
<br />
2、如何拷贝行。<br />
<br />
我在gmane.org的poi用户论坛翻遍了每个相关的帖子，找遍了api，也没看到一个拷贝行的方法，没办法，只能自己写：<br />
<br />
//注：this.fWorkbook是一个HSSHWorkbook，请自行在外部new<br />
public
void copyRows(String pSourceSheetName, String pTargetSheetName, int pStartRow,
int pEndRow, int pPosition)<br />
{<br />
HSSFRow sourceRow = null;<br />
HSSFRow
targetRow = null;<br />
HSSFCell sourceCell = null;<br />
HSSFCell targetCell =
null;<br />
HSSFSheet sourceSheet = null;<br />
HSSFSheet targetSheet =
null;<br />
Region region = null;<br />
int cType;<br />
int i;<br />
short
j;<br />
int targetRowFrom;<br />
int targetRowTo;<br />
<br />
if ((pStartRow ==
-1) || (pEndRow == -1))<br />
{<br />
return;<br />
}<br />
sourceSheet =
this.fWorkbook.getSheet(pSourceSheetName);<br />
targetSheet =
this.fWorkbook.getSheet(pTargetSheetName);<br />
//拷贝合并的单元格<br />
for (i = 0; i
&lt; sourceSheet.getNumMergedRegions(); i++)<br />
{<br />
region =
sourceSheet.getMergedRegionAt(i);<br />
if ((region.getRowFrom() &gt;= pStartRow)
&amp;&amp; (region.getRowTo() &lt;= pEndRow))<br />
{<br />
targetRowFrom =
region.getRowFrom() - pStartRow + pPosition;<br />
targetRowTo =
region.getRowTo() - pStartRow +
pPosition;<br />
region.setRowFrom(targetRowFrom);<br />
region.setRowTo(targetRowTo);<br />
targetSheet.addMergedRegion(region);<br />
}<br />
}<br />
//设置列宽<br />
for
(i = pStartRow; i &lt;= pEndRow; i++)<br />
{<br />
sourceRow =
sourceSheet.getRow(i);<br />
if (sourceRow != null)<br />
{<br />
for (j =
sourceRow.getFirstCellNum(); j &lt; sourceRow.getLastCellNum();
j++)<br />
{<br />
targetSheet.setColumnWidth(j,
sourceSheet.getColumnWidth(j));<br />
}<br />
break;<br />
}<br />
}<br />
//拷贝行并填充数据<br />
for
(;i &lt;= pEndRow; i++)<br />
{<br />
sourceRow = sourceSheet.getRow(i);<br />
if
(sourceRow == null)<br />
{<br />
continue;<br />
}<br />
targetRow =
targetSheet.createRow(i - pStartRow +
pPosition);<br />
targetRow.setHeight(sourceRow.getHeight());<br />
for (j =
sourceRow.getFirstCellNum(); j &lt; sourceRow.getLastCellNum();
j++)<br />
{<br />
sourceCell = sourceRow.getCell(j);<br />
if (sourceCell ==
null)<br />
{<br />
continue;<br />
}<br />
targetCell =
targetRow.createCell(j);<br />
targetCell.setEncoding(sourceCell.getEncoding());<br />
targetCell.setCellStyle(sourceCell.getCellStyle());<br />
cType
= sourceCell.getCellType();<br />
targetCell.setCellType(cType);<br />
switch
(cType)<br />
{<br />
case
HSSFCell.CELL_TYPE_BOOLEAN:<br />
targetCell.setCellValue(sourceCell.getBooleanCellValue());<br />
break;<br />
case
HSSFCell.CELL_TYPE_ERROR:<br />
targetCell.setCellErrorValue(sourceCell.getErrorCellValue());<br />
break;<br />
case
HSSFCell.CELL_TYPE_FORMULA:<br />
//parseFormula这个函数的用途在后面说明<br />
targetCell.setCellFormula(parseFormula(sourceCell.getCellFormula()));<br />
break;<br />
case
HSSFCell.CELL_TYPE_NUMERIC:<br />
targetCell.setCellValue(sourceCell.getNumericCellValue());<br />
break;<br />
case
HSSFCell.CELL_TYPE_STRING:<br />
targetCell.setCellValue(sourceCell.getStringCellValue());<br />
break;<br />
}<br />
}<br />
}<br />
}<br />
<br />
这个函数有两个问题暂时无法解决：<br />
<br />
a、只能在同一个Workbook里面使用，跨Workbook总是拷不过去，不知道为什么？<br />
<br />
b、由于在拷贝行时也把行高也拷过去了，如果往这些单元格里写入的数据长度超过单元格长度，那么他们不会自动调整行高！<br />
<br />
有哪位大侠知道上面两个问题任意一个的解决方法，请第一时间通知我！！！<br />
<br />
3、公式的问题。<br />
<br />
POI对Excel公式的支持是相当好的，但是我发现一个问题，如果公式里面的函数不带参数，比如now()或today()，那么你通过getCellFormula()取出来的值就是now(ATTR(semiVolatile))和today(ATTR(semiVolatile))，这样的值写入Excel是会出错的，这也是我上面copyRow的函数在写入公式前要调用parseFormula的原因，parseFormula这个函数的功能很简单，就是把ATTR(semiVolatile)删掉，我把它的代码贴出来：<br />
private
String parseFormula(String pPOIFormula)<br />
{<br />
final String
cstReplaceString = "ATTR(semiVolatile)"; //$NON-NLS-1$<br />
StringBuffer result
= null;<br />
int index;<br />
<br />
result = new StringBuffer();<br />
index =
pPOIFormula.indexOf(cstReplaceString);<br />
if (index &gt;=
0)<br />
{<br />
result.append(pPOIFormula.substring(0,
index));<br />
result.append(pPOIFormula.substring(index +
cstReplaceString.length()));<br />
}<br />
else<br />
{<br />
result.append(pPOIFormula);<br />
}<br />
<br />
return
result.toString();<br />
}<br />
至于为什么会出现ATTR(semiVolatile)，希望哪位大侠现身跟我解释一下。<br />
<br />
4、向Excel写入图片的问题。<br />
<br />
我上poi论坛查相关帖子，得到两种结论：1、不支持写入图片；2、支持写入图片，通过EscherGraphics2d这个Class实现。于是我就去查EscherGraphics2d这个Class，发现这个Class提供了N个drawImage方法，喜出望外的我开始写代码，结果调了一天，一直看不到效果，黔驴技穷的我在万般无奈下只好跟踪进drawImage这个函数内部，经过N个函数调用后在最底层函数发现了最终答案（偶当场暴走！！！）：<br />
<br />
public
boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int
sy1,<br />
int sx2, int sy2, Color bgColor, ImageObserver
imageobserver)<br />
{<br />
if (logger.check( POILogger.WARN
))<br />
logger.log(POILogger.WARN,"drawImage() not supported");<br />
return
true;<br />
}<br />
所以我强烈建议大家，以后使用第三方开发包一定尽量下载它的源代码，这样你在碰到问题时，看看它的的内部是怎么实现的，很多时候就可以不必重蹈我的覆辙了。既然POI不能写入图片，那我们只能把目光投向JXL，我用JXL写入图片功能是实现了，付出的代价是now()和today()这些函数丢失掉了，鱼与熊掌不能兼得吧，至于怎么解决JXL公式的问题，到下面这个帖子里和我一起守株待兔吧：<br />
<br />
http://community.csdn.net/Expert/topic/3569/3569340.xml?temp=.8480646<br />
<br />
好了我所知道得就这些，我接触poi也才几星期时间，上面有些内容可能说得并不正确，希望各位大虾砸砖指正！<br />
<br />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/185165.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-10 18:18 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185165.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用JXL读取Excel表格,拷贝、更新Excel工作薄 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185162.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 10 Mar 2008 10:15:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185162.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/185162.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185162.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/185162.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/185162.html</trackback:ping><description><![CDATA[http://www.javaresearch.org/article/45784.htm <br />
<br />
<br />
/**<br />
*&nbsp;&lt;p&gt;读取Excel表格,拷贝、更新Excel工作薄&nbsp;&lt;/p&gt;<br />
*&nbsp;&lt;p&gt;Description:&nbsp;可以读取Excel文件的内容,更新Excel工作薄<br />
*&nbsp;&lt;/p&gt;<br />
*&nbsp;&lt;p&gt;Copyright:&nbsp;Copyright&nbsp;(c)&nbsp;Corparation&nbsp;2005&lt;/p&gt;<br />
*&nbsp;&lt;p&gt;程序开发环境为eclipse&lt;/p&gt;<br />
*&nbsp;@author&nbsp;Walker<br />
*&nbsp;@version&nbsp;1.0<br />
*/<br />
package&nbsp;cn.com.yitong.xls;<br />
<br />
import&nbsp;java.io.File;<br />
import&nbsp;java.io.FileInputStream;<br />
import&nbsp;java.io.InputStream;<br />
import&nbsp;java.util.Vector;<br />
<br />
import&nbsp;cn.com.yitong.ChartImg;<br />
import&nbsp;cn.com.yitong.VireObj;<br />
import&nbsp;cn.com.yitong.platform.log.YTLogger;<br />
<br />
import&nbsp;jxl.CellType;<br />
import&nbsp;jxl.Workbook;<br />
import&nbsp;jxl.format.CellFormat;<br />
import&nbsp;jxl.format.Colour;<br />
import&nbsp;jxl.format.UnderlineStyle;<br />
import&nbsp;jxl.write.Formula;<br />
import&nbsp;jxl.write.Label;<br />
import&nbsp;jxl.write.Number;<br />
import&nbsp;jxl.write.WritableCell;<br />
import&nbsp;jxl.write.WritableCellFormat;<br />
import&nbsp;jxl.write.WritableFont;<br />
import&nbsp;jxl.write.WritableImage;<br />
import&nbsp;jxl.write.WritableSheet;<br />
import&nbsp;jxl.write.WritableWorkbook;<br />
import&nbsp;jxl.write.WriteException;<br />
import&nbsp;jxl.write.biff.RowsExceededException;<br />
<br />
public&nbsp;class&nbsp;XLSDemo<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;static&nbsp;final&nbsp;int&nbsp;TITLE_LENGTH&nbsp;=&nbsp;7;<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;static&nbsp;final&nbsp;int&nbsp;SHEET_WIDTH&nbsp;=&nbsp;32;<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;static&nbsp;final&nbsp;int&nbsp;SHEET_HEIGHT&nbsp;=&nbsp;116;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;/**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;创建Excel<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;makeXls()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Workbook&nbsp;workbook&nbsp;=&nbsp;null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;构建Workbook对象,&nbsp;只读Workbook对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;直接从本地文件创建Workbook,&nbsp;从输入流创建Workbook<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InputStream&nbsp;ins&nbsp;=&nbsp;new&nbsp;FileInputStream("D:/Workspace/testproj/source.xls");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;workbook&nbsp;=&nbsp;Workbook.getWorkbook(ins);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;利用已经创建的Excel工作薄创建新的可写入的Excel工作薄<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;outFile&nbsp;=&nbsp;new&nbsp;File("D:/Workspace/testproj/test.xls");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WritableWorkbook&nbsp;wwb&nbsp;=&nbsp;Workbook.createWorkbook(outFile,&nbsp;workbook);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;读取第一张工作表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WritableSheet&nbsp;dataSheet&nbsp;=&nbsp;wwb.getSheet(0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;设置冻结单元格<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataSheet.getSettings().setVerticalFreeze(7);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataSheet.getSettings().setHorizontalFreeze(2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;测试模拟数据<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Vector&nbsp;vecData&nbsp;=&nbsp;new&nbsp;Vector();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;50;&nbsp;i&nbsp;++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VireObj&nbsp;obj&nbsp;=&nbsp;new&nbsp;VireObj();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setOrgNo("00"&nbsp;+&nbsp;i&nbsp;+&nbsp;"0");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setOrgName("机构"&nbsp;+&nbsp;(i&nbsp;+&nbsp;1));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setOpenAcc((int)(100&nbsp;*&nbsp;Math.random()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setDestoryAcc((int)(10&nbsp;*&nbsp;Math.random()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setTotalAcc((int)(500&nbsp;*&nbsp;Math.random()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setMonthInCount((int)(500&nbsp;*&nbsp;Math.random()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setMonthInMoney(500&nbsp;*&nbsp;Math.random());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setMonthOutCount((int)(500&nbsp;*&nbsp;Math.random()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.setMonthOutMoney(500&nbsp;*&nbsp;Math.random());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vecData.add(obj);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;插入数据<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insertData(wwb,&nbsp;dataSheet,&nbsp;vecData);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;插入模拟图像数据<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Vector&nbsp;vecImg&nbsp;=&nbsp;new&nbsp;Vector();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;3;&nbsp;i&nbsp;++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ChartImg&nbsp;img&nbsp;=&nbsp;new&nbsp;ChartImg();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;img.setImgTitle("图像"&nbsp;+&nbsp;(i&nbsp;+&nbsp;1));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;img.setImgName("D:/Workspace/testproj/images/barchart.png");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vecImg.add(img);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;插入图表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insertImgsheet(wwb,&nbsp;vecImg);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//写入Excel对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wwb.write();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wwb.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;YTLogger.logDebug(e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;finally<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;操作完成时，关闭对象，释放占用的内存空间<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;workbook.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;/**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;插入数据<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;wwb&nbsp;WritableWorkbook&nbsp;:&nbsp;工作簿<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;dataSheet&nbsp;WritableSheet&nbsp;:&nbsp;工作表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;RowsExceededException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;insertData(WritableWorkbook&nbsp;wwb,&nbsp;WritableSheet&nbsp;dataSheet,&nbsp;Vector&nbsp;vecData)&nbsp;throws&nbsp;RowsExceededException,&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;获得标题单元格对象&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiStrCell(dataSheet,&nbsp;2,&nbsp;0,&nbsp;"工商银行江苏省分行&nbsp;个人网上银行业务种类/开销户明细报表（2005-12）",&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;修改数据单元格数据<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;vecData.size();&nbsp;i&nbsp;++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VireObj&nbsp;obj&nbsp;=&nbsp;(VireObj)vecData.get(i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiStrCell(dataSheet,&nbsp;0,&nbsp;TITLE_LENGTH&nbsp;+&nbsp;i,&nbsp;obj.getOrgNo(),&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiStrCell(dataSheet,&nbsp;1,&nbsp;TITLE_LENGTH&nbsp;+&nbsp;i,&nbsp;obj.getOrgName(),&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiNumCell(dataSheet,&nbsp;2,&nbsp;TITLE_LENGTH&nbsp;+&nbsp;i,&nbsp;obj.getOpenAcc(),&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiNumCell(dataSheet,&nbsp;3,&nbsp;TITLE_LENGTH&nbsp;+&nbsp;i,&nbsp;obj.getDestoryAcc(),&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiNumCell(dataSheet,&nbsp;4,&nbsp;TITLE_LENGTH&nbsp;+&nbsp;i,&nbsp;obj.getTotalAcc(),&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiNumCell(dataSheet,&nbsp;5,&nbsp;TITLE_LENGTH&nbsp;+&nbsp;i,&nbsp;obj.getMonthInCount(),&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiNumCell(dataSheet,&nbsp;6,&nbsp;TITLE_LENGTH&nbsp;+&nbsp;i,&nbsp;obj.getTotalInMoney(),&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiNumCell(dataSheet,&nbsp;7,&nbsp;TITLE_LENGTH&nbsp;+&nbsp;i,&nbsp;obj.getMonthOutCount(),&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiNumCell(dataSheet,&nbsp;8,&nbsp;TITLE_LENGTH&nbsp;+&nbsp;i,&nbsp;obj.getMonthOutMoney(),&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;删除空行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;j&nbsp;=&nbsp;vecData.size()&nbsp;+&nbsp;TITLE_LENGTH;&nbsp;j&nbsp;&lt;&nbsp;SHEET_HEIGHT;&nbsp;j++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataSheet.removeRow(vecData.size()&nbsp;+&nbsp;TITLE_LENGTH);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;插入公式<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int&nbsp;i&nbsp;=&nbsp;2;&nbsp;i&nbsp;&lt;&nbsp;SHEET_WIDTH;&nbsp;i&nbsp;++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modiFormulaCell(dataSheet,&nbsp;i,&nbsp;vecData.size()&nbsp;+&nbsp;TITLE_LENGTH,&nbsp;8,&nbsp;vecData.size()&nbsp;+&nbsp;TITLE_LENGTH,&nbsp;null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;/**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;修改字符单元格的值<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;dataSheet&nbsp;WritableSheet&nbsp;:&nbsp;工作表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;col&nbsp;int&nbsp;:&nbsp;列<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;row&nbsp;int&nbsp;:&nbsp;行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;str&nbsp;String&nbsp;:&nbsp;字符<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;format&nbsp;CellFormat&nbsp;:&nbsp;单元格的样式<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;RowsExceededException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;modiStrCell(WritableSheet&nbsp;dataSheet,&nbsp;int&nbsp;col,&nbsp;int&nbsp;row,&nbsp;String&nbsp;str,&nbsp;CellFormat&nbsp;format)&nbsp;throws&nbsp;RowsExceededException,&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;获得单元格对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WritableCell&nbsp;cell&nbsp;=&nbsp;dataSheet.getWritableCell(col,&nbsp;row);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;判断单元格的类型,&nbsp;做出相应的转化<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(cell.getType()&nbsp;==&nbsp;CellType.EMPTY)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Label&nbsp;lbl&nbsp;=&nbsp;new&nbsp;Label(col,&nbsp;row,&nbsp;str);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(null&nbsp;!=&nbsp;format)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lbl.setCellFormat(format);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lbl.setCellFormat(cell.getCellFormat());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataSheet.addCell(lbl);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(cell.getType()&nbsp;==&nbsp;CellType.LABEL)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Label&nbsp;lbl&nbsp;=&nbsp;(Label)cell;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lbl.setString(str);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(cell.getType()&nbsp;==&nbsp;CellType.NUMBER)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;数字单元格修改<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Number&nbsp;n1&nbsp;=&nbsp;(Number)cell;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;n1.setValue(42.05);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;/**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;修改数字单元格的值<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;dataSheet&nbsp;WritableSheet&nbsp;:&nbsp;工作表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;col&nbsp;int&nbsp;:&nbsp;列<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;row&nbsp;int&nbsp;:&nbsp;行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;num&nbsp;double&nbsp;:&nbsp;数值<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;format&nbsp;CellFormat&nbsp;:&nbsp;单元格的样式<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;RowsExceededException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;modiNumCell(WritableSheet&nbsp;dataSheet,&nbsp;int&nbsp;col,&nbsp;int&nbsp;row,&nbsp;double&nbsp;num,&nbsp;CellFormat&nbsp;format)&nbsp;throws&nbsp;RowsExceededException,&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;获得单元格对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WritableCell&nbsp;cell&nbsp;=&nbsp;dataSheet.getWritableCell(col,&nbsp;row);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;判断单元格的类型,&nbsp;做出相应的转化<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(cell.getType()&nbsp;==&nbsp;CellType.EMPTY)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Number&nbsp;lbl&nbsp;=&nbsp;new&nbsp;Number(col,&nbsp;row,&nbsp;num);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(null&nbsp;!=&nbsp;format)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lbl.setCellFormat(format);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lbl.setCellFormat(cell.getCellFormat());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataSheet.addCell(lbl);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(cell.getType()&nbsp;==&nbsp;CellType.NUMBER)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;数字单元格修改<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Number&nbsp;lbl&nbsp;=&nbsp;(Number)cell;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lbl.setValue(num);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(cell.getType()&nbsp;==&nbsp;CellType.LABEL)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Label&nbsp;lbl&nbsp;=&nbsp;(Label)cell;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lbl.setString(String.valueOf(num));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;/**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;修改公式单元格的值<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;dataSheet&nbsp;WritableSheet&nbsp;:&nbsp;工作表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;col&nbsp;int&nbsp;:&nbsp;列<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;row&nbsp;int&nbsp;:&nbsp;行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;startPos&nbsp;int&nbsp;:&nbsp;开始位置<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;endPos&nbsp;int&nbsp;:&nbsp;结束位置<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;format<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;RowsExceededException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;modiFormulaCell(WritableSheet&nbsp;dataSheet,&nbsp;int&nbsp;col,&nbsp;int&nbsp;row,&nbsp;int&nbsp;startPos,&nbsp;int&nbsp;endPos,&nbsp;CellFormat&nbsp;format)&nbsp;throws&nbsp;RowsExceededException,&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;f&nbsp;=&nbsp;getFormula(col,&nbsp;row,&nbsp;startPos,&nbsp;endPos);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;插入公式（只支持插入，不支持修改）<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WritableCell&nbsp;cell&nbsp;=&nbsp;dataSheet.getWritableCell(col,&nbsp;row);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(cell.getType()&nbsp;==&nbsp;CellType.EMPTY)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;公式单元格<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Formula&nbsp;lbl&nbsp;=&nbsp;new&nbsp;Formula(col,&nbsp;row,&nbsp;f);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(null&nbsp;!=&nbsp;format)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lbl.setCellFormat(format);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lbl.setCellFormat(cell.getCellFormat());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataSheet.addCell(lbl);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(cell.getType()&nbsp;==&nbsp;CellType.STRING_FORMULA)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;YTLogger.logWarn("Formula&nbsp;modify&nbsp;not&nbsp;supported!");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;/**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;得到公式<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;col&nbsp;int&nbsp;:&nbsp;列<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;row&nbsp;int&nbsp;:&nbsp;行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;startPos&nbsp;int&nbsp;:&nbsp;开始位置<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;endPos&nbsp;int&nbsp;:&nbsp;结束位置<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@return&nbsp;String<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;RowsExceededException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;getFormula(int&nbsp;col,&nbsp;int&nbsp;row,&nbsp;int&nbsp;startPos,&nbsp;int&nbsp;endPos)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;RowsExceededException,&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;base&nbsp;=&nbsp;'A';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;c1&nbsp;=&nbsp;base;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuffer&nbsp;formula&nbsp;=&nbsp;new&nbsp;StringBuffer(128);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;组装公式<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formula.append("SUM(");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(col&nbsp;&lt;=&nbsp;25)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c1&nbsp;=&nbsp;(char)&nbsp;(col&nbsp;%&nbsp;26&nbsp;+&nbsp;base);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formula.append(c1).append(startPos).append(":")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(c1).append(endPos).append(")");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(col&nbsp;&gt;&nbsp;25)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;c2&nbsp;=&nbsp;(char)&nbsp;((col&nbsp;-&nbsp;26)&nbsp;/&nbsp;26&nbsp;+&nbsp;base);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c1&nbsp;=&nbsp;(char)&nbsp;((col&nbsp;-&nbsp;26)&nbsp;%&nbsp;26&nbsp;+&nbsp;base);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formula.append(c2).append(c1).append(startPos).append(":")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(c2).append(c1).append(endPos).append(")");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;formula.toString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;/**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;插入图表工作表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;wwb&nbsp;WritableWorkbook&nbsp;:&nbsp;工作簿<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;vecImg&nbsp;Vector&nbsp;:&nbsp;图像链表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;RowsExceededException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;insertImgsheet(WritableWorkbook&nbsp;wwb,&nbsp;Vector&nbsp;vecImg)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;RowsExceededException,&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;插入图像<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WritableSheet&nbsp;imgSheet;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((wwb.getSheets()).length&nbsp;&lt;&nbsp;2)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imgSheet&nbsp;=&nbsp;wwb.createSheet("图表",&nbsp;1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imgSheet&nbsp;=&nbsp;wwb.getSheet(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;vecImg.size();&nbsp;i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ChartImg&nbsp;chart&nbsp;=&nbsp;(ChartImg)&nbsp;vecImg.get(i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;插入图像标题<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Label&nbsp;lbl&nbsp;=&nbsp;new&nbsp;Label(0,&nbsp;2&nbsp;+&nbsp;20&nbsp;*&nbsp;i,&nbsp;chart.getImgTitle());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WritableFont&nbsp;font&nbsp;=&nbsp;new&nbsp;WritableFont(WritableFont.ARIAL,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WritableFont.DEFAULT_POINT_SIZE,&nbsp;WritableFont.NO_BOLD,&nbsp;false,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UnderlineStyle.NO_UNDERLINE,&nbsp;Colour.DARK_BLUE2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WritableCellFormat&nbsp;background&nbsp;=&nbsp;new&nbsp;WritableCellFormat(font);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;background.setWrap(true);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;background.setBackground(Colour.GRAY_25);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imgSheet.mergeCells(0,&nbsp;2&nbsp;+&nbsp;20&nbsp;*&nbsp;i,&nbsp;9,&nbsp;2&nbsp;+&nbsp;20&nbsp;*&nbsp;i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lbl.setCellFormat(background);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imgSheet.addCell(lbl);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;插入图像单元格<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insertImgCell(imgSheet,&nbsp;2,&nbsp;4&nbsp;+&nbsp;20&nbsp;*&nbsp;i,&nbsp;8,&nbsp;15,&nbsp;chart.getImgName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;/**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;插入图像到单元格（图像格式只支持png）<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;dataSheet&nbsp;WritableSheet&nbsp;:&nbsp;工作表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;col&nbsp;int&nbsp;:&nbsp;列<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;row&nbsp;int&nbsp;:&nbsp;行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;width&nbsp;int&nbsp;:&nbsp;宽<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;height&nbsp;int&nbsp;:&nbsp;高<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;imgName&nbsp;String&nbsp;:&nbsp;图像的全路径<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;RowsExceededException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@throws&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;insertImgCell(WritableSheet&nbsp;dataSheet,&nbsp;int&nbsp;col,&nbsp;int&nbsp;row,&nbsp;int&nbsp;width,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;height,&nbsp;String&nbsp;imgName)&nbsp;throws&nbsp;RowsExceededException,&nbsp;WriteException<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;imgFile&nbsp;=&nbsp;new&nbsp;File(imgName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WritableImage&nbsp;img&nbsp;=&nbsp;new&nbsp;WritableImage(col,&nbsp;row,&nbsp;width,&nbsp;height,&nbsp;imgFile);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataSheet.addImage(img);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;/**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;测试<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;args<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;XLSDemo&nbsp;demo&nbsp;=&nbsp;new&nbsp;XLSDemo();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;demo.makeXls();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
<br />
<br />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/185162.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-10 18:15 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185162.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebLogic教程：在WebLogic Server中集成Apache Poi</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185159.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 10 Mar 2008 10:05:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185159.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/185159.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185159.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/185159.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/185159.html</trackback:ping><description><![CDATA[<span class="h1b">http://dev2dev.bea.com.cn/techdoc/20060517793.html<br />
<br />
<br />
WebLogic教程：在WebLogic Server中集成Apache Poi</span><br />
<strong>--Apache
Jakarta POI项目提供了访问和生成Excel文档的组件</strong><br />
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td height="64">时间：2006-05-17<br />
            作者：<a href="/author/253.html">Ajay Vohra</a>,&nbsp;<a href="/author/406.html">Deepak Vohra</a><br />
            浏览次数：
            <script language="JavaScript" src="/beadevcount.jsp?d_id=162273" type="text/JavaScript"></script>
            8856 <br />
            本文关键字：<a href="/products/search.jsp?searchtype=keywords&amp;keywords=Apache">Apache</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=Poi">Poi</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=Apache%20Poi">Apache
            Poi</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=WebLogic%20Server">WebLogic
            Server</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=Excel">Excel</a></td>
            <td>
            <table class="box_content" border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td><span class="h2b">文章工具</span><br />
                        <img alt="推荐给朋友" src="http://www.blogjava.net/images/letter001.gif" align="absmiddle" height="10" width="19" />&nbsp;<a href="javascript:sendmail()">推荐给朋友</a><br />
                        <img alt="打印文章" src="http://www.blogjava.net/images/print001.gif" align="absmiddle" height="18" width="19" />&nbsp;<a href="javascript:window.print()">打印文章</a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<!-- 提取技术文章 -->
<div class="beas"><img alt="" src="http://www.blogjava.net/images/dot6B6B6B.gif" height="1" width="100%" /></div>
<p>　　Apache Jakarta POI项目提供了用于访问和生成Excel文档的组件。POI HSSF API用于生成Excel
Workbook以及将Excel电子表格添加到workbook。Excel电子表格由行和单元格组成。电子表格的页面布局和字体也使用POI HSSF
API进行设置。</p>
<p>　　通常要求将数据库表呈现在Excel电子表格中。同样，开发人员的需求也可以保存到数据库表中的Excel电子表格中。Apache POI
HSSF项目旨在提供一个创建Excel电子表格的API。使用POI
HSSF项目生成的Excel电子表格中的数据可以是XML文档中的静态数据，或者是从一个数据库中动态检索到的数据。此外，Excel文档也可以被转换为XML文档或者保存在数据库中。在本教程中，我们将讨论在WebLogic
Server中从一个MySQL数据库表创建Excel电子表格的步骤，随后将该Excel电子表格保存在一个数据库表中。</p>
<h3>初步设置</h3>
<p>　　org.apache.poi.hssf.usermodel包提供POI
HSSF项目的实现。要生成或解析Excel电子表格，需要在类路径中设置org.apache.poi.hssf.usermodel包类。下载Apache
POI库poi-bin-2.5.1-final-20040804.zip文件，将该zip文件解压缩所到一个安装目录(<a href="http://jakarta.apache.org/poi/" target="_blank">http://jakarta.apache.org/poi/</a>)。安装WebLogic Server
8.1。下载MySQL数据库(<a href="http://www.mysql.com/" target="_blank">www.mysql.com</a>)。解压缩MySQL
zip文件mysql-4.0.25-win32.zip到一个目录下。安装MySQL数据库。下载MySQL Connector/J JDBC驱动程序(<a href="http://www.mysql.com/products/connector/j/" target="_blank">www.mysql.com/products/connector/j/</a>)。解压缩MySQL
zip文件mysql-connector-java-3.1.10.zip到一个目录下。添加MySQL
JDBC驱动程序jar文件mysql-connector-java-3.1.10-bin.jar到&lt;weblogic81&gt;\samples\domains\examples\startExamplesServer脚本CLASSPATH变量。</p>
<p>　　使用下面的DOS命令登录到MySQL数据库：</p>
<p>　　&gt;mysql</p>
<p>　　使用下面的命令访问示例数据库测试：</p>
<p>　　mysql&gt;use test</p>
<p>　　在MySQL数据库中创建一个示例数据库表，将在此生成Excel电子表格。清单1展示了创建示例表Catalog的SQL脚本。</p>
<p>　　清单1：<strong>Catalog.sql</strong></p>
<pre class="code">CREATE TABLE Catalog(CatlogId VARCHAR(25)<br />
PRIMARY KEY, Journal VARCHAR(25), Section VARCHAR(25),<br />
Edition VARCHAR(25),  Title Varchar(125), Author Varchar(25));<br />
<br />
INSERT INTO Catalog VALUES('catalog1', 'dev2dev',<br />
'WebLogic Platform 8.1',<br />
'Oct 2004',  'BEA WebLogic Platform 8.1 SP3 Evaluation Guide',<br />
'dev2dev');<br />
<br />
INSERT INTO Catalog VALUES('catalog2', 'dev2dev',<br />
'WebLogic Server',<br />
'Feb 2005',  'Application Architecture for Applications Built on<br />
BEA WebLogic Platform 8.1', 'Bob Hensle');<br />
<br />
INSERT INTO Catalog VALUES('catalog3', 'dev2dev',<br />
'WebLogic Integration',<br />
'March 2005',  'The BEA WebLogic Platform and Host Integration',<br />
'Tom Bice');<br />
</pre>
<p>　　接下来，我们将向WebLogic Server Classpath添加Apache POI .jar文件，并在WebLogic
Server中创建一个JDBC数据源来检索用于Excel电子表格的数据。</p>
<p>　　添加poi-2.5.1-final-20040804.jar文件到&lt;weblogic81&gt;\samples\domains\examples\startExamplesServer脚本中的CLASSPATH变量中。&lt;weblogic81&gt;是安装WebLogic
Server的目录。</p>
<p>　　接下来，使用MySQL数据库在WebLogic
Server中创建JDBC连接。使用startExamplesServer脚本启动示例服务器。使用URL
http://localhost:7001/console或者WebLogic Server Examples索引中的Administration
Console链接来访问管理控制台。在管理控制台中，右击examples&gt;Services&gt;JDBC&gt;Connection
Pools节点，并选择Configure a new JDBCConnectionPool。指定以下连接属性来配置JDBC连接池：</p>
<ul>
    <li>数据库类型：MySQL
    </li>
    <li>JDBC驱动程序：MySQL's Driver (Type 4)
    </li>
    <li>数据库名称：test
    </li>
    <li>主机名：localhost
    </li>
    <li>端口号：3306
    </li>
    <li>驱动程序类名：com.mysql.jdbc.Driver
    </li>
    <li>连接URL：jdbc:mysql://localhost:3306/test </li>
</ul>
<p>　　接下来，在管理控制台中配置JDBC数据源。右击examples&gt;Services&gt;JDBC&gt;DataSources节点，并选择Configure
a new JDBCTxDataSource。在Configure the data
source框中指定数据源名和JNDI名——例如，MySqlDS。在Connect to connection
pool框中选择先前使用MySQL数据库配置的连接池。在Target the
datasource框中选择examplesServer。这样就使用MySQL数据库配置了一个数据源。</p>
<h3>使用Apache POI生成Excel文档</h3>
<p>　　在这一节中，我们将从示例数据库表生成Excel电子表格。首先，创建一个生成Excel电子表格的JSP应用程序。</p>
<p>　　在JSP中，将从一个MySQL数据库表创建Excel电子表格。使用Apache POI HSSF API来生成Excel电子表格。Apache POI
HSSF包中含有用于Excel电子表格的不同组件的类。表1列出了Apache POI HSSF包中一些常用的类：</p>
<p align="center"><img alt="使用Apache POI生成Excel文档" src="http://www.blogjava.net/images/image060517001.gif" border="0" height="218" width="468" /></p>
<p>　　首先，导入Apache POI HSSF包：</p>
<pre class="code">  &lt;%@ page import="org.apache.poi.hssf.usermodel.*,  java.sql.*,<br />
java.io.*,javax.naming.InitialContext"%&gt;</pre>
创建一个Excel workbook：
<pre class="code">  HSSFWorkbook wb=new HSSFWorkbook();</pre>
<p>　　接下来，创建一个Excel电子表格：</p>
<pre class="code">  HSSFSheet sheet1=wb.createSheet("sheet1");</pre>
<p>　　该电子表格的数据是从MySQL数据库表检索得到的。从数据库获得JDBC连接。JDBC连接是使用datasource JNDI
MySqlDS获得的。</p>
<pre class="code">  InitialContext initialContext = new InitialContext();<br />
javax.sql.DataSource ds = (javax.sql.DataSource)<br />
initialContext.lookup("MySqlDS");<br />
java.sql.Connection conn = ds.getConnection();</pre>
<p>　　创建一个java.sql.Statement，并从示例表Catalog获得结果集：</p>
<pre class="code">  Statement stmt=conn.createStatement();<br />
ResultSet resultSet=stmt.executeQuery("Select * from Catalog");</pre>
<p>　　为Excel电子表格创建一个标题行。Excel电子表格中的行从&#8220;0&#8221;开始。</p>
<pre class="code">  HSSFRow row=sheet1.createRow(0);</pre>
<p>　　对应于表的列设置标题行单元格的值。行单元格也是从&#8220;0&#8221;开始。例如，行中第一个单元格的值被使用setCellValue方法设置为CatalogId。</p>
<pre class="code">  row.createCell((short)0).setCellValue("CatalogId");</pre>
<p>　　要向电子表格添加行，迭代结果集并为每个表格行添加一行。从ResultSet检索列值，并在行单元格中设置这些值。</p>
<pre class="code">for (int i=1;resultSet.next(); i++)<br />
{<br />
row=sheet1.createRow(i);<br />
row.createCell((short)0).setCellValue(resultSet.getString(1));<br />
row.createCell((short)1).setCellValue(resultSet.getString(2));<br />
row.createCell((short)2).setCellValue(resultSet.getString(3));<br />
row.createCell((short)3).setCellValue(resultSet.getString(4));<br />
row.createCell((short)4).setCellValue(resultSet.getString(5));<br />
} <br />
</pre>
<p>　　创建一个FileOutputStream来将Excel电子表格输出到XLS文件。每个XLS文件表示一个Excel电子表格：</p>
<p>　　FileOutputStream
output=new文件OutputStream(new文件("c:/excel/catalog.xls"));</p>
<p>　　将Excel电子表格输出到XLS文件：</p>
<pre class="code">  wb.write(output);</pre>
<p>　　在&#8220;参考资料&#8221;部分有用于生成Excel电子表格的ExcelWebLogic.jsp JSP。</p>
<p>　　要在WebLogic Server中运行ExcelWebLogic.jsp
JSP，复制该JSP到&lt;weblogic81&gt;\samples\server\examples\build\mainWebApp目录。使用URL
http://localhost:7001/ExcelWebLogic.jsp运行该JSP。</p>
<p>　　生成的Excel电子表格可以在Excel (<a href="http://office.microsoft.com/en-us/FX010858001033.aspx" target="_blank">http://office.microsoft.com/en-us/FX010858001033.aspx</a>)或Excel
Viewer工具tool (<a href="http://office.microsoft.com/en-us/assistance/HA011620741033.aspx" target="_blank">http://office.microsoft.com/en-us/assistance/HA011620741033.aspx</a>)中打开。</p>
<h3>将Excel文档保存在数据库表中</h3>
<p>　　本节我们将使用Apache POI
API将一个Excel电子表格保存在MySQL数据库表中。要保存的示例Excel文档是catalog.xls，即，前一节所生成的电子表格。该Excel电子表格被保存在MySQL表Catalog中。使用MySQL命令删除前一节中用于生成Excel文档的Catalog表：</p>
<pre class="code">  MySQL&gt;DROP table Catalog;</pre>
<p>　　开发一个JSP应用程序来将示例Excel文档保存在MySQL数据库中。在JSP应用程序中导入Apache
POI包org.apache.poi.poifs.filesystem和org.apache.poi.hssf.usermodel。org.apache.poi.poifs.filesystem包中包含创建Excel
workbook的类，而org.apache.poi.hssf.usermodel包中包含表示Excel
workbook、电子表格、电子表格行和行单元格的类。</p>
<pre class="code">  &lt;%@ page import="org.apache.poi.poifs.filesystem.*,<br />
org.apache.poi.hssf.usermodel.*, java.sql.*,<br />
java.io.*,javax.naming.InitialContext"%&gt;</pre>
<p>　　像前一节一样，从MySQL datasource获得JDBC连接：</p>
<pre class="code">  InitialContext initialContext = new InitialContext();<br />
javax.sql.DataSource ds =  (javax.sql.DataSource)<br />
initialContext.lookup("MySqlDS");<br />
java.sql.Connection conn = ds.getConnection();</pre>
<p>　　从JDBC连接创建java.sql.Statement：</p>
<pre class="code">  Statement stmt=conn.createStatement();</pre>
<p>　　创建一个MySQL表来保存Excel电子表格：</p>
<pre class="code">  String createTable="CREATE TABLE Catalog(CatalogId  VARCHAR(25) PRIMARY KEY,Journal<br />
VARCHAR(25),Section VARCHAR(25),Edition VARCHAR(25),Title Varchar(125),Author  Varchar(25))";</pre>
<pre class="code">  stmt.execute(createTable);</pre>
<p>　　创建一个POIFSFileSystem来读取Excel文档：</p>
<pre class="code">  File catalogExcel=new File("C:/ExcelWebLogic/catalog.xls");<br />
FileInputStream inputStream=new FileInputStream(catalogExcel);<br />
POIFSFileSystem fileSystem=new POIFSFileSystem(inputStream);</pre>
<p>　　从POIFSFileSystem获得一个HSSF workbook：</p>
<pre class="code">  HSSFWorkbook wb=new HSSFWorkbook(fileSystem);</pre>
<p>　　从Excel workbook获得一个Excel电子表格：</p>
<pre class="code">  HSSFSheet sheet1=wb.getSheet("sheet1");</pre>
<p>　　使用row iterator迭代电子表格中的行：</p>
<pre class="code">  java.util.Iterator rowIterator=sheet1.rowIterator();<br />
HSSFRow row=(HSSFRow)rowIterator.next();</pre>
<p>　　对于每一行，迭代行单元格的值。例如，CatalogId行单元格值是使用以下代码检索的：</p>
<pre class="code">  String catalogId=row.getCell((short)0).getStringCellValue();</pre>
<p>　　对于Excel电子表格中的每一行，添加一个表格行：</p>
<pre class="code">String exceltable="INSERT INTO Catalog VALUES("+"\'"+catalogId+"\<br />
'"+","+"\'"+journal<br />
+"\'"+","+"\'"+section+"\'"+","+"\'"+edition+"\'"+","+"\'"+title+"\<br />
'"+","+"\'"+author+"\'"+")";<br />
stmt.execute(exceltable); <br />
</pre>
<p>　　复制POIWebLogic.jsp到&lt;weblogic81&gt;\samples\server\examples\build\mainWebApp目录。使用URL
http://localhost:7001/POIWebLogic.jsp运行JSP。这样就从Excel电子表格生成一个MySQL数据库表。在&#8220;参考资料&#8221;部分有用于从Excel电子表格生成数据库表的POIWebLogic.jsp。</p>
<h3>结束语</h3>
<p>　　在本教程中，我们从一个MySQL数据库表生成了一个Excel电子表格，随后将该电子表格保存在一个数据库表中。WebLogic
Server提供了一个数据源以及运行JSP应用程序的J2EE应用服务器，从而方便了从数据库表到Excel电子表格以及从电子表格到数据库表的转换。</p>
<p><strong>原文出处:</strong><a href="http://wldj.sys-con.com/read/185302.htm" target="_blank">http://wldj.sys-con.com/read/185302.htm</a></p>
<!--文章其他信息-->
<div class="dot001"><img alt="" src="http://www.blogjava.net/images/_.gif" height="1" width="100%" /></div>
<table border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr valign="bottom">
            <td colspan="2" height="20">&nbsp;<span class="h2b">作者简介</span></td>
        </tr>
        <tr>
            <td align="center" valign="top" width="0"><br />
            </td>
            <td><a href="http://wldj.sys-con.com/author/3394vohra.htm" target="_blank">Ajay
            Vohra</a> 是DataSynapse公司的高级解决方案架构师。</td>
        </tr>
        <tr>
            <td align="center" valign="top" width="0"><br />
            </td>
            <td><a href="http://www.oreillynet.com/pub/au/1723" target="_blank">Deepak
            Vohra</a> 是一名NuBean顾问兼web开发人员。他拥有Sun Certified Java 1.4 Programmer和Sun Certified
            Web Component Developer for J2EE证书。</td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/185159.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-10 18:05 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185159.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用XMLBeans绑定XML－java数据 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185149.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 10 Mar 2008 09:32:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185149.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/185149.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185149.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/185149.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/185149.html</trackback:ping><description><![CDATA[<span class="h1b">http://dev2dev.bea.com.cn/techdoc/2007/08/java-XMLBeans.html<br />
<br />
<br />
使用XMLBeans绑定XML－java数据</span><br />
<br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td height="64">时间：2007-08-16<br />
            作者：<a href="/author/HetalC-Shah-070816.html">Hetal C. Shah</a><br />
            浏览次数：
            <script language="JavaScript" src="/beadevcount.jsp?d_id=1180439" type="text/JavaScript"></script>
            1202 <br />
            本文关键字：<a href="/products/search.jsp?searchtype=keywords&amp;keywords=XMLBeans">XMLBeans</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=xml">xml</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=xml%20schema">xml
            schema</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=xsd">xsd</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=schema">schema</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=%20apache">
            apache</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=%E6%A8%A1%E5%BC%8F">模式</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=%20xml-java%E7%BB%91%E5%AE%9A">
            xml-java绑定</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=%20java%E7%BB%91%E5%AE%9A">
            java绑定</a>,&nbsp;<a href="/products/search.jsp?searchtype=keywords&amp;keywords=%20%E6%95%B0%E6%8D%AE%E7%BB%91%E5%AE%9A">
            数据绑定</a></td>
            <td>
            <table class="box_content" border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td><span class="h2b">文章工具</span><br />
                        <img alt="推荐给朋友" src="http://www.blogjava.net/images/letter001.gif" align="absmiddle" height="10" width="19" />&nbsp;<a href="javascript:sendmail()">推荐给朋友</a><br />
                        <img alt="打印文章" src="http://www.blogjava.net/images/print001.gif" align="absmiddle" height="18" width="19" />&nbsp;<a href="javascript:window.print()">打印文章</a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<!-- 提取技术文章 -->
<div class="beas"><img alt="" src="http://www.blogjava.net/images/dot6B6B6B.gif" height="1" width="100%" /></div>
<p>　　<a href="http://xmlbeans.apache.org/" target="_blank">XMLBeans</a>提供了底层XML数据的对象视图，同时还能访问原始的XML信息集合。通过递增的解除封送xml数据和高效的访问XML
模式内置数据类型的方法，XMLBeans交付了较好的性能。下面两种特性几乎百分之百的支持XML模式，并在操作数据期间定时验证XML数据
，从而使XMLBeans非常适用于XML-Java 数据绑定。</p>
<p>　　XMLBeans目前处于 <a href="http://www.apache.org/" target="_blank">Apache</a>
项目的孵化过程中，并且证明对于Java开发人员进行XML-Java数据绑定是非常有用的。</p>
<p>　　本文后面的资源部分提供了本文示例代码和其他文件的下载。所有示例代码均在Apache XMLBeans 1.02、Java
1.4.2_02和Microsoft Windows 2000的环境下进行了测试。</p>
<h3>创建一个XMLBean</h3>
<p>　　在开始创建XMLBeans之前，需要下载并在系统中安装Apache XMLBeans 1.02。当从XMLBeans
的归档文件中提取出文件之后，将会在解压文件中看到bin目录和lib目录。随后，把bin目录放到路径中，把lib目录中的xbean.jar包放到classpath路径中。</p>
<p>　　XML 模式文件(XSD文件)创建了XMLBeans类。这些XMLBeans类能够解析所有符合XML模式的XML
实例文档。同样，通过使用这些XMLBeans类，也能够创建出实例文档。</p>
<p>　　例如，下面的weather_latlong.xsd模式列表描述了xml文档的内容,该文档包含了某个地理位置的天气、经纬度信息，这些信息全部基于zip代码。</p>
<pre class="code">&lt;xsd:schema<br />
xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;<br />
&lt;!-- This XML Schema describes xml documents<br />
containing either weather details or latlong<br />
details of a location based on Zipcode Two Global<br />
elements Weather and Latlong, and one Global<br />
Attribute Zipcode are declared.--&gt;<br />
&lt;xsd:element name="Weather"&gt;<br />
&nbsp; &lt;xsd:complexType&gt;<br />
&nbsp;&nbsp; &lt;xsd:sequence&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;xsd:element name="Temperature"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type="xsd:float"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;xsd:element name="Humidity"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type="xsd:float"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;xsd:element name="Visibility"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type="xsd:float"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;xsd:element name="Datetime"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type="xsd:dateTime"/&gt;<br />
&nbsp;&nbsp; &lt;/xsd:sequence&gt;<br />
&nbsp; &lt;xsd:attribute ref="Zipcode"/&gt;<br />
&lt;/xsd:complexType&gt;<br />
&lt;/xsd:element&gt;<br />
&lt;xsd:element name="Latlong"&gt;<br />
&nbsp; &lt;xsd:complexType&gt;<br />
&nbsp;&nbsp; &lt;xsd:sequence&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;xsd:element name="Latitude"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type="xsd:string"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;xsd:element name="Longitude"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type="xsd:string"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/xsd:sequence&gt;<br />
&nbsp;&nbsp; &lt;xsd:attribute ref="Zipcode"/&gt;<br />
&nbsp; &lt;/xsd:complexType&gt;<br />
&lt;/xsd:element&gt;<br />
&lt;xsd:attribute name="Zipcode"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type="xsd:string"/&gt;<br />
&lt;/xsd:schema&gt;<br />
</pre>
<p>　　接下来的步骤将生成一组XMLBeans类，它们表示上面的XSD类型模式。在工作目录（从示例归档文件提取文件的位置）的提示符中，输入以下命令行：</p>
<pre class="code">scomp -out weather.jar weather_latlong.xsd</pre>
<p>　　在编译完以上的模式后，XMLBeans生成如下五个接口。WeatherDocument、WeatherDocument$Weather、LatlongDocument、LatlongDocument$Latlong和ZipcodeAttribute。</p>
<p>　　在此，WeatherDocument接口表示文档元素，WeatherDocument$Weather接口表示全局元素Weather。类似地，LatlongDocument和LatlongDocument$Latlong接口表示全局元素Latlong。ZipcodeAttribute接口代表了全局属性Zipcode。</p>
<h3>XMLBeans类</h3>
<p>　　下面将详细讨论XMLBeans类。 XMLBeans提供了46种java类型，反映了XML
模式规范中定义的46种内置类型。例如，W3C定义了一个xsd:string类型，XMLBeans就提供了一个XmlString数据类型与之对应。</p>
<p>　　在weather_latlong.xsd
模式创建的Weather接口为xsd:float类型的局部元素Visibility声明了如下的两种方法：</p>
<pre class="code">float getVisibility();</pre>
<p>　　和</p>
<pre class="code">org.apache.xmlbeans.XmlFloat xgetVisibility();</pre>
<p>　　对于46种java类型中的任何一种，XMLBeans
都提供了两种访问数据的方法。在此，一种方法为xsd:float返回了XmlFloat类型，而另一种方法为xsd:float返回了一个普通的java类型如float类型。</p>
<p>　　Xget形式的函数在性能上要优于get形式的函数，因为get形式的函数必须要把数据转化成为最合适的java类型。</p>
<p>　　当模式被编译后，模式类型的名称将会变得符合java的命名规则。换句话说，stock-quote这样的名称将变为StockQuote。另外，模式名称空间的URIs变成了模式生成的XMLBeans类型的包名。如果包含的模式没有声明目标名称空间，那么所有的java类都将放在noNamespace这个包中。当出现了类命名冲突时，生成的类名字后面将加上相应的数字——例如,
timeStamp3。</p>
<p>　　对于全局元素和属性，XMLBeans 模式编译器将分别生成名称以Document和Attribute结尾的接口。</p>
<p>　　对于在另一个元素或类型的声明中局部声明的命名类型，XMLBeans会在元素或类型接口中生成一个内部接口，形成嵌套结构。</p>
<p>　　考虑下面的employee.xsd 模式列表。</p>
<pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />
&lt;!-- This XML Schema describes Employee's<br />
&nbsp;&nbsp;&nbsp; Jobstatus --&gt;<br />
&lt;xsd:schema<br />
xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;<br />
&lt;xsd:complexType name="Employee"&gt;<br />
&nbsp; &lt;xsd:sequence&gt;<br />
&nbsp;&nbsp; &lt;xsd:element name="Jobstatus"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;xsd:simpleType&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsd:restriction base="xsd:NMTOKEN"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsd:enumeration value="fullTime"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsd:enumeration value="hourly"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsd:restriction&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/xsd:simpleType&gt;<br />
&nbsp;&nbsp; &lt;/xsd:element&gt;<br />
&nbsp; &lt;/xsd:sequence&gt;<br />
&lt;/xsd:complexType&gt;<br />
&lt;/xsd:schema&gt;<br />
</pre>
<p>　　因此，XMLBeans在元素Employee的接口中生成了一个内部接口Jobstatus，嵌套在了Employee接口中。</p>
<pre class="code">public interface Employee<br />
&nbsp; extends org.apache.xmlbeans.XmlObject<br />
{<br />
...<br />
public interface Jobstatus<br />
&nbsp;&nbsp; extends org.apache.xmlbeans.XmlNMTOKEN<br />
&nbsp; {<br />
&nbsp; }<br />
}<br />
</pre>
<p>　　Employee类在这里扩展了org.apache.xmlbeans.XmlObject，这是所有XMLBeans类型的基础接口。所有的内置模式类型，用户定义类型和派生的模式类型都从XmlObject中继承而来。</p>
<h3>使用XMLBeans类解除封送XML文件</h3>
<p>　　下面的一小段weather_unmarshal.java代码阐明了怎样使用XMLBeans类从weatherInput.xml.文件的XML文档中获取天气信息。</p>
<pre class="code">String filePath = "weatherInput.xml";<br />
java.io.File inputXMLFile =<br />
new java.io.File(filePath);<br />
// Parse XML Document.<br />
WeatherDocument weatherDoc =<br />
WeatherDocument.Factory.parse(inputXMLFile);<br />
// Get object reference of root element Weather.<br />
WeatherDocument.Weather weatherElement =<br />
weatherDoc.getWeather();<br />
</pre>
<p>　　通过调用WeatherDocument.Factory.parse(File)方法来解析XML文件，该方法返回一个WeatherDocument对象。随后对weatherDocument对象调用getWeather()方法来获取根元素Weather的对象引用。</p>
<p>　　要获得Weather元素的内容，简单调用weatherElement的相应的get方法，它将直接映射模式定义的元素和属性名称：</p>
<pre class="code">// Call the appropriate 'get' methods of<br />
// weatherElement that<br />
// directly map to the element and attribute names<br />
// defined in the schema.<br />
Calendar timeStamp = weatherElement.getDatetime();<br />
System.out.println("Weather details of zipcode "<br />
+ weatherElement.getZipcode() + " at "<br />
+ timeStamp);<br />
System.out.println("Temperature is "<br />
+ weatherElement.getTemperature());<br />
System.out.println("Humidity is "<br />
+ weatherElement.getHumidity());<br />
System.out.println("Visibility is "<br />
+ weatherElement.getVisibility());<br />
</pre>
<p>　　输出的结果是：</p>
<pre class="code">Weather details of zipcode 92834-2345 at 2003-11-13T05:29:27-03:01<br />
Temperature is 85.3<br />
Humidity is 50.0<br />
Visibility is 5.5<br />
</pre>
<h3>模式声明多个全局元素时如何解除封送</h3>
<p>　　在上面的例子中，我们假设输入XML文档始终包含天气信息。然而，在实际中，由于weather_latlong.xsd文件通过声明两个全局元素（Weather和Latlong）同时描述了二者的详细信息，因此输入XML文档中可能包含天气信息也可能包含经纬度信息。。</p>
<p>　　有两种方法可以解析一个xml文档并将其绑定到相应XMLBeans类型的实例。在上述的例子中，我们用WeatherDocument.Factory.parse()方法解析XML文档。另外一种方式是使用XMLBeans内置的XmlObject类。</p>
<p>　　下面的一小段weather_unmarshal_xmlObject.java代码阐述了怎样使用XmlObject类获取xml实例文档中包含的天气和经纬度信息。</p>
<pre class="code">public static void main(String args[]) {<br />
try {<br />
if (args.length &lt; 1 ) {<br />
System.out.println("Usage : java "<br />
+"weather_unmarshal_xmlObject &lt;&lt;InputFilePath&gt;&gt;");<br />
return;<br />
}<br />
String filePath = args[0];<br />
java.io.File inputXMLFile<br />
&nbsp;&nbsp; = new java.io.File(filePath);<br />
XmlObject xmlObjExpected =<br />
XmlObject.Factory.parse(inputXMLFile);<br />
// Check document type of the object returned by<br />
// the call to XmlObject.Factory.parse() method.<br />
// If type of object returned is of<br />
//noNamespace.WeatherDocument, then input xml<br />
//document carries weather details of a location.<br />
if (xmlObjExpected instanceof<br />
noNamespace.WeatherDocument) {<br />
&nbsp; WeatherDocument weatherDoc =<br />
&nbsp;&nbsp;&nbsp; (noNamespace.WeatherDocument)xmlObjExpected;<br />
&nbsp; WeatherDocument.Weather weatherElement =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; weatherDoc.getWeather();<br />
&nbsp; Calendar timeStamp =<br />
&nbsp;&nbsp;&nbsp; weatherElement.getDatetime();<br />
&nbsp; System.out.println<br />
&nbsp;&nbsp;&nbsp; ("Weather details of zipcode "<br />
&nbsp;&nbsp;&nbsp; + weatherElement.getZipcode() + " at "<br />
&nbsp;&nbsp;&nbsp; + timeStamp&nbsp; + " : \n\n");<br />
&nbsp; System.out.println("Temperature is "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + weatherElement.getTemperature());<br />
&nbsp; System.out.println("Humidity is "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + weatherElement.getHumidity());<br />
&nbsp; System.out.println("Visibility is "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + weatherElement.getVisibility());<br />
// else if type of object returned is of<br />
// noNamespace.LatlongDocument, then input xml<br />
//document carries latlong details of a location.<br />
} else if(xmlObjExpected instanceof<br />
&nbsp;&nbsp;&nbsp; noNamespace.LatlongDocument) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LatlongDocument latLongDoc =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (noNamespace.LatlongDocument)xmlObjExpected;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LatlongDocument.Latlong latLongElement =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; latLongDoc.getLatlong();<br />
&nbsp;&nbsp;&nbsp; System.out.println<br />
&nbsp;&nbsp;&nbsp;("Latlong details of zipcode "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + latLongElement.getZipcode() + " : \n\n");<br />
&nbsp;&nbsp;&nbsp; System.out.println("Latitude is "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + latLongElement.getLatitude());<br />
&nbsp;&nbsp;&nbsp; System.out.println("Longitude is "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + latLongElement.getLongitude());<br />
// else input xml document is well formed , but<br />
// doesn't conform to weather_latlong.xsd schema<br />
// file.<br />
} else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Input xml document "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; + "doesn't conform to weather_latlong.xsd");<br />
}<br />
} catch (Exception e) {<br />
&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp; }<br />
}<br />
}<br />
</pre>
<p>　　为了获得输入XML文档的内容,我们先检查XmlObject.Factory.parse()返回的对象的文档类型，然后把返回的对象转化为相应的文档类型，以供稍后处理。另一段有趣的代码是最后的else代码块，它将处理格式良好的XML文档不符合weather_latlong.xsd模式的情况。</p>
<h3>创建一个新的XML文档</h3>
<p>　　下面的一小段latlong_marshal.java代码阐述了如何使用XMLBeans生成的类创建一个包含经纬度信息的新xml实例文档。</p>
<pre class="code">LatlongDocument latLongDoc;<br />
LatlongDocument.Latlong latLongElement;<br />
XmlOptions xmlOptions;<br />
// LatlongDocument.Factory.newInstance() creates<br />
// and returns a LatlongDocument object.<br />
latLongDoc= LatlongDocument.Factory.newInstance();<br />
// addNewLatlong() method is called on the<br />
// document object to create and add a new<br />
// LatLong Element to document.<br />
latLongElement = latLongDoc.addNewLatlong();<br />
</pre>
<p>　　LatlongDocument.Factory.newInstance()创建了一个LatlongDocument对象并且返回该对象。随后对文档对象调用addNewLatlong()方法创建并向文档增加一个新的LatLong元素。</p>
<p>　　要向LatLong元素添加数据，&nbsp;简单调用latLongElement的相应的Set方法即可，它将直接映射模式中定义的的元素和属性名称。</p>
<pre class="code">latLongElement.setZipcode("91023");<br />
latLongElement.setLatitude("33.8792");<br />
latLongElement.setLongitude("117.8974");<br />
</pre>
<p>　　最后的代码段将LatLong元素的当前状态写到了标准的输出流中。</p>
<pre class="code">xmlOptions = new XmlOptions();<br />
&nbsp;<br />
// Requests use of whitespace for easier reading<br />
xmlOptions.setSavePrettyPrint();<br />
&nbsp;<br />
// Requests that nested levels of the xml<br />
// document to be indented by multiple of 4<br />
// whitespace characters<br />
xmlOptions.setSavePrettyPrintIndent(4);<br />
&nbsp;<br />
String xmlStr = latLongDoc.xmlText(xmlOptions);<br />
&nbsp;<br />
// Writes the current state of the LatLong<br />
// element to a standard output stream<br />
&nbsp;<br />
System.out.println("XML Instance Document is : "<br />
&nbsp; + "\n\n\n " + xmlStr );<br />
</pre>
<p>　　xmlText方法用可选的xmlOptions对象控制它的行为。setSavePrettyPrint()方法要求使用空白符，以便方便阅读，而setSavePrettyPrintIndent(4)方法要求在嵌套的XML文档中首行缩进四的倍数个空白符。</p>
<p>　　输出的结果是：</p>
<pre class="code">XML Instance Document is :<br />
&nbsp;<br />
&lt;Latlong Zipcode="91023"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;Latitude&gt;33.8792&lt;/Latitude&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;Longitude&gt;117.8974&lt;/Longitude&gt;<br />
&lt;/Latlong&gt;<br />
</pre>
<h3>性能优势</h3>
<p>　　与DOM的不同之处是，XMLBeans没有采用解除封送整个xml文档和为每个xml文档结点提供一个对象的方法。使用XMLBeans，只在需要时进行封送和解除封送，因此对于你从来没有查看过的代码，它们是不会被封送和解除封送的。这提高了XMLBeans解决方案的性能。</p>
<p>　　XMLBeans也提供高效的xget版本的函数访问XML模式内置数据类型。</p>
<h3>验证</h3>
<p>　　分配给内置XMLBeans
java类型的值将按照其表示的模式类型的规则进行验证。例如，如果将一个符合条件的名称分配给一个XmlQName数据类型时，如果该名称的前缀不能解析为任何URI，将会抛出XmlValueOutOfRange异常。</p>
<p>　　当xml文档第一次被解析时，将根据模式定义验证其中包含的数据。更有意思的是，无论何时通过XMLBeans生成的java类处理xml文档时，XMLBeans系统将确保遵守模式约束条件。</p>
<h3>其他特性</h3>
<p>　　XMLBeans对象是可序列化的，因而，可以通过RMI边界传送，也能够很容易的从XML
字符流和字节流中提取，并保存回去。XMLBeans也具有配置功能，因而可以将XML名称映射到Java名称。这样将避免在XML的名称发生变化时重新编写Java代码。由于篇幅限制我们在此不做过多讨论。</p>
<h3>结束语</h3>
<p>　　<a href="http://xmlbeans.apache.org/" target="_blank">XMLBeans</a>
提出了底层XML数据的对象视图，同时还能访问原始的XML信息集合。通过递增的解除封送xml数据和高效的访问XML模式内置数据类型的方法，XMLBeans交付了较好的性能。下面两种特性几乎百分之百的支持XML
模式，并在操作数据期间定时验证XML数据，从而使XMLBeans非常适用于XML-Java 数据绑定。现在web
services、BPEL、BPML、基于规则的XML数据转换引擎等实现，都用到了该数据绑定技术。</p>
<h3>资源</h3>
<ul>
    <li>本文的 <a href="http://www.onjava.com/onjava/2004/07/28/examples/XMLJavaDataBindingUsingXMLBeans.zip" target="_blank">示例代码</a>包含了模式、Java代码和XML文件。
    </li>
    <li><a href="http://xmlbeans.apache.org/" target="_blank">XMLeans主页</a></li>
</ul>
<!--文章其他信息-->
<div class="dot001"><img alt="" src="http://www.blogjava.net/images/_.gif" height="1" width="100%" /></div>
<table border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr valign="bottom">
            <td colspan="2" height="20">&nbsp;<span class="h2b">作者简介</span></td>
        </tr>
        <tr>
            <td align="center" valign="top" width="0"><br />
            </td>
            <td><a href="http://www.onjava.com/pub/au/1355" target="_blank"><em>Hetal C.
            Shah</em></a> 是一名IT顾问，擅长与Internet相关的技术。</td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/185149.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-10 17:32 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185149.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Digester简化XML文档处理 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185132.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 10 Mar 2008 09:09:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185132.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/185132.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185132.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/185132.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/185132.html</trackback:ping><description><![CDATA[<p style="text-indent: 2em;">http://hi.baidu.com/python811022/blog/item/5ed31dcee519243ab700c86c.html</p>
<p style="text-indent: 2em;"><br />
</p>
<p style="text-indent: 2em;">Digester框架属于Jakarta
Commons，它以规则和模式为基础处理XML文档。与SAX和DOM之类的标准API相比，Digester不涉及太多的细节问题，非常适合于对XML文档进行简单的处理。</p>
<p style="text-indent: 2em;">在Java和XML开发中，一个常见的任务是把XML文档转换成对应的Java
Bean对象的层次结构。人们经常用标准的SAX和DOM
API来完成这个任务。虽然这两种API都很强大和灵活，但对于某些简单的任务来说，它们显得操作层次太低，也就是说，涉及了太多的细节问题。Jakarta
Digester框架能够很好地满足这类场合的需要。</p>
<center><font color="#000099"><strong>Digester框架简介 </strong></font></center>
<p style="text-indent: 2em;">Jakarta的Digester框架从Struts框架发展而来，原先被用来处理struts-config.xml配置文件，但很快人们认识到它有着更广泛的用途，把它转入了Jakarta
Commons项目。Jakarta
Commons的目标是提供一个&#8220;可重用Java组件的仓库&#8221;。Digester最新的版本是1.3，于2002年8月13日发布。</p>
<p style="text-indent: 2em;">Digester框架允许开发者指定一组动作，当解析器在XML文档中发现某些特定的简单模式时动作被执行。Digester框架带有10个预定义的规则（Rule），涵盖了unmarshalling
XML（例如创建Bean或设置Bean属性）的大多数需求（
marshalling的原意是指&#8220;配制整齐，编组列车&#8221;，marshalling是在内存中为Java对象生成XML描述文档的过程，而unmarshalling是指把XML形式的描述转换到可用Java代码操作的对象的过程，我们称之为&#8220;反配制&#8221;），但必要时用户可以定义和实现自己的规则。</p>
<p style="text-indent: 2em;">在本文的例子中，我们将反配制下面这个XML文档：</p>
<p style="text-indent: 2em;">
<table bordercolordark="#ffffff" bordercolorlight="#000000" align="center" border="1" cellpadding="2" cellspacing="0" width="550">
    <tbody>
        <tr>
            <td bgcolor="#e6e6e6">
            <pre>&lt;?xml version="1.0"?&gt;<br />
            &lt;catalog library="somewhere"&gt;<br />
            &lt;book&gt;<br />
            &lt;author&gt;Author 1&lt;/author&gt;<br />
            &lt;title&gt;Title 1&lt;/title&gt;<br />
            &lt;/book&gt;<br />
            &lt;book&gt;<br />
            &lt;author&gt;Author 2&lt;/author&gt;<br />
            &lt;title&gt;His One Book&lt;/title&gt;<br />
            &lt;/book&gt;<br />
            &lt;magazine&gt;<br />
            &lt;name&gt;Mag Title 1&lt;/name&gt;<br />
            &lt;article page="5"&gt;<br />
            &lt;headline&gt;Some Headline&lt;/headline&gt;<br />
            &lt;/article&gt;<br />
            &lt;article page="9"&gt;<br />
            &lt;headline&gt;Another Headline&lt;/headline&gt;<br />
            &lt;/article&gt;<br />
            &lt;/magazine&gt;<br />
            &lt;book&gt;<br />
            &lt;author&gt;Author 2&lt;/author&gt;<br />
            &lt;title&gt;His Other Book&lt;/title&gt;<br />
            &lt;/book&gt;<br />
            &lt;magazine&gt;<br />
            &lt;name&gt;Mag Title 2&lt;/name&gt;<br />
            &lt;article page="17"&gt;<br />
            &lt;headline&gt;Second Headline&lt;/headline&gt;<br />
            &lt;/article&gt;<br />
            &lt;/magazine&gt;<br />
            &lt;/catalog&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p style="text-indent: 2em;">下面是Bean的代码。注意使用Digester框架时，Bean类必须定义成public。</p>
<p style="text-indent: 2em;">
<table bordercolordark="#ffffff" bordercolorlight="#000000" align="center" border="1" cellpadding="2" cellspacing="0" width="550">
    <tbody>
        <tr>
            <td bgcolor="#e6e6e6">
            <pre>import java.util.Vector;<br />
            public class Catalog {<br />
            private Vector books;<br />
            private Vector magazines;<br />
            public Catalog() {<br />
            books = new Vector();<br />
            magazines = new Vector();<br />
            }<br />
            public void addBook( Book rhs ) {<br />
            books.addElement( rhs );<br />
            }<br />
            public void addMagazine( Magazine rhs ) {<br />
            magazines.addElement( rhs );<br />
            }<br />
            public String toString() {<br />
            String newline = System.getProperty( "line.separator" );<br />
            StringBuffer buf = new StringBuffer();<br />
            buf.append( "--- Books ---" ).append( newline );<br />
            for( int i=0; i&lt;books.size(); i++ ){<br />
            buf.append( books.elementAt(i) ).append( newline );<br />
            }<br />
            buf.append( "--- Magazines ---" ).append( newline );<br />
            for( int i=0; i&lt;magazines.size(); i++ ){<br />
            buf.append( magazines.elementAt(i) ).append( newline );<br />
            }<br />
            return buf.toString();<br />
            }<br />
            }<br />
            //===================================================<br />
            public class Book {<br />
            private String author;<br />
            private String title;<br />
            public Book() {}<br />
            public void setAuthor( String rhs ) { author = rhs; }<br />
            public void setTitle(  String rhs ) { title  = rhs; }<br />
            public String toString() {<br />
            return "Book: Author='" + author + "' Title='" + title + "'";<br />
            }<br />
            }<br />
            //===================================================<br />
            import java.util.Vector;<br />
            public class Magazine {<br />
            private String name;<br />
            private Vector articles;<br />
            public Magazine() {<br />
            articles = new Vector();<br />
            }<br />
            public void setName( String rhs ) { name = rhs; }<br />
            public void addArticle( Article a ) {<br />
            articles.addElement( a );<br />
            }<br />
            public String toString() {<br />
            StringBuffer buf = new StringBuffer( "Magazine: Name='" + name + "' ");<br />
            for( int i=0; i&lt;articles.size(); i++ ){<br />
            buf.append( articles.elementAt(i).toString() );<br />
            }<br />
            return buf.toString();<br />
            }<br />
            }<br />
            //===================================================<br />
            public class Article {<br />
            private String headline;<br />
            private String page;<br />
            public Article() {}<br />
            public void setHeadline( String rhs ) { headline = rhs; }<br />
            public void setPage(     String rhs ) { page     = rhs; }<br />
            public String toString() {<br />
            return "Article: Headline='" + headline + "' on page='" + page + "' ";<br />
            }<br />
            }</pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<br />
<br />
<table style="table-layout: fixed;">
    <tbody>
        <tr>
            <td>
            <div class="cnt" id="blog_text">
            <center><font color="#000099"><strong>指定模式和规则 </strong></font></center>
            <p style="text-indent: 2em;">Digester框架以模式（Pattern）和规则（Rule）为基础处理输入的XML。模式必须与XML元素匹配，包括其名字和在文档树内的位置。描述匹配模式的语法类似于XPath匹配模式，例如：catalog模式匹配顶层的<ccid_code></ccid_code>&lt;catalog&gt;元素，catalog/book模式匹配直接嵌套在&lt;catalog&gt;元素内的&lt;book&gt;元素（但不匹配文档内其他位置的&lt;book&gt;元素）。</p>
            <p style="text-indent: 2em;">所有的模式都必须指定其完整名称——从根元素开始的完整路径。唯一的例外是包含通配符（&#8220;*&#8221;）的模式，例如*/name模式匹配XML文档内任何位置的<ccid_code></ccid_code>&lt;name&gt;元素。但是根元素不必特别指出，因为所有的路径都是从根元素开始的绝对路径。</p>
            <p style="text-indent: 2em;">当Digester发现一个指定的模式，它就执行关联的任务。由此可见，Digester框架显然与SAX解析器有着密切的关系（实际上，Digester类实现了org.xml.sax.ContentHandler，并维护着解析栈）。所有在Digester中使用的规则必须扩展org.apache.commons.digester.Rule，后者本身提供了一些类似于SAX的ContentHandler回调函数的方法。例如，当遇到匹配元素的开始标记和结束标记时，begin()方法和end()方法将分别被调用。</p>
            <p style="text-indent: 2em;">一旦遇到匹配元素的内容，body()方法被调用；最后被调用的方法是finish()，这个方法在匹配元素的结束标记处理完毕之后被调用，用来执行可能需要的事后清理任务。然而，大多数时候我们不必关注这些方法，因为框架提供的标准规则很可能已经提供了所有必需的功能。</p>
            <p style="text-indent: 2em;">要反配制一个文档，首先创建一个org.apache.commons.digester.Digester类的实例，如果必要的话，进行一些配置操作，指定必需的模式和规则，最后向parse()方法传递一个XML文件的引用。下面的DigesterDriver示范了这一处理过程（必须在命令行上指定输入XML文档的名称）。</p>
            <p style="text-indent: 2em;"><ccid_nobr></ccid_nobr>
            <table bordercolordark="#ffffff" bordercolorlight="#000000" align="center" border="1" cellpadding="2" cellspacing="0" width="550">
                <tbody>
                    <tr>
                        <td class="code" bgcolor="#e6e6e6">
                        <pre><ccid_code></ccid_code>import org.apache.commons.digester.*;<br />
                        import java.io.*;<br />
                        import java.util.*;<br />
                        public class DigesterDriver {<br />
                        public static void main( String[] args ) {<br />
                        try {<br />
                        Digester digester = new Digester();<br />
                        digester.setValidating( false );<br />
                        digester.addObjectCreate( "catalog", Catalog.class );<br />
                        digester.addObjectCreate( "catalog/book", Book.class );<br />
                        digester.addBeanPropertySetter( "catalog/book/author", "author" );<br />
                        digester.addBeanPropertySetter( "catalog/book/title", "title" );<br />
                        digester.addSetNext( "catalog/book", "addBook" );<br />
                        digester.addObjectCreate( "catalog/magazine", Magazine.class );<br />
                        digester.addBeanPropertySetter( "catalog/magazine/name", "name" );<br />
                        digester.addObjectCreate( "catalog/magazine/article", Article.class );<br />
                        digester.addSetProperties( "catalog/magazine/article", "page", "page" );<br />
                        digester.addBeanPropertySetter( "catalog/magazine/article/headline" ); <br />
                        digester.addSetNext( "catalog/magazine/article", "addArticle" );<br />
                        digester.addSetNext( "catalog/magazine", "addMagazine" );<br />
                        File input = new File( args[0] );<br />
                        Catalog c = (Catalog)digester.parse( input );<br />
                        System.out.println( c.toString() );<br />
                        } catch( Exception exc ) {<br />
                        exc.printStackTrace();<br />
                        }<br />
                        }<br />
                        }</pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            </p>
            <p style="text-indent: 2em;">在上面的代码中，我们首先创建了Digester类的一个实例digester，然后指定它不要用DTD验证XML文档的合法性——这是因为我们没有为XML文档定义DTD。接下来，我们指定了模式和关联的规则：ObjectCreateRule创建指定类的一个实例，并将它压入解析栈。SetPropertiesRule把Bean属性设置成当前XML元素的属性值——规则的第一个参数是XML属性的名称，第二个参数是Bean属性的名称。</p>
            <p style="text-indent: 2em;">SetPropertiesRule获取的是XML属性的值，而BeanPropertySetterRule获取的是位于当前元素内的原始字符数据值。使用BeanPropertySetterRule时不必指定要设置的Bean属性名字，默认是当前XML元素的名称。在上面的例子中，在匹配catalog/magazine/article/headline模式的规则定义中使用的就是默认值。最后，SetNextRule弹出解析栈顶部的对象，并把该对象传递给它下面对象的指定名称的方法——通常用来把一个配置完毕的Bean插入父对象。</p>
            <p style="text-indent: 2em;">注意，我们可以为同一个模式注册多个规则。如果注册了多个规则，则这些规则按照它们被加入到Digester的次序执行，例如，如果要处理catalog/magazine/article的<article></article>元素，我们首先创建合适的article
            Bean，然后设置page属性，最后弹出完成后的article Bean，并把它插入magazine。</p>
            <center><font color="#000099"><strong>调用任意方法 </strong></font></center>
            <p style="text-indent: 2em;">我们不仅可以设置Bean的属性，而且还可以调用堆栈内对象的任意方法。这通过CallMethodRule完成，我们只需指定方法名字，如有必要，再说明调用的参数类型和数量。CallParamRule用来定义传递给被调用函数的参数值，参数值可以从当前XML元素的命名的属性获取，也可以从当前元素包含的原始字符数据获取。例如，在前面实现DigesterDriver的例子中，我们可以不用BeanPropertySetterRule，而是通过显式调用属性的set方法达到同样的目的：</p>
            <p style="text-indent: 2em;"><ccid_nobr></ccid_nobr>
            <table bordercolordark="#ffffff" bordercolorlight="#000000" align="center" border="1" cellpadding="2" cellspacing="0" width="550">
                <tbody>
                    <tr>
                        <td class="code" bgcolor="#e6e6e6">
                        <pre><ccid_code></ccid_code>digester.addCallMethod( "catalog/book/author", "setAuthor", 1 );<br />
                        digester.addCallParam( "catalog/book/author", 0 );</pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            </p>
            <p style="text-indent: 2em;">上面的第一行代码给出了要调用的方法（即setAuthor()），以及该调用需要的参数数量（即1）。第二行代码的意思是从<author></author>元素包含的字符数据获取函数参数的值，把它作为参数数组的第一个传入（即索引是0的数组元素）。如果我们指定了XML元素属性的名称（例如digester.addCallParam(
            "catalog/book/author", 0, "author" );），则参数值将从当前元素的相应属性值获取。</p>
            <p style="text-indent: 2em;">这里必须注意的是，&#8220;digester.addCallMethod( "pattern",
            "methodName", 0
            );&#8221;这个语句不是指定了一个不带参数的方法调用，而是指定了带有一个参数的方法调用，它的值就是当前XML元素的字符数据！这样，我们又有了另一种替代BeanPropertySetterRule的办法：</p>
            <p style="text-indent: 2em;"><ccid_nobr></ccid_nobr>
            <table bordercolordark="#ffffff" bordercolorlight="#000000" align="center" border="1" cellpadding="2" cellspacing="0" width="550">
                <tbody>
                    <tr>
                        <td class="code" bgcolor="#e6e6e6">
                        <pre><ccid_code></ccid_code>digester.addCallMethod( "catalog/book/author", "setAuthor", 0 );</pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            </p>
            <p style="text-indent: 2em;">如果要调用一个确实没有参数的方法，必须采用如下形式：digester.addCallMethod(
            "pattern", "methodName" );。</p>
            <center><font color="#000099"><strong>标准规则概要 </strong></font></center>
            <p style="text-indent: 2em;">下面简要说明所有标准规则。</p>
            <p style="text-indent: 2em;"><strong>创建 </strong></p>
            <p style="text-indent: 2em;">ObjectCreateRule：利用指定类的默认构造函数，创建该类的一个对象，并把对象压入栈。当元素处理结束时，对象被弹出。被实例化的类可通过class对象或类的全称给出。</p>
            <p style="text-indent: 2em;">FactoryCreateRule：利用指定的工厂类创建一个对象，把对象压入栈。对于没有提供默认构造函数的类，这一规则很有用。用于该规则的工厂类必须实现org.apache.commons.digester.ObjectCreationFactory接口。</p>
            <p style="text-indent: 2em;"><strong>设置属性 </strong></p>
            <p style="text-indent: 2em;">SetPropertiesRule：利用指定名称的XML元素属性值，设置顶层Bean的一个或者多个指定名称的属性。XML元素的属性名称和Bean的属性名称以String[]数组形式传入该规则（通常用来处理<article page="10"></article>之类的结构）。</p>
            <p style="text-indent: 2em;">BeanPropertySetterRule：把顶层Bean的指定名称的属性设置成当前XML元素包含的字符数据。（通常用来处理<ccid_code></ccid_code>&lt;page&gt;10&lt;/page&gt;之类的结构）。</p>
            <p style="text-indent: 2em;">SetPropertyRule：设置顶层Bean的一个属性。无论是Bean属性的名称，还是赋予该属性的值，都在当前XML元素中以属性的形式指定，例如：<ccid_code></ccid_code>&lt;article
            key="page" value="10" /&gt;。</p>
            <p style="text-indent: 2em;"><strong>管理父/子关系 </strong></p>
            <p style="text-indent: 2em;">SetNextRule：弹出栈顶的对象，把它传递给紧接其下的另一个对象的指定名称的方法。通常用来把一个已经初始化的Bean插入到父对象。</p>
            <p style="text-indent: 2em;">SetTopRule：把栈里面上数第二的对象传递给顶层的对象。当子对象提供了一个setParenet方法时，这一规则很有用。</p>
            <p style="text-indent: 2em;">SetRootRule：调用栈底对象的一个方法，并把栈顶的对象作为参数传入。</p>
            <p style="text-indent: 2em;"><strong>调用任意方法 </strong></p>
            <p style="text-indent: 2em;">CallMethodRule：调用顶层Bean的指定名称的方法。被调用的方法可以有任意多个参数，参数的值通过后继的CallParamRule给出。</p>
            <p style="text-indent: 2em;">CallParamRule：表示方法调用的参数。参数的值或者取自指定名称的XML元素的属性，或者是当前元素包含的原始字符数据。这个规则要求用一个整数指定它在参数列表中的位置。</p>
            <center><font color="#000099"><strong>通过XML指定规则 </strong></font></center>
            <p style="text-indent: 2em;">在前面的内容中，我们用程序代码的方式指定模式和规则，这些模式和规则都是在编译的时候就已经确定，虽然从概念上来讲比较简单，但却不能说尽善尽美：Digester框架的总体目标是在运行时识别和处理各种数据结构，但如果我们用编程的方法指定模式和规则，则所有行为在编译时已经固定！如果Java源程序中包含了大量固定的字符串，通常意味着程序在执行某些配置操作，这部分操作可以被（或许是应该被）延迟到运行时进行。</p>
            <p style="text-indent: 2em;">org.apache.commons.digester.xmlrules包解决了这个问题。这个包提供了一个DigesterLoader类，它能够从XML文档读取模式/规则对，返回配置好的Digester对象。用来配置Digester对象的XML文档必须遵从digester-rules.dtd，这个DTD是xmlrules包的一部分。</p>
            <p style="text-indent: 2em;">下面就是本文例子的配置文件rules.xml。有几点必须说明。</p>
            <p style="text-indent: 2em;">首先，模式可以用两种方式指定：或者使用<ccid_code></ccid_code>&lt;pattern&gt;元素，或者通过代表规则的XML元素的属性。这两种办法可以混合使用，且<ccid_code></ccid_code>&lt;pattern&gt;元素是可以嵌套的。其次，<ccid_code></ccid_code>&lt;alias&gt;元素和&lt;set-properties-rule&gt;一起使用，用来把XML属性映射到Bean属性。最后，就当前发行的Digester软件包而言，我们不能在配置文件中指定BeanPropertySetterRule，正如前面所介绍的，我们用CallMethodRule来达到同样的目标。</p>
            <p style="text-indent: 2em;"><ccid_nobr></ccid_nobr>
            <table bordercolordark="#ffffff" bordercolorlight="#000000" align="center" border="1" cellpadding="2" cellspacing="0" width="550">
                <tbody>
                    <tr>
                        <td class="code" bgcolor="#e6e6e6">
                        <pre><ccid_code></ccid_code>&lt;?xml version="1.0"?&gt;<br />
                        &lt;digester-rules&gt;<br />
                        &lt;object-create-rule pattern="catalog" classname="Catalog" /&gt;<br />
                        &lt;set-properties-rule pattern="catalog" &gt;<br />
                        &lt;alias attr-name="library" prop-name="library" /&gt;<br />
                        &lt;/set-properties-rule&gt;<br />
                        &lt;pattern value="catalog/book"&gt;<br />
                        &lt;object-create-rule classname="Book" /&gt;<br />
                        &lt;call-method-rule pattern="author" methodname="setAuthor"<br />
                        paramcount="0" /&gt;<br />
                        &lt;call-method-rule pattern="title" methodname="setTitle" <br />
                        paramcount="0" /&gt;<br />
                        &lt;set-next-rule methodname="addBook" /&gt;<br />
                        &lt;/pattern&gt;<br />
                        &lt;pattern value="catalog/magazine"&gt;<br />
                        &lt;object-create-rule classname="Magazine" /&gt;<br />
                        &lt;call-method-rule pattern="name" methodname="setName" paramcount="0" /&gt;<br />
                        &lt;pattern value="article"&gt;<br />
                        &lt;object-create-rule classname="Article" /&gt;<br />
                        &lt;set-properties-rule&gt;<br />
                        &lt;alias attr-name="page" prop-name="page" /&gt;<br />
                        &lt;/set-properties-rule&gt;    <br />
                        &lt;call-method-rule pattern="headline" methodname="setHeadline" <br />
                        paramcount="0" /&gt;<br />
                        &lt;set-next-rule methodname="addArticle" /&gt;<br />
                        &lt;/pattern&gt;<br />
                        &lt;set-next-rule methodname="addMagazine" /&gt; <br />
                        &lt;/pattern&gt;<br />
                        &lt;/digester-rules&gt;</pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            </p>
            <p style="text-indent: 2em;">现在，所有实际的操作都转移到了Digester和DigesterLoader类，XmlRulesDriver类就变得相当简单。运行下面的XmlRulesDriver时，在第一个命令行参数中指定目录文档的名字，在第二个参数中指定rules.xml（注意，DigesterLoader不是从File或者org.xml.sax.InputSource读取rules.xml文件，而是要求指定一个URL，因此，下面代码中File引用被转换成了等价的URL）。</p>
            <p style="text-indent: 2em;"><ccid_nobr></ccid_nobr>
            <table bordercolordark="#ffffff" bordercolorlight="#000000" align="center" border="1" cellpadding="2" cellspacing="0" width="550">
                <tbody>
                    <tr>
                        <td class="code" bgcolor="#e6e6e6">
                        <pre><ccid_code></ccid_code>import org.apache.commons.digester.*;<br />
                        import org.apache.commons.digester.xmlrules.*;<br />
                        import java.io.*;<br />
                        import java.util.*;<br />
                        public class XmlRulesDriver {<br />
                        public static void main( String[] args ) {<br />
                        try {<br />
                        File input = new File( args[0] );<br />
                        File rules = new File( args[1] );<br />
                        Digester digester = DigesterLoader.createDigester( rules.toURL() );<br />
                        Catalog catalog = (Catalog)digester.parse( input );<br />
                        System.out.println( catalog.toString() );<br />
                        } catch( Exception exc ) {<br />
                        exc.printStackTrace();<br />
                        }<br />
                        }<br />
                        }</pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            </p>
            <p style="text-indent: 2em;">结束语：本文对Jakarta Commons
            Digester的介绍就到这里结束。当然，还有许多内容这里尚未涉及。其中一个在这里忽略的主题是XML名称空间：Digester允许把规则定义成只能对某一个名称空间内定义的元素起作用。</p>
            <p style="text-indent: 2em;">另外，我们简单地提及了通过扩展Rule类开发定制规则的问题。按照习惯，Digester类提供了push()、peek()和pop()方法，使得开发者能够自由地直接操作解析栈。</p>
            <p style="text-indent: 2em;"><strong>参考：</strong></p>
            <p style="text-indent: 2em;">Jakarta Commons Digester Homepage</p>
            <p style="text-indent: 2em;">Jakarta Struts Homepage</p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<br />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/185132.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-10 17:09 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/10/185132.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>sitemesh简化布局 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184537.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 07 Mar 2008 08:30:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184537.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/184537.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184537.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/184537.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/184537.html</trackback:ping><description><![CDATA[http://www.iocblog.net/static/2007/566.html<br />
<br />
<br />
sitemesh一个系统，添加到Web应用中并促进模式HTML世界里的应用。<br />
可从http://www.opensymphony.com/sitemesh/
获得。<br />
<br />
sitemesh仅处理html的内容，象图象，PDF文件和下载这样的媒体是被忽略的。<br />
<br />
下面我们将用实例来介绍如何分析内容网页和装饰器如何被映射到网页。以及从sitemesh中获取<br />
信息的一些技术。<br />
<br />
整个事例用到login.jsp,
date.jsp , 索引的index.html <br />
配置文件decorators.xml<br />
装饰文件：window.jsp
,simple.jsp<br />
运用的样式表：style.css<br />
<br />
<br />
目标是把内容与布局分离，一达到简化布局，以及重用代码的作用。<br />
<br />
<br />
实质是使用simple.jsp,window.jsp以及提取的longin.jsp,date,jsp的实际主体来构成最终显示<br />
给用户的页面。<br />
<br />
<br />
布局主体是simple.jsp控制：它控制了网页的整体架构。<br />
<br />
&lt;%@
taglib uri=<font color="#00bb00" size="2">"sitemesh-decorator"</font><font color="#000000" size="2"> prefix=</font><font color="#00bb00" size="2">"decorator"</font><font color="#000000" size="2"> %&gt;<br />
&lt;%@ taglib
uri=</font><font color="#00bb00" size="2">"sitemesh-page"</font><font color="#000000" size="2"> prefix=</font><font color="#00bb00" size="2">"page"</font><font color="#000000" size="2">
%&gt;<br />
<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;&lt;decorator:title/&gt;&lt;/title&gt;<br />
&lt;link
rel=</font><font color="#00bb00" size="2">"stylesheet"</font><font color="#000000" size="2"> href=</font><font color="#00bb00" size="2">"decorators/style.css"</font><font color="#000000" size="2">&gt;<br />
&lt;decorator:head/&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
<br />
&lt;table
width=</font><font color="#00bb00" size="2">"100%"</font><font color="#000000" size="2">&gt;<br />
&lt;tr&gt;<br />
&lt;td <font color="#0000ff" size="2">class</font>=</font><font color="#00bb00" size="2">"title"</font><font color="#000000" size="2"> colspan=</font><font color="#00bb00" size="2">"2"</font><font color="#000000" size="2">&gt;<br />
&lt;decorator:title/&gt;<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;tr&gt;<br />
&lt;td
<font color="#0000ff" size="2">class</font>=</font><font color="#00bb00" size="2">"body"</font><font color="#000000" size="2"> valign=</font><font color="#00bb00" size="2">"top"</font><font color="#000000" size="2">&gt;<br />
&lt;decorator:body/&gt;<br />
&lt;/td&gt;<br />
&lt;td
valign=</font><font color="#00bb00" size="2">"top"</font><font color="#000000" size="2">&gt;<br />
<br />
&lt;page:applyDecorator name=</font><font color="#00bb00" size="2">"window"</font><font color="#000000" size="2"> page=</font><font color="#00bb00" size="2">"date.jsp"</font><font color="#000000" size="2">/&gt;<br />
<br />
&lt;page:applyDecorator name=</font><font color="#00bb00" size="2">"window"</font><font color="#000000" size="2"> title=</font><font color="#00bb00" size="2">"Disclaimer"</font><font color="#000000" size="2">&gt;<br />
This
site is not legally binding in any way. &lt;br&gt;<br />
All rights reserved. Elvis
has left the
building.<br />
&lt;/page:applyDecorator&gt;<br />
<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;tr&gt;<br />
&lt;td
<font color="#0000ff" size="2">class</font>=</font><font color="#00bb00" size="2">"footer"</font><font color="#000000" size="2"> valign=</font><font color="#00bb00" size="2">"top"</font><font color="#000000" size="2">
colspan=</font><font color="#00bb00" size="2">"2"</font><font color="#000000" size="2">&gt;<br />
&lt;b&gt;Title:&lt;/b&gt; &lt;decorator:title/&gt;
&lt;br&gt;<br />
&lt;b&gt;Author:&lt;/b&gt; &lt;decorator:getProperty
property=</font><font color="#00bb00" size="2">"meta.author"</font><font color="#000000" size="2">/&gt;
&lt;br&gt;<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;/table&gt;
<br />
<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
这个文件将在decorators.xml中被定义,其中给出装饰器的名称，位置，模式匹配。<br />
模式匹配可以使用通配符和正则表达式。在这对于simple.jsp使用了通配符*配置该<br />
装饰器用来匹配Web应用中的所有网页。有关正则表达式的相关内容请参看本博客
http:</font><font color="#0000aa" size="2"><em>//192.168.0.3/blog3/meiking_archive_2005_05_20_18525.html)。</em></font><font color="#000000" size="2"><br />
<br />
&lt;decorators&gt;<br />
<br />
&lt;decorator
name=</font><font color="#00bb00" size="2">"simple"</font><font color="#000000" size="2"> page=</font><font color="#00bb00" size="2">"/decorators/simple.jsp"</font><font color="#000000" size="2">&gt;<br />
&lt;pattern&gt;*&lt;/pattern&gt;<br />
&lt;/decorator&gt;<br />
<br />
<br />
&lt;/decorators&gt;<br />
<br />
上面我们讨论了装饰器设计模式，将装饰器和内容组合，下面我们来看看如何运用组合设计模式<br />
<br />
在页面中引用子装饰器
，子内容。<br />
让我们来看看一个子装饰器window.jsp<br />
<br />
<br />
&lt;%@ taglib uri=</font><font color="#00bb00" size="2">"sitemesh-decorator"</font><font color="#000000" size="2">
prefix=</font><font color="#00bb00" size="2">"decorator"</font><font color="#000000" size="2"> %&gt;<br />
&lt;table <font color="#0000ff" size="2">class</font>=</font><font color="#00bb00" size="2">"window"</font><font color="#000000" size="2">&gt;<br />
&lt;tr&gt;<br />
&lt;th&gt;&lt;img src=</font><font color="#00bb00" size="2">"decorators/snazzy.gif"</font><font color="#000000" size="2">&gt;&lt;decorator:title/&gt;&lt;/th&gt;<br />
&lt;/tr&gt;<br />
&lt;tr&gt;<br />
&lt;td&gt;<br />
&lt;decorator:body/&gt;<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
在回头看看simple.jsp在其中我们已经包含了window.jsp的运用，我们把他运用到了date.jsp<br />
上，运用样式表date.jsp的主体内容应该会出现在最终显示页面的右上角。<br />
<br />
在decorators.xml中被定义<br />
&lt;decorators&gt;<br />
<br />
&lt;decorator
name=</font><font color="#00bb00" size="2">"window"</font><font color="#000000" size="2"> page=</font><font color="#00bb00" size="2">"/decorators/window.jsp"</font><font color="#000000" size="2">/&gt;<br />
<br />
&lt;/decorators&gt;<br />
<br />
<br />
<br />
现在我们可以给出完整的decorators.xml了：<br />
&lt;decorators&gt;<br />
<br />
&lt;decorator
name=</font><font color="#00bb00" size="2">"simple"</font><font color="#000000" size="2"> page=</font><font color="#00bb00" size="2">"/decorators/simple.jsp"</font><font color="#000000" size="2">&gt;<br />
&lt;pattern&gt;*&lt;/pattern&gt;<br />
&lt;/decorator&gt;<br />
<br />
&lt;decorator
name=</font><font color="#00bb00" size="2">"window"</font><font color="#000000" size="2"> page=</font><font color="#00bb00" size="2">"/decorators/window.jsp"</font><font color="#000000" size="2">/&gt;<br />
<br />
&lt;/decorators&gt;<br />
在装饰器中拥有一个技术：sitemesh有一个pageparser对象，它通过内容网页获取输出的内容<br />
并把它解析到一个page对象中<br />
假如一个内容网页有如下列标题：<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;Please
login&lt;/title&gt;<br />
&lt;meta name=</font><font color="#00bb00" size="2">"author"</font><font color="#000000" size="2"> content=</font><font color="#00bb00" size="2">"Homer Simpson"</font><font color="#000000" size="2">&gt;<br />
&lt;/head&gt;<br />
...<br />
装饰器使用JSP标识符可以访问title属性，也能访问auther属性<br />
让我们看看simple是如何做的：<br />
&lt;%@
taglib uri=</font><font color="#00bb00" size="2">"sitemesh-decorator"</font><font color="#000000" size="2"> prefix=</font><font color="#00bb00" size="2">"decorator"</font><font color="#000000" size="2">
%&gt;<br />
...<br />
&lt;b&gt;Title:&lt;/b&gt; &lt;decorator:title/&gt;
&lt;br&gt;<br />
&lt;b&gt;Author:&lt;/b&gt; &lt;decorator:getProperty
property=</font><font color="#00bb00" size="2">"meta.author"</font><font color="#000000" size="2">/&gt;
&lt;br&gt;<br />
...<br />
<br />
<br />
简单的login.jsp,date.jsp页面<br />
<br />
login.jsp<br />
<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;Please
login&lt;/title&gt;<br />
&lt;meta name=</font><font color="#00bb00" size="2">"author"</font><font color="#000000" size="2"> content=</font><font color="#00bb00" size="2">"Homer Simpson"</font><font color="#000000" size="2">&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
&lt;form action=</font><font color="#00bb00" size="2">"#"</font><font color="#000000" size="2"> method=</font><font color="#00bb00" size="2">"post"</font><font color="#000000" size="2">&gt;<br />
<br />
&lt;input type=</font><font color="#00bb00" size="2">"hidden"</font><font color="#000000" size="2"> name=</font><font color="#00bb00" size="2">"section"</font><font color="#000000" size="2">
value=</font><font color="#00bb00" size="2">"store"</font><font color="#000000" size="2">&gt;<br />
<br />
Login Name:&lt;br&gt;<br />
&lt;input type=</font><font color="#00bb00" size="2">"text"</font><font color="#000000" size="2"> name=</font><font color="#00bb00" size="2">"loginname"</font><font color="#000000" size="2">&gt;&lt;br&gt;<br />
<br />
Password:&lt;br&gt;<br />
&lt;input type=</font><font color="#00bb00" size="2">"password"</font><font color="#000000" size="2">
name=</font><font color="#00bb00" size="2">"password"</font><font color="#000000" size="2">&gt;&lt;br&gt;<br />
<br />
&lt;input type=</font><font color="#00bb00" size="2">"submit"</font><font color="#000000" size="2"> value=</font><font color="#00bb00" size="2">"Login"</font><font color="#000000" size="2">&gt;<br />
<br />
&lt;/form&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
<br />
&lt;---------------------------------------------------&gt;<br />
date.jsp<br />
<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;Time
and date&lt;/title&gt;<br />
&lt;meta name=</font><font color="#00bb00" size="2">"author"</font><font color="#000000" size="2"> content=</font><font color="#00bb00" size="2">"Fred Flintstone"</font><font color="#000000" size="2">&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
Right now,
it's:&lt;br&gt;<br />
&lt;b&gt;&lt;%= <font color="#0000ff" size="2">new</font>
java.util.Date().toString() %&gt;&lt;/b&gt;<br />
&lt;/body&gt;
<br />
&lt;/html&gt;<br />
<br />
<br />
<br />
其他：1 对于页面可将公共代码重构到应用文件中来简化代码和重用代码<br />
2
对于页面可以使用样式表做的更彻底，使页面的主体部分不会淹没带大量的结构代码中<br />
</font><br />
<br />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/184537.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-07 16:30 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184537.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SiteMesh参考  zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184536.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 07 Mar 2008 08:28:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184536.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/184536.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/184536.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/184536.html</trackback:ping><description><![CDATA[http://www.iocblog.net/static/2007/571.html<br />
<br />
<div>
<h2>安装 </h2>
<ul>
    <li>首先从<a title="sitemesh" href="http://www.opensymphony.com/sitemesh/decorators.html">sitemesh</a>下载安装包，这里使用的是<span class="releaseName">2.2.1版本。</span> </li>
</ul>
<ul>
    <li>创建一个Web应用程序，这里我创建一个名为myapp的Web应用程序； </li>
</ul>
<ul>
    <li>复制sitemesh-2.2.1.jar文件到{myapp}/WEB-INF/lib目录下； </li>
</ul>
<ul>
    <li>编辑{myapp}/WEB-INF/web.xml文件 </li>
</ul>
<div>
<table class="zeroBorder" style="margin-left: 40px; width: 803px; height: 486px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />
            <br />
            &lt;web-app
            xmlns="http://java.sun.com/xml/ns/j2ee"<br />
            &nbsp;&nbsp;&nbsp;
            &nbsp;xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
            &nbsp;&nbsp;&nbsp;
            &nbsp;xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
            http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"<br />
            &nbsp;&nbsp;&nbsp;
            &nbsp;version="2.4"&gt;<br />
            &nbsp;&nbsp;&nbsp; <span style="color: #3333ff;">&lt;filter&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;
            &lt;filter-name&gt;sitemesh&lt;/filter-name&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;
            &lt;filter-class&gt;com.opensymphony.module.sitemesh.filter.PageFilter&lt;/filter-class&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;
            &lt;/filter&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp; </span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp; &lt;filter-mapping&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;
            &lt;filter-name&gt;sitemesh&lt;/filter-name&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;
            &lt;url-pattern&gt;/*&lt;/url-pattern&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;
            &lt;/filter-mapping&gt;</span><br />
            &nbsp;&nbsp;&nbsp;<br />
            &nbsp;&nbsp;&nbsp; &lt;session-config&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;session-timeout&gt;<br />
            &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 30<br />
            &nbsp;&nbsp;&nbsp; &lt;/session-timeout&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/session-config&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;welcome-file-list&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;welcome-file&gt;<br />
            &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; index.jsp<br />
            &nbsp;&nbsp;&nbsp; &lt;/welcome-file&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/welcome-file-list&gt;<br />
            &lt;/web-app&gt;<br />
            <br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<div style="margin-left: 40px;">添加蓝色高亮部分。<br />
</div>
<ul>
    <li>在{myapp}/WEB-INF/目录下创建decorators.xml文件，并且输入一下内容 </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 802px; height: 180px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;?xml version="1.0"
            encoding="ISO-8859-1"?&gt;<br />
            <br />
            &lt;decorators
            defaultdir="/decorators"&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;decorator name="main"
            page="main.jsp"&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;pattern&gt;/*&lt;/pattern&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/decorator&gt;<br />
            <br />
            &nbsp;&nbsp;&nbsp; &lt;decorator name="panel"
            page="panel.jsp"/&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;decorator name="printable"
            page="printable.jsp"/&gt;<br />
            &lt;/decorators&gt;<br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>安装完毕。 </li>
</ul>
<h2>例子1 </h2>
<ul>
    <li>在{myapp}/WEB-INF/decorators.xml文件中添加以下decorator </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 799px; height: 61px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;decorator name="mydecorator1"
            page="mydecorator1.jsp"&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;pattern&gt;/test1.jsp&lt;/pattern&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;/decorator&gt;
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>在{myapp}/decorators目录下添加mydecorator1.jsp文件，内容如下: </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 797px; height: 197px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;%@ taglib
            uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator"
            %&gt;<br />
            &lt;html&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;head&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;title&gt;My Site -
            &lt;decorator:title default="Welcome!" /&gt;&lt;/title&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;decorator:head /&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;/head&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;body&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;decorator:body /&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p&gt;This message is in
            /decorators/mydecorator1.jsp&lt;/p&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/body&gt;<br />
            &lt;/html&gt;<br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>在{myapp}目录下添加test1.jsp文件，内容如下： </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 797px; height: 214px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;%@page contentType="text/html"%&gt;<br />
            &lt;%@page
            pageEncoding="UTF-8"%&gt;<br />
            &lt;html&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;head&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;meta http-equiv="Content-Type" content="text/html;
            charset=UTF-8"&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;title&gt;This is test1&lt;/title&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/head&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;body&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;b&gt;This is test1&lt;/b&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/body&gt;<br />
            &lt;/html&gt;<br />
            <br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>打开浏览器，访问http://localhost:8080/myapp/test1.jsp，将会出现一下内容： </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 796px; height: 67px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">
            <p><span style="font-weight: bold;">This is test1</span><br />
            </p>
            <p>This message is in /decorators/mydecorator1.jsp
            </p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<br />
<h2>例子2 (decorator:getProperty
tag)<br />
</h2>
有时候，我们期望修改页面中某个有固定标记的片段，例如我们的jsp中有一个标记&lt;mytag&gt;...&lt;/mytag&gt;，此时可以用如下方法实现：<br />
<ul>
    <li>在{myapp}/WEB-INF/decorators.xml文件中添加以下decorator </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 795px; height: 61px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;decorator name="mydecorator2"
            page="mydecorator2.jsp"&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;pattern&gt;/test2.jsp&lt;/pattern&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;/decorator&gt;
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>在{myapp}/decorators目录下添加mydecorator2.jsp文件，内容如下: </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 791px; height: 299px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;%@ taglib
            uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator"
            %&gt;<br />
            <br />
            &lt;html&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;head&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;title&gt;My Site -
            &lt;decorator:title default="Welcome!" /&gt;&lt;/title&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;decorator:head /&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;/head&gt;<br />
            <br />
            &nbsp;&nbsp;&nbsp; &lt;body&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;decorator:body /&gt;<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #3333ff;">&lt;decorator:getProperty
            property="page.content1"/&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;decorator:getProperty
            property="page.content2"/&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;!-- do nothing --&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;decorator:getProperty
            property="page.content3"/&gt;</span><br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p&gt;This message
            is in /decorators/mydecorator2.jsp&lt;/p&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/body&gt;<br />
            &lt;/html&gt;<br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>在{myapp}目录下添加test2.jsp文件，内容如下： </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 790px; height: 265px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;%@page contentType="text/html"%&gt;<br />
            &lt;%@page
            pageEncoding="UTF-8"%&gt;<br />
            &lt;html&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;head&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;meta http-equiv="Content-Type" content="text/html;
            charset=UTF-8"&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;title&gt;This is test2&lt;/title&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/head&gt;<br />
            &nbsp;&nbsp;&nbsp;<br />
            &nbsp;&nbsp;&nbsp; &lt;body&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;b&gt;This is
            test2&lt;/b&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;b&gt;Use &amp;lt;decorator:getProperty&amp;gt;
            tag&lt;/b&gt;<br />
            &nbsp;&nbsp;&nbsp;<br />
            &nbsp;&nbsp;&nbsp; <span style="color: #3333ff;">&lt;content
            tag="content1"&gt;&lt;p&gt;This is content1&lt;/p&gt;&lt;/content&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;
            &lt;content tag="content2"&gt;&lt;p&gt;This is
            content2&lt;/p&gt;&lt;/content&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp; &lt;content tag="content4"&gt;&lt;p&gt;This is
            content4, it shouldn't be display&lt;/p&gt;&lt;/content&gt;</span><br />
            &nbsp;&nbsp;&nbsp;
            &lt;/body&gt;<br />
            &lt;/html&gt;<br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>打开浏览器，访问http://localhost:8080/myapp/test2.jsp，将会出现一下内容： </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 789px; height: 103px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">
            <p style="font-weight: bold;">This is test2 </p>
            <p><span style="font-weight: bold;">Use &lt;decorator:getProperty&gt; tag</span>
            </p>
            <p>This is content1 </p>
            <p>This is content2 </p>
            <p>This message is in /decorators/mydecorator2.jsp
            </p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<h2>例子3 (page:applyDecorator tag) </h2>
<ul>
    <li>在{myapp}/WEB-INF/decorators.xml文件中添加以下decorator </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 795px; height: 61px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&nbsp;&nbsp;&nbsp; &lt;decorator name="mydecorator3"
            page="mydecorator3.jsp"&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;pattern&gt;/test3.jsp&lt;/pattern&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;/decorator&gt;<br />
            &nbsp;&nbsp;
            &nbsp;<br />
            &nbsp;&nbsp;&nbsp; &lt;decorator name="mydecorator31" page="mydecorator31.jsp"&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/decorator&gt;<br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>在{myapp}/decorators目录下添加mydecorator3.jsp文件，内容如下: </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 791px; height: 299px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;%@ taglib
            uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator"
            %&gt;<br />
            &lt;%@ taglib uri="http://www.opensymphony.com/sitemesh/page"
            prefix="page" %&gt;<br />
            &lt;html&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;head&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;title&gt;My Site - &lt;decorator:title default="Welcome!"
            /&gt;&lt;/title&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;decorator:head /&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/head&gt;<br />
            <br />
            &nbsp;&nbsp;&nbsp; &lt;body&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;decorator:body
            /&gt;<br />
            <br />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;page:applyDecorator
            name="mydecorator31"&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;content
            tag="content1"&gt;&lt;p&gt;This is content1&lt;/p&gt;&lt;/content&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;content tag="content2"&gt;&lt;p&gt;This is
            content2&lt;/p&gt;&lt;/content&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/page:applyDecorator&gt;</span><br />
            &nbsp;&nbsp;&nbsp;
            &lt;/body&gt;<br />
            &lt;/html&gt;<br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<div style="margin-left: 40px;">在{myapp}/decorators目录下添加mydecorator31.jsp文件，内容如下:<br />
</div>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 791px; height: 146px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;%@ taglib
            uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator"
            %&gt;<br />
            &lt;%@ taglib uri="http://www.opensymphony.com/sitemesh/page"
            prefix="page" %&gt;<br />
            <br />
            <span style="color: #3333ff;">&lt;p&gt;&lt;i&gt;begin&lt;/i&gt;&lt;/&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&lt;decorator:getProperty
            property="page.content1"/&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&lt;decorator:getProperty
            property="page.content2"/&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&lt;p&gt;&lt;i&gt;end&lt;/i&gt;&lt;/&gt;</span><br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>在{myapp}目录下添加test3.jsp文件，内容如下： </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 790px; height: 240px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;%@page contentType="text/html"%&gt;<br />
            &lt;%@page
            pageEncoding="UTF-8"%&gt;<br />
            &lt;html&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;head&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;meta http-equiv="Content-Type" content="text/html;
            charset=UTF-8"&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;title&gt;This is test3&lt;/title&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/head&gt;<br />
            &nbsp;&nbsp;&nbsp;<br />
            &nbsp;&nbsp;&nbsp; &lt;body&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;b&gt;This is
            test3&lt;/b&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;b&gt;Use &amp;lt;page:applyDecorator&amp;gt;
            tag&lt;/b&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;/body&gt;<br />
            &lt;/html&gt;
            </td>
        </tr>
    </tbody>
</table>
</div>
<div style="margin-left: 40px;">注意：相对于例子2，这里已经没有了&lt;content
tag="XXX"/&gt;标签。<br />
</div>
<ul>
    <li>打开浏览器，访问http://localhost:8080/myapp/test3.jsp，将会出现一下内容： </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 789px; height: 103px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">
            <p style="font-weight: bold;">This is test3 </p>
            <p><span style="font-weight: bold;">Use &lt;page:applyDecorator&gt; tag</span>
            </p>
            <p><em>begin</em> </p>
            <p>This is content1 </p>
            <p>This is content2 </p>
            <p><em>end</em> </p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p style="margin-left: 40px;">这里，我在mydecorator3.jsp中应用了mydecorator31.jsp的的decorator，并且将原来在test2.jsp中的
&lt;content /&gt;标签复制到mydecorator3.jsp中，此时对于&lt;content
tag="xxx"/&gt;的标签将会由mydecorator31.jsp了装饰。 </p>
<h2>例子4 (page:param tag) </h2>
<ul>
    <li>在{myapp}/WEB-INF/decorators.xml文件中添加以下decorator </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 795px; height: 61px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&nbsp;&nbsp;&nbsp; &lt;decorator name="mydecorator4"
            page="mydecorator4.jsp"&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;pattern&gt;/test4.jsp&lt;/pattern&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;/decorator&gt;<br />
            &nbsp;&nbsp;
            &nbsp;<br />
            &nbsp;&nbsp;&nbsp; &lt;decorator name="mydecorator41" page="mydecorator41.jsp"&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/decorator&gt;<br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>在{myapp}/decorators目录下添加mydecorator4.jsp文件，内容如下: </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 791px; height: 299px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;%@ taglib
            uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator"
            %&gt;<br />
            &lt;%@ taglib uri="http://www.opensymphony.com/sitemesh/page"
            prefix="page" %&gt;<br />
            <br />
            &lt;html&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;head&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;title&gt;My Site - &lt;decorator:title default="Welcome!"
            /&gt;&lt;/title&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;decorator:head /&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/head&gt;<br />
            <br />
            &nbsp;&nbsp;&nbsp; &lt;body&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;decorator:body
            /&gt;<br />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;page:applyDecorator
            name="mydecorator41" &gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;content
            tag="content1"&gt;&lt;p&gt;This is content1&lt;/p&gt;&lt;/content&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;content tag="content2"&gt;&lt;p&gt;This is
            content2&lt;/p&gt;&lt;/content&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold;">&lt;page:param name="page.content1"&gt;&lt;p&gt;This
            content1 has been replaced&lt;/p&gt;&lt;/page:param&gt;</span></span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;/page:applyDecorator&gt;</span><br />
            &nbsp;&nbsp;&nbsp;
            &lt;/body&gt;<br />
            &lt;/html&gt;<br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<div style="margin-left: 40px;">在{myapp}/decorators目录下添加mydecorator41.jsp文件，内容如下:<br />
</div>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 791px; height: 146px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;%@ taglib
            uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator"
            %&gt;<br />
            &lt;%@ taglib uri="http://www.opensymphony.com/sitemesh/page"
            prefix="page" %&gt;<br />
            <br />
            <span style="color: #3333ff;">&lt;p&gt;&lt;i&gt;begin&lt;/i&gt;&lt;/&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&lt;decorator:getProperty
            property="page.content1"/&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&lt;decorator:getProperty
            property="page.content2"/&gt;</span><br style="color: #3333ff;" />
            <span style="color: #3333ff;">&lt;p&gt;&lt;i&gt;end&lt;/i&gt;&lt;/&gt;</span><br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>在{myapp}目录下添加test4.jsp文件，内容如下： </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 790px; height: 240px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">&lt;%@page contentType="text/html"%&gt;<br />
            &lt;%@page
            pageEncoding="UTF-8"%&gt;<br />
            &lt;html&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;head&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            &lt;meta http-equiv="Content-Type" content="text/html;
            charset=UTF-8"&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;title&gt;This is test4&lt;/title&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/head&gt;<br />
            &nbsp;&nbsp;&nbsp;<br />
            &nbsp;&nbsp;&nbsp; &lt;body&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;b&gt;This is
            test4&lt;/b&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;b&gt;Use &amp;lt;page:param&amp;gt;
            tag&lt;/b&gt;<br />
            &nbsp;&nbsp;&nbsp;
            &lt;/body&gt;<br />
            &lt;/html&gt;&nbsp;<br />
            </td>
        </tr>
    </tbody>
</table>
</div>
<ul>
    <li>打开浏览器，访问http://localhost:8080/myapp/test4.jsp，将会出现一下内容： </li>
</ul>
<div style="margin-left: 40px;">
<table class="zeroBorder" style="width: 789px; height: 103px;" classname="zeroBorder FCK__ShowTableBorders" bgcolor="#cccccc" border="0" cellpadding="3" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td width="100%">
            <p style="font-weight: bold;">This is test4 </p>
            <p><span style="font-weight: bold;">Use &lt;page:param&gt; tag</span> </p>
            <p><em>begin</em> </p>
            <p style="color: #3333ff;">This content1 has been replaced </p>
            <p>This is content2 </p>
            <p><em>end</em> </p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div style="margin-left: 40px;">这里，我在mydecorator4.jsp中应用了mydecorator41.jsp的的decorator，并且添加了<span style="color: #000000;"><span style="font-style: italic;">&lt;page:param
name="page.content1"&gt;</span>标签，那么此时页面上将会用&lt;page:param&gt;标签中的内容替换原来在</span><span style="color: #333333; font-style: italic;">&lt;decorator:getProperty
property="page.content1"/&gt;</span><span style="color: #333333;">中的内容，因此页面将不在<span style="color: #000000; font-style: italic;">&#8220;</span></span><span style="color: #000000; font-style: italic;">This is content1&#8221;</span><span style="color: #000000;">而显示<span style="font-style: italic;">&#8220;</span></span><span style="color: #000000; font-style: italic;">This content1 has been
replaced</span><span style="color: #000000;"><span style="font-style: italic;">&#8221;。</span></span> </div>
</div>
<br />
<br />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/184536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-07 16:28 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SiteMesh：一个优于Apache Tiles的Web页面布局、装饰框架 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184535.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 07 Mar 2008 08:27:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184535.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/184535.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184535.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/184535.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/184535.html</trackback:ping><description><![CDATA[<p><font face="Arial">http://www.iocblog.net/static/2007/565.html</font></p>
<p><font face="Arial"><br />
</font></p>
<p><font face="Arial"><br />
</font></p>
<p><font face="Arial">一、SiteMesh项目简介<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
OS(OpenSymphony)的SiteMesh是一个用来在JSP中实现页面布局和装饰（layout and
decoration）<br />
的框架组件，能够帮助网站开发人员较容易实现页面中动态内容和静态装饰外观的分离。</font></p>
<p><font face="Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Sitemesh是由一个基于Web页面布局、装饰以及与现存Web应用整合的框架。它能帮助我们在由大<br />
量页面构成的项目中创建一致的页面布局和外观，如一致的导航条，一致的banner，一致的版权，等等。<br />
它不仅仅能处理动态的内容，如jsp，php，asp等产生的内容，它也能处理静态的内容，如htm的内容，<br />
使得它的内容也符合你的页面结构的要求。甚至于它能将HTML文件象include那样将该文件作为一个面板<br />
的形式嵌入到别的文件中去。所有的这些，都是GOF的Decorator模式的最生动的实现。尽管它是由java语言来实现的，但它能与其他Web应用很好地集成。</font></p>
<p><font face="Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 官方：http://www.opensymphony.com/sitemesh/</font></p>
<p><font face="Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
下载地址：http://www.opensymphony.com/sitemesh/download.action 目前的最新版本是Version
2.3；</font></p>
<p><font face="Arial">二、为什么要使用SiteMesh?</font></p>
<p><font face="Arial">&nbsp;&nbsp;&nbsp; 我们的团队开发J2EE应用的时候，经常会碰到一个比较头疼的问题：</font></p>
<p><font face="Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
由于Web页面是由不同的人所开发，所以开发出来的界面通常是千奇百怪，通常让项目管理人员苦笑不得。</font></p>
<p><font face="Arial">&nbsp;&nbsp;&nbsp;&nbsp;
而实际上，任何一个项目都会要求界面的统一风格和美观，既然风格统一，那就说明UI层肯定有很多可以抽出来<br />
共用的静态或动态部分；如何整合这些通用的静态或动态UI呢？Apache
Tiles框架站了出来很好的解决了这一问题，<br />
再加上他与struts的完美集成，导致大小项目都把他作为UI层的首选框架，</font></p>
<p><font face="Arial">但是：</font></p>
<p><font face="Arial">&nbsp;&nbsp; Tiles确实有着它很多的不足之处,下文我会介绍,本文想说的是，除了Apache
Tiles框架,其实我们还有更好的解<br />
决方案，那就是:SiteMesh；</font></p>
<p><font face="Arial">本文<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;
介绍了一个基于Web页面的布局、装饰以及应用整合的框架Sitemesh，它能帮助你为你的应用创建一致的外观，<br />
很好的取代Apache
Tiles;</font></p>
<p><font face="Arial">三、SiteMesh VS Apache Tiles</font></p>
<p><font face="Arial">&nbsp;&nbsp;&nbsp;&nbsp; 用过struts的朋友应该对Apache
Tiles的不会陌生，我曾经有一篇文章介绍过struts中tiles框架的组合与继承，<br />
现在怎么看怎么觉得复杂;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
从使用角度来看，Tiles似乎是Sitemesh标签&lt;page:applyDecorator&gt;的一个翻版。其实sitemesh最强的<br />
一个特性是sitemesh将decorator模式用在过滤器上。任何需要被装饰的页面都不知道它要被谁装饰，所以它就<br />
可以用来装璜来自php、asp、CGI等产生的页面了。你可以定义若干个装饰器，根据参数动态地选择装饰器，<br />
产生动态的外观以满足你的需求。它也有一套功能强大的属性体系，它能帮助你构建功能强大而灵活的装饰器。<br />
相比较而言，在这方面Tiles就逊色许多。</font></p>
<p><font face="Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 个人觉得在团队开发里面，Apache Tiles框架会导致所有人不仅仅要了解并且清楚Apache
Tiles的存在，<br />
并且要特别熟悉每一个Tiles
layout模板的作用，否则就可能出现用错模板的情况；除此之外，每个人涉及到<br />
的所有WEB页面都需要去配置文件里面逐个配置，不仅麻烦出错的几率还高；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
而以上所有的不足都是SiteMesh所不存在的；</font></p>
<font face="Arial">
<p><br />
四、SiteMesh的基本原理</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
一个请求到服务器后，如果该请求需要sitemesh装饰，服务器先解释被请求的资源，然后根据配置文件<br />
获得用于该请求的装饰器，最后用装饰器装饰被请求资源，将结果一同返回给客户端浏览器。</p>
<p>五、如何使用SiteMesh</p>
<p>&nbsp;&nbsp; 这里以struts2+spring2+hibernate3构架的系统为例<br />
&nbsp;&nbsp;&nbsp;&nbsp; 1、下载SiteMesh <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
下载地址：http://www.opensymphony.com/sitemesh/download.action 目前的最新版本是Version
2.3；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
2、在工程中引入SiteMesh的必要jar包，和struts2-sitemesh-plugin-2.0.8.jar；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
3、修改你的web.xml,在里面加入sitemesh的过滤器，示例代码如下：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&lt;!-- sitemesh配置
--&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;filter&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;filter-name&gt;sitemesh&lt;/filter-name&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;filter-class&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
com.opensymphony.module.sitemesh.filter.PageFilter<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;/filter-class&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/filter&gt;<br />
&nbsp;&nbsp;&nbsp;
&lt;filter-mapping&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;filter-name&gt;sitemesh&lt;/filter-name&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/filter-mapping&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
注意过滤器的位置：应该在struts2的org.apache.struts2.dispatcher.FilterDispatcher过滤器之前
org.apache.struts2.dispatcher.ActionContextCleanUp过滤器之后，否则会有问题；</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
4、在下载的SiteMesh包中找到sitemesh.xml，(\sitemesh-2.3\src\example-webapp\WEB-INF目录下就有)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将其拷贝到/WEB-INF目录下；</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
5、在sitemesh.xml文件中有一个property结点(如下)，该结点指定了decorators.xml在工程中的位置，让sitemesh.xml能找到他;<br />
按照此路径新建decorators.xml文件，当然这个路径你可以任意改变，只要property结点的value值与其匹配就行；</p>
<p>&lt;property name="decorators-file"
value="/WEB-INF/sitemesh/decorators.xml"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;
6、在WebRoot目录下新建decorators目录，并在该目录下新建一个模板jsp，根据具体项目风格编辑该模板，<br />
如下示例：我的模板：main.jsp<br />
&lt;%@
page language="java" pageEncoding="UTF-8"%&gt;<br />
&lt;%@taglib
prefix="decorator"<br />
&nbsp;&nbsp;&nbsp;
uri="http://www.opensymphony.com/sitemesh/decorator"%&gt;<br />
&lt;%@taglib
prefix="page"
uri="http://www.opensymphony.com/sitemesh/page"%&gt;<br />
&lt;!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.1 Transitional//EN"
<br />
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;<br />
&lt;%<br />
&nbsp;&nbsp;&nbsp;
response.setHeader("Pragma", "no-cache");<br />
&nbsp;&nbsp;&nbsp;
response.setHeader("Cache-Control", "no-cache");<br />
&nbsp;&nbsp;&nbsp;
response.setDateHeader("Expires", 0);<br />
%&gt;<br />
&lt;html&gt;<br />
&nbsp;&nbsp;&nbsp;
&lt;head&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;title&gt;&lt;decorator:title default="kangxm test"
/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/title&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 页面Head由引用模板的子页面来替换
--&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;decorator:head /&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/head&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;body
id="page-home"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div id="page-total"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div
id="page-header"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;table width="100%" border="0"
cellspacing="0" cellpadding="0"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;div class="topFunc"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
我的账户<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
退出<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/div&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;/table&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/div&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/div&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;!-- end header --&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!--&nbsp; Menu Tag begin --&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;div id="page-menu" style="margin-top: 8px; margin-bottom:
8px;"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里放菜单<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;/div&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/div&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!--&nbsp; Menu Tag end
--&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div id="page-content" class="clearfix"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;center&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;table width="100%" border="0"
cellpadding="0" cellspacing="0"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;decorator:body /&gt;&lt;!-- 这里的内容由引用模板的子页面来替换
--&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/table&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;/center&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/div&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- end content
--&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div id="page-footer" class="clearfix"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里放页面底部<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- end footer --&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;/div&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- end page --&gt;<br />
&nbsp;&nbsp;&nbsp;
&lt;/body&gt;<br />
&lt;/html&gt;</p>
<p><br />
这就是个简单的模板，页面的头和脚都由模板里的静态HTML决定了，主页面区域用的是&lt;decorator:body
/&gt;标签；<br />
也就是说凡是能进入过滤器的请求生成的页面都会默认加上模板上的头和脚，然后页面自身的内容将自动放到&lt;decorator:body
/&gt;标签所在位置；</p>
<p>&lt;decorator:title default="Welcome to test sitemesh!"
/&gt;：读取被装饰页面的标题，并给出了默认标题。<br />
&lt;decorator:head
/&gt;：读取被装饰页面的&lt;head&gt;中的内容；<br />
&lt;decorator:body
/&gt;：读取被装饰页面的&lt;body&gt;中的内容；</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;
7、说到这里大家就要想了，那如果某个特殊的需求请求路径在过滤器的范围内，但又不想使用模板怎么办？<br />
你总不能这么不讲道理吧！<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
大家放心吧，SiteMesh早就考虑到这一点了，上面第5步说道的decorators.xml这个时候就起到作用了！<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
下面是我的decorators.xml：<br />
&lt;?xml version="1.0"
encoding="ISO-8859-1"?&gt;<br />
&lt;decorators defaultdir="/decorators"&gt;<br />
&nbsp;&nbsp;&nbsp;
&lt;!-- Any urls that are excluded will never be decorated by Sitemesh
--&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;excludes&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;pattern&gt;/index.jsp*&lt;/pattern&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;pattern&gt;/login/*&lt;/pattern&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/excludes&gt;<br />
&nbsp;&nbsp;&nbsp;
&lt;decorator name="main" page="main.jsp"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;pattern&gt;/*&lt;/pattern&gt;<br />
&nbsp;&nbsp;&nbsp;
&lt;/decorator&gt;<br />
&lt;/decorators&gt;</p>
<p><br />
decorators.xml有两个主要的结点：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
decorator结点指定了模板的位置和文件名，通过pattern来指定哪些路径引用哪个模板<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
excludes结点则指定了哪些路径的请求不使用任何模板</p>
<p>如上面代码，/index.jsp和凡是以/login/开头的请求路径一律不使用模板；</p>
<p>另外还有一点要注意的是：decorators结点的defaultdir属性指定了模板文件存放的目录;</p>
<p>六、实战感受</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;
刚刚做完一个用到sitemesh的项目，跟以前用tiles框架相比，最大的感受就是简单，系统设计阶段<br />
就把模板文件和sitemesh框架搭好了！哪些页面使用框架哪些不使用，全部都通过UI
Demo很快就定义出来了；<br />
在接下来的开发中所有成员几乎感受不到sitemesh的存在，各自仅仅关心自己的模块功能实现；<br />
七、总结</p>
<p>&nbsp;&nbsp;&nbsp;
使用sitemesh给我们带来的是不仅仅是页面结构问题，它的出现让我们有更多的时间去关注底层业务<br />
逻辑，而不是整个页面的风格和结构。它让我们摆脱了大量用include方式复用页面尴尬局面，也避免了tiles<br />
框架在团队开发中的复杂度，它还提供了很大的灵活性以及给我们提供了整合异构Web系统页面的一种方案。
</p>
</font>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/184535.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-07 16:27 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184535.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Advanced SiteMesh zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184534.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 07 Mar 2008 08:26:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184534.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/184534.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184534.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/184534.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/184534.html</trackback:ping><description><![CDATA[http://www.iocblog.net/static/2007/566.html<br />
<br />
sitemesh一个系统，添加到Web应用中并促进模式HTML世界里的应用。<br />
可从http://www.opensymphony.com/sitemesh/
获得。<br />
<br />
sitemesh仅处理html的内容，象图象，PDF文件和下载这样的媒体是被忽略的。<br />
<br />
下面我们将用实例来介绍如何分析内容网页和装饰器如何被映射到网页。以及从sitemesh中获取<br />
信息的一些技术。<br />
<br />
整个事例用到login.jsp,
date.jsp , 索引的index.html <br />
配置文件decorators.xml<br />
装饰文件：window.jsp
,simple.jsp<br />
运用的样式表：style.css<br />
<br />
<br />
目标是把内容与布局分离，一达到简化布局，以及重用代码的作用。<br />
<br />
<br />
实质是使用simple.jsp,window.jsp以及提取的longin.jsp,date,jsp的实际主体来构成最终显示<br />
给用户的页面。<br />
<br />
<br />
布局主体是simple.jsp控制：它控制了网页的整体架构。<br />
<br />
&lt;%@
taglib uri=<font color="#00bb00" size="2">"sitemesh-decorator"</font><font color="#000000" size="2"> prefix=</font><font color="#00bb00" size="2">"decorator"</font><font color="#000000" size="2"> %&gt;<br />
&lt;%@ taglib
uri=</font><font color="#00bb00" size="2">"sitemesh-page"</font><font color="#000000" size="2"> prefix=</font><font color="#00bb00" size="2">"page"</font><font color="#000000" size="2">
%&gt;<br />
<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;&lt;decorator:title/&gt;&lt;/title&gt;<br />
&lt;link
rel=</font><font color="#00bb00" size="2">"stylesheet"</font><font color="#000000" size="2"> href=</font><font color="#00bb00" size="2">"decorators/style.css"</font><font color="#000000" size="2">&gt;<br />
&lt;decorator:head/&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
<br />
&lt;table
width=</font><font color="#00bb00" size="2">"100%"</font><font color="#000000" size="2">&gt;<br />
&lt;tr&gt;<br />
&lt;td <font color="#0000ff" size="2">class</font>=</font><font color="#00bb00" size="2">"title"</font><font color="#000000" size="2"> colspan=</font><font color="#00bb00" size="2">"2"</font><font color="#000000" size="2">&gt;<br />
&lt;decorator:title/&gt;<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;tr&gt;<br />
&lt;td
<font color="#0000ff" size="2">class</font>=</font><font color="#00bb00" size="2">"body"</font><font color="#000000" size="2"> valign=</font><font color="#00bb00" size="2">"top"</font><font color="#000000" size="2">&gt;<br />
&lt;decorator:body/&gt;<br />
&lt;/td&gt;<br />
&lt;td
valign=</font><font color="#00bb00" size="2">"top"</font><font color="#000000" size="2">&gt;<br />
<br />
&lt;page:applyDecorator name=</font><font color="#00bb00" size="2">"window"</font><font color="#000000" size="2"> page=</font><font color="#00bb00" size="2">"date.jsp"</font><font color="#000000" size="2">/&gt;<br />
<br />
&lt;page:applyDecorator name=</font><font color="#00bb00" size="2">"window"</font><font color="#000000" size="2"> title=</font><font color="#00bb00" size="2">"Disclaimer"</font><font color="#000000" size="2">&gt;<br />
This
site is not legally binding in any way. &lt;br&gt;<br />
All rights reserved. Elvis
has left the
building.<br />
&lt;/page:applyDecorator&gt;<br />
<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;tr&gt;<br />
&lt;td
<font color="#0000ff" size="2">class</font>=</font><font color="#00bb00" size="2">"footer"</font><font color="#000000" size="2"> valign=</font><font color="#00bb00" size="2">"top"</font><font color="#000000" size="2">
colspan=</font><font color="#00bb00" size="2">"2"</font><font color="#000000" size="2">&gt;<br />
&lt;b&gt;Title:&lt;/b&gt; &lt;decorator:title/&gt;
&lt;br&gt;<br />
&lt;b&gt;Author:&lt;/b&gt; &lt;decorator:getProperty
property=</font><font color="#00bb00" size="2">"meta.author"</font><font color="#000000" size="2">/&gt;
&lt;br&gt;<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;/table&gt;
<br />
<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
这个文件将在decorators.xml中被定义,其中给出装饰器的名称，位置，模式匹配。<br />
模式匹配可以使用通配符和正则表达式。在这对于simple.jsp使用了通配符*配置该<br />
装饰器用来匹配Web应用中的所有网页。有关正则表达式的相关内容请参看本博客
http:</font><font color="#0000aa" size="2"><em>//192.168.0.3/blog3/meiking_archive_2005_05_20_18525.html)。</em></font><font color="#000000" size="2"><br />
<br />
&lt;decorators&gt;<br />
<br />
&lt;decorator
name=</font><font color="#00bb00" size="2">"simple"</font><font color="#000000" size="2"> page=</font><font color="#00bb00" size="2">"/decorators/simple.jsp"</font><font color="#000000" size="2">&gt;<br />
&lt;pattern&gt;*&lt;/pattern&gt;<br />
&lt;/decorator&gt;<br />
<br />
<br />
&lt;/decorators&gt;<br />
<br />
上面我们讨论了装饰器设计模式，将装饰器和内容组合，下面我们来看看如何运用组合设计模式<br />
<br />
在页面中引用子装饰器
，子内容。<br />
让我们来看看一个子装饰器window.jsp<br />
<br />
<br />
&lt;%@ taglib uri=</font><font color="#00bb00" size="2">"sitemesh-decorator"</font><font color="#000000" size="2">
prefix=</font><font color="#00bb00" size="2">"decorator"</font><font color="#000000" size="2"> %&gt;<br />
&lt;table <font color="#0000ff" size="2">class</font>=</font><font color="#00bb00" size="2">"window"</font><font color="#000000" size="2">&gt;<br />
&lt;tr&gt;<br />
&lt;th&gt;&lt;img src=</font><font color="#00bb00" size="2">"decorators/snazzy.gif"</font><font color="#000000" size="2">&gt;&lt;decorator:title/&gt;&lt;/th&gt;<br />
&lt;/tr&gt;<br />
&lt;tr&gt;<br />
&lt;td&gt;<br />
&lt;decorator:body/&gt;<br />
&lt;/td&gt;<br />
&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
在回头看看simple.jsp在其中我们已经包含了window.jsp的运用，我们把他运用到了date.jsp<br />
上，运用样式表date.jsp的主体内容应该会出现在最终显示页面的右上角。<br />
<br />
在decorators.xml中被定义<br />
&lt;decorators&gt;<br />
<br />
&lt;decorator
name=</font><font color="#00bb00" size="2">"window"</font><font color="#000000" size="2"> page=</font><font color="#00bb00" size="2">"/decorators/window.jsp"</font><font color="#000000" size="2">/&gt;<br />
<br />
&lt;/decorators&gt;<br />
<br />
<br />
<br />
现在我们可以给出完整的decorators.xml了：<br />
&lt;decorators&gt;<br />
<br />
&lt;decorator
name=</font><font color="#00bb00" size="2">"simple"</font><font color="#000000" size="2"> page=</font><font color="#00bb00" size="2">"/decorators/simple.jsp"</font><font color="#000000" size="2">&gt;<br />
&lt;pattern&gt;*&lt;/pattern&gt;<br />
&lt;/decorator&gt;<br />
<br />
&lt;decorator
name=</font><font color="#00bb00" size="2">"window"</font><font color="#000000" size="2"> page=</font><font color="#00bb00" size="2">"/decorators/window.jsp"</font><font color="#000000" size="2">/&gt;<br />
<br />
&lt;/decorators&gt;<br />
在装饰器中拥有一个技术：sitemesh有一个pageparser对象，它通过内容网页获取输出的内容<br />
并把它解析到一个page对象中<br />
假如一个内容网页有如下列标题：<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;Please
login&lt;/title&gt;<br />
&lt;meta name=</font><font color="#00bb00" size="2">"author"</font><font color="#000000" size="2"> content=</font><font color="#00bb00" size="2">"Homer Simpson"</font><font color="#000000" size="2">&gt;<br />
&lt;/head&gt;<br />
...<br />
装饰器使用JSP标识符可以访问title属性，也能访问auther属性<br />
让我们看看simple是如何做的：<br />
&lt;%@
taglib uri=</font><font color="#00bb00" size="2">"sitemesh-decorator"</font><font color="#000000" size="2"> prefix=</font><font color="#00bb00" size="2">"decorator"</font><font color="#000000" size="2">
%&gt;<br />
...<br />
&lt;b&gt;Title:&lt;/b&gt; &lt;decorator:title/&gt;
&lt;br&gt;<br />
&lt;b&gt;Author:&lt;/b&gt; &lt;decorator:getProperty
property=</font><font color="#00bb00" size="2">"meta.author"</font><font color="#000000" size="2">/&gt;
&lt;br&gt;<br />
...<br />
<br />
<br />
简单的login.jsp,date.jsp页面<br />
<br />
login.jsp<br />
<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;Please
login&lt;/title&gt;<br />
&lt;meta name=</font><font color="#00bb00" size="2">"author"</font><font color="#000000" size="2"> content=</font><font color="#00bb00" size="2">"Homer Simpson"</font><font color="#000000" size="2">&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
&lt;form action=</font><font color="#00bb00" size="2">"#"</font><font color="#000000" size="2"> method=</font><font color="#00bb00" size="2">"post"</font><font color="#000000" size="2">&gt;<br />
<br />
&lt;input type=</font><font color="#00bb00" size="2">"hidden"</font><font color="#000000" size="2"> name=</font><font color="#00bb00" size="2">"section"</font><font color="#000000" size="2">
value=</font><font color="#00bb00" size="2">"store"</font><font color="#000000" size="2">&gt;<br />
<br />
Login Name:&lt;br&gt;<br />
&lt;input type=</font><font color="#00bb00" size="2">"text"</font><font color="#000000" size="2"> name=</font><font color="#00bb00" size="2">"loginname"</font><font color="#000000" size="2">&gt;&lt;br&gt;<br />
<br />
Password:&lt;br&gt;<br />
&lt;input type=</font><font color="#00bb00" size="2">"password"</font><font color="#000000" size="2">
name=</font><font color="#00bb00" size="2">"password"</font><font color="#000000" size="2">&gt;&lt;br&gt;<br />
<br />
&lt;input type=</font><font color="#00bb00" size="2">"submit"</font><font color="#000000" size="2"> value=</font><font color="#00bb00" size="2">"Login"</font><font color="#000000" size="2">&gt;<br />
<br />
&lt;/form&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
<br />
&lt;---------------------------------------------------&gt;<br />
date.jsp<br />
<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;Time
and date&lt;/title&gt;<br />
&lt;meta name=</font><font color="#00bb00" size="2">"author"</font><font color="#000000" size="2"> content=</font><font color="#00bb00" size="2">"Fred Flintstone"</font><font color="#000000" size="2">&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
Right now,
it's:&lt;br&gt;<br />
&lt;b&gt;&lt;%= <font color="#0000ff" size="2">new</font>
java.util.Date().toString() %&gt;&lt;/b&gt;<br />
&lt;/body&gt;
<br />
&lt;/html&gt;<br />
<br />
<br />
<br />
其他：1 对于页面可将公共代码重构到应用文件中来简化代码和重用代码<br />
2
对于页面可以使用样式表做的更彻底，使页面的主体部分不会淹没带大量的结构代码中</font>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/184534.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-07 16:26 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184534.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SiteMesh介绍 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184533.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 07 Mar 2008 08:25:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184533.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/184533.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184533.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/184533.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/184533.html</trackback:ping><description><![CDATA[<div>
<p>http://www.iocblog.net/static/2007/569.html</p>
<p><br />
</p>
<p><br />
</p>
<p>注：sitemesh，一个不错的tiles替代方案，比tiles做的更漂亮优雅。本文是sitemesh官方推荐的入门文档，本来想自己翻译的，突然发现有人先行一步了，就转过来看吧。</p>
<p><br />
以前我通常使用旧式的方法来建立自己的web应用：手工排版，仔细使用每一个字节使其工作在Unicode下，同时使用make文件来适应不同的CPU&#8230;&#8230;</p>
<p>或许现在我们可以换一种方式。</p>
<p>尽管我从没有感觉到需要使用assembly (CISC or
RISC)来建立web应用，但也会偶尔觉得我的开发伙伴的工作相当繁琐。特别是我发现很多的开发者在痛苦的寻求一种比较好的方式来控制web应用的基本模块：例如那些页头、页尾、导航栏、打印页面、手持设备的轻量级页面，以及其他更多的问题。到了最后，令人惊异的是大部分人都采用了落后的
includes和复制粘贴方式。</p>
<p>根据经验，我可以采用在<u><font color="#0000ff"> java.net </font></u>上开源的servlet&nbsp;过滤器 <a href="http://www.opensymphony.com/sitemesh/">SiteMesh</a>n
来简单明了并优雅的解决这些问题。作为一种替代新的templating语言（XSLT）或部署您的页面到新的系统的解决方法，应用SiteMesh可以相当容易处理你的页面，这一切只需要普通的HTML，JSP，servlet(包括Struts)，以及其他常用的技术。</p>
<h2 id="How_Does_It_Work">工作原理</h2>
<p>SiteMesh
利用了一种很少人知道的servlet规范实现了一种页面过滤器。设想一下，现在有一个简单的jsp页面用来返回当前的日期和时间。通常这个页面请求来到应用服务器，页面被处理，最后处理结果返回到web浏览器。SiteMesh作为一个页面过滤器，在页面被处理之后，返回web浏览器之前，对页面做了一些附加的操作。这个变化简单描述为图一和图二所示的附加步骤。
</p>
<p><img title="点击看大图http://today.java.net/images/2004/03/sitemesh_fig1.gif" style="cursor: pointer;" alt="Figure 1" src="http://today.java.net/images/2004/03/sitemesh_fig1.gif" height="202" width="520" /><br />
<em>图一：普通页面处理情况</em></p>
<p><img title="点击看大图http://today.java.net/images/2004/03/sitemesh_fig2.gif" style="cursor: pointer;" alt="Figure 2" src="http://today.java.net/images/2004/03/sitemesh_fig2.gif" height="202" width="520" /><br />
<em>图二：SiteMesh对页面处理情况</em></p>
<p>现在看一个简单的例子。</p>
<pre><code>&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;Simple Document&lt;/title&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
Hello World! &lt;br /&gt;<br />
&lt;%= 1+1 %&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;</code></pre>
<p>你会发现这个页面有一个title和body（类似普通的HTML页面）。你也会发现一小段JSP代码——它将会如同你期望的那样被处理。同时你可以使用任何你想使用的JSP语法和特性来替换这一小段代码。
</p>
<p>现在来看一个简单的SiteMesh "装饰（decorator）"页面。列表2显示了一个被SiteMesh调用的JSP页面。</p>
<pre><code>&lt;%@ taglib uri="sitemesh-decorator"<br />
prefix="decorator" %&gt;<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;<br />
My Site - &lt;decorator:title default="Welcome!" /&gt;<br />
&lt;/title&gt;<br />
&lt;decorator:head /&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
&lt;h1&gt;&lt;decorator:title default="Welcome!" /&gt;&lt;/h1&gt;<br />
&lt;p&gt;&lt;decorator:body /&gt;&lt;/p&gt; <br />
&lt;p&gt;&lt;small&gt;<br />
(&lt;a <br />
href="?printable=true"&gt;printable version&lt;/a&gt;<img class="smiley" title=";)" alt=";)" src="http://blog.51766.com/images/smileys/wink.gif" /><br />
&lt;/small&gt;&lt;/p&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;</code></pre>
<p>查看这个装饰器（decorator），我们能看到一些有趣的东西。首先，在第一行申明了一个SiteMesh标签库。这个标签库包含了与原始页面一起工作时所需的所有东西。你能看到我们使用了两个SiteMesh的装饰标签（declared
tags）， <code><font face="新宋体">&lt;decorator:title&gt;</font></code>&nbsp;和 <code><font face="新宋体">&lt;decorator:body&gt;</font></code> 。不要惊讶于标签<font face="Courier New">&lt;decorator:title&gt;在</font>原始页面中显示<font face="Courier New">&lt;title&gt;标签</font>中的内容， <code><font face="新宋体">&lt;decorator:body&gt;</font></code>
中的内容也是如此。我们在这个页面的HEAD和BODY元素都使用了同一个title标签。（We're making a few fairly radical
changes to the page, including repeating the title both in the <code><font face="新宋体">HEAD</font></code> element as well as the <code><font face="新宋体">BODY</font></code>. ）同时，我们还增加了一个到可打印版本页面的链接。</p>
<p>作为对照，图三显示了原始处理页面，图四显示了被修饰过的处理页面。留意被装饰页面在浏览器窗口显示的标题文字和HTML内容。同时也可以看到增加了一个可打印页面的链接——这个我们回头再说。
</p>
<p><img alt="Figure 3" src="http://today.java.net/images/2004/03/sitemesh_fig3.gif" height="186" width="309" /><br />
<em>图三：原始未修饰页面</em></p>
<p><img alt="Figure 4" src="http://today.java.net/images/2004/03/sitemesh_fig4.gif" height="299" width="309" /><br />
<em>图四：被修饰页面</em></p>
<p>很明显，对比起使用include（例如<font face="Courier New">&lt;jsp:include page="foo.jsp"
flush="true"
/&gt;</font>）来说，以这样的方式使用页头、页尾系统结构要清晰得多。这种方式更易移植、更易理解，同时也鼓励了JSP页面不再使用导航或其他类似的表现层代码。我发现在JSP页面中使用装饰器和CSS的组合比标准HTML的标签更容易去除格式信息。</p>
<h2 id="Installing_SiteMesh">安装SiteMesh</h2>
<p>注意下面的屏幕截图是基于Windows XP Professional， <a href="http://jakarta.apache.org/tomcat/index.html">Tomcat 5.0.19</a>，和Java 2 SDK
1.4.2_03的环境之上的。在这里我假定你的Tomcat已经安装完毕并且可以正常工作了。你或许会有一些混淆，但我们已经成功地在Tomcat 4.1 和
WebLogic 测试过，同时 SiteMesh 也支持大部分的web应用服务器。 </p>
<p>本文描述的SiteMesh 2.0.1可以在 <a href="https://sitemesh.dev.java.net/servlets/ProjectDocumentList?folderID=542">下载</a>到。
在java.net 上SiteMesh's 的项目库中有四个文件可以下载。<em>sitemesh-2.0.1.jar</em> 是其核心 JAR 文件，
<em>sitemesh-2.0.1-sources.zip</em> 的作用正如同其名字所述， <em>sitemesh-example.war</em>
则提供了一个复杂的例子用来显示一些SiteMesh的高级特性。</p>
<p>为了使描述更加简单，我们从<a href="https://sitemesh.dev.java.net/files/documents/887/2097/sitemesh-example.war"><em>sitemesh-blank.war</em></a>
文件开始。将该WAR文件放入Tomcat 的<em>webapps</em>
目录，WAR包将自动解压显示内容（SoSo注：这里的前提是你的tomcat已经开始工作），如图五所示。</p>
<p><img alt="Figure 5" src="http://today.java.net/images/2004/03/sitemesh_fig5.gif" height="255" width="201" /><br />
<em>图五： SiteMesh_blank.WAR解开后的内容</em></p>
<p>我们花点时间描述一下这些文件的作用。</p>
<h5 id="web_xml"><em>web.xml</em></h5>
<p>首先，<em>WEB-INF/web.xml</em> 文件显示如列表3，这些语句用来安装SiteMesh
过滤器和标签库。如果你决定在一个已有的Web应用中使用SiteMesh，你必须把这些语句添加到你的<em>WEB-INF/web.xml</em>
文件中。</p>
<pre><code>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />
&lt;!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" <br />
"http://java.sun.com/dtd/web-app_2_3.dtd"&gt; <br />
&lt;web-app&gt; <br />
&lt;!-- Start of SiteMesh stuff --&gt; <br />
&lt;filter&gt;<br />
&lt;filter-name&gt;sitemesh&lt;/filter-name&gt;<br />
&lt;filter-class&gt;com.opensymphony.module.sitemesh.filter.PageFilter&lt;/filter-class&gt;<br />
&lt;/filter&gt; <br />
&lt;filter-mapping&gt;<br />
&lt;filter-name&gt;sitemesh&lt;/filter-name&gt;<br />
&lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;<br />
&lt;/filter-mapping&gt; <br />
&lt;taglib&gt;<br />
&lt;taglib-uri&gt;sitemesh-page&lt;/taglib-uri&gt;<br />
&lt;taglib-location&gt;/WEB-INF/sitemesh-page.tld&lt;/taglib-location&gt;<br />
&lt;/taglib&gt; <br />
&lt;taglib&gt;<br />
&lt;taglib-uri&gt;sitemesh-decorator&lt;/taglib-uri&gt;<br />
&lt;taglib-location&gt;/WEB-INF/sitemesh-decorator.tld&lt;/taglib-location&gt;<br />
&lt;/taglib&gt; <br />
&lt;!-- End of SiteMesh stuff --&gt; <br />
&lt;/web-app&gt;</code></pre>
<p>注意：这里需要注意一下<code><font face="新宋体">url-pattern</font></code>的写法-- 如果使用的是Tomcat
5（而不是 Tomcat 4 ），需要将默认的*修改如*.jsp的形式。最新的servlet规范不再支持*样式。</p>
<h5 id="decorators_xml"><em>decorators.xml</em></h5>
<p><em>WEB-INF/decorators.xml</em>
文件用来将一个装饰器名字同一个专门的JSP装饰文件绑定。作为一个例子，这里将JSP装饰文件minimal.jsp同一个称为handheld的装饰器绑定起来。</p>
<pre><code>&lt;decorators defaultdir="/decorators"&gt;<br />
&lt;decorator name="main" page="main.jsp"&gt;<br />
&lt;pattern&gt;*&lt;/pattern&gt;<br />
&lt;/decorator&gt;<br />
<br />
&lt;decorator name="panel" page="panel.jsp"/&gt;<br />
&lt;decorator name="printable" page="printable.jsp"/&gt;<br />
&lt;/decorators&gt;</code></pre>
<p>正如我们在代码列表里看到的一样，我们定义了三个装饰器，他们分别绑定了三个类似的JSP页面。我们可以看到一个默认装饰器（main.jsp），它将被默认运用于所有文件。</p>
<p>缺省的，SiteMesh使用下面的逻辑来选择使用哪一个装饰器：</p>
<!-- sidebar begins -->
<table align="right" border="0" cellpadding="6" cellspacing="12" width="200">
    <tbody>
        <tr>
            <td bgcolor="#efefef" valign="top">
            <p>这个逻辑在<em>sitemesh-2.0.1.jar</em> 包的
            <em>\com\opensymphony\module\sitemesh\factor\sitemesh-default.xml</em>
            文件里被描述。你可以针对诸如：客户端操作系统，web浏览器，用户代理等在<em>WEB-INF\sitemesh.xml</em>文件里，通过一个变量覆盖这个行为。(You
            can override this behavior with a wide variety of <a href="http://www.opensymphony.com/sitemesh/api/com/opensymphony/module/sitemesh/mapper/package-summary.html">built-in
            mappers </a>for things like language, client operating system, web browser/user
            agent, etc. by creating a <em>WEB-INF\sitemesh.xml</em> file.
            ）可以在<em>sitemesh-example.war</em> 找到例子。</p>
            </td>
        </tr>
    </tbody>
</table>
<!-- sidebar ends -->
<ol>
    <li>页面是否使用meta装饰器标签（meta decorator tag）特别指定了一个装饰器？
    </li>
    <li>页面是否是一个框架集（是的话则不应用装饰器）？
    </li>
    <li>页面是否使用了<code><font face="新宋体">printable=true</font></code>&nbsp;参数（是的话则使用打印装饰器）
    </li>
    <li>页面时候使用装饰器文件名特别指定了一个装饰器？
    </li>
    <li>页面是否匹配 <em>decorators.xml</em> 文件里描述的样式？ </li>
</ol>
<p>通常第一条规则仅用来确定该装饰器是否被使用（Conceptually, the first rule that evaluates to true
determines the decorator that is used. ）在上面的例子中，当出现<code><font face="新宋体">printable=true</font></code> 参数的时候，装饰器<code><font face="新宋体">printable.jsp</font></code> （规则 #3）替代了 <code><font face="新宋体">main.jsp</font></code> （规则 #5）。在SiteMesh中，这些规则被描述为
<em>mappers。</em></p>
<h5 id="decorators_jsp"><em>decorators/*.jsp</em></h5>
<p>这三个decorators目录下的文件是<em>decorators.xml</em>文件中描述的不同装饰器JSP文件。上面是一个简单的装饰器例子，在后面我们将讨论更复杂的示例。</p>
<h5 id="sitemesh_jar"><em>sitemesh-2.0.1.jar</em></h5>
<p>这是SiteMesh最主要的二进制文件，通常被安装在 <em>WEB-INF/lib</em> 目录下。可以在<a href="http://www.opensymphony.com/sitemesh/api">www.opensymphony.com/sitemesh/api</a>
找到这个库的javadoc。</p>
<h5 id="tld"><em>*.tld</em></h5>
<p>SiteMesh使用两个标签库，但大多数人都只需要<em>sitemesh-decorator.tld</em>。你可以在 <a href="http://www.opensymphony.com/sitemesh/tags.html">www.opensymphony.com/sitemesh/tags.html</a>
找到相应的文档。我们已经讲述了最主要的标签：head，title和body。在下一章我们来讨论剩下的标签：getProperty。 </p>
<h2 id="Advanced_SiteMesh">SiteMesh高级特性</h2>
<p>SiteMesh的一个重要特性是使用原始HTML的meta标签（例如<font face="Courier New">&lt;meta
name="foo"
content="bar"&gt;</font>）从基础页面传递信息到装饰器。作为一个例子，下面我们使用一个meta标签来定义HTML页面的作者。 </p>
<pre><code>&lt;html&gt;<br />
&lt;meta name="author" content="test@example.com"&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;Simple Document&lt;/title&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
Hello World! &lt;br /&gt;<br />
&lt;%= 1+1 %&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;</code></pre>
<p>我们定义一个&#8220;smart&#8221;装饰器来研究meta标签，如果出现这个标签，则可以得到一个相应的HTML：</p>
<pre><code>&lt;%@ taglib uri="sitemesh-decorator" prefix="decorator" %&gt;<br />
&lt;decorator:usePage id="myPage" /&gt;<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&lt;title&gt;My Site -<br />
&lt;decorator:title default="Welcome!" /&gt;<br />
&lt;/title&gt;<br />
&lt;decorator:head /&gt;<br />
&lt;/head&gt;<br />
<br />
&lt;body&gt;<br />
&lt;h1&gt;&lt;decorator:title default="Welcome!" /&gt;&lt;/h1&gt;<br />
&lt;h3&gt;<br />
&lt;a href="mailto:&lt;decorator:getProperty property="meta.author" <br />
default="staff@example.com" /&gt;"&gt;<br />
&lt;decorator:getProperty property="meta.author"<br />
default="staff@example.com" /&gt;<br />
&lt;/a&gt;&lt;/h3&gt;&lt;hr /&gt;<br />
&lt;decorator:body /&gt;<br />
&lt;p&gt;&lt;small&gt;<br />
(&lt;a href="?printable=true"&gt;printable version&lt;/a&gt;<img class="smiley" title=";)" alt=";)" src="http://blog.51766.com/images/smileys/wink.gif" /><br />
&lt;/small&gt;<br />
&lt;/p&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;</code></pre>
<p>可以看到我们使用了<font face="Courier New">getProperty标签</font>的一个默认属性——如果没有指定author，我们就设定其为staff。如果你决定使用这个模型储存页面的meta数据，你或许需要和你的开发伙伴一起来确定将使用什么标签以及如何使用他们。简单的，你或许想要使用meta标签来描述诸如页面作者及时间戳之类的东西。更复杂一些，你或许会想像XML文件一样标准化的管理你的站点导航，同时使用meta标签来通过页面节点转到装饰器。（At
the complex end, you may do things like standardize on an XML file to manage
your site navigation and use a <code><font face="新宋体">meta</font></code> tag to
pass the page's node to the decorator. ）</p>
<p>图六显示了应用上面的装饰器JSP页面之后生成的结果。</p>
<p><img alt="Figure 6" src="http://today.java.net/images/2004/03/sitemesh_fig6.gif" height="359" width="392" /><br />
<em>图六：meta标签显示</em></p>
<p>这些页面属性非常强大，并且拥有着很多不同的特性，并不仅止于meta标签（<a href="http://www.opensymphony.com/sitemesh/api/com/opensymphony/module/sitemesh/HTMLPage.html">
常用页面特性列表</a>）。使用SiteMesh一段时间之后，你就会开始思考HTML和JSP作为一种简单标记语言的机制——接近最原始的HTML——无需操作就可以完整的切换到XML/XSL
或其他模版引擎。</p>
<h2 id="Summary">小结</h2>
<p>综上所述，SiteMesh
提供了一个强大、易用、易结合的机制来使用页面模版。可以想象，它将会有很广泛的用户群。例如，你可以定义一个装饰器针对不同的浏览器输出额外的页面调试信息（和特定web浏览器结合之后将产生一个特别的功能，你可以强制指定使用某一种用户代理）。你也可以定义一个装饰器产生stripped-down
XML输出，用来进行简单的自动化测试。你甚至可以使用装饰器从其他页面提取内容，例如输出到一些简单的门户容器。</p>
<p>从<em>sitemesh-blank.war</em>入手比较容易，但我建议学习 <em>sitemesh-example.war</em>
以获取更多的特性和思想。</p>
<p>不论你如何使用SiteMesh，我都发现它将大量的代码从表现层中移到我的装饰器中，而无需学习一种新的编程语言或是模版系统。 </p>
<p>对了，作为最后的补充，如果你仍然对组合建立web页面感兴趣，可以查看<a href="http://home.worldonline.dk/viksoe/asmil.htm">home.worldonline.dk/viksoe/asmil.htm</a>
。</p>
<p>祝好运并享受编程的乐趣！</p>
</div>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/184533.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-07 16:25 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/07/184533.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Taglib原理和实现 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/06/184218.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Thu, 06 Mar 2008 04:03:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/06/184218.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/184218.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/06/184218.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/184218.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/184218.html</trackback:ping><description><![CDATA[<h1>Taglib原理和实现</h1>
<p align="left">Tag究竟是什么？如何实现一个Tag？&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;一个tag就是一个普通的java类，它惟一特别之处是它必须继承TagSupport或者BodyTagSupport类。这两个类提供了一些方法，负责jsp页面和你编写的类之间的交互，例如输入，输出。而这两个类是由jsp容器提供的，无须开发人员自己实现。换句话说，你只需把实现了业务逻辑的类继承TagSupport或者BodyTagSupport，再做一些特别的工作，你的类就是一个Tag。并且它自己负责和jsp页面的交互，不用你多操心。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&#8220;特别的工作&#8221;通常有以下几个步骤： <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;1）提供属性的set方法，此后这个属性就可以在jsp页面设置。以jstl标签为例 c:out value=""/，这个value就是jsp数据到tag之间的入口。所以tag里面必须有一个setValue方法，具体的属性可以不叫value。例如setValue(String data){this.data = data;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;这个&#8220;value&#8221;的名称是在tld里定义的。取什么名字都可以，只需tag里提供相应的set方法即可。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;2）处理 doStartTag 或 doEndTag 。这两个方法是 TagSupport提供的。 还是以c:out value=""/为例，当jsp解析这个标签的时候，在&#8220;&lt;&#8221;处触发 doStartTag 事件，在&#8220;&gt;&#8221;时触发 doEndTag 事件。通常在 doStartTag 里进行逻辑操作，在 doEndTag 里控制输出。<br />
&nbsp;&nbsp;&nbsp;&nbsp;3）编写tld文件。<br />
&nbsp;&nbsp;&nbsp;&nbsp;4）在jsp页面导入tld。这样，你的jsp页面就可以使用自己的tag了。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;通常你会发现自己绝大多数活动都集中在 doStartTag 或 doEndTag 方法里。确实如此，熟悉一些接口和类之后，写taglib很容易。正如《jsp设计》的作者所言：里面的逻辑稍微有点复杂，但毕竟没有火箭上天那么难。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>一个简单的例子：OutputTag</strong> <br />
<br />
</p>
<ccid_nobr></ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt" bgcolor="#e6e6e6">
            <pre><ccid_code></ccid_code>package diegoyun;
            import javax.servlet.jsp.JspException;
            import javax.servlet.jsp.JspWriter;
            import javax.servlet.jsp.tagext.TagSupport;
            /**
            * @author chenys
            */
            public class OutputTag extends TagSupport
            {
            private String name=null;
            public void setName(String name)
            {
            this.name = name;
            }
            public int doStartTag() throws JspException{
            try
            {
            JspWriter out = pageContext.getOut();
            out.print("Hello! " + name);
            }
            catch (Exception e)
            {
            throw new JspException(e);
            }
            return EVAL_PAGE;
            }
            }</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;简要说明：<br />
&nbsp;&nbsp;&nbsp;&nbsp;1 如何输出到jsp页面：调用JspWriter JspWriter out = pageContext.getOut();out.print......记住这个方法就可以了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;2 输出后如何作处理，函数会返回几个值之一。EVAL_PAGE 表示tag已处理完毕，返回jsp页面。还有几个值，例如 EVAL_BODY_AGAIN 和EVAL_BODY_INCLUDE等，后面我们会作讨论。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>编写tld</strong> <br />
<br />
<ccid_nobr></ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt" bgcolor="#e6e6e6">
            <pre><ccid_code></ccid_code>&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
            &lt;!DOCTYPE taglib
            PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
            "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"&gt;
            &lt;taglib&gt;
            &lt;tlib-version&gt;1.0&lt;/tlib-version&gt;
            &lt;jsp-version&gt;1.2&lt;/jsp-version&gt;
            &lt;short-name&gt;diego&lt;/short-name&gt;
            &lt;!--OutputTag--&gt;
            &lt;tag&gt;
            &lt;name&gt;out&lt;/name&gt;
            &lt;tag-class&gt;diegoyun.OutputTag&lt;/tag-class&gt;
            &lt;body-content&gt;empty&lt;/body-content&gt;
            &lt;attribute&gt;
            &lt;name&gt;name&lt;/name&gt;
            &lt;required&gt;false&lt;/required&gt;
            &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;
            &lt;/attribute&gt;
            &lt;/tag&gt;
            &lt;/taglib&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;在WEB-INF下新建tlds文件夹，把这个文件取名为diego.tld，放到tlds文件夹下。路径应该这样：WEB-INF\tlds\diego.tld <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;关于tld的简单说明：<br />
&nbsp;&nbsp;&nbsp;&nbsp;short-name：taglib的名称，也称为前缀。比如&#8220;c:out value=""/&#8221; 里的&#8220;c&#8221;<br />
&nbsp;&nbsp;&nbsp;&nbsp;name：tag的名字。例如&#8220;c:out value=""/&#8221; 里的"out&#8221;，我们的类也取名为out，由于有前缀作区分，不会混淆<br />
&nbsp;&nbsp;&nbsp;&nbsp;tag-class：具体的tag类。带包名<br />
&nbsp;&nbsp;&nbsp;&nbsp;body-content：指tag之间的内容。例如c:out value="" ...... /c 起始和关闭标签之间就是body-content。由于没有处理body-content，所以上面设为empty<br />
&nbsp;&nbsp;&nbsp;&nbsp;&#8220;attribute&#8221;里的name：属性名字。例如c:out value=""/里的value。名字可任意取，只要类里提供相应的set方法即可。<br />
&nbsp;&nbsp;&nbsp;&nbsp;required：是否必填属性。<br />
&nbsp;&nbsp;&nbsp;&nbsp;rtexprvalue：是否支持运行时表达式取值。这是tag的强大功能。以后我们会讨论。暂时设为false <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>编写jsp页面</strong> <br />
<br />
<ccid_nobr></ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt" bgcolor="#e6e6e6">
            <pre><ccid_code></ccid_code>&lt;%@ page language="java"%&gt;
            &lt;%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%&gt;
            &lt;html&gt;
            &lt;body&gt;
            Test Tag:
            &lt;diego:out name="diegoyun"/&gt;
            &lt;/body&gt;
            &lt;/html&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;我的编程环境是eclipse+tomcat.启动服务器，如果一切按照上面步骤的话，就能看到 Test Tag: Hello! diegoyun 字样。最简单的tag就这么出来了。并不难,是不是?(T111) </span><br />
<br />
================================================================<br />
Taglib 原理和实现:第二章 让Tag支持El表达式<br />
<ul>
    <li class="xl_b3">
    <p>1.先看这么一个例子</p>
    <p>&lt;%@ page contentType="text/html; charset=gb2312" language="java"%&gt;</p>
    <p>&lt;%@ taglib uri="/WEB-INF/tlds/c.tld" prefix="c"%&gt;</p>
    <p>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&gt;</p>
    <p>&lt;html&gt;</p>
    <p>&lt;body&gt;</p>
    <p>&lt;%</p>
    <p>String tut = "tutorial";</p>
    <p>request.setAttribute("tut",tut);</p>
    <p>%&gt;</p>
    <p>The String in request is :</p>
    <p>&lt;c:out value="${tut}"/&gt;</p>
    <p>&lt;/body&gt;</p>
    <p>&lt;/html&gt;</p>
    <p>2.如何支持el表达式</p>
    <p>在路径org.apache.taglibs.standard.lang.support下，有个叫ExpressionEvaluatorManager.evaluate 的方法，当el表达式作为入参时，调用这个方法，在tag内即可自动把el表达式转化。例如，你想tag的value字段支持el表达式，那么只需在 set方法里如下调用：</p>
    <p>public void setValue(Object value)throws JspException</p>
    <p>{</p>
    <p>&nbsp;&nbsp;&nbsp; this.value = ExpressionEvaluatorManager.evaluate(</p>
    <p>&nbsp;&nbsp;&nbsp; "value", value.toString(), Object.class, this, pageContext);</p>
    <p>}</p>
    <p>&nbsp;&nbsp;&nbsp; ExpressionEvaluatorManager.evaluate有四个参数。第一个表示tag的名字，在取el表达式出错时使用。一般和属性名 字相同。第二个要求字符串，通常简单调用输入对象的toString方法。第三个是类，通常用Object.class。第四个用this即可，第五个是 pageContext变量。</p>
    <p>通常不用对这个方法思考太多。只需改改属性名字，其他照搬即可。</p>
    <p>注意：当你的tag属性支持el表达式时，你必须把它声明为Object对象。如上述的value，应该声明为：</p>
    <p>private Object value = null;</p>
    <p>3.实例：让OutputTag支持El表达式</p>
    <p>package diegoyun;</p>
    <p>import javax.servlet.jsp.JspException;</p>
    <p>import javax.servlet.jsp.JspWriter;</p>
    <p>import javax.servlet.jsp.tagext.TagSupport;</p>
    <p>import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;</p>
    <li class="xl_b3">
    <p>public class NewOutputTag extends TagSupport</p>
    <p>{</p>
    <p>private Object name = null;</p>
    <p>public void setName(Object name) throws JspException</p>
    <p>{</p>
    <p>this.name = ExpressionEvaluatorManager.evaluate(</p>
    <p>"name", name.toString(), Object.class, this, pageContext);</p>
    <p>}</p>
    <p>public int doStartTag() throws JspException{</p>
    <p>try</p>
    <p>{</p>
    <p>JspWriter out = pageContext.getOut();</p>
    <p>out.print("Hello! " + name);</p>
    <p>}</p>
    <p>catch (Exception e)</p>
    <p>{</p>
    <p>throw new JspException(e);</p>
    <p>}</p>
    <p>return EVAL_PAGE;</p>
    <p>}</p>
    <p>}</p>
    <p>在diego.tld里添加声明</p>
    <p>&lt;!--NewOutputTag--&gt;</p>
    <p>&lt;tag&gt;</p>
    <p>&lt;name&gt;newout&lt;/name&gt;</p>
    <p>&lt;tag-class&gt;diegoyun.NewOutputTag&lt;/tag-class&gt;</p>
    <p>&lt;body-content&gt;empty&lt;/body-content&gt;</p>
    <p>&lt;attribute&gt;</p>
    <p>&lt;name&gt;name&lt;/name&gt;</p>
    <p>&lt;required&gt;false&lt;/required&gt;</p>
    <p>&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;</p>
    <p>&lt;/attribute&gt;</p>
    <p>&lt;/tag&gt;</p>
    <p>编写jsp测试</p>
    <p>&lt;%@ page language="java" %&gt;</p>
    <p>&lt;%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%&gt;</p>
    <p>&lt;html&gt;</p>
    <p>&lt;body bgcolor="#FFFFFF"&gt;</p>
    <p>&lt;%</p>
    <p>String s = "diego";</p>
    <p>request.setAttribute("name",s);</p>
    <p>%&gt;</p>
    <p>Test El supported tag:</p>
    <p>&lt;br&gt;</p>
    <p>&lt;diego:newout name="${name}"/&gt;</p>
    <p>&lt;/body&gt;</p>
    <p>&lt;/html&gt;</p>
    <p>可以看到页面输出为：</p>
    <p>Test El supported tag:</p>
    <p>Hello! diego</p>
    </li>
</ul>
=================================================<br />
Taglib 原理和实现:第三章 tag之间的嵌套和属性读取<br />
<br />
1。问题：在request里有一个 Man 对象，它有两个属性：name和age。现在，我们想用一个嵌套的tag，父tag取得对象，子tag取得name属性并显示在页面上。例如，它的形式如下：<br />
&lt;diego:with object="${Man}"&gt;<br />
&nbsp;&nbsp; &lt;diego:output property="name"/&gt;<br />
&lt;/diego:with&gt;<br />
object 支持el表达式，表示取得 Man 对象。output的property表示从该对象取得名为name的属性。<br />
<br />
2。如何支持tag之间的嵌套<br />
在子tag里调用getParent 方法，可以得到父tag对象。用 findAncestorWithClass 方法，则可以通过递归找到想要找的tag。例如<br />
&lt;diego:with object="${people}"&gt;&nbsp;&nbsp;&nbsp; &lt;!--表示取得一个对象--&gt;<br />
&nbsp;&nbsp; &lt; diego:withCollection property="men"&gt; &lt;!--表示取得对象里的一个属性，这个属性是个 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection,Collection里添加了许多man，每个man有名字和年龄--&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;diego:output property="name"/&gt;&nbsp;&nbsp; &lt;!--取得name属性并显示--&gt;<br />
&nbsp;&nbsp; &lt;/diego:withCollection&gt;<br />
&lt;/diego:with&gt;<br />
对于最内层的outputTag来说，调用getParent，可以得到 withCollectionTag，<br />
通过如findAncestorWithClass（this，WithTag.class）的方式，可以得到withTag<br />
得到Tag之后，就可以取得Tag的属性，进行业务逻辑处理，然后输出到jsp<br />
<br />
3。如何支持类属性查找功能<br />
显然，在上面的outputTag中，我们要根据属性的名字，查找类中有没有这个属性。然后取出属性的值并显示。通常，这可以编写自己的反射函数来完成。 更简单的办法，是通过 BeanUtil 的PropertyUtils方法来完成功能。BeanUtil 是apache上的一个开源项目。<br />
示例如下：<br />
import org.apache.commons.beanutils.PropertyUtils;<br />
。。。。。。<br />
property = PropertyUtils.getProperty(currentClass, propertyName);<br />
propertyName是待查找属性的名字，例如上面的"name"，currentClass是待查找的类，例如上面的People<br />
记得把 commons-beanutils.jar添加到WEB-INF\lib目录下<br />
<br />
4。现在让我们实现开篇提出的问题，编写WithTag如下：<br />
<br />
package diegoyun;<br />
<br />
import java.io.IOException;<br />
<br />
import javax.servlet.jsp.JspException;<br />
import javax.servlet.jsp.tagext.BodyTagSupport;<br />
<br />
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;<br />
<br />
/**<br />
* @author chenys<br />
*/<br />
public class WithTag extends BodyTagSupport<br />
{<br />
private Object value = null;<br />
private Object output = null;<br />
<br />
public void setOutput(Object output)<br />
{<br />
&nbsp;&nbsp; this.output = output;<br />
}<br />
public Object getValue()<br />
{<br />
&nbsp;&nbsp; return value;<br />
}<br />
public void setValue(Object value)throws JspException<br />
{<br />
&nbsp;&nbsp; this.value = ExpressionEvaluatorManager.evaluate(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "value", value.toString(), Object.class, this, pageContext);<br />
}<br />
public int doStartTag()<br />
{<br />
&nbsp;&nbsp; return EVAL_BODY_INCLUDE;<br />
}<br />
public int doEndTag()throws JspException<br />
{<br />
&nbsp;&nbsp; try<br />
&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; pageContext.getOut().print(output);<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; catch (IOException e)<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp; throw new JspException(e);<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; return EVAL_PAGE;<br />
}<br />
}<br />
<br />
编写 NestedOutputTag 如下：<br />
<br />
package diegoyun;<br />
<br />
import javax.servlet.jsp.JspException;<br />
import javax.servlet.jsp.tagext.BodyTagSupport;<br />
<br />
import org.apache.commons.beanutils.PropertyUtils;<br />
<br />
/**<br />
* @author chenys<br />
*/<br />
public class NestedOutputTag extends BodyTagSupport<br />
{<br />
private String property = null;<br />
<br />
public void setProperty(String property)<br />
{<br />
&nbsp;&nbsp; this.property = property;<br />
}<br />
<br />
public int doEndTag()throws JspException<br />
{&nbsp;&nbsp;<br />
&nbsp;&nbsp; WithTag parent =(WithTag)getParent();&nbsp;&nbsp;<br />
&nbsp;&nbsp; if(parent==null) <br />
&nbsp;&nbsp;&nbsp; throw new JspException("Can not find parent Tag ");<br />
&nbsp;&nbsp; try<br />
&nbsp;&nbsp; { <br />
&nbsp;&nbsp;&nbsp; Object propertyValue = PropertyUtils.getProperty(parent.getValue(), property);<br />
&nbsp;&nbsp;&nbsp; parent.setOutput(propertyValue);<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; catch (Exception e)<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp; throw new JspException(e);<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; return EVAL_PAGE;<br />
}<br />
}<br />
<br />
在包diegoyun下添加一个包vo，在vo下写一个Man类：<br />
<br />
package diegoyun.vo;<br />
<br />
/**<br />
* @author chenys<br />
*/<br />
public class Man<br />
{<br />
private String name = null;<br />
private int age = 0;<br />
<br />
public int getAge()<br />
{<br />
&nbsp;&nbsp; return age;<br />
}<br />
public void setAge(int age)<br />
{<br />
&nbsp;&nbsp; this.age = age;<br />
}<br />
public String getName()<br />
{<br />
&nbsp;&nbsp; return name;<br />
}<br />
public void setName(String name)<br />
{<br />
&nbsp;&nbsp; this.name = name;<br />
}<br />
}<br />
<br />
<br />
写tld<br />
<br />
&lt;!--WithTag--&gt;<br />
&lt;tag&gt;<br />
&nbsp;&nbsp; &lt;name&gt;with&lt;/name&gt;<br />
&nbsp;&nbsp; &lt;tag-class&gt;diegoyun.WithTag&lt;/tag-class&gt;<br />
&nbsp;&nbsp; &lt;body-content&gt;JSP&lt;/body-content&gt;<br />
&nbsp;&nbsp; &lt;attribute&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;name&gt;value&lt;/name&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;required&gt;false&lt;/required&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;<br />
&nbsp;&nbsp; &lt;/attribute&gt;<br />
&lt;/tag&gt;<br />
&lt;!--OutputTag3--&gt;<br />
&lt;tag&gt;<br />
&nbsp;&nbsp; &lt;name&gt;nestedout&lt;/name&gt;<br />
&nbsp;&nbsp; &lt;tag-class&gt;diegoyun.NestedOutputTag&lt;/tag-class&gt;<br />
&nbsp;&nbsp; &lt;body-content&gt;empty&lt;/body-content&gt;<br />
&nbsp;&nbsp; &lt;attribute&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;name&gt;property&lt;/name&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;required&gt;false&lt;/required&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;<br />
&nbsp;&nbsp; &lt;/attribute&gt;<br />
&lt;/tag&gt;<br />
<br />
写jsp页面<br />
&lt;%@ page language="java" %&gt;<br />
&lt;%@ page import="diegoyun.vo.*"%&gt;<br />
&lt;%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%&gt;<br />
<br />
&lt;html&gt;<br />
&lt;body bgcolor="#FFFFFF"&gt;<br />
&lt;%<br />
Man man = new Man();<br />
man.setName("diego");<br />
<br />
request.setAttribute("man",man);<br />
%&gt;<br />
Test nested tag:<br />
&lt;br&gt;<br />
&lt;diego:with value="${man}"&gt;<br />
&lt;diego:nestedout property="name"/&gt;<br />
&lt;/diego:with&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
<br />
运行页面，则可以看到：<br />
Test nested tag: <br />
diego <br />
<br />
5。结束语：<br />
上述例子简单描绘了嵌套的Tag之间如何交互。通常子Tag负责取得数据，然后设置父Tag的属性，最后在父Tag里显示到jsp页面。如上面的例子，父 Tag 的 output 表示待打印的对象，通过 nestedoutTag 取得name的值，设置output，然后打印出来。 <br />
通过支持El表达式和动态属性联结，Tag可以实现强大的处理功能。将逻辑都集中到Tag里，极大的简化页面的编写。<br />
<br />
<br />
=================================================================<br />
Taglib 原理和实现:第四章 循环的Tag<br />
<br />
<br />
1。问题：在request里的 People 对象，有个属性叫 men ，men 是一个Collection ，有许多个man 。现在，把 collection里的man的名字都显示出来<br />
<br />
显然，这是一个嵌套Tag的问题。有三个Tag互相作用：最外层的Tag找到People对象，中间的Tag取得Collection，子Tag负责打印。<br />
例如:<br />
&lt;diego:withObject value="${people}"&gt;<br />
&nbsp;&nbsp; &lt;diego:withCollection property="men"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;diego:elementout property="name"/&gt;&nbsp;&nbsp;<br />
&nbsp;&nbsp; &lt;/diego:withCollection&gt;<br />
&lt;/diego:withObject&gt;<br />
<br />
思路如下:<br />
1.编写WithObjectTag,负责从El表达式中取得对象<br />
2.编写WithCollectionTag,负责从对象中取得 Collection ,遍历 Collection ,每遍历一次 Collection ,执行一次body<br />
3.编写ElementoutTag ,把 Collection 中每个men对象的 name 打印出来<br />
<br />
<br />
2. 完整程序如下：<br />
<br />
在上例的diegoyun.vo包内，编写 People 类<br />
<br />
package diegoyun.vo;<br />
import java.util.Collection;<br />
public class People<br />
{<br />
private Collection men = null; <br />
public Collection getMen()<br />
{<br />
&nbsp;&nbsp; return men;<br />
}<br />
public void setMen(Collection men)<br />
{<br />
&nbsp;&nbsp; this.men = men;<br />
}<br />
}<br />
<br />
编写 withObject ，这是从request里取得People对象的最外层Tag<br />
<br />
package diegoyun;<br />
import javax.servlet.jsp.JspException;<br />
import javax.servlet.jsp.tagext.BodyTagSupport;<br />
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;<br />
public class WithObjectTag extends BodyTagSupport<br />
{<br />
private Object value = null;<br />
<br />
public Object getValue()<br />
{<br />
&nbsp;&nbsp; return value;<br />
}<br />
public void setValue(Object value)throws JspException<br />
{<br />
&nbsp;&nbsp; this.value = ExpressionEvaluatorManager.evaluate(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "value", value.toString(), Object.class, this, pageContext);<br />
}<br />
public int doStartTag()<br />
{&nbsp;&nbsp;<br />
&nbsp;&nbsp; return EVAL_BODY_INCLUDE;<br />
}<br />
public int doEndTag()throws JspException<br />
{&nbsp;&nbsp;<br />
&nbsp;&nbsp; return EVAL_PAGE;<br />
}<br />
}<br />
<br />
<br />
<br />
编写WithCollectionTag，该Tag负责取得Collection，并遍历执行子Tag<br />
package diegoyun;<br />
<br />
import java.util.Collection;<br />
import java.util.Iterator;<br />
<br />
import javax.servlet.jsp.JspException;<br />
import javax.servlet.jsp.tagext.BodyTagSupport;<br />
<br />
import org.apache.commons.beanutils.PropertyUtils;<br />
<br />
public class WithCollectionTag extends BodyTagSupport {<br />
private Object element = null;<br />
<br />
private Collection list = null;<br />
<br />
private Iterator iterator = null;<br />
<br />
public Object getElement() {<br />
&nbsp;&nbsp; return element;<br />
}<br />
<br />
public void setProperty(String property) throws JspException {<br />
//取得父Tag对象,并且得到Collection<br />
&nbsp;&nbsp; WithObjectTag parent = (WithObjectTag) getParent();<br />
&nbsp;&nbsp; if (parent == null)<br />
&nbsp;&nbsp;&nbsp; throw new JspException("parent tag is null");<br />
&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp; Object propertyValue = PropertyUtils.getProperty(parent.getValue(),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; property);<br />
&nbsp;&nbsp;&nbsp; this.list = (Collection) propertyValue;<br />
&nbsp;&nbsp;&nbsp; if (list == null)<br />
&nbsp;&nbsp;&nbsp;&nbsp; throw new JspException("Collection is null");<br />
&nbsp;&nbsp; } catch (Exception e) {<br />
&nbsp;&nbsp;&nbsp; throw new JspException(e);<br />
&nbsp;&nbsp; }<br />
}<br />
<br />
public int doStartTag() throws JspException {<br />
//设置第一个元素,然后执行子Tag<br />
&nbsp;&nbsp; iterator = list.iterator();<br />
&nbsp;&nbsp; if (iterator.hasNext())<br />
&nbsp;&nbsp;&nbsp; element = iterator.next();<br />
<br />
&nbsp;&nbsp; return EVAL_BODY_INCLUDE;<br />
}<br />
<br />
public int doAfterBody() {<br />
&nbsp;&nbsp; if (iterator.hasNext()) {<br />
&nbsp;&nbsp; //如果还存在子元素,设置子元素,并且再次执行子Tag<br />
&nbsp;&nbsp; //循环由此而来<br />
&nbsp;&nbsp; //否则不再执行子Tag<br />
&nbsp;&nbsp;&nbsp; element = iterator.next();<br />
&nbsp;&nbsp;&nbsp; return EVAL_BODY_AGAIN;<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp; return EVAL_PAGE;<br />
}<br />
}<br />
<br />
编写 ElementOutputTag<br />
<br />
package diegoyun;<br />
import java.io.IOException;<br />
<br />
import javax.servlet.jsp.JspException;<br />
import javax.servlet.jsp.tagext.TagSupport;<br />
<br />
import org.apache.commons.beanutils.PropertyUtils;<br />
<br />
public class ElementOutputTag extends TagSupport<br />
{<br />
private Object propertyValue&nbsp;&nbsp; = null;<br />
public void setProperty(String property)throws JspException<br />
{<br />
&nbsp;&nbsp; WithCollectionTag parent = (WithCollectionTag)getParent();<br />
&nbsp;&nbsp; if(parent == null)<br />
&nbsp;&nbsp;&nbsp; throw new JspException("parent tag is null");<br />
&nbsp;&nbsp; try<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp; //判断上层tag中是否存在该属性名称,如果存在,取得属性值,否则报错<br />
&nbsp;&nbsp;&nbsp; propertyValue = PropertyUtils.getProperty(parent.getElement(), property);<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; catch (Exception e)<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp; throw new JspException(e);<br />
&nbsp;&nbsp; }<br />
}<br />
public int doEndTag()throws JspException<br />
{<br />
&nbsp;&nbsp; try<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp; //简单的把值打印到jsp页面<br />
&nbsp;&nbsp;&nbsp; pageContext.getOut().print(propertyValue);<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; catch (IOException e)<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp; throw new JspException(e);<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; return EVAL_PAGE;<br />
}<br />
}<br />
<br />
编写tld<br />
&lt;!--WithObjectTag--&gt;<br />
&lt;tag&gt;<br />
&nbsp;&nbsp; &lt;name&gt;withObject&lt;/name&gt;<br />
&nbsp;&nbsp; &lt;tag-class&gt;diegoyun.WithObjectTag&lt;/tag-class&gt;<br />
&nbsp;&nbsp; &lt;body-content&gt;JSP&lt;/body-content&gt;<br />
&nbsp;&nbsp; &lt;attribute&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;name&gt;value&lt;/name&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;required&gt;false&lt;/required&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;<br />
&nbsp;&nbsp; &lt;/attribute&gt;<br />
&lt;/tag&gt;<br />
&lt;!--WithCollectionTag--&gt;<br />
&lt;tag&gt;<br />
&nbsp;&nbsp; &lt;name&gt;withCollection&lt;/name&gt;<br />
&nbsp;&nbsp; &lt;tag-class&gt;diegoyun.WithCollectionTag&lt;/tag-class&gt;<br />
&nbsp;&nbsp; &lt;body-content&gt;JSP&lt;/body-content&gt;<br />
&nbsp;&nbsp; &lt;attribute&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;name&gt;property&lt;/name&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;required&gt;false&lt;/required&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;<br />
&nbsp;&nbsp; &lt;/attribute&gt;<br />
&lt;/tag&gt;<br />
&lt;!--ElementOutputTag--&gt;<br />
&lt;tag&gt;<br />
&nbsp;&nbsp; &lt;name&gt;elementout&lt;/name&gt;<br />
&nbsp;&nbsp; &lt;tag-class&gt;diegoyun.ElementOutputTag&lt;/tag-class&gt;<br />
&nbsp;&nbsp; &lt;body-content&gt;empty&lt;/body-content&gt;<br />
&nbsp;&nbsp; &lt;attribute&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;name&gt;property&lt;/name&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;required&gt;false&lt;/required&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;<br />
&nbsp;&nbsp; &lt;/attribute&gt;<br />
&lt;/tag&gt;<br />
<br />
编写jsp<br />
&lt;%@ page language="java" %&gt;<br />
&lt;%@ page import="diegoyun.vo.*"%&gt;<br />
&lt;%@ page import="java.util.*"%&gt;<br />
&lt;%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%&gt;<br />
<br />
&lt;html&gt;<br />
&lt;body bgcolor="#FFFFFF"&gt;<br />
&lt;%<br />
Collection c = new ArrayList();<br />
<br />
Man man1 = new Man();<br />
man1.setName("diego");<br />
c.add(man1);<br />
<br />
Man man2 = new Man();<br />
man2.setName("Zidane");<br />
c.add(man2);<br />
<br />
Man man3 = new Man();<br />
man3.setName("Rui");<br />
c.add(man3);<br />
<br />
People p =new People();<br />
p.setMen(c);<br />
request.setAttribute("people",p);<br />
%&gt;<br />
Test loop tag:<br />
&lt;br&gt;<br />
&lt;diego:withObject value="${people}"&gt;<br />
&lt;diego:withCollection property="men"&gt;<br />
&nbsp;&nbsp; &lt;diego:elementout property="name"/&gt;<br />
&lt;br&gt;<br />
&lt;/diego:withCollection&gt;<br />
&lt;/diego:withObject&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
<br />
运行,则可以看到:<br />
Test loop tag: <br />
diego <br />
Zidane <br />
Rui<br />
<br />
=======================================================================<br />
Taglib原理和实现 第五章：再论支持El表达式和jstl标签<br />
<br />
<br />
1.问题：你想和jstl共同工作。比如，在用自己的标签处理一些逻辑之后，让jstl处理余下的工作。<br />
<br />
2。看这个jsp例子：<br />
....<br />
&lt;%<br />
String name="diego";<br />
request.setAttribute("name",name);<br />
%&gt;<br />
<br />
&lt;c:out value="${name}"/&gt;<br />
......<br />
<br />
&nbsp;&nbsp; 许多jstl标签支持El表达式，所以，只要你在自己的标签内部把值塞进request，其他jstl标签就能使用它们<br />
<br />
3。下面这个例子，从request里面取得对象，找到它属性的值，塞到request里去。<br />
<br />
package diegoyun;<br />
<br />
import javax.servlet.jsp.JspException;<br />
import javax.servlet.jsp.tagext.TagSupport;<br />
import org.apache.commons.beanutils.PropertyUtils;<br />
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;<br />
<br />
public class SetVarTag extends TagSupport<br />
{<br />
private Object value = null; <br />
private String property = null; <br />
private String var = null;<br />
public void setVar(String var)<br />
{<br />
&nbsp;&nbsp; this.var = var;<br />
}<br />
public void setProperty(String property)<br />
{<br />
&nbsp;&nbsp; this.property = property;<br />
}<br />
public void setValue(Object value)throws JspException{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.value = ExpressionEvaluatorManager.evaluate(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "value", value.toString(), Object.class, this, pageContext);&nbsp;&nbsp;<br />
}<br />
public int doEndTag() throws JspException{<br />
&nbsp;&nbsp; Object propertyValue = null;<br />
&nbsp;&nbsp; try{<br />
&nbsp;&nbsp;&nbsp; propertyValue = PropertyUtils.getProperty(value, property);<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; catch (Exception e) {<br />
&nbsp;&nbsp;&nbsp; throw new JspException(e);<br />
&nbsp;&nbsp; }&nbsp;&nbsp;<br />
&nbsp;&nbsp; pageContext.setAttribute(var,propertyValue);<br />
&nbsp;&nbsp; return EVAL_PAGE;<br />
}<br />
}<br />
<br />
编写tld<br />
&lt;!--SetVarTag--&gt;<br />
&lt;tag&gt;<br />
&nbsp;&nbsp; &lt;name&gt;set&lt;/name&gt;<br />
&nbsp;&nbsp; &lt;tag-class&gt;diegoyun.SetVarTag&lt;/tag-class&gt;<br />
&nbsp;&nbsp; &lt;body-content&gt;empty&lt;/body-content&gt;<br />
&nbsp;&nbsp; &lt;attribute&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;name&gt;value&lt;/name&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;required&gt;true&lt;/required&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;<br />
&nbsp;&nbsp; &lt;/attribute&gt;<br />
&nbsp;&nbsp; &lt;attribute&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;name&gt;property&lt;/name&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;required&gt;false&lt;/required&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;<br />
&nbsp;&nbsp; &lt;/attribute&gt;<br />
&nbsp;&nbsp; &lt;attribute&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;name&gt;var&lt;/name&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;required&gt;false&lt;/required&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;<br />
&nbsp;&nbsp; &lt;/attribute&gt;<br />
&lt;/tag&gt; <br />
<br />
编写jsp<br />
&lt;%@ page language="java" %&gt;<br />
&lt;%@ page import="diegoyun.vo.*"%&gt;<br />
&lt;%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%&gt;<br />
&lt;%@ taglib uri="/WEB-INF/tlds/c.tld" prefix="c"%&gt;<br />
&lt;html&gt;<br />
&lt;body bgcolor="#FFFFFF"&gt;<br />
&lt;%<br />
Man man = new Man();<br />
man.setName("diego");<br />
request.setAttribute("man",man);<br />
%&gt;<br />
Get value from request and set it's property value into request:&lt;br&gt;<br />
&lt;diego:set value="${man}" property="name" var="myname"/&gt;<br />
now use OutTag of jstl taglib to get the name:&lt;br&gt;<br />
value is : &lt;c:out value="${myname}" /&gt;<br />
<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
<br />
运行，效果如下：<br />
<br />
Get value from request and set it's property value into request:<br />
now use OutTag of jstl taglib to get the name:<br />
value is : diego <br />
<br />
4。结语。和jstl交互是非常有用的技术。在jstl里提供了许多完成基本功能的标签，如输出，循环，条件选择等。仅在处理自己特定逻辑的时候才实现自己的标签，并提供和jstl交互，能大大提高重用性和减少工作量<br />
<br />
<br />
========================================================<br />
Taglib原理和实现 第六章：标签内常用方法总结<br />
<br />
<br />
1。支持el表达式：<br />
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;<br />
private Object value = null;<br />
this.value = ExpressionEvaluatorManager.evaluate("value", value.toString(), Object.class, this, pageContext); <br />
<br />
2.用BeanUtil取属性值<br />
import org.apache.commons.beanutils.PropertyUtils;<br />
private String property=null;<br />
Object propertyValue = PropertyUtils.getProperty(value, property);<br />
<br />
3.设置request里的值<br />
pageContext.setAttribute("var",propertyValue);<br />
<br />
4。打印<br />
pageContext.getOut().print(outputString);<br />
<br />
5。取得父标签，取得想要的标签，即使它非父<br />
getParent()<br />
findAncestorWithClass(this,ancestorTag.class);<br />
<br />
6。标签自带方法和常量，方法按照容器的调用顺序排列。示例&nbsp;&nbsp;<br />
&lt;c:if test="..."&gt;<br />
&nbsp;&nbsp; &lt;c:out value="..."/&gt;<br />
&lt;/c:if&gt;<br />
doStartTag : 容器解析到c:if左尖括号（&#8220;&lt;&#8221;）时调用<br />
doInitBody : 容器解析到c:if右尖括号（&#8220;&gt;&#8221;）和c:out左尖括号（&#8220;&lt;&#8221;）时调用<br />
doAfterBody : 容器解析到c:out结束标记（&#8220;/&gt;&#8221;）时调用<br />
doEndTag ：容器解析到c:if结束标记（&#8220;/&gt;&#8221;）时调用<br />
<br />
EVAL_BODY_SKIP : 通常在 doStartTag 方法里调用，忽略标签包括的内容，假如返回这个值，上面的c:if忽略c:out<br />
EVAL_BODY_INCLUDE ：通常在 doAfterBody 方法里调用，再次执行body，假如返回这个值，上面的c:out被执行多次<br />
EVAL_PAGE ：可在任何方法里调用。返回jsp页面
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/184218.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-06 12:03 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/06/184218.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Designing JSP Custom Tag Libraries zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/05/184053.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Wed, 05 Mar 2008 09:06:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/05/184053.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/184053.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/05/184053.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/184053.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/184053.html</trackback:ping><description><![CDATA[http://www.onjava.com/pub/a/onjava/2000/12/15/jsp_custom_tags.html<br />
<br />
<br />
<h2>Designing JSP Custom Tag Libraries</h2>
by <a href="http://www.onjava.com/pub/au/113">Sue Spielman</a><br />
04/19/2001
<p>In this article you'll learn</p>
<div class="adtag right" style="margin: 8px; width: 336px"><!-- dy --><!-- begin ad tag --><script language="JavaScript" type="text/javascript">
//<![cdata[ var ord="(ord" != null ? ord : Math.random()*10000000000000000 );
document.write('<script language="JavaScript" src="http://ad.doubleclick.net/adj/ttm.onjava/j2eeart;sz=336x280;tile=3;ord=' + ord + '?" type="text/javascript"><\/script>'); //]]&gt; </script>
<script language="JavaScript" src="http://ad.doubleclick.net/adj/ttm.onjava/j2eeart;sz=336x280;tile=3;ord=9303969195885698?" type="text/javascript"></script><!-- Template id="1" Template name="Banner" Creative (Flash) --><!-- Copyright 2002 DoubleClick Inc., All rights reserved. --><script src="http://m1.2mdn.net/879366/flashwrite_1_2.js"></script><script language="VBScript">
dcmaxversion = 9
dcminversion = 6
Do
On Error Resume Next
plugin = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash." & dcmaxversion & "")))
If plugin = true Then Exit Do
dcmaxversion = dcmaxversion - 1
Loop While dcmaxversion >= dcminversion
</script>
<object id="FLASH_AD" height="250" width="300" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
<param name="_cx" value="7938" /><param name="_cy" value="6615" /><param name="FlashVars" value="" /><param name="Movie" value="http://m1.2mdn.net/876092/devel300x250.swf?clickTag=http%3A//ad.doubleclick.net/click%253Bh%3Dv8/367a/3/0/%252a/h%253B193511825%253B0-0%253B0%253B25219394%253B4307-300/250%253B25168728/25186585/1%253B%253B%257Efdr%253D193490148%253B0-0%253B0%253B14622588%253B4252-336/280%253B25216140/25233997/1%253B%253B%257Esscs%253D%253fhttp%3A//www.sun.com/events/mysqltour/index.jsp%3Fcid%3D923531" /><param name="Src" value="http://m1.2mdn.net/876092/devel300x250.swf?clickTag=http%3A//ad.doubleclick.net/click%253Bh%3Dv8/367a/3/0/%252a/h%253B193511825%253B0-0%253B0%253B25219394%253B4307-300/250%253B25168728/25186585/1%253B%253B%257Efdr%253D193490148%253B0-0%253B0%253B14622588%253B4252-336/280%253B25216140/25233997/1%253B%253B%257Esscs%253D%253fhttp%3A//www.sun.com/events/mysqltour/index.jsp%3Fcid%3D923531" /><param name="WMode" value="Opaque" /><param name="Play" value="0" /><param name="Loop" value="-1" /><param name="Quality" value="High" /><param name="SAlign" value="" /><param name="Menu" value="-1" /><param name="Base" value="" /><param name="AllowScriptAccess" value="never" /><param name="Scale" value="ShowAll" /><param name="DeviceFont" value="0" /><param name="EmbedMovie" value="0" /><param name="BGColor" value="" /><param name="SWRemote" value="" /><param name="MovieData" value="" /><param name="SeamlessTabbing" value="1" /><param name="Profile" value="0" /><param name="ProfileAddress" value="" /><param name="ProfilePort" value="0" /><param name="AllowNetworking" value="all" /><param name="AllowFullScreen" value="false" /><embed src="http://m1.2mdn.net/876092/devel300x250.swf?clickTag=http%3A//ad.doubleclick.net/click%253Bh%3Dv8/367a/3/0/%252a/h%253B193511825%253B0-0%253B0%253B25219394%253B4307-300/250%253B25168728/25186585/1%253B%253B%257Efdr%253D193490148%253B0-0%253B0%253B14622588%253B4252-336/280%253B25216140/25233997/1%253B%253B%257Esscs%253D%253fhttp%3A//www.sun.com/events/mysqltour/index.jsp%3Fcid%3D923531" quality="high" wmode="opaque" swliveconnect="TRUE" width="300" height="250" bgcolor="#" type="application/x-shockwave-flash" allowscriptaccess="never"></embed></object><noscript><a target="_blank" href="http://ad.doubleclick.net/click%3Bh=v8/367a/3/0/%2a/h%3B193511825%3B0-0%3B0%3B25219394%3B4307-300/250%3B25168728/25186585/1%3B%3B%7Efdr%3D193490148%3B0-0%3B0%3B14622588%3B4252-336/280%3B25216140/25233997/1%3B%3B%7Esscs%3D%3fhttp://www.sun.com/events/mysqltour/index.jsp?cid=923531"><img src="http://m1.2mdn.net/876092/devel300x250.gif" alt="" border="0" /></a></noscript><noscript><a href="http://ad.doubleclick.net/jump/ttm.onjava/j2eeart;sz=336x280;tile=3;ord=7087521304?" target="_blank"><img src="http://ad.doubleclick.net/ad/ttm.onjava/j2eeart;sz=336x280;ord=123456789?" width="336" height="280" border="0" alt=""   /></a></noscript><!-- End ad tag --></div>
    <ul>
        <li>what a custom tag library is,
        <li>why you want to use a custom tag library,
        <li>the composition of a tag library, and
        <li>how to build and use a complete library.</li>
    </ul>
    <h3>What a Custom Tag Library Is</h3>
    <p>If you've ever had the opportunity to build a web application using Java technology, chances are you have used Java Server Pages (JSP) for content display. JSP is the technology that helps separate the front end presentation from the middle and backend tiers. The custom tag library is a powerful feature of JSP v1.1 that aids in that separation. This technology is valuable to anyone who is building production-quality web applications, and it is very applicable in today's market.</p>
    <p>Custom tag libraries allow the Java programmer to write code that provides data access and other services, and they make those features available to the JSP author in a simple to use XML-like fashion. An action in a web application -- for example, gaining database access -- can be customized by using a custom tag in a JSP page. The JSP author doesn't have to understand the underlying detail to complete the action. In short, a tag library is a collection of custom actions presented as tags.</p>
    <p>Custom tags have many features that make them attractive to use from any JSP. Custom tags can</p>
    <ul>
        <li>be customized via attributes passed from the calling page, either staticly or determined at runtime;
        <li>have access to all the objects available to JSP pages including request, response, in and out;
        <li>modify the response generated by the calling page;
        <li>communicate with each other; you can create and initialize a JavaBeans component, create a variable that refers to that bean in one tag, and then use the bean in another tag;
        <li>be nested within one another, allowing for complex interactions within a JSP page; and
        <li>encapsulate both simple and complex behaviors in an easy to use syntax and greatly simplify the readability of JSP pages.</li>
    </ul>
    <p>Any of these points is reason enough to consider using a tag library.</p>
    <p>Let's look at what makes up a tag library and build one step by step.</p>
    <h3>The Composition of a Tag Library</h3>
    <p>There are two types of components for a tag library: the tag library descriptor file and the tag handlers. With these a JSP is able to use tags contained in the library within its page.</p>
    <p><strong>The TLD File</strong><br />
    A tag library descriptor (TLD) file is an XML document that describes the library. A TLD contains information about the library as a whole and about each tag contained in the library. TLDs are used by a JSP container to validate the tags.</p>
    <p>There is typically some header information followed by elements used to define the tag library. The elements are</p>
    <table cellspacing="1" cellpadding="4" bgcolor="#ffffff" border="0">
        <tbody>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary"><strong>&lt;taglib&gt;</strong></p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">The tag library itself.</p>
                </td>
            </tr>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary"><strong>&lt;tlibversion&gt;</strong></p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">The tag library's version.</p>
                </td>
            </tr>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary"><strong>&lt;jspversion&gt;</strong></p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">The JSP specification version the tag library depends on.</p>
                </td>
            </tr>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary"><strong>&lt;shortname&gt;</strong></p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">A simple default name with a mnemonic value. For example, &lt;shortname&gt; may be used as the preferred prefix value in taglib directives and/or to create prefixes for IDs.</p>
                </td>
            </tr>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary"><strong>&lt;uri&gt;</strong></p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">A optional URI that uniquely identifies the tag library.</p>
                </td>
            </tr>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary"><strong>&lt;info&gt;</strong></p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">Descriptive information about the tag library.</p>
                </td>
            </tr>
        </tbody>
    </table>
    <p>Then each tag contained in the library is described. There can be one or many tags per library. There is only one TLD element required for all tags, and that is the one used to specify a tag handler's class: <code>&lt;tagclass&gt;classname&lt;/tagclass&gt;</code></p>
    <p>There are various other elements used to describe tags. Which elements a tag uses will depend on how the tag is implemented in the handler. We'll get to that discussion in the section below.</p>
    <p>If a tag has attributes associated with it, then each attribute must be described within the <code>&lt;tag&gt;</code> element. If an attribute is required by a tag, <code>&lt;required&gt;</code> is set to "true" or "yes". To allow a runtime expression value to be used by the tag, the <code>&lt;rtexpvalue&gt;</code> is set to "true" or "yes". For each attribute of a tag, a Bean-like getter/setter method needs to be defined in the handler class. It's also possible to define scripting variables for use in tags. This is accomplished using a <code>TagExtraInfo</code> class and will be discussed in the tag handler section. If a <code>TagExtraInfo</code> is to be used, the class must be defined using the <code>&lt;teiclass&gt;classname&lt;teiclass&gt;</code> within the tag definition.</p>
    <p>A sample TLD named <code>oreillySample.tld</code> looks like</p>
    <pre><code>&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
    &lt;!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
    "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"&gt;
    &lt;taglib&gt;
    &lt;tlibversion&gt;1.0&lt;/tlibversion&gt;
    &lt;jspversion&gt;1.1&lt;/jspversion&gt;
    &lt;shortname&gt;oreillySamples&lt;/shortname&gt;
    &lt;info&gt;OReilly Sample Tag library&lt;/info&gt;
    &lt;!-A Simple tag --&gt;
    &lt;tag&gt;
    &lt;name&gt;hello&lt;/name&gt;
    &lt;tagclass&gt;oreilly.examples.Hello &lt;/tagclass&gt;
    &lt;!--Body content can have a value of
    empty: no body
    JSP: body that is evaluated by container, then possibly processed by the tag
    tagdependent: body is only processed by tag; JSP in body is not evaluated.
    --&gt;
    &lt;bodycontent&gt;empty&lt;/bodycontent&gt;
    &lt;info&gt;
    This is a simple hello tag.
    &lt;/info&gt;
    &lt;!-- Optional attributes  --&gt;
    &lt;!- personalized name --&gt;
    &lt;attribute&gt;
    &lt;name&gt;name&lt;/name&gt;
    &lt;required&gt;false&lt;/required&gt;
    &lt;rtexpvalue&gt;false&lt;/rtexpvalue&gt;
    &lt;/attribute&gt;
    &lt;/tag&gt;
    &lt;/taglib&gt;</code></pre>
    <p><strong>The Tag Handler</strong><br />
    The tag is defined in a handler class. <code>TagSupport</code> is the base class used for simple tags. It can be found in the <code>javax.servlet.tagext</code> package. What your tag is implementing will depend on what methods could potentially be called and what needs to be implemented. <code>TagSupport</code> and <code>TagBodySupport</code> supply default implementations of the methods listed below.</p>
    <table cellspacing="1" cellpadding="4" bgcolor="#ffffff" border="0">
        <tbody>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary"><strong>If your Tag Handler:</strong></p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary"><strong>You need to implement the following methods:</strong></p>
                </td>
            </tr>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">has no attributes and no body</p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">doStartTag, doEndTag, release</p>
                </td>
            </tr>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">has attributes</p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">doStartTag, doEndTag, set/getAttribute1...N</p>
                </td>
            </tr>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">has a body with no interaction</p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">doStartTag, doEndTag, release</p>
                </td>
            </tr>
            <tr>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">has a body with interaction</p>
                </td>
                <td valign="top" bgcolor="#efefef">
                <p class="secondary">doStartTag, doEndTag, release, doInitBody, doAfterBody</p>
                </td>
            </tr>
        </tbody>
    </table>
    <p>A more advanced feature is the use of scripting variables. Typically an attribute is passed to the tag that contains the ID of the object to be used. The usual operation is that the tag handler retrieves a scripting variable value object using <code>pageContext.getAttribute(name)</code>, performs some processing on it, and then sets the scripting variable's value using the <code>pageContext.setAttribute(name, object)</code>. In addition to setting the value of the variable within the tag handler, you must define a class derived from <code>TagExtraInfo</code> that provides information to the JSP container about the nature of the variable. That class is then listed in the <code>&lt;teiclass&gt;</code> attribute of the tag.</p>
    <p>The Java code for the tag defined in the oreillySample.tld file would look like</p>
    <pre><code>package oreilly.examples
    import javax.servlet.jsp.*;
    import javax.servlet.jsp.tagext.*;
    /**
    * This is a simple tag example to show how content is added to the
    * output stream when a tag is encountered in a JSP page.
    */
    public class Hello extends TagSupport {
    private String name=null;
    /**
    * Getter/Setter for the attribute name as defined in the tld file
    * for this tag
    */
    public void setName(String value){
    name = value;
    }
    public String getName(){
    return(name);
    }
    /**
    * doStartTag is called by the JSP container when the tag is encountered
    */
    public int doStartTag() {
    try {
    JspWriter out = pageContext.getOut();
    out.println("&lt;table border="\1\"&gt;");
    if (name != null)
    out.println("&lt;tr&gt;&lt;td&gt; Hello " + name + " &lt;/td&gt;&lt;/tr&gt;");
    else
    out.println("&lt;tr&gt;&lt;td&gt; Hello World &lt;/td&gt;&lt;/tr&gt;");
    } catch (Exception ex) {
    throw new Error("All is not well in the world.");
    }
    // Must return SKIP_BODY because we are not supporting a body for this
    // tag.
    return SKIP_BODY;
    }
    /**
    * doEndTag is called by the JSP container when the tag is closed
    */
    public int doEndTag(){
    try {
    JspWriter out = pageContext.getOut();
    out.println("&lt;/table&gt;");
    } catch (Exception ex){
    throw new Error("All is not well in the world.");
    }
    }
    }<br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <p>Once your TLD and tag handlers are created, you can begin accessing the tags in your JSP. You declare that a JSP page will use tags defined in a tag library by including a <code>taglib</code> directive in the page before any custom tag is used. The prefix attribute is a shortcut to referencing the library throughout the page.</p>
    <br clear="all" />
    <div class="adtag center" style="margin: 8px; width: 336px"><!-- dy --><!-- begin ad tag --><script language="JavaScript" type="text/javascript">
//<![cdata[ var ord="(ord" != null ? ord : Math.random()*10000000000000000 );
document.write('<script language="JavaScript" src="http://ad.doubleclick.net/adj/ttm.onjava/j2eeart;sz=336x280;tile=3;ord=' + ord + '?" type="text/javascript"><\/script>'); //]]&gt; </script>
    <script language="JavaScript" src="http://ad.doubleclick.net/adj/ttm.onjava/j2eeart;sz=336x280;tile=3;ord=7276846758555915?" type="text/javascript"></script><!-- Template id="1" Template name="Banner" Creative (Flash) --><!-- Copyright 2002 DoubleClick Inc., All rights reserved. --><script src="http://m1.2mdn.net/879366/flashwrite_1_2.js"></script><script language="VBScript">
dcmaxversion = 9
dcminversion = 6
Do
On Error Resume Next
plugin = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash." & dcmaxversion & "")))
If plugin = true Then Exit Do
dcmaxversion = dcmaxversion - 1
Loop While dcmaxversion >= dcminversion
</script><object id="FLASH_AD" height="250" width="300" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
                                                                                                    <param name="_cx" value="7938" /><param name="_cy" value="6615" /><param name="FlashVars" value="" /><param name="Movie" value="http://m1.2mdn.net/876092/devel300x250.swf?clickTag=http%3A//ad.doubleclick.net/click%253Bh%3Dv8/367a/3/0/%252a/h%253B193511825%253B0-0%253B0%253B25219394%253B4307-300/250%253B25168728/25186585/1%253B%253B%257Efdr%253D193490148%253B0-0%253B0%253B14622588%253B4252-336/280%253B25216140/25233997/1%253B%253B%257Esscs%253D%253fhttp%3A//www.sun.com/events/mysqltour/index.jsp%3Fcid%3D923531" /><param name="Src" value="http://m1.2mdn.net/876092/devel300x250.swf?clickTag=http%3A//ad.doubleclick.net/click%253Bh%3Dv8/367a/3/0/%252a/h%253B193511825%253B0-0%253B0%253B25219394%253B4307-300/250%253B25168728/25186585/1%253B%253B%257Efdr%253D193490148%253B0-0%253B0%253B14622588%253B4252-336/280%253B25216140/25233997/1%253B%253B%257Esscs%253D%253fhttp%3A//www.sun.com/events/mysqltour/index.jsp%3Fcid%3D923531" /><param name="WMode" value="Opaque" /><param name="Play" value="0" /><param name="Loop" value="-1" /><param name="Quality" value="High" /><param name="SAlign" value="" /><param name="Menu" value="-1" /><param name="Base" value="" /><param name="AllowScriptAccess" value="never" /><param name="Scale" value="ShowAll" /><param name="DeviceFont" value="0" /><param name="EmbedMovie" value="0" /><param name="BGColor" value="" /><param name="SWRemote" value="" /><param name="MovieData" value="" /><param name="SeamlessTabbing" value="1" /><param name="Profile" value="0" /><param name="ProfileAddress" value="" /><param name="ProfilePort" value="0" /><param name="AllowNetworking" value="all" /><param name="AllowFullScreen" value="false" /><embed src="http://m1.2mdn.net/876092/devel300x250.swf?clickTag=http%3A//ad.doubleclick.net/click%253Bh%3Dv8/367a/3/0/%252a/h%253B193511825%253B0-0%253B0%253B25219394%253B4307-300/250%253B25168728/25186585/1%253B%253B%257Efdr%253D193490148%253B0-0%253B0%253B14622588%253B4252-336/280%253B25216140/25233997/1%253B%253B%257Esscs%253D%253fhttp%3A//www.sun.com/events/mysqltour/index.jsp%3Fcid%3D923531" quality="high" wmode="opaque" swliveconnect="TRUE" width="300" height="250" bgcolor="#" type="application/x-shockwave-flash" allowscriptaccess="never"></embed></object><noscript><a target="_blank" href="http://ad.doubleclick.net/click%3Bh=v8/367a/3/0/%2a/h%3B193511825%3B0-0%3B0%3B25219394%3B4307-300/250%3B25168728/25186585/1%3B%3B%7Efdr%3D193490148%3B0-0%3B0%3B14622588%3B4252-336/280%3B25216140/25233997/1%3B%3B%7Esscs%3D%3fhttp://www.sun.com/events/mysqltour/index.jsp?cid=923531"><img src="http://m1.2mdn.net/876092/devel300x250.gif" alt="" border="0" /></a></noscript><noscript><a href="http://ad.doubleclick.net/jump/ttm.onjava/j2eeart;sz=336x280;tile=3;ord=5750690347?" target="_blank"><img src="http://ad.doubleclick.net/ad/ttm.onjava/j2eeart;sz=336x280;ord=123456789?" width="336" height="280" border="0" alt=""   /></a></noscript><!-- End ad tag --></div>
        <br clear="all" />
        <!-- me -->
        <p>Sample Hello.jsp:</p>
        <pre><code>&lt;%@ taglib uri="/oreillySample.tld" prefix="sample" %&gt;
        &lt;html&gt;
        &lt;head&gt;
        &lt;title&gt;Your Standard Hello World Demo&lt;/title&gt;
        &lt;/head&gt;
        &lt;body bgcolor="#ffffff"&gt;
        &lt;hr /&gt;
        &lt;sample:hello name="Sue"/&gt;
        &lt;hr /&gt;
        &lt;/body&gt;
        &lt;/html&gt;</code></pre>
        <p>The HTML source output from this JSP would look like:</p>
        <pre><code>&lt;html&gt;
        &lt;head&gt;
        &lt;title&gt;Your Standard Hello World Demo&lt;/title&gt;
        &lt;/head&gt;
        &lt;body bgcolor="#ffffff"&gt;
        &lt;hr /&gt;
        &lt;table border="1"&gt;
        &lt;tr&gt;&lt;td&gt; Hello Sue &lt;/td&gt;&lt;/tr&gt;
        &lt;/table&gt;
        &lt;hr /&gt;
        &lt;/body&gt;
        &lt;/html&gt;</code></pre>
        <p>You can see how the tag was evaluated and the contents of the tag inserted into the output stream.</p>
        <p>If you wanted to add functionality to our tag to make it more flexible, say to evaluate a body a certain number of times and make the name attribute evaluated at runtime, you can do so fairly easily with a few changes. First you would make the following changes to the TLD file. The tag definition would look like</p>
        <pre><code>  &lt;tag&gt;
        &lt;name&gt;hello&lt;/name&gt;
        &lt;tagclass&gt;oreilly.examples.Hello &lt;/tagclass&gt;
        &lt;!-- Allow for a body to be included for this tag --&gt;
        &lt;bodycontent&gt;JSP&lt;/bodycontent&gt;
        &lt;info&gt;
        This is a simple hello tag.
        &lt;/info&gt;
        &lt;!-- Optional attributes  --&gt;
        &lt;!-- personalized name --&gt;
        &lt;attribute&gt;
        &lt;name&gt;name&lt;/name&gt;
        &lt;required&gt;false&lt;/required&gt;
        &lt;rtexpvalue&gt;true&lt;/rtexpvalue&gt;
        &lt;/attribute&gt;
        &lt;!-allow for the jsp coder to specify how many times to iterate --&gt;
        &lt;attribute&gt;
        &lt;name&gt;iterations&lt;/name&gt;
        &lt;required&gt;false&lt;/required&gt;
        &lt;rtexpvalue&gt;false&lt;/rtexpvalue&gt;
        &lt;/attribute&gt;
        &lt;/tag&gt;</code></pre>
        <p>Then you must alter the handler class by extending <code>TagBodySupport</code> and implementing the body methods if you wanted different behavior from that provided in the base class implementation. The handler class would now look like</p>
        <pre><code>public class Hello extends BodyTagSupport {
        private String name=null;
        private int iterations=1;
        /**
        * Getter/Setter for the attribute name as defined in the tld file
        * for this tag
        */
        public void setName(String value){
        name = value;
        }
        public String getName(){
        return(name);
        }
        /**
        * Getter/Setter for the attribute iterations as defined in the tld file
        * for this tag
        */
        public void setIterations(String value){
        try {
        iterations = Integer.parseInt(value);
        } catch(NumberFormatException nfe) {
        iterations = 1;
        }
        }
        public String getIterations(){
        return(Integer.toString(iterations));
        }
        public int doStartTag() throws JspTagException{
        try {
        JspWriter out = pageContext.getOut();
        out.println("&lt;table border=\"1\"&gt;");
        if (name != null)
        out.println("&lt;tr&gt;&lt;td&gt; Hello " + name + " &lt;/td&gt;&lt;/tr&gt;");
        else
        out.println("&lt;tr&gt;&lt;td&gt; Hello World &lt;td&gt;&lt;/tr&gt;");
        } catch (Exception ex) {
        throw new JspTagException("All is not well in the world." + ex );
        }
        // Evaluate the body if there is one
        return EVAL_BODY_TAG;
        }
        public int doEndTag()throws JspTagException {
        try {
        JspWriter out = pageContext.getOut();
        out.println("&lt;/table&gt;");
        } catch (Exception ex){
        throw new JspTagException("All is not well in the world." + ex);
        }
        }
        public int doAfterBody() throws JspTagException {
        if (iterations-- &gt;= 1) {
        BodyContent body = getBodyContent();
        try {
        // Make sure we put anything in the output stream in the
        // body to the output stream of the JSP
        JspWriter out = body.getEnclosingWriter();
        out.println(body.getString());
        body.clearBody(); // Clear for next evaluation
        } catch(IOException ioe) {
        throw new JspTagException("Error in Hello tag doAfterBody " + ioe);
        }
        return(EVAL_BODY_TAG);
        } else {
        return(SKIP_BODY);
        }
        }</code></pre>
        <p>Now let's make the simple changes in the JSP file. Our sample <code>Hello.jsp</code> looks like</p>
        <pre><code>&lt;%@ taglib uri="/oreillySample.tld" prefix="sample" %&gt;
        &lt;%!
        // allow a username to be passed in the request
        String userName = request.getParameter("NAME");
        %&gt;
        &lt;html&gt;
        &lt;head&gt;
        &lt;title&gt;Your Standard Hello World Demo&lt;/title&gt;
        &lt;/head&gt;
        &lt;body bgcolor="#ffffff"&gt;
        &lt;hr /&gt;
        &lt;sample:hello name="&lt;%= userName %&gt;" iterations="2"&gt;
        &lt;tr&gt;&lt;td&gt;&lt;b&gt;Have a nice day&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;
        &lt;/sample:hello&gt;
        &lt;hr /&gt;
        &lt;/body&gt;
        &lt;/html&gt;</code></pre>
        <p>If our JSP page was called like <code>hello.jsp?NAME=Sue</code> our output would look like</p>
        <pre><code>&lt;html&gt;
        &lt;head&gt;
        &lt;title&gt;Your Standard Hello World Demo&lt;/title&gt;
        &lt;/head&gt;
        &lt;body bgcolor="#ffffff"&gt;
        &lt;hr /&gt;
        &lt;table border="1"&gt;
        &lt;tr&gt;&lt;td&gt; Hello Sue &lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;&lt;b&gt;Have a nice day&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;&lt;b&gt;Have a nice day&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;
        &lt;/table&gt;
        &lt;hr /&gt;
        &lt;/body&gt;
        &lt;/html&gt;</code></pre>
        <p>This simple example is but a small glimpse at the power of custom tag libraries. We examined how to create and use a simple tag library and then easily extend its functionality. The next article will examine some of the advanced features of tags, including defining scripting variables and cooperating tags. It will also go through working examples of each.</p>
        <p><em><a href="http://www.onjava.com/pub/au/113">Sue Spielman</a> is an associate editor for ONJava.com, covering JSP and Servlets technologies. She is also President and Senior Consulting Engineer for <a href="http://www.switchbacksoftware.com/">Switchback Software LLC</a>. </em></p>
        </code></pre>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/184053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-05 17:06 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/05/184053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>页面中太多的自定义标签会不会影响性能？zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/03/05/184027.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Wed, 05 Mar 2008 08:10:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/03/05/184027.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/184027.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/03/05/184027.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/184027.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/184027.html</trackback:ping><description><![CDATA[<br />
http://hi.baidu.com/yanhua365/blog/item/a05930ad04b777094a36d662.html<br />
<br />
页面中太多的自定义标签会不会影响性能？
<p>太多的自定义标签（尤其是这种包含了一部分业务逻辑的标签）会不会影响性能呢？下面一段文字是我以前写的，可以说明一些问题，但真正对性能会不会产生太大的影响，还要看具体的情况，如果有必要的话，做一下性能测试得出的结果才是可靠的。</p>
<p>我们知道，JSP归根到底是Servlet，也就是说最终是要编译成Servlet的。如果是在Servlet的service方法中new一个Tag处理类来处理出现的标签的话，那就意味着每一次客户的请求都会生成新的Tag处理类，这样对服务器的性能和资源影响都很大。而如果把Tag处理类的对象做为Servlet的一个属性的话，又会遇到线程安全等问题。据说，不同的JSP容器有不同的处理方式，有的容器下，一个页面上出现太多的自定义标签，响应速度就会很慢。真担心太多的JSP和太多的自定义标签会使服务器承受不了啊。－－今天突然想到，可以看一下Tomcat是怎么把包含有自定义标签的JSP页面编译成Servlet的，也许就清楚自定义标签对性能的影响有多大了！<br />
原来，Tomcat用了缓冲池的概念来解决。。。<br />
比如我在JSP中用到了一个Struts标签&lt;html:text property="username"&gt;&lt;/html:text&gt;那么，在对应的JSP编译的Servlet中会有一个这样的属性private org.apache.jasper.runtime.TagHandlerPool _jspx_tagPool_html_text_property;可以看出来，这是Tomcat提供的一个标签处理的缓冲池，在下面会用到它。在Servlet中会生成下面这个方法：<br />
&nbsp;private boolean _jspx_meth_html_text_0(javax.servlet.jsp.tagext.JspTag _jspx_th_html_form_0, PageContext pageContext)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws Throwable {<br />
&nbsp;&nbsp;&nbsp; JspWriter out = pageContext.getOut();<br />
&nbsp;&nbsp;&nbsp; /* ----&nbsp; html:text ---- */<br />
&nbsp;&nbsp;&nbsp; org.apache.struts.taglib.html.TextTag _jspx_th_html_text_0 = (org.apache.struts.taglib.html.TextTag) _jspx_tagPool_html_text_property.get(org.apache.struts.taglib.html.TextTag.class);<br />
&nbsp;&nbsp;&nbsp; _jspx_th_html_text_0.setPageContext(pageContext);<br />
&nbsp;&nbsp;&nbsp; if (_jspx_th_html_form_0 instanceof javax.servlet.jsp.tagext.SimpleTag)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _jspx_th_html_text_0.setParent(new javax.servlet.jsp.tagext.TagAdapter((javax.servlet.jsp.tagext.SimpleTag) _jspx_th_html_form_0));<br />
&nbsp;&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _jspx_th_html_text_0.setParent((javax.servlet.jsp.tagext.Tag) _jspx_th_html_form_0);<br />
&nbsp;&nbsp;&nbsp; _jspx_th_html_text_0.setProperty("username");<br />
&nbsp;&nbsp;&nbsp; int _jspx_eval_html_text_0 = _jspx_th_html_text_0.doStartTag();<br />
&nbsp;&nbsp;&nbsp; if (_jspx_th_html_text_0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br />
&nbsp;&nbsp;&nbsp; _jspx_tagPool_html_text_property.reuse(_jspx_th_html_text_0);<br />
&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp; }<br />
可以看出，在这个方法中，对标签处理类做了缓冲池处理，可以大大提高性能。<br />
当然，_jspx_tagPool_html_text_property这个Servlet的属性，要在初始化时赋值，在最后释放掉。<br />
public void _jspInit() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _jspx_tagPool_html_text_property = org.apache.jasper.runtime.TagHandlerPool.getTagHandlerPool(getServletConfig());<br />
｝</p>
<p>&nbsp;public void _jspDestroy() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _jspx_tagPool_html_text_property.release();<br />
｝</p>
<p>需要说明的是，这种缓冲池是在这个JSP生命周期的范围内的，也就是说，另一个JSP页面用到了相同的标签，也会重新建立一个缓冲池，和这个页面毫无关系。还有一点，就是在同一个JSP页面中，也不是一个标签对应一个缓冲池，比如说我用到了link标签的两种形式，它们属性的个数是不一样的：<br />
&nbsp; 　&lt;html:link action="/test"&gt;Test with a link&lt;/html:link&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;html:link action="/test" paramId="username" paramName="haha"&gt;<br />
那就会生成两个缓冲池：<br />
&nbsp;&nbsp;&nbsp; private org.apache.jasper.runtime.TagHandlerPool _jspx_tagPool_html_link_action;<br />
&nbsp;　 private org.apache.jasper.runtime.TagHandlerPool _jspx_tagPool_html_link_paramName_paramId_action;<br />
这样，虽然服务器上会有很多这样的缓冲池，不过相对于每次请求创建Tag处理类来说，这不算什么，大大提高了性能和利用了服务器资源。<br />
我们这里说的都是Tomcat的解决方案，至于其它容器，也许有不同的解决方案。</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/184027.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-03-05 16:10 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/03/05/184027.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>fuck u to death   ---无法使用 "default package"  类 </title><link>http://www.blogjava.net/jinfeng_wang/archive/2008/01/25/177720.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 25 Jan 2008 03:52:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2008/01/25/177720.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/177720.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2008/01/25/177720.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/177720.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/177720.html</trackback:ping><description><![CDATA[<span class="javascript" id="text87491"><a class="ilink" href="http://java.sun.com/j2se/1.4/compatibility.html" target="_blank">http://java.sun.com/j2se/1.4/compatibility.html</a><br />
<br />
<blockquote><font color="#0000a0"><br />
The compiler now rejects import statements that import a type from the unnamed namespace. Previous versions of the compiler would accept such import declarations, even though they were arguably not allowed by the language (because the type name appearing in the import clause is not in scope). The specification is being clarified to state clearly that you cannot have a simple name in an import statement, nor can you import from the unnamed namespace.<br />
<br />
To summarize, the syntax<br />
<br />
import SimpleName;<br />
<br />
is no longer legal. Nor is the syntax <br />
import ClassInUnnamedNamespace.Nested;<br />
<br />
which would import a nested class from the unnamed namespace. To fix such problems in your code, move all of the classes from the unnamed namespace into a named namespace.</font></span></blockquote>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/177720.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2008-01-25 11:52 <a href="http://www.blogjava.net/jinfeng_wang/archive/2008/01/25/177720.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Effective Java Exceptions  &amp;&amp; 高效的Java异常处理 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2007/12/12/167119.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Wed, 12 Dec 2007 01:17:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2007/12/12/167119.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/167119.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2007/12/12/167119.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/167119.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/167119.html</trackback:ping><description><![CDATA[Java开发人员可以做出的最重要的架构性决策之一就是如何使用Java异常模型。Java异常一直以来就是社群中许多争议的靶子。有人争论到，在Java语言中的异常检查已是一场失败的试验。本文将辨析，失败的原因不在于Java异常模型，而在于Java类库的设计者未能充分了解到方法失败的两个基本原因。<br />
<br />
本文倡导一种对异常条件本质的思考方式，并描述一些有助于设计的模式。最后，本文还将在AOP模型中，作为相互渗透的问题，来讨论异常的处理。当你能正确使用异常时，它们会有极大的好处。本文将帮助你做到这一点。<br />
<br />
<h2><strong>为何异常是如此重要</strong></h2>
<br />
Java应用中的异常处理在很大程度上揭示了其所基于架构的强度。架构是在应用程序各个层次上所做出并遵循的决定。其中最重要的一个就是决定应用程序中的类，亚系统，或层之间沟通的方式。Java异常是Java方法将另类执行结果交流出去的方式，所以值得在应用架构中给予特殊关注。<br />
<br />
一个衡量Java设计师水平和开发团队纪律性的好方法就是读读他们应用程序里的异常处理代码。首先要注意的是有多少代码用于捕获异常，写进日志文件，决定发生了什么，和在不同的异常间跳转。干净，简捷，关联性强的异常处理通常表明开发团队有着稳定的使用Java异常的方式。当异常处理代码的数量甚至要超过其他代码时，你可以看出团队之间的交流合作有很大的问题（可能在一开始就不存在），每个人都在用他们自己的方式来处理异常。<br />
<br />
对突发异常的处理结果是可以预见的。如果你问问团队成员为什么异常会被抛出，捕获，或在特定的一处代码里忽视了异常的发生，他们的回答通常是，&#8220;我没有别的可做&#8221;。如果你问当他们编写的异常真的发生了会怎么样，他们会皱皱眉，你得到的回答类似于这样，&#8220;我不知道。我们从没测试过。&#8221;<br />
<br />
你可以从客户端的代码判断一个java的组件是否有效利用了java的异常。如果它们包含着大堆的逻辑去弄清楚在何时一笔操作失败了，为何失败，是否有弥补的余地，那么原因很有可能要归咎于组件的报错设计。错误的报错系统会在客户端产生大量的&#8220;记录然后忘掉&#8221;的代码，这些代码鲜有用途。最差的是弄拧的逻辑，嵌套的try/catch/finally代码块，和一些其他的混乱而导致脆弱而难于管理的应用程序。<br />
<br />
事后再来解决Java异常的问题，或根本就不解决，是软件项目产生混乱并导致滞后的主要原因。异常处理是一个在设计的各个部分都急需解决的问题。对异常处理建立一个架构性的约定是项目中首要做出的决定。合理使用Java异常模型对确保你的应用简单，易维护，和正确有着长远的影响。<br />
<br />
<br />
<h2><strong>解析异常</strong></h2>
<br />
正确使用Java异常模型所包含的内容一直以来有着很大的争议。Java不是第一种支持异常算法语义的；但是，它却是第一种通过编译器来执行声明和处理某些异常的规则的语言。许多人都认为编译时的异常检查对精确的软件设计颇有帮助。图1显示的Java异常的等级。<br />
<br />
<img alt="\\\\\\\\\" src="file:///C:/DOCUME%7E1/TianTian/LOCALS%7E1/Temp/moz-screenshot-1.jpg" /><img src="http://dev2dev.bea.com/images/2006/10/JavaExceptions.jpg"  alt="" /><br />
<div align="center">图1：Java异常的等级<br />
<br />
<div align="left">通常，Java编译器强迫抛出基于java.lang.Throwable的异常的方法要在它声明中的&#8220;throws&#8221;部分加上那个异常。而且，编译器还会证实客户端的方法或者捕获已声明的异常，或者特别声明自己也抛出同样的异常。这些简单的规则对世界范围的Java程序员都有深远的影响。<br />
<br />
编译器放松了对Throwable继承树中两个分支的异常检查。java.long.Error和java.lang.RuntimeException的子类免于编译时的检查。在这两类中，软件工程师通常对运行中异常更感兴趣。&#8220;不检查&#8221;的异常指的是这一组，以便和所有其它&#8220;检查&#8221;的异常区别开。<br />
<br />
我可以想象那些接受&#8220;检查&#8221;的异常的人，也会很看重Java的数据类型。毕竟，编译器对数据类型施加的限制鼓励严格的编码和精确的思维。编译时的类型检查对减少运行时的严重问题有帮助。编译时的异常检查也能起到类似的作用，它会提醒开发人员某个方法可能会有预想不到的结果需要处理好。<br />
<br />
早期的建议是尽可能的使用&#8220;检察的异常&#8221;，以此来最大限度的利用编译器提供的帮助来写出无错误的软件。Java类库API的设计者们都认同这一点，他们广泛地使用&#8220;检察的异常&#8221;来模拟类库方法中几乎所有的紧急应变措施。在J2SE5.1 API规格中，&#8220;检察的异常&#8221;类型已2比1的比率超过了&#8220;未检查的异常&#8221;类型。<br />
<br />
对程序员而言，看上去在Java类库中大多数的常用方法对每一个可能的失败都声明了&#8220;检察的异常&#8221;。例如，java.io包</div>
</div>
对IOException这个&#8220;检察的异常&#8221;就有着很大的依赖。至少有63个Java类库包，或直接，或通过十几个下面的子类，抛出这个异常。<br />
<br />
I/O的失败极其稀有，但是却很严重。而且，一旦发生，从你所写的代码里基本上是无法补救的。Java程序员意识到他们不得不提供IOException或类似的不可补救的事件，而一个简单的Java类库方法的调用就可能让这些事件发生。捕获这些异常给本来简单的代码带来了一定的晦涩，因为即使在捕获的代码块里也基本上帮不上忙。但是不加以捕获又可能更糟糕，因为编译器要求你的方法必须要抛出那些异常。这样你的实施细则就不得不暴露在外了，而通常好的面向对象的设计都是要隐藏细节的。<br />
<br />
这样一个不可能赢的局面导致了我们今天所警告的绝大多数臭名卓著的异常处理的颠覆性格局。同时也衍生了很多正确或错误的补救之道。<br />
<br />
一些Java界的知名人物开始质疑Java的&#8220;检察的异常&#8221;的模型是否是一个失败的试验。有一些东西肯定是失败的，但是这和在Java语言里加入对异常的检查是毫无关联的。失败是由于在Java API的设计者们的思维里，大多数失败的情形是雷同的，所以可以通过同一种异常传达出去。<br />
<br />
<strong><br />
</strong>
<h2><strong>故障和应变</strong></h2>
<br />
让我们来考虑在一个假想的银行应用中的CheckingAccount类。一个CheckingAcccount属于一个用户，记载着用户的存款余额，也能接受存款，接受止兑的通知，和处理汇入的支票。一个CheckingAcccount对象必须协调同步线程的访问，因为任何一个线程都可能改变它的状态。CheckingAcccount类里processCheck的方法会接受一个Check对象为参数，通常从帐户余额里减去支票的金额。但是一个管理支票清算的用户端程序调用processCheck方法时，必须有两种可能的应变措施。一，CheckingAccount对象里可能对该支票已有一个止付的命令；二，帐户的余额可能不足已满足支票的金额。<br />
<br />
所以，processCheck的方法对来自客户端的调用可以有3种方式回应。正常的是处理好支票，并把方法签名里声明的结果返回给调用方。两种应变的回应则是需要与支票清算端沟通的在银行领域实实在在存在的情况。processCheck方法所有3种返回结果都是按照典型的银行支票帐户的行为而精心设计的。<br />
<br />
在Java里，一个自然的方法来表示上述紧急的应变是定义两种异常，比如StopPaymentException（止付异常）和InsufficientFundsException（余额不足异常）。一个客户端如果忽略这些异常是不对的，因为这些异常在正常操作的情况下一定会被抛出。他们如同方法的签名一样反映了方法的全面行为。<br />
<br />
客户端可以很容易的处理好这两种异常。如果对支票的兑付被停止了，客户端把该支票交付特别处理。如果是因为资金不足，用户端可以从用户的储蓄帐户里转移一些资金到支票帐户里，然后再试一次。<br />
<br />
在使用CheckingAccount的API时，这些应变都是可以预计的和自然的结果。他们并不是意味着软件或运行环境的失败。这些异常和由于CheckingAccount类中一些内部实施细则引起的真正失败是不同的。<br />
<br />
设想CheckingAccount对象在数据库里保持着一个恒定的状态，并使用JDBC API来对之访问。在那个API里，几乎所有的数据库访问方法都有可能因为和CheckingAccount实施无关的原因而失败。比如，有人可能忘了把数据库服务器运行起来，一个未有连上的网络数据线，访问数据库的密码改变了，等等。<br />
<br />
JDBC依靠一种&#8220;检查的异常&#8221;，SQLException，来汇报任何可能的错误。可能出错的绝大多数原由都是数据库的配置，连接，或其所在的硬件设施。对processCheck方法而言，它对上述错误是无计可施的。这不应该，因为processCheck至少了解它自己的实施细则。在调用栈里上游的方法能处理这些问题的可能就更小。<br />
<br />
CheckingAccount这个例子说明了一个方法不能成功返回它想要的结果的两个基本原因。这里是两个描述性的术语：<br />
<strong><br />
应变</strong><br />
与实际预料相符，一个方法给出另外一种回应，而这种回应可以表达成该方法所要达到的目的之一。这个方法的调用者预料到这个情况的出现，并有相对的应付之道。<br />
<br />
<strong>故障</strong><br />
在未经计划的情况下，一个方法不能达到它的初衷，这是一个不诉诸该方法的实施细则就很难搞清的情况。<br />
<br />
应用这些术语，对processCheck方法而言，一个止付的命令和一个超额的提取是两种可能的应变。而SQLException反映了可能的故障。processCheck方法的调用者应该能够提供应变，但却不一定能有效的处理好可能发生的故障。<br />
<br />
<strong><br />
Java异常的匹配</strong><br />
<br />
在建立应用架构中Java异常的规则时，以应变和故障的方式仔细考虑好&#8220;什么可能会出错&#8221;是有长远意义的。<br />
<br />
<table cellspacing="2" cellpadding="2" border="1">
    <tbody>
        <tr>
            <td>条件<br />
            </td>
            <td>应变<br />
            </td>
            <td>故障<br />
            </td>
        </tr>
        <tr>
            <td>被考虑成<br />
            </td>
            <td>设计的一部分<br />
            </td>
            <td>一个糟糕的意外<br />
            </td>
        </tr>
        <tr>
            <td>预计到会发生<br />
            </td>
            <td>经常发生<br />
            </td>
            <td>绝不发生</td>
        </tr>
        <tr>
            <td>关注方<br />
            </td>
            <td>上游对该方法的调用者<br />
            </td>
            <td>需要修好这个问题的人<br />
            </td>
        </tr>
        <tr>
            <td>举例</td>
            <td>另一种返回方式<br />
            </td>
            <td>程序bug，硬件系统故障，配置错误，丢失的文件，服务器没有运行</td>
        </tr>
        <tr>
            <td>最好的匹配<br />
            </td>
            <td>一个检查的异常<br />
            </td>
            <td>一个未检查的异常<br />
            </td>
        </tr>
    </tbody>
</table>
<br />
应变情况恰如其分地匹配给了Java检查的异常。因为它们是方法的语义算法合同中不可缺少的一部分，在这里借助于编译器的帮助来确保它们得到解决是很有道理的。如果你发现编译器坚持应变的异常必须要处理或者在不方便的时候必须要声明会给你带来些麻烦，你在设计上几乎肯定要做些重构了。这其实是件好事。<br />
<br />
出现故障的情况对开发人员而言是蛮有意思的，但对软件逻辑而言却并非如此。那些软件&#8221;消化问题&#8220;的专家们需要关于故障的信息以便来解决问题。因此，未检查的异常是表示故障的很好方式。他们让故障的通知原封不动地从调用栈上所有的方法滤过，到达一个专门来捕获它们的地方，并得到它们自身包含的有利于诊断的信息，对整个事件提供一个有节制的优雅的结论。产生故障的方法不需要来声明（异常），上游的调用方法不需要捕获它们，方法的实施细则被正确的隐藏起来－ 以最低的代码复杂度。<br />
<br />
新一些的Java API，比如像Spring架构和Java Data Ojects类库对检查的异常几乎没有依赖。Hibernate ORM架构在3.0版本里重新定义了一些关键功能来去除对检查的异常的使用。这就意味着在这些架构举报的绝大部分异常都是不可恢复的，归咎于错误的方法调用代码，或是类似于数据库服务器之类的底层部件的失败。特别的，强迫一个调用方来捕获或声明这些异常几乎没有任何好处。<br />
<br />
<br />
<strong>设计里的故障处理</strong><br />
<br />
在你的计划里，承认你需要去做就迈好了有效处理好故障的第一步。对那些坚信自己能写出无懈可击的软件的工程师们来说，承认这一点是不容易的。这里是一些有帮助的思考方式。首先，如果错误俯拾即是，应用的开发时间将很长，当然前提是程序员自己的bug自己修理。第二，在Java类库中，过度使用检查的异常来处理故障情形将迫使你的代码要应对好故障，即使你的调用次序完全正确。如果没有一个故障处理的架构，凑合的异常处理将导致应用中的信息丢失。<br />
<br />
一个成功的故障处理架构一定要达到下面的目标：<br />
<ul>
    <li>减少代码的复杂性
    <li>捕获和保存诊断性信息
    <li>对合适的人提醒注意
    <li>优雅地退出行动 </li>
</ul>
故障是应用的真实意图的干扰。因此，用来处理它们的代码应尽量的少，理想上，把它们和应用的语义算法部分隔离开。故障的处理必须满足那些负责改正它们的人的需要。开发人员需要知道故障发生了，并得到能帮助他们搞清为何发生的信息。即使一个故障，在定义上而言，是不可补救的，好的故障处理会试着优雅地结束引起故障的活动。<br />
<br />
<strong><br />
对故障情况使用未检查的异常</strong><br />
<br />
在做框架上的决定时，用未检查的异常来代表故障情况是有很多原因的。Java的运行环境对代码的错误会抛出&#8220;运行时异常&#8221;的子类，比如，ArithmeticException或ClassCastException。这为你的框架设了一个先例。未检查的异常让上游的调用方法不需要为和它们目的不相关的情况而添加代码，从而减少了混乱。<br />
<br />
你的故障处理策略应该认识到Java类库的方法和其他API可能会使用检查的异常来代表对你的应用而言只可能是故障的情况。在这种情形下，采用设计约定来捕获API异常，将其以故障来看待，抛出一个未检查的异常来指示故障的情况和捕获诊断的信息。<br />
<br />
在这种情况下抛出的特定异常类型应该由你的框架来定义。不要忘记一个故障异常的主要目的是传递记录下来的诊断信息，以便让人们来想出出错的原因。使用多个故障异常类型可能有些过，因为你的架构对它们都一视同仁。多数情况下，一条好的，描述性强的信息将单一的故障类型嵌入就够用了。使用Java基本的RuntimeException来代表故障情况是很容易的。截止到Java1.4，RuntimeException，和其他的抛出类型一样，都支持异常的嵌套，这样你就可以捕获和报出导向故障的检查的异常。<br />
<br />
你也许会为了故障报告的目的而定义你自己的未检查的异常。这样做可能是必要的，如果你使用Java1.3或更早的版本，它们都不支持异常的嵌套。实施一个类似的嵌套功能来捕获和转换你应用中构成故障的检查的异常是很简单的。你的应用在报错时可能需要一个特殊的行为。这可能是你在架构中创建RuntimeException子类的另一个原因。<br />
<br />
<br />
<strong>建立一个故障的屏障</strong><br />
<br />
对你的故障处理架构而言，决定抛出什么样的异常，何时抛出是重要的决定。同样重要的是，何时来捕获一个故障异常，之后再怎么办。这里的目的是让你应用中的功能性部分不需要处理故障。把问题分开来处理通常都是一件好事情，有一个中央故障处理机制长远来看是很有裨益的。<br />
<br />
在故障屏障的模式里，任何应用组件都可以抛出故障异常，但是只有作为&#8220;故障屏障&#8221;的组件才捕获异常。采用此种模式去除了大多数程序员为了在本地处理故障而插入的复杂的代码。故障屏障逻辑上位于调用栈的上层，这样在一个默认的行动被激发前，一个异常向上举报的行为就被阻止了。根据不同的应用类型，默认的行动所指也不同。对一个独立的Java应用而言，这个行动指活着的线程被停止。对一个位于应用服务器上的Web应用而言，这个行动指应用服务器向浏览器送出不友好的（甚至令人尴尬的）回应。<br />
<br />
一个故障屏障组件的第一要务就是记录下故障异常中包含的信息以为将来所用。到现在为止，一个应用日志是做成此事的首选。异常的嵌套的信息，栈日志，等等，都是对诊断有价值的信息。传递故障信息最差的地方是通过用户界面。把应用的使用者卷进查错的进程对你，对你的用户而言都不好。如果你真的很想把诊断信息放上用户界面，那可能意味着你的日志策略需要改进。<br />
<br />
故障屏障的下一个要务是以一种可控的方式来结束操作。这具体的意义要取决于你应用的设计，但通常包括产生一个可通用的回应给可能正在等待的客户端。如果你的应用是一个Web service，这就意味着在回应中用soap:Server的&lt;faultcode&gt;和通用的失败信息&lt;faultstring&gt;来建立一个SOAP故障元素&lt;fault&gt;。如果你的应用于浏览器交流，这个屏障就会安排好一个通用的HTML回应来表明需求是不能被处理的。<br />
<br />
在一个Struts的应用里，你的故障屏障会以一种全局异常处理器的形式出现，并被配置成处理RuntimeException的任何子类。你的故障屏障类将延伸org.apache.struts.action.ExceptionHandler类，必要的话，重写它的方法来实施用户自己的特别处理。这样就会处理好不小心产生的故障情况和在处理一个Struts动作时发现的故障。图2显示的就是应变和故障异常。<br />
<br />
<img src="http://dev2dev.bea.com/images/2006/10/ExceptionBarrierPattern.jpg"  alt="" /> <br />
<br />
<div align="center">图2 应变和故障异常<br />
</div>
<br />
如果你使用的是Spring MVC架构，你可以继承SimpleMappingExceptionResolver类，并配置成处理RuntimeException和它的子类们，这样很容易的就建起了故障屏障。通过重写resolveException的方法，你可以在使用父类的方法来把需求导引到一个发出通用错误提示的view组件之前，加入你需要的用户化的处理。<br />
<br />
当你的架构包含了故障屏障，程序员都知晓了后，再写出一次性的故障异常的冲动就会锐减。结果就是应用中出现更干净，更易于维护的代码。<br />
<br />
<br />
<h2><strong>架构中应变的处理</strong></h2>
<strong><br />
</strong><br />
将故障处理交与屏障后，主要组件间的应变交流变得容易多了。一个应变代表着与主要返回结果同等重要的另外一种方法结果。因此，检查的异常类型是一个能够很好地传递应变情况的存在并提供必要的信息来与它竞争的工具。这个方式借助于Java编译器的帮助来提醒程序员关于他们所用的API的方方面面以及提供全套的方法输出的必要性。<br />
<br />
仅仅使用方法的返回值类型来传递简单的应变是可能的。比如，返回一个空引用，而不是一个具体的对象，可以意味着对象由于一个已定义的原因不能被建立。Java I/O的方法通常返回一个整数值－1，而不是字节的值或字节的数来表示文件的结尾。如果你的方法的语义简单到可以允许的地步，另一种返回值的方法是可以使用的，因为它摒弃了异常带来的额外的花销。不足之处是方法的调用方要检测一下返回的值来判断是主要结果，还是应变结果。但是，编译器没有办法来保证方法调用者会使用这个判断。<br />
<br />
如果一个方法有一个void的返回类型，异常是唯一的方法来表示应变发生了。如果一个方法返回的是一个对象的引用，那么返回值只可能是空或非空（null and non-null)。如果一个方法返回一个整数型，选择与主要返回值不冲突的，可以表示多种应变情况的数值是可能的。但是这样的话，我们就进入了错误代码检查的世界，而这正式Java异常模式所着力避免的。<br />
<br />
<br />
<strong>提供一些有用的信息</strong><br />
<br />
<br />
定义不同的故障报告的异常类型是没什么道理的，因为故障屏障对所有异常类型一视同仁。应变异常就有很大的不同，因为它们的原意是要向方法调用者传递各种情况。你的架构可能会指出这些异常应该继承java.lang.Exception或一个指定的基类。<br />
<br />
不要忘记你的异常应该是百分百的Java类型，你可以用它来存放为你的特殊目的服务的特殊字段，方法，甚至是构造器。比如，被假想的processCheck()方法抛出的InsufficientFundsException这个异常类型就应该包含着一个OverdraftProtection的对象，它能够从另外一个帐户里把短缺的资金转过来。<br />
<br />
<br />
<strong>日志还是不要日志<br />
<br />
</strong>记录下故障异常是有用处的，因为日志的目的是在一些需要改正的情况下，日志可以吸引人们的注意力。但对应变异常而言却并非如此。应变异常可能代表的只是极少数情况，但是在你的应用里，每一个情况还是会发生的。它们意味着你的应用正在如最初的设计般正常工作着。经常把日志代码加进应变的捕获块里会使你的代码晦涩难懂，而又没有实际的好处。如果一个应变代表了一重要的事件，在抛出一个异常应变来警醒调用者之前，产生一笔日志，记录下这个事件可能会让这个方法更好些。<br />
<br />
<h2><strong>异常的各个方面</strong></h2>
在Aspect Oriented Programming（AOP）的术语里，故障和应变的处理是互相渗透的问题。比如，要实施故障屏障的模式，所有参与的类必须遵循通用规格：<br />
<ul>
    <li>故障屏障方法必须存活在遍历参与类的方法调用图的最前端
    <li>参与类必须使用未检查的异常来表示故障情况
    <li>参与类必须使用故障屏障期望得到的有针对性的未检查的异常类型
    <li>参与类必须捕获并从低端方法中把在执行情境下注定的故障转换成检查的异常
    <li>参与类不能干扰故障异常被传递到故障屏障的过程<br />
    </li>
</ul>
这些问题超越了那些本不相干的类的边界。结果就是少数零散的故障处理代码，以及屏障类和参与类间暗含的耦合（这已经比不使用模式进步多了！）。AOP让故障处理的问题被封装在通用的可以作用到参与类的层面上。如AspectJ和Spring AOP这样的Java AOP架构认为异常的处理是添加故障处理行为的切入点。这样，把参与者绑定在故障屏障的模式可以放松些。故障的处理可以存活在一个独立的，不相干的方面里，从而摒弃了屏障方法需要放在方法激活次序的最前头的要求。<br />
<br />
如果在你的架构里利用了AOP，故障和应变的处理是理想的在应用里用到的在方面上的候选。对故障和应变的处理在AOP架构下的使用做一个完整的勘探将是将来论文里一个很有意思的题目。<br />
<br />
<br />
<h2><strong>结论</strong></h2>
虽然Java异常模型自它出现以来就激发了热烈的讨论，如果使用正确的话，它的价值还是很大的。作为一个设计师，你的任务是建立好规格来最大限度地利用好这个模型。以故障和应变的方式来考量异常可以帮助你做出正确的决定。合理使用好Java异常模型可以让你的应用简单，易维护，和正确。AOP技术将故障和应变定位为相互渗透的问题，这个方法可能会对你的架构提供一些帮助。<br />
<br />
<br />
<h2>引用</h2>
<ul>
    <li><a href="http://java.sun.com/docs/books/tutorial/essential/exceptions/index.html">Sun's Exception Tutorial</a> Java异常的基本知识<br />
    <li><a href="http://www.mindview.net/Etc/Discussions/CheckedExceptions">Does Java Need Checked Exception?</a> Bruce Eckel对Java中检查的异常的异议
    <li><a href="http://www.octopull.demon.co.uk/java/ExceptionalJava.html">Exceptional Java</a>&nbsp; 关于异常的很好的讨论，有架构式的异常规则来模仿<br />
    <li><a href="http://www-128.ibm.com/developerworks/java/library/j-jtp05254.html">The Exceptions Debate</a>&nbsp; 来自于developerWorks的关于异常的来龙去脉<br />
    <li><a href="http://struts.apache.org/1.1/index.html">The Apache Struts Web Application Framework</a>&nbsp; Struts的信息源<br />
    <li><a href="http://www.springframework.org/">The Spring Framework</a>&nbsp; Spring框架的信息源<br />
    <li><a href="http://en.wikipedia.org/wiki/Aspect-oriented_programming">Wikipedia: Aspect Oriented Programming</a>&nbsp; 一个很好的对AOP概念的介绍<br />
    <li><a href="http://www.eclipse.org/aspectj/">The AspectJ Project</a>&nbsp; AspectJ的信息源 </li>
</ul>
<br />
作者<a href="http://dev2dev.bea.com/pub/au/3424">Barry Ruzek</a>被Open Group提名为注册IT设计师的大师。他有着30多年的开发操作系统和企业应用的经验。<br />
<br />
<br />
<br />
<br />
<h3>Abstract</h3>
<p>One of the most important architectural decisions a Java developer can make is how to use the Java exception model. Java exceptions have been the subject of considerable debate in the community. Some have argued that checked exceptions in the Java language are an experiment that failed. This article argues that the fault does not lie with the Java model, but with Java library designers who failed to acknowledge the two basic causes of method failure. It advocates a way of thinking about the nature of exceptional conditions and describes design patterns that will help your design. Finally, it discusses exception handling as a crosscutting concern in the Aspect Oriented Programming model. Java exceptions are a great benefit when they are used correctly. This article will help you do that.</p>
<h3>Why Exceptions Matter</h3>
<p>Exception handling in a Java application tells you a lot about the strength of the architecture used to build it. Architecture is about decisions made and followed consistently at all levels of an application. One of the most important decisions to make is the way that the classes, subsystems, or tiers within your application will communicate with each other. Java exceptions are the means by which methods communicate alternative outcomes for an operation and therefore deserve special attention in your application architecture.</p>
<p>A good way to measure the skill of a Java architect and the development team's discipline is to look at exception handling code inside their application. The first thing to observe is how much code is devoted to catching exceptions, logging them, trying to determine what happened, and translating one exception to another. Clean, compact, and coherent exception handling is a sign that the team has a consistent approach to using Java exceptions. When the amount of exception handling code threatens to outweigh everything else, you can tell that communication between team members has broken down (or was never there in the first place), and everyone is treating exceptions "their own way."</p>
<p>The results of <em>ad hoc</em> exception handling are very predictable. If you ask team members why they threw, caught, or ignored an exception at a particular point in their code, the response is usually, "I didn't know what else to do." If you ask them what would happen if an exception they are coding for actually occurred, a frown follows, and you get a statement similar to, "I don't know. We never tested that."</p>
<p>You can tell if a Java component has made effective use of Java exceptions by looking at the code of its clients. If they contain reams of logic to figure out when an operation failed, why it failed, and if there's anything to do about it, the reason is almost always because of the component's error reporting design. Flawed reporting produces lots of "log and forget" code in clients and rarely anything useful. Worst of all are the twisted logic paths, nested try/catch/finally blocks, and other confusion that results in a fragile and unmanageable application.</p>
<p>Addressing exceptions as an afterthought (or not addressing them at all) is a major cause of confusion and delay in software projects. Exception handling is a concern that cuts across all parts of a design. Establishing architectural conventions for exceptions should be among the first decisions made in your project. Using the Java exception model properly will go a long way toward keeping your application simple, maintainable, and correct.</p>
<h3>Challenging the Exception Canon</h3>
<p>What constitutes "proper use" of Java's exception model has been the subject of considerable debate. Java was not the first language to support exception-like semantics; however, it was the first language in which the compiler enforced rules governing how certain exceptions were declared and treated. Compile-time exception checking was seen by many as an aid to precise software design that harmonized nicely with other language features. Figure 1 shows the Java exception hierarchy. </p>
<p>In general, the Java compiler forces a method that throws an exception based on <code>java.lang.Throwable</code> including that exception in the "throws" clause in its declaration. Also, the compiler verifies that clients of the method either catch the declared exception type or specify that they throw that exception type themselves. These simple rules have had far-reaching consequences for Java developers world-wide.</p>
<p>The compiler relaxes its exception checking behavior for two branches of the <code>Throwable</code> inheritance tree. Subclasses of <code>java.lang.Error</code> and <code>java.lang.RuntimeException</code> are exempt from compile-time checking. Of the two, runtime exceptions are usually of greater interest to software designers. The term "unchecked" exception is applied to this group to distinguish it from all other "checked" exceptions.</p>
<p><img alt="Java Exception Hierarchy" src="http://dev2dev.bea.com/images/2006/10/JavaExceptions.jpg" /><br />
<em>Figure 1. Java exception hierarchy</em></p>
<p>I imagine that checked exceptions were embraced by those who also valued strong typing in Java. After all, compiler-imposed constraints on data types encouraged rigorous coding and precise thinking. Compile-time type checking helped prevent nasty surprises at run-time. Compile-time exception checking would work similarly, reminding developers that a method had potential alternate outcomes that needed to be addressed.</p>
<p>Early on, the recommendation was to use checked exceptions wherever possible to take maximum advantage of the help provided by the compiler to produce error-free software. The designers of the Java library API evidently subscribed to the checked exception canon, using these exceptions extensively to model almost any contingency that could occur in a library method. Checked exception types still outnumber unchecked types by more than two to one in the J2SE 5.1 API Specification.</p>
<p>To programmers, it seemed like most of the common methods in Java library classes declared checked exceptions for every possible failure. For example, the <code>java.io</code> package relies heavily on the checked exception <code>IOException</code>. At least 63 Java library packages issue this exception, either directly or through one of its dozens of subclasses.</p>
<p>An I/O failure is a serious but extremely rare event. On top of that, there is usually nothing your code can do to recover from one. Java programmers found themselves forced to provide for <code>IOException</code> and similar unrecoverable events that could possibly occur in a simple Java library method call. Catching these exceptions added clutter to what should be simple code because there was very little that could be done in a catch block to help the situation. Not catching them was probably worse since the compiler required that you add them to the list of exceptions your method throws. This exposes implementation details that good object-oriented design would naturally want to hide.</p>
<p>This no-win situation resulted in most of the notorious exception handling anti-patterns we are warned about today. It also spawned lots of advice on the right ways and the wrong ways to build workarounds.</p>
<p>Some Java luminaries started to question whether Java's checked exception model was a failed experiment. Something failed for sure, but it had nothing to do with including exception checking in the Java language. The failure was in the thinking by the Java API designers that most failure conditions were the same and could be communicated by the same kind of exception.</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td>
            <p><!-- CS_PAGE_INDEX --></p>
            </td>
            <td>
            <p align="right"><a href="http://dev2dev.bea.com/lpt/a/<!--CS_NEXT_REF-->"></a></p>
            </td>
        </tr>
    </tbody>
</table>
<!-- CS_PAGE_BREAK -->
<p><!-- CS_PAGE_INDEX --></p>
<h3>Faults and Contingencies</h3>
<p>Consider a <code>CheckingAccount</code> class within an imaginary banking application. A <code>CheckingAccount</code> belongs to a customer, maintains a current balance, and is able to accept deposits, accept stop payment orders on checks, and process incoming checks. A <code>CheckingAccount</code> object must coordinate accesses by concurrent threads, any of which may alter its state. <code>CheckingAccount</code>'s <code>processCheck()</code> method accepts a <code>Check</code> object as an argument and normally deducts the check amount from the account balance. But a check-clearing client that calls <code>processCheck()</code> must be ready for two contingencies. First, the <code>CheckingAccount</code> may have a stop payment order registered for the check. Second, the account may not have sufficient funds to cover the check amount.</p>
<p>So, the <code>processCheck()</code> method can respond to its caller in three possible ways. The nominal response is that the check gets processed and the result declared in the method signature is returned to the invoking service. The two contingency responses represent very real situations in the banking domain that need to be communicated to the check-clearing client. All three <code>processCheck()</code> responses were designed intentionally to model the behavior of a typical checking account.</p>
<p>The natural way to represent the contingency responses in Java is to define two exceptions, say <code>StopPaymentException</code> and <code>InsufficientFundsException</code>. It wouldn't be right for a client to ignore these, since they are sure to be thrown in the normal operation of the application. They help express the full behavior of the method just as importantly as the method signature.</p>
<p>Clients can easily handle both kinds of exception. If payment on a check is stopped, the client can route the check for special handling. If there are insufficient funds, the client can transfer funds from the customer's savings account to cover the check and try again.</p>
<p>The contingencies are expected and natural consequences of using the <code>CheckingAccount</code> API. They do not represent a failure of the software or of the execution environment. Contrast these with actual failures that could arise due to problems related to the internal implementation details of the <code>CheckingAccount</code> class.</p>
<p>Imagine that <code>CheckingAccount</code> maintains its persistent state in a database and uses the JDBC API to access it. Almost every database access method in that API has the potential to fail for reasons unrelated to the implementation of <code>CheckingAccount</code>. For example, someone may have forgotten to turn on the database server, unplugged a network cable, or changed the password needed to access the database.</p>
<p>JDBC relies on a single checked exception, <code>SQLException</code>, to report everything that could possibly go wrong. Most of what could go wrong has to do with configuring the database, the connectivity to it, and the hardware it resides on. There's nothing that the <code>processCheck()</code> method could do to deal with these situations in a meaningful way. That's a shame, because <code>processCheck()</code> at least knows about its own implementation. Upstream methods in the call stack have an even smaller chance of being able to address problems.</p>
<p>The <code>CheckingAccount</code> example illustrates the two basic reasons that a method execution can fail to return its intended result. They are worthy of some descriptive terms:</p>
<dl>
<dt style="font-weight: bold">Contingency
<dd>An expected condition demanding an alternative response from a method that can be expressed in terms of the method's intended purpose. The caller of the method expects these kinds of conditions and has a strategy for coping with them.
<dt style="font-weight: bold">Fault
<dd>An unplanned condition that prevents a method from achieving its intended purpose that cannot be described without reference to the method's internal implementation. </dd></dl>
<p>Using this terminology, a stop payment order and an overdraft are the two possible contingencies for the <code>processCheck()</code> method. The SQL problem represents a possible fault condition. The caller of <code>processCheck()</code> ought to have a way of providing for the contingencies, but could not be reasonably expected to handle the fault, should it occur.</p>
<h3>Mapping Java Exceptions</h3>
<p>Thinking about "what could go wrong" in terms of contingencies and faults will go a long way toward establishing conventions for Java exceptions in your application architecture.</p>
<table cellspacing="2" cellpadding="2" border="1">
    <tbody>
        <tr>
            <td style="font-weight: bold; color: blue">Condition<br />
            </td>
            <td style="font-weight: bold; color: blue">Contingency</td>
            <td style="font-weight: bold; color: blue">Fault</td>
        </tr>
        <tr>
            <td style="font-weight: bold; color: blue">Is considered to be</td>
            <td>A part of the design</td>
            <td>A nasty surprise</td>
        </tr>
        <tr>
            <td style="font-weight: bold; color: blue">Is expected to happen</td>
            <td>Regularly but rarely</td>
            <td>Never</td>
        </tr>
        <tr>
            <td style="font-weight: bold; color: blue">Who cares about it</td>
            <td>The upstream code that invokes the method</td>
            <td>The people who need to fix the problem</td>
        </tr>
        <tr>
            <td style="font-weight: bold; color: blue">Examples</td>
            <td>Alternative return modes</td>
            <td>Programming bugs, hardware malfunctions, configuration mistakes, missing files, unavailable servers</td>
        </tr>
        <tr>
            <td style="font-weight: bold; color: blue">Best Mapping</td>
            <td>A checked exception</td>
            <td>An unchecked exception</td>
        </tr>
    </tbody>
</table>
<p>Contingency conditions map admirably well to Java checked exceptions. Since they are an integral part of a method's semantic contract, it makes sense to enlist the compiler's help to ensure that they are addressed. If you find that the compiler is "getting in the way" by insisting that contingency exceptions be handled or declared when it is inconvenient, it's a sure bet that your design could use some refactoring. That's actually a good thing.</p>
<p>Fault conditions are interesting to people but not to software logic. Those acting in the role of "software proctologist" need information about faults to diagnose and fix whatever caused them to happen. Therefore, unchecked Java exceptions are the perfect way to represent faults. They allow fault notifications to percolate untouched through all methods on the call stack to a level specifically designed to catch them, capture the diagnostic information they contain, and provide a controlled and graceful conclusion to the activity. The fault-generating method is not required to declare them, upstream methods are not required to catch them, and the method's implementation stays properly hidden—all with a minimum of code clutter.</p>
<p>Newer Java APIs such as the Spring Framework and the Java Data Objects library have little or no reliance on checked exceptions. The Hibernate ORM framework redefined key facilities as of release 3.0 to eliminate the use of checked exceptions. This reflects the realization that the great majority of the exception conditions that these frameworks report are unrecoverable, stemming from incorrect coding of a method call, or a failure of some underlying component such as a database server. Practically speaking, there is almost no benefit to be gained by forcing a caller to catch or declare such exceptions.</p>
<h4>Fault handling in your architecture</h4>
<p>The first step toward handling faults effectively in your architecture is to admit that you need to do it. Coming to this acceptance is difficult for engineers who take pride in their ability to create impeccable software. Here is some reasoning that will help. First, your application will be spending a great deal of time in development where mistakes are commonplace. Providing for programmer-generated faults will make it easier for your team to diagnose and fix them. Second, the (over)use of checked exceptions in the Java library for fault conditions will force your code to deal with them, even if your calling sequences are completely correct. If there's no fault handling framework in place, the resulting makeshift exception handling will inject entropy into your application.</p>
<p>A successful fault handling framework has to accomplish four goals:</p>
<ul>
    <li>Minimize code clutter
    <li>Capture and preserve diagnostics
    <li>Alert the right person
    <li>Exit the activity gracefully </li>
</ul>
<p>Faults are a distraction from your application's real purpose. Therefore, the amount of code devoted to processing them should be minimal and, ideally, isolated from the semantic parts of the application. Fault processing must serve the needs of the people responsible for correcting them. They need to know that a fault happened and get the information that will help them figure out why. Even though a fault, by definition, is not recoverable, good fault handling will attempt to terminate the activity that encountered the fault in a graceful way.</p>
<h4>Use unchecked exceptions for fault conditions</h4>
<p>There are lots of reasons to make the architectural decision to represent fault conditions with unchecked exceptions. The Java runtime rewards programming mistakes by throwing <code>RuntimeException</code> subclasses such as <code>ArithmeticException</code> and <code>ClassCastException</code>, setting a precedent for your architecture. Unchecked exceptions minimize clutter by freeing upstream methods from the requirement to include code for conditions that are irrelevant to their purpose.</p>
<p>Your fault handling strategy should recognize that methods in the Java library and other APIs may use checked exceptions to represent what could only be fault conditions in the context of your application. In this case, adopt the architectural convention to catch the API exception where it happens, treat it as a fault, and throw an unchecked exception to signal the fault condition and capture diagnostic information.</p>
<p>The specific exception type to throw in this situation should be defined by your architecture. Don't forget that the primary purpose of a fault exception is to convey diagnostic information that will be recorded to help people figure out what went wrong. Using multiple fault exception types is probably overkill, since your architecture will treat them all identically. A good, descriptive message embedded inside a single fault exception type will do the job in most cases. It's easy to defend using Java's generic <code>RuntimeException</code> to represent your fault conditions. As of Java 1.4, <code>RuntimeException</code>, like all throwables, supports exception chaining, allowing you to capture and report a fault-inducing checked exception.</p>
<p>You may choose to define your own unchecked exception for the purpose of fault reporting. This would be necessary if you need to use Java 1.3 or earlier versions that do not support exception chaining. It is simple to implement a similar chaining capability to capture and translate checked exceptions that constitute faults in your application. Your application may have a need for special behavior in a fault reporting exception. That would be another reason to create a subclass of <code>RuntimeException</code> for your architecture.</p>
<h4>Establish a fault barrier</h4>
<p>Deciding which exception to throw and when to throw it are important decisions for your fault-handling framework. Just as important are the questions of when to catch a fault exception and what to do afterward. The goal here is to free the functional portions of your application from the responsibility of processing faults. Separation of concerns is generally a good thing, and a central facility responsible for dealing with faults will pay benefits down the road.</p>
<p>In the fault barrier pattern, any application component can throw a fault exception, but only the component acting as the "fault barrier" catches them. Adopting this pattern eliminates much of the intricate code that developers insert locally to deal with faults. The fault barrier resides logically toward the top of the call stack where it stops the upward propagation of an exception before default action is triggered. Default action means different things depending on the application type. For a stand-alone Java application, it means that the active thread is terminated. For a Web application hosted by an application server, it means that the application server sends an unfriendly (and embarrassing) response to the browser.</p>
<p>The first responsibility of a fault barrier component is to record the information contained in the fault exception for future action. An application log is by far the best place to do this. The exception's chained messages, stack traces, and so on, are all valuable pieces of information for diagnosticians. The worst place to send fault information is across the user interface. Involving the client of your application in your debugging process is hardly ever good for you or your client. If you are really tempted to paint the user interface with diagnostic information, it probably means that your logging strategy needs improvement.</p>
<p>The next responsibility of a fault barrier is to close out the operation in a controlled manner. What that means is up to your application design but usually involves generating a generic response to a client that may be waiting for one. If your application is a Web service, it means building a SOAP <code>&lt;fault&gt;</code> element into the response with a <code>&lt;faultcode&gt;</code> of <code>soap:Server</code> and a generic <code>&lt;faultstring&gt;</code> failure message. If your application communicates with a Web browser, the barrier would arrange to send a generic HTML response indicating that the request could not be processed.</p>
<p>In a Struts application, your fault barrier can take the form of a global exception handler configured to process any subclass of <code>RuntimeException</code>. Your fault barrier class will extend<code>org.apache.struts.action.ExceptionHandler</code>, overriding methods as needed to implement the custom processing you need. This will take care of inadvertently generated fault conditions and fault conditions explicitly discovered during the processing of a Struts action. Figure 2 shows contingency and fault exceptions.</p>
<p><img alt="Contingency and Fault Exceptions" src="http://dev2dev.bea.com/images/2006/10/ExceptionBarrierPattern.jpg" /><br />
<em>Figure 2. Contingency and fault exceptions</em></p>
<p>If you are using the Spring MVC framework, your fault barrier can easily be built by extending <code>SimpleMappingExceptionResolver</code> and configuring it to handle <code>RuntimeException</code> and its subclasses. By overriding the <code>resolveException()</code> method, you can add any custom handling you need before using the superclass method to route the request to a view component that sends a generic error display.</p>
<p>When your architecture includes a fault barrier and developers are made aware of it, the temptation to write one-off fault exception handling code decreases dramatically. The result is cleaner and more maintainable code throughout your application.</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td>
            <p><!-- CS_PAGE_INDEX --></p>
            </td>
            <td>
            <p align="right"><a href="http://dev2dev.bea.com/lpt/a/<!--CS_NEXT_REF-->"></a></p>
            </td>
        </tr>
    </tbody>
</table>
<!-- CS_PAGE_BREAK -->
<p><!-- CS_PAGE_INDEX --></p>
<h3>Contingency Handling in Your Architecture</h3>
<p>With fault processing relegated to the barrier, contingency communication between primary components becomes much simpler. A contingency represents an alternative method result that is just as important as the principal return result. Therefore, checked exception type is a good vehicle to convey the existence of a contingency condition and supply the information needed to contend with it. This practice enlists the help of the Java compiler to remind developers of all aspects of the API they are using and the need to provide for the full range of method outcomes.</p>
<p>It is possible to convey simple contingencies by using a method's return type alone. For example, returning a null reference instead of an actual object can signify that the object could not be created for a defined reason. Java I/O methods typically return an integer value of -1 instead of a byte value or byte count to indicate an end-of-file condition. If your method's semantics are simple enough to allow it, alternative return values may be the way to go, since they eliminate the overhead that comes with exceptions. The downside is that the method caller is responsible for testing the return value to see if it is a primary result or a contingency result. The compiler will not insist that the method caller makes that test, however.</p>
<p>If a method has a void return type, an exception is the only way to indicate that a contingency occurred. If a method is returns an object reference, the vocabulary that the return value can express is limited to two values (null and non-null). If a method returns an integral value, it may be possible to express several contingency conditions by choosing values that are guaranteed not to conflict with the primary return values. But now we have entered the world of error code checking, something the Java exception model was developed to avoid.</p>
<h4>Supply something useful</h4>
<p>It made little sense to define different fault reporting exception types, since the fault barrier treats them all the same. Contingency exceptions are quite different, because they are meant to convey diverse conditions to method callers. Your architecture would probably specify that these exceptions should all extend <code>java.lang.Exception</code> or a designated base class that does.</p>
<p>Do not forget your exceptions are complete Java types that can accommodate specialized fields, methods, and even constructors that can be shaped for your unique purposes. For example, the <code>InsufficientFundsException</code> type thrown by the imaginary <code>CheckingAccount</code> <code>processCheck()</code> method could include an <code>OverdraftProtection</code> object that is able to transfer funds needed to cover the shortfall from another account whose identity depends on how the checking account is set up.</p>
<h4>To log or not to log</h4>
<p>Logging fault exceptions makes sense because their purpose is to draw the attention of people to situations that need to be corrected. The same cannot be said for contingency exceptions. They may represent relatively rare events, but every one of them is expected to happen during the life of your application. If anything, they signify that the application is working the way it was designed to work. Routinely adding logging code to contingency catch blocks adds clutter to your code with no actual benefit. If a contingency represents a significant event, it is probably better for a method to generate a log entry recording the event before throwing a contingency exception to alert its caller.</p>
<h3>Exception Aspects</h3>
<p>In Aspect Oriented Programming (AOP) terms, fault and contingency handling are crosscutting concerns. To implement the fault barrier pattern, for example, all the participating classes must follow common conventions:</p>
<ul>
    <li>The fault barrier method must reside at the head of a graph of method calls that traverses the participating classes.
    <li>They must all use unchecked exceptions to signify fault conditions.
    <li>They must all use the specific unchecked exception types that the fault barrier is expecting to receive.
    <li>They all must catch and translate checked exceptions from lower methods that are deemed to be faults in their execution context.
    <li>They must not interfere with the propagation of fault exceptions on their way to the barrier. </li>
</ul>
<p>These concerns cut across the boundaries of otherwise unrelated classes. The result is minor bits of scattered fault handling code and implicit coupling between the barrier class and the participants (although still a great improvement over not using a pattern at all!). AOP allows the fault handling concern to be encapsulated in a common Aspect applied to the participating classes. Java AOP frameworks such as AspectJ and Spring AOP recognize exception handling as a join point to which fault handling behavior (or advice) can be attached. In this way, the conventions that bind participants in the fault barrier pattern can be relaxed. Fault processing can now reside within an independent, out-of-line aspect, eliminating the need for a "barrier" method to be placed at the head of a method invocation sequence.</p>
<p>If you are exploiting AOP in your architecture, fault and contingency handling are ideal candidates for aspects that apply throughout an application. A full exploration of how fault and contingency handling could work in the AOP world would make an interesting topic for a future article.</p>
<h3>Conclusion</h3>
<p>Although the Java exception model has generated spirited discussion during its lifetime, it provides excellent value when it is applied correctly. As an architect, it is up to you to establish conventions that get the most from the model. Thinking of exceptions in terms of faults and contingencies can help you make the right choices. Using the Java exception model properly will keep your application simple, maintainable, and correct. Aspect Oriented Programming techniques may offer some definite advantages for your architecture by recognizing fault and contingency handling as crosscutting concerns.</p>
<h3>References</h3>
<ul>
    <li><a href="http://java.sun.com/docs/books/tutorial/essential/exceptions/index.html">Sun's Exception Tutorial</a> - all of the basics on Java exceptions
    <li><a href="http://www.mindview.net/Etc/Discussions/CheckedExceptions">Does Java Need Checked Exceptions?</a> - Bruce Eckel argues against checked exceptions in Java
    <li><a href="http://www.octopull.demon.co.uk/java/ExceptionalJava.html">Exceptional Java</a> - a good discussion of these topics and an architectural exceptions policy to emulate
    <li><a href="http://www-128.ibm.com/developerworks/java/library/j-jtp05254.html">The Exceptions Debate</a> - more exceptions background from developerWorks
    <li><a href="http://struts.apache.org/1.1/index.html">The Apache Struts Web Application Framework</a> - the source for Struts information
    <li><a href="http://www.springframework.org/">The Spring Framework</a> - the source for the information on Spring
    <li><a href="http://en.wikipedia.org/wiki/Aspect-oriented_programming">Wikipedia: Aspect Oriented Programming</a> - a good introduction to AOP concepts
    <li><a href="http://www.eclipse.org/aspectj/">The AspectJ Project</a> - the source for the information on AspectJ </li>
</ul>
<p><em><a href="http://dev2dev.bea.com/pub/au/3424">Barry Ruzek</a> has been named a Master Certified IT Architect by the Open Group. He has over 30 years of experience developing operating systems and enterprise applications.</em></p>
<br />
<div id="new_input"></div>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/167119.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2007-12-12 09:17 <a href="http://www.blogjava.net/jinfeng_wang/archive/2007/12/12/167119.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>批量重命名代码， 比total command 速度快很多。</title><link>http://www.blogjava.net/jinfeng_wang/archive/2007/06/11/123308.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 11 Jun 2007 02:24:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2007/06/11/123308.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/123308.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2007/06/11/123308.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/123308.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/123308.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">package</span><span style="COLOR: #000000">&nbsp;common;<br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000">&nbsp;java.io.File;<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000">&nbsp;java.io.IOException;<br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img id=Codehighlighter1_91_830_Open_Image onclick="this.style.display='none'; Codehighlighter1_91_830_Open_Text.style.display='none'; Codehighlighter1_91_830_Closed_Image.style.display='inline'; Codehighlighter1_91_830_Closed_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_91_830_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_91_830_Closed_Text.style.display='none'; Codehighlighter1_91_830_Open_Image.style.display='inline'; Codehighlighter1_91_830_Open_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;RenameFile&nbsp;</span><span id=Codehighlighter1_91_830_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"></span><span id=Codehighlighter1_91_830_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img id=Codehighlighter1_152_281_Open_Image onclick="this.style.display='none'; Codehighlighter1_152_281_Open_Text.style.display='none'; Codehighlighter1_152_281_Closed_Image.style.display='inline'; Codehighlighter1_152_281_Closed_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_152_281_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_152_281_Closed_Text.style.display='none'; Codehighlighter1_152_281_Open_Image.style.display='inline'; Codehighlighter1_152_281_Open_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="COLOR: #0000ff">throws</span><span style="COLOR: #000000">&nbsp;IOException&nbsp;</span><span id=Codehighlighter1_152_281_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"></span><span id=Codehighlighter1_152_281_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;String&nbsp;folder&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">G:\\shopdata\\\\userdata_bak\\</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;renameFileName(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;File(folder));<br></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">over</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img id=Codehighlighter1_352_828_Open_Image onclick="this.style.display='none'; Codehighlighter1_352_828_Open_Text.style.display='none'; Codehighlighter1_352_828_Closed_Image.style.display='inline'; Codehighlighter1_352_828_Closed_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_352_828_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_352_828_Closed_Text.style.display='none'; Codehighlighter1_352_828_Open_Image.style.display='inline'; Codehighlighter1_352_828_Open_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;renameFileName(File&nbsp;folder)&nbsp;</span><span style="COLOR: #0000ff">throws</span><span style="COLOR: #000000">&nbsp;IOException&nbsp;</span><span id=Codehighlighter1_352_828_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"></span><span id=Codehighlighter1_352_828_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">folder:&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;folder);<br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;File[]&nbsp;files&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;folder.listFiles();<br></span><span style="COLOR: #008080">16</span><span style="COLOR: #000000"><img id=Codehighlighter1_474_824_Open_Image onclick="this.style.display='none'; Codehighlighter1_474_824_Open_Text.style.display='none'; Codehighlighter1_474_824_Closed_Image.style.display='inline'; Codehighlighter1_474_824_Closed_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_474_824_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_474_824_Closed_Text.style.display='none'; Codehighlighter1_474_824_Open_Image.style.display='inline'; Codehighlighter1_474_824_Open_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;i&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;files.length;&nbsp;i</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)&nbsp;</span><span id=Codehighlighter1_474_824_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"></span><span id=Codehighlighter1_474_824_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;File&nbsp;file&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;files[i];<br></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img id=Codehighlighter1_528_559_Open_Image onclick="this.style.display='none'; Codehighlighter1_528_559_Open_Text.style.display='none'; Codehighlighter1_528_559_Closed_Image.style.display='inline'; Codehighlighter1_528_559_Closed_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_528_559_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_528_559_Closed_Text.style.display='none'; Codehighlighter1_528_559_Open_Image.style.display='inline'; Codehighlighter1_528_559_Open_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(file.isDirectory())&nbsp;</span><span id=Codehighlighter1_528_559_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"></span><span id=Codehighlighter1_528_559_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">19</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;renameFileName(file);<br></span><span style="COLOR: #008080">20</span><span style="COLOR: #000000"><img id=Codehighlighter1_566_820_Open_Image onclick="this.style.display='none'; Codehighlighter1_566_820_Open_Text.style.display='none'; Codehighlighter1_566_820_Closed_Image.style.display='inline'; Codehighlighter1_566_820_Closed_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_566_820_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_566_820_Closed_Text.style.display='none'; Codehighlighter1_566_820_Open_Image.style.display='inline'; Codehighlighter1_566_820_Open_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">&nbsp;</span><span id=Codehighlighter1_566_820_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"></span><span id=Codehighlighter1_566_820_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">21</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;oldName</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">file.getCanonicalPath();<br></span><span style="COLOR: #008080">22</span><span style="COLOR: #000000"><img id=Codehighlighter1_646_665_Open_Image onclick="this.style.display='none'; Codehighlighter1_646_665_Open_Text.style.display='none'; Codehighlighter1_646_665_Closed_Image.style.display='inline'; Codehighlighter1_646_665_Closed_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_646_665_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_646_665_Closed_Text.style.display='none'; Codehighlighter1_646_665_Open_Image.style.display='inline'; Codehighlighter1_646_665_Open_Text.style.display='inline';" src="http://www.blogjava.net/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(oldName.endsWith(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">html</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">))&nbsp;</span><span id=Codehighlighter1_646_665_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"></span><span id=Codehighlighter1_646_665_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">23</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">24</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">25</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;newName&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;File(oldName.replace(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">aspx</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">html</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">));<br></span><span style="COLOR: #008080">26</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">boolean</span><span style="COLOR: #000000">&nbsp;bool</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">file.renameTo(newName);<br></span><span style="COLOR: #008080">27</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">System.out.println(bool&nbsp;+"&nbsp;:&nbsp;"+newName);</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">28</span><span style="COLOR: #008000"><img src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">29</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">30</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">31</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">32</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">33</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">34</span><span style="COLOR: #000000"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top></span></div>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/123308.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2007-06-11 10:24 <a href="http://www.blogjava.net/jinfeng_wang/archive/2007/06/11/123308.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>