﻿<?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-优雅天平-文章分类-开源相关</title><link>http://www.blogjava.net/Victor/category/2893.html</link><description>享受喧嚣 安于平静</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 10:23:20 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 10:23:20 GMT</pubDate><ttl>60</ttl><item><title>JFreeChart完全攻略示例各种方法和属性</title><link>http://www.blogjava.net/Victor/articles/28941.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Sun, 22 Jan 2006 06:48:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/28941.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/28941.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/28941.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/28941.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/28941.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 封装FreeChart的BeanFreeChart.javapackage com.function;/*基于JFreeChart 1.0.0 Pre2.jar*/import java.io.*;import java.awt.Font;import java.awt.Color;import java.text.SimpleDateFormat;import java.text.Dec...&nbsp;&nbsp;<a href='http://www.blogjava.net/Victor/articles/28941.html'>阅读全文</a><img src ="http://www.blogjava.net/Victor/aggbug/28941.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2006-01-22 14:48 <a href="http://www.blogjava.net/Victor/articles/28941.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开启log4j的记录源代码行号的配置，会对系统性能产生影响！ </title><link>http://www.blogjava.net/Victor/articles/23099.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 09 Dec 2005 03:05:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/23099.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/23099.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/23099.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/23099.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/23099.html</trackback:ping><description><![CDATA[今天看到一篇文章，谈Log4j对系统的效能的影响...整理如下：<BR><BR>一般，我们用log4j写log的时候，很习惯于如下面记录：<BR><BR>
<TABLE style="FONT-SIZE: 9pt; FONT-FAMILY: courier new; BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=3 width=780 bgColor=#cccc99 border=1>
<TBODY>
<TR>
<TD>2005-01-11 09:57:57,816 DEBUG (<SPAN style="FONT-WEIGHT: bold; COLOR: rgb(0,0,128)">BizProc.java:275</SPAN>) Begin to get connection...<BR>2005-01-11 09:57:57,816 DEBUG (BizProc.java:275)&nbsp;&nbsp; Obtain connection 2345@defd <BR>2005-01-11 09:57:57,816 DEBUG (BizProc.java:275)&nbsp;&nbsp; Begin transaction [TX_2015_RECV] processing...<BR>2005-01-11 09:57:57,925 DEBUG (BizProc.java:328)&nbsp;&nbsp;&nbsp;&nbsp; All of the values were:</TD></TR></TBODY></TABLE>
<P>这里会把Java Source Code中的行号带出来，查找错误很方便...</P>
<P>但，这也是问题之所在...文章中介绍，Log4j之所以可以得到行号，是用了JVM的一个特性，不知道大家有没有注意到，如果我们的Java程序在运行时出现错误，会出现下面的信息： </P>
<TABLE style="FONT-SIZE: 9pt; FONT-FAMILY: courier new; BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=3 width=780 bgColor=#cccc99 border=1>
<TBODY>
<TR>
<TD><FONT face="Courier New" color=#993300 size=2>javax.naming.NamingException: Cannot create resource instance<BR>&nbsp;at org.apache.naming.factory.ResourceFactory.getObjectInstance(<SPAN style="FONT-WEIGHT: bold">ResourceFactory.java:189</SPAN>)<BR>&nbsp;at javax.naming.spi.NamingManager.getObjectInstance(<SPAN style="FONT-WEIGHT: bold">NamingManager.java:301</SPAN>)<BR>&nbsp;at org.apache.naming.NamingContext.lookup(<SPAN style="FONT-WEIGHT: bold">NamingContext.java:834</SPAN>)<BR>&nbsp;at org.apache.naming.NamingContext.lookup(NamingContext.java:181)<BR>&nbsp;at org.apache.naming.NamingContext.lookup(NamingContext.java:822)<BR>&nbsp;at org.apache.naming.NamingContext.lookup(NamingContext.java:181)<BR>&nbsp;at org.apache.naming.NamingContext.lookup(NamingContext.java:822)<BR>&nbsp;at org.apache.naming.NamingContext.lookup(NamingContext.java:181)<BR>&nbsp;at org.apache.naming.NamingContext.lookup(NamingContext.java:822)<BR>&nbsp;at org.apache.naming.NamingContext.lookup(NamingContext.java:194)<BR>&nbsp;at org.apache.naming.SelectorContext.lookup(SelectorContext.java:183)<BR>&nbsp;at javax.naming.InitialContext.lookup(InitialContext.java:347)</FONT></TD></TR></TBODY></TABLE>
<P>看到黑体字没有，原来JVM已经内嵌支持Debug出行号信息了！可是，这里是出错的时候，JVM抛出的，那为什么用<SPAN style="COLOR: rgb(0,0,255); FONT-FAMILY: Courier New"> log4j.info() </SPAN>的时候，也可以看到行号呢？ <BR></P>
<P>下面就是重点，Log4j中记录行号的过程...</P>
<TABLE style="FONT-SIZE: 9pt; FONT-FAMILY: courier new; BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=3 width=780 bgColor=#cccc99 border=1>
<TBODY>
<TR>
<TD>
<P>(1) 调用了 log4j.info() 或是log4j.debug()方法...<BR>(2) 建立一个<CODE style="FONT-WEIGHT: bold; COLOR: rgb(0,0,255)">Throwable</CODE>的对象来取得当前运行堆栈的快照...<CODE><SPAN style="FONT-WEIGHT: bold; COLOR: rgb(0,0,255)">Throwable.fillInStackTrace()</SPAN>;</CODE><BR>(3) 从抛出的<SPAN style="FONT-WEIGHT: bold; COLOR: rgb(0,0,255)">Throwable</SPAN>对象中，来分析出当前log信息的行号...<BR type="_moz"></P></TD></TR></TBODY></TABLE>
<P><SPAN style="FONT-WEIGHT: bold; COLOR: rgb(0,0,255)">但是，请注意，建立运行时的堆栈跟踪，对JVM来说，是一项非常大的开销！</SPAN></P>
<P><SPAN style="FONT-WEIGHT: bold; COLOR: rgb(0,0,255)"></SPAN>所以，我们在配置log4j的PatternLayout的时候，如果是要上线到正式环境，一定要记者把参数关掉，<BR>一般来说，用 <SPAN style="FONT-WEIGHT: bold; COLOR: rgb(0,0,255); FONT-FAMILY: Courier New">“%d %-5p&nbsp; %m%n"</SPAN> 就可以满足了！</P><BR>原文请参考这里：<A href="http://weblogs.java.net/blog/zarar/archive/2005/11/sneaky_sneaky_l.html" target=_blank>Sneaky, sneaky Log4J</A><BR><BR>===========================================================================<BR>补遗篇~~~~~！<BR><BR><FONT size=2><SPAN style="FONT-FAMILY: Courier New">log4j.rootLogger=info, Console </SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.logger.net.csdn.blog.xport=debug, RollingFile </SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.additivity.net.csdn.blog.xport=false </SPAN><BR style="FONT-FAMILY: Courier New"><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.appender.Console=org.apache.log4j.ConsoleAppender </SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.appender.Console.layout=org.apache.log4j.PatternLayout </SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.appender.Console.layout.ConversionPattern=%d %-5p [%t] %C{1} - %m%n </SPAN><BR style="FONT-FAMILY: Courier New"><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.appender.RollingFile=org.apache.log4j.RollingFileAppender </SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.appender.RollingFile.File=SystemLog.log </SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.appender.RollingFile.Append=true </SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.appender.RollingFile.MaxFileSize=2MB </SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.appender.RollingFile.layout=org.apache.log4j.PatternLayout </SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">log4j.appender.RollingFile.layout.ConversionPattern=%d %-5p [%t] %C{1} - %m%n</SPAN></FONT><img src ="http://www.blogjava.net/Victor/aggbug/23099.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-09 11:05 <a href="http://www.blogjava.net/Victor/articles/23099.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java开源Blog实现，Roller 2.0 的安装手记！ (转)</title><link>http://www.blogjava.net/Victor/articles/23097.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 09 Dec 2005 02:55:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/23097.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/23097.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/23097.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/23097.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/23097.html</trackback:ping><description><![CDATA[<A href="http://www.rollerweblogger.org/" target=_blank><IMG alt="" src="http://blog.csdn.net/images/blog_csdn_net/xport/72526/o_Roller.jpg" border=1></A><BR><BR>Roller 是一个Struts + Hibernate 的 Java 开源实现，正好拿来研究研究，首先就是要把它安装起来了，整理<BR>了一下步骤和注意事项，记录如下：<BR><BR>准备需要的基本环境：<BR><BR><FONT style="COLOR: rgb(0,0,255)" size=2><SPAN style="FONT-FAMILY: courier new">1. <A href="http://java.sun.com/j2se/1.4.2/download.html" target=_blank>Java 2 SDK 1.4.2_10</A></SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">2. <A href="http://tomcat.apache.org/download-55.cgi#5.0.28" target=_blank>Tomcat 5.0.28</A></SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">3. <A href="https://roller.dev.java.net/servlets/ProjectDocumentList?folderID=4476&amp;expandFolder=4476&amp;folderID=4396" target=_blank>Roller 2.0</A> <BR>4. <A href="http://dev.mysql.com/get/Downloads/MySQL-5.0/mysql-noinstall-5.0.16-win32.zip/from/http://mirror.vmmatrix.net/mysql/" target=_blank>MySQL 5.0.16</A><BR><BR></SPAN></FONT>首先，确保MySQL开启了对InnoDB和UTF-8的支持，在my.ini中开启下面的参数：<BR>
<TABLE style="WIDTH: 560px; FONT-FAMILY: courier new; HEIGHT: 40px" cellSpacing=1 cellPadding=1 border=1>
<TBODY>
<TR>
<TD><FONT size=2><BR>[mysqld]<BR></FONT><FONT size=2>default-storage-engine=innodb</FONT><BR><FONT size=2>default-character-set=utf8<BR></FONT><BR type="_moz"></TD></TR></TBODY></TABLE><BR>启动好MySQL待用...<FONT size=2><SPAN style="FONT-FAMILY: courier new"><SPAN style="COLOR: rgb(0,0,255)">D:\MySQL\mysqld.exe --console --datadir=I:\MyData\Data</SPAN><BR></SPAN></FONT><BR>假设我的Tomcat主目录为：<FONT style="COLOR: rgb(0,0,255)" size=2><SPAN style="FONT-FAMILY: courier new">$CATALINA_HOME=D:\J2EE_HOME\AppServer\Tomcat<BR></SPAN></FONT><BR>把压缩的Roller文件<FONT size=2><SPAN style="FONT-FAMILY: courier new"> roller-2.0-incubating.tar</SPAN></FONT> 解开...<FONT size=2><SPAN style="FONT-FAMILY: courier new"><BR>并放到</SPAN></FONT><FONT style="COLOR: rgb(0,0,255)" size=2><SPAN style="FONT-FAMILY: courier new">$CATALINA_HOME\WebApp\roller</SPAN></FONT>下面...如图...<BR><BR><IMG alt="" src="http://blog.csdn.net/images/blog_csdn_net/xport/72526/o_RollerDirStructure.jpg" border=1><BR><BR>开始建立MySQL中的数据库：<BR><FONT size=2><SPAN style="FONT-FAMILY: courier new"></SPAN></FONT><PRE><FONT style="COLOR: rgb(0,0,255)" size=2><SPAN style="FONT-FAMILY: Courier New">    <SPAN style="FONT-WEIGHT: bold; COLOR: rgb(0,0,0)">I:&gt;</SPAN> CD %CATALINA_HOME%\webapps\roller\WEB-INF\dbscripts\mysql</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">    <SPAN style="FONT-WEIGHT: bold; COLOR: rgb(0,0,0)">I:&gt;</SPAN> mysql -u root -p</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">    password: *****</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">    <SPAN style="COLOR: rgb(0,0,0)">mysql&gt;</SPAN> create database roller;</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">    <SPAN style="COLOR: rgb(0,0,0)">mysql&gt;</SPAN> grant all on roller.* to scott@'%' identified by 'tiger';</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">    <SPAN style="COLOR: rgb(0,0,0)">mysql&gt;</SPAN> grant all on roller.* to scott@'localhost' identified by 'tiger';</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">    <SPAN style="COLOR: rgb(0,0,0)">mysql&gt;</SPAN> use roller;</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">    <SPAN style="COLOR: rgb(0,0,0)">mysql&gt;</SPAN> source createdb.sql</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">    <SPAN style="COLOR: rgb(0,0,0)">mysql&gt;</SPAN> quit</SPAN></FONT><BR><BR></PRE>在 <SPAN style="COLOR: rgb(0,0,255); FONT-FAMILY: Courier New">$CATALINA_HOME\conf\Catalina\localhost</SPAN> 中放置Roller的Web Application的部署<BR>配置档案 <SPAN style="COLOR: rgb(0,0,255); FONT-FAMILY: Courier New">roller.xml</SPAN>，内容如下：<BR><BR type="_moz">
<TABLE style="FONT-FAMILY: Courier New" cellSpacing=1 cellPadding=1 width=780 border=1>
<TBODY>
<TR>
<TD><PRE style="FONT-FAMILY: Courier New"><FONT size=2>&lt;Context path="/roller" docBase="${catalina.home}/webapps/roller" debug="0"&gt;<BR>    &lt;Realm className="org.apache.catalina.realm.JDBCRealm"<BR>        driverName="com.mysql.jdbc.Driver" <BR>        connectionURL="jdbc:mysql://localhost:3306/roller?autoReconnect=true&amp;amp;useUnicode=true&amp;amp;characterEncoding=utf-8&amp;amp;mysqlEncoding=utf8"<BR>        connectionName="scott"<BR>        connectionPassword="tiger"<BR>        userTable="rolleruser"<BR>        userNameCol="username"<BR>        userCredCol="passphrase"<BR>        userRoleTable="userrole"<BR>        roleNameCol="rolename" debug="0" /&gt;<BR><BR>    &lt;Resource name="jdbc/rollerdb" auth="Container" type="javax.sql.DataSource" /&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>&lt;ResourceParams name="jdbc/rollerdb"&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;name&gt;factory&lt;/name&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;value&gt;org.apache.commons.dbcp.BasicDataSourceFactory&lt;/value&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;/parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;name&gt;driverClassName&lt;/name&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;value&gt;com.mysql.jdbc.Driver&lt;/value&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;/parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;name&gt;url&lt;/name&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;value&gt;jdbc:mysql://localhost:3306/roller?autoReconnect=true&amp;amp;useUnicode=true&amp;amp;characterEncoding=utf-8&amp;amp;mysqlEncoding=utf8&lt;/value&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;/parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;name&gt;username&lt;/name&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;value&gt;scott&lt;/value&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;/parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;name&gt;password&lt;/name&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;value&gt;tiger&lt;/value&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;/parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;name&gt;maxActive&lt;/name&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;value&gt;20&lt;/value&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;/parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;name&gt;maxIdle&lt;/name&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;value&gt;3&lt;/value&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;/parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;parameter&gt;<BR>    </FONT><FONT size=2>    </FONT><FONT size=2>    &lt;name&gt;removeAbandoned&lt;/name&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;value&gt;true&lt;/value&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;/parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;name&gt;maxWait&lt;/name&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;value&gt;3000&lt;/value&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>     &lt;/parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>&lt;/ResourceParams&gt;<BR><BR></FONT><FONT size=2>    </FONT><FONT size=2>&lt;!--//<BR>    </FONT><FONT size=2>    </FONT><FONT size=2>To enable email notification of comments: uncomment the resouce below, <BR></FONT><FONT size=2>    </FONT><FONT size=2>    set your mailhost, and make sure you have mail.jar and activation.jar <BR></FONT><FONT size=2>    </FONT><FONT size=2>    in &lt;tomcat&gt;/common/lib.<BR></FONT><FONT size=2>    </FONT><FONT size=2>//--&gt;<BR><BR></FONT><FONT size=2>    </FONT><FONT size=2>&lt;!--//<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;Resource name="mail/Session" auth="Container" type="javax.mail.Session"/&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>    &lt;ResourceParams name="mail/Session"&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>        &lt;parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>            &lt;name&gt;mail.smtp.host&lt;/name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT><FONT size=2>    </FONT><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;mailhost.example.com&lt;/value&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/parameter&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>&nbsp;&nbsp;&nbsp; &lt;/ResourceParams&gt;<BR></FONT><FONT size=2>    </FONT><FONT size=2>//--&gt;<BR><BR>&lt;/Context&gt;</FONT></PRE></TD></TR></TBODY></TABLE>
<P>完成上面的设定后，接下来，我们修改一下Tomcat中的Connector的设定，使它能够支持UTF-8的编码，<BR>打开 <SPAN style="COLOR: rgb(0,0,255); FONT-FAMILY: Courier New">$CATALINA_HOME\conf\server.xml</SPAN>，做如下修改...<BR><BR></P>
<TABLE style="FONT-FAMILY: Courier New" cellSpacing=1 cellPadding=1 width=780 border=1>
<TBODY>
<TR>
<TD><FONT size=2>&lt;!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 8080 --&gt;<BR>&lt;Connector port="80"<BR>&nbsp;&nbsp;&nbsp; <SPAN style="COLOR: rgb(0,0,255)">URIEncoding="UTF-8"</SPAN> <BR>&nbsp;&nbsp;&nbsp; maxThreads="150" minSpareThreads="25" maxSpareThreads="75"<BR>&nbsp;&nbsp;&nbsp; enableLookups="false" redirectPort="8443" acceptCount="100"<BR>&nbsp;&nbsp;&nbsp; debug="0" connectionTimeout="20000" <BR>&nbsp;&nbsp;&nbsp; disableUploadTimeout="true" /&gt;</FONT></TD></TR></TBODY></TABLE><BR>到这里，基本上Tomcat相关的设定就完成了，因为Roller Blog可以支持上传附件和全文检索等功能，<BR>所以，我们还有一个设定的动作... 
<P>在 <FONT style="COLOR: rgb(0,0,255)" size=2><SPAN style="FONT-FAMILY: courier new">$CATALINA_HOME\WebApp\roller</SPAN></FONT> 中新建下面层次的目录：<BR></P>
<P></P>
<TABLE style="WIDTH: 551px; HEIGHT: 104px" cellSpacing=1 cellPadding=1 border=1>
<TBODY>
<TR>
<TD><FONT size=2><SPAN style="FONT-FAMILY: Courier New">&nbsp;$CATALINA_HOME\WebApp\roller</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +---data<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; |<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; +---search-index<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; +---uploads<BR type="_moz"></SPAN></FONT></TD></TR></TBODY></TABLE><BR>找到 <SPAN style="COLOR: rgb(0,0,255); FONT-FAMILY: Courier New">$CATALINA_HOME\webapps\roller\WEB-INF\classes\roller.properties</SPAN>&nbsp; ，做如下修改：<BR><BR><SPAN style="COLOR: rgb(0,0,255); FONT-FAMILY: Courier New">
<TABLE style="WIDTH: 552px; HEIGHT: 23px" cellSpacing=1 cellPadding=1 border=1>
<TBODY>
<TR>
<TD><FONT style="COLOR: rgb(0,0,0)" size=2><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New"># The directory in which Roller will upload files</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New"># uploads.dir=${user.home}/roller_data/uploads</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">uploads.dir=${TOMCAT_HOME}/webapps/roller/data/uploads</SPAN><BR style="FONT-FAMILY: Courier New"><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New"># force Roller to recreate the entire search index)</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New"># search.index.dir=${user.home}/roller_data/search-index</SPAN><BR style="FONT-FAMILY: Courier New"><SPAN style="FONT-FAMILY: Courier New">search.index.dir=${TOMCAT_HOME}/webapps/roller/data/search-index</SPAN></FONT><BR><BR type="_moz"></TD></TR></TBODY></TABLE><BR type="_moz"></SPAN>
<P>好了，大功告成，启动Tomcat，可以运行Roller了！</P><img src ="http://www.blogjava.net/Victor/aggbug/23097.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-09 10:55 <a href="http://www.blogjava.net/Victor/articles/23097.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Jakarta-Common-BeanUtils研究心得(2)[转载]</title><link>http://www.blogjava.net/Victor/articles/21918.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Wed, 30 Nov 2005 02:39:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/21918.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/21918.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/21918.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/21918.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/21918.html</trackback:ping><description><![CDATA[作者 :SonyMusic&nbsp;&nbsp;&nbsp; 2003.05.13<BR><BR>六、ConstructorUtils补遗<BR>创建对象还有一个方法：invokeExactConstructor，该方法对参数要求<BR>更加严格，传递进去的参数必须严格符合构造方法的参数列表。<BR>例如：<BR>Object[] args={new Integer(1), "Jan"};<BR>Class[] argsType={int.class, String.class};<BR>Object obj;<BR>//下面这句调用将不会成功，因为args[0]的类型为Integer，而不是int<BR>//obj = ConstructorUtils.invokeExactConstructor(Month.class, args);<BR><BR>//这一句就可以，因为argsType指定了类型。<BR>obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType);<BR>Month month=(Month)obj;<BR>System.out.println(BeanUtils.getProperty(month,"value"));<BR><BR><BR>七、MethodUtils<BR>与ConstructorUtils类似，不过调用的时候，通常需要再指定一个method name的参数。<BR><BR>八、DynaClass/DynaBean<BR>这似乎是BeanUtils中最有趣的部分之一了，很简单，简单到光看这两个接口中的方法会不明白<BR>为什么要设计这两个接口。不过看到ResultSetDynaClass后，就明白了。下面是java doc中的代码：<BR>&nbsp;&nbsp;&nbsp;ResultSet rs = ...;<BR>&nbsp;&nbsp;&nbsp;ResultSetDynaClass rsdc = new ResultSetDynaClass(rs);<BR>&nbsp;&nbsp;&nbsp;Iterator rows = rsdc.iterator();<BR>&nbsp;&nbsp;&nbsp;while (rows.hasNext())&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DynaBean row = (DynaBean) rows.next();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;... process this row ...<BR>&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;rs.close();<BR>原来这是一个ResultSet的包装器，ResultSetDynaClass实现了DynaClass，它的iterator方法返回一个<BR>ResultSetIterator，则是实现了DynaBean接口。<BR>在获得一个DynaBean之后，我们就可以用<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DynaBean row = (DynaBean) rows.next();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(row.get("field1")); //field1是其中一个字段的名字<BR><BR>再看另一个类RowSetDynaClass的用法，代码如下：<BR>String driver="com.mysql.jdbc.Driver";<BR>String url="jdbc:mysql://localhost/2hu?useUnicode=true&amp;characterEncoding=GBK";<BR>String username="root";<BR>String password="";<BR><BR>java.sql.Connection con=null;<BR>PreparedStatement ps=null;<BR>ResultSet rs=null;<BR>try {<BR>Class.forName(driver).newInstance();<BR>con = DriverManager.getConnection(url);<BR>ps=con.prepareStatement("select * from forumlist");<BR>rs=ps.executeQuery();<BR>//先打印一下，用于检验后面的结果。<BR>while(rs.next()){<BR>System.out.println(rs.getString("name"));<BR>}<BR>rs.beforeFirst();//这里必须用beforeFirst，因为RowSetDynaClass只从当前位置向前滚动<BR><BR>RowSetDynaClass rsdc = new RowSetDynaClass(rs);<BR>rs.close();<BR>ps.close();<BR>List rows = rsdc.getRows();//返回一个标准的List，存放的是DynaBean<BR>for (int i = 0; i &lt;rows.size(); i++) {<BR>DynaBean b=(DynaBean)rows.get(i);<BR>System.out.println(b.get("name"));<BR>}<BR>} catch (Exception e) {<BR>e.printStackTrace();<BR>}<BR>finally{<BR>try {<BR>con.close();<BR>} catch (Exception e) {<BR>}<BR>}<BR><BR>是不是很有趣？封装了ResultSet的数据，代价是占用内存。如果一个表有10万条记录，rsdc.getRows()<BR>就会返回10万个记录。@_@<BR><BR>需要注意的是ResultSetDynaClass和RowSetDynaClass的不同之处：<BR>1，ResultSetDynaClass是基于Iterator的，一次只返回一条记录，而RowSetDynaClass是基于<BR>List的，一次性返回全部记录。直接影响是在数据比较多时ResultSetDynaClass会比较的快速，<BR>而RowSetDynaClass需要将ResultSet中的全部数据都读出来（并存储在其内部），会占用过多的<BR>内存，并且速度也会比较慢。<BR>2，ResultSetDynaClass一次只处理一条记录，在处理完成之前，ResultSet不可以关闭。<BR>3，ResultSetIterator的next()方法返回的DynaBean其实是指向其内部的一个固定<BR>对象，在每次next()之后，内部的值都会被改变。这样做的目的是节约内存，如果你需要保存每<BR>次生成的DynaBean，就需要创建另一个DynaBean，并将数据复制过去，下面也是java doc中的代码：<BR>&nbsp;&nbsp;&nbsp;ArrayList results = new ArrayList(); // To hold copied list<BR>&nbsp;&nbsp;&nbsp;ResultSetDynaClass rsdc = ...;<BR>&nbsp;&nbsp;&nbsp;DynaProperty properties[] = rsdc.getDynaProperties();<BR>&nbsp;&nbsp;&nbsp;BasicDynaClass bdc =<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new BasicDynaClass("foo", BasicDynaBean.class,<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;rsdc.getDynaProperties());<BR>&nbsp;&nbsp;&nbsp;Iterator rows = rsdc.iterator();<BR>&nbsp;&nbsp;&nbsp;while (rows.hasNext()) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DynaBean oldRow = (DynaBean) rows.next();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DynaBean newRow = bdc.newInstance();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PropertyUtils.copyProperties(newRow, oldRow);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;results.add(newRow);<BR>&nbsp;&nbsp;&nbsp;}<BR><BR>事实上DynaClass/DynaBean可以用于很多地方，存储各种类型的数据。自己想吧。嘿嘿。<BR><BR><BR>九、自定义的CustomRowSetDynaClass<BR>两年前写过一个与RowSetDynaClass目标相同的类，不过多一个功能，就是分页，只取需要的数据，<BR>这样内存占用就会减少。<BR><BR>先看一段代码：<BR>String driver="com.mysql.jdbc.Driver";<BR>String url="jdbc:mysql://localhost/2hu?useUnicode=true&amp;characterEncoding=GBK";<BR>String username="root";<BR>String password="";<BR><BR>java.sql.Connection con=null;<BR>PreparedStatement ps=null;<BR>ResultSet rs=null;<BR>try {<BR>Class.forName(driver).newInstance();<BR>con = DriverManager.getConnection(url);<BR>ps=con.prepareStatement("select * from forumlist order by name");<BR>rs=ps.executeQuery();<BR>/*<BR>while(rs.next()){<BR>System.out.println(rs.getString("name"));<BR>}<BR>rs.beforeFirst();<BR>*/<BR><BR>//第二个参数表示第几页，第三个参数表示页的大小<BR>CustomRowSetDynaClass rsdc = new CustomRowSetDynaClass(rs, 2, 5);<BR>//RowSetDynaClass rsdc = new RowSetDynaClass(rs);<BR>rs.close();<BR>ps.close();<BR>List rows = rsdc.getRows();<BR>for (int i = 0; i &lt;rows.size(); i++) {<BR>DynaBean b=(DynaBean)rows.get(i);<BR>System.out.println(b.get("name"));<BR>}<BR>} catch (Exception e) {<BR>e.printStackTrace();<BR>}<BR>finally{<BR>try {<BR>con.close();<BR>} catch (Exception e) {<BR>}<BR>}<BR>在这里用到了一个CustomRowSetDynaClass类，构造方法中增加了page和pageSize两个参数，<BR>这样，不管数据库里有多少条记录，最多只取pageSize条记录，若pageSize==-1，则功能和<BR>RowSetDynaClass一样。这在大多数情况下是适用的。该类的代码如下：<BR><BR>package test.jakarta.commons.beanutils;<BR><BR>import java.io.*;<BR>import java.sql.*;<BR>import java.util.*;<BR><BR>import org.apache.commons.beanutils.*;<BR><BR>/**<BR>* @author SonyMusic<BR>*<BR>* To change this generated comment edit the template variable "typecomment":<BR>* Window&gt;Preferences&gt;Java&gt;Templates.<BR>* To enable and disable the creation of type comments go to<BR>* Window&gt;Preferences&gt;Java&gt;Code Generation.<BR>*/<BR>public class CustomRowSetDynaClass implements DynaClass, Serializable {<BR><BR>// ----------------------------------------------------------- Constructors<BR><BR>/**<BR>* &lt;p&gt;Construct a new {@link RowSetDynaClass} for the specified<BR>* &lt;code&gt;ResultSet&lt;/code&gt;.&nbsp;&nbsp;The property names corresponding<BR>* to column names in the result set will be lower cased.&lt;/p&gt;<BR>*<BR>* @param resultSet The result set to be wrapped<BR>*<BR>* @exception NullPointerException if &lt;code&gt;resultSet&lt;/code&gt;<BR>*&nbsp;&nbsp;is &lt;code&gt;null&lt;/code&gt;<BR>* @exception SQLException if the metadata for this result set<BR>*&nbsp;&nbsp;cannot be introspected<BR>*/<BR>public CustomRowSetDynaClass(ResultSet resultSet) throws SQLException {<BR><BR>this(resultSet, true);<BR><BR>}<BR><BR>/**<BR>* &lt;p&gt;Construct a new {@link RowSetDynaClass} for the specified<BR>* &lt;code&gt;ResultSet&lt;/code&gt;.&nbsp;&nbsp;The property names corresponding<BR>* to the column names in the result set will be lower cased or not,<BR>* depending on the specified &lt;code&gt;lowerCase&lt;/code&gt; value.&lt;/p&gt;<BR>*<BR>* &lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt; - If you specify &lt;code&gt;false&lt;/code&gt;<BR>* for &lt;code&gt;lowerCase&lt;/code&gt;, the returned property names will<BR>* exactly match the column names returned by your JDBC driver.<BR>* Because different drivers might return column names in different<BR>* cases, the property names seen by your application will vary<BR>* depending on which JDBC driver you are using.&lt;/p&gt;<BR>*<BR>* @param resultSet The result set to be wrapped<BR>* @param lowerCase Should property names be lower cased?<BR>*<BR>* @exception NullPointerException if &lt;code&gt;resultSet&lt;/code&gt;<BR>*&nbsp;&nbsp;is &lt;code&gt;null&lt;/code&gt;<BR>* @exception SQLException if the metadata for this result set<BR>*&nbsp;&nbsp;cannot be introspected<BR>*/<BR>public CustomRowSetDynaClass(ResultSet resultSet, boolean lowerCase)<BR>throws SQLException {<BR><BR>this(resultSet, 1, -1, lowerCase);<BR><BR>}<BR><BR>public CustomRowSetDynaClass(<BR>ResultSet resultSet,<BR>int page,<BR>int pageSize,<BR>boolean lowerCase)<BR>throws SQLException {<BR><BR>if (resultSet == null) {<BR>throw new NullPointerException();<BR>}<BR>this.lowerCase = lowerCase;<BR>this.page = page;<BR>this.pageSize = pageSize;<BR><BR>introspect(resultSet);<BR>copy(resultSet);<BR><BR>}<BR><BR>public CustomRowSetDynaClass(ResultSet resultSet, int page, int pageSize)<BR>throws SQLException {<BR>this(resultSet, page, pageSize, true);<BR>}<BR><BR>// ----------------------------------------------------- Instance Variables<BR><BR>/**<BR>* &lt;p&gt;Flag defining whether column names should be lower cased when<BR>* converted to property names.&lt;/p&gt;<BR>*/<BR>protected boolean lowerCase = true;<BR><BR>protected int page = 1;<BR>protected int pageSize = -1;<BR><BR>/**<BR>* &lt;p&gt;The set of dynamic properties that are part of this<BR>* {@link DynaClass}.&lt;/p&gt;<BR>*/<BR>protected DynaProperty properties[] = null;<BR><BR>/**<BR>* &lt;p&gt;The set of dynamic properties that are part of this<BR>* {@link DynaClass}, keyed by the property name.&nbsp;&nbsp;Individual descriptor<BR>* instances will be the same instances as those in the<BR>* &lt;code&gt;properties&lt;/code&gt; list.&lt;/p&gt;<BR>*/<BR>protected Map propertiesMap = new HashMap();<BR><BR>/**<BR>* &lt;p&gt;The list of {@link DynaBean}s representing the contents of<BR>* the original &lt;code&gt;ResultSet&lt;/code&gt; on which this<BR>* {@link RowSetDynaClass} was based.&lt;/p&gt;<BR>*/<BR>protected List rows = new ArrayList();<BR><BR>// ------------------------------------------------------ DynaClass Methods<BR><BR>/**<BR>* &lt;p&gt;Return the name of this DynaClass (analogous to the<BR>* &lt;code&gt;getName()&lt;/code&gt; method of &lt;code&gt;java.lang.Class&lt;/code), which<BR>* allows the same &lt;code&gt;DynaClass&lt;/code&gt; implementation class to support<BR>* different dynamic classes, with different sets of properties.&lt;/p&gt;<BR>*/<BR>public String getName() {<BR><BR>return (this.getClass().getName());<BR><BR>}<BR><BR>/**<BR>* &lt;p&gt;Return a property descriptor for the specified property, if it<BR>* exists; otherwise, return &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;<BR>*<BR>* @param name Name of the dynamic property for which a descriptor<BR>*&nbsp;&nbsp;is requested<BR>*<BR>* @exception IllegalArgumentException if no property name is specified<BR>*/<BR>public DynaProperty getDynaProperty(String name) {<BR><BR>if (name == null) {<BR>throw new IllegalArgumentException("No property name specified");<BR>}<BR>return ((DynaProperty) propertiesMap.get(name));<BR><BR>}<BR><BR>/**<BR>* &lt;p&gt;Return an array of &lt;code&gt;ProperyDescriptors&lt;/code&gt; for the properties<BR>* currently defined in this DynaClass.&nbsp;&nbsp;If no properties are defined, a<BR>* zero-length array will be returned.&lt;/p&gt;<BR>*/<BR>public DynaProperty[] getDynaProperties() {<BR><BR>return (properties);<BR><BR>}<BR><BR>/**<BR>* &lt;p&gt;Instantiate and return a new DynaBean instance, associated<BR>* with this DynaClass.&nbsp;&nbsp;&lt;strong&gt;NOTE&lt;/strong&gt; - This operation is not<BR>* supported, and throws an exception.&lt;/p&gt;<BR>*<BR>* @exception IllegalAccessException if the Class or the appropriate<BR>*&nbsp;&nbsp;constructor is not accessible<BR>* @exception InstantiationException if this Class represents an abstract<BR>*&nbsp;&nbsp;class, an array class, a primitive type, or void; or if instantiation<BR>*&nbsp;&nbsp;fails for some other reason<BR>*/<BR>public DynaBean newInstance()<BR>throws IllegalAccessException, InstantiationException {<BR><BR>throw new UnsupportedOperationException("newInstance() not supported");<BR><BR>}<BR><BR>// --------------------------------------------------------- Public Methods<BR><BR>/**<BR>* &lt;p&gt;Return a &lt;code&gt;List&lt;/code&gt; containing the {@link DynaBean}s that<BR>* represent the contents of each &lt;code&gt;Row&lt;/code&gt; from the<BR>* &lt;code&gt;ResultSet&lt;/code&gt; that was the basis of this<BR>* {@link RowSetDynaClass} instance.&nbsp;&nbsp;These {@link DynaBean}s are<BR>* disconnected from the database itself, so there is no problem with<BR>* modifying the contents of the list, or the values of the properties<BR>* of these {@link DynaBean}s.&nbsp;&nbsp;However, it is the application's<BR>* responsibility to persist any such changes back to the database,<BR>* if it so desires.&lt;/p&gt;<BR>*/<BR>public List getRows() {<BR><BR>return (this.rows);<BR><BR>}<BR><BR>// ------------------------------------------------------ Protected Methods<BR><BR>/**<BR>* &lt;p&gt;Copy the column values for each row in the specified<BR>* &lt;code&gt;ResultSet&lt;/code&gt; into a newly created {@link DynaBean}, and add<BR>* this bean to the list of {@link DynaBean}s that will later by<BR>* returned by a call to &lt;code&gt;getRows()&lt;/code&gt;.&lt;/p&gt;<BR>*<BR>* @param resultSet The &lt;code&gt;ResultSet&lt;/code&gt; whose data is to be<BR>*&nbsp;&nbsp;copied<BR>*<BR>* @exception SQLException if an error is encountered copying the data<BR>*/<BR>protected void copy(ResultSet resultSet) throws SQLException {<BR>int abs = 0;<BR>int rowsCount = 0;<BR>int currentPageRows = 0;<BR>resultSet.last();<BR>rowsCount = resultSet.getRow();<BR>if (pageSize != -1) {<BR>int totalPages = (int) Math.ceil(((double) rowsCount) / pageSize);<BR>if (page &gt; totalPages)<BR>page = totalPages;<BR>if (page &lt; 1)<BR>page = 1;<BR>abs = (page - 1) * pageSize;<BR><BR>//currentPageRows=(page==totalPages?rowsCount-pageSize*(totalPages-1):pageSize);<BR>} else<BR>pageSize = rowsCount;<BR>if (abs == 0)<BR>resultSet.beforeFirst();<BR>else<BR>resultSet.absolute(abs);<BR>//int <BR>while (resultSet.next() &amp;&amp; ++currentPageRows &lt;= pageSize) {<BR>DynaBean bean = new BasicDynaBean(this);<BR>for (int i = 0; i &lt; properties.length; i++) {<BR>String name = properties[i].getName();<BR>bean.set(name, resultSet.getObject(name));<BR>}<BR>rows.add(bean);<BR>}<BR><BR>}<BR><BR>/**<BR>* &lt;p&gt;Introspect the metadata associated with our result set, and populate<BR>* the &lt;code&gt;properties&lt;/code&gt; and &lt;code&gt;propertiesMap&lt;/code&gt; instance<BR>* variables.&lt;/p&gt;<BR>*<BR>* @param resultSet The &lt;code&gt;resultSet&lt;/code&gt; whose metadata is to<BR>*&nbsp;&nbsp;be introspected<BR>*<BR>* @exception SQLException if an error is encountered processing the<BR>*&nbsp;&nbsp;result set metadata<BR>*/<BR>protected void introspect(ResultSet resultSet) throws SQLException {<BR><BR>// Accumulate an ordered list of DynaProperties<BR>ArrayList list = new ArrayList();<BR>ResultSetMetaData metadata = resultSet.getMetaData();<BR>int n = metadata.getColumnCount();<BR>for (int i = 1; i &lt;= n; i++) { // JDBC is one-relative!<BR>DynaProperty dynaProperty = createDynaProperty(metadata, i);<BR>if (dynaProperty != null) {<BR>list.add(dynaProperty);<BR>}<BR>}<BR><BR>// Convert this list into the internal data structures we need<BR>properties =<BR>(DynaProperty[]) list.toArray(new DynaProperty[list.size()]);<BR>for (int i = 0; i &lt; properties.length; i++) {<BR>propertiesMap.put(properties[i].getName(), properties[i]);<BR>}<BR><BR>}<BR><BR>/**<BR>* &lt;p&gt;Factory method to create a new DynaProperty for the given index<BR>* into the result set metadata.&lt;/p&gt;<BR>* <BR>* @param metadata is the result set metadata<BR>* @param i is the column index in the metadata<BR>* @return the newly created DynaProperty instance<BR>*/<BR>protected DynaProperty createDynaProperty(<BR>ResultSetMetaData metadata,<BR>int i)<BR>throws SQLException {<BR><BR>String name = null;<BR>if (lowerCase) {<BR>name = metadata.getColumnName(i).toLowerCase();<BR>} else {<BR>name = metadata.getColumnName(i);<BR>}<BR>String className = null;<BR>try {<BR>className = metadata.getColumnClassName(i);<BR>} catch (SQLException e) {<BR>// this is a patch for HsqlDb to ignore exceptions<BR>// thrown by its metadata implementation<BR>}<BR><BR>// Default to Object type if no class name could be retrieved<BR>// from the metadata<BR>Class clazz = Object.class;<BR>if (className != null) {<BR>clazz = loadClass(className);<BR>}<BR>return new DynaProperty(name, clazz);<BR><BR>}<BR><BR>/**<BR>* &lt;p&gt;Loads and returns the &lt;code&gt;Class&lt;/code&gt; of the given name.<BR>* By default, a load from the thread context class loader is attempted.<BR>* If there is no such class loader, the class loader used to load this<BR>* class will be utilized.&lt;/p&gt;<BR>*<BR>* @exception SQLException if an exception was thrown trying to load<BR>*&nbsp;&nbsp;the specified class<BR>*/<BR>protected Class loadClass(String className) throws SQLException {<BR><BR>try {<BR>ClassLoader cl = Thread.currentThread().getContextClassLoader();<BR>if (cl == null) {<BR>cl = this.getClass().getClassLoader();<BR>}<BR>return (cl.loadClass(className));<BR>} catch (Exception e) {<BR>throw new SQLException(<BR>"Cannot load column class '" + className + "': " + e);<BR>}<BR><BR>}<BR><BR>}<BR><BR>大部分代码从BeanUtils的源码中取得，只做了简单的修改，没有加多余的注释。如果要正式使用，<BR>需要再做精加工。<BR><BR>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝<BR>关于这个包，只准备测试到这里了，不过已经有了大概的印象了，至少，知道这个包可以做些什么。<BR>其实这个笔记也只是起到这个作用。@_@<BR></SPAN><!--结束正文--><img src ="http://www.blogjava.net/Victor/aggbug/21918.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-11-30 10:39 <a href="http://www.blogjava.net/Victor/articles/21918.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Jakarta-Common-BeanUtils研究心得(1)[转载]</title><link>http://www.blogjava.net/Victor/articles/21917.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Wed, 30 Nov 2005 02:38:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/21917.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/21917.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/21917.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/21917.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/21917.html</trackback:ping><description><![CDATA[作者: SonyMusic&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2003.05.13<BR><BR>一、概述<BR>第一次看到BeanUtils包，是在Struts项目中，作为Struts一个工具来使用的，<BR>估计功能越弄越强，就移到Common项目中了吧。<BR><BR>BeanUtils一共有四个package:<BR>org.apache.commons.beanutils<BR>org.apache.commons.beanutils.converters<BR>org.apache.commons.beanutils.locale<BR>org.apache.commons.beanutils.locale.converters<BR>后三个包主要是用于数据的转换，围绕着一个Converter接口，该接口只有一个方法：<BR>java.lang.Object convert(java.lang.Class type, java.lang.Object value) ，<BR>用于将一个value转换成另一个类型为type的Object。在一些自动化的应用中应该会有用。<BR>这里不作评论，以后有兴趣了，或者觉得有用了，再行研究。<BR>这里只讲第一个包。<BR><BR>二、测试用的Bean<BR>在开始所有的测试之前，我写了一个简单的Bean，以便于测试，代码如下：<BR>package test.jakarta.commons.beanutils;<BR><BR>/**<BR>&nbsp;* @author SonyMusic<BR>&nbsp;*<BR>&nbsp;*/<BR>public class Month {<BR>&nbsp;private int value;<BR>&nbsp;private String name;<BR>&nbsp;private int[] days={11,22,33,44,55};<BR><BR>&nbsp;public Month(int v, String n){<BR>&nbsp; &nbsp;value=v;<BR>&nbsp; &nbsp;name=n;<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;/**<BR>&nbsp; * Returns the name.<BR>&nbsp; * @return String<BR>&nbsp; */<BR>&nbsp;public String getName() {<BR>&nbsp; &nbsp;return name;<BR>&nbsp;}<BR><BR>&nbsp;/**<BR>&nbsp; * Returns the value.<BR>&nbsp; * @return int<BR>&nbsp; */<BR>&nbsp;public int getValue() {<BR>&nbsp; &nbsp;return value;<BR>&nbsp;}<BR><BR>&nbsp;/**<BR>&nbsp; * Sets the name.<BR>&nbsp; * @param name The name to set<BR>&nbsp; */<BR>&nbsp;public void setName(String name) {<BR>&nbsp; &nbsp;this.name = name;<BR>&nbsp;}<BR><BR>&nbsp;/**<BR>&nbsp; * Sets the value.<BR>&nbsp; * @param value The value to set<BR>&nbsp; */<BR>&nbsp;public void setValue(int value) {<BR>&nbsp; &nbsp;this.value = value;<BR>&nbsp;}<BR><BR>&nbsp;/**<BR>&nbsp; * @see java.lang.Object#toString()<BR>&nbsp; */<BR>&nbsp;public String toString() {<BR>&nbsp; &nbsp;return value+"("+name+")";<BR>&nbsp;}<BR><BR>&nbsp;public int[] getDays() {<BR>&nbsp; &nbsp;return days;<BR>&nbsp;}<BR><BR>&nbsp;public void setDays(int[] is) {<BR>&nbsp; &nbsp;days = is;<BR>&nbsp;}<BR><BR>}<BR><BR>三、BeanUtils<BR>这是一个主要应用于Bean的Util（呵呵，这个解释很绝吧），以下是其中几个方法的例子<BR><BR>//static java.util.Map describe(java.lang.Object bean) <BR>//这个方法返回一个Object中所有的可读属性，并将属性名/属性值放入一个Map中，另外还有<BR>//一个名为class的属性，属性值是Object的类名，事实上class是java.lang.Object的一个属性<BR>&nbsp;Month month=new Month(1, "Jan");<BR>&nbsp;<BR>&nbsp;try {<BR>&nbsp; &nbsp;Map map=BeanUtils.describe(month);<BR>&nbsp; &nbsp;Set keySet=map.keySet();<BR>&nbsp; &nbsp;for (Iterator iter = keySet.iterator(); iter.hasNext();) {<BR>&nbsp; &nbsp; &nbsp;Object element = (Object) iter.next();<BR>&nbsp; &nbsp; &nbsp;System.out.println("KeyClass:"+element.getClass().getName());<BR>&nbsp; &nbsp; &nbsp;System.out.println("ValueClass:"+map.get(element).getClass().getName());<BR>&nbsp; &nbsp; &nbsp;System.out.print(element+"\t");<BR>&nbsp; &nbsp; &nbsp;System.out.print(map.get(element));<BR>&nbsp; &nbsp; &nbsp;System.out.println();<BR>&nbsp; &nbsp;}<BR>&nbsp;} catch (IllegalAccessException e) {<BR>&nbsp; &nbsp;e.printStackTrace();<BR>&nbsp;} catch (InvocationTargetException e) {<BR>&nbsp; &nbsp;e.printStackTrace();<BR>&nbsp;} catch (NoSuchMethodException e) {<BR>&nbsp; &nbsp;e.printStackTrace();<BR>&nbsp;}<BR>输出为：<BR>KeyClass:java.lang.String<BR>ValueClass:java.lang.String<BR>value &nbsp;1<BR>KeyClass:java.lang.String<BR>ValueClass:java.lang.String<BR>class &nbsp;class test.jakarta.commons.beanutils.Month<BR>KeyClass:java.lang.String<BR>ValueClass:java.lang.String<BR>name &nbsp;Jan<BR>注意到所有Map中的key/value都是String，而不管object中实际的值是多少。<BR>与此对应的还有static void populate(java.lang.Object bean, java.util.Map properties)<BR>用于将刚才describe出的Map再装配成一个对象。<BR><BR><BR>再看这样一段代码<BR>曹晓钢也许还记得，为了取一个不确定对象的property，着实花了不少时间，<BR>难度不大，但要做到100%的正确，仍然需要付出很大的精力。<BR>//static java.lang.String getProperty(java.lang.Object bean, java.lang.String name) <BR>&nbsp;Month month=new Month(1, "Jan");<BR>&nbsp;<BR>&nbsp;try {<BR>&nbsp; &nbsp;System.out.println(BeanUtils.getProperty(month,"value"));<BR>&nbsp;} catch (Exception e) {<BR>&nbsp; &nbsp;e.printStackTrace();<BR>&nbsp;}<BR>//输出是：1<BR><BR>与getProperty类似的还有getIndexedProperty, getMappedProperty，<BR>以getIndexedProperty为例：<BR>&nbsp;Month month=new Month(1, "Jan");<BR>&nbsp;<BR>&nbsp;try {<BR>&nbsp; &nbsp;System.out.println(BeanUtils.getIndexedProperty(month,"days",1));<BR>&nbsp; &nbsp;System.out.println(BeanUtils.getIndexedProperty(month,"days[1]"));<BR>&nbsp;} catch (Exception e) {<BR>&nbsp; &nbsp;e.printStackTrace();<BR>&nbsp;}<BR>这两个调用是相同的。<BR><BR><BR>BeanUtils中还有一个方法：<BR>static void copyProperties(java.lang.Object dest, java.lang.Object orig) <BR>它真是太有用，还记得struts中满天飞的都是copyProperties，我甚至怀疑整个BeanUtils最初<BR>是不是因为这个方法的需求才写出来的。<BR>它将对象orig中的属性复制到dest中去。<BR><BR><BR>四、PropertyUtils<BR>这个类和BeanUtils类很多的方法在参数上都是相同的，但返回值不同。<BR>BeanUtils着重于"Bean"，返回值通常是String，而PropertyUtils着重于属性，<BR>它的返回值通常是Object。<BR><BR><BR>五、ConstructorUtils<BR>这个类中的方法主要分成两种，一种是得到构造方法，一种是创建对象。<BR>事实上多数时候得到构造方法的目的就是创建对象，这里只介绍一下创建对象。<BR>//static java.lang.Object ConstructorUtils.invokeConstructor<BR>//(java.lang.Class klass, java.lang.Object[] args) <BR>//根据一个java.lang.Class以及相应的构造方法的参数，创建一个对象。<BR>&nbsp;Object obj=ConstructorUtils.invokeConstructor(Month.class, {new Integer(1), "Jan"});<BR>&nbsp;Month month=(Month)obj;<BR>&nbsp;try {<BR>&nbsp; &nbsp;System.out.println(BeanUtils.getProperty(month,"value"));<BR>&nbsp;} catch (Exception e) {<BR>&nbsp; &nbsp;e.printStackTrace();<BR>&nbsp;}<BR>输出证明，构造方法的调用是成功的。<BR>如果需要强制指定构造方法的参数类型，可以这样调用：<BR>&nbsp; &nbsp;Object[] args={new Integer(1), "Jan"};<BR>&nbsp; &nbsp;Class[] argsType={int.class, String.class};<BR>&nbsp; &nbsp;Object obj;<BR>&nbsp; &nbsp;obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType);<BR>&nbsp; &nbsp;Month month=(Month)obj;<BR>&nbsp; &nbsp;System.out.println(BeanUtils.getProperty(month,"value"));<BR>argsType指定了参数的类型。<BR><img src ="http://www.blogjava.net/Victor/aggbug/21917.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-11-30 10:38 <a href="http://www.blogjava.net/Victor/articles/21917.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实施Jakarta Commons-DbUtils</title><link>http://www.blogjava.net/Victor/articles/21911.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Wed, 30 Nov 2005 02:18:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/21911.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/21911.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/21911.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/21911.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/21911.html</trackback:ping><description><![CDATA[<SPAN class=hui>作者： <FONT color=#880000>Builder.com</FONT></SPAN><BR><SPAN class=hui>Monday, April 26 2004 4:43 PM<BR><BR>
<P align=left>JDBC码是Java译码的一个部分，它给已写的编码带来了数量惊人的重复。另外，JDBC码几乎会经常性的带来一些低级错误。写出好的JDBC编码并不难，但是很痛苦。</P>
<TABLE cellSpacing=0 cellPadding=0 width=0 align=right border=0>
<TBODY>
<TR>
<TD align=middle><IMG src="http://www.zdnet.com.cn/ads/image/advertisement_e1.gif"></TD></TR>
<TR>
<TD><!--start banner ad--><!--ba-->
<SCRIPT language=JavaScript1.1 src="http://ad.cn.doubleclick.net/adj/messagingplus.zdnet.com.cn/developer/webdevelop;sz=1x1;ord=537335511?"> </SCRIPT>
<A href="http://ad.cn.doubleclick.net/click;h=v5|3340|0|0|%2a|x;44306;0-0;0;10768906;31-1|1;0|0|0;;~sscs=%3f" target=_top><IMG alt="Click here to find out more!" src="http://ad.cn.doubleclick.net/viewad/817-grey.gif" border=0></A> <NOSCRIPT><A href="http://ad.cn.doubleclick.net/jump/messagingplus.zdnet.com.cn/developer/webdevelop;sz=1x1;ord=537335511?"><IMG src="http://ad.cn.doubleclick.net/ad/messagingplus.zdnet.com.cn/developer/webdevelop;sz=1x1;ord=537335511?" border=0></A> </NOSCRIPT><!--end banner ad--></TD></TR></TBODY></TABLE>
<P><BR>DbUtils组件是一个精密而简单的组件，它并不做什么复杂的事而仅仅只是使很多的JDBC任务对开发者来说变得稍容易一点。尽管这时候很多持久框架和包都可以用来使数据持久变得更容易，然而JDBC仍然是大多数Java和Java2企业版(J2EE)开发者赖以生存的工具。因此，任何能让使用JDBC工作更容易的东西都是好消息。</P>
<P align=left>&nbsp;</P>
<P align=left>DbUtils可以<A href="http://downloads.builder.com/abstract.aspx?docid=83569">免费下载</A></SPAN>，它不依赖于任何其它的通用组件而只是依赖下面这些：</P>
<UL type=disc>
<LI>Java Development Kit (JDK) 1.2 (or later) 
<LI>JDBC 2.0 (or later) </LI></UL>
<P align=left><BR>&nbsp;DbUtils文档并不是最好的，但是足以使你的工作正常进行。在下一节，你会看到DbUtils中最有用的类以及一些关于它们的用法的例子。你应该能够很容易地使用这些编码和例子，然后能够马上在你自己的项目中开始使用DbUtils。我将会集中精力于两个类（org.apache.commons.dbutils.DbUtils&nbsp; 和org.apache.commons.dbutils.QueryRunner）和一个接口（org.apache.commons.dbutils.ResultSethandler）.在我给你们一些关于它们的用法的例子之前，让我们深入DbUtils里面来仔细看看它给我们提供了些什么。</P>
<H5>DbUtils </H5>DbUtils是一个为做一些诸如关闭连接、装载JDBC驱动程序之类的常规工作提供有用方法的类，它里面所有的方法都是静态的。<BR>
<P align=left>这个类里的重要方法有：</P>
<UL type=disc>
<LI><B>close</B>：DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL，如果不是的话，它们就关闭连接、声明和结果集（ResultSet）。 
<LI><B>CloseQuietly</B>: CloseQuietly这一方法不仅能在连接、声明或者结果集（ResultSet）为NULL情况下避免关闭，还能隐藏一些在程序中抛出的SQLEeception。如果你不想捕捉这些异常的话，这对你是非常有用的。在重载CloseQuietly方法时，特别有用的一个方法是closeQuietly(Connection conn,Statement stmt,ResultSet rs)，这是因为在大多数情况下，连接、声明和结果集（ResultSet）是你要用的三样东西，而且在最后的块你必须关闭它们。使用这一方法，你最后的块就可以只需要调用这一方法即可。 
<LI><B>CommitAndCloseQuietly(Connection conn)</B>：这一方法用来提交连接，然后关闭连接，并且在关闭连接时不向上抛出在关闭时发生的一些SQL异常。 
<LI><B>LoadDriver(String driveClassName): </B>这一方法装载并注册JDBC驱动程序，如果成功就返回TRUE。使用这种方法，你不需要去捕捉这个异常ClassNotFoundException。使用loadDrive方法，编码就变得更容易理解，你也就得到了一个很好的Boolean返回值，这个返回值会告诉你驱动类是不是已经加载成功了。 </LI></UL>
<H5>ResultSetHandler </H5>
<TABLE cellSpacing=0 cellPadding=0 width=0 align=right border=0>
<TBODY>
<TR>
<TD align=middle><IMG src="http://www.zdnet.com.cn/ads/image/advertisement_e1.gif"></TD></TR>
<TR>
<TD><!--start banner ad--><!--ba-->
<SCRIPT language=JavaScript1.1 src="http://ad.cn.doubleclick.net/adj/messagingplus.zdnet.com.cn/developer/webdevelop;sz=1x1;ord=1460273263?"> </SCRIPT>
<A href="http://ad.cn.doubleclick.net/click;h=v5|3340|0|0|%2a|x;44306;0-0;0;10768906;31-1|1;0|0|0;;~sscs=%3f" target=_top><IMG alt="Click here to find out more!" src="http://ad.cn.doubleclick.net/viewad/817-grey.gif" border=0></A> <NOSCRIPT><A href="http://ad.cn.doubleclick.net/jump/messagingplus.zdnet.com.cn/developer/webdevelop;sz=1x1;ord=1460273263?"><IMG src="http://ad.cn.doubleclick.net/ad/messagingplus.zdnet.com.cn/developer/webdevelop;sz=1x1;ord=1460273263?" border=0></A> </NOSCRIPT><!--end banner ad--></TD></TR></TBODY></TABLE>
<P>正如它的名字所提示的，这一接口执行处理一个jaca.sql.ResultSet，将数据转变并处理为任何一种形式，这样有益于其应用而且使用起来更容易。这一组件提供了ArrayHandler, ArrayListHandler, BeanHandler, BeanListHandler, MapHandler, MapListHandler, and ScalarHandler等执行程序。<BR>&nbsp;</P>
<P>ResultSetHandler接口提供了一个单独的方法：<I>Object handle</I> (java.sql.ResultSet .rs)。因此任何ResultSetHandler 的执行需要一个结果集（ResultSet）作为参数传入，然后才能处理这个结果集，再返回一个对象。因为返回类型是java.lang.Object，所以除了不能返回一个原始的Java类型之外，其它的返回类型并没有什么限制。如果你发现这七个执行程序中没有任何一个提供了你想要的服务，你可以自己写执行程序并使用它。</P>
<H5>QreryRunner </H5>
<P align=left>这个类使执行SQL查询简单化了，它与ResultSetHandler串联在一起有效地履行着一些平常的任务，它能够大大减少你所要写的编码。QueryRunner类提供了两个构造器：其中一个是一个空构造器，另一个则拿一个 javax.sql.DataSource 来作为参数。因此，在你不用为一个方法提供一个数据库连接来作为参数的情况下，提供给构造器的数据源(DataSource) 被用来获得一个新的连接并将继续进行下去。</P>
<P align=left>&nbsp;</P>
<P align=left>这一类中的重要方法包括以下这些：</P>
<UL type=disc>
<LI><B>query(Connection conn, String sql, Object[] params, ResultSetHandler rsh)</B>:这一方法执行一个选择查询，在这个查询中，对象阵列的值被用来作为查询的置换参数。这一方法内在地处理PreparedStatement 和ResultSet&nbsp; 的创建和关闭。ResultSetHandler对把从 ResultSet得来的数据转变成一个更容易的或是应用程序特定的格式来使用。 
<LI><B>query(String sql, Object[] params, ResultSetHandler rsh)</B>:这几乎与第一种方法一样；唯一的不同在于它不将数据库连接提供给方法，并且它是从提供给构造器的数据源(DataSource) 或使用的setDAtaSource 方法中重新获得的。 
<LI><B>query(Connection conn, String sql, ResultSetHandler rsh)</B>:这执行一个不要参数的选择查询。 
<LI><B>update(Connection conn, String sql, Object[] params)</B>:这一方法被用来执行一个插入、更新或删除操作。对象阵列为声明保存着置换参数。 </LI></UL>
<P align=left>&nbsp;</P>
<P align=left>现在让我们来看一个例子，在这里你可以从一个数据库中获得一些数据。比如说，我正在使用<A href="http://downloads.builder.com/abstract.aspx?docid=83570">MySQL 数据库</A>.</SPAN>你还需要下载<A href="http://downloads.builder.com/abstract.aspx?docid=83571">MYSQL JDBC</A></SPAN>驱动程序。我正在使用的MySQL数据库在本地主机，端口号为3306上运行。这个数据库地名字叫做test。你将要用到的Student表的结构如下：<BR>Columns&nbsp;&nbsp;&nbsp; Type<BR>-------&nbsp;&nbsp;&nbsp; ----<BR>StudId &nbsp;&nbsp;&nbsp; int<BR>Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; varchar<BR><BR>在列表A中，你将会从Student表中得到一些信息，而且你可以按照你自己的额外需要修改这些信息。尽管你在使用JDBC，但要注意你几乎没写JDBC编码。(你可能要改变在例子中所规定的用户名和密码，这是以你的具体的数据库配置为基础的。)<BR></P>
<P>这个编码遵从以下步骤：</P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=0 width=0 align=right border=0>
<TBODY>
<TR>
<TD align=middle><IMG src="http://www.zdnet.com.cn/ads/image/advertisement_e1.gif"></TD></TR>
<TR>
<TD><!--start banner ad--><!--ba-->
<SCRIPT language=JavaScript1.1 src="http://ad.cn.doubleclick.net/adj/messagingplus.zdnet.com.cn/developer/webdevelop;sz=1x1;ord=278468657?"> </SCRIPT>
<A href="http://ad.cn.doubleclick.net/click;h=v5|3340|0|0|%2a|x;44306;0-0;0;10768906;31-1|1;0|0|0;;~sscs=%3f" target=_top><IMG alt="Click here to find out more!" src="http://ad.cn.doubleclick.net/viewad/817-grey.gif" border=0></A> <NOSCRIPT><A href="http://ad.cn.doubleclick.net/jump/messagingplus.zdnet.com.cn/developer/webdevelop;sz=1x1;ord=278468657?"><IMG src="http://ad.cn.doubleclick.net/ad/messagingplus.zdnet.com.cn/developer/webdevelop;sz=1x1;ord=278468657?" border=0></A> </NOSCRIPT><!--end banner ad--></TD></TR></TBODY></TABLE></P>
<P>1．加载JDBC驱动程序类，并用DriverManager来得到一个数据库连接。</P>
<P align=left>2．例示 QueryRunner 类。</P>
<P align=left>3．使用连接、SQL查询、参数和ResultSetHandler来作为输入的查询方法。你使用一个类org.apache.commons.dbutils.handlers.MapListHandler，一个类 MapListHandler来获得一个结果集（ResultSet）并返回一个jaca.util.Map的实例java.util.List。因此结果集（ResultSet） 的每一行都变成了一个java.util.Map,所有这些java.util.Map的实例绑在一起放在一个java.util.List 中。</P>
<P align=left>4. 反复得到列表(List)的值就是通过在列表(List)中获得每一个Map的值。</P>
<P align=left>5.用QueryRunner 来执行一个没有参数的方法。在这里你要用BeanListHandler ，它是一个非常有用的ResultSetHandler ，因为你可以把ResultSet 转变成一个指定的Bean的列表中。这时你可以指定一个Bean类到Bean StudentBean中，如同在<A href="http://builder.com.com/Listing%20B#Listing%20B">列表B</A></SPAN>中所显示的那样。</P>
<P align=left>6.　你通过反复从列表（List）中得到多个bean，然后就可从每一个StudentBean实例中获取值。</P>
<P align=left>&nbsp;</P>
<P align=left>注释：在列表B中，StudentBean 类中的StudId 必须是int，这是因为表Student的StudId列的类型是int。坚持这个类型的匹配是我们需要遵从的唯一规则。<BR>&nbsp;</P>
<P align=left>因为在这种情况下，StudentBean 类的属性和表Student 的字段是完好的对映着的，只要将StuentBean 类作为一个参数就是一个技巧。字段值用和字段名一样的名字插入到类的属性中。然而，如果你想要更多地控制bean的创建，则类BeanListHandler提供了第二个构造器：BeanListHandler(java.lang.Class type, RowProcessor convert). 接口Rowprocessor的执行把结果集（ResultSet）的各行转化成一个对象组。在 StudentBean这一案例中，RowProcessor中的BasicRowProcessor 的执行被利用上了，它能够执行这项任务。然而，你可以写一个新的执行并把它提供给BeanListHandler的构造器。<BR>&nbsp;</P>
<P align=left>当然，执行这一编码的输出取决于你从表Student中获得哪些数据。对我来说，我得到了以下这些输出：<BR>***Using MapListHandler***<BR>&nbsp; Id &gt;&gt;1<BR>&nbsp; Name &gt;&gt;One<BR>&nbsp; Id &gt;&gt;2<BR>&nbsp; Name &gt;&gt;Two<BR>***Using BeanListHandler***<BR>&nbsp; Id &gt;&gt;1<BR>&nbsp; Name &gt;&gt;One<BR>&nbsp; Id &gt;&gt;2<BR>&nbsp; Name &gt;&gt;Two<BR>&nbsp; Id &gt;&gt;3<BR>&nbsp; Name &gt;&gt;Three<BR><BR>除了到目前为止你已经看了的类以外，另外一些你需要研究的类是：</P>
<UL type=disc>
<LI><B>org.apache.commons.dbutils.QueryLoader</B>:QueryLoader是一个从一个文件加载查询到一个Map的简单的类。然后，当需要的时候，你从 Map 中选择一些查询。在没有专门去接触代码的情况下，一个文件中的Having查询也可以改变得尽可能的简单。 
<LI><B>org.apache.commons.dbutils.wrappers.SqlNullCheckedResultSet</B>:这个类对使用一个系统方法来解决NULL值问题是很有用的。用一个 SqINullCheckedResultSet 的实例来限制一个常规的结果集（ResultSet） ，然后详细地说明在遇NULL值的情况下应该做些什么。 
<LI><B>org.apache.commons.dbutils.wrappers.StringTrimmedResultSet</B>:用类StringTrimmedResultSet 来约束一个结果集，这样一来，你就可以修整所有getString()和getObject()方法返回的字符串。 </LI></UL>
<P>&nbsp;</P>
<P>DbUtils 组件很好也很小巧，很值得在所有用到JDBC的项目中去使用。<BR><BR>DBUtils的JUnit例子：<BR></P>
<P>//使用dbutils1.0版本</P>
<P>import java.util.*;<BR>import java.util.logging.*;<BR>import java.sql.*;<BR>import org.apache.commons.dbutils.*;<BR>import org.apache.commons.dbutils.handlers.*;</P>
<P>public class TestDBUnits {<BR>&nbsp;<BR>&nbsp;public static void main(String[]args) throws Exception {<BR>&nbsp;&nbsp;TestDBUnits test = new TestDBUnits();<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;for(int i = 0 ; i &lt; 1 ; i++) {&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;test.testQuery1();<BR>&nbsp;&nbsp;&nbsp;test.testQuery2();<BR>&nbsp;&nbsp;&nbsp;test.testUpdate();<BR>&nbsp;&nbsp;}<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;public void testQuery1(){<BR>&nbsp;&nbsp;try {<BR>&nbsp;&nbsp;&nbsp;QueryRunner qr = new QueryRunner() ;<BR>&nbsp;&nbsp;&nbsp;ResultSetHandler rsh = new ArrayListHandler();&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;String strsql = "select * from test1";&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;ArrayList result = (ArrayList)qr.query(getConnection() ,strsql ,rsh);<BR>&nbsp;&nbsp;&nbsp;//System.out.print("");<BR>&nbsp;&nbsp;} catch(Exception ex) {<BR>&nbsp;&nbsp;&nbsp;ex.printStackTrace(System.out);<BR>&nbsp;&nbsp;}<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;public void testQuery2(){<BR>&nbsp;&nbsp;try {<BR>&nbsp;&nbsp;&nbsp;QueryRunner qr = new QueryRunner() ;<BR>&nbsp;&nbsp;&nbsp;ResultSetHandler rsh = new MapListHandler();&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;String strsql = "select * from test1";&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;ArrayList result = (ArrayList)qr.query(getConnection() ,strsql ,rsh);<BR>&nbsp;&nbsp;&nbsp;for(int i = 0 ; i &lt; result.size() ; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;Map map = (Map)result.get(i);<BR>&nbsp;&nbsp;&nbsp;&nbsp;//System.out.println(map);&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;//System.out.print("");<BR>&nbsp;&nbsp;} catch(Exception ex) {<BR>&nbsp;&nbsp;&nbsp;ex.printStackTrace(System.out);<BR>&nbsp;&nbsp;}<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;public void testUpdate(){<BR>&nbsp;&nbsp;try {<BR>&nbsp;&nbsp;&nbsp;QueryRunner qr = new QueryRunner() ;<BR>&nbsp;&nbsp;&nbsp;ResultSetHandler rsh = new ArrayListHandler();<BR>&nbsp;&nbsp;&nbsp;String strsql = "insert test1(page ,writable ,content)values('ttt','ttt','faskldfjklasdjklfjasdklj')";<BR>&nbsp;&nbsp;&nbsp;qr.update(getConnection() ,strsql);<BR>&nbsp;&nbsp;&nbsp;//System.out.print("");<BR>&nbsp;&nbsp;} catch(Exception ex) {<BR>&nbsp;&nbsp;&nbsp;ex.printStackTrace(System.out);<BR>&nbsp;&nbsp;}<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;private&nbsp; Connection getConnection() throws InstantiationException,<BR>&nbsp;&nbsp;&nbsp;IllegalAccessException, ClassNotFoundException, SQLException {<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;String strDriver = "org.gjt.mm.mysql.Driver";<BR>&nbsp;&nbsp;String strUrl = "jdbc:mysql://localhost:3306/test";<BR>&nbsp;&nbsp;String strUser = "root";<BR>&nbsp;&nbsp;String strPass = "";<BR>&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;Class.forName(strDriver).newInstance();&nbsp;&nbsp;<BR>&nbsp;&nbsp;return DriverManager.getConnection(strUrl, strUser, strPass);<BR>&nbsp;}<BR>}<BR></P></SPAN><img src ="http://www.blogjava.net/Victor/aggbug/21911.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-11-30 10:18 <a href="http://www.blogjava.net/Victor/articles/21911.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现图形JSF组件</title><link>http://www.blogjava.net/Victor/articles/21857.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Tue, 29 Nov 2005 10:11:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/21857.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/21857.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/21857.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/21857.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/21857.html</trackback:ping><description><![CDATA[<STRONG>--很简单地构建一个纯HTML无法轻松实现的图形Web应用程序组件<BR><BR></STRONG>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD height=64>时间：2005-07-07<BR>作者：<A href="http://dev2dev.bea.com.cn/author/350.html">Marc Durocher</A><BR>浏览次数：
<SCRIPT language=JavaScript src="http://203.81.25.103/cgi-bin/beadevcount.cgi?d_id=484" type=text/JavaScript></SCRIPT>
 2464 <BR>本文关键字：<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=JSF">JSF</A></TD>
<TD>
<TABLE class=box_content cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD><SPAN class=h2b>文章工具</SPAN><BR><IMG height=10 alt=推荐给朋友 src="http://dev2dev.bea.com.cn/images/letter001.gif" width=19 align=absMiddle>&nbsp;<A href="javascript:sendmail()">推荐给朋友</A><BR><IMG height=18 alt=打印文章 src="http://dev2dev.bea.com.cn/images/print001.gif" width=19 align=absMiddle>&nbsp;<A href="javascript:window.print()">打印文章</A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><!-- 提取技术文章 -->
<DIV class=beas><IMG height=1 alt="" src="http://dev2dev.bea.com.cn/images/dot6B6B6B.gif" width="100%"></DIV>
<P>　　开发人员认为，如果有合适的工具来创建交互式Web界面，他们就能将时间集中在核心需求和定制上，并在规定时间内及时得交付应用程序。与其他技术如JavaServer Pages或Apache Struts 相比，JavaServer Faces (JSF)技术为创建交互式Web应用程序带来了很多便利。JSF在程序逻辑和GUI表示之间划出一条清晰的界限，提高了对Web程序的维护能力，并为Web用户界面组件的开发和重用提供了一个框架。</P>
<P>　　如今，许多Web应用程序开发人员都在转而使用JSF，但是他们发现，预先定制的JSF UI组件受到基本DHTML窗口部件的限制。监管或业务流程监控之类的高级应用程序需要能与JSF框架兼容的高级可视化组件。JSF框架的标准化使它易于开发能够重用的自定义Web GUI组件。另外，Web组件开发商现在能提供更复杂的组件，并承诺Web应用程序开发人员能够轻松地使用这些组件。此类JSF用户界面组件必须集成并部署到JSF运行时框架中去，并在其中完全展开，还必须在设计时很好地集成到提供JSF支持的IDE中去。</P>
<P>　　尽管JSF带来了标准用户界面框架，但对于开发第一个自定义JSF组件而言，还是存在几个缺陷和漏洞。让我们看看怎样创建一个纯HTML无法轻松创建的图形JSF组件。图形JSF组件的特点不仅要求生成DHTML，而且还需要对图像生成和客户端交互提供补充支持。我们将以一个图形组件的例子来阐述这些特点。该图形组件能够提供曲线图，并为各种客户端导航和交互提供便利。我们还会了解到将该图形组件集成到JSF-enabled IDE中所需要的步骤。通过理解图形组件的设计方法，您将会更好地理解如何实现JSF组件，而这应该能使您开发出定制的JSF图形组件。</P>
<P><STRONG>什么是JSF？</STRONG></P>
<P>　　JSF是一种能够简化Web应用程序表示层结构的标准服务器端框架。定义JSF框架的JSR 127（参见参考资料）带有一个能提供基本UI组件（如输入栏和按纽）的参考实现。您可以将可重用用户界面组件集中起来创建Web页，将这些组件绑定到应用数据源上，并用服务器端事件控制程序处理客户端事件。根据说明书介绍，组件供应商能编写与JSF运行时框架集成的组件，并将其集成到在设计时与JSF兼容的IDE中去。</P>
<P>　　从很大程度上讲，JSF组件同在HTML 2.0技术要求下可用的HTML组件和标签直接相符合。对许多Web应用程序而言，这套相对简单的组件是够用的。然而，许多应用程序如监管或监控程序需要更复杂的数据显示与交互，比如制表、制图和映射。由于JSF组件在HTML中直接提交复杂图形小部件的能力有限，所以设计这些高级组件的能力并不突出。解决方案要求服务器端组件向客户传输图像，却会给自身带来问题，因为在基本HTML图像上进行交互要受到限制。最后，使用JavaScript时，必须能调用客户端交互来使用户能对数据进行导航和交互。</P>
<P>　　让我们看看开发一个简单的、将CSS输入HTML页面的JSF组件需要哪些步骤。当开发高级JSF图形组件时，这一简单组件的描述和代码样本会作为背景。图1显示了如何使用即将开发的组件，并显示将要得到的结果。使用这种组件的好处是能够通过改变某个JSF动作的组件值，来改变整个页面的外观。 </P>
<P><IMG height=239 src="http://dev2dev.bea.com.cn/images/wlworkshop/images05070604_01.jpg" width=300> </P>
<P>图1：显示了我们如何使用一个非常简单的JSF组件将CSS输入某个HTML页面并得出结果。</P>
<P><STRONG>开发组件</STRONG></P>
<P>　　JSF组件包含若干个Java类和配置文件。为创建一个自定义JSF组件，您需要开发一个扩展JSF基本组件类的Java类；为默认呈现软件包开发呈现程序；开发一个将在JSP页面中用于描述标签的Java类；编写一个标签库定义（TLD）文件；编写JSF配置文件。让我们更深入地了解这5个步骤。</P>
<P>　　开发组件Java类。组件类负责管理代表组件状态的属性。因此，我们必须根据组件的行为（如输入组件或输出组件），给组件选择适当的基类（参见清单1）。这里描述的组件可进行扩展javax.faces.component.UIOutput，以显示指向某个样式表文件的URL，或内联式样式表的内容。该组件可用于在JSF动作中将某个样式表转换成另一个样式表。关联属性规定着值的类型：要么是一个URL，要么是内联样式。该组件还必须能够在向服务器发送请求期间，使用经过JSF框架处理的对象，来存储并修复自己的状态。组件的状态由重建对象所需的重要属性值组成。JSF框架自动调用saveState()和restoreState()方法，我们可以在组件中实现这两种方法来达到这一目标。</P>
<P align=left><STRONG>清单</STRONG><STRONG>1. </STRONG>组件类管理显示组件状态的属性。可依据组件的行为，为其选择一个适当的基类。在本例中，该组件扩展javax.faces.component.UIOutput，以显示指向某个样式表文件的URL，或者某个内联式样式表的内容。<STRONG></STRONG></P><PRE class=code>import javax.faces.component.*;
public class CSSComponent extends UIOutput {
	private Boolean link; 
	public String getFamily() { 
		return "faces.CSSFamily"; 
	} 
	public boolean isLink() { 
		if (link != null) 
			return link.booleanValue(); 
			ValueBinding vb = getValueBinding("link"); 
	if (vb != null) { 
		Boolean bvb = (Boolean) vb.getValue(
			FacesContext.getCurrentInstance()); 
		if (bvb != null) 
			return bvb.booleanValue(); 
	} 
	return false; 
	} 
	public void setLink(boolean link) { 
		this.link = new Boolean(link); 
	} 
	public Object saveState(FacesContext context) { 
		return new Object[] { super.saveState(context), 
			link }; 
	} 
	public void restoreState(FacesContext context, 
	Object stateObj) { 
		Object[] state = (Object[]) stateObj; 
		super.restoreState(context, state[0]); 
		link = (Boolean) state[1]; 
	} 
}
</PRE>
<P><STRONG>开发呈现程序。</STRONG>呈现程序有两个作用。第一，呈现程序负责发送适当的HTML程序段，该程序段能在客户端中呈现组件。通常情况下，这个HTML程序段由一些适于呈现整个Web浏览器的HTML标签组成。此JSF生存周期称作编码阶段或呈现—响应阶段。该响应阶段还能发送增强客户端交互性的JavaScript代码。</P>
<P>　　呈现程序的第二个作用是解析来自客户端的数据，从而对服务器端的组件状态进行更新（如用户在文本字段输入的文本）。标准呈现程序软件包具有强制性，但也可以提供其他呈现程序软件包，用于提供可替换的客户端表示法或SVG之类的语言（参见参考资料）。通过检验组件的连接属性，您实现的呈现程序（参见清单2）将选择在HTML页面中发送的CSS样式。</P>
<P align=left><STRONG>清单</STRONG><STRONG>2. </STRONG>标准呈现程序软件包具有强制性，但是，您可以使用其他呈现程序软件包，来提供可替换的客户端表示法或语言。通过检验组件的连接属性，您实现的呈现程序将选择在HTML页面中发出的CSS样式。</P><PRE class=code>import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
public class CSSRenderer extends Renderer {
public void encodeEnd(FacesContext context,
UIComponent component)
	throws IOException {
		super.encodeEnd(context, component);
		if (component instanceof CSSComponent) {
			CSSComponent cssComponent  =
				(CSSComponent) component;
			String css = (String)cssComponent.getValue();
			boolean isLink = cssComponent.isLink();
			if (css != null) 
				if (isLink)  
				context.getResponseWriter().write(
				"&lt;link type='text/css' rel='stylesheet' 
				href='" + css + "'/&gt;");
			else 
				context.getResponseWriter().write(
					"&lt;style&gt;;\n" + css + "\n&lt;style/&gt;\n"); 
		}
	}
}
</PRE>
<P><STRONG>开发标签类。</STRONG>同样，JSF框架提供了用于扩展的基类，来编写与组件相关的标签。该标签类负责定义并呈现将在faces-config.xml文件中应用的组件样式（这种样式的描述很简短）。它还负责创建JSF组件（由JSF框架来处理），传递JSF标签中所包含的属性，该属性用于初始化组件（参见清单3）。</P>
<P align=left><STRONG>清单</STRONG><STRONG>3. </STRONG>该标签类定义了将在faces-config.xml文件中应用的组件的样式和组件呈现方式。</P><PRE class=code>import javax.faces.webapp.UIComponentTag;
public class CSSTag 
	extends UIComponentTag {
	private String value;
	private String link;
	public String getComponentType() {
		return "faces.CSSComponent";
	}
	public String getRendererType() {
		return “HTML.LinkOrInlineRenderer";
	}
	protected void setProperties(UIComponent component) {
		super.setProperties(component);
		Application app = 
			getFacesContext().getApplication();
		if (value != null)
			if (isValueReference(value)) 
				component.setValueBinding("value", 
				app.createValueBinding(value));
			else
				component.getAttributes().put("value", value);
		if (link != null) 
			if (isValueReference(link))
				component.setValueBinding("link",
				app.createValueBinding(link));
			else
				component.getAttributes().put("link",
				new Boolean(link));
	}
	public String getLink() {
		return link;
	}
	public void setLink(String link) {
		this.link = link;
	}
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
}
</PRE>
<P>　　该标签提供setter和getter来管理链接和值属性。组件一旦创建，便会调用setProperties()方法，对标签属性进行初始化。每个标签属性都无外乎两种：要么是文字值，要么是bean属性的一个绑定。</P>
<P><STRONG>编写标签库定义（TLD</STRONG><STRONG>）。</STRONG>TLD是一个XML文件，它通过将标签名与相应的Java类相关联来描述标签。TLD还描述了标签所允许的属性（参见清单4）。这个TLD定义了一个名为css的标签，该标签绑定到CSSTag类。它还声明了链接和值标签属性。</P>
<P align=left><STRONG>清单</STRONG><STRONG>4. </STRONG>TLD是一个通过将标签名与相应的Java类相关联来描述标签的XML文件。TLD定义了名为css的标签，使其与CSSTag类绑定。它还声明了链接和值标签属性。</P><PRE class=code>&lt;?xml version="1.0" encoding="UTF-8"?&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;custom&lt;/short-name&gt;
	&lt;uri&gt;http://www.ilog.com/jviews/tlds/css.tld&lt;/uri&gt;
	&lt;description&gt;This tag library contains a tag for a 
		sample custom JSF Component.&lt;/description&gt;
	&lt;tag&gt;
		&lt;name&gt;css&lt;/name&gt;
		&lt;tag-class&gt;path.to.CSSTag&lt;/tag-class&gt;
		&lt;description&gt;A component that displays the style 
			inline or a link a to a css file&lt;/description&gt;
		&lt;attribute&gt;
			&lt;name&gt;id&lt;/name&gt;
			&lt;required&gt;false&lt;/required&gt;
			&lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;
			&lt;type&gt;java.lang.String&lt;/type&gt;
			&lt;description&gt;The id of this component.
			&lt;/description&gt;
		&lt;/attribute&gt;
		&lt;attribute&gt;
			&lt;name&gt;binding&lt;/name&gt;
			&lt;required&gt;false&lt;/required&gt;
			&lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;
			&lt;type&gt;java.lang.String&lt;/type&gt;
			&lt;description&gt;The value binding expression 
				linking this component to a property in a
				backing bean. If this attribute is set, the 
				tag does not create the component itself but
				retrieves it from the bean property. This
				attribute must be a value binding.
			&lt;/description&gt;
		&lt;/attribute&gt;
		&lt;attribute&gt;
			&lt;name&gt;value&lt;/name&gt;
			&lt;required&gt;true&lt;/required&gt;
			&lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;
			&lt;type&gt;java.lang.String&lt;/type&gt;
			&lt;description&gt;The inline css text or the url to 
				the css file to link.&lt;/description&gt;
		&lt;/attribute&gt;
		&lt;attribute&gt;
			&lt;name&gt;link&lt;/name&gt;
			&lt;required&gt;false&lt;/required&gt;
			&lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;
			&lt;type&gt;java.lang.String&lt;/type&gt;
			&lt;description&gt;Whether the value is a link or 
				the inline style.&lt;/description&gt;
		&lt;/attribute&gt;
	&lt;/tag&gt;
&lt;/taglib&gt;
</PRE>
<P><STRONG>编写JSF</STRONG><STRONG>配置文件。</STRONG>为了将某个JSF组件集成到框架中，您必须提供一个名为faces-config.xml的配置文件。该文件将组件类型和呈现程序类型（用于JSP定制标签处理程序）与对应的Java类关联起来。它还能描述与每个组件一同使用的呈现程序（参见清单5）。该文件定义了faces.CSSFamily组件家族。在本例中，该家族由faces.CSSComponent这一个组件类型（该类型与CSSComponent类绑定）组成。最后，HTML.LinkOrInlineRenderer类型的呈现程序（由CSSComponent类实现）要与faces.CSSFamily家族相关联。</P><STRONG>清单5. </STRONG>该文件将组件类型和呈现程序类型与对应的Java类联系起来，并描述与每个组件一同使用的呈现程序。它还定义了faces.CSSFamily组件家族。 <PRE class=code>&lt;!DOCTYPE faces-config PUBLIC 
	"-//Sun Microsystems, Inc.//
	DTD JavaServer Faces Config 1.0//EN" 
	"http://java.sun.com/dtd/web-facesconfig_1_0.dtd"&gt;
&lt;faces-config&gt;
	&lt;component&gt;
		&lt;component-type&gt;faces.CSSComponent
		&lt;/component-type&gt;
		&lt;component-class&gt;path.to.CSSComponent
		&lt;/component-class&gt;
		&lt;component-extension&gt;
			&lt;component-family&gt;faces.CSSFamily
			&lt;/component-family&gt;
			&lt;renderer-type&gt;HTML.LinkOrInlineRenderer
			&lt;/renderer-type&gt;
		&lt;/component-extension&gt;
	&lt;/component&gt;
	&lt;render-kit&gt;
		&lt;renderer&gt;
			&lt;component-family&gt;faces.CSSFamily
			&lt;/component-family&gt;
			&lt;renderer-type&gt; HTML.LinkOrInlineRenderer 
			&lt;/renderer-type&gt;
			&lt;renderer-class&gt;path.to.CSSRenderer
			&lt;/renderer-class&gt;
		&lt;/renderer&gt;
	/render-kit&gt;
&lt;/faces-config&gt;
</PRE>
<P><STRONG>开始制图 </STRONG></P>
<P>　　如果您希望将自己的组件集成到JSF-enabled IDE中，您还可以提供补充说明。比如说，除提供其他的设计时信息外，还可以提供一个名为sun-faces-config.xml的XML配置文件，用于描述应在IDE中公开的组件属性。 </P>
<P>　　既然已经看到如何创建一个简单的JSF组件，不妨再来看看怎样创建一个图形JSF组件。我们将遵循同样的基本步骤来设计一个高级JSF图形组件。让我们以一个图形组件（如ILOG JSF图形组件）为例，通过一组分类，该组件为数据值分布提供了可视化表示。该图形能够以条型统计图、圆形分格统计图和气泡式统计图等各种显示方法来显示数据集合。该JSF图形组件有两个初始设计限制：</P>
<P>　　我们已经拥有Java图形bean组件，它具备所有图形显示能力。该组件可以显示很多图形，而且可定制性很高。在理想情况下，我们希望利用bean组件，使用它的功能来构成我们的JSF组件的基础。 </P>
<P>　　普通JSF应用程序需要重新载入整个页面以更新视图。这种方法适合基于表单的应用程序，但在很多情况下却不适用于高度图形化的用户界面。因此，我们的JSF图形组件必须能在不更新整个页面的前提下处理某些简单的导航，以提供更好的用户体验。 </P>
<P>　　以下是满足这些需求的解决方案：该JSF图形组件将管理图形bean组件，包括创建图形bean、定制该bean以及使该bean可用于服务器端操作。呈现JSF组件将分为两个阶段完成。JSF呈现程序会产生一个&lt;img&gt;标签和一套JavaScript对象（参见图2）。客户端将请求服务器发回一张图像。这一请求由某个servlet完成，该servlet获得图形bean，并利用图形提供的方法生成一幅图像（参见图3）。任何只改变该图形外观的进一步用户交互（放大、扫视、更改样式表等）都会引起图形的一次增量更新。如果客户端不只是要求对图形图像进行更新，那么将提交该页面（参见图4）。</P>
<P><IMG height=150 src="http://dev2dev.bea.com.cn/images/wlworkshop/images05070604_02.jpg" width=300> </P>
<P>　　图2JSF图形组件管理图形bean组件，包括创建图形bean、对其进行定制，并使其可用于服务器端动作。JSF呈现程序生成一个&lt;img&gt;标签和一套JavaScript对象。</P>
<P><IMG height=168 src="http://dev2dev.bea.com.cn/images/wlworkshop/images05070604_03.jpg" width=336> </P>
<P>　　图3 客户机通过servlet要求服务器获得一张图像。该servlet获得图形bean，并通过由图形提供的方法生成一幅图像。 </P>
<P><IMG height=150 src="http://dev2dev.bea.com.cn/images/wlworkshop/images05070604_04.jpg" width=300> </P>
<P>　　图4如果客户端不只是要求对图形外观的进行更新，那么页面将被提交。</P>
<P>　　JSF图形组件还配有一套附加的JSF组件。<EM>overview</EM>可显示该图形整体视图，显示一个代表图形视图的长方形，还应允许用户扫描可视区域。<EM>legend</EM>组件可显示数据集合的相关信息，还能自行在图形中显示，依被显示数据的样式而定。也能提供客户端的<EM>interactors</EM>如扫描和放大，这些功能可看成是客户端交互，表示与图形的交互不会像一次正常的JSF交互那样重新载入整个页面。</P>
<P>　　要想呈现图形组件，只需使用chartView标签： </P>
<P>&lt;jvcf:chartView id="c" style="width:500px;height:300px" … /&gt; </P>
<P>　　该数据在HTML页面中作为图像显示。该图像由servlet创建，旨在响应一次HTTP请求（该请求包括指定结果图像、生成图像映射以及生成内联式图例等各种参数）。结果图像随之被嵌入客户端DOM，页面中只有图像自身这一部分被更新。</P>
<P><STRONG>应用程序核心部件</STRONG></P>
<P>　　让我们看看简单的定制JSF组件和高级图形组件之间的一些区别。JSF图形组件类很像一个标准组件，不过是多了一个可访问图形bean（该图形bean负责生成在HTML页面中显示的图像）的图形属性。JSF组件可以通过某个绑定值或在当前会话中对这个图形bean进行局部检索。当JSF图形组件成为某个应用程序的核心部件时，可选的JSF组件（如概览或图例）便与主图形相关联，来显示附加信息（见清单6）。</P>
<P align=left><STRONG>清单</STRONG><STRONG>6. </STRONG>当JSF图形组件成为某个应用程序的核心部件时，可选的JSF组件便与主图形相关联，来显示附加信息。</P><PRE class=code>&lt;jvcf:chartZoomInteractor id="chartZoomInteractor" 
	XZoomAllowed="true" 
	YZoomAllowed="true" /&gt; 
&lt;jvcf:chartView id="chartView" 
						 chart="#{myBean.chart}" 
					 servlet="demo.ImageMapServlet" 
			interactorId="chartZoomInteractor" 
						 width="500" 
						height="300" 
			 styleSheets="/data/line.css" 
			waitingImage="data/images/wait.gif" 
			 imageFormat="PNG"  /&gt;
&lt;jvcf:chartOverview id="chartOverview" 
					style="height:100;width:150px" 
						viewId="chartView" 
						lineWidth="3" 
						lineColor="red" /&gt;

&lt;jvcf:chartLegend id="legendView" 
							viewId="chartView" 
							 width="400" 
							height="180" 
							layout="vertical" 
				waitingImage="data/images/wait.gif" /&gt;

</PRE>
<P>　　呈现程序是实现这个JSF的一大难点。如前所述，呈现程序并不生成简单的HTML，而是生成由HTML（&lt;IMG&gt; tag）和JavaScript proxy（代理程序）组成的动态HTML（DHTML）。</P>
<P>　　Proxy是一个负责管理客户机组件图像显示的JavaScript类实例。该对象是服务器端Java组件类在客户端显示；它与组件类具有相同属性。页面上的每个组件、图形及其配件都有一个proxy实例。呈现JavaScript时，在每个可视的JavaScript变量上使用facesContext.getExternalContext().encodeNamespace(name)方法是个很好的实践。这样做在今后方便地将组件集成到到JSR 168-compliant端口环境中。</P>
<P>　　为举例说明客户机上的proxy，必须在页面上导入JavaScript支持库。为保持客户端尽量瘦，需要基于JavaScript库支持的proxy类，对JavaScript库进行模块化。因此，需要给每个proxy类输入一套不同的、有可能重叠的库。图形呈现的困难部分，出现在发送这些script库的阶段。每个组件的呈现程序都要声明自己需要哪个库，以及什么时候发送引用的库，必须认清已发送的库，以避免重复。仅在页面呈现期间的存在script管理器负责这项筛选工作。每当呈现程序想要发送整套库输入时，它都会向筛选出已发送库的script管理器提供列表。</P>
<P>　　客户端proxy的目的在于允许编写脚本，并避免不必要的页面更新。一旦呈现了图形，在客户端便可使用proxy，以便动态安装interactor，并显示或隐藏图像映射。Proxy对象也可供支持JavaScript鼠标事件处理的常规JSF组件使用。 </P><PRE class=code>&lt;jvcf:chartView id=
	"chartView" .. /&gt;
&lt;h:selectBooleanCheckbox id=
	"genImageMap" onclick=
	"chartView.setGenerateImageMap(
	this.checked ? true : false, 
 true);" /&gt;
</PRE>
<P>　　对组件客户端proxy进行局部修改的问题在于，其状态不再与服务器上的Java组件的状态同步。为解决这个问题，proxy使用一个隐藏的输入标签（&lt;INPUT TYPE="HIDDEN"&gt;）来保存客户机上的新状态。当执行某个标准JSF动作并提交页面时，呈现程序将解析该隐藏状态，使客户机与服务器同步。这种行为需要呈现程序类中有专门的破解行为。标准破解方法得以改进，以便解析来自客户机的状态，并更新服务器端组件的状态。</P>
<P><STRONG>测试实例</STRONG></P>
<P>　　图形及其相关组件之间的关联由标记引用与绑定来完成。为使页面设计具有灵活性，一个组件可以在呈现之前被引用。因此，在呈现时间内，如果某个组件属性引用另一个尚未呈现的组件，那么，将延迟发送依赖于客户机进行解决的JavaScript代码，直到呈现已引用的组件。此工作可由依赖性管理器完成。</P>
<P>为证实这一点，不妨看一个典型实例，该实例涉及某个概览，该概览引用一张图形。</P><PRE class=code>&lt;jvcf:overview viewId=
	"chart" [...] /&gt; 
&lt;jvcf:chartView id=
	"chart" [....] /&gt;</PRE>
<P>　　存在两种情况。被引用图形组件已经呈现，因此不存在任何问题</P><PRE class=code>JSP:
&lt;jvcf:chartView id=
	"chart" [....] /&gt;
&lt;jvcf:overview viewId=
	"chart" id="overview" [...] /&gt; 

render:
[...]
var chart = 
	new IlvChartViewProxy ( .. );
[...]

var overview= 
	new IlvFacesOverviewProxy (
	 .. );
overview.setView(chart);
[...]
</PRE>
<P>　　已引用图形的组件在依赖的概览组件之前不会呈现。既然如此，可在依赖性管理器上注册一个组件创建监视器。已引用图形组件最终呈现时，其呈现程序会通知自己创建的依赖性管理器。此时，将发送解决依赖性所需的代码：</P><PRE class=code>JSP:
&lt;jvf:overview viewId=
	"chart" id="overview" [...] /&gt; 
&lt;jvdf:chartView id=
	"chart" [....] /&gt;

render:
[...]
var overview = 
	new IlvFacesOverviewProxy (
	 .. );
[...]

var chart = 
	new IlvChartViewProxy ( .. );
overview.setView(chart);
[...]
</PRE>
<P>　　开发JSF组件的目的之一，是能够将它们应用于任何与JSF兼容的IDE。尽管如此，JSF兼容性并不足以保证这种设计时集成将会有效。下面是在开发JSF组件过程中，为了便于在今后与IDE集成需要注意的一些简单思想：</P>
<P>　　首先，定制JSF组件应该提供一个基本HTML呈现程序。在设计时，JSF IDE不能呈现请求有效数据或app服务器连接的动态图形组件。因此，带有复杂的或非传统的（比如不是HTML）呈现程序的组件，应该使用Beans.isDesignTime()来确定是提供一个基本HTML表示法，还是提供真正的组件呈现程序。</P>
<P>　　另一个设计时问题是组件的位置和大小。不同IDE使用不同的标志和属性。能够调整大小的组件（如一幅图像）应能处理定义大小的不同方式。</P>
<P>　　最后，为了与IDE集成，组件必须提供尚未被JSF说明定义的补充信息。遗憾的是，当前每个IDE都需要特殊处理程序来集成组件，即：在一种情况就需要XML文件，而在另一种情况下需要eclipse插件，如此等等。下一个JSF JSR（2.0版）的主要目的之一，将是指定附加的元数据格式。</P>
<P>　　如您所见，编写一个简单的JSF组件并不难，因为框架已经完成了大部分工作。JSF框架管理着组件状态、呈现程序等等。在本文中，我们已经扩展了这些基本概念，来设计一个能够显示复杂元数据、提供增量更新、支持大量客户端交互并与配套组件协作的高级图形JSF组件。支持这些特点需要对基本JSF组件的结构进行许多改进。当然，增量更新的概念今后对JSF框架将是一个很好的完善，因为它只允许呈现页面已改变的部分，避免了更新整个页面。按照JSF说明书工作往往不足以确保组件完全集成到JSF IDE中；一个新JSR应能及时解决这些难题。尽管存在缺陷，JSF框架仍能极大地加快Web组件开发速度、方便的融合来自各种资源的组件，以创建完整的、复杂的Web应用程序。</P>
<P><STRONG>参考资料</STRONG></P>
<UL>
<LI>developer.sun.com<BR>Java 工具<BR><A href="http://developers.sun.com/prodtech/javatools/jscreator/index.jsp" target=_blank>Sun Java Studio Creator</A><BR><A href="http://java.sun.com/developer/technicalArticles/GUI/JavaServerFaces" target=_blank>Developing Web Applications with JavaServer Faces</A> 
<LI>IBM <BR>Rational 软件<BR><A href="http://www-306.ibm.com/software/awdtools/developer/application/index.html" target=_blank>Rational Application Developer for WebSphere Software</A> 
<LI>Java Community Process <BR>Java Specification Requests <A href="http://jcp.org/en/jsr/detail?id=127" target=_blank>JSR 127: JavaServer Faces</A><BR><A href="http://jcp.org/en/jsr/detail?id=168" target=_blank>JSR 168: Portlet Specification</A> </LI></UL>
<P><STRONG>作者简介</STRONG></P>
<P>　　Marc Durocher是ILOG的一名软件架构师，ILOG是企业级软件组件和服务的主要提供商。Marc Durocher在ILOG负责开发ILOG JViews生产线上的JSF组件。可以通过<A href="mailto:mdurocher@ilog.fr">mdurocher@ilog.fr</A>联系Marc。</P>
<P><STRONG>原文出处</STRONG></P>
<P><A href="http://www.ftponline.com/weblogicpro/2005_03/magazine/features/mdurocher/" target=_blank>http://www.ftponline.com/weblogicpro/2005_03/magazine/features/mdurocher/</A></P><!--文章其他信息--><img src ="http://www.blogjava.net/Victor/aggbug/21857.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-11-29 18:11 <a href="http://www.blogjava.net/Victor/articles/21857.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Spring改进J2EE编程</title><link>http://www.blogjava.net/Victor/articles/18071.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 04 Nov 2005 00:45:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/18071.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/18071.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/18071.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/18071.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/18071.html</trackback:ping><description><![CDATA[时间：2005-09-27<BR>作者：<A href="http://dev2dev.bea.com.cn/author/388.html">Peter Braswell</A><BR>本文关键字：<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=spring">spring</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=轻量级">轻量级</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=反向控制">反向控制</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=IoC">IoC</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=J2EE">J2EE</A><BR><BR>
<P><STRONG>摘要</STRONG></P>
<P>　　J2EE编程正在变得越来越复杂。J2EE已经发展为一个API、复杂化的编程和配置的复杂网络。为了应对这种复杂性，新的框架和方法不断涌现。这些框架高度依赖于一个称为IoC（Inversion of Control，反向控制）的概念。本文将探讨这种方法的一些特性和优点，因为这种方法与J2EE编程相关，而且可以使J2EE编程变得更轻松。</P>
<P><STRONG>简介</STRONG></P>
<P>　　马克·吐温的一句话常被引用：“……关于我死亡的报道是一种夸张。”现在已经出现了很多关于.Net的流言，以及认为J2EE API的复杂性无法克服和EJB作为一种组件架构即将灭亡的流行极客(geek)文化。从学术或者只是想像的立场来看，这没什么大不了的，但事实是J2EE/EJB API已经经历了一场达尔文式的进化。具有DCOM或CORBA项目经验的读者会明白我的意思。过去，人们都乐于听闻EJB组件模型的美好前景。实际情况是，人们在与J2EE相关的各个方面都投入巨大。宣布抛弃以前的所有工作并重新组织，这种想法看起来也许有理，但是它并没有建立在良好的业务洞察力之上。EJB继续发展，而术语、实践和框架也随之涌现(spring up)，它们弥补了J2EE API的不足。我说的不是“Spring出现(up)”，对吧？</P>
<P>　　我是一名顾问，职责是帮助构建大型的分布式应用程序，而且通常是J2EE应用程序。因此，我有机会亲历许多项目的整个生命周期。另外我还能够将我从一个刚刚完成的项目中刚刚学到的东西直接带入一个全新的项目。从某种意义上说我的“自然选择”过程加快了。我可以说最近Spring（更具体地说就是IoC，即反向控制）已经越来越多地融入到我的项目中了。在本文中，我将从支持或增强J2EE项目的角度来探讨Spring。更确切地讲，Spring框架能够标准化许多J2EE最佳实践，还能同类化(homogenize)许多无处不在的J2EE模式。接下来我们将浏览Spring庞大体系中的一小部分内容，重点介绍（依我浅见）能够帮助改进J2EE应用程序的功能。</P>
<P><STRONG>IoC简介</STRONG></P>
<P>　　一般来说，IoC是一种管理类之间关联的技术。没错，就这么简单！任何人都不是孤立的，对于各个对象来说也是如此。应用程序中的对象是相互依赖的。通过编程方式来表现这种依赖性通常既冗长又容易出错。好的IoC框架将声明式地（通过一个XML配置文件）而不是编程式地（这种方式的可靠性较差）——串连起应用程序之间的相互依赖性。</P>
<P>　　自由使用接口是IoC开发的一个主要方针。接口编程大大提高了应用程序的灵活性，从而增强了声明式的关联。接口实现是通过IoC配置在运行时声明的，这样就能够在不影响或少影响实际应用程序代码的情况下“重建(rewire)”关联。这在各种IoC框架中是反复提及的一个主题，一般而言，也是应该遵循的良好实践。</P>
<P><STRONG>一个小例子</STRONG></P>
<P>　　我喜欢通过例子来更快地理解概念。下面就是运用了IoC的一组例子；您将看到，这些例子的复杂性是逐递增的。大多数人在一开始使用IoC容器时都是利用其依赖注入(inject dependency)功能——即，声明式地将对象关联起来。利用IoC有助于创建更整洁的代码，如有必要重建对象之间的关联，一般来说对于这些代码也会更灵活、更容易。IoC的优点远不止依赖注入，而其扩展功能确是以依赖注入程序为起点的。</P>
<P>　　我们将从构建简单的依赖注入例子开始。第一个例子用于阐明已经提及的两个概念。第一个概念是IoC在运行时构建和关联对象的能力，第二个是与接口编码相结合而产生的灵活性。首先假定架构师递交了图1所示的UML。</P>
<P><IMG height=187 src="http://dev2dev.bea.com.cn/images/050927/0927001.jpg" width=334> <BR></P>
<P>图1. 接口可插性</P>
<P>　　这个小例子表示一个温度测量系统。几个传感器对象属于不同的类型，但都实现了ProtocolAdapterIfc接口，因此在将它们插入TemperatureSensor对象时，它们是可互换的。在需要TemperatureSensor时，系统中的某个实体必须知道要生成并与该传感器对象关联的ProtocolAdapterIfc的具体类型。在本例中，该传感器可基于命令行参数、数据库中的行或通过属性文件进行配置。本例还不足以造成挑战或展示一个复杂框架，但它足以阐明IoC基础。</P>
<P>　　但是，想象一下：在一个相当复杂的应用程序中这种情况屡屡发生，而您还希望能动态地——至少要在外部——改变对象关联。假设有一个DummyProtocolAdapter，它总是返回42这个值，使用它来进行测试。为什么不提供一个单个的统一框架？——让开发人员能够依靠该框架，以一种一致的、外部配置的方式建立类之间的关联，并且不引起工厂单元素类(factory singleton classe)的异常增加。这听起来可能没什么大不了，但它要依赖于IoC的简单性。</P>　　我们使用一个TemperatureSensor类，它与一个实现ProtocolAdapterIfc接口的类有关联。TemperatureSensor将使用该委托类来获得温度值。如UML图所示，在实现ProtocolAdapterIfc并且随后可用于该关联的应用程序中有若干个类。我们将使用IoC框架（在本例中是Spring）来声明要使用的ProtocolAdaperIfc的实现。Spring将在运行时建立关联。我们先来看XML代码，它将实例化TemperatureSensor对象并将一个ProtocolAdapterIfc实现与它关联起来。该代码如下所示： <PRE class=code>&lt;bean id="tempSensor"
class="yourco.project.sensor.TemperatureSensor"&gt;
  &lt;property name="sensorDelegate"&gt;
    &lt;ref bean="sensor"/&gt;
  &lt;/property&gt;
&lt;/bean&gt;

&lt;!-- Sensor to associate with tempSensor --&gt;
&lt;bean id="sensor" class="yourco.project.comm.RS232Adapter"/&gt;

</PRE>
<P>　　看了这些代码之后，对于其目的就应该非常清楚了。我们配置Spring来实例化TemperatureSensor对象，并将其与RS232Adapter相关联，作为实现ProtocolAdapterIfc接口的类。若想改变已经与TemperatureSensor关联的实现，惟一需要更改的就是sensor bean标记中的class值。只要实现了ProtocolAdapterIfc接口，TemperatureSensor就不再关心关联了什么。</P>
<P>　　将这应用于应用程序相当简单。我们必须先接入Spring框架，将它指向正确的配置文件，然后根据名称向Spring索取tempSensor对象的实例。下面是相应的代码：</P><PRE class=code>ClassPathXmlApplicationContext appContext =
  new ClassPathXmlApplicationContext(
  new String[]
  { "simpleSensor.xml" });
BeanFactory bf = (BeanFactory) appContext;
TemperatureSensor ts = (TemperatureSensor)
  bf.getBean("tempSensor");
System.out.println("The temp is: "+
  ts.getTemperature());

</PRE>
<P>　　可以看出，这些代码并不是非常难。首先是启动Spring并指定要使用的配置文件。接下来根据名称（tempSensor）引用Bean。Spring使用这样一种机制：基于simpleSensor.xml文件的描述创建该对象并与其他对象关联。它用于注入依赖性——在本例中，通过将它作为一个参数传递给sensorDelegate()方法而实例化RS232Adapter对象并将其与TemperatureSensor对象关联。</P>
<P>　　比较起来，使用编程式Java完成这一任务也不是很难。如下所示：</P><PRE class=code>TemperatureSensor ts2 = new TemperatureSensor();
ts2.setSensorDelegate(new RS232Adapter());
</PRE>
<P>　　纯粹主义者或许会认为实际上这是更好的方法。代码行数少，并且可读性可能更强。确实如此，但这种方法的灵活性要小得多。</P>
<UL>
<LI>可以随意换入和换出不同层中不同对象的不同实现。例如，若Web层中的组件需要来自新业务对象的额外的功能，您只需将该业务对象与Web层对象相关联，就像上面TemperatureSensor例子中的做法。它将被“注入”到Web对象中以随时使用。 
<LI>能够重新配置整个应用程序的结构，意味着可以轻松更改数据源。比如说，或者为不同的部署场景创建不同的配置文件，或者为测试场景创建更有用的、不同的配置文件。在测试场景中可能会注入实现接口的模拟对象，而不注入真正的对象。稍后我们将介绍一个这样的例子。 </LI></UL>
<P>　　上面所述的例子可能是依赖注入的最简单形式。利用相同的策略，我们不仅能够关联不同的类，还能够在类中安装属性。诸如字符串、整数或浮点数之类的属性，只要具有JavaBean样式的存取器，就可以通过Spring配置文件将它们注入类中。我们还可以通过构造函数来创建对象和安装属性或bean引用。其语法只比通过属性进行设置稍稍复杂一些。</P>
<P>　　所有这一切都是利用一种灵活的声明性配置完成的。无需更改代码，建立依赖关联的所有艰难任务都由Spring来完成。</P>
<P><STRONG>Spring--标准化的定位器模式</STRONG></P>
<P>　　我一直将服务定位器模式视作良好的J2EE规范的主要组成部分。对于不熟悉这一术语的人来说，可以这样理解它：我们一般认为典型的J2EE应用程序由若干层组成。通常有Web层、服务层（EJB、JMS、WS、WLS控件）以及数据库。一般来说，完成某一请求所需的“查找”服务中都包含了一些方法。Service Locator（服务定位器）模式认为，将这些方法包装在某种隐藏了生成或查找给定服务的复杂性的工厂类中是一个好主意。这减少了JNDI或只会造成Web层操作类混乱的其他服务产品代码的增加。在Spring出现以前，这通常是由经过考验证明可靠的(tried-and-true)Singleton类来实现的。Singleton/Locator/Factory模式可以描绘为：</P>
<P><IMG height=229 src="http://dev2dev.bea.com.cn/images/050927/09270102.jpg" width=409> <BR></P>
<P>图2. 定位器模式的顺序图</P>
<P>　　这是对散布在整个Web控制器代码中的增加的JNDI查找代码的一个巨大改进。它被巧妙地隐藏在工厂内部的协作类中。我们可以使用Spring来改进这一术语。此外，该解决方案将适用于EJB、Web services、异步JMS调用，甚至还有基于WLS控件的服务。由Spring实现的这种定位器模式的变体考虑了业务服务之间的一些抽象化和同质性。换句话说，Web控制器的开发人员真的可以不考虑他们所使用的服务的种类，一个类似于“WLS控件”但是更通用的概念。</P>
<P>　　IoC框架大大改进了这种模式的效用，而且实际上废除了复杂而特殊的singleton代码来实现它。通过借用上例中引入的概念，我们实际上无需额外代码便能构建一个非常强大且无处不在的Service Locator模式。为此，在一开始有一个简单的要求，即Web操作的开发人员应专门处理实现接口的那些事情。这基本上已经通过EJB编程实现，但并不是说Web操作的开发人员处理的服务必须通过EJB来实现。它们可能只是普通Java对象或Web services。要点是应当通过接口（这样实现能够换入换出）来编写服务程序，并且运行时配置能够由Spring处理。</P>
<P>　　Spring之所以非常适合于Service Locator模式，是因为它或多或少能够统一地处理不同类型的对象。通过少许的规划和大量使用IoC，我们多少都能够以一种通用方式来处理大多数对象，而不用管它们的特性（EJB、POJO等等）如何，并且不会引起Singleton工厂类的增加。这使Web层编程变得更加轻松和灵活。</P>　　我们先来看一个关于这种模式如何应用于EJB的例子。我们都知道使用EJB可能是最复杂的方法，因为要将一个活动的引用引入EJB要做很多工作。若使用Spring，建议用EJB接口扩展非特定于EJB的业务接口。这样做有两个目的：保持两个接口自动同步，以及帮助保证业务服务对非EJB实现是可交换的，以便进行测试或清除(stubbing)。我们可以利用Spring固有的实用工具来定位和创建EJB实例，同时为我们处理所有难以处理的工作。相应代码如下所示： <PRE class=code>&lt;bean id="myBizServiceRef"
         class="org.springframework.ejb.access.
         LocalStatelessSessionProxyFactoryBean"&gt;
  &lt;property name="jndiName"&gt;
    &lt;value&gt;myBizComponent&lt;/value&gt;
  &lt;/property&gt;
  &lt;property name="businessInterface"&gt;
    &lt;value&gt;
      yourco.project.biz.MyBizInterface
    &lt;/value&gt;
  &lt;/property&gt;
&lt;/bean&gt;
</PRE>
<P>　　接下来可以检索bean并开始使用它，方法如下：</P>
<P>　　MyBizInterface myService = bf.getBean("myBizServiceRef");</P>
<P>　　这将返回Spring动态创建并包装了底层目标（在本例中是一个本地EJB实例）的一个对象。这种方法非常好，因为它完全隐藏了我们在处理EJB这一事实。我们将与一个实现简单业务接口的代理对象交互。Spring已经基于“真正的”业务对象考虑周到地动态生成了该对象。所包装的对象当然就是Spring定位和检索引用所要获得的本地EJB。此外，您还会注意到，这种代码形式与前面用于检索tempSensor对象的代码完全相同。</P>
<P>　　那么如果我们改变主意，想用普通Java对象来实现业务组件；或者可能在测试中，我们想用一个返回“固定(canned)”响应的已清除(stubbed)对象来替换重量级EJB，该怎么做呢？利用IoC和Spring，通过更改Spring上下文文件就可轻而易举地实现这些目标。我们只需使用更常规一点的东西（如我们在第一个Spring例子中所看到的）来替换EJB代理的连接即可：</P>
<P>&lt;bean id="myBizServiceRef"</P>
<P>class="yourco.project.biz.MyStubbedBizService"&gt;</P>
<P>&lt;/bean&gt;</P>
<P>　　请注意，我只更改了Spring框架所返回的内容的细节，没有更改bean id。最后的结果是业务对象的解决方案未变；它看上去和以前完全一样：</P>
<P>MyBizInterface myService =</P>
<P>bf.getBean("myBizServiceRef");</P>
<P>　　最大的区别显然是实现该业务接口的对象现在由一个普通Java对象（POJO）支持，并且只是该接口的一个已清除(stubbed)版本。这给单元测试或改变业务服务的特性带来了极大方便，而对客户端代码的影响很小。</P>
<P><STRONG>使用Spring来标准化异常</STRONG></P>
<P>Spring的一大贡献是“模板化”代码块。这在纯JDBC编程中表现得最为明显。我们都曾写过具有下述功能的代码：</P>
<UL>
<LI>创建一个数据库连接，可以的话从某个池创建。 
<LI>构造一个查询字符串并提交。 
<LI>迭代结果并将数据封送到域对象中。 
<LI>处理不同阶段出现的大量异常。 
<LI>确保记得编写finally代码块以关闭连接。 </LI></UL>
<P>　　但是各处的这种代码往往都会或多或少地有点“样板化”。一般来说这是有害的，不仅因为不需要的代码会增加，还因为有些东西可能会遗漏，如非常重要的关闭连接，如果没有实现它，可能导致数据资源池的泄漏。</P>
<P>　　虽然我敢肯定我们都曾多次写过这类“样板”代码，但是将Spring方法和直接的JDBC实现对照来看，其结果将会有趣而又对比鲜明。“传统”的JDBC实现可能如下：</P><PRE class=code>Connection con = null;
try
{
  String url = "jdbc://blah.blah.blah;";
  con = myDataSource().getConnection();
  Statement stmt = con.createStatement();
  String query = "SELECT TYPE FROM SENSORS";
  ResultSet rs = stmt.executeQuery(query);
  while(rs.next()){
    String s = rs.getString("TYPE);
    logger.debug(s + "   " + n);
  }
} catch(SQLException ex)
{
  logger.error("SQL ERROR!",ex);
}
finally
{
  con.close();
}</PRE>
<P>　　对于该方法要做一些说明。首先，它是有效的！该代码绝对不会出现任何错误。它会连接到数据库，并从‘SENSOR’表获取所需的数据。该方法的基本问题源于缺乏抽象化。在大型应用程序中，必须反复剪切和粘贴这段代码，或者至少会出现类似的其他情况。较大的问题在于它依赖于编程人员去做“该做的事”。我们都知道，不管数据库操作的结果是什么，都必须用finally语句来关闭该数据库。有时我们忘记做该做的事。我和所有人一样感到内疚！</P>
<P>　　编写一个小框架来解决这一问题会非常容易。我相信大家也都曾这样做过，但为何不让Spring帮我们处理这一问题呢？我不敢保证我能想出一个更整洁、更优雅的解决方案。我们来看Spring框架是如何处理这种样板JDBC场景的。</P>
<P>　　Spring支持各种各样的JDBC、Hibernate、JDO和iBatis模板。模板采用样板概念，并将它转换为合法的编程术语。例如，下面的代码片断封装了上面列出的各个步骤：</P><PRE class=code>DataSource ds = (DataSource) bf.getBean("myDataSource");
JdbcTemplate temp = new JdbcTemplate(ds);
List sensorList = temp.query("select sensor.type FROM sensors",
  new RowMapper() {
     public Object mapRow(ResultSet rs, int rowNum) throws SQLException;
        return rs.getString(1);
     }
  });
</PRE>
<P>　　这段简短的代码消除了JDBC编程的冗长，代表了前面提及的样板的思路。请注意我们使用了Spring的IoC来查找该查询的数据源。Spring还支持对已检查异常使用未检查异常；因此许多已检查的JDBC异常会重新映射到通常更有用而且更友好的未检查异常层次结构中。在Spring的上下文文件中配置该数据源类似于下面代码：</P><PRE class=code>&lt;bean id="myDataSource"
  class="org.apache.commons.dbcp.BasicDataSource"&gt;
  &lt;property name="driverClassName"&gt;
    &lt;value&gt;org.gjt.mm.mysql.Driver&lt;/value&gt;
  &lt;/property&gt;
  &lt;property name="url"&gt;
    &lt;value&gt;jdbc:mysql://romulus/sensors&lt;/value&gt;
  &lt;/property&gt;
  &lt;property name="username"&gt;
    &lt;value&gt;heater&lt;/value&gt;
  &lt;/property&gt;
  &lt;property name="password"&gt;
    &lt;value&gt;hotshot&lt;/value&gt;
  &lt;/property&gt;
&lt;/bean&gt;
</PRE>
<P>　　在本例中，我们利用Apache commons工具箱配置了一个基本数据源。但并不是说我们只能使用它。我们可以改变配置，使用在JNDI中配置并装载的数据源。Spring提供了一些实用工具和IoC功能以配置和返回存储在JNDI中的对象。例如，若想配置并使用与JNDI上下文关联的数据源，则可以输入以下代码，替换先前的数据源配置：</P><PRE class=code>&lt;bean id="myDataSource"
  class="org.springframework.jndi.
  JndiObjectFactoryBean"&gt;
  &lt;property name="tempSensorDS"&gt;
    &lt;value&gt;ConnectionFactory&lt;/value&gt;
  &lt;/property&gt;
&lt;/bean&gt;
</PRE>
<P>　　该代码突出了Spring所提供的关于表格的测试灵活性。该代码可以在“容器内”运行（从JNDI查找数据源），经过细微的改动之后也可在“容器外”运行。</P>
<P>　　虽然模板化机制仅适用于某些特定场合，但我们可以泛化这一概念，将它应用于更广泛的场合。例如，一种将已检查异常转变为未检查异常、此外还可能为异常处理提供一些中间或统一的异常处理策略的机制将会很有帮助。我们还可以使用该机制将底层的基本异常“软化”得更合乎人意。我们可以将PacketFrameParityFaultException软化为CommunicationsUnreliableException。这个重新映射的较软的异常表示情况可能并不那么严重，重新请求也是可以的。</P>
<P>　　Spring已经具备了一种类似于包装EJB调用（在最后一节介绍）的机制，但遗憾的是它并不具备任何“通用”的东西，至少在异常软化意义上是这样的。但Spring的确有一个非常健壮的AOP（面向方面编程）框架，我们可以用它来逼近这种行为。下面是一个关于这种软化适用领域的（公认的）精心设计的例子。</P>
<P>　　我们再来看看本文前面已经开始探讨的一些概念。在第一节中我们介绍了一个基于远程传感器的小应用程序。现在我们继续探讨这个例子。我们将从一个简单的传感器接口开始介绍，该接口代码如下：</P><PRE class=code>public interface ProtocolAdapterIfc
{
  public Integer getRemoteSensorValue()
    throws CommChecksumFault,
           CommConnectFailure,
           CommPacketSequenceFault;
}

</PRE>
<P>　　这并没有什么特别之处。显然实现该接口的任何人都会获得一个远程值并将它返回给调用者。在此期间调用者可能要面对某些可怕的灾难，该接口可能抛出的已检查异常就是例证。</P>
<P>　　接下来我们来看该接口的一个实现程序。实现ProtocolAdapter的类是CarrierPigeon，其代码类似于：</P><PRE class=code>public class CarrierPigeon
      implements ProtocolAdapterIfc
{
  private boolean isTired = true;
  private boolean canFlapWings = false;

  public Integer getRemoteSensorValue()
    throws CommChecksumFault,
           CommConnectFailure,
           CommPacketSequenceFault
    {
        if(isTired &amp;&amp; !canFlapWings )
        {
          throw new
               CommConnectFailure("I'm Tired!");
        }
        return new Integer(42);
    }
}


</PRE>
<P>　　为简洁起见，这里省略了属性的getter和setter方法。当调用getRemoteSensorValue()时，CarrierPigeon方法将检查它是否使用过度以及它还能否执行。如果它确是使用过度并且不能执行，我们将无法获得任何值，必须抛出CommConnectionFailure，它是一个已检查异常。到现在为止，一直都还不错。但别高兴得太早了！我已经决定不让应用程序编程人员应对“疲劳的信鸽”。可以将它包装在某种对象中，并在达到目的之前捕获异常，但必须处理20种不同的传感器。此外，我更喜欢对这些东西进行适度透明地处理，随着对该系统了解的增多，或许还会更改异常处理程序中的逻辑。我想要的是一个兼容API，应用程序编程人员能够使用它，并且随着时间的推移能够改变或增强。我想模板化异常处理，并让应用程序编程人员能够处理软的未检查异常，而不是硬异常。Spring非常符合这种情况。下面是为此而使用的策略的要点：</P>
<UL>
<LI>定义一个较软的消除已检查异常的最小接口。这就是应用编程人员将使用的接口。 
<LI>使用Spring AOP结构，开发一个客户机调用和目标对象调用之间的拦截器，在本例中是“信鸽”。 
<LI>使用Spring安装该拦截器并运行。 </LI></UL>
<P>　　首先来看这个较软的接口：</P><PRE class=code>public interface SensorIfc
{
  public Integer getSensorValue();
}

</PRE>
<P>　　请注意，在限定范围内可以重命名方法，使其更有意义。还可以消除已检查异常，就像这里所做的一样。接下来，也可能会更有趣的是我们想要让Spring将其注入调用栈的拦截器：</P><PRE class=code>import org.aopalliance.intercept.MethodInterceptor;

public class SensorInvocationInterceptor
  implements MethodInterceptor
{
  public Object invoke(MethodInvocation
     invocationTarget) throws Throwable
  {
    // Return object reference
    Object o = null;

    // Convert it to the protocol interface type
    ProtocolAdapterIfc pai =
      (ProtocolAdapterIfc) invocationTarget.getThis();

    try
    {
      o = pai.getRemoteSensorValue();
    }
    catch (CommChecksumFault csf)
    {
      throw new SoftenedProtocolException(
       "protocol error [checksum error]: "
        + csf.getMessage());
    }
    catch (CommConnectFailure cf)
    {
      throw new SoftenedProtocolException(
        "protocol error [comm failure]: "
        + cf.getMessage());
    }
    catch (CommPacketSequenceFault psf)
    {
      throw new SoftenedProtocolException(
        "protocol error [message sequence error]"
        + psf.getMessage());
    }

    return o;
  }
}</PRE>
<P>　　通过实现Spring MethodInterceptor接口并将该类插入Spring系统（稍后我们将进行讨论），我们将通过MethodInvocation参数获得实际的目标方法。这样就能提取我们想要的真正对象。请记住，我们更改了被调用者看作SensorIfc的接口，该接口使目标对象得以实现ProtocolAdapterIfc。我们这样做是为了简化调用，更是为了消除所有的已检查异常。该拦截器只调用用来捕获可能抛出的任何已检查异常、并用SoftenedProtocolException将它们重新包装的目标方法。实际上，我们只重新包装消息，但要做到心中有数。SoftenedProtocolException扩展了RuntimeException，后者无疑是一个未检查异常。该操作的最终结果是应用程序开发人员不必处理任何已检查异常。如果他们真想处理异常，他们只需（非强制地）处理一个：SoftenedProtocolException。不错吧！</P>
<P>　　那么，如何才能让Spring完成这一切呢？答案就是要有正确的配置文件。我们现在就来看一看该配置文件，并了解其工作原理：</P><PRE class=code>&lt;!-- TARGET OBJECT --&gt;
&lt;bean id="protocolAdapter"
  class="yourco.project.comm.CarrierPigeon"&gt;
  &lt;property name="isTired"&gt;
    &lt;value&gt;true&lt;/value&gt;
  &lt;/property&amp;gl;
  &lt;property name="canFlapWings"&gt;
    &lt;value&gt;true&lt;/value&gt;
  &lt;/property&amp;gl;
&lt;/bean&amp;gl;

&lt;!-- INTERCEPTOR --&gt;
&lt;bean id="sensorInterceptor"
  class="yourco.project.springsupport.
  SensorInvocationInterceptor"/&gt;

&lt;!--WIRE EVERYTHING UP, HAND BACK TO THE USER--&gt;
&lt;bean id="temperatureSensorOne"
  class="org.springframework.aop.framework.
  ProxyFactoryBean"&gt;
  &lt;property name="proxyInterfaces"&gt;
    &lt;value&gt;
yourco.project.interfaces.SensorIfc
    &lt;/value&gt;
  &lt;/property&gt;
  &lt;property name="target"&gt;
    &lt;ref local="protocolAdapter"/&gt;
  &lt;/property&gt;
  &lt;property name="interceptorNames"&gt;
    &lt;list&gt;
      &lt;value&gt;sensorInterceptor&lt;/value&gt;
    &lt;/list&gt;
  &lt;/property&gt;
&lt;/bean&gt;
</PRE>
<P>　　我们逐节看这些代码时，可以看到它比我们前面看到的Spring配置文件要稍微复杂一些。“TARGET OBJECT”注释下面的第一节指定了要作为调用的目标对象的类。还记得拦截器曾将一个参数传入其中来表示目标对象吗？这就是其工作原理。基于该参数，Spring现在知道要将哪个对象传递进来。“INTERCEPTOR”下面的代码节是在目标方法之前调用的类。此时，如果目标类抛出已检查异常，要开始处理异常，并进行软化。Spring将它与目标类相关联。最后一节是将各种元素联系起来。用户要请求的bean位于bean键temperatureSensorOne下。ProxyFactoryBean将生成并返回一个代理类，它用来实现yourco.project.interfaces.SensorIfc接口。目标对象当然就是protocolAdapter bean，它由yourco.project.comm.CarrierPigeon的实例支持。只要用户调用代理的方法就插入并调用的拦截器位于bean键sensorInterceptor下，并由yourco.project.springsupport.SensorInvocationInterceptor支持。请注意，可使用多个拦截器，因为该属性是一个列表，因此可以将许多拦截器对象关联到方法调用中，这是一个非常有用的理念。</P>
<P>　　运行该应用程序时，根据插入的CarrierPigeon值，我们可以看到一些有趣的行为。如果我们的CarrierPigeon没有使用过度并能执行，我们将看到这样的输出：</P>
<P>The sensor says the temp is：42 </P>
<P>　　显然“信鸽”没问题而且状况很好，并计算出温度为42。如果由于改变CarrierPigeon Spring节中的值，而造成“信鸽”使用过度或不能执行，我们将得到如下所示的结果：</P><PRE class=code>yourco.project.exceptions.comm.SoftenedProtocolException: protocol 
 error [comm failure]: I'm Tired!
  at yourco.project.springsupport.SensorInvocationInterceptor.invoke
	(SensorInvocationInterceptor.java:57)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed
    (ReflectiveMethodInvocation.java:144)
  at org.springframework.aop.framework.JdkDynamicAopProxy.invoke
	(JdkDynamicAopProxy.java:174)
  at .getSensorValue(Unknown Source)
  at yourco.project.main.Main.main(Main.java:32)

</PRE>
<P>　　在这种情况下，“信鸽”或者使用过度，或者不能执行，因此得到的结果是SoftProtocolException，并附带一条消息说明发生的情况：“I'm tired!”够酷吧！</P>
<P>　　我希望人们能够开始了解Spring的强大及其多种功能。Spring框架悄然兴起，并成为开发人员的编程工具箱中的真正的“瑞士军刀”。Spring在实现让开发人员能够集中于应用程序的基本组成部分这一传说般的承诺方面做得不错，这也正是它得以大行其道的业务逻辑。Spring将您从J2EE中的一些错综复杂的方面中解放出来。Spring的强大在于它能够使大多数东西看起来就如同非常普通的Java对象一样，而不管它们的性质或来源如何。既然Spring自身肩负起创建、关联和配置的重担，那么开发人员要做的只是掌握使用对象而不是构造对象的方法。Spring就如同在杂货店购买的预煮的饭菜。您要做的只是决定想吃什么、把它带回家、加热，然后吃！</P>
<P><STRONG>综合说明</STRONG></P>
<P>　　Spring是一个非常健壮的轻量级框架，它极好地弥补了J2EE/EJB环境的不足。Spring真正伟大的一点在于它不走极端。您可以以一种非常简单的方式开始使用Spring（正如我所做的那样），只是建立常见的关联，作为定位器模式的一种实现。稍后，您将发现其惊人的功能，并且很快对它的要求会越来越多。</P>
<P>　　我所发现的一个惊人之处是Spring所提供的测试灵活性。现在人们对通过Junit进行单元测试日益重视，这增加了测试，而不是减少测试。J2EE容器的使用使测试极端复杂，以至于难以进行。这种困难局面源于业务逻辑和容器框架服务之间产生的耦合。借助于Spring的配置机制，使实现可以动态切换，这样就有助于将业务对象从容器中释放出来。我们已经看到，如果只想测试Web组件，将活动的EJB换成其代理或一个已清除的业务服务并不会造成太大影响。借助于JDBC或Hibernate数据访问对象，我们可以使用常见的简单JDBC、非XA的数据源连接来测试这些组件，并将它们无缝地换出，代之以健壮的基于JTA、JNDI的对应连接。结论是：如果代码易于测试，并因此测试得更多，那么质量必然会提高。</P>
<P><STRONG>结束语</STRONG></P>
<P>　　本文粗略地概括介绍了IoC，并详细介绍了Spring。Spring框架具有许多功能，其中许多功能在本文中只是点到为止。从基本的依赖注入到复杂的AOP操作，这些组成了Spring的强大功能，这是它的主要优点之一。能够根据问题需要使用或多或少的IoC功能是一个极具吸引力的理念，我认为，这在通常错综复杂的J2EE编程领域也是颇受欢迎的。现在看完了这篇文章，我衷心地希望它能对您有所帮助，并可以应用到您的工作中。欢迎随时提供反馈！</P>
<P><STRONG>参考资料</STRONG></P>
<UL>
<LI><A href="http://www.springframework.com/" target=_blank>Spring</A>——Spring的官方Web站点，有许多很棒的说明文档！ 
<LI><A href="http://dev2dev.bea.com/pub/a/2005/07/spring_transactions.html" target=_blank>Implementing Transaction Suspension in Spring</A>——更深入地探讨Spring中的事务支持 </LI></UL>
<P><STRONG>原文出处</STRONG></P>
<P><A href="http://dev2dev.bea.com/pub/a/2005/07/better_j2eeing.html" target=_blank>http://dev2dev.bea.com/pub/a/2005/07/better_j2eeing.html</A></P><!--文章其他信息-->
<DIV class=dot001><IMG height=1 alt="" src="http://dev2dev.bea.com.cn/images/_.gif" width="100%"></DIV>
<TABLE cellSpacing=0 cellPadding=3 width="100%" border=0>
<TBODY>
<TR vAlign=bottom>
<TD colSpan=2 height=20>&nbsp;<SPAN class=h2b>作者简介</SPAN></TD></TR>
<TR>
<TD vAlign=top align=middle width=0></TD>
<TD>
<P><A href="http://dev2dev.bea.com/pub/au/314" target=_blank>Peter Braswell</A>是ALTERThought的首席科学家。Peter具备超过15年的软件工程方面的专业经验。他曾领导多个行业的前沿性任务关键型系统和应用程序的实现工作，这些行业包括高科技、金融、零售和电信等。</P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/Victor/aggbug/18071.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-11-04 08:45 <a href="http://www.blogjava.net/Victor/articles/18071.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NetUI页面流：Struts的发展</title><link>http://www.blogjava.net/Victor/articles/18070.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 04 Nov 2005 00:41:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/18070.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/18070.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/18070.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/18070.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/18070.html</trackback:ping><description><![CDATA[时间：2005-10-18<BR>作者：<A href="http://dev2dev.bea.com.cn/author/391.html">Srinivas Jaini</A><BR>本文关键字：<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=struts">struts</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords= netui"> netui</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords= page flow"> page flow</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords= Controls"> Controls</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords= 控件"> 控件</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords= 页面流"> 页面流</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords= MVC"> MVC<BR></A><BR>Struts是一个用来构建企业级J2EE应用程序的流行框架。通过使用Struts，J2EE Web应用程序的开发变得更加轻松且更易于管理。Beehive是Apache软件基金会（Apache Software Foundation，ASF）的一个开源项目，它在Struts的基础上构建一个简单的页面流（Page Flow）模型，使Web应用程序的开发变得更加简单明了。通过使用新的 JSR-175和 JSR-181元数据注释功能，Beehive减少了J2EE应用程序开发的编码量。本文将介绍Beehive页面流技术，以及如何使用它来提高Struts软件的生产力和质量。本文还介绍了如何将该技术迁移到普通的Struts应用程序中去。本文假定读者对Struts有一定的了解。<STRONG>&nbsp;</STRONG> 
<P><STRONG>普通的Struts </STRONG></P>
<P>　　Struts是Apache软件基金会的Jakarta项目的一部分，是一个基于模型－视图－控制器（Model-View-Controller，MVC）设计模式构建Web应用程序的开源框架。Struts使用动作(action)类来构建框架的控制器组件。典型的Struts应用程序要求使用大量动作类来处理某一流程流（process flow）的几个动作，并要求使用一个XML配置文件来声明跳转。因此，像数据管理和维护之类的问题已经成为现有Struts应用程序的一个主要关注点。图1显示了一个示例应用程序的流程图，意在对Struts和页面流作一比较。</P>
<P><IMG height=243 src="http://dev2dev.bea.com.cn/images/051018/0510180101.jpg" width=315> <BR></P>
<P align=center>图1. LoginProcessFlow应用程序的流程图</P>
<P>　　在Struts中开发该应用程序的时候，通常要求针对每个动作（begin、signup、login和logout）使用动作类。清单1显示了一个典型的动作类，在本例中是图中描绘的begin动作。 </P>
<P>清单1. BeginAction.java </P><PRE class=code>package comparision.struts;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public final class BeginAction extends Action {
 
    public ActionForward execute(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
        throws Exception {
        
        return (mapping.findForward("success"));
    }
}
</PRE>
<P>　　为完成该Struts应用程序，需要针对signup、login和logout使用类似的动作类。HTTP会话被用来跨动作传递数据，这使得管理和维护数据变得很困难。Struts使用一个配置文件（struts-config.xml）来声明到JSP页面的跳转。因此，要更改业务规则通常还需要更改动作类和配置文件。具体来说，更改业务逻辑要求更改动作，而更改业务流程则要求更改配置文件（通常二者都需要更改）。清单2显示的是LoginProcessFlow应用程序的典型的struts-config.xml文件。</P>
<P>清单2.struts-config.xml<BR></P><PRE class=code>&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;
&lt;struts-config&gt;
    &lt;!-- Form Bean Definitions --&gt;
    &lt;form-beans&gt;
       &lt;form-bean name="UserForm" type="Samples.Struts.UserForm"/&gt; 
    &lt;/form-beans&gt;
    &lt;!--  Action Mapping Definitions  --&gt;
    &lt;action-mappings&gt;
	&lt;action path="/Startup" 
	    	type="Samples.Struts.begin" 
		scope="request"&gt;
          &lt;forward name="login" path="/index.jsp"/&gt;
  	&lt;/action&gt;
       
	&lt;action path="/LoginForm" forward="/mypage.jsp"/&gt;

        &lt;action path="/login"
            	type="Samples.Struts.Login"
            	name="LoginForm"
            	scope="request"
            	validate="false"
            	&gt;
           &lt;forward
              name="success"
              path="/mypage.jsp"/&gt;
        &lt;/action&gt;
    &lt;/action-mappings&gt;
    &lt;!-- message resources --&gt;
    &lt;message-resources
        parameter="ApplicationResources"
        null="false" /&gt;
&lt;/struts-config&gt;<BR>
</PRE><STRUTS-CONFIG>
<P><STRONG>页面流</STRONG></P>
<P>　　了解开发Struts应用程序的复杂性后，让我们来深入研究页面流及其提供的功能。Beehive的NetUI页面流框架使得开发和维护工作更加轻松和易于管理。清单3是一个页面流控制器（通常简写为Java页面流(Java Page Flow，JPF)）,用于前面所述的LoginProcessFlow应用程序。页面流框架使用一个JPF文件来代替所有的动作类和配置文件。</P>
<P>清单3. LoginPageFlowController.java</P><PRE class=code>package comparison.pageflows;
import org.apache.beehive.netui.pageflow.PageFlowController;
import org.apache.beehive.netui.pageflow.Forward;
import org.apache.beehive.netui.pageflow.annotations.Jpf;

public class LoginPageFlowController extends PageFlowController
{
    @Jpf.Action(
        forwards = { 
            @Jpf.Forward(name = "success", path = "/index.jsp")
        }
    )
    protected Forward begin()
    {
       //initial processing
       return new Forward("success");
    }

    @Jpf.Action(
        forwards = { 
            @Jpf.Forward(name = "success", path = "/mypage.jsp"),
            @Jpf.Forward(name = "failure", path = "/index.jsp")
        }
    )
    protected Forward login()
    {
        //authentication logic
	if(user_authenticated)
	   return new Forward("success");
	else 
           return new Forward("failure")
    }

    @Jpf.Action(
        forwards = { 
            @Jpf.Forward(name = "success", path = "/confirm.jsp")
        }
    )
    protected Forward signup()
    {
        return new Forward("success");
    }

    @Jpf.Action(
        forwards = { 
            @Jpf.Forward(name = "success", path = "/begin.do")
        }
    )    
    protected Forward logOut()
    {
        return new Forward("success");
    }
}
</PRE>
<P>　　清单3清楚地显示了Struts和页面流所要求的开发工作量和维护任务方面的差别。</P>
<P>　　如前所述，JPF是注释驱动的，并且不需要在配置文件中声明跳转。页面流利用Beehive框架自动生成基于注释的配置文件（如：JPF中声明的 @Jpf.Action）。清单3显示了如何在一个JPF文件中处理所有的动作以及以注释的形式声明的跳转。例如begin动作，如果成功的话，就跳转到index.jsp页面。您可以清楚地看到动作如何处理逻辑，以及简单的注释如何处理跳转，这些注释在使用前要进行声明。注释可以在动作级别声明，也可以在页面流级别声明。在Beehive提供的众多注释中，@Jpf.Forward、@Jpf.Controller和 @Jpf.Action是最常用的。对所有注释的讨论不在本文的范围之内。要获得有关的更多信息，请访问 <A href="http://incubator.apache.org/beehive/pageflow/jpf-annotations/pageflow_annotations.html" target=_blank>Page Flow Annotations</A>说明文档。</P>
<P>　　Struts应用程序要求对每个动作使用一个动作类，而使用页面流开发应用程序只需一个JPF文件就足够了。页面流提供一种简单的、单一文件的编程模型。在构建复杂的Web应用程序时，页面流使开发人员能够立即开始开发，而无需经历与学习Struts有关的曲折过程。当遇到因为业务规则的更改而引起的修改时，页面流显得更为灵活。</P>
<P><STRONG>会话数据管理</STRONG></P>
<P>　　页面流提供的编程框架添加了多处改进，刚刚就是注释驱动的自动生成和同步XML配置文件的例子。接下来让我们看看页面流的另一个优点：使用页面流级的(Page Flow-scoped)变量可以更轻松地跨多个动作管理和维护数据。</P>
<P>　　在Struts中，动作类获取会话对象并设置属性（或赋值），其后其他动作会对这些属性进行检索。清单4显示的例子有两个Struts动作类：LoginAction和LogOutAction，它们使用HTTP会话共享一个变量username。</P>
<P>清单4. LoginAction.java和LogOutAction.java</P><PRE class=code>package comparision.struts;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public final class LoginAction extends Action {
 
    public ActionForward execute(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
        throws Exception {
        String username = form.getUserName();
        // get this session
        HttpSession session = request.getSession();
        session.setAttribute("username",  username);
        
        return (mapping.findForward("login"));
    }
}

package comparision.struts;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public final class LogOutAction extends Action {
 
    public ActionForward execute(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
        throws Exception {
        
        // get this session
        HttpSession session = request.getSession();
        String username = session.getAttribute("username");
        //logic to log out user
        return (mapping.findForward("logout"));
    }
}
</PRE>
<P>　　上面的代码清楚地显示了LoginAction和LogOutAction显式地共享username变量的方式，在本例中是使用HTTP会话。跨多个动作类管理这些会话变量可能会相当复杂。</P>
<P>　　相比之下，页面流提供一个以JPF级别声明的简单变量，它可以跨所有的页面流动作共享数据。在清单5的例子中，以页面流级别声明了一个String变量（username），并在login和logout动作中使用它。</P>
<P>清单5. LoginPageFlowController.java</P><PRE class=code>package comparision.pageflows;
import org.apache.beehive.netui.pageflow.PageFlowController;
import org.apache.beehive.netui.pageflow.Forward;
import org.apache.beehive.netui.pageflow.annotations.Jpf;

public class LoginPageFlowController extends PageFlowController
{
    private String username;
    @Jpf.Action(
        forwards = { 
            @Jpf.Forward(name = "success", path = "/index.jsp")
        }
    )
    protected Forward begin()
    {
       //intial processing
       return new Forward("success");
    }

    @Jpf.Action(
        forwards = { 
            @Jpf.Forward(name = "success", path = "/mypage.jsp"),
            @Jpf.Forward(name = "failure", path = "/index.jsp")
        }
    )
    protected Forward login(ProfileForm form)
    {
        username = form.getUsername();
        //authentication logic
	if(user_authenticated)
	return new Forward("success");
	else return new Forward("failure")
    }

    @Jpf.Action(
        forwards = { 
            @Jpf.Forward(name = "success", path = "/confirm.jsp")
        }
    )
    protected Forward signup()
    {
        return new Forward("success");
    }

    @Jpf.Action(
        forwards = { 
            @Jpf.Forward(name = "success", path = "/begin.do")
        }
    )    
    protected Forward logOut(ProfileForm form)
    {
        logout(username);
        return new Forward("success");
    }
}
</PRE>
<P>　　如您所见，共享变量的处理方式简单直观。</P>
<P><STRONG>页面流的优点</STRONG></P>
<P>　　除了易于使用和其他优点之外，这里还列出了页面流的一些重要优点：</P>
<UL>
<LI>页面流是模块化的：简单的Web应用程序可能包括几个页面流，每个页面流处理一部分逻辑相关的功能。可以使用共享页面流或全局页面流来处理异常和未处理的动作。 
<LI>页面流是可嵌套的：页面流嵌套提供细粒度、可重用的模块化，以处理功能和数据。 
<LI>页面流是有状态的：由于可以声明页面流级的变量，所以数据管理非常容易。 
<LI>页面流是注释驱动的：最后但并非最不重要的是，注释驱动的编程是一个受欢迎的改变，它替代了多文件的、分散代码的编程。 </LI></UL>
<P>　　尽管本文仅仅是介绍一些基本知识，但是我希望读者能对这些特性如何有助于创建可管理的大型应用程序有所了解。模块化和嵌套特性对于开发可重用的Web应用程序组件特别有用，而诸如会话数据管理和丰富的数据绑定标签之类的特性则有助于简化编程。</P>
<P><STRONG>集成Struts和页面流</STRONG></P>
<P>　　我希望前面的一节能够说明页面流是Struts的一个有用扩展。现在，让我们来看一看用什么方法能够把页面流的功能添加到现有Struts项目中，或者如何使Struts和页面流应用程序能够互操作。</P>
<P>　　页面流构建于Apache Struts 1.1的基础之上。每个页面流都被编译为一个Struts模块。因此，页面流和Struts 1.1应用程序可以紧密合作。</P>
<P>　　有两种在页面流应用程序中使用Struts组件的方法：</P>
<UL>
<LI>利用Struts互操作性：在页面流应用程序中直接使用Struts组件。 
<LI>利用Struts合并功能：将简单的Struts应用程序导入到新的页面流应用程序中。 </LI></UL>
<P>&nbsp;</P>
<P><STRONG>集成：Struts互操作性</STRONG></P>
<P>　　Struts互操作性功能允许用户使用现有的基于Struts的组件但不修改它们。这允许现有的Struts组件与页面流组件交互，并可以充分利用现有的JSP页面、动作类、表单bean和流配置。</P>
<P>　　Struts和页面流应用程序可以在一个Web应用程序中共存并交互。要从页面流跳转到（纯）Struts模块，只需引用Struts模块中对应的动作即可。反之亦然：在Struts模块中，只需配置一个指向页面流中对应方法的动作，就可以跳转到页面流。</P>
<P>　　页面流可以用作前端控制器处理初始请求，然后把控制权转交给Struts中的动作，这些动作进行某种处理之后会将控制权再传回给页面流。在嵌套页面流中可以看到类似的模式。也可以用这种方式在Struts应用程序和页面流之间共享表单bean。</P>
<UL>
<LI>在页面流中，可以导入并使用由Struts 1.1模块定义的表单bean。因此用户可以快速地利用现有的Struts代码，而不必修改Struts动作类和表单bean。 
<LI>在Struts 1.1动作类中，可以导入和使用页面流定义的表单bean。 </LI></UL>
<P>　　包含Struts模块和页面流（如果它们要进行互操作的话）的文件必须在同一个Web项目中。</P>
<H4>对Struts模块和页面流的要求</H4>
<P>　　这里是对将在同一个Web项目中进行互操作的Struts模块和页面流的要求：</P>
<UL>
<LI>现有的Struts应该是版本1.1。 
<LI>包含Struts模块的文件必须和页面流属于同一个Web项目。 
<LI>JAR文件中所有已编译的、与Struts相关的类（字节码）必须存放在WEB-INF/lib目录下。 
<LI>为避免命名冲突，Struts模块名称和页面流目录名称在整个Web项目中必须是惟一的。 
<LI>同一个页面流目录下的JSP页面总是在页面流JPF上下文中呈现。调用动作和呈现在Struts模块上下文中的JSP页面应当存放到其他目录下，比如<PROJECT-ROOT>/strutsModule目录下。 
<LI>如果页面流控制器类和Struts动作类共享同一表单bean，可以将表单bean定义为内部类，也可以将其定义为外部Java文件。将表单bean类导入页面流JPF和将要使用它的Struts动作类中。 
<LI>在共享表单bean时，Struts模块的XML文件中 <FORM-BEAN name="...">属性值必须与页面流的jpf-struts-config-<PAGEFLOW-NAME>.xml文件中生成的name属性值精确匹配。 
<LI>默认情况下，页面流的范围是从表单bean实例到请求。共享数据的最容易的方式就是在Struts和页面流之间传递会话级(session-scoped)表单bean。这可以通过使用Struts合并功能将（为页面流生成的）Struts配置XML合并到会话级表单bean中来实现。 
<LI>如果一个Web项目综合使用了页面流和Struts模块，必须通过编辑项目的 /WEB-INF/web.xml文件注册每个Struts模块。 </LI></UL>
<H4>互操作性的一个例子</H4>
<P>　　本例显示了页面流动作如何调用Struts动作以及Struts动作如何调用页面流动作。本例的目的仅仅是显示互操作性，因此代码并不完整。PageFlowActionA将控制权和会话级表单bean传递给StrutsActionA。StrutsActionA进行某种处理并跳转到Page.jsp，后者包含一个Struts动作StrutsActionB。StrutsActionB将控制权传回PageFlowActionB。 </P>
<P><IMG style="WIDTH: 252px; HEIGHT: 194px" height=175 src="http://dev2dev.bea.com.cn/images/051018/0510180102.gif" width=220> </P>
<P><STRONG>集成：Struts合并</STRONG></P>
<P>　　Struts合并可以将简单的Struts应用程序转换为页面流应用程序。这种方法是将简单的Struts应用程序迁移到页面流框架中的一种快捷方式。</P>
<P>　　Struts合并功能不同于Struts互操作功能。对于Struts合并功能，可以在控制器级别指定Struts XML配置文件，如下： </P><PRE class=code>/**
 * @jpf:controller struts-merge="/WEB-INF/struts-config-merge-example.xml"
 */
 public class ExampleStrutsMergeController extends PageFlowController
 {
 ...
 }
</PRE>
<P>　　这一步允许现有的纯Struts的XML配置文件与页面流的生成的jpf-struts-config-<PAGEFLOW>.xml文件合并（在项目编译时）。Struts合并功能的目的是重写页面流的默认值，或者为页面流指定页面流注释或其属性不提供的设置。Struts合并文件通常很小，而且仅仅修改页面流及其动作和表单bean。尽管可以将动作映射添加到Struts合并文件中，但并不推荐这种实践。Struts动作映射应当使用 @jpf注释在页面流的 .jpf文件中声明，然后（如果需要的话）使用Struts合并文件修改。</P>
<P>　　还可以使用struts合并功能，将纯Struts应用程序中的配置数据读入页面流应用程序的配置文件。一般来说，页面流的配置文件完全由应用程序的Java源文件（具体地说是由散布于控制器文件的元数据注释）生成。但是，如果要把Struts模块集成到应用程序中，配置文件既可以由Java源文件生成，也可以由Struts模块的配置文件生成。如果是后者，就可以在生成的配置文件中灵活地更改或添加任何标签。例如，可以将一个动作表单的默认作用域从请求级重写为会话级。为此，只需创建重写页面流配置文件的Struts配置文件的适当部分，然后从页面流的Java源文件中引用这个重写后的文件，正如上面的例子所示。</P>
<P>　　考虑到诸如现有Struts的复杂性、时间和工作量等因素，可以在页面流中综合使用Struts合并功能和Struts互操作性功能，以取得最佳效果。</P>
<P><STRONG>Beehive</STRONG></P>
<P>　　Apache Beehive Project（蜂巢计划）不只包含页面流。它有三个子项目：</P>
<UL>
<LI>NetUI PageFlows（NetUI页面流）：本文中介绍的Web应用程序框架。 
<LI>Controls：一个帮助程序员构建组件（如：JavaBean，它将元数据合并到它的编程模型中）的轻量级组件框架。该项目也提供一些预制的控件。 
<LI>Web Services：一个针对JSR-181规范所描述的Web services的注释驱动的编程模型。 </LI></UL>　　Java页面流全面支持控件，控件是访问企业资源和包装业务逻辑的简单的编程模型抽象。Java控件使开发人员可以在一个简单直观的环境中使用方法、事件和属性访问J2EE平台的强大功能（安全性、事务和异步），而且能够构建遵从面向服务架构（SOA）的最佳实践的Web应用程序。例如，可以在页面流中使用一个简单的注释来声明一个Web service控件，如下所示： <PRE class=code>@Control 
public MyWebService myWebService;
</PRE>
<P>　　环境会自动将控件插入页面流中。</P>
<P>　　与XMLBeans相结合，Beehive就可以提供无缝地构建和部署企业SOA的基础架构。Beehive 1.0预定在本月底发布，2.0的规划正在进行中。可以使用诸如BEA WebLogic Workshop之类的IDE或Pollinate项目中开发的Eclipse插件来开发Beehive应用程序。</P>
<P><STRONG>结束语</STRONG></P>
<P>　　页面流非常坚定地承诺要使企业软件的开发和维护变得更加轻松快捷。在本文中，我们介绍了页面流超越Struts的一些优点、页面流如何增强现有的Struts应用程序，以及可以用于升级和无缝构建企业级SOA业务应用程序的几种方法，包括Struts互操作性和Struts合并。</P>
<P><STRONG>参考资料</STRONG></P>
<UL>
<LI><A href="http://incubator.apache.org/beehive/index.html" target=_blank>Apache Beehive</A>——官方主页 
<LI><A href="http://incubator.apache.org/beehive/pageflow/pageflow_overview.html" target=_blank>Apache Beehive Page Flow说明文档</A> 
<LI><A href="http://struts.apache.org/" target=_blank>Apache Struts</A> </LI></UL>
<P><STRONG>原文出处</STRONG></P>
<P><A href="http://dev2dev.bea.com/pub/a/2005/07/pageflows.html" target=_blank>http://dev2dev.bea.com/pub/a/2005/07/pageflows.html</A></P><img src ="http://www.blogjava.net/Victor/aggbug/18070.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-11-04 08:41 <a href="http://www.blogjava.net/Victor/articles/18070.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ajax简介(转自EBA)</title><link>http://www.blogjava.net/Victor/articles/18069.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 04 Nov 2005 00:35:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/18069.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/18069.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/18069.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/18069.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/18069.html</trackback:ping><description><![CDATA[时间：2005-11-01<BR>作者：<A href="http://dev2dev.bea.com.cn/author/353.html">David Teare</A><BR>本文关键字：<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=ajax">ajax</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=dhtml">dhtml</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=dwr">dwr</A>,&nbsp;<A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords= javascript"> javascript</A><BR><BR>作为J2EE开发人员，我们似乎经常关注“后端机制（backend mechanics）”。我们通常会忘记，J2EE的主要成功之处在Web应用程序方面；许多原因使得人们喜欢利用Web开发应用程序，但主要还是因为其易于部署的特点允许站点以尽可能低的成本拥有上百万的用户。遗憾的是，在过去几年中，我们在后端投入了太多的时间，而在使我们的Web用户界面对用户自然和响应灵敏方面却投入不足。
<P>　　本文介绍一种方法，Ajax，使用它可以构建更为动态和响应更灵敏的Web应用程序。该方法的关键在于对浏览器端的JavaScript、DHTML和与服务器异步通信的组合。本文也演示了启用这种方法是多么简单：利用一个Ajax框架（指DWR）构造一个应用程序，它直接从浏览器与后端服务进行通信。如果使用得当，这种强大的力量可以使应用程序更加自然和响应灵敏，从而提升用户的浏览体验。</P>
<P>　　该应用程序中所使用的示例代码已打包为单独的WAR文件，可供下载。</P>
<P><STRONG>简介</STRONG></P>
<P>　　术语Ajax用来描述一组技术，它使浏览器可以为用户提供更为自然的浏览体验。在Ajax之前，Web站点强制用户进入提交/等待/重新显示范例，用户的动作总是与服务器的“思考时间”同步。Ajax提供与服务器异步通信的能力，从而使用户从请求/响应的循环中解脱出来。借助于Ajax，可以在用户单击按钮时，使用JavaScript和DHTML立即更新UI，并向服务器发出异步请求，以执行更新或查询数据库。当请求返回时，就可以使用JavaScript和CSS来相应地更新UI，而不是刷新整个页面。最重要的是，用户甚至不知道浏览器正在与服务器通信：Web站点看起来是即时响应的。</P>
<P>　　虽然Ajax所需的基础架构已经出现了一段时间，但直到最近异步请求的真正威力才得到利用。能够拥有一个响应极其灵敏的Web站点确实激动人心，因为它最终允许开发人员和设计人员使用标准的HTML/CSS/JavaScript堆栈创建“桌面风格的（desktop-like）”可用性。</P>
<P>　　通常，在J2EE中，开发人员过于关注服务和持久性层的开发，以至于用户界面的可用性已经落后。在一个典型的J2EE开发周期中，常常会听到这样的话，“我们没有可投入UI的时间”或“不能用HTML实现”。但是，以下Web站点证明，这些理由再也站不住脚了：</P>
<UL>
<LI><A href="http://backpackit.com/" target=_blank>BackPack</A> 
<LI><A href="http://www.google.com/webhp?complete=1&amp;hl=en" target=_blank>Google Suggest</A> 
<LI><A href="http://maps.google.com/" target=_blank>Google Maps</A> 
<LI><A href="http://www.palmsphere.com/" target=_blank>PalmSphere</A> </LI></UL>
<P>　　所有这些Web站点都告诉我们，Web应用程序不必完全依赖于从服务器重新载入页面来向用户呈现更改。一切似乎就在瞬间发生。简而言之，在涉及到用户界面的响应灵敏度时，基准设得更高了。</P>
<P><STRONG>定义Ajax</STRONG></P>
<P>　　Adaptive Path公司的Jesse James Garrett这样<A href="http://www.adaptivepath.com/publications/essays/archives/000385.php" target=_blank>定义Ajax</A>：</P>
<P>　　Ajax不是一种技术。实际上，它由几种蓬勃发展的技术以新的强大方式组合而成。Ajax包含：</P>
<UL>
<LI>基于<A href="http://www.w3.org/TR/xhtml1/" target=_blank>XHTML</A>和<A href="http://www.w3.org/Style/CSS/" target=_blank>CSS</A>标准的表示； 
<LI>使用<A href="http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/introduction.html" target=_blank>Document Object Model</A>进行动态显示和交互； 
<LI>使用XMLHttpRequest与服务器进行异步通信； 
<LI>使用JavaScript绑定一切。 </LI></UL>
<P>　　这非常好，但为什么要以Ajax命名呢？其实术语Ajax是由Jesse James Garrett创造的，他说它是“Asynchronous JavaScript + XML的简写”。</P>
<P><STRONG>Ajax的工作原理</STRONG></P>
<P>　　Ajax的核心是JavaScript对象XmlHttpRequest。该对象在Internet Explorer 5中首次引入，它是一种支持异步请求的技术。简而言之，XmlHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应，而不阻塞用户。</P>
<P>　　在创建Web站点时，在客户端执行屏幕更新为用户提供了很大的灵活性。下面是使用Ajax可以完成的功能：</P>
<UL>
<LI>动态更新购物车的物品总数，无需用户单击Update并等待服务器重新发送整个页面。 
<LI>提升站点的性能，这是通过减少从服务器下载的数据量而实现的。例如，在Amazon的购物车页面，当更新篮子中的一项物品的数量时，会重新载入整个页面，这必须下载32K的数据。如果使用Ajax计算新的总量，服务器只会返回新的总量值，因此所需的带宽仅为原来的百分之一。 
<LI>消除了每次用户输入时的页面刷新。例如，在Ajax中，如果用户在分页列表上单击Next，则服务器数据只刷新列表而不是整个页面。 
<LI>直接编辑表格数据，而不是要求用户导航到新的页面来编辑数据。对于Ajax，当用户单击Edit时，可以将静态表格刷新为内容可编辑的表格。用户单击Done之后，就可以发出一个Ajax请求来更新服务器，并刷新表格，使其包含静态、只读的数据。 </LI></UL>
<P>　　一切皆有可能！但愿它能够激发您开始开发自己的基于Ajax的站点。然而，在开始之前，让我们介绍一个现有的Web站点，它遵循传统的提交/等待/重新显示的范例，我们还将讨论Ajax如何提升用户体验。</P>
<P><STRONG>Ajax可用于那些场景？——一个例子：MSN Money页面</STRONG></P>
<P>　　前几天，在浏览MSN Money页面的时候，有一篇<A href="http://moneycentral.msn.com/content/Investing/Realestate/P63633.asp" target=_blank>关于房地产投资的文章</A>引起了我的好奇心。我决定使用站点的“Rate this article”（评价本文）功能，鼓励其他的用户花一点时间来阅读这篇文章。在我单击vote按钮并等待了一会儿之后，整个页面被刷新，在原来投票问题所在的地方出现了一个漂亮的感谢画面。</P>
<P><IMG height=63 src="http://dev2dev.bea.com.cn/images/051101/0511010101.jpg" width=303> </P>
<P>　　而Ajax能够使用户的体验更加愉快，它可以提供响应更加灵敏的UI，并消除页面刷新所带来的闪烁。目前，由于要刷新整个页面，需要传送大量的数据，因为必须重新发送整个页面。如果使用Ajax，服务器可以返回一个包含了感谢信息的500字节的消息，而不是发送26,813字节的消息来刷新整个页面。即使使用的是高速Internet，传送26K和1/2K的差别也非常大。同样重要的是，只需要刷新与投票相关的一小节，而不是刷新整个屏幕。</P>
<P>　　让我们利用Ajax实现自己的基本投票系统。</P>
<P><STRONG>原始的Ajax：直接使用XmlHttpRequest</STRONG></P>
<P>　　如上所述，Ajax的核心是JavaScript对象XmlHttpRequest。下面的示例文章评价系统将带您熟悉Ajax的底层基本知识：<A href="http://tearesolutions.com/ajax-demo/raw-ajax.html" target=_blank>http://tearesolutions.com/ajax-demo/raw-ajax.html</A>。注：如果您已经在本地WebLogic容器中安装了<A href="http://dev2dev.bea.com/2005/08/ajax-demo.war">ajax-demo.war</A>，可以导航到<A href="http://localhost:7001/ajax-demo/raw-ajax.html" target=_blank>http://localhost:7001/ajax-demo/raw-ajax.html</A>，</P>
<P>　　浏览应用程序，参与投票，并亲眼看它如何运转。熟悉了该应用程序之后，继续阅读，进一步了解其工作原理细节。</P>　　首先，您拥有一些简单的定位点标记，它连接到一个JavaScriptcastVote(rank)函数。 <PRE class=code>function castVote(rank) {
  var url = "/ajax-demo/static-article-ranking.html";
  var callback = processAjaxResponse;
  executeXhr(callback, url);
}
</PRE>
<P>　　该函数为您想要与之通信的服务器资源创建一个URL并调用内部函数executeXhr，提供一个回调JavaScript函数，一旦服务器响应可用，该函数就被执行。由于我希望它运行在一个简单的Apache环境中，“cast vote URL”只是一个简单的HTML页面。在实际情况中，被调用的URL将记录票数并动态地呈现包含投票总数的响应。</P>　　下一步是发出一个XmlHttpRequest请求： <PRE class=code>function executeXhr(callback, url) {
  // branch for native XMLHttpRequest object
  if (window.XMLHttpRequest) {
    req = new XMLHttpRequest();
    req.onreadystatechange = callback;
    req.open("GET", url, true);
    req.send(null);
  } // branch for IE/Windows ActiveX version
  else if (window.ActiveXObject) {
    req = new ActiveXObject("Microsoft.XMLHTTP");
    if (req) {
      req.onreadystatechange = callback;
      req.open("GET", url, true);
      req.send();
    }
  }
}

</PRE>
<P>　　如您所见，执行一个XmlHttpRequest并不简单，但非常直观。和平常一样，在JavaScript领域，大部分的工作量都花在确保浏览器兼容方面。在这种情况下，首先要确定XmlHttpRequest是否可用。如果不能用，很可能要使用Internet Explorer，这样就要使用所提供的ActiveX实现。</P>
<P>executeXhr()方法中最关键的部分是这两行： </P><PRE class=code>req.onreadystatechange = callback;
req.open("GET", url, true);
</PRE>
<P>　　第一行定义了JavaScript回调函数，您希望一旦响应就绪它就自动执行，而req.open()方法中所指定的“true”标志说明您想要异步执行该请求。</P>　　一旦服务器处理完XmlHttpRequest并返回给浏览器，使用req.onreadystatechange指派所设置的回调方法将被自动调用。 <PRE class=code>function processAjaxResponse() {
  // only if req shows "loaded"
  if (req.readyState == 4) {
    // only if "OK"
    if (req.status == 200) {
      502 502'votes').innerHTML = req.responseText;
    } else {
      alert("There was a problem retrieving the XML data:
" +
      req.statusText);
    }
  }
} 
</PRE>
<P>　　该代码相当简洁，并且使用了几个幻数，这使得难以一下子看出发生了什么。为了弄清楚这一点，下面的表格（引用自<A href="http://developer.apple.com/internet/webcontent/xmlhttpreq.html" target=_blank>http://developer.apple.com/internet/webcontent/xmlhttpreq.html</A>）列举了常用的XmlHttpRequest对象属性。</P>
<TABLE cellSpacing=1 cellPadding=0 width="80%" bgColor=#cccccc border=0>
<TBODY>
<TR bgColor=#ffffff>
<TD height=22>
<P><STRONG>属性</STRONG></P></TD>
<TD>
<P><STRONG>描述</STRONG></P></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top height=22>
<P>onreadystatechange</P></TD>
<TD vAlign=top>
<P>每次状态改变所触发事件的事件处理程序</P></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top height=22>
<P>readyState</P></TD>
<TD vAlign=top>
<P>对象状态值： 
<UL>
<LI>0 = 未初始化（uninitialized） 
<LI>1 = 正在加载（loading） 
<LI>2 = 加载完毕（loaded） 
<LI>3 = 交互（interactive） 
<LI>4 = 完成（complete） </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top height=22>
<P>responseText</P></TD>
<TD vAlign=top>
<P>从服务器进程返回的数据的字符串形式</P></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top height=22>
<P>responseXML</P></TD>
<TD vAlign=top>
<P>从服务器进程返回的DOM兼容的文档数据对象</P></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top height=22>
<P>status</P></TD>
<TD vAlign=top>
<P>从服务器返回的数字代码，比如404（未找到）或200（就绪）</P></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top height=22>
<P>statusText</P></TD>
<TD vAlign=top>
<P>伴随状态码的字符串信息</P></TD></TR></TBODY></TABLE>
<P>　　现在processVoteResponse()函数开始显示出其意义了。它首先检查XmlHttpRequest的整体状态以保证它已经完成（readyStatus == 4），然后根据服务器的设定询问请求状态。如果一切正常（status == 200）,就使用innerHTML属性重写DOM的“votes”节点的内容。</P>
<P>　　既然您亲眼看到了XmlHttpRequest对象是如何工作的，就让我们利用一个旨在简化JavaScript与Java应用程序之间的异步通信的框架来对具体的细节进行抽象。</P>
<P><STRONG>Ajax: DWR方式</STRONG></P>
<P>　　按照与文章评价系统相同的流程，我们将使用Direct Web Remoting（DWR）框架实现同样的功能。</P>
<P>　　假定文章和投票结果存储在一个数据库中，使用某种对象/关系映射技术来完成抽取工作。为了部署起来尽可能地简单，我们不会使用数据库进行持久性存储。此外，为使应用程序尽可能通用，也不使用Web框架。相反，应用程序将从一个静态HTML文件开始，可以认为它由服务器动态地呈现。除了这些简化措施，应用程序还应该使用Spring Framework关联一切，以便轻松看出如何在一个“真实的”应用程序中使用DWR。</P>
<P>　　现在应该下载示例应用程序并熟悉它。该应用程序被压缩为标准的WAR文件，因此您可以把它放置到任何一个Web容器中——无需进行配置。部署完毕之后，就可以导航到<A href="http://localhost:7001/ajax-demo/dwr-ajax.html" target=_blank>http://localhost:7001/ajax_demo/dwr-ajax.html</A>来运行程序。</P>
<P>　　可以查看<A href="http://dev2dev.bea.com/2005/08/source.html" target=_blank>HTML 源代码</A>，了解它如何工作。给人印象最深的是，代码如此简单——所有与服务器的交互都隐藏在JavaScript对象ajaxSampleSvc的后面。更加令人惊讶的是，ajaxSampleSvc服务不是由手工编写而是完全自动生成的！让我们继续，看看这是如何做到的。</P>
<P><STRONG>引入DWR</STRONG></P>
<P>　　如同在“原始的Ajax”一节所演示的那样，直接使用XmlHttpRequest创建异步请求非常麻烦。不仅JavaScript代码冗长，而且必须考虑服务器端为定位Ajax请求到适当的服务所需做的工作，并将结果封送到浏览器。</P>
<P>　　设计DWR的目的是要处理将Web页面安装到后端服务上所需的所有信息管道。它是一个Java框架，可以很轻松地将它插入到Web应用程序中，以便JavaScript代码可以调用服务器上的服务。它甚至直接与Spring Framework集成，从而允许用户直接向Web客户机公开bean。</P>
<P>　　DWR真正的巧妙之处是，在用户配置了要向客户机公开的服务之后，它使用反射来生成JavaScript对象，以便Web页面能够使用这些对象来访问该服务。然后Web页面只需接合到生成的JavaScript对象，就像它们是直接使用服务一样；DWR无缝地处理所有有关Ajax和请求定位的琐碎细节。</P>
<P>　　让我们仔细分析一下示例代码，弄清它是如何工作的。</P>
<P><STRONG>应用程序细节：DWR分析</STRONG></P>
<P>　　关于应用程序，首先要注意的是，它是一个标准的Java应用程序，使用分层架构（Layered Architecture）设计模式。使用DWR通过JavaScript公开一些服务并不影响您的设计。 </P>
<P><IMG height=344 src="http://dev2dev.bea.com.cn/images/051101/0511010102.jpg" width=209> </P>
<P>　　下面是一个简单的Java服务，我们将使用DWR框架直接将其向JavaScript代码公开：</P><PRE class=code>package com.tearesolutions.service;

public interface AjaxSampleSvc { 
  Article castVote(int rank);
}
</PRE>
<P>　　这是一个被简化到几乎不可能的程度的例子，其中只有一篇文章可以投票。该服务由Spring管理，它使用的bean名是ajaxSampleSvc，它的持久性需求则依赖于ArticleDao。详情请参见applicationContext.xml。</P>
<P>　　为了把该服务公开为JavaScript对象，需要配置DWR，添加dwr.xml文件到WEB-INF目录下： </P><PRE class=code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE dwr PUBLIC
 "-//GetAhead Limited//DTD Direct Web Remoting 0.4//EN"
 "http://www.getahead.ltd.uk/dwr/dwr.dtd"&gt;
	
&lt;dwr&gt;
 &lt;allow&gt;
  &lt;create creator="spring" javascript="ajaxSampleSvc"&gt;
   &lt;param name="beanName" value="ajaxSampleSvc" /&gt;
  &lt;/create&gt;
  &lt;convert converter="bean" match="com.tearesolutions.model.Article"/&gt;
  &lt;exclude method="toString"/&gt;
  &lt;exclude method="setArticleDao"/&gt;
 &lt;/allow&gt;
&lt;/dwr&gt;
</PRE>
<P>　　dwr.xml文件告诉DWR哪些服务是要直接向JavaScript代码公开的。注意，已经要求公开Spring bean ajaxSampleSvc。DWR将自动找到由应用程序设置的SpringApplicationContext。为此，必须使用标准的servlet过滤器ContextLoaderListener来初始化Spring ApplicationContext。</P>　　DWR被设置为一个servlet，所以把它的定义添加到web.xml： <PRE class=code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD 
 Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"&gt;

&lt;web-app&gt;
 &lt;display-name&gt;Ajax Examples&lt;/display-name&gt;

 &lt;listener&gt;
  &lt;listener-class&gt;
      org.springframework.web.context.ContextLoaderListener
  &lt;/listener-class&gt;
 &lt;/listener&gt;
	
 &lt;servlet&gt;
  &lt;servlet-name&gt;ajax_sample&lt;/servlet-name&gt;
  &lt;servlet-class&gt;com.tearesolutions.web.AjaxSampleServlet&lt;/servlet-class&gt;
  &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
 &lt;/servlet&gt;

 &lt;servlet&gt;
  &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;
  &lt;display-name&gt;DWR Servlet&lt;/display-name&gt;
  &lt;description&gt;Direct Web Remoter Servlet&lt;/description&gt;
  &lt;servlet-class&gt;uk.ltd.getahead.dwr.DWRServlet&lt;/servlet-class&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;debug&lt;/param-name&gt;
   &lt;param-value&gt;true&lt;/param-value&gt;
  &lt;/init-param&gt;
 &lt;/servlet&gt;

 &lt;servlet-mapping&gt;
  &lt;servlet-name&gt;ajax_sample&lt;/servlet-name&gt;
  &lt;url-pattern&gt;/ajax_sample&lt;/url-pattern&gt;
 &lt;/servlet-mapping&gt;

 &lt;servlet-mapping&gt;
  &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;
  &lt;url-pattern&gt;/dwr/*&lt;/url-pattern&gt;
 &lt;/servlet-mapping&gt;
&lt;/web-app&gt;
</PRE>
<P>　　做完这些之后，可以加载<A href="http://localhost:7001/ajax-demo/dwr" target=_blank>http://localhost:7001/ajax-demo/dwr</A>，看看哪些服务可用。结果如下：</P>
<P><IMG height=102 src="http://dev2dev.bea.com.cn/images/051101/0511010103.jpg" width=305></P>
<P>图3. 可用的服务</P>　　单击ajaxSampleSvc链接，查看有关如何在HTML页面内直接使用服务的示例实现。其中包含的两个JavaScript文件完成了大部分的功能： <PRE class=code>&lt;script type='text/javascript' 
   src='/ajax-demo/dwr/interface/ajaxSampleSvc.js'&gt;&lt;/script&gt;
&lt;script type='text/javascript' 
   src='/ajax-demo/dwr/engine.js'&gt;&lt;/script&gt;
</PRE>
<P>ajaxSampleSvc.js是动态生成的：</P><PRE class=code>function ajaxSampleSvc() { }

ajaxSampleSvc.castVote = function(callback, p0)
{ 
  DWREngine._execute(callback, '/ajax-demo/dwr', 
 'ajaxSampleSvc', 'castVote', p0);
}
</PRE>
<P>　　现在可以使用JavaScript对象ajaxSampleSvc替换所有的XmlHttpRequest代码，从而重构raw-ajax.html文件。可以在dwr-ajax.html文件中看到改动的结果；下面是新的JavaScript函数：</P><PRE class=code>function castVote(rank) {
  ajaxSampleSvc.castVote(processResponse, rank);
}
function processResponse(data) {
 var voteText = "<P><STRONG>Thanks for Voting!</STRONG></P>"
    + "<P>Current ranking: " + data.voteAverage 
    + " out of 5</P>" 
    + "<P>Number of votes placed: " 
    + data.numberOfVotes + "</P>";
 502 502'votes').innerHTML = voteText;       
}
</PRE>
<P>　　惊人地简单，不是吗？由ajaxSampleSvc对象返回的Article域对象序列化为一个JavaScript对象，允许在它上面调用诸如numberOfVotes()和voteAverage()之类的方法。在动态生成并插入到DIV元素“votes”中的HTML代码内使用这些数据。</P>
<P><STRONG>下一步工作</STRONG></P>
<P>　　　在后续文章中，我将继续有关Ajax的话题，涉及下面这些方面：</P>
<UL>
<LI>Ajax最佳实践 </LI></UL>
<P>　　像许多技术一样，Ajax是一把双刃剑。对于一些用例，其应用程序其实没有必要使用Ajax，使用了反而有损可用性。我将介绍一些不适合使用的模式，突出说明Ajax的一些消极方面，并展示一些有助于缓和这些消极方面的机制。例如，对<A href="http://www.netflix.com/BrowseSelection">Netflix电影浏览器</A>来说，Ajax是合适的解决方案吗？或者，如何提示用户确实出了一些问题，而再次单击按钮也无济于事？</P>
<UL>
<LI>管理跨请求的状态 </LI></UL>
<P>　　在使用Ajax时，最初的文档DOM会发生一些变化，并且有大量的页面状态信息存储在客户端变量中。当用户跟踪一个链接到应用程序中的另一个页面时，状态就丢失了。当用户按照惯例单击Back按钮时，呈现给他们的是缓存中的初始页面。这会使用户感到非常迷惑！</P>
<UL>
<LI>调试技巧 </LI></UL>
<P>　　使用JavaScript在客户端执行更多的工作时，如果事情不按预期方式进行，就需要一些调试工具来帮助弄清出现了什么问题。</P>
<P><STRONG>结束语</STRONG></P>
<P>　　本文介绍了Ajax方法，并展示了如何使用它来创建一个动态且响应灵敏的Web应用程序。通过使用DWR框架，可以轻松地把Ajax融合到站点中，而无需担心所有必须执行的实际管道工作。</P>
<P>　　特别感谢Getahead IT咨询公司的Joe Walker和他的团队开发出DWR这样神奇的工具。感谢你们与世界共享它！</P>
<P><STRONG>下载</STRONG></P>
<P>　　本文中演示的应用程序源代码可供下载：<A href="http://dev2dev.bea.com/2005/08/ajax-demo.war" target=_blank>ajax-demo.war</A>（1.52 MB）。</P><img src ="http://www.blogjava.net/Victor/aggbug/18069.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-11-04 08:35 <a href="http://www.blogjava.net/Victor/articles/18069.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Log4J配置之全套功能</title><link>http://www.blogjava.net/Victor/articles/14073.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Mon, 26 Sep 2005 03:19:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/14073.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/14073.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/14073.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/14073.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/14073.html</trackback:ping><description><![CDATA[<H1 class=block_title><A id=viewpost.ascx_TitleUrl href="http://www.donews.net/szriver/archive/2004/07/30/54969.aspx"><FONT color=#4a664d>Log4J配置之全套功能</FONT></A></H1>
<DIV class=post>
<DIV class=postcontent>
<P>LOG4J的配置之简单使它遍及于越来越多的应用中了：</P>
<P>Log4J配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。择其一二使用就够用了，</P><BR>
<P>log4j.rootLogger=DEBUG,CONSOLE,A1,im<BR>log4j.addivity.org.apache=true<BR><BR><BR><BR># 应用于控制台<BR><BR>log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender<BR>log4j.appender.Threshold=DEBUG<BR>log4j.appender.CONSOLE.Target=System.out<BR>log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR>#log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD] n%c[CATEGORY]%n%m[MESSAGE]%n%n<BR><BR><BR>#应用于文件</P>
<P>log4j.appender.FILE=org.apache.log4j.FileAppender<BR>log4j.appender.FILE.File=file.log<BR>log4j.appender.FILE.Append=false<BR>log4j.appender.FILE.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR># Use this layout for LogFactor 5 analysis<BR><BR><BR><BR># 应用于文件回滚<BR><BR>log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender<BR>log4j.appender.ROLLING_FILE.Threshold=ERROR<BR>log4j.appender.ROLLING_FILE.File=rolling.log<BR>log4j.appender.ROLLING_FILE.Append=true<BR>log4j.appender.ROLLING_FILE.MaxFileSize=10KB<BR>log4j.appender.ROLLING_FILE.MaxBackupIndex=1<BR>log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR><BR><BR>#应用于socket<BR>log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender<BR>log4j.appender.SOCKET.RemoteHost=localhost<BR>log4j.appender.SOCKET.Port=5001<BR>log4j.appender.SOCKET.LocationInfo=true<BR># Set up for Log Facter 5<BR>log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n<BR><BR><BR># Log Factor 5 Appender<BR>log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender<BR>log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000<BR><BR><BR><BR># 发送日志给邮件<BR><BR>log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender<BR>log4j.appender.MAIL.Threshold=FATAL<BR>log4j.appender.MAIL.BufferSize=10<BR><A href="mailto:log4j.appender.MAIL.From=web@www.wuset.com"><FONT color=#4a664d>log4j.appender.MAIL.From=web@www.wuset.com</FONT></A><BR>log4j.appender.MAIL.SMTPHost=www.wusetu.com<BR>log4j.appender.MAIL.Subject=Log4J Message<BR><A href="mailto:log4j.appender.MAIL.To=web@www.wusetu.com"><FONT color=#4a664d>log4j.appender.MAIL.To=web@www.wusetu.com</FONT></A><BR>log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR><BR><BR><BR># 用于数据库<BR>log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender<BR>log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test<BR>log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver<BR>log4j.appender.DATABASE.user=root<BR>log4j.appender.DATABASE.password=<BR>log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')<BR>log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR><BR><BR>log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender<BR>log4j.appender.A1.File=SampleMessages.log4j<BR>log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'<BR>log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout<BR><BR>#自定义Appender<BR><BR>log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender<BR><BR>log4j.appender.im.host = mail.cybercorlin.net<BR>log4j.appender.im.username = username<BR>log4j.appender.im.password = password<BR>log4j.appender.im.recipient = corlin@cybercorlin.net<BR><BR>log4j.appender.im.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR></P></DIV></DIV><img src ="http://www.blogjava.net/Victor/aggbug/14073.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-09-26 11:19 <a href="http://www.blogjava.net/Victor/articles/14073.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Log4j 学习笔记</title><link>http://www.blogjava.net/Victor/articles/13704.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Thu, 22 Sep 2005 00:47:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/13704.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/13704.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/13704.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/13704.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/13704.html</trackback:ping><description><![CDATA[<TABLE class=main width="100%" align=center>
<TBODY>
<TR>
<TD align=middle>转贴自：<A href="http://zooo.51.net/heavyz_cs/notebook/log4j.html">http://zooo.51.net/heavyz_cs/notebook/log4j.html</A><BR><BR>
<TABLE class=title-bg align=center>
<TBODY>
<TR class=title>
<TD noWrap align=middle><FONT size=+1><B>Log4j 学习笔记</B></FONT></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD noWrap align=middle><BR>by heavyz<BR>2003-04-15<BR><BR></TD></TR>
<TR>
<TD>
<TABLE class=intro-bg cellSpacing=1 align=center>
<TBODY>
<TR class=main>
<TD align=middle>
<HR noShade SIZE=1>
</TD></TR>
<TR class=main>
<TD>
<TABLE class=main cellSpacing=0 width="90%" align=center>
<TBODY>
<TR>
<TD>
<P>
<CENTER><IMG src="http://zooo.51.net/heavyz_cs/notebook/res/log4j-logo.jpg"></CENTER>
<P></P>
<P>
<CENTER>Log4j主页：<A href="http://jakarta.apache.org/log4j">http://jakarta.apache.org/log4j</A> </CENTER>
<P></P></TD></TR></TBODY></TABLE></TD></TR>
<TR class=main>
<TD align=middle>
<HR noShade SIZE=1>
</TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD>
<TABLE class=chapter-bg cellSpacing=1 align=center>
<TBODY>
<TR class=chapter>
<TD noWrap><B>Index</B></TD></TR>
<TR class=main>
<TD>
<P>
<OL>
<LI>Log4j的类图 
<LI>Logger：日志写出器 
<OL>
<LI>Logger的输出方法 
<LI>Logger的命名规则 
<LI>Log level 
<LI>示例代码 
<LI>关于logger的两点说明 </LI></OL>
<LI>Appender：日志目的地 
<OL>
<LI>ConsoleAppender 
<LI>FileAppender 
<LI>RollingFileAppender </LI></OL>
<LI>Layout：日志格式化器 
<OL>
<LI>PatternLayout 
<LI>patterns in PatternLayout </LI></OL>
<LI>Configuration：配置 
<OL>
<LI>默认的log4j初始化过程 
<LI>BasicConfigurator.configure() 
<LI>xml格式的log4j配置文件概述 
<LI>在xml文件中配置appender和layout 
<LI>我自己的一个使用xml文件配置log4j环境的很简单的例子 </LI></OL>
<LI>Log4j的编码习惯 
<LI>参考资料 </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=chapter-bg cellSpacing=1 align=center>
<TBODY>
<TR class=chapter>
<TD noWrap><B>Log4j的类图</B></TD></TR>
<TR class=main>
<TD>
<P>
<CENTER><IMG src="http://zooo.51.net/heavyz_cs/notebook/res/log4j-classes.gif"></CENTER>
<P></P>
<P>
<OL>
<LI>Logger - 日志写出器，供程序员输出日志信息 
<LI>Appender - 日志目的地，把格式化好的日志信息输出到指定的地方去 
<OL>
<LI>ConsoleAppender - 目的地为控制台的Appender 
<LI>FileAppender - 目的地为文件的Appender 
<LI>RollingFileAppender - 目的地为大小受限的文件的Appender </LI></OL>
<LI>Layout - 日志格式化器，用来把程序员的logging request格式化成字符串 
<OL>
<LI>PatternLayout - 用指定的pattern格式化logging request的Layout </LI></OL></LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=chapter-bg cellSpacing=1 align=center>
<TBODY>
<TR class=chapter>
<TD noWrap><B>Logger：日志写出器</B></TD></TR>
<TR class=main>
<TD>
<P><TT>Logger</TT>对象是用来取代<TT>System.out</TT>或者<TT>System.err</TT>的日志写出器，用来供程序员输出日志信息。</P>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>Logger的输出方法</B></TD></TR>
<TR class=main>
<TD>
<P>Logger类对象提供一系列方法供程序员输出日志信息。</P>
<P><TT><PRE>        
<B>
        ------ Log4j APIs : class Logger ------
        </B>
        
        <FONT color=green>
        // Printing methods :
        </FONT>
        public void debug(Object msg);
        public void debug(Object msg, Throwable t);
        
        public void info(Object msg);
        public void info(Object msg, Throwable t);
        
        public void warn(Object msg);
        public void warn(Object msg, Throwable t);
        
        public void error(Object msg);
        public void error(Object msg, Throwable t);
        
        public void fatal(Object msg);
        public void fatal(Object msg, Throwable t);
        <FONT color=green>
        // Generic printing method :
        </FONT>
        public void log(Level l, Object msg);
      </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>Logger的命名规则</B></TD></TR>
<TR class=main>
<TD>
<P>Logger由一个String类的名字识别，logger的名字是大小写敏感的，且名字之间具有继承的关系，子名有父名作为前缀，用点号<B>.</B>分隔。如：<TT>x.y</TT>是<TT>x.y.z</TT>的父亲。</P>
<P>根logger (<TT>root logger</TT>)是所有logger的祖先，它具有如下属性：<B>1)</B> 它总是存在的；<B>2)</B> 它不可以通过名字获得。</P>
<P>通过调用<TT>public static Logger Logger.getRootLogger()</TT>获得<TT>root logger</TT>；通过调用<TT>public static Logger Logger.getLogger(String name)</TT>或者<TT>public static Logger Logger.getLogger(Class clazz)</TT>获得（或者创建）一个<TT>named logger</TT>。后者相当于调用<TT>Logger.getLogger(clazz.getName())</TT>。</P>
<P>在某对象中，用该对象所属的类为参数，调用<TT>Logger.getLogger(Class clazz)</TT>以获得logger被认为是目前所知的最理智的命名logger的方法。</P></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>Log level</B></TD></TR>
<TR class=main>
<TD>
<P>每个logger都被分配了一个日志级别 (<TT>log level</TT>)，用来控制日志信息的输出。未被分配level的logger将继承它最近的父logger的level。</P>
<P>每条输出到logger的日志请求(<TT>logging request</TT>)也都有一个level，如果该request的level大于等于该logger的level，则该request将被处理（称为enabled）；否则该request将被忽略。故可得知： 
<OL>
<LI>logger的level越低，表示该logger越详细 
<LI>logging request的level越高，表示该logging request越优先输出 </LI></OL>
<P></P>
<P><TT>Level</TT>类中预定义了五个level，它们的大小关系如下：</P>
<P><TT><PRE>        Level.ALL &lt; Level.DEBUG &lt; Level.INFO &lt; Level.WARN &lt; Level.ERROR &lt; Level.FATAL &lt; Level.OFF
      </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>示例代码</B></TD></TR>
<TR class=main>
<TD>
<P>以下代码将用自己所属的类为参数，创建一个logger，启用默认配置，设置其level并向其输出若干logging request。</P>
<P><TT><PRE>import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;

public class Log4jTest {
    public static void main(String argv[]) {
    
        <FONT color=green>
        // Create a logger by the name of class Log4jTest.
        </FONT>
        Logger logger = Logger.getLogger(Log4jTest.class);
        <FONT color=green>
        // Use the default configuration.
        </FONT>
        BasicConfigurator.configure();
        <FONT color=green>
        // Set the logger level to Level.INFO
        </FONT>
        logger.setLevel(Level.INFO);
        <FONT color=green>
        // This request will be disabled since Level.DEBUG &lt; Level.INFO.
        </FONT>          
        logger.debug("This is debug.");
        <FONT color=green>
        // These requests will be enabled.
        </FONT>          
        logger.info("This is an info.");
        logger.warn("This is a warning.");
        logger.error("This is an error.");
        logger.fatal("This is a fatal error.");
        
        return;
    }
}
      </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>关于logger的两点说明</B></TD></TR>
<TR class=main>
<TD>
<P>
<OL>
<LI>用同名参数调用<TT>Logger.getLogger(String name)</TT>将返回同一个logger的引用。故可以在一个地方配置logger，在另外一个地方获得配置好的logger，而无须相互间传递logger的引用。 
<LI>logger的创建可以按照任意的顺序，即，父logger可以后于子logger被创建。log4j将自动维护logger的继承树。 </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE><BR>
<TABLE class=chapter-bg cellSpacing=1 align=center>
<TBODY>
<TR class=chapter>
<TD noWrap><B>Appender：日志目的地</B></TD></TR>
<TR class=main>
<TD>
<P>每个logger都可以拥有一个或者多个appender，每个appender表示一个日志的输出目的地，比如console或者某个文件。可以使用<TT>Logger.addAppender(Appender app)</TT>为logger增加一个appender；可以使用<TT>Logger.removeAppender(Appender app)</TT>为logger移除一个appender。</P>
<P>默认情况下，logger的additive标志被设置为true，表示子logger将继承父logger的所有appenders。该选项可以被重新设置，表示子logger将不再继承父logger的appenders。</P>
<P><TT>root logger</TT>拥有目标为<TT>system.out</TT>的<TT>consoleAppender</TT>，故默认情况下，所有的logger都将继承该appender。</P>
<P><TT><PRE>      
<B>
      ------ Log4j APIs : class Logger ------
      </B>
      
      <FONT color=green>
      // 为logger对象增加或者移除一个Appender对象 :.
      </FONT>
      public void appAppender(Appender app);
      public void removeAppender(Appender app);
      <FONT color=green>
      // 获得和设置additive标志：是否继承父logger的appenders :.
      // <B>注意：</B>在设置additive标志为false时，必须保证已经为该logger设置了新的appender， :.
      // 否则log4j将报错：log4j:WARN No appenders could be found for logger (x.y.z). :.
      </FONT>
      public boolean getAdditivity();
      public void setAdditivity(boolean additive);
    </PRE></TT>
<P></P>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>ConsoleAppender</B></TD></TR>
<TR class=main>
<TD>
<P>可以使用<TT>ConsoleAppender</TT>对象把日志输出到控制台。每个<TT>ConsoleAppender</TT>都有一个<TT>target</TT>，表示它的输出目的地。它可以是<TT>System.out</TT>，标准输出设备（缓冲显示屏）；或者是<TT>System.err</TT>，标准错误设备（不缓冲显示屏）。<TT>ConsoleAppender</TT>的使用方法参考如下API :.</P>
<P><TT><PRE>        
<B>
        ------ Log4j APIs : class ConsoleAppender extends WriterAppender ------
        </B>
      
        <FONT color=green>
        // 构造方法，使用一个Layout对象构造一个ConsoleAppender对象 :.
        // 默认情况下，ConsoleAppender的target是System.out :.
        </FONT>
        public ConsoleAppender(Layout layout);
        <FONT color=green>
        // 构造方法，使用一个Layout对象和一个target字符串构造ConsoleAppender对象 :.
        // target的可能取值为ConsoleAppender.SYSTEM_OUT和ConsoleAppender.SYSTEM_ERR :.
        </FONT>
        public ConsoleAppender(Layout layout, String target);
      </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>FileAppender</B></TD></TR>
<TR class=main>
<TD>
<P>可以使用<TT>FileAppender</TT>对象把日志输出到一个指定的日志文件中去。使用方法可以参考如下的API :.</P>
<P><TT><PRE>        
<B>
        ------ Log4j APIs : class FileAppender extends WriterAppender ------
        </B>
      
        <FONT color=green>
        // 构造方法，使用一个Layout对象和日志文件名构造一个FileAppender对象 :.
        </FONT>
        public FileAppender(Layout layout, String filename)
            throws IOException;
        public FileAppender(Layout layout, String filename, boolean append)
            throws IOException;
      </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>RollingFileAppender</B></TD></TR>
<TR class=main>
<TD>
<P>可以使用<TT>FileAppender</TT>的子类<TT>RollingFileAppender</TT>对象，把日志输出到一个指定的日志文件中。不同的是该日志文件的大小受到限制，当日志内容超出最大的尺寸时，该文件将向上滚动（最老的日志被擦除）。还可以在该类对象中指定为日志文件做多少个备份。具体使用方法参考如下API :.</P>
<P><TT><PRE>        
<B>
        ------ Log4j APIs : class RollingFileAppender extends FileAppender ------
        </B>
      
        <FONT color=green>
        // 构造方法，使用一个Layout对象和日志文件名构造一个RollingFileAppender对象 :.
        </FONT>
        public RollingFileAppender(Layout layout, String filename)
            throws IOException;
        public RollingFileAppender(Layout layout, String filename, boolean append)
            throws IOException;
        <FONT color=green>
        // 获得和设置日志备份文件的个数 :.
        </FONT>
        public int getMaxBackupIndex();
        public void setMaxBackupIndex(int index);
        <FONT color=green>
        // 获得和设置滚动日志文件的最大尺寸 :.
        </FONT>
        public long getMaximumFileSize();
        public void setMaximumFileSize(long size);
      </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE><BR>
<TABLE class=chapter-bg cellSpacing=1 align=center>
<TBODY>
<TR class=chapter>
<TD noWrap><B>Layout：日志格式化器</B></TD></TR>
<TR class=main>
<TD>
<P>每个appender都和一个layout相联系；layout的任务是格式化用户的logging request，appender的任务是把layout格式化好的输出内容送往指定的目的地。</P>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>PatternLayout</B></TD></TR>
<TR class=main>
<TD>
<P><TT>PatternLayout</TT>是Layout的一个子类，用来使用类似C语言的<TT>printf</TT>函数中使用的格式控制字符串来控制日志的输出格式。使用方法参考如下API :.</P>
<P><TT><PRE>        
<B>
        ------ Log4j APIs : class PatternLayout extends Layout ------
        </B>
      
        <FONT color=green>
        // 无参数构造方法，使用DEFAULT_CONVERSION_PATTERN构造一个PatternLayout :.
        // <B>注意：</B>DEFAULT_CONVERSION_PATTERN为"%m%n"，只打印消息信息 :.
        </FONT>
        public PatternLayout();
        <FONT color=green>
        // 构造方法，使用自定义的pattern构造一个PatternLayout :.
        </FONT>
        public PatternLayout(String pattern);
        <FONT color=green>
        // 获得和设置PatternLayout对象的日志pattern :.
        </FONT>
        public String getConversionPattern();
        public void setConversionPattern(String pattern);
      </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>patterns in PatternLayout</B></TD></TR>
<TR class=main>
<TD>
<P><FONT color=#ff0000>未完待续</FONT> </P></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE><BR>
<TABLE class=chapter-bg cellSpacing=1 align=center>
<TBODY>
<TR class=chapter>
<TD noWrap><B>Configuration：配置</B></TD></TR>
<TR class=main>
<TD>
<P>对log4j环境的配置就是对root logger的配置，包括把root logger设置为哪个级别(level)；为它增加哪些appender，等等。这些可以通过设置系统属性的方法来隐式地完成，也可以在程序里调用<TT>XXXConfigurator.configure()</TT>方法来显式地完成。</P>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>默认的log4j初始化过程</B></TD></TR>
<TR class=main>
<TD>
<P><TT>Logger</TT>类的静态初始化块(<TT>static initialization block</TT>)中对log4j的环境做默认的初始化。<B>注意：</B>如果程序员已经通过设置系统属性的方法来配置了log4j环境，则不需要再显式地调用<TT>XXXConfigurator.configure()</TT>方法来配置log4j环境了。</P>
<P>Logger的静态初始化块在完成初始化过程时将检查一系列log4j定义的系统属性。它所做的事情如下： 
<OL>
<LI>检查系统属性<TT>log4j.defaultInitOverride</TT>，如果该属性被设置为false，则执行初始化；否则（只要不是false，无论是什么值，甚至没有值，都是否则），跳过初始化。 
<LI>把系统属性<TT>log4j.configuration</TT>的值赋给变量resource。如果该系统变量没有被定义，则把resource赋值为<TT>"log4j.properties"</TT>。<B>注意：</B>在apache的log4j文档中建议使用定义<TT>log4j.configuration</TT>系统属性的方法来设置默认的初始化文件是一个好方法。 
<LI>试图把resource变量转化成为一个<TT>URL</TT>对象<TT>url</TT>。如果一般的转化方法行不通，就调用<TT>org.apache.log4j.helpers.Loader.getResource(resource, Logger.class)</TT>方法来完成转化。 
<LI>如果<TT>url</TT>以".html"结尾，则调用方法<TT>DOMConfigurator.configure(url)</TT>来完成初始化；否则，则调用方法<TT>PropertyConfigurator.configure(url)</TT>来完成初始化。如果<TT>url</TT>指定的资源不能被获得，则跳出初始化过程。 </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>BasicConfigurator.configure()</B></TD></TR>
<TR class=main>
<TD>
<P><TT>BasicConfigurator.configure()</TT>方法使用最简的方法配置log4j环境。<B>注：</B>所谓配置log4j环境，就是指配置root logger，因为所有其它的logger都是root logger的后代，所以它们（默认情况下）都将继承root logger的性质。</P>
<P><TT>BasicConfigurator.configure()</TT>完成的任务是： 
<OL>
<LI>用默认pattern创建PatternLayout对象p：<BR>PatternLayout p = new PatternLayout("%-4r[%t]%-5p%c%x - %m%n"); 
<LI>用p创建ConsoleAppender对象a，目标是system.out，标准输出设备：<BR>ConsoleAppender a = new ConsoleAppender(p,ConsoleAppender.SYSTEM_OUT); 
<LI>为root logger增加一个ConsoleAppender p：<BR>rootLogger.addAppender(p); 
<LI>把root logger的log level设置为DEBUG级别：<BR>rootLogger.setLevel(Level.DEBUG); </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>xml格式的log4j配置文件概述</B></TD></TR>
<TR class=main>
<TD>
<P>xml格式的log4j配置文件需要使用<TT>org.apache.log4j.html.DOMConfigurator.configure()</TT>方法来读入。对xml文件的语法定义可以在log4j的发布包中找到：<TT>org/apache/log4j/xml/log4j.dtd</TT>。</P>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>log4j的xml配置文件的树状结构</B></TD></TR>
<TR class=main>
<TD>
<P>log4j的xml配置文件的树状结构如下所示，注意下图只显示了常用的部分。 :.</P>
<P><TT><PRE>          xml declaration and dtd
            |
          log4j:configuration
            |
            +-- appender (name, class)
            |     |
            |     +-- param (name, value)
            |     +-- layout (class)
            |           |
            |           +-- param (name, value)
            +-- logger (name, additivity)
            |     |
            |     +-- level (class, value)
            |     |     |
            |     |     +-- param (name, value)
            |     +-- appender-ref (ref)
            +-- root
                  |
                  +-- param (name, class)
                  +-- level
                  |     |
                  |     +-- param (name, value)
                  +-- appender-ref (ref)  
        </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>xml declaration and dtd</B></TD></TR>
<TR class=main>
<TD>
<P>xml配置文件的头部包括两个部分：xml声明和dtd声明。头部的格式如下： :.</P>
<P><TT><PRE>          &lt;?xml version="1.0" encoding="UTF-8" ?&gt;
          &lt;!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"&gt;
        </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>log4j:configuration (root element)</B></TD></TR>
<TR class=main>
<TD>
<P>
<OL>
<LI><TT><B>xmlns:log4j</B> [#FIXED attribute]</TT> : 定义log4j的名字空间，取定值<TT>"http://jakarta.apache.org/log4j/"</TT> 
<LI><TT><B>appender</B> [* child]</TT> : 一个appender子元素定义一个日志输出目的地 
<LI><TT><B>logger</B> [* child]</TT> : 一个logger子元素定义一个日志写出器 
<LI><TT><B>root</B> [? child]</TT> : root子元素定义了root logger </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>appender</B></TD></TR>
<TR class=main>
<TD>
<P>appender元素定义一个日志输出目的地。</P>
<P>
<OL>
<LI><TT><B>name</B> [#REQUIRED attribute]</TT> : 定义appender的名字，以便被后文引用 
<LI><TT><B>class</B> [#REQUIRED attribute]</TT> : 定义appender对象所属的类的全名 
<LI><TT><B>param</B> [* child]</TT> : 创建appender对象时传递给类构造方法的参数 
<LI><TT><B>layout</B> [? child]</TT> : 该appender使用的layout对象 </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>layout</B></TD></TR>
<TR class=main>
<TD>
<P>layout元素定义与某一个appender相联系的日志格式化器。</P>
<P>
<OL>
<LI><TT><B>class</B> [#REQUIRED attribute]</TT> : 定义layout对象所属的类的全名 
<LI><TT><B>param</B> [* child]</TT> : 创建layout对象时传递给类构造方法的参数 </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>logger</B></TD></TR>
<TR class=main>
<TD>
<P>logger元素定义一个日志输出器。</P>
<P>
<OL>
<LI><TT><B>name</B> [#REQUIRED attribute]</TT> : 定义logger的名字，以便被后文引用 
<LI><TT><B>additivity</B> [#ENUM attribute]</TT> : 取值为"true"（默认）或者"false"，是否继承父logger的属性 
<LI><TT><B>level</B> [? child]</TT> : 定义该logger的日志级别 
<LI><TT><B>appender-ref</B> [* child]</TT> : 定义该logger的输出目的地 </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>root</B></TD></TR>
<TR class=main>
<TD>
<P>root元素定义根日志输出器root logger。</P>
<P>
<OL>
<LI><TT><B>param</B> [* child]</TT> : 创建root logger对象时传递给类构造方法的参数 
<LI><TT><B>level</B> [? child]</TT> : 定义root logger的日志级别 
<LI><TT><B>appender-ref</B> [* child]</TT> : 定义root logger的输出目的地 </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>level</B></TD></TR>
<TR class=main>
<TD>
<P>level元素定义logger对象的日志级别。</P>
<P>
<OL>
<LI><TT><B>class</B> [#IMPLIED attribute]</TT> : 定义level对象所属的类，默认情况下是"org.apache.log4j.Level类 
<LI><TT><B>value</B> [#REQUIRED attribute]</TT> : 为level对象赋值。可能的取值从小到大依次为"all"、"debug"、"info"、"warn"、"error"、"fatal"和"off"。当值为"off"时表示没有任何日志信息被输出 
<LI><TT><B>param</B> [* child]</TT> : 创建level对象时传递给类构造方法的参数 </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>appender-ref</B></TD></TR>
<TR class=main>
<TD>
<P>appender-ref元素引用一个appender元素的名字，为logger对象增加一个appender。</P>
<P>
<OL>
<LI><TT><B>ref</B> [#REQUIRED attribute]</TT> : 一个appender元素的名字的引用 
<LI>appender-ref元素没有子元素 </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>param</B></TD></TR>
<TR class=main>
<TD>
<P>param元素在创建对象时为类的构造方法提供参数。它可以成为<TT>appender</TT>、<TT>layout</TT>、<TT>filter</TT>、<TT>errorHandler</TT>、<TT>level</TT>、<TT>categoryFactory</TT>和<TT>root</TT>等元素的子元素。</P>
<P>
<OL>
<LI><TT><B>name</B> and <B>value</B> [#REQUIRED attributes]</TT> : 提供参数的一组名值对 
<LI>param元素没有子元素 </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>在xml文件中配置appender和layout</B></TD></TR>
<TR class=main>
<TD>
<P>创建不同的Appender对象或者不同的Layout对象要调用不同的构造方法。可以使用param子元素来设定不同的参数值。</P>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>创建ConsoleAppender对象</B></TD></TR>
<TR class=main>
<TD>
<P>ConsoleAppender的构造方法不接受其它的参数。 :.</P>
<P><TT><PRE>          ... ... ... ...
          &lt;appender name="console.log" class="org.apache.log4j.ConsoleAppender"&gt;
            &lt;layout ... &gt;
              ... ...
            &lt;/layout&gt;
          &lt;/appender&gt;
          ... ... ... ...
        </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>创建FileAppender对象</B></TD></TR>
<TR class=main>
<TD>
<P>可以为FileAppender类的构造方法传递两个参数：<TT>File</TT>表示日志文件名；<TT>Append</TT>表示如文件已存在，是否把日志追加到文件尾部，可能取值为"true"和"false"（默认）。 :.</P>
<P><TT><PRE>          ... ... ... ...
          &lt;appender name="file.log" class="org.apache.log4j.FileAppender"&gt;
            &lt;param name="File" value="/tmp/log.txt" /&gt;
            &lt;param name="Append" value="false" /&gt;
            &lt;layout ... &gt;
              ... ...
            &lt;/layout&gt;
          &lt;/appender&gt;
          ... ... ... ...
        </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>创建RollingFileAppender对象</B></TD></TR>
<TR class=main>
<TD>
<P>除了<TT>File</TT>和<TT>Append</TT>以外，还可以为RollingFileAppender类的构造方法传递两个参数：<TT>MaxBackupIndex</TT>备份日志文件的个数（默认是1个）；<TT>MaxFileSize</TT>表示日志文件允许的最大字节数（默认是10M）。 :.</P>
<P><TT><PRE>          ... ... ... ...
          &lt;appender name="rollingFile.log" class="org.apache.log4j.RollingFileAppender"&gt;
            &lt;param name="File" value="/tmp/rollingLog.txt" /&gt;
            &lt;param name="Append" value="false" /&gt;
            &lt;param name="MaxBackupIndex" value="2" /&gt;
            &lt;param name="MaxFileSize" value="1024" /&gt;
            &lt;layout ... &gt;
              ... ...
            &lt;/layout&gt;
          &lt;/appender&gt;
          ... ... ... ...
        </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=subsection-bg cellSpacing=1 align=center>
<TBODY>
<TR class=subsection>
<TD noWrap><B>创建PatternLayout对象</B></TD></TR>
<TR class=main>
<TD>
<P>可以为PatternLayout类的构造方法传递参数ConversionPattern。 :.</P>
<P><TT><PRE>          ... ... ... ...
          &lt;layout class="org.apache.log4j.PatternLayout&gt;
            &lt;param name="Conversion" value="%d [%t] %p - %m%n" /&gt;
          &lt;/layout&gt;
          ... ... ... ...
        </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE><BR>
<TABLE class=section-bg cellSpacing=1 align=center>
<TBODY>
<TR class=section>
<TD noWrap><B>我自己的一个使用xml文件配置log4j环境的很简单的例子</B></TD></TR>
<TR class=main>
<TD>
<P>为WSOTA项目开发java web start的胖客户端时，使用了如下的xml文件配置log4j环境（文件名为wsota-rc.log4j.html）：:.</P>
<P><TT><PRE>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"&gt;

&lt;log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"&gt;
  <FONT color=green>
  &lt;!-- ================================================================= --&gt;
  &lt;!--                     a rolling file appender                       --&gt;
  &lt;!-- ================================================================= --&gt;
  </FONT>
  &lt;appender name="wsota-rc.file.log" class="org.apache.log4j.RollingFileAppender"&gt;
    &lt;param name="File" value="/tmp/wsota-rc.log" /&gt;
    &lt;param name="Append" value="false" /&gt;
    &lt;layout class="org.apache.log4j.PatternLayout"&gt;
      &lt;param name="ConversionPattern" value="%d [%t] %p - %m%n" /&gt;
    &lt;/layout&gt;
  &lt;/appender&gt;
  <FONT color=green>
  &lt;!-- ================================================================= --&gt;
  &lt;!--                       a console appender                          --&gt;
  &lt;!--     debug can be turned off by setting level of root to "off"     --&gt;
  &lt;!-- ================================================================= --&gt;
  </FONT>
  &lt;appender name="wsota-rc.console.log" class="org.apache.log4j.ConsoleAppender"&gt;
    &lt;layout class="org.apache.log4j.PatternLayout"&gt;
      &lt;param name="ConversionPattern" value="%d [%t] %p - %m%n" /&gt;
    &lt;/layout&gt;
  &lt;/appender&gt;
  <FONT color=green>
  &lt;!--  use this to turn on debug to a rolling file. --&gt;
  </FONT>
  &lt;root&gt;
    &lt;level value="debug" /&gt;
    &lt;appender-ref ref="wsota-rc.file.log" /&gt;
  &lt;/root&gt;
  <FONT color=green>
  &lt;!--  use this to turn on debug to console. --&gt;
  &lt;!--
  &lt;root&gt;
    &lt;level value="off" /&gt;
    &lt;appender-ref ref="wsota-rc.console.log" /&gt;
  &lt;/root&gt;
  --&gt;
  </FONT>
  <FONT color=green>
  &lt;!--  use this to turn off debug. --&gt;
  &lt;!--
  &lt;root&gt;
    &lt;level value="off" /&gt;
    &lt;appender-ref ref="wsota-rc.console.log" /&gt;
  &lt;/root&gt;
  --&gt;
  </FONT>
&lt;/log4j:configuration&gt;
      </PRE></TT>
<P></P>
<P>在胖客户程序中使用了如下代码来使用外部xml文件配置log4j环境，注意该代码段位于程序的main class的静态初始化块中，含有以下代码的类和xml配置文件在同一个目录下：:.</P>
<P><TT><PRE>  import org.apache.log4j.html.DOMConfigurator;
  
  public class SapFrame extends JFrame {
      static {
          DOMConfigurator.configure(SapFrame.class.getResource("wsota-rc.log4j.html"));
      }
      ... ... ... ...
  }
      </PRE></TT>
<P></P></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE><BR>
<TABLE class=chapter-bg cellSpacing=1 align=center>
<TBODY>
<TR class=chapter>
<TD noWrap><B>Log4j的编码习惯</B></TD></TR>
<TR class=main>
<TD>
<P>
<OL>
<LI>让每个类都拥有一个private static的Logger对象，用来输出该类中的全部日志信息 
<LI>使用xml文件来完成对log4j环境的配置。在项目的main class中的静态初始化块里放log4j环境的配置代码。注意：在一个项目中，log4j环境只需要被配置一次，而不是在每个使用了logger的类里都需要调用一次 
<LI>用<TT>MyClass.class</TT>作为参数创建该类的静态Logger对象 
<LI><FONT color=#ff0000>补充中...</FONT> </LI></OL>
<P></P></TD></TR></TBODY></TABLE><BR>
<TABLE class=chapter-bg cellSpacing=1 align=center>
<TBODY>
<TR class=chapter>
<TD noWrap><B>参考资料</B></TD></TR>
<TR class=main>
<TD>
<P>
<OL>
<LI>Log4j主页上的相关文档：<A href="http://jakarta.apache.org/log4j/docs">http://jakarta.apache.org/log4j/docs</A> 
<LI>Ashley J.S Mills的log4j教程：<A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html">http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html</A> </LI></OL></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/Victor/aggbug/13704.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-09-22 08:47 <a href="http://www.blogjava.net/Victor/articles/13704.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate+Spring+Struts扩展Struts 3(转) </title><link>http://www.blogjava.net/Victor/articles/13012.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Wed, 14 Sep 2005 05:16:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/13012.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/13012.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/13012.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/13012.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/13012.html</trackback:ping><description><![CDATA[<STRONG>创建你自己的RequestProcessor<BR><BR></STRONG>　　通过上面，我们了解到了RequestProcessor的默认实现是如何工作的。现在我们要演示一个例子来说明如何定制你自己的RequestProcessor。为了展示创建用户定制的RequestProcessor，我们将会让我们的示例实现下面两个业务需求：<BR><BR>　　·我们想创建一个ContactImageAction类，它将生成图片而不是平常的HTML页面。<BR><BR>　　·在每个请求处理之前，我们都想通过检查session中的userName属性来确定用户是否已经登陆。如果那个属性没有找到，我们会把用户重定向到登陆页面。<BR><BR>　　我们将分两步实现这些业务需求。<BR><BR>　　1、创建你的CustomRequestProcessor类，它将继承自RequestProcessor类，如下：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>public class CustomRequestProcessor<BR>extends RequestProcessor {<BR>　protected boolean processPreprocess (<BR>　　HttpServletRequest request,HttpServletResponse response) {<BR>　　　HttpSession session = request.getSession(false);<BR>　　　//If user is trying to access login page<BR>　　　// then don't check<BR>　　　if( request.getServletPath().equals("/loginInput.do")<BR>|| request.getServletPath().equals("/login.do") )<BR>　　　　return true;<BR>　　　//Check if userName attribute is there is session.<BR>　　　//If so, it means user has allready logged in<BR>　　　if( session != null &amp;&amp; session.getAttribute("userName") != null)<BR>　　　　return true;<BR>　　　else{<BR>　　　　try{<BR>　　　　　//If no redirect user to login Page<BR>　　　　　request.getRequestDispatcher("/Login.jsp").forward(request,response);<BR>　　　　}catch(Exception ex){<BR>　　　　}<BR>　　　}<BR>　　　return false;<BR>　　}<BR><BR>　protected void processContent(HttpServletRequest request,<BR>HttpServletResponse response) {<BR>　　//Check if user is requesting ContactImageAction<BR>　　// if yes then set image/gif as content type<BR>　　if( request.getServletPath().equals("/contactimage.do")){<BR>　　　response.setContentType("image/gif");<BR>　　　return;<BR>　　}<BR>　　　super.processContent(request, response);<BR>　}　<BR>}</TD></TR></TBODY></TABLE><BR>　　在CustomRequestProcessor类的processPreprocess方法中，我们检查session的userName属性，如果没有找到，就将用户重定向到登陆页面。<BR><BR>　　对于生成图片作为输出的需求，我们必须覆盖processContent方法，首先检查请求是否是/contactimage路径。如果是的话，我们就会将contentType设置为image/gif；否则设置为text/html。<BR><BR>　　2、在你的struts-config.xml文件的＜action-mappint＞元素之后加入下面的文字，告诉Struts CustomRequestProcessor应当被用作RequestProcessor类：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>＜controller＞<BR>＜set-property property="processorClass"value="com.sample.util.CustomRequestProcessor"/＞<BR>＜/controller＞</TD></TR></TBODY></TABLE><BR>　　请注意，当你只有很少的action类需要生成非text/html类型的输出时，你覆写processContent()方法是OK的。如果不是这样子的话，你应该创建一个Struts的子应用来处理请求生成图片的Action，并为它们将contentType设置为image/gif。<BR><BR>　　Struts的Tiles框架就是使用它自己的RequestProcessor来装饰Struts的输出。<BR><BR>　　ActionServlet<BR><BR>　　如果你查看你的Struts web应用的web.xml，你会看到这样的文字：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>＜web-app ＞<BR>　＜servlet＞<BR>　　＜servlet-name＞action=＜/servlet-name＞<BR>　　＜servlet-class＞org.apache.struts.action.ActionServlet＜/servlet-class＞<BR>　　＜!-- All your init-params go here--＞<BR>　＜/servlet＞<BR>　＜servlet-mapping＞<BR>　　＜servlet-name＞action＜/servlet-name＞<BR>　　＜url-pattern＞*.do＜/url-pattern＞<BR>　＜/servlet-mapping＞<BR>＜/web-app ＞</TD></TR></TBODY></TABLE><BR>　　这意味着ActionServlet负责处理你所有Struts的请求。你可以创建一个ActionServlet的子类，当应用启动，关闭，每个请求的时候做一些特定的事情。但是在继承ActionServlet类之前，你应该尽量创建一个PlugIn或RequestProcessor去解决你的问题。在Servlet1.1之前，Tiles框架是基于ActionServlet来修饰生成的响应。但是从1.1之后，它开始使用TilesRequestProcessor类。<BR><BR>　　<B>总结</B><BR><BR>　　决定开发你自己的MVC框架是一个非常大的决定，你必须要考虑开发和维护框架代码所花费的时间和资源。Struts是一个非常强大和稳定的框架，你可以修改它来满足你绝大多数的业务需求。<BR><BR>　　但另一方面，也不要草率地做出扩展Struts的决定。如果你在RequestProcessor中写了一些性能比较低的代码，它将会在每次请求时执行，因而降低你整个应用的效率。而且还是有一些情况，开发自己的MVC框架要比扩展Struts好。<BR><img src ="http://www.blogjava.net/Victor/aggbug/13012.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-09-14 13:16 <a href="http://www.blogjava.net/Victor/articles/13012.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate+Spring+Struts扩展Struts 2(转) </title><link>http://www.blogjava.net/Victor/articles/13011.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Wed, 14 Sep 2005 05:07:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/13011.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/13011.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/13011.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/13011.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/13011.html</trackback:ping><description><![CDATA[<STRONG>Request是如何被处理的<BR><BR></STRONG>　　ActionServlet是Struts框架中唯一的Servlet，它负责处理所有request。无论何时接收到一个request，它都会先尝试为当前的request寻找一个sub-application。一旦一个sub-application被找到，ActionServlet就会为那个sub-application创建一个RequestProcessor对象，调用这个对象的process()方法并把HttpServletRequest和HttpServletResponse对象传入。<BR><BR>　　RequestProcessor.process()就是大部分request被处理的地方。process()方法使用了Template Method模式实现，其中有很多独立的方法来执行请求处理的每一步骤，这些方法将会在process方法中依次被调用。比如，将会有一个独立的方法用来寻找当前request对应的ActionForm类，一个方法来检查当前用户是否有执行action mapping所必须的权限。这些给与我们极大的灵活性。在发布的Struts包中有一个RequestProcessor类提供了请求处理每一步骤的默认实现。这就意味着你可以仅仅重写你感兴趣的方法，其它的使用默认的实现。举例来说，默认地Struts调用request.isUserInRole()来检查用户是否有权限执行当前的ActionMapping；这时如果你想通过查询数据库来实现，你所要做的就是重写processRoles()方法，通过查询出的用户是否拥有必须的权限来返回true或false。<BR><BR>　　首先我们将会看到缺省情况下，process()方法是如何实现的，然后我将会详细解释默认的RequestProcessor类中的每一个方法，这样你就可以决定哪一部分是你想要改变的。<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>public void process(HttpServletRequest request,HttpServletResponse response)<BR>throws IOException, ServletException {<BR>　// Wrap multipart requests with a special wrapper<BR>　request = processMultipart(request);<BR>　// Identify the path component we will<BR>　// use to select a mapping<BR>　String path = processPath(request, response);<BR>　if (path == null) {<BR>　　return;<BR>　}<BR>　if (log.isDebugEnabled()) {<BR>　　log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'");<BR>　}<BR>　// Select a Locale for the current user if requested<BR>　processLocale(request, response);<BR>　// Set the content type and no-caching headers<BR>　// if requested<BR>　processContent(request, response);<BR>　processNoCache(request, response);<BR>　// General purpose preprocessing hook<BR>　if (!processPreprocess(request, response)) {<BR>　　return;<BR>　}<BR>　// Identify the mapping for this request<BR>　ActionMapping mapping =<BR>　processMapping(request, response, path);<BR>　if (mapping == null) {<BR>　　return;<BR>　}<BR>　// Check for any role required to perform this action<BR>　if (!processRoles(request, response, mapping)) {<BR>　　return;<BR>　}<BR>　// Process any ActionForm bean related to this request<BR>　ActionForm form = processActionForm(request, response, mapping);<BR>　processPopulate(request, response, form, mapping);<BR>　if (!processValidate(request, response, form, mapping)) {<BR>　　return;<BR>}<BR>// Process a forward or include specified by this mapping<BR>if (!processForward(request, response, mapping)) {<BR>　return;<BR>}<BR>if (!processInclude(request, response, mapping)) {<BR>　return;<BR>}<BR>// Create or acquire the Action instance to<BR>// process this request<BR>Action action =<BR>processActionCreate(request, response, mapping);<BR>if (action == null) {<BR>　return;<BR>}<BR>// Call the Action instance itself<BR>ActionForward forward = processActionPerform(request, response,action, form, mapping);<BR>// Process the returned ActionForward instance<BR>processForwardConfig(request, response, forward);<BR>}</TD></TR></TBODY></TABLE><BR>　　1、processMutipart()：在这个方法中，Struts将会读取request来检查request的contentType是否是multipart/form-data。如果是的话，将会解析request并且将之包装到HttpServletRequest中。当你创建了一个HTML FORM用来提交数据，那么request的contentType默认就是application/x-www-form-urlencoded。但是如果你的form使用了file类型的input控件允许用户上传文件的话，你就必须将contentType改为multipart/form-data。如果是这样的情况，你就不能再通过getParameter()来获取用户提交的数据；你必须将request作为一个InputStream来读取，并且自己解析它来获得参数值。<BR><BR>　　2、processPath()：在这个方法中，Struts将会读取request的URI，来确定路径元素，这个元素是用来获取ActionMappint元素。<BR><BR>　　3、processLocale()：在这个方法中，Struts将会为当前request取得Locale，如果配置过的话，还可以将这个对象作为HttpSession中org.apache.struts.action.LOCALE属性的值而保存。作为这个方法的副作用，HttpSession将会被创建，如果你不想创建的话，你可以在ControllerConfig中将locale属性设为false，在struts-config.xml中象如下这样：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>＜controller＞<BR>　＜set-property property="locale" value="false"/＞<BR>＜/controller＞</TD></TR></TBODY></TABLE><BR>　　4、processContent()：通过调用response.setContentType()来为response设置contentType。这个方法首先会尝试从struts-config.xml配置中得到contentType。缺省情况下使用text/html。如果想覆盖它，可以象如下这样：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>＜controller＞<BR>＜set-property property="contentType" value="text/plain"/＞<BR>＜/controller＞</TD></TR></TBODY></TABLE><BR>　　5、processNoCache()：如果配置是no-cache，Struts将会为每个response设置下面三个headers：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>requested in struts config.xml<BR>response.setHeader("Pragma", "No-cache");<BR>response.setHeader("Cache-Control", "no-cache");<BR>response.setDateHeader("Expires", 1);</TD></TR></TBODY></TABLE><BR>　　如果你想设置no-cache header，在struts-config.xml中加入下面信息：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>＜controller＞<BR>＜set-property property="noCache" value="true"/＞<BR>＜/controller＞ </TD></TR></TBODY></TABLE><BR>　　6、processPreprocess()：这个方法为预处理提供一个hook，可以在子类中覆盖它。它的缺省实现没有作任何事情，总是返回true。返回false的话将会终止当前请求的处理。<BR><BR>　　7、processMapping()：这个方法将会用路径信息得到一个ActionMapping对象。也就是struts-config.xml文件中的＜action＞元素：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>＜action path="/newcontact" type="com.sample.NewContactAction" name="newContactForm" scope="request"＞<BR>＜forward name="sucess" path="/sucessPage.do"/＞<BR>＜forward name="failure" path="/failurePage.do"/＞<BR>＜/action＞</TD></TR></TBODY></TABLE><BR>　　ActionMapping元素包含了Action类的名称和处理请求使用的ActionForm等等信息。它还包含当前ActionMapping配置的ActionForwards信息。<BR><BR>　　8、processRoles()：Struts web应用提供了一个授权方案。也就是说，一旦一个用户登入了容器，struts的processRoles()方法将会通过调用request.isUserInRole()，来检查他是否有必须的角色来运行一个给定的ActionMapping。<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>＜action path="/addUser" roles="administrator"/＞ </TD></TR></TBODY></TABLE><BR>　　假设你有一个AddUserAction并且你只想让administrator能够增加新的user。你所要做的就是给你的AddUserAction元素增加一个role属性，这个属性的值为administrator。这样，在运行AddUserAction之前，这个方法会确保用户拥有administraotr的角色。<BR><BR>　　9、processActionForm()：每一个ActionMapping都一个相应的ActionForm类。当Struts处理一个ActionMapping的时候，它将会从＜action＞元素的name属性中找出对应的ActionForm类的名称。<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>＜form-bean name="newContactForm" type="org.apache.struts.action.DynaActionForm"＞<BR>　＜form-property name="firstName" type="java.lang.String"/＞<BR>　＜form-property name="lastName" type="java.lang.String"/＞<BR>＜/form-bean＞</TD></TR></TBODY></TABLE><BR>　　在我们的例子中，它会先在request scope中检查是否有一个org.apache.struts.action.DynaActionForm类的对象存在。如果有它将会使用这个对象，如果没有它将会创建一个新的对象并把它设置在request scope。<BR><BR>　　10、processPopulate()：在这个方法中，Struts将会用相匹配的request参数装配ActionForm的实例变量。<BR><BR>　　11、processValidate()：Struts将会调用你的ActionForm类的validate方法。如果你从validate()返回ActionErrors，它将会将user重定向到＜action＞元素的input属性指定的页面。<BR><BR>　　12、processForward()和processInclude()：在这些方法中，Struts将会检查＜action＞元素的forward或include属性，如果找到了，将会把forward或include请求放置到配置的页面中。<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>＜action forward="/Login.jsp" path="/loginInput"/＞<BR>＜action include="/Login.jsp" path="/loginInput"/＞</TD></TR></TBODY></TABLE><BR>　　你可以从这些方法的名字上猜测它们的不同：processForward()最终调用RequestDispatcher.forward()，而processInclude()调用RequestDispatcher.include()。如果你同时配置了forward和include属性，它将会总是调用forward，因为forward先被处理。<BR><BR>　　13、processActionCreate()：这个方法从＜action＞元素的type属性中获取获得Action类的名字并且创建返回它的实例。在我们的例子中，它将会创建一个com.sample.NewContactAction类的实例。<BR><BR>　　14、processActionPerform()：这个方法调用你的Action类的excute()方法，你的业务逻辑也就是在excute方法中。<BR><BR>　　15、processForwardConfig()：你的Action类的excute()方法将会返回一个ActionForward对象，这个对象将指出哪个页面是显示给用户的页面。因此，Struts将会为那个页面创建一个RequestDispatcher，并且调用RequestDispatcher.forward()。<BR><BR>　　上面的列表说明了默认的RequestProcessor实现在处理请求时每一步作的工作，以及执行的顺序。正如你所看到的，RequestProcessor是非常灵活的，允许你通过设置＜controller＞元素的属性来配置它。举例来说，如果你的应用准备生成XML内容来代替HTML，你就可以通过设置controller元素的属性来通知Struts这些情况。<BR><img src ="http://www.blogjava.net/Victor/aggbug/13011.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-09-14 13:07 <a href="http://www.blogjava.net/Victor/articles/13011.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate+Spring+Struts扩展Struts 1(转)</title><link>http://www.blogjava.net/Victor/articles/13010.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Wed, 14 Sep 2005 05:06:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/13010.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/13010.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/13010.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/13010.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/13010.html</trackback:ping><description><![CDATA[我看到很多项目中，开发者实现了自己的MVC框架，并不是因为他们想做同Struts根本不同的东西，而是因为他们并没有意识到如何扩展Struts。开发自己的MVC框架可以获得全部的控制权，但是这也意味着需要很多资源来实现它（人力物力），在紧张的日程安排下，有时候这是不可能的。 <BR><BR>　　Struts不仅仅是一个强大的框架，同时它也是可扩展的。你可以以三种方式来扩展Struts。<BR><BR>　　1、PlugIn：如果你想在application startup或shutdown的时候做一些业务逻辑的话，那就创建你自己的PlugIn类。<BR><BR>　　2、RequestProcessor：如果你想在请求被处理的过程中某个时刻做一些业务逻辑的话，那么创建你自己的RequestProcessor类。比如说，在每次请求执行之前，你可以扩展RequestProcessor来检查用户是否登陆了以及他是否有权限去执行某个特定的action。<BR><BR>　　3、ActionServlet：如果你想在application startup和shutdown的时候以及请求被处理的时候做某些业务逻辑，你也可以扩张ActionServlet类。不过你应当在PlugIn和RequestProcessor都不能解决你的需求的时候来使用ActionServlet。<BR><BR>　　在这篇文章中，我们将使用一个Struts应用的示例来示范如何使用这三种方式来扩展Struts。示例程序的代码可以从http://www.onjava.com/onjava/2004/11/10/examples/sample1.zip下载。两个扩展Struts成功的范例是Struts自身的Validation和Tiles框架。<BR><BR>　　我们假设你已经比较熟悉Struts框架并且知道如何使用它创建一个简单的应用。如果你想知道更多关于Struts的内容，请参考官方主页。<BR><BR>　　<B>PlugIn</B><BR>　<BR>　　PlugIn是一个接口，你可以创建一个实现该接口的类，当application startup或shutdown的时候做些事情。<BR><BR>　　比方说，我创建了一个使用Hibernate作为持久层的web应用，我想当应用启动的时候就初始化Hibernate，这样子当我的web应用受到第一个请求的时候，Hibernate就已经是配置好的并且可用的。同时我们想当application关闭的时候关闭Hibernate。我们可以用一个Hibernate PlugIn来实现这个需求，通过如下的两步：<BR><BR>　　1、创建一个类实现了PlugIn接口：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>public class HibernatePlugIn implements PlugIn{<BR>　private String configFile;<BR>　// This method will be called at application shutdown time<BR>　public void destroy() {<BR>　　System.out.println("Entering HibernatePlugIn.destroy()");<BR>　　//Put hibernate cleanup code here<BR>　　System.out.println("Exiting HibernatePlugIn.destroy()");<BR>　}<BR>　//This method will be called at application startup time<BR>　public void init(ActionServlet actionServlet, ModuleConfig config)<BR>　　throws ServletException {<BR>　　　System.out.println("Entering HibernatePlugIn.init()");<BR>　　　System.out.println("value of init parameter " +<BR>　　　getConfigFile());<BR>　　　System.out.println("Exiting HibernatePlugIn.init()");<BR>　　}<BR>　public String getConfigFile() {<BR>　　return name;<BR>　}<BR>　public void setConfigFile(String string) {<BR>　　configFile = string;<BR>　}<BR>}</TD></TR></TBODY></TABLE><BR>　　实现PlugIn接口的类必须完成两个方法：init()和destroy()。当application startup的时候init()方法被调用，当shutdown的时候destroy()方法被调用。Struts还允许给你的PlugIn类传递初始化参数。为了传递参数，你必须在PlugIn类中为每一个参数创建JavaBean式的setter方法。在我们的HibernatePlugIn类中，我会把configFile的name作为参数传进去，而不是硬编码到程序中。<BR><BR>　　2、在struts-config.xml中添加如下的代码来通告Struts有新的PlugIn：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#c9c9b8 border=1>
<TBODY>
<TR>
<TD>＜struts-config＞<BR>　...<BR>　＜!-- Message Resources --＞<BR>　＜message-resources parameter= "sample1.resources.ApplicationResources"/＞<BR><BR>　＜!-- Declare your plugins --＞<BR>　＜plug-in className="com.sample.util.HibernatePlugIn"＞<BR>　　＜set-property property="configFile" value="/hibernate.cfg.xml"/＞<BR>　＜/plug-in＞<BR>＜/struts-config＞</TD></TR></TBODY></TABLE><BR>　　属性className是实现了PlugIn接口的类的全限定名。对于每一个初始化参数，可以使用＜set-property＞元素传递参数。在我们的例子中，我要把config文件的名字传进去，所以使用了一个带有配置文件路径的＜set-property＞。<BR><BR>　　Struts的Tiles和Validator框架都使用PlugIn来读取配置文件进行初始化。另外两件PlugIn可以帮你做到的事情是：<BR><BR>　　·如果你的application依赖于某些配置文件，那么你可以在PlugIn类中检查它们是否可用，如果不可用的话抛出一个ServletException，这样就可以使ActionServlet变为不可用。<BR><BR>　　·PlugIn接口的init()方法是你可以改变ModuleConfig的最后机会，ModuleConfig是一组静态配置信息的集合，用来描述基于Struts模块。Struts将会在所有PlugIn处理完后释放ModuleConfig。<BR><img src ="http://www.blogjava.net/Victor/aggbug/13010.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-09-14 13:06 <a href="http://www.blogjava.net/Victor/articles/13010.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>log4j--新的日志操作方法（转） </title><link>http://www.blogjava.net/Victor/articles/10609.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Sun, 21 Aug 2005 02:37:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/10609.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/10609.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/10609.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/10609.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/10609.html</trackback:ping><description><![CDATA[<P>1.1准备工作<BR>&nbsp;一。Tomcat已正确配置与使用。<BR>&nbsp;二。软件下载：log4j------<IMG alt=::URL:: hspace=2 src="http://www.blogcn.com/images/aurl.gif" align=absBottom border=0><A href="http://www.apache.org/dist/jakarta/log4j/jakarta-log4j-1.2.8.zip" target=_blank>http://www.apache.org/dist/jakarta/log4j/jakarta-log4j-1.2.8.zip</A> </P>
<P></P>
<P>&nbsp;1.2.&nbsp;Log4j简介<BR>&nbsp;在强调可重用组件开发的今天，除了自己从头到尾开发一个可重用的日志操作类外，Apache为我们提供了一个强有力的日志操作包-Log4j。</P>
<P>&nbsp;Log4j是Apache的一个开放源代码项目，通过使用Log4j，我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX&nbsp;Syslog守护进程等；我们也可以控制每一条日志的输出格式；通过定义每一条日志信息的级别，我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是，这些可以通过一个配置文件来灵活地进行配置，而不需要修改应用的代码。</P>
<P>&nbsp;此外，通过Log4j其他语言接口，您可以在C、C++、.Net、PL/SQL程序中使用Log4j，其语法和用法与在Java程序中一样，使得多语言分布式系统得到一个统一一致的日志组件模块。而且，通过使用各种第三方扩展，您可以很方便地将Log4j集成到J2EE、JINI甚至是SNMP应用中。本文介绍的Log4j版本是1.2.8，怎样通过一个配置文件来灵活地进行配置，主要的应用平台是Tomcat4.</P>
<P>&nbsp;1.3。Log4j的配置。</P>
<P>&nbsp;首先到jakarta下载一个log4j的组件。把jakarta-log4j-1.2.8\dist\lib下的log4j-1.2.8.jar文件copy到classpath指定的目录下！可以是Tomcat的common\lib目录下，也可以是你需要用到log4j的application下的lib目录。</P>
<P>&nbsp;1.4在Application目录下的web.xml文件加入以后代码</P>
<P>&nbsp;&lt;servlet&gt;<BR>&nbsp;&lt;servlet-name&gt;log4j&lt;/servlet-name&gt;<BR>&nbsp;&lt;servlet-class&gt;com.apache.jakarta.log4j.Log4jInit&lt;/servlet-class&gt;<BR>&nbsp;&lt;init-param&gt;<BR>&nbsp;&lt;param-name&gt;log4j&lt;/param-name&gt;<BR>&nbsp;&lt;param-value&gt;/WEB-INF/log4j.properties&lt;/param-value&gt;<BR>&nbsp;&lt;/init-param&gt;<BR>&nbsp;&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;<BR>&nbsp;&lt;/servlet&gt;</P>
<P>&nbsp;这段代码的意思是说，在Tomcat启动时加载com.apache.jakarta.log4j.Log4jInit这个名叫Log4jInit.class这个类文件。其中Log4jInit.class的源代码如下</P>
<P>&nbsp;package&nbsp;com.apache.jakarta.log4j;<BR>&nbsp;import&nbsp;org.apache.log4j.PropertyConfigurator;<BR>&nbsp;import&nbsp;javax.servlet.http.HttpServlet;<BR>&nbsp;import&nbsp;javax.servlet.http.HttpServletRequest;<BR>&nbsp;import&nbsp;javax.servlet.http.HttpServletResponse;<BR>&nbsp;public&nbsp;class&nbsp;Log4jInit&nbsp;extends&nbsp;HttpServlet&nbsp;{<BR>&nbsp;public&nbsp;void&nbsp;init()&nbsp;{<BR>&nbsp;String&nbsp;prefix&nbsp;=&nbsp;getServletContext().getRealPath("/"<IMG onclick="window.open('/images/wink.gif','_blank');" alt="" hspace=2 src="http://www.blogcn.com/images/wink.gif" onload="javascript:if(this.width>screen.width/2)this.width=screen.width/2" vspace=2 border=0>;<BR>&nbsp;String&nbsp;file&nbsp;=&nbsp;getInitParameter("log4j"<IMG onclick="window.open('/images/wink.gif','_blank');" alt="" hspace=2 src="http://www.blogcn.com/images/wink.gif" onload="javascript:if(this.width>screen.width/2)this.width=screen.width/2" vspace=2 border=0>;<BR>&nbsp;//&nbsp;if&nbsp;the&nbsp;log4j-init-file&nbsp;is&nbsp;not&nbsp;set,&nbsp;then&nbsp;no&nbsp;point&nbsp;in&nbsp;trying<BR>&nbsp;System.out.println("................log4j&nbsp;start"<IMG onclick="window.open('/images/wink.gif','_blank');" alt="" hspace=2 src="http://www.blogcn.com/images/wink.gif" onload="javascript:if(this.width>screen.width/2)this.width=screen.width/2" vspace=2 border=0>;<BR>&nbsp;if(file&nbsp;!=&nbsp;null)&nbsp;{<BR>&nbsp;PropertyConfigurator.configure(prefix+file);<BR>&nbsp;}<BR>&nbsp;}<BR>&nbsp;public&nbsp;void&nbsp;doGet(HttpServletRequest&nbsp;req,&nbsp;HttpServletResponse&nbsp;res)&nbsp;{<BR>&nbsp;}<BR>&nbsp;}<BR>&nbsp;这段代码很简单，可以看出，在加载的过程中，程序会读取/WEB-INF/log4j.properties这个文件<BR>&nbsp;这个文件就是本文的重点，也就是log4j的配置文件。</P>
<P>&nbsp;#&nbsp;Set&nbsp;root&nbsp;logger&nbsp;level&nbsp;to&nbsp;DEBUG&nbsp;and&nbsp;its&nbsp;only&nbsp;appender&nbsp;to&nbsp;A1&nbsp;<BR>&nbsp;#log4j中有五级logger&nbsp;<BR>&nbsp;#FATAL&nbsp;0&nbsp;<BR>&nbsp;#ERROR&nbsp;3<BR>&nbsp;#WARN&nbsp;4&nbsp;<BR>&nbsp;#INFO&nbsp;6&nbsp;<BR>&nbsp;#DEBUG&nbsp;7&nbsp;<BR>&nbsp;#配置根Logger，其语法为：<BR>&nbsp;#log4j.rootLogger&nbsp;=&nbsp;[&nbsp;level&nbsp;]&nbsp;,&nbsp;appenderName,&nbsp;appenderName,&nbsp;…<BR>&nbsp;log4j.rootLogger=INFO,&nbsp;A1&nbsp;,R<BR>&nbsp;#这一句设置以为着所有的log都输出<BR>&nbsp;#如果为log4j.rootLogger=WARN,&nbsp;则意味着只有WARN,ERROR,FATAL<BR>&nbsp;#被输出，DEBUG,INFO将被屏蔽掉.<BR>&nbsp;#&nbsp;A1&nbsp;is&nbsp;set&nbsp;to&nbsp;be&nbsp;a&nbsp;ConsoleAppender.&nbsp;<BR>&nbsp;#log4j中Appender有几层如控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX&nbsp;Syslog守护进程等<BR>&nbsp;#ConsoleAppender输出到控制台&nbsp;<BR>&nbsp;log4j.appender.A1=org.apache.log4j.ConsoleAppender&nbsp;<BR>&nbsp;#&nbsp;A1&nbsp;使用的输出布局，其中log4j提供4种布局.&nbsp;org.apache.log4j.HTMLLayout（以HTML表格形式布局）<BR>&nbsp;#org.apche.log4j.PatternLayout（可以灵活地指定布局模式），<BR>&nbsp;#org.apache.log4j.SimpleLayout（包含日志信息的级别和信息字符串），<BR>&nbsp;#org.apache.log4j.TTCCLayout（包含日志产生的时间、线程、类别等等信息）<BR>&nbsp;log4j.appender.A1.layout=org.apache.log4j.PatternLayout&nbsp;<BR>&nbsp;#灵活定义输出格式&nbsp;具体查看log4j&nbsp;javadoc&nbsp;org.apache.log4j.PatternLayout&nbsp;<BR>&nbsp;#d&nbsp;时间&nbsp;....&nbsp;<BR>&nbsp;log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd&nbsp;HH:mm:ss}&nbsp;[%c]-[%p]&nbsp;%m%n&nbsp;<BR>&nbsp;#R&nbsp;输出到文件&nbsp;RollingFileAppender的扩展，可以提供一种日志的备份功能。<BR>&nbsp;log4j.appender.R=org.apache.log4j.RollingFileAppender&nbsp;<BR>&nbsp;#日志文件的名称<BR>&nbsp;log4j.appender.R.File=log4j.log&nbsp;<BR>&nbsp;#日志文件的大小<BR>&nbsp;log4j.appender.R.MaxFileSize=100KB&nbsp;<BR>&nbsp;#&nbsp;保存一个备份文件<BR>&nbsp;log4j.appender.R.MaxBackupIndex=1&nbsp;<BR>&nbsp;log4j.appender.R.layout=org.apache.log4j.TTCCLayout<BR>&nbsp;#log4j.appender.R.layout.ConversionPattern=%-d{yyyy-MM-dd&nbsp;HH:mm:ss}&nbsp;[%c]-[%p]&nbsp;%m%n<BR>&nbsp;配置以这里就差不多了，如果你想更深入了解配置文件的各个细节，可以去查看docs。还有，在文章的最后面我们提供配置文件中一些主要的语法。下面我们来看看怎样在程序中使用log4j.<BR>&nbsp;1.4　Log4j的使用。<BR>&nbsp;使用Log4j，第一步就是获取日志记录器，这个记录器将负责控制日志信息。其语法为：<BR>&nbsp;public&nbsp;static&nbsp;Logger&nbsp;getLogger(&nbsp;String&nbsp;name)，<BR>&nbsp;必须在使用前要把这个类导入<BR>&nbsp;import&nbsp;org.apache.log4j.Logger;<BR>&nbsp;name一般是类文件的名字，如下：<BR>&nbsp;static&nbsp;Logger&nbsp;logger&nbsp;=&nbsp;Logger.getLogger&nbsp;("".class.getName&nbsp;()&nbsp;)&nbsp;;<BR>&nbsp;您就可以轻松地使用不同优先级别的日志记录语句插入到您想记录日志的任何地方，其语法如下：<BR>&nbsp;logger.debug&nbsp;(&nbsp;Object&nbsp;message&nbsp;)&nbsp;;<BR>&nbsp;logger.info&nbsp;(&nbsp;Object&nbsp;message&nbsp;)&nbsp;;<BR>&nbsp;logger.warn&nbsp;(&nbsp;Object&nbsp;message)&nbsp;;<BR>&nbsp;logger.error&nbsp;(&nbsp;Object&nbsp;message&nbsp;)&nbsp;;<BR>&nbsp;为什么这里要分级别的呢？试想一下，我们在写程序的时候，为了调试程序，会在很多会出错的地方加入大量的logger.info();信息。当然程序调试完毕，我们不需要这些输出信息了，那怎么办呢？以前的做法是把每个程序中的logger.info删除，但这是不现实的，如果程序不大还可以，但如果程序很多，做这些事情就很烦人了。但因为log4j分级别了，当我们不需要输出这样调试时用到的log.info()时，我们可以把输出的级别调高，如调到warn,或error级别，这样info级别及以下的级别就不会出输出了，是不是很方便的呢？<BR>&nbsp;其实除了这种使用方式，log4j还有其它的使用方面，不需要配置文件，直接在程序中定义输入出级别，层次等信息，如果要了解这方法的使用，可以参考文档。<BR>&nbsp;1.5。附注：<BR>&nbsp;以下是配置文件的一些重要的语法<BR>&nbsp;定义配置文件<BR>&nbsp;其实您也可以完全不使用配置文件，而是在代码中配置Log4j环境。但是，使用配置文件将使您的应用程序更加灵活。<BR>&nbsp;Log4j支持两种配置文件格式，一种是XML格式的文件，一种是Java特性文件（键=值）。下面我们介绍使用Java特性文件做为配置文件的方法：<BR>&nbsp;配置根Logger，其语法为：<BR>&nbsp;log4j.rootLogger&nbsp;=&nbsp;[&nbsp;level&nbsp;]&nbsp;,&nbsp;appenderName,&nbsp;appenderName,&nbsp;…<BR>&nbsp;其中，level&nbsp;是日志记录的优先级，分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别，优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别，您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别，则应用程序中所有DEBUG级别的日志信息将不被打印出来。<BR>&nbsp;appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。<BR>&nbsp;配置日志信息输出目的地Appender，其语法为<BR>&nbsp;log4j.appender.appenderName&nbsp;=&nbsp;fully.qualified.name.of.appender.class<BR>&nbsp;log4j.appender.appenderName.option1&nbsp;=&nbsp;value1<BR>&nbsp;…<BR>&nbsp;log4j.appender.appenderName.option&nbsp;=&nbsp;valueN<BR>&nbsp;其中，Log4j提供的appender有以下几种：<BR>&nbsp;org.apache.log4j.ConsoleAppender（控制台），<BR>&nbsp;org.apache.log4j.FileAppender（文件），<BR>&nbsp;org.apache.log4j.DailyRollingFileAppender（每天产生一个日志文件），org.apache.log4j.RollingFileAppender（文件大小到达指定尺寸的时候产生一个新的文件），<BR>&nbsp;org.apache.log4j.WriterAppender（将日志信息以流格式发送到任意指定的地方）<BR>&nbsp;配置日志信息的格式（布局），其语法为：<BR>&nbsp;log4j.appender.appenderName.layout&nbsp;=&nbsp;fully.qualified.name.of.layout.class<BR>&nbsp;log4j.appender.appenderName.layout.option1&nbsp;=&nbsp;value1<BR>&nbsp;…<BR>&nbsp;log4j.appender.appenderName.layout.option&nbsp;=&nbsp;valueN<BR>&nbsp;其中，Log4j提供的layout有以下几种：<BR>&nbsp;org.apache.log4j.HTMLLayout（以HTML表格形式布局），<BR>&nbsp;org.apache.log4j.PatternLayout（可以灵活地指定布局模式），<BR>&nbsp;org.apache.log4j.SimpleLayout（包含日志信息的级别和信息字符串），<BR>&nbsp;org.apache.log4j.TTCCLayout（包含日志产生的时间、线程、类别等等信息）<BR></P>
<P><BR><BR>本文引用通告地址： http://blog.csdn.net/zlfh/services/trackbacks/459662.aspx </P><img src ="http://www.blogjava.net/Victor/aggbug/10609.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-08-21 10:37 <a href="http://www.blogjava.net/Victor/articles/10609.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>