??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲精品国产精品乱码不99按摩 ,亚洲成人毛片,九九视频这里只有精品http://www.aygfsteel.com/ytfei/category/3100.htmlzh-cnSat, 30 Jan 2010 18:42:14 GMTSat, 30 Jan 2010 18:42:14 GMT60ant 的?/title><link>http://www.aygfsteel.com/ytfei/archive/2006/10/20/76397.html</link><dc:creator>飞翔</dc:creator><author>飞翔</author><pubDate>Fri, 20 Oct 2006 07:48:00 GMT</pubDate><guid>http://www.aygfsteel.com/ytfei/archive/2006/10/20/76397.html</guid><wfw:comment>http://www.aygfsteel.com/ytfei/comments/76397.html</wfw:comment><comments>http://www.aygfsteel.com/ytfei/archive/2006/10/20/76397.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/ytfei/comments/commentRss/76397.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/ytfei/services/trackbacks/76397.html</trackback:ping><description><![CDATA[ <blockquote>转自:http://www.csbus.com/forum/ShowDetail.asp?Ici_ID=849&ForumID=5&ForumName=%D4%B1%B9%A4%D6%AE%BC%D2<br /><br /><br />本文主要介绍 Ant 在多用户开发的情况下,如何?EclipseQƈ且根据不同的目标环境~译不同的部|包。文中首先介l一个场景,l出在开发、编译、部|过E中遇到的问题;然后介绍如何?Eclipse 来简化你?Ant 开发过E;文章的最后解释如何通过~写有效?Ant 脚本来提高你的工作效率?/blockquote> <!--START RESERVED FOR FUTURE USE INCLUDE FILES--> <!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --> <!--END RESERVED FOR FUTURE USE INCLUDE FILES--> <p>读者定位ؓh Java ?Ant 使用l验的开发h员?/p> <p>读者可以学习到如何使用 Ant 解决一些多用户开发环境中Q根据不同的目标环境~译成不同部|包的问题?/p> <p> <a name="N10061"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">工作场景</font> </span> </a> </p> <p>现在有一?web 目Q是多hq行开发的Q通过 CVS 来管理。另外该目q有一些测试h员,他们试部v在测试服务器上的应用E序Q发?bug 后通过 bug 理pȝ通知开发h员,在开发h员修?bug q经q本地测试后Q由专门的h负责出(check outQ代码,~译打包后部|到试服务器上?/p> <p>该项目的成员A是负责Z码、编译打包,q|到服务器上的h。除了这个Q务之外,他还是该目的编Eh员。在目q入试阶段后,A在得到组中别的成员修复了 bug q且入(check inQ了代码的消息后Q也有可能是A自己入了代码Q,A首先更新本地的代码,先在本地做测试,认修复?bug 后打?WAR 包部|到试服务器上Qƈ通知试人员已经修复?bugQ让试人员l箋q行试?/p> <p> <a name="N1006C"> <span id="wmqeeuq" class="smalltitle"> <strong> <font face="Arial" size="3">A的烦?/font> </strong> </span> </a> </p> <p>在该目中,有一些ؓ试方便开发而写的代码和面Q比如蟩q用戯证,但是在部|到试机环境的时候,需要删除这些代码和面Q另外作Z个具有灵zL和扩展性的应用E序Q又有一些配|文Ӟ配置文g中的gҎ环境的改变而变动。例如,在项目中使用?Log4j 记录日志Q需要给 Log4j 指定日志文g的保存\径,本地E序员开发的时候用的是 Windows pȝQ给 Log4j 指定的也?Windows 的文件系l,在测试阶D늚时候,需要部|到 Linux pȝ中,那么日志的保存\径也需要做相应的改动。部|到试服务器上的时候,除了 Log4j 需要改之外Q还有很多别的配|项目也需要变动,q且分散在各?package 中。小A的烦g随之而来Q每ơ他在做完本地测之后Q就Ҏ试机的需要逐个N|文Ӟ更改相应的|q删除那些ؓ试方便写的代码和页面,每天可能Ҏ需要做好几ơ这L事情Q最烦的是他在快做完Ҏ试机环境更改的时候,某开发h员突焉知A_“我又改了一点代码,刚检入,你再重打一个包吧。”,A又不得不从头开始做C轮的Z码、本地测试、更攚w|文件、删除不需要的文g、打包部|的工作。另外小A在测试阶D늚后期被通知要求除了每次生成一个测试环境的 WAR 包外q必ȝ成一个在产品环境下的 WAR 包,他做的事情就更多了?/p> <p>从上面的场景可以看出Q小A的工作效率低而且Ҏ出错Q甚x可能D整个目的工作效率低下。其实可以通过 Ant 来帮助小A快速而且有效地完成这个工作。在 Ant 中,Ҏ目标环境的需要,可以把所有要更改的配|文件的目集中写到某个配置文g中。这h据不同的目标环境得到不同的配|文ӞAnt 在编译包时根据不同的目标环境切换不同的配|文件即可。比如小A现在到的有 3 中环境:开发环境、测试环境以及品环境,Ҏq三U环境可以生成三个不同的配置文gQdevelop_deploy.property、test_deploy. property和product_deploy. propertyQ当A想生成不同的包时只需在这三个配置文g之间切换可以了?/p> <p>在正式开始编写脚本之前,我们需要下载安装相应的软g?/p> <ul> <li>EclipseQؓ了 Ant 的开发更加简单,我们选择?Eclipse3.1 作ؓ开发环境。当然你可以使用M你喜Ƣ的文本~辑工具来开发你?Ant。Eclipse 的最新版本可以在 <a ><u><font color="#5c81a7">http://eclipse.org/</font></u></a> 上下载? </li> <li>AntQAnt 是基?Java 的编译工P它就?C/C++ 中的 makeQ但是没?make 那样隄。Ant 的最新版本可以在 <a ><u><font color="#5c81a7">http://ant.apache.org/bindownload.cgi</font></u></a> 上下载。如果你?Eclipse 来开?AntQ则不用d独下?AntQ因为在 Eclipse 中已l集成了 Ant? </li> <li>CVS 客户端(cvs.exeQ:CVS 可以?<a ><u><font color="#5c81a7">http://www.nongnu.org/cvs/</font></u></a> 上下?</li> </ul> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="500" onload="javascript:if(this.width>500)this.width=500" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" onload="javascript:if(this.width>500)this.width=500" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width>500)this.width=500" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width>500)this.width=500" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N10092"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">?Eclipse3.1 来创?Ant 脚本</font> </span> </a> </p> <p>如果你?Eclipse 来编?AntQ徏议?Eclipse 3.1 以后的版本。除了以?Ant ~辑器提供的语法高亮Q提C法错误等功能外,Eclipse3.1 版本增加了许多新的功能。比如:脚本代码的折叠;快速定位某属性或者目标(targetQ段的定义;在同一 builder 文g中重构属性名和目标名Q快h式Alt + Shift +RQ;调试 Ant 脚本{?/p> <p>下面我们来看看 Eclipse 3.1 中对 Ant 的支?/p> <ul> <li> <p>打开“File”-“New”-“Project”-“Simple”-“Project”,点击“Next”,输入工程名“Ant”,然后点击直到“Finish?/p> </li> <li> <p>在新建的 Ant 工程中,新徏 Test.xmlQƈ且拷贝下面的脚本。该D脚本的内容׃做介l了Q我们主要看 Eclipse 提供了哪些功能。注意这时候打开的ƈ不是 Ant ~辑器,内Ҏ贝进M后,x打开的“Test.xml”,然后再重新打开它,q样 Eclipse ׃?Ant ~辑器打开它,q且也语法高亮了?/p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"><?xml version='1.0' encoding='UTF-8'?> <project name='Test' default='init' basedir='.'> <property name='test' value='test'/> <target name='init'> <echo>${test}</echo> </target> </project> </pre> </code> </td> </tr> </tbody> </table> <br /> </li> <li> <p>自动提示和代码折叠功能。如果是 Ant 内置的命令或者前面已l定义的变量Q编辑器会自动提C;~辑器右边的?减号可以代码折叠。如下所C:</p> <img alt="?Q编辑器双的加/减号可以代码折叠" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-ant-eclipse/image001.jpg" width="500" onload="javascript:if(this.width>500)this.width=500" /> </li> <li> <p>快速定位目标(targetQ或者定义变量处。在上图中,鼠标移?default=”init?中的 init 上之后,按下 ctrl 键,鼠标变成手状Q单d可以定位到定义该目标的地?/p> </li> <li> <p>快速重构目标名或者属性名。选中目标/属性名Q按?Alt + Shift + RQ然后键入你要修改后的|所有引用到的地斚w会随之改动。如下图所C,选中 init 后,按下快捷键,Ҏ initialQ?/p> <img alt="?Q改?initial" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-ant-eclipse/image002.jpg" onload="javascript:if(this.width>500)this.width=500" /> </li> <li> <p>调试 Ant 脚本。在标签?lt;target name=..”的左边讄一断点Q然后在~辑器中叛_Q出现的菜单中选“Debug As”-“Ant Build”,出现后的H口与调?Java E序的界面差不多?/p> <p>q是调试H口Q这里可以选择单步跟进、蟩出等Q?/p> <img alt="?Q选择单步跟进、蟩出等" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-ant-eclipse/image003.jpg" onload="javascript:if(this.width>500)this.width=500" /> <p>下面是运行时变量H口。可以看?test 变量的值是“test”:</p> <img alt="?Qtest 变量的值是 test" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-ant-eclipse/image004.jpg" onload="javascript:if(this.width>500)this.width=500" /> <p>q行l果H口Q?/p> <img alt="?Q运行结果窗? src="http://www-128.ibm.com/developerworks/cn/java/j-lo-ant-eclipse/image005.jpg" width="500" onload="javascript:if(this.width>500)this.width=500" /> </li> </ul> <p>由此可见Q如果用好 Eclipse Ant ~辑器所提供强大的功能的话能大大提高?Ant 脚本的效率?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="500" onload="javascript:if(this.width>500)this.width=500" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" onload="javascript:if(this.width>500)this.width=500" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width>500)this.width=500" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width>500)this.width=500" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N100DD"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">Ant 中?propertyQ属性)文g</font> </span> </a> </p> <p>刚开始写 Ant 的初学者可能会把所有的信息都放?build.xml 中,下面是q样的一个例子?/p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"><project name='testBuild' default='compile' basedir='.'> <target name='init'> <mkdir dir='c:/temp/dest/testProj' /> </target> <target name='compile' depends='init'> <javac srcdir='c:/temp/src/testProj' destdir='c:/temp/dest/testProj '/> <echo>Build into c:/temp/dest/testProj, successfully.</echo> </target> </project> </pre> </code> </td> </tr> </tbody> </table> <br /> <p>在上面的例子中,所有的路径信息都是写在 build.xml 中。但?Ant 脚本可能在不同的机器或者不同的pȝ上运行,也有可能一些值需要根据环境的不同而变化,?Ant 中可以把所有这些可能变化的地方都放C个配|文件中Q然后在 Ant 脚本中引用这个配|文件就可以了,针对上面的例子,如下所C:</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"><project name='testBuild' default='compile' basedir='.'> <property file='build.properties'/> <target name='init'> <mkdir dir='${dest.dir}' /> </target> <target name='compile' depends='init'> <javac srcdir='${src.dir}' destdir='${dest.dir}'/> <echo>Build into ${dest.dir}, successfully.</echo> </target> </project> build.properties的内容: dest.dir=c:/temp/dest/testProj src.dir=c:/temp/src/testProj </pre> </code> </td> </tr> </tbody> </table> <br /> <p>如果惛_ Ant 脚本中引用值的话,只需?W号开_在一?{}'中写入要引用的键倹{如上例中,需要引用编译的目标路径?${dest.dir}'?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="500" onload="javascript:if(this.width>500)this.width=500" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" onload="javascript:if(this.width>500)this.width=500" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width>500)this.width=500" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width>500)this.width=500" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N100F3"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">使用 Ant d?CVS 中检出(check outQ源代码Qƈ~译打包</font> </span> </a> </p> <p>Ant 中提供了 cvs dQTaskQ可以从 CVS 服务器中源(注意Q在使用 Ant ?cvs d之前Q请先将 cvs.exe 下蝲C的机器,q且它dC本地?PATH 环境变量中,然后重新启动 Eclipse。否则在执行下面脚本的时候就会得?error=2 的错误)。cvs 的可选用属性很多,在这里介l经怋用到的几个属性。从 CVS 中检源一般需要指定:</p> <ul> <li>CVS 所在的服务器地址Q目?CVS 服务器地址 </li> <li>用户名:d?CVS 服务器你指定的用户名 </li> <li>密码Q登录该 CVS 服务器需要的密码 </li> <li>库\径(Repository PathQ:服务器中的库路径 </li> <li>模块名:当前需要检出的模块名,一般都是以工程的名字作为模块名 </li> <li>标签名:需要从 CVS 中检出哪个标{?</li> </ul> <p>在介l?Ant ?cvs 之前Q先说一下本地的目录l构。在 C 盘的 temp 目录下,分别有四个目录,如下所C:</p> <ul> <li>build 目录Q放~译后的cM及资源文? </li> <li>dist 目录Q放生成?jar 文g或?war 文g </li> <li>lib 目录Q放在编译过E中需要用到的 jar 文g </li> <li>src 目录Q放?cvs 中检出的源文Ӟ包括 JSP {)<br /><img alt="?Qsrc 目录" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-ant-eclipse/image006.jpg" onload="javascript:if(this.width>500)this.width=500" /></li> </ul> <p>?Ant 中这样写可以从中检源:</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"> <cvs cvsRoot=':pserver:username:pwd@cvs.server:/home/testPath' package='TestProj' dest=' c:/temp/src/testProj ' failonerror='true' /> </pre> </code> </td> </tr> </tbody> </table> <br /> <p>q段脚本片断的意思就是从?cvs.server'的服务器中,用用户名?username、密码ؓ pwd 的用h出在库\径是 /home/testPath 下的 TestProj 模块Q项目)Q检出后的资源放入本地目?c:/temp/src/testProj 中。在上面q段脚本中,可以看到有很多值可能会Ҏ不同的环境或者用户随之改变的Q比如用户名和密码等Q而且从脚本的重复可利用性来_需要把有些值抽出来攑ֈ配置文g中,如服务器的地址和库路径{。因此把q些可能需要更改的地方攑ֈ property 文g中,效果会更好。改完后的完?Ant 脚本如下所C:</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"> <?xml version='1.0'?> <project name='testWeb' default='checkout' basedir='.'> <target name='checkout'> <property file='TestWeb.properties' /> <cvs cvsRoot='${cvs.root}' package='${cvs.projectName}' tag='${cvs.tag}' dest='${src.dir}' failonerror='true' /> </target> </project> </pre> </code> </td> </tr> </tbody> </table> <br /> <p>对应?TestWeb.properties 文g内容如下所C:</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section">base.dir=c:/temp/ src.dir=${base.dir}/src cvs.server=cvs.server cvs.user=username cvs.pw=pwd cvs.repositoryPath=/home/testPath cvs.projectName=TestProj cvs.root=:pserver:${cvs.user}:${cvs.pw}@${cvs.server}:${cvs.repositoryPath} cvs.tag= </pre> </code> </td> </tr> </tbody> </table> <br /> <p>在检Z资源后,需要对其进行编译打包。ؓ了 Ant 脚本更加h可读性和灉|性,我们需要对上面?Ant 脚本q行一些改动。首先将 Ant 脚本中进行分D,如下所C:</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"><?xml version='1.0'?> <project name='testWeb' default='all' basedir='.'> <target name='all' depends='init,clean,checkout,build'> <!--脚本的入口点--> </target> <target name='init'> <!--做初始化属性文件和讄classpath{设|初始条?-> </target> <target name='clean'> <!--删除上一ơ留下的没用的目录和文g--> </target> <target name='checkout'> <!--从CVS中检?-> </target> <target name='build'> <!--~译源文件ƈ打包到指定的目录--> </target> </project> </pre> </code> </td> </tr> </tbody> </table> <br /> <p>上面的脚本中Qd分成?个目标(targetQ,脚本的入口点?all'Qall 按顺序调?initQcleanQcheckoutQbuild。其中:</p> <ul> <li>init 是用来做初始化属性文件和讄 classpath {设|初始条件的事情 </li> <li>clean 用来删除上一ơ留下的没用的目录和文g </li> <li>checkout 已经介绍q了Q是用来?CVS 中检? </li> <li>build 用来~译源文件ƈ?WAR 包到指定的目?</li> </ul> <p>详细?Ant 脚本可以参见随本文所附的 TestWeb.xml ?TestWeb.properties?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="500" onload="javascript:if(this.width>500)this.width=500" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" onload="javascript:if(this.width>500)this.width=500" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width>500)this.width=500" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width>500)this.width=500" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N10159"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">~译q程与生不同目标环境的脚本分开执行</font> </span> </a> </p> <p>在前面介l的 Ant 脚本中,Ҏ?CVS 服务器中出的资源打成了一个默认的 war 包,q没有考虑Ҏ不同的目标环境来生成不同的包Q从下一节开始介l如何根据不同的环境来生成不同的部v包?/p> <p>q有一个问题是Qؓ什么需要把?CVS 中检源、编译的q程跟根据目标环境打包的q程分开Q?/p> <p>q是因ؓ本n CVS 源是需要花一定的旉Q如果资源比较多q个q程׃p挺长旉Q另外,在多人开发的情况下必M证在生成不同的部|包的时候是用的是同一套代码生成的Q否则会出现各个服务器上q行的版本不一_如果源、编译的q程跟生成包的脚本一h行的话就会出现这个问题(比如A在测试服务器试通过的时候之后,再生成一个在产品环境下的部v包,如果分两ơ从 CVS 服务器中源的话,在此期间可能会有开发h员往 CVS 服务器中入代码,D生成的版本不一_Q因此,必须这两个q程分开执行。现在我们开始徏立另外一?Ant 脚本文gQ叫 deploy.xmlQ专门用来生成包Q另外与 deploy.xml 相对应的q有一?deploy.properties 文g。在 deploy.xml 中会引用 deploy.properties 文g。另外根据在前面的场景中到的环境,创徏三个不同的属性文? develop_deploy.property、test_deploy. property ?product_deploy. PropertyQ在打包的时候,Ҏ不同的目标环境,相应属性文件中的内Ҏ贝至 deploy.properties 文g中(或者也可以直接?deploy.xml 中直接切换不同的属性文ӞQ然后在 Eclipse 中直接执?deploy.xmlQ如果在命o行中Q可以用下面的命令来执行Q?/p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"> ant –f deploy.xml </pre> </code> </td> </tr> </tbody> </table> <br /> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="500" onload="javascript:if(this.width>500)this.width=500" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" onload="javascript:if(this.width>500)this.width=500" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width>500)this.width=500" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width>500)this.width=500" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N1016B"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">解开 WAR ?/font> </span> </a> </p> <p>我们首先得徏立一个目录(q里?unpackQ用来存放解压后的文件。Ant 中提供了 unzip 命o用来解压 ear/war/jar 包。除了这个目录外Q根据不同的目标环境Q在q行目录下徏立一个与目标环境相对应的目录Q重新打好的 war 包就攑֜q个目录下,比如针对场景中的情况Q如果需要创Z个品环境下的部|包Q我们可以徏立一?TestWebProduct 目录Q目录名写在配置文g中(${pack.base.dir}Q?/p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"> <target name='init'> <echo>init in deploy</echo> <property file='deploy.properties' /> <delete dir='${unpack.base.dir}' failonerror='false' /> <delete dir='${pack.base.dir}' failonerror='false' /> <mkdir dir='${unpack.base.dir}' /> <mkdir dir='${pack.base.dir}' /> </target> <target name='unpack'> <echo>unpack the ${war.name}</echo> <copy file='${dest.dir}/${war.name}' todir='${unpack.base.dir}' /> <unzip src='${unpack.base.dir}/${war.name}' dest='${unpack.base.dir}/${projectName}' /> <delete file='${unpack.base.dir}/${war.name}' /> </target> </pre> </code> </td> </tr> </tbody> </table> <br /> <p>?init D中首先删除掉上ơ解压的目录和目标打包目录,然后重新建立目录Q在 unpack 中,首先编译好的默?war 包拷贝至 unpack 定义的目录,解压之后?unpack 下的 war 包删除。下面是q时候对应的属性文件?/p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section">projectName=MTSWeb war.name=MTSWeb.war #根目? base.dir=c:/temp #默认的war包所在的目录 dest.dir=${base.dir}/dist #解压后的目录 unpack.base.dir=${base.dir}/unpack #目标环境相对应的目录 pack.base.dir=${base.dir}/TestWebProduct </pre> </code> </td> </tr> </tbody> </table> <br /> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="500" onload="javascript:if(this.width>500)this.width=500" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" onload="javascript:if(this.width>500)this.width=500" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width>500)this.width=500" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width>500)this.width=500" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N1017E"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">利用 Ant 提供?filter d替换属性?/font> </span> </a> </p> <p>现在Ҏ不同环境的需要,Ҏ些配|文件的值做一些替换。在 Ant 中,提供?filter dQ得替换值很方便。当然也可以使用下面介绍的正则表辑ּ来替换属性倹{filter 主要用来在同一行内容中的替换,而正则表辑ּ一下子可以替换多行内容。filter 的用例子:</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"> <filter token=' log4j.logger' value='INFO'/> <copy todir='${dest.dir}' filtering='true'> <fileset dir='${src.dir}'/> </copy> </pre> </code> </td> </tr> </tbody> </table> <br /> <p>q段脚本的意思就是在 src.dir 目录下的所有文件中Q如果有预先定义好的'@log4j.logger@'占位W的话,在拷贝到 dest.dir 目录后,所有的占位W都被替换成?INFO'?/p> <p>你也可以所有被替换的值放到某个属性文件中Qfilter d属性文件中的每一个条目读出来q且讄成一?Filter。如下所C:</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"> <filter filtersfile='deploy_env.properties'/> <copy todir='${dest.dir}' filtering='true'> <fileset dir='${src.dir}'/> </copy> </pre> </code> </td> </tr> </tbody> </table> <br /> <p>上面的脚本表C所有在 deploy_env 中出现的条目被作ؓ一?filterQ在拯?dest.dir 目录后,所?src.dir 目录中存在的占位W将被替换成 deploy_env 中的倹{具体的例子可以参见随本文附带的 deploy.xmlQ?deploy_env.properties ?Test.properties?</p> <p>其中 deploy.xml ?ant 脚本Qdeploy_env.properties 中包含所有要替换的|?Test.properties 中是包含有占位符的资源文件?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="500" onload="javascript:if(this.width>500)this.width=500" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" onload="javascript:if(this.width>500)this.width=500" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width>500)this.width=500" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width>500)this.width=500" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N1019A"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">利用正则表达式替换属性?/font> </span> </a> </p> <p>Ant 中支持多U正则表辑ּQ在q行 Ant 的时候用哪种正则表达式可以通过讄 ant.regexp.regexpimpl 的值来切换QAnt 支持的的正则表达式有Q?/p> <ul> <li>java.util.regex package of JDK 1.4 </li> <li>jakarta-regexp </li> <li>installation dependencies </li> </ul> <p>正则表达式的例子Q?/p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"><replaceregexp byline='true'> <regexp pattern='正则表达?/> <substitution expression='要替换的?/> <fileset dir='${unpack.war.dir}/WEB-INF' includes='web.xml'/> </replaceregexp> </pre> </code> </td> </tr> </tbody> </table> <br /> <p>byline 属性用来确认被替换的时候是一ơ替换一行还是多行;pattern 属性用来指明正则表辑ּQsubstitution expression 中是替换的|替换的值都定义在相对应的配|文件中Qfileset 属性中?dir 用来指定被替换文件所在的目录Qincludes 用来指定要替换哪个文件。需要注意的是,如果在正则表辑ּ或者替换的g出现'<'的话Q需要用转义W?<'?/p> <p>?Eclipse3.1 中已l内|了Ҏ则表辑ּ的支持;但是如果你在命o行中q行需要正则表辑ּ支持的脚本的话,则需要自己将正则表达式的包下载下来加?classpath 中。在随文章的 deploy.xml 中提供了一个简单的替换属性文件的值的例子。正则表辑ּ的例子可以在本文所带的 deploy.xml 中找到?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="500" onload="javascript:if(this.width>500)this.width=500" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" onload="javascript:if(this.width>500)this.width=500" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width>500)this.width=500" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width>500)this.width=500" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N101BB"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">Ant 使用条g表达式,Ҏ属性值删除不需要的文g</font> </span> </a> </p> <p>在生成部|包的时候,q有可能会根据目标环境的不同Q删除一些不同的文g。比如在产品环境中那些作为测试需要的代码往往需要删除,但是试环境中ƈ不需要。因此就需要条件表辑ּ来做判断。如下所C:</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"><target name='checkTestEnv'> <condition property='isTestEnv'> <istrue value='${deploy.isTestEnv}' /> </condition> <antcall target='trueCondition' /> <antcall target='falseCondition' /> </target> <target name='trueCondition' if='isTestEnv'> <echo message='true condition in ${projectName}' /> </target> <target name='falseCondition' unless='isTestEnv'> <echo message='false condition in ${projectName}' /> </target> </pre> </code> </td> </tr> </tbody> </table> <br /> <p>在上面的例子中,先读?${deploy.isTestEnv} 的|在配|文?deloy.properties 中)Q根据读出的值对属?isTestEnv 讑ր|如果 ${deploy.isTestEnv} 的值是 trueQisTestEnv 的g?trueQ否则是 false。然后分别调用目标段 trueCondition ?falseCondition。在q里Q表辑ּ值的判断是用'istrue'Q在 Ant 中还提供了很多别的表辑ּQ比?not/and/orQequals {等Q具体关于条件表辑ּ的信息可以参考:<a target="_blank"><u><font color="#5c81a7">http://ant.apache.org/manual/CoreTasks/condition.html</font></u></a> Q该也可以在随下蝲下来的文档中扑ֈ?/p> <p>在段 trueCondition 中,判断 isTestEnvQ如果是真的话就q行Q否则不q行Q在D?falseCondition 中,也判?isTestEnvQ如果是假就q行Q否则不q行Q在D中可以Ҏ情况做相应的事情。条件判断式的例子可以在本文?deploy.xml 中找到?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="500" onload="javascript:if(this.width>500)this.width=500" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" onload="javascript:if(this.width>500)this.width=500" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width>500)this.width=500" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width>500)this.width=500" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N101D2"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">重新打包Qƈ拯C同的目录</font> </span> </a> </p> <p>在上面的替换q程完成后,调用 war ?unpack 目录下的内容重新打包?/p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <code> <pre class="section"><target name='repack'> <war destfile='${pack.base.dir}/${projectName}.war' basedir='${unpack.base.dir}/${projectName}' webxml='${unpack.base.dir}/${projectName}/WEB-INF/web.xml' manifest='${unpack.base.dir}/${projectName}/META-INF/MANIFEST.MF'> </war> </target> </pre> </code> </td> </tr> </tbody> </table> <br /> <p>详细的例子可以参见随本文的附?deploy.xml ?deploy.properties?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="500" onload="javascript:if(this.width>500)this.width=500" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" onload="javascript:if(this.width>500)this.width=500" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width>500)this.width=500" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width>500)this.width=500" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N101E1"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">l论</font> </span> </a> </p> <p>通过本文可以看出~写好有效的 Ant 脚本能很好的解决在编译部|包的时候出现的问题Q将那些变化的内Ҏ到配|文件中Q在部v的时候切换不同的配置文g可以实现生成不同的部v包。文中也介绍了如何?Eclipse 来提高你~写/调试 Ant 脚本的效率?/p> <img src ="http://www.aygfsteel.com/ytfei/aggbug/76397.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/ytfei/" target="_blank">飞翔</a> 2006-10-20 15:48 <a href="http://www.aygfsteel.com/ytfei/archive/2006/10/20/76397.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++学习W记http://www.aygfsteel.com/ytfei/archive/2006/06/25/54983.html飞翔飞翔Sun, 25 Jun 2006 05:35:00 GMThttp://www.aygfsteel.com/ytfei/archive/2006/06/25/54983.htmlhttp://www.aygfsteel.com/ytfei/comments/54983.htmlhttp://www.aygfsteel.com/ytfei/archive/2006/06/25/54983.html#Feedback0http://www.aygfsteel.com/ytfei/comments/commentRss/54983.htmlhttp://www.aygfsteel.com/ytfei/services/trackbacks/54983.htmlTeacher * t,tobj; People * p,pobj;

1.使用基类指针操作zcd象?p = &tobj;0只能讉K定义于基cȝ共有成员?必须基cL针强制{换ؓzcL针,才能讉Kzcȝ成员?(Teacher *) p-> ...
2.使用zcL针操作基cd??t = (Teacher *) &pobj ; q且如此只能讉K基类中的成员Q不能访问派生类成员Q?BR>
***********************************
虚函敎ͼ实现多态)
在基cMl方法加上virtual 标记Q派生类实现该方法?
则用时Q采用基cL针指向具体子cd象的时候,卛_以实现多台。通过指针调用的将是具体子cȝҎ法?BR>
U虚函数Q?BR>virtual double  method  const=0;   包含U虚函数的类UCؓ 抽象c!

cȝ析构函数应该被设|ؓ虚析构函?BR>

const Teacher t;//帔R对象

void method const(){} //帔R函数

帔R对象不能调用非常量成员函?BR>帔R函数不允许调用非帔R成员函数?BR>

**********

文g写对?BR>ofstream outFile("filename",ios::out| ios::binary);
outFile.write( (char *) &st,sizeof(st)); //st是一个类的对?BR>infile.read((char *)&st,sizeof(st)); //d出对?BR>
*******
throw 可以抛出Lcd的异?比如 int a=0; throw a;
catch(...){ //捕获Lcd的异?BR>;
}

catch(int){
//捕获整型异常
}

********
函数模板
template <class T>
void sortArray(T b[],int len);
cL?
template <class T>
class Test{
..
}
所以在cL板外面定义的成员函数都要以template <class T>开?BR>
实例化类对象. Test <int> test;


飞翔 2006-06-25 13:35 发表评论
]]>
memcached 的?/title><link>http://www.aygfsteel.com/ytfei/archive/2006/06/10/51787.html</link><dc:creator>飞翔</dc:creator><author>飞翔</author><pubDate>Sat, 10 Jun 2006 02:44:00 GMT</pubDate><guid>http://www.aygfsteel.com/ytfei/archive/2006/06/10/51787.html</guid><wfw:comment>http://www.aygfsteel.com/ytfei/comments/51787.html</wfw:comment><comments>http://www.aygfsteel.com/ytfei/archive/2006/06/10/51787.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.aygfsteel.com/ytfei/comments/commentRss/51787.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/ytfei/services/trackbacks/51787.html</trackback:ping><description><![CDATA[ <ul> <li>用户讉K|页Ӟ查看 memcached 中是否有当前用户?SESSION 数据Q?session_id() 作ؓ唯一标识W;如果数据存在Q则直接q回Q如果不存在Q再q行数据库连接,获取 SESSION 数据Qƈ此数据保存?memcached 中,供下ơ用; </li> <li>当前?PHP q行l束Q或使用?<a >session_write_close()</a>Q时Q会调用 My_Sess::write() ҎQ将数据写入数据库,q样的话Q每ơ仍然会有数据库操作Q对于这个方法,也需要进行优化。用一个全局变量Q记录用戯入页面时?SESSION 数据Q然后在 write() Ҏ内比较此数据与想要写入的 SESSION 数据是否相同Q不同才q行数据库连接、写入数据库Q同时将 memcached 中对应的对象删除Q如果相同的话,则表C?SESSION 数据未改变,那么可以不做Q何操作,直接q回了; </li> <li>那么用户 SESSION q期旉怎么解决呢?记得 memcached ?add() Ҏ有个q期旉参数 $exp 吗?把这个参数D|成于 SESSION 最大存zL间即可。另外别忘了l那些一直在U的用户延箋 SESSION 旉Q这个可以在 write() Ҏ中解冻I通过判断旉Q符合条件则更新数据库数据?</li> </ul> <img src ="http://www.aygfsteel.com/ytfei/aggbug/51787.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/ytfei/" target="_blank">飞翔</a> 2006-06-10 10:44 <a href="http://www.aygfsteel.com/ytfei/archive/2006/06/10/51787.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>?千万U邮件系l的设计及数据库中间仉分实?/title><link>http://www.aygfsteel.com/ytfei/archive/2006/06/09/51742.html</link><dc:creator>飞翔</dc:creator><author>飞翔</author><pubDate>Fri, 09 Jun 2006 09:27:00 GMT</pubDate><guid>http://www.aygfsteel.com/ytfei/archive/2006/06/09/51742.html</guid><wfw:comment>http://www.aygfsteel.com/ytfei/comments/51742.html</wfw:comment><comments>http://www.aygfsteel.com/ytfei/archive/2006/06/09/51742.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/ytfei/comments/commentRss/51742.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/ytfei/services/trackbacks/51742.html</trackback:ping><description><![CDATA[ <!--开始头?-> <script language="javascript" src="Huihoo Power! - 千万U邮件系l的设计及数据库中间仉分实?files/header.js"> </script>   <table cellspacing="0" cellpadding="0" width="760" align="center" border="0"><tbody><tr><td width="86"><a ><img height="23" src="http://www.huihoo.com/i/a01.gif" width="86" border="0" /></a></td><td width="91"><a ><img height="23" src="http://www.huihoo.com/i/a02.gif" width="91" border="0" /></a></td><td width="90"><a target="_blank"><img height="23" src="http://www.huihoo.com/i/a03.gif" width="90" border="0" /></a></td><td width="92"><a ><img height="23" src="http://www.huihoo.com/i/a04.gif" width="92" border="0" /></a></td><td width="92"><a ><img height="23" src="http://www.huihoo.com/i/a05.gif" width="92" border="0" /></a></td><td width="91"><a ><img height="23" src="http://www.huihoo.com/i/a06.gif" width="91" border="0" /></a></td><td width="93"><a ><img height="23" src="http://www.huihoo.com/i/a07.gif" width="93" border="0" /></a></td><td><img height="23" src="http://www.huihoo.com/i/title.gif" width="125" /></td></tr></tbody></table><table cellspacing="0" cellpadding="0" width="760" align="center" border="0"><tbody><tr><td width="525"><img height="99" src="http://www.huihoo.com/i/banner-001.gif" width="525" /></td><td><img height="99" src="http://www.huihoo.com/i/logo-001.gif" width="235" /></td></tr></tbody></table><table cellspacing="0" cellpadding="0" width="760" align="center" border="0"><tbody><tr><td><img height="3" src="http://www.huihoo.com/i/kong.gif" width="4" /></td></tr><tr><td>  <a >OS</a>  <a >Java</a>  <a >CORBA</a>  <a >COM+</a>  <a >Middleware</a>  <a >XML&WebService</a>  <a >Patterns</a>  <a >ONE&NET</a>  <a >P2P</a>  <a >Development</a>  <a >Database</a>  <a >Download</a>  <a >Doc</a></td></tr></tbody></table><table cellspacing="0" cellpadding="0" width="760" align="center" border="0"><tbody><tr><td><img height="3" src="http://www.huihoo.com/i//bar-001.gif" width="760" /></td></tr></tbody></table><!--l束头部--><!--开始中?-><table cellspacing="0" cellpadding="3" width="760" align="center" border="0"><tbody><tr width="100%"><td width="1%"></td><!--开始正?-><td width="98%"><br /><center><h4>千万U邮件系l的设计及数据库中间仉分实?/h4></center><br />(作者:艺龙公司技术研发经?高鹏) <br /><br /><p>  q日Q艺龙公司推Z“艺龙五星企业邮箱pȝ”和“艺龙个?U邮”的收费邮gpȝQ这两套pȝ都采用的是艺龙公怸全世界最大的邮gq营商mail.com合作设计和开发的千万U邮件系l:“ComConnect”。笔者是该系l的主要设计人员Q本文将介绍q套pȝ的技术特Ԍ以及数据库中间g部分实现?/p><p>  艺龙公司的“ComConnect”系l可以运行在大部分的unixpȝq_Q包括solaris、linux{,采用模块化的设计Q整个系l分Z面八大模块:</p><p>  <b>1Q数据库中间件模块+~存模块</b></p><p>    q是整套pȝ的主U,贯穿于系l的其他所有模块?/p><p>  在大规模邮gpȝ中,Z理的方便和pȝ的分布式部vQ都需要采用SQL数据库保存一些信息,例如帐号信息{。例如,当用户通过POP3登陆Ӟ服务器端会启动一个程序,与SQL数据库连接,q查询该用户w䆾的合法性,当用户退出时Q该E序会断开与SQL数据库的q接。这P大量用户的登陆、退出,都会对应于SQL数据库的q接、断开操作Q降低了pȝ的效率,也增加了SQL数据库的负蝲?/p><p><br />  在ComConnectpȝ中,所有数据库的操作,都是通过“数据库中间件”模块完成的。该模块会保持与SQL数据库的q接Q永不断开。当有程序需要查询数据库Ӟ会通过unix domain socket与本地的数据库中间g服务器相q接Q中间g会取得查询需求,q进行实际的SQL查询Qƈ结果通过unix domain socketq回l应用程序。同Ӟ对于所得的查询Q中间g服务器会q行本地~存Q保存到内存数据库BerkeleyDB3(http://www.sleepycat.com)中,q样下次同样的查询请求,׃必SQL查询了,因ؓl果已经被保存在本地~存了。这U设计,一斚wQ避免了频繁的SQL数据库的q接、断开的操作,另一斚wQ由于本地缓存的存在Q也大大降低了SQL数据库的负蝲Q提高了pȝ的响应效率?/p><p>  <b> 2Q收信模?</b></p><p><br />  该模块对应于DNS中的MX记录?/p><p>  传统的收信模块运行方式是Q在得到一邮件后Q保存到本地Q或通过SMTP协议q行转发。而在ComConnectpȝ中,采用LMTP(local mail transport protocol)协议q接“收信模块”和“邮件存储模块”,在这两个模块之间Q永久保持若q个通道Q当收信模块得到新的邮gӞ׃利用q些已经存在的通道邮件内容传输给后台的邮件存储模块。这U设计,大大减少了SMTP服务器频J的fork、exitq程Q从而提高了效率?/p><p>   <b>3Q发信模?/b></p><p>   该模块是整个pȝ中最独立的模块。ؓ了提高安全性,ComConnectpȝ通过cyrus-sasl实现了发信服务器的用戯n份认证功能。认证部分通过“认证模块”完成?/p><p>  <b> 4Q中央数据库模块</b></p><p><br />  与其他大规模邮gpȝ一PComConnectpȝ也需要中央数据库(支持SQL标准)模块。但与其他系l不同的是,ComConnectpȝ的中央数据库保存的内定w常少Q仅仅保存用户帐号信息。而且Q由于“数据库中间件”和“缓存”模块的存在Q在理想状态下Q中央数据库Ҏ不需要存在?/p><p>  q种设计Q极大地提高了系l性能Q更提高了系l的可扩展性。与此相对应Q其他的邮gpȝ尽可能多的信息都保存在中央数据库中Q随着用户数目的增大,效率会越来越低,最l成为系l的瓉?<br /><br /> <b>  5QHTTP session模块</b></p><p>  ׃HTTP协议的无状态性质Q所以当用户登陆后需要保存用Lsession(会话)信息。传l的session模块都是通过数据库完成的Q这P如果用户量非常大Q会Ҏ据库造成很大的负载,最lŞ成整套系l的瓉。ComConnectpȝ对session的处理,采用了自行开发的专门的session server处理Q系l拿Z台单独的服务器充当session serverQ内部定义了一套session协议来维护每个session的状态,q在一定时间客L没有讉K时自动删除session记录以实现session垃圾回收机制。该模块的数据库采用了高效的内存数据库:BerkeleyDB3。在q种设计下,web服务器作为session模块的客LQsession server作ؓsession模块的服务器端。当用户登陆ӞWeb服务器会通过session协议讉K后台的session serverQ以记录该次session的信息?/p><p>  与传l的session理机制相比Q这U方式减了中央数据库的负蝲Q又׃内存数据库的高效性,以及session协议的简单性,大大提高pȝ的响应速度?/p><p><b>   6Q认证模?/b></p><p>  pȝ需要认证的部分有:Web登陆、发信时的用戯n份认证、POP3登陆。这些认证都通过本地的认证模块实玎ͼ该模块工作机制与“数据库中间件”类|通过unix domain socketq行q程间通讯Qƈ与数据库持箋q接Q以及维护本地的~存?/p><p>  <b> 7QWeb模块</b></p><p>  q是用户使用的Web界面部分。该模块通过IMAP协议Q与后台的“邮件存储模块”进行通讯。其中的sessionQ是通过“HTTP session模块”完成的?/p><p><br />  <b> 8Q邮件存储模?/b></p><p>  该部分是整个pȝ最为核心的模块Q所有用L邮g最l都保存在该模块上。每个用L邮箱是一个目录,每封邮g是一个文件?/p><p><br />  有些大规模邮件系l将邮g的信?header)保存到数据库中,而邮件的正文(body)保存到文件中。这U设计,可以提高用户在访问邮时的速度Q尤其是邮箱中有很多邮件的时候,另外Q在实际实现的时候也有代码简单的优点。但它的~点也是H出的,首先Q“收信模块”要对进入的每封邮gq行处理Q以提取Z头和正文Q这会降低收信的效率Q另一斚wQ由于每邮仉会引发对数据库的INSERT操作Q因此会加大数据库的负担Q还有,随着pȝ接收邮g数目的增大,数据库中的记录数也会相应地增大,最l可能出现瓶颈?/p><p>  与上面的解决办法不同QComConnectpȝ在文件系l中建立了烦引机Ӟ而不采用数据库的索引。每个用L邮箱(对应一个目?下,都有该用h有邮件的索引。这P当用h作邮时Q速度可以非常快;另外Q对每封q入的邮Ӟ“收信模块”不需要做M处理Q可以直接通过LMTP通道传输l“邮件存储模块”,而“邮件存储模块”直接将内容写入对应用户的邮q录下?/p><p><br />  q八大模块构成了ComConnect整个pȝQ其中每个模块都h很好的扩展机Ӟ可以通过增加计算机数目来提高性能。更详细的信息,误问: http://bms.elong.com/ads/</p><p><br />下面是数据库中间件的部分代码Q读者可以从中了解其q行机制Q?/p><p><br />dbServer dbserver = db_unknown;</p><p>int midInited = 0;</p><p><br />/*</p><p>*--------------------------------------------------------------</p><p>*</p><p>* mid_init</p><p>*</p><p>*--------------------------------------------------------------</p><p>*/</p><p>void</p><p>mid_init(const char *conf)</p><p>{</p><p>  const char fname[] = "mid_init";</p><p>  const char *server ; </p><p><br />  if ( !conf || !*conf ) {</p><p>    myconfig_read(MIDWARE_CONFIG_FIENAME);</p><p>  } else {</p><p>    myconfig_read(conf);</p><p>  }</p><p>  server = myconfig_getstring("backend_server",NULL);</p><p>  if ( !server || !*server ) {</p><p>    syslog(LOG_ERR,"[%s]invalid 'backend_server' in conf file",fname);</p><p>exit(1); /* yes, we exit directly */</p><p>  }</p><p><br />  /* judge what backend server is */</p><p>  if ( !strcasecmp(server,BACKEND_MYSQL) ) {</p><p>    syslog(LOG_DEBUG3,"[%s]backend server is %s",fname,BACKEND_MYSQL);</p><p>    dbserver = db_mysql;</p><p>  } else if ( !strcasecmp(server,BACKEND_ORACLE) ) {</p><p>    syslog(LOG_DEBUG3,"[%s]backend server is %s",fname,BACKEND_ORACLE);</p><p>    dbserver = db_oracle;</p><p>  } else if ( !strcasecmp(server,BACKEND_BERKELEYDB) ) {</p><p>    syslog(LOG_DEBUG3,"[%s]backend server is %s",fname,BACKEND_BERKELEYDB);</p><p>    dbserver = db_berkeleydb;</p><p>  } else {</p><p>    dbserver = db_unknown;</p><p>    syslog(LOG_CRIT,"[%s]unknown backend server: %s",fname,server);</p><p>exit(1);</p><p>  } </p><p><br />  midInited = 1;</p><p><br />} /* mid_init */</p><p><br />/*</p><p>*------------------------------------------------------------------------</p><p>*</p><p>* sendCmd</p><p>*</p><p>* -- midware protocol implementation on midwared client side</p><p>*</p><p>* ask midware server something via unix domain socket</p><p>*</p><p>* RET:</p><p>* CMD_ERR_SYS: system error</p><p>* CMD_INVALID: null cmd sent to midwared server</p><p>* CMD_OK : ok</p><p>*</p><p>*------------------------------------------------------------------------</p><p>*/</p><p>static cmdResult</p><p>sendCmd(char *cmd,char *sqlcmd,const char *param2,const char *param3,</p><p>      const char *param4,char **reply)</p><p>{</p><p>  const char fname[]="sendCmd";</p><p>  int s; int r,iovcount=0;</p><p>  struct sockaddr_un srvaddr;</p><p>  struct iovec iov[6];</p><p>  static char response[MAX_REP_LEN];</p><p>  char sockfile[1024];</p><p>  int start, n;</p><p></p><p>  if (reply) *reply = NULL;</p><p>  if ( !cmd || !*cmd ) {</p><p>    syslog(LOG_ERR,"[%s]null cmd specified",fname);</p><p>    return CMD_INVALID;</p><p>  }</p><p></p><p>  /* create socket and connect to midware server */</p><p>  s = socket(AF_UNIX, SOCK_STREAM, 0);</p><p>  if (s == -1) {</p><p>    syslog(LOG_CRIT,"[%s]create socket failed: %m",fname);</p><p>return CMD_ERR_SYS;</p><p>  }</p><p>  memset(sockfile,0,sizeof(sockfile));</p><p>  strncpy(sockfile, myconfig_getstring("unixsock_dir",DEFAULT_MIDWARED_DIR) ,</p><p>    sizeof(sockfile));</p><p>  strcat(sockfile,"/");</p><p>  strcat(sockfile,SOCKET_FILENAME);</p><p>  syslog(LOG_DEBUG3,"[%s]socket filename: %s",fname,sockfile);</p><p>  memset((char *)&srvaddr, 0, sizeof(srvaddr));</p><p>  srvaddr.sun_family = AF_UNIX;</p><p>  strncpy(srvaddr.sun_path, sockfile, sizeof(srvaddr.sun_path));</p><p>  r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));</p><p>  if (r == -1) {</p><p>    if (reply) *reply="Cannot connect to midwared server";</p><p>    syslog(LOG_ERR,"[%s]:connect: %m",fname);</p><p>    return CMD_ERR_SYS;</p><p>  } </p><p></p><p>  /* </p><p>   * connected with the unix-domain socket ,next </p><p>   * prepare the parameters for midwared server </p><p>   */</p><p>  iov[iovcount].iov_base = (char *)cmd;</p><p>  iov[iovcount].iov_len = strlen(cmd)+1;</p><p>  /* check sqlcmd */</p><p>  if ( sqlcmd && *sqlcmd ){</p><p>iovcount++;</p><p>    iov[iovcount].iov_base = (char *)sqlcmd;</p><p>    iov[iovcount].iov_len = strlen(sqlcmd)+1;</p><p>  } else {</p><p>    goto startsend;</p><p>  }</p><p>  /* check parameter2 */</p><p>  if ( param2 && *param2 ){</p><p>iovcount++;</p><p>    iov[iovcount].iov_base = (char *)param2;</p><p>    iov[iovcount].iov_len = strlen(param2)+1;</p><p>  } else {</p><p>    goto startsend;</p><p>  }</p><p>  /* check parameter3 */</p><p>  if ( param3 && *param3 ){</p><p>iovcount++;</p><p>    iov[iovcount].iov_base = (char *)param3;</p><p>    iov[iovcount].iov_len = strlen(param3)+1;</p><p>  } else { </p><p>    goto startsend;</p><p>  }</p><p>  /* check parameter4 */</p><p>  if ( param4 && *param4 ){</p><p>iovcount++;</p><p>    iov[iovcount].iov_base = (char *)param4;</p><p>    iov[iovcount].iov_len = strlen(param4)+1;</p><p>  }</p><p><br />startsend:</p><p>  retry_writev(s, iov, iovcount+1);</p><p></p><p>  /* get reply from midwared server */</p><p>  start = 0;</p><p>  while (start     n = read(s, response+start, sizeof(response) - 1 - start);</p><p>    if (n <1) break; </p><p>    start += n;</p><p>  } /* while */</p><p>  close(s);</p><p></p><p>  /* marshell the reply for client call */</p><p>  if ( start <) =1 { </p><p>    /* failurely got the reply */</p><p>    if ( reply ) {</p><p>*reply=response;</p><p>}</p><p>    return CMD_ERR_SYS;</p><p>  } else {</p><p>    /* successfully got the reply */</p><p>    response[start] = '\0';</p><p>    if (reply) { </p><p>*reply=response;</p><p>}</p><p>    return CMD_OK;</p><p>  }</p><p><br />} /* sendCmd */</p><p><br />/*</p><p>*--------------------------------------------------------------</p><p>*</p><p>* mid_query</p><p>* - exported for end user directly</p><p>*</p><p>* ARG -</p><p>* key: used for cache</p><p>*</p><p>* PRECON:</p><p>* argument 'result' must be initialized before this call</p><p>*</p><p>*--------------------------------------------------------------</p><p>*/</p><p>midQueryResult</p><p>mid_query(char *sqlcmd,const char *key,int useCache,char result[])</p><p>{</p><p>  extern char *cache_mapstr(int);</p><p>  const char fname[]="mid_query";</p><p>  char *reply;</p><p>  int r = 0;</p><p><br />  if ( midInited == 0 ) {</p><p>    mid_init(NULL);</p><p>  }</p><p>                </p><p>  switch ( dbserver ) {</p><p>    case db_mysql:</p><p>      r = sendCmd(REQ_MYSQL_QUERY,sqlcmd,key,cache_mapstr(useCache),</p><p>        NULL,(char **)&reply);</p><p>      break;</p><p></p><p>    case db_oracle:</p><p>      r = sendCmd(REQ_ORACLEL_QUERY,sqlcmd,key,cache_mapstr(useCache),</p><p>        NULL,(char **)&reply);</p><p>      break;</p><p></p><p>    case db_berkeleydb:</p><p>      break;</p><p></p><p>    default:</p><p>      syslog(LOG_CRIT,"[%s]unknown backend server",fname);</p><p>      break;</p><p>  } /* switch */</p><p><br />  /* let midware client know what happened */</p><p>  if ( result && *result ) strcpy(result,reply);</p><p></p><p>  if ( r != CMD_OK ) {</p><p>    syslog(LOG_DEBUG3,"[%s]no reply,system error",fname);</p><p>return QUERY_SYS_ERR;</p><p>  }</p><p><br />  if ( !strcmp(reply,REP_DB_QUERY_FAIL) ) {</p><p>    return QUERY_FAIL;</p><p>  } else if ( !strcmp(reply,REP_DB_QUERY_NOTFOUND) ) {</p><p>    return QUERY_NOTFOUND;</p><p>  } else if ( !strcmp(reply,REP_DB_QUERY_MANYRECORD) ) {</p><p>    return QUERY_FOUNDMANY;</p><p>  } else {</p><p>    return QUERY_OK;</p><p>  }</p><p><br />} /* mid_query */</p><p><br />/*</p><p>*--------------------------------------------------------------</p><p>*</p><p>* mid_insert</p><p>*</p><p>*--------------------------------------------------------------</p><p>*/</p><p>midInsertResult</p><p>mid_insert(char *sqlcmd)</p><p>{</p><p>  const char fname[]="mid_insert";</p><p>  char *reply;</p><p>  int r = 0;</p><p>                </p><p>  if ( midInited == 0 ) {</p><p>    mid_init(NULL);</p><p>  }</p><p>                </p><p>  switch ( dbserver ) {</p><p>    case db_mysql:</p><p>      r = sendCmd(REQ_MYSQL_INSERT,sqlcmd,NULL,NULL,NULL,(char **)&reply);</p><p>      break;</p><p></p><p>    case db_oracle:</p><p>      r = sendCmd(REQ_ORACLE_INSERT,sqlcmd,NULL,NULL,NULL,</p><p>(char **)&reply);</p><p>      break;</p><p></p><p>    case db_berkeleydb:</p><p>      break;</p><p></p><p>    default:</p><p>      syslog(LOG_CRIT,"[%s]unknown backend server",fname);</p><p>      break;</p><p>  } /* switch */</p><p></p><p>  if ( r != CMD_OK ) {</p><p>    syslog(LOG_DEBUG3,"[%s]no reply,system error",fname);</p><p>return INSERT_SYS_ERR;</p><p>  }</p><p><br />  if ( !strcmp(reply,REP_DB_INSERT_FAIL) ) {</p><p>    return INSERT_FAIL;</p><p>  } else {</p><p>    return INSERT_OK;</p><p>  }</p><p><br />} /* mid_insert */</p><p></p></td><!--l束正文--><td width="1%"></td></tr></tbody></table><!--l束中部--><!--开始底?-><script language="javascript" src="Huihoo Power! - 千万U邮件系l的设计及数据库中间仉分实?files/bottom.js"></script><table cellspacing="0" cellpadding="0" width="760" align="center" border="0"><tbody><tr><td align="middle"><font size="2">© 2006 Huihoo</font><br /></td></tr></tbody></table><!--l束底部--><img src ="http://www.aygfsteel.com/ytfei/aggbug/51742.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/ytfei/" target="_blank">飞翔</a> 2006-06-09 17:27 <a href="http://www.aygfsteel.com/ytfei/archive/2006/06/09/51742.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>?在JavaE序中截h制台输出http://www.aygfsteel.com/ytfei/archive/2006/05/21/47293.html飞翔飞翔Sun, 21 May 2006 07:52:00 GMThttp://www.aygfsteel.com/ytfei/archive/2006/05/21/47293.htmlhttp://www.aygfsteel.com/ytfei/comments/47293.htmlhttp://www.aygfsteel.com/ytfei/archive/2006/05/21/47293.html#Feedback0http://www.aygfsteel.com/ytfei/comments/commentRss/47293.htmlhttp://www.aygfsteel.com/ytfei/services/trackbacks/47293.html

在JavaE序中截h制台输出

文档提供者:newebug () ?2005-3-15

在JavaE序中截h制台输出
  
  
  
  
 

俞良?/a> (javaman@163.net)
软g工程师,独立N和自由撰Eh
2001 q?10 ?/p>

在Java开发中Q控制台输出仍是一个重要的工具Q但默认的控制台输出有着各种各样的局限。本文介l如何用Java道截取控制台输出Q分析管道流应用中应该注意的问题Q提供了截取JavaE序和非JavaE序控制台输出的实例?/blockquote>

即在图形用L面占l治C的今天,控制台输Z旧在JavaE序中占有重要地位。控制台不仅是JavaE序默认的堆栈跟t和错误信息输出H口Q而且q是一U实用的调试工具Q特别是对习惯于使用println()的h来说Q。然而,控制台窗口有着许多局限。例如在Windows 9xq_上,DOS控制台只能容U?0行输出。如果JavaE序一ơ性向控制台输出大量内容,要查看这些内容就很困难了?/p>

对于使用javawq个启动E序的开发者来_控制台窗口尤其宝c因为用javaw启动javaE序ӞҎ不会有控制台H口出现。如果程序遇C问题q抛出异常,Ҏ无法查看Javaq行时环境写入到System.out或System.err的调用堆栈跟t信息。ؓ了捕获堆栈信息,一些h采取了用try/catch()块封装main()的方式,但这U方式不一定L有效Q在Javaq行时的某些时刻Q一些描q性错误信息会在抛出异怹前被写入System.out和System.errQ除非能够监这两个控制台流Q否则这些信息就无法看到?/p>

因此Q有些时候检查Javaq行时环境(或第三方E序Q写入到控制台流的数据ƈ采取合适的操作是十分必要的。本文讨论的主题之一是创徏q样一个输入流Q从q个输入中可以d以前写入Java控制台流Q或M其他E序的输出流Q的数据。我们可以想象写入到输出的数据立即以输入的形式“回”到了JavaE序?/p>

本文的目标是设计一个基于Swing的文本窗口显C控制台输出。在此期_我们q将讨论一些和Java道(PipedInputStream和PipedOutputStreamQ有关的重要注意事项。图一昄了用来截取和昄控制台文本输出的JavaE序Q用L面的核心是一个JTextArea。最后,我们q要创徏一个能够捕获和昄其他E序Q可以是非Java的程序)控制台输出的单程序?/p>


图一Q多U程的控制台输出截取E序


一、Java道?/span>
要在文本框中昄控制台输出,我们必须用某U方法“截取”控制台。换句话_我们要有一U高效地d写入到System.out和System.err所有内容的Ҏ。如果你熟悉Java的管道流PipedInputStream和PipedOutputStreamQ就会相信我们已l拥有最有效的工兗?/p>

写入到PipedOutputStream输出的数据可以从对应的PipedInputStream输入读取。Java的管道流极大地方便了我们截取控制台输出。Listing 1昄了一U非常简单的截取控制台输出方案?/p>
【Listing 1Q用道截取控制台输出?
PipedInputStream pipedIS = new PipedInputStream();
PipedOutputStream pipedOS = new PipedOutputStream();
try {
   pipedOS.connect(pipedIS);
}
catch(IOException e) {
   System.err.println("q接p|");
   System.exit(1);
}
PrintStream ps = new PrintStream(pipedOS);
System.setOut(ps);
System.setErr(ps);


可以看到Q这里的代码极其单。我们只是徏立了一个PipedInputStreamQ把它设|ؓ所有写入控制台的数据的最l目的地。所有写入到控制台流的数据都被{到PipedOutputStreamQ这P从相应的PipedInputStreamd可以迅速地截获所有写入控制台的数据。接下来的事情似乎只剩下在Swing JTextArea中显CZpipedIS读取的数据Q得C个能够在文本框中昄控制台输出的E序。遗憄是,在用Java道时有一些重要的注意事项。只有认真对待所有这些注意事Ҏ能保证Listing 1的代码稳定地q行。下面我们来看第一个注意事V?/p>

1.1 注意事项一
PipedInputStreamq用的是一?024字节固定大小的@环缓冲区。写入PipedOutputStream的数据实际上保存到对应的PipedInputStream的内部缓冲区。从PipedInputStream执行L作时Q读取的数据实际上来自这个内部缓冲区。如果对应的PipedInputStream输入~冲区已满,M企图写入PipedOutputStream的线E都被d。而且q个写操作线E将一直阻塞,直至出现dPipedInputStream的操作从~冲区删除数据?/p>

q意味着Q向PipedOutputStream写数据的U程不应该是负责从对应PipedInputStreamd数据的唯一U程。从图二可以清楚地看里的问题所在:假设U程t是负责从PipedInputStreamd数据的唯一U程Q另外,假定t企图在一ơ对PipedOutputStream的write()Ҏ的调用中向对应的PipedOutputStream写入2000字节的数据。在tU程d之前Q它最多能够写?024字节的数据(PipedInputStream内部~冲区的大小Q。然而,一旦t被阻塞,dPipedInputStream的操作就再也不会出现Q因为t是唯一dPipedInputStream的线E。这PtU程已经完全被阻塞,同时Q所有其他试囑֐PipedOutputStream写入数据的线E也遇到同L情Ş?/p>


图二Q管道流工作q程


qƈ不意味着在一ơwrite()调用中不能写入多?024字节的数据。但应当保证Q在写入数据的同Ӟ有另一个线E从PipedInputStreamd数据?/p>

Listing 2C了这个问题。这个程序用一个线E交替地dPipedInputStream和写入PipedOutputStream。每ơ调用write()向PipedInputStream的缓冲区写入20字节Q每ơ调用read()只从~冲取ƈ删除10个字节。内部缓冲区最l会被写满,D写操作阻塞。由于我们用同一个线E执行读、写操作Q一旦写操作被阻塞,׃能再从PipedInputStreamd数据?/p>
【Listing 2Q用同一个线E执行读/写操作导致线E阻塞?

import java.io.*;
public class Listing2 {
    static PipedInputStream pipedIS = new PipedInputStream();
    static PipedOutputStream pipedOS = 
        new PipedOutputStream();

    public static void main(String[] a){
        try {
            pipedIS.connect(pipedOS);
        }
        catch(IOException e) {
            System.err.println("q接p|");
                System.exit(1);
            }

        byte[] inArray    = new byte[10];
        byte[] outArray = new byte[20];
        int bytesRead = 0;

        try {
            // 向pipedOS发?0字节数据
            pipedOS.write(outArray, 0, 20);
            System.out.println("     已发?0字节...");

           // 在每一ơ@环P代中Q读?0字节
           // 发?0字节
            bytesRead = pipedIS.read(inArray, 0, 10);
            int i=0;
            while(bytesRead != -1) {
                pipedOS.write(outArray, 0, 20);
                System.out.println("     已发?0字节..."+i);
                i++;
                bytesRead = pipedIS.read(inArray, 0, 10);
            }
        }
        catch(IOException e) {
                System.err.println("dpipedIS时出现错? " + e);
                System.exit(1);
        }
    } // main()
}


只要把读/写操作分开C同的U程QListing 2的问题就可以L地解冟뀂Listing 3是Listing 2l过修改后的版本Q它在一个单独的U程中执行写入PipedOutputStream的操作(和读取线E不同的U程Q。ؓ证明一ơ写入的数据可以过1024字节Q我们让写操作线E每ơ调用PipedOutputStream的write()Ҏ时写?000字节。那么,在startWriterThread()Ҏ中创建的U程是否会阻塞呢Q按照Javaq行时线E调度机Ӟ它当然会d。写操作在阻塞之前实际上最多只能写?024字节的有效蝲P即PipedInputStream~冲区的大小Q。但qƈ不会成ؓ问题Q因ZU程QmainQ很快就会从PipedInputStream的@环缓冲区d数据Q空出缓冲区I间。最l,写操作线E会从上一ơ中止的地方重新开始,写入2000字节有效载荷中的剩余部分?/p>
【Listing 3Q把?写操作分开C同的U程?
import java.io.*;

public class Listing3 {
    static PipedInputStream pipedIS =
        new PipedInputStream();
    static PipedOutputStream pipedOS =
        new PipedOutputStream();

    public static void main(String[] args) {
        try {
            pipedIS.connect(pipedOS);
        }
        catch(IOException e) {
            System.err.println("q接p|");
            System.exit(1);
        }

        byte[] inArray = new byte[10];
        int bytesRead = 0;

        // 启动写操作线E?
        startWriterThread();

        try {
            bytesRead = pipedIS.read(inArray, 0, 10);
            while(bytesRead != -1) {
                System.out.println("已经d" +
                    bytesRead + "字节...");
                bytesRead = pipedIS.read(inArray, 0, 10);
            }
        }
        catch(IOException e) {
            System.err.println("d输入错误.");
            System.exit(1);
        }
    } // main()

    // 创徏一个独立的U程
    // 执行写入PipedOutputStream的操?
    private static void startWriterThread() {
        new Thread(new Runnable() {
            public void run() {
                byte[] outArray = new byte[2000];

                while(true) { // 无终止条件的循环
                    try {
                        // 在该U程d之前Q有最?024字节的数据被写入
                        pipedOS.write(outArray, 0, 2000);
                    }
                    catch(IOException e) {
                        System.err.println("写操作错?);
                        System.exit(1);
                    }
                    System.out.println("     已经发?000字节...");
                }
            }
        }).start();
    } // startWriterThread()
} // Listing3


也许我们不能说这个问题是Java道设计上的缺P但在应用道时Q它是一个必d切注意的问题。下面我们来看看W二个更重要Q更危险的)问题?/p>

1.2 注意事项?/b>
从PipedInputStreamd数据Ӟ如果W合下面三个条gQ就会出现IOException异常Q?/p>

  1. 试图从PipedInputStreamd数据Q?
  2. PipedInputStream的缓冲区为“空”(即不存在可读取的数据Q,
  3. 最后一个向PipedOutputStream写数据的U程不再zdQ通过Thread.isAlive())?


q是一个很微妙的时刻,同时也是一个极光要的时刻。假定有一个线Ew向PipedOutputStream写入数据Q另一个线Er从对应的PipedInputStreamd数据。下面一pd的事件将DrU程在试图读取PipedInputStream旉到IOException异常Q?/p>

  1. w向PipedOutputStream写入数据?
  2. wl束Qw.isAlive()q回falseQ?
  3. r从PipedInputStreamdw写入的数据,清空PipedInputStream的缓冲区?
  4. r试图再次从PipedInputStreamd数据。这时PipedInputStream的缓冲区已经为空Q而且w已经l束Q从而导致在L作执行时出现IOException异常?


构造一个程序示范这个问题ƈ不困难,只需从Listing 3的startWriterThread()Ҏ中,删除while(true)条g。这个改动阻止了执行写操作的Ҏ循环执行Q得执行写操作的方法在一ơ写入操作之后就l束q行。如前所qͼ此时ȝE试图读取PipedInputStraemӞ׃遇到一个IOException异常?/p>

q是一U比较少见的情况Q而且不存在直接修正它的方法。请不要通过从管道流z子类的方法修正该问题——在q里使用l承是完全不合适的。而且Q如果Sun以后改变了管道流的实现方法,现在所作的修改不再有效?/p>

最后一个问题和W二个问题很怼Q不同之处在于,它在ȝE(而不是写U程Q结束时产生IOException异常?/p>

1.3 注意事项?/b>
如果一个写操作在PipedOutputStream上执行,同时最q从对应PipedInputStreamd的线E已l不再活动(通过Thread.isAlive())Q则写操作将抛出一个IOException异常。假定有两个U程w和rQw向PipedOutputStream写入数据Q而r则从对应的PipedInputStreamd。下面一pd的事件将DwU程在试囑ֆ入PipedOutputStream旉到IOException异常Q?/p>

  1. 写操作线Ew已经创徏Q但rU程q不存在?
  2. w向PipedOutputStream写入数据?
  3. ȝEr被创建,q从PipedInputStreamd数据?
  4. rU程l束?
  5. w企图向PipedOutputStream写入数据Q发现r已经l束Q抛出IOException异常?


实际上,q个问题不象W二个问题那h手。和多个ȝE?单个写线E的情况相比Q也许在应用中有一个读U程Q作为响应请求的服务器)和多个写U程Q发求)的情冉|为常见?/p>

1.4 解决问题
要防止管道流前两个局限所带来的问题,Ҏ之一是用一个ByteArrayOutputStream作ؓ代理或替代PipedOutputStream。Listing 4昄了一个LoopedStreamsc,它用一个ByteArrayOutputStream提供和Java道类似的功能Q但不会出现死锁和IOException异常。这个类的内部仍旧用管道流Q但隔离了本文介l的前两个问题。我们先来看看这个类的公用方法(参见?Q。构造函数很单,它连接管道流Q然后调用startByteArrayReaderThread()ҎQ稍后再讨论该方法)。getOutputStream()Ҏq回一个OutputStreamQ具体地_是一个ByteArrayOutputStreamQ用以替代PipedOutputStream。写入该OutputStream的数据最l将在getInputStream()Ҏq回的流中作入出现。和使用PipedOutputStream的情形不同,向ByteArrayOutputStream写入数据的线E的ȀzR写数据、结束不会带来负面效果?/p>


图三QByteArrayOutputStream原理


【Listing 4Q防止管道流应用中出现的常见问题?
import java.io.*;

public class LoopedStreams {
    private PipedOutputStream pipedOS = 
        new PipedOutputStream();
    private boolean keepRunning = true;
    private ByteArrayOutputStream byteArrayOS =
        new ByteArrayOutputStream() {
        public void close() {
            keepRunning = false;
            try {
                super.close();
                pipedOS.close();
            }
            catch(IOException e) {
                // 记录错误或其他处?
                // 为简单计Q此处我们直接结?
                System.exit(1);
            }
        }
    };


    private PipedInputStream pipedIS = new PipedInputStream() {
        public void close() {
            keepRunning = false;
            try    {
                super.close();
            }
            catch(IOException e) {
                // 记录错误或其他处?
                // 为简单计Q此处我们直接结?
                System.exit(1);
            }
        }
    };


    public LoopedStreams() throws IOException {
        pipedOS.connect(pipedIS);
        startByteArrayReaderThread();
    } // LoopedStreams()


    public InputStream getInputStream() {
        return pipedIS;
    } // getInputStream()


    public OutputStream getOutputStream() {
        return byteArrayOS;
    } // getOutputStream()


    private void startByteArrayReaderThread() {
        new Thread(new Runnable() {
            public void run() {
                while(keepRunning) {
                    // 查流里面的字节数
                    if(byteArrayOS.size() > 0) {
                        byte[] buffer = null;
                        synchronized(byteArrayOS) {
                            buffer = byteArrayOS.toByteArray();
                            byteArrayOS.reset(); // 清除~冲?
                        }
                        try {
                            // 把提取到的数据发送给PipedOutputStream
                            pipedOS.write(buffer, 0, buffer.length);
                        }
                        catch(IOException e) {
                            // 记录错误或其他处?
                            // 为简单计Q此处我们直接结?
                            System.exit(1);
                        }
                    }
                    else // 没有数据可用Q线E进入睡眠状?
                        try {
                            // 每隔1U查看ByteArrayOutputStream查新数据
                            Thread.sleep(1000);
                        }
                        catch(InterruptedException e) {}
                    }
             }
        }).start();
    } // startByteArrayReaderThread()
} // LoopedStreams


startByteArrayReaderThread()Ҏ是整个类真正的关键所在。这个方法的目标很简单,是创徏一个定期地查ByteArrayOutputStream~冲区的U程。缓冲区中找到的所有数据都被提取到一个byte数组Q然后写入到PipedOutputStream。由于PipedOutputStream对应的PipedInputStream由getInputStream()q回Q从该输入流d数据的线E都读取到原先发送给ByteArrayOutputStream的数据。前面提刎ͼLoopedStreamsc解决了道存在的前二个问题,我们来看看这是如何实现的?/p>

ByteArrayOutputStreamhҎ需要扩展其内部~冲区的能力。由于存在“完全缓农y,U程向getOutputStream()q回的流写入数据时不会被d。因而,W一个问题不会再l我们带来麻烦。另外还要顺便说一句,ByteArrayOutputStream的缓冲区永远不会~减。例如,假设在能够提取数据之前,有一?00 K的数据被写入到流Q缓冲区永q保持至?00 K的容量。如果这个类有一个方法能够在数据被提取之后修正缓冲区的大,它就会更完善?/p>

W二个问题得以解决的原因在于Q实际上M时候只有一个线E向PipedOutputStream写入数据Q这个线E就是由startByteArrayReaderThread()创徏的线E。由于这个线E完全由LoopedStreamscLӞ我们不必担心它会产生IOException异常?/p>

LoopedStreamsc还有一些细节值得提及。首先,我们可以看到byteArrayOS和pipedIS实际上分别是ByteArrayOutputStream和PipedInputStream的派生类的实例,也即在它们的close()Ҏ中加入了Ҏ的行为。如果一个LoopedStreams对象的用户关闭了输入或输出流Q在startByteArrayReaderThread()中创建的U程必须关闭。覆盖后的close()Ҏ把keepRunning标记讄成false以关闭线E。另外,h意startByteArrayReaderThread()中的同步块。要保在toByteArray()调用和reset()调用之间ByteArrayOutputStream~冲Z被写入流的线E修改,q是必不可少的。由于ByteArrayOutputStream的write()Ҏ的所有版本都在该上同步Q我们保证了ByteArrayOutputStream的内部缓冲区不被意外C攏V?/p>

注意LoopedStreamscdƈ不涉及管道流的第三个问题。该cȝgetInputStream()Ҏq回PipedInputStream。如果一个线E从该流dQ一D|间后l止Q下ơ数据从ByteArrayOutputStream~冲Z输到PipedOutputStream时就会出现IOException异常?/p>

二、捕获Java控制台输?/span>
Listing 5的ConsoleTextAreacL展Swing JTextArea捕获控制台输出。不要对q个cLq么多代码感到惊Ӟ必须指出的是QConsoleTextAreacL过50%的代码用来进行测试?/p>
【Listing 5Q截获Java控制台输出?
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.text.*;

public class ConsoleTextArea extends JTextArea {
    public ConsoleTextArea(InputStream[] inStreams) {
        for(int i = 0; i < inStreams.length; ++i)
            startConsoleReaderThread(inStreams[i]);
    } // ConsoleTextArea()

    public ConsoleTextArea() throws IOException {
        final LoopedStreams ls = new LoopedStreams();

        // 重定向System.out和System.err
        PrintStream ps = new PrintStream(ls.getOutputStream());
        System.setOut(ps);
        System.setErr(ps);

        startConsoleReaderThread(ls.getInputStream());
    } // ConsoleTextArea()


    private void startConsoleReaderThread(
        InputStream inStream) {
        final BufferedReader br =
            new BufferedReader(new InputStreamReader(inStream));
        new Thread(new Runnable() {
            public void run() {
                StringBuffer sb = new StringBuffer();
                try {
                    String s;
                    Document doc = getDocument();
                    while((s = br.readLine()) != null) {
                        boolean caretAtEnd = false;
                        caretAtEnd = getCaretPosition() == doc.getLength() ?
                            true : false;
                        sb.setLength(0);
                        append(sb.append(s).append('\n').toString());
                        if(caretAtEnd)
                            setCaretPosition(doc.getLength());
                    }
                }
                catch(IOException e) {
                    JOptionPane.showMessageDialog(null,
                        "从BufferedReaderd错误Q? + e);
                    System.exit(1);
                }
            }
        }).start();
    } // startConsoleReaderThread()


    // 该类剩余部分的功能是q行试
    public static void main(String[] args) {
        JFrame f = new JFrame("ConsoleTextArea试");
        ConsoleTextArea consoleTextArea = null;

        try {
            consoleTextArea = new ConsoleTextArea();
        }
        catch(IOException e) {
            System.err.println(
                "不能创徏LoopedStreamsQ? + e);
            System.exit(1);
        }

        consoleTextArea.setFont(java.awt.Font.decode("monospaced"));
        f.getContentPane().add(new JScrollPane(consoleTextArea),
            java.awt.BorderLayout.CENTER);
        f.setBounds(50, 50, 300, 300);
        f.setVisible(true);

        f.addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(
                java.awt.event.WindowEvent evt) {
                System.exit(0);
            }
        });

        // 启动几个写操作线E向
        // System.out和System.err输出
        startWriterTestThread(
            "写操作线E?#1", System.err, 920, 50);
        startWriterTestThread(
            "写操作线E?#2", System.out, 500, 50);
        startWriterTestThread(
            "写操作线E?#3", System.out, 200, 50);
        startWriterTestThread(
            "写操作线E?#4", System.out, 1000, 50);
        startWriterTestThread(
            "写操作线E?#5", System.err, 850,    50);
    } // main()


    private static void startWriterTestThread(
        final String name, final PrintStream ps, 
        final int delay, final int count) {
        new Thread(new Runnable() {
            public void run() {
                for(int i = 1; i <= count; ++i) {
                    ps.println("***" + name + ", hello !, i=" + i);
                    try {
                        Thread.sleep(delay);
                    }
                    catch(InterruptedException e) {}
                }
            }
        }).start();
    } // startWriterTestThread()
} // ConsoleTextArea


main()Ҏ创徏了一个JFrameQJFrame包含一个ConsoleTextArea的实例。这些代码ƈ没有什么特别之处。Frame昄出来之后Qmain()Ҏ启动一pd的写操作U程Q写操作U程向控制台输出大量信息。ConsoleTextArea捕获q显C些信息,如图一所C?/p>

ConsoleTextArea提供了两个构造函数。没有参数的构造函数用来捕获和昄所有写入到控制台流的数据,有一个InputStream[]参数的构造函数{发所有从各个数组元素d的数据到JTextArea。稍后将有一个例子显C个构造函数的用处。首先我们来看看没有参数的ConsoleTextArea构造函数。这个函数首先创Z个LoopedStreams对象Q然后请求Javaq行时环境把控制台输{发到LoopedStreams提供的OutputStreamQ最后,构造函数调用startConsoleReaderThread()Q创Z个不断地把文本行q加到JTextArea的线E。注意,把文本追加到JTextArea之后Q程序小心地保证了插入点的正位|?/p>

一般来_Swing部g的更C应该在AWT事g分派U程QAWT Event Dispatch ThreadQAEDTQ之外进行。对于本例来_q意味着所有把文本q加到JTextArea的操作应该在AEDT中进行,而不是在startConsoleReaderThread()Ҏ创徏的线E中q行。然而,事实上在Swing中向JTextAreaq加文本是一个线E安全的操作。读取一行文本之后,我们只需调用JText.append()可以把文本q加到JTextArea的末?/p>

三、捕获其他程序的控制台输?/span>
在JTextArea中捕获JavaE序自己的控制台输出是一回事Q去捕获其他E序Q甚臛_括一些非JavaE序Q的控制台数据又是另一回事。ConsoleTextArea提供了捕获其他应用的输出旉要的基础功能QListing 6的AppOutputCapture利用ConsoleTextAreaQ截取其他应用的输出信息然后昄在ConsoleTextArea中?/p>
【Listing 6Q截获其他程序的控制台输出?
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;

public class AppOutputCapture {
        private static Process process;

        public static void main(String[] args) {
                if(args.length == 0) {
             System.err.println("用法Qjava AppOutputCapture " +
                 "<E序名字> {参数1 参数2 ...}");
             System.exit(0);
                }

                try {
                        // 启动命o行指定程序的新进E?
                        process = Runtime.getRuntime().exec(args);
                }
                catch(IOException e) {
                        System.err.println("创徏q程时出?..\n" + e);
                        System.exit(1);
                }

                // 获得新进E所写入的流
                InputStream[] inStreams =
                        new InputStream[] {
                    process.getInputStream(),process.getErrorStream()};
                ConsoleTextArea cta = new
    ConsoleTextArea(inStreams);
                cta.setFont(java.awt.Font.decode("monospaced"));

                JFrame frame = new JFrame(args[0] +
                        "控制台输?);

                frame.getContentPane().add(new JScrollPane(cta),
                    BorderLayout.CENTER);
                frame.setBounds(50, 50, 400, 400);
                frame.setVisible(true);

                frame.addWindowListener(new WindowAdapter() {
                        public void windowClosing(WindowEvent evt) {
                                process.destroy();
                                try {
                                        process.waitFor(); // 在Win98下可能被挂v
                                }
                                catch(InterruptedException e) {}
                                        System.exit(0);
                                }
                        });
        } // main()
} // AppOutputCapture


AppOutputCapture的工作过E如下:首先利用Runtime.exec()Ҏ启动指定E序的一个新q程。启动新q程之后Q从l果Process对象得到它的控制台流。之后,把这些控制台传入ConsoleTextArea(InputStream[])构造函敎ͼq就是带参数ConsoleTextArea构造函数的用处Q。用AppOutputCaptureӞ在命令行上指定待截取其输出的E序名字。例如,如果在Windows 2000下执行javaw.exe AppOutputCapture ping.exe www.yahoo.comQ则l果如图四所C?/p>


囑֛Q截取其他程序的控制台输?/center>

使用AppOutputCapture时应该注意,被截取输出的应用E序最初输出的一些文本可能无法截取。因为在调用Runtime.exec()和ConsoleTextArea初始化完成之间存在一段旉差。在q个旉差内Q应用程序输出的文本会丢失。当AppOutputCaptureH口被关闭,process.destory()调用试图关闭JavaE序开始时创徏的进E。测试结果显C出Qdestroy()Ҏ不一定L有效Q至在Windows 98上是q样的)。似乎当待关闭的q程启动了额外的q程Ӟ则那些进E不会被关闭。此外,在这U情况下AppOutputCaptureE序看v来未能正常结束。但在Windows NT下,一切正常。如果用JDK v1.1.xq行AppOutputCaptureQ关闭窗口时会出C个NullPointerException。这是一个JDK的BugQJDK 1.2.x和JDK 1.3.x下就不会出现问题?/p>



飞翔 2006-05-21 15:52 发表评论
]]>
想作个静态网는成器http://www.aygfsteel.com/ytfei/archive/2006/05/20/47215.html飞翔飞翔Sat, 20 May 2006 12:48:00 GMThttp://www.aygfsteel.com/ytfei/archive/2006/05/20/47215.htmlhttp://www.aygfsteel.com/ytfei/comments/47215.htmlhttp://www.aygfsteel.com/ytfei/archive/2006/05/20/47215.html#Feedback2http://www.aygfsteel.com/ytfei/comments/commentRss/47215.htmlhttp://www.aygfsteel.com/ytfei/services/trackbacks/47215.html        我想实现q样的功能,
1Q与开发代码分R开发的时候不用考虑静态的|页生成Q完全可以按照普通的动态网站开发方法开?br />2Q可以局部调整生成优先权?br />3Q可以调整生成速度?卌整生成器的数量,和时间间隔?br />4Q本静态网는成系l实现时因充分考虑今后的调_随着自己对系l及技术了解的深入Q可以方便更改以前的实现。即要求模块分离Q一定要W合设计模式Q?br />目前的局限性:
1Q当然不能实Ӟ因ؓQ是采用W三方的工具Q因此只局限在实时性要求不高的pȝQ但Ҏ以上2Q?点及通过dg设施Q尽量满生成的实时性?img src ="http://www.aygfsteel.com/ytfei/aggbug/47215.html" width = "1" height = "1" />

飞翔 2006-05-20 20:48 发表评论
]]>
调试JSP解决TOMCAT~存问题http://www.aygfsteel.com/ytfei/archive/2006/05/12/45799.html飞翔飞翔Fri, 12 May 2006 03:10:00 GMThttp://www.aygfsteel.com/ytfei/archive/2006/05/12/45799.htmlhttp://www.aygfsteel.com/ytfei/comments/45799.htmlhttp://www.aygfsteel.com/ytfei/archive/2006/05/12/45799.html#Feedback2http://www.aygfsteel.com/ytfei/comments/commentRss/45799.htmlhttp://www.aygfsteel.com/ytfei/services/trackbacks/45799.html 调试JSP解决TOMCAT~存问题
来源: 今晚在线   作?我容易么?/a>   cd: 原创   旉: 2005-4-18   览:
jsp里的beanQ每ơ生?class?hjsp叉Q显C的q是老的bean的信?非要重vtomcatl过多方打探ȝ几点l验分nl大家?br />
1。让JSP不缓存方法网头部加?br />
代码内容
<% 
response.setHeader("Pragma","No-cache");//HTTP 1.1
response.setHeader("Cache-Control","no-cache");//HTTP 1.0
response.setHeader("Expires","0");//防止被proxy
%>


2。删除work目录下的~存文g?/p>

3。conf/server.xml 文gContext path 中间加上 reloadable="true"

例如Q?lt;Context path="" docBase="E:\MYJSP\" debug="0" reloadable="true" />

q样基本可以解决~存遗留问题?/p>



飞翔 2006-05-12 11:10 发表评论
]]>
JSTL 入门: 表达式语ahttp://www.aygfsteel.com/ytfei/archive/2006/05/10/45550.html飞翔飞翔Wed, 10 May 2006 14:33:00 GMThttp://www.aygfsteel.com/ytfei/archive/2006/05/10/45550.htmlhttp://www.aygfsteel.com/ytfei/comments/45550.htmlhttp://www.aygfsteel.com/ytfei/archive/2006/05/10/45550.html#Feedback0http://www.aygfsteel.com/ytfei/comments/commentRss/45550.htmlhttp://www.aygfsteel.com/ytfei/services/trackbacks/45550.html阅读全文

飞翔 2006-05-10 22:33 发表评论
]]>
攚wjpetstore4.0 (struts+ibatis) ===> (struts+spring+ibatis) 的轻量J2EE开?/title><link>http://www.aygfsteel.com/ytfei/archive/2006/05/02/44329.html</link><dc:creator>飞翔</dc:creator><author>飞翔</author><pubDate>Mon, 01 May 2006 18:53:00 GMT</pubDate><guid>http://www.aygfsteel.com/ytfei/archive/2006/05/02/44329.html</guid><wfw:comment>http://www.aygfsteel.com/ytfei/comments/44329.html</wfw:comment><comments>http://www.aygfsteel.com/ytfei/archive/2006/05/02/44329.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.aygfsteel.com/ytfei/comments/commentRss/44329.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/ytfei/services/trackbacks/44329.html</trackback:ping><description><![CDATA[ <p>U别: 初</p> <p> <a > <font color="#996699">吴高?/font> </a>常dL厂信息技术部<br /></p> <p>2005 q?2 ?01 ?/p> <blockquote>大多数IT l织都必解决三个主要问题:1Q帮助组l减成?2Q增加ƈ且保持客?3Q加快业务效率。完成这些问题一般都需要实现对多个业务pȝ的数据和业务逻辑的无~访问,也就是说Q要实施pȝ集成工程Q以便联l业务流E、实现数据的讉K与共享?/blockquote> <!--START RESERVED FOR FUTURE USE INCLUDE FILES--> <!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --> <!--END RESERVED FOR FUTURE USE INCLUDE FILES--> <p>JpetStore 4.0是ibatis的最新示例程序,ZStruts MVC框架Q注Q非传统Struts开发模式)Q以ibatis作ؓ持久化层。该CZE序设计优雅Q层ơ清晎ͼ可以学习以及作ؓ一个高效率的编E模型参考。本文是在其基础上,采用Spring对其中间层(业务层)q行攚w。开发量q一步减,同时又拥有了Spring的一些好处?/p> <p> <a name="N1003A"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">1. 前言</font> </span> </a> </p> <p>JpetStore 4.0是ibatis的最新示例程序。ibatis是开源的持久层品,包含SQL Maps 2.0 ?Data Access Objects 2.0 框架。JpetStoreCZE序很好的展CZ如何利用ibatis来开发一个典型的J2EE web应用E序。JpetStore有如下特点:</p> <ul> <li>ibatis数据? </li> <li>POJO业务? </li> <li>POJO领域c? </li> <li>Struts MVC </li> <li>JSP 表示?</li> </ul> <p>以下是本文用到的关键技术介l,本文假设您已l对StrutsQSpringFramewokQibatis有一定的了解Q如果不是,请首先查阅附录中的参考资料?/p> <ul> <li>Struts 是目前Java Web MVC框架中不争的王者。经q长达五q的发展QStruts已经逐渐成长Z个稳定、成熟的框架Qƈ且占有了MVC框架中最大的市场份额。但是Struts某些技术特性上已经落后于新兴的MVC框架。面对Spring MVC、Webwork2 q些设计更精密,扩展性更强的框架QStruts受到了前所未有的挑战。但站在产品开发的角度而言QStruts仍然是最E_的选择。本文的原型例子JpetStore 4.0是ZStruts开发的Q但是不拘惔于Struts的传l固定用法,例如只用了一个自定义Actionc,q且在form beancȝ定义上也是开创性的Qo目一斎ͼE后具体剖析一下? </li> <li>Spring Framework 实际上是Expert One-on-One J2EE Design and Development 一书中所阐述的设计思想的具体实现。Spring Framework的功能非常多。包含AOP、ORM、DAO、Context、Web、MVC{几个部分组成。Web、MVC暂不用考虑QJpetStore 4.0用的是更成熟的Struts和JSPQDAO׃目前Hibernate、JDO、ibatis的流行,也不考虑QJpetStore 4.0用的是ibatis。因此最需要用的是AOP、ORM、Context。Context中,最重要的是BeanfactoryQ它能将接口与实现分开Q非常强大。目前AOP应用最成熟的还是在事务理上? </li> <li>ibatis 是一个功能强大实用的SQL Map工具Q不同于其他ORM工具Q如hibernateQ,它是SQL语句映射成Java对象Q而对于ORM工具Q它的SQL语句是根据映定义生成的。ibatis 以SQL开发的工作量和数据库移植性上的让步,为系l设计提供了更大的自q间。有ibatis代码生成的工P可以ҎDDL自动生成ibatis代码Q能减少很多工作量?</li> </ul> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N10064"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">2. JpetStoreq?/font> </span> </a> </p> <p> <a name="N1006A"> <span id="wmqeeuq" class="smalltitle"> <strong> <font face="Arial">2.1. 背景</font> </strong> </span> </a> </p> <p>最初是Sun公司的J2EE petstoreQ其最主要目的是用于学习J2EEQ但是其~点也很明显Q就是过度设计了。接着Oracle用J2EE petstore来比较各应用服务器的性能。微软推ZZ.Netq_?Pet shopQ用于竞争J2EE petstore。而JpetStore则是l过改良的基于struts的轻便框架J2EE web应用E序Q相比来_JpetStore设计和架构更优良Q各层定义清晎ͼ使用了很多最佛_践和模式Q避免了很多"反模?Q如使用存储q程Q在java代码中嵌入SQL语句Q把HTML存储在数据库中等{。最新版本是JpetStore 4.0?/p> <p> <a name="N10073"> <span id="wmqeeuq" class="smalltitle"> <strong> <font face="Arial">2.2. JpetStore开发运行环境的建立</font> </strong> </span> </a> </p> <p>1、开发环?/p> <ul> <li>Java SDK 1.4.2 </li> <li>Apache Tomcat 4.1.31 </li> <li>Eclipse-SDK-3.0.1-win32 </li> <li>HSQLDB 1.7.2 </li> </ul> <p>2、Eclipse插g</p> <ul> <li>EMF SDK 2.0.1QEclipse建模框架Qlomboz插g需要,可以使用runtime版本? </li> <li>lomboz 3.0QJ2EE插gQ用来在Eclipse中开发J2EE应用E序 </li> <li>Spring IDE 1.0.3QSpring Bean配置理插g </li> <li>xmlbuddy_2.0.10Q编辑XMLQ用免费版功能即? </li> <li>tomcatPluginV3Qtomcat理插g </li> <li>Properties EditorQ编辑java的属性文?q可以预览以及自动存盘ؓUnicode格式。免M手工或者ANT调用native2ascii的麻烦?</li> </ul> <p>3、示例源E序</p> <ul> <li>ibatisCZE序JpetStore 4.0 http://www.ibatis.com/jpetstore/jpetstore.html </li> <li>攚w后的源E序Q?springQ(源码链接Q?</li> </ul> <p> <a name="N100AF"> <span id="wmqeeuq" class="smalltitle"> <strong> <font face="Arial">2.3. 架构</font> </strong> </span> </a> </p> <br /> <a name="N100B7"> <strong>? JpetStore架构?/strong> </a> <br /> <img height="279" alt="? JpetStore架构? src="http://www-128.ibm.com/developerworks/cn/java/j-s-s-i/images/image002.jpg" width="553" border="0" /> <br /> <p>? 是JPetStore架构图,更详l的内容请参见JPetStore的白皮书。参照这个架构图Q让我们E微剖析一下源代码Q得出JpetStore 4.0的具体实现图Q见?Q,思\一下子p然开朗了。前a中提到的非传l的struts开发模式,关键在struts Actioncdform beancM?/p> <p>struts Actioncd有一个:BeanAction。没错,实是一个!与传l的struts~程方式很不同。再仔细研究BeanActionc,发现它其实是一个通用c,利用反射原理Q根据URL来决定调用formbean的哪个方法。BeanAction大大化了struts的编E模式,降低了对struts的依赖(与struts以及WEB容器有关的几个类都放在com.ibatis.struts包下Q其它的c都可以直接复用Q。利用这U模式,我们会很Ҏ的把它移植到新的框架如JSFQspring?/p> <p>q样重心p{Udform bean上了Q它已经不是普通意义上的form bean了。查看源代码Q可以看到它不仅仅有数据和校?重置ҎQ而且已经h了行为,从这个意义上来说Q它更像一个BO(Business Object)。这是前文讲到的,BeanActioncd用反原理,ҎURL来决定调用form bean的哪个方法(行ؓQ。form bean的这些方法的{֐很简单,例如Q?/p> <br /> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <pre> <code class="section"> <font face="Lucida Console"> public String myActionMethod() { //..work return "success"; } </font> </code> </pre> </td> </tr> </tbody> </table> <br /> <p>Ҏ的返回值直接就是字W串Q对应的是forward的名Uͼ而不再是ActionForward对象Q创建ActionForward对象的Q务已l由BeanActioncM劳了?/p> <p>另外Q程序还提供了ActionContext工具c,该工L装了request 、response、form parameters、request attributes、session attributes?application attributes中的数据存取操作Q简单而线E安全,form beancM用该工具cd以进一步从表现层框架解耦?/p> <p>在这里需要特别指出的是,BeanActioncL对struts扩展的一个有益尝试,虽然提供了非常好的应用开发模式,但是它还非常斎ͼ一直在发展中?/p> <br /> <a name="N100E4"> <b>? JpetStore 4.0具体实现</b> </a> <br /> <img height="588" alt="? JpetStore 4.0具体实现" src="http://www-128.ibm.com/developerworks/cn/java/j-s-s-i/images/image004.gif" width="582" border="0" /> <br /> <p> <a name="N100F4"> <span id="wmqeeuq" class="smalltitle"> <strong> <font face="Arial">2.4. 代码剖析</font> </strong> </span> </a> </p> <p>下面p我们开始进一步分析JpetStore4.0的源代码Qؓ下面的改造铺路?/p> <ul> <li>BeanAction.java是唯一一个Struts actionc,位于com.ibatis.struts包下。正如上文所aQ它是一个通用的控制类Q利用反机Ӟ把控制{Udform bean的某个方法来处理。详l处理过E参考其源代码,单明晰? </li> <li> <p>Form beancM于com.ibatis.jpetstore.presentation包下Q命名规则ؓ***Bean。Form beancd部承于BaseBeanc,而BaseBeancd际承于ActionFormQ因此,Form beancd是Struts?ActionFormQForm beancȝ属性数据就由struts框架自动填充。而实际上QJpetStore4.0扩展了struts中ActionForm的应用: Form beanc还h行ؓQ更像一个BO,其行为(ҎQ由BeanActionҎ配置Qstruts-config.xmlQ的URL来调用。虽然如此,我们q是把Form beancd位于表现层?/p> <p>Struts-config.xml的配|里?U映方式,来告诉BeanAction把控制{到哪个form bean对象的哪个方法来处理?/p> <p>以这个请求连接ؓ例http://localhost/jpetstore4/shop/viewOrder.do</p> <p>1. URL Pattern</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <pre> <code class="section"> <font face="Lucida Console"> <action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction" name="orderBean" scope="session" validate="false"> <forward name="success" path="/order/ViewOrder.jsp"/> </action> </font> </code> </pre> </td> </tr> </tbody> </table> <br /> <p>此种方式表示Q控制将被{发到"orderBean"q个form bean对象 ?viewOrder"ҎQ行为)来处理。方法名?path"参数的以"/"分隔的最后一部分?/p> <p>2. Method Parameter</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <pre> <code class="section"> <font face="Lucida Console"> <action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction" name="orderBean" parameter="viewOrder" scope="session" validate="false"> <forward name="success" path="/order/ViewOrder.jsp"/> </action> </font> </code> </pre> </td> </tr> </tbody> </table> <br /> <p>此种方式表示Q控制将被{发到"orderBean"q个form bean对象?viewOrder"ҎQ行为)来处理。配|中?parameter"参数表示form beancM的方法?parameter"参数优先?path"参数?/p> <p>3. No Method call</p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <pre> <code class="section"> <font face="Lucida Console"> <action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction" name="orderBean" parameter="*" scope="session" validate="false"> <forward name="success" path="/order/ViewOrder.jsp"/> </action> </font> </code> </pre> </td> </tr> </tbody> </table> <br /> <p>此种方式表示Qform bean上没有Q何方法被调用。如果存?name"属性,则struts把表单参数等数据填充到form bean对象后,把控制{发到"success"。否则,如果name为空Q则直接转发控制?success"?/p> <p>q就相当于struts内置的org.apache.struts.actions.ForwardAction的功?/p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <pre> <code class="section"> <font face="Lucida Console"> <action path="/shop/viewOrder" type="org.apache.struts.actions.ForwardAction" parameter="/order/ViewOrder.jsp " scope="session" validate="false"> </action> </font> </code> </pre> </td> </tr> </tbody> </table> <br /> </li> <li>ServicecM于com.ibatis.jpetstore.service包下Q属于业务层。这些类装了业务以及相应的事务控制。Servicecȝform beancL调用? </li> <li>com.ibatis.jpetstore.persistence.iface包下的类是DAO接口Q属于业务层Q其屏蔽了底层的数据库操作,供具体的ServicecL调用。DaoConfigcL工具c(DAO工厂c)QServicec通过DaoConfigcL获得相应的DAO接口Q而不用关心底层的具体数据库操作,实现了如?中{耦合2}的解耦? </li> <li>com.ibatis.jpetstore.persistence.sqlmapdao包下的类是对应DAO接口的具体实玎ͼ在JpetStore4.0中采用了ibatis来实现ORM。这些实现类l承BaseSqlMapDaoc,而BaseSqlMapDaocdl承ibatis DAO 框架中的SqlMapDaoTemplatecRibatis的配|文件存攑֜com.ibatis.jpetstore.persistence.sqlmapdao.sql目录下。这些类和配|文件位于数据层 </li> <li>DomaincM于com.ibatis.jpetstore.domain包下Q是普通的javabean。在q里用作数据传输对象QDTOQ,贯穿视图层、业务层和数据层Q用于在不同层之间传输数据?</li> </ul> <p>剩下的部分就比较单了Q请看具体的源代码,非常清晰?/p> <p> <a name="N10141"> <span id="wmqeeuq" class="smalltitle"> <strong> <font face="Arial">2.5. 需要改造的地方</font> </strong> </span> </a> </p> <p>JpetStore4.0的关键就在struts Actioncdform beancMQ这也是其精华之一Q虽然该实现方式是试验性,待扩充和验证Q,在此ơ改造中我们要保留下来,x制层一点不变,表现层获取相应业务类的方式变了(要加载spring环境Q,其它保持不变。要特别x的改动是业务层和持久层,q运的是JpetStore4.0设计非常好,需要改动的地方非常,而且由模式可循,如下Q?/p> <p>1. 业务层和数据层用Spring BeanFactory机制理?/p> <p>2. 业务层的事务由spring 的aop通过声明来完成?/p> <p>3. 表现层(form beanQ获取业务类的方法改p定义工厂cL实现Q加载spring环境Q?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /> <br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /> </td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td> <img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /> <br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="center"> <img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /> <br /> </td> <td valign="top" align="right"> <a class="fbox" > <b> <font color="#996699">回页?/font> </b> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p> <a name="N10153"> <span id="wmqeeuq" class="atitle"> <font face="Arial" size="4">3. JPetStore的改?/font> </span> </a> </p> <p> <a name="N10159"> <span id="wmqeeuq" class="smalltitle"> <strong> <font face="Arial">3.1. 攚w后的架?/font> </strong> </span> </a> </p> <br /> <strong> <font face="Arial"> <img height="640" alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-s-s-i/images/image005.gif" width="582" border="0" /> </font> </strong> <br /> <p>其中U色部分是要增加的部分,蓝色部分是要修改的部分。下面就让我们逐一剖析?/p> <p> <a name="N10173"> <span id="wmqeeuq" class="smalltitle"> <strong> <font face="Arial">3.2. Spring Context的加?/font> </strong> </span> </a> </p> <p>Z在Struts中加载Spring ContextQ一般会在struts-config.xml的最后添加如下部分:</p> <br /> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <pre> <code class="section"> <font face="Lucida Console"><plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml" /> </plug-in> </font> </code> </pre> </td> </tr> </tbody> </table> <br /> <p>Spring在设计时充分考虑C与Struts的协同工作,通过内置的Struts Plug-in在两者之间提供了良好的结合点。但是,因ؓ在这里我们一点也不改动JPetStore的控制层(q是JpetStore4.0的精华之一)Q所以本文不准备采用此方式来加蝲ApplicationContext。我们利用的是spring framework 的BeanFactory机制,采用自定义的工具c(bean工厂c)来加载spring的配|文Ӟ从中可以看出Spring有多灉|Q它提供了各U不同的方式来用其不同的部?层次Q您只需要用你想用的Q不需要的部分可以不用?/p> <p>具体的来_是在com.ibatis.spring包下创徏CustomBeanFactoryc,spring的配|文件applicationContext.xml也放在这个目录下。以下就是该cȝ全部代码Q很单:</p> <br /> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"> <tbody> <tr> <td> <pre> <code class="section"> <font face="Lucida Console">public final class CustomBeanFactory { static XmlBeanFactory factory = null; static { Resource is = new InputStreamResource( CustomBeanFactory.class.getResourceAsStream("applicationContext.xml")); factory = new XmlBeanFactory(is); } public static Object getBean(String beanName){ return factory.getBean(beanName); } } </font> </code> </pre> </td> </tr> </tbody> </table> <br /> <p>实际上就是封装了Spring 的XMLBeanFactory而已Qƈ且Spring的配|文件只需要加载一ơ,以后可以直接用CustomBeanFactory.getBean("someBean")来获得需要的对象?例如someBean)Q而不需要知道具体的cRCustomBeanFactorycȝ于{耦合1}的解耦?/p> <p>CustomBeanFactorycd本文中只用于表现层的form bean对象获得servicecȝ对象Q因为我们没有把form bean对象配置在applicationContext.xml中。但是,Z么不把表现层的form beancM配置h呢,q样q不着qCustomBeanFactory个类了,Spring会帮助我们创建需要的一切?问题的答案就在于form beancLstruts的ActionFormc!如果大家熟悉strutsQ就会知道ActionFormcLstruts自动创徏的:在一ơ请求中Qstruts判断Q如果ActionForm实例不存在,创Z个ActionForm对象Q把客户提交的表单数据保存到ActionForm对象中。因此formbeancȝ对象׃能由spring来创建,但是servicecM及数据层的DAOcd以,所以只有他们在spring中配|?/p> <p>所以,很自然的Q我们就创徏了CustomBeanFactoryc,在表现层来衔接struts和spring。就q么单,实现了另一U方式的{耦合一}的解耦?/p> <p> <a name="N1019D"> <span id="wmqeeuq" class="smalltitle"> <strong> <font face="Arial">3.3. 表现?/font> </strong> </span> </a> </p>? <p>面分析到Qstruts和spring是在表现层衔接v来的Q那么表现层p做稍微的更改Q即所需要的servicecȝ对象创徏上。以表现层的AccountBeancMؓ例:</p><p>原来的源代码如下</p><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console"> private static final AccountService accountService = AccountService.getInstance(); private static final CatalogService catalogService = CatalogService.getInstance(); </font></code></pre></td></tr></tbody></table><br /><p>攚w后的源代码如下</p><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console"> private static final AccountService accountService = (AccountService)CustomBeanFactory.getBean("AccountService"); private static final CatalogService catalogService = (CatalogService)CustomBeanFactory.getBean("CatalogService"); </font></code></pre></td></tr></tbody></table><br /><p>其他的几个presentationcM同样方式攚w。这P表现层就完成了。关于表现层的其它部分如JSP{一概不动。也许您会说Q没有看Z么特别之处的好处啊?你还是额外实C一个工厂类。别着急,帷幕刚刚开启,spring是在表现层引入,但您发没发现Q?/p><ul><li>presentationcM仅面向servicecȝ接口~程Q具?AccountService"是哪个实现类QpresentationcM知道Q是在spring的配|文仉配置。(本例中,Z最大限度的保持原来的代码不作变化,没有抽象出接口)。Spring鼓励面向接口~程Q因为是如此的方便和自然Q当然您也可以不q么做? </li><li>CustomBeanFactoryq个工厂cMؓ什么会如此单,因ؓ其直接用了Spring的BeanFactory。Spring从其核心而言Q是一个DI容器Q其设计哲学是提供一U无侵入式的高扩展性的框架。ؓ了实现这个目标,Spring 大量引入了Java 的Reflection机制Q通过动态调用的方式避免编码方式的U束Qƈ在此基础上徏立了其核心组件BeanFactoryQ以此作为其依赖注入机制的实现基。org.springframework.beans包中包括了这些核心组件的实现c,核心中的核心为BeanWrapper和BeanFactorycR?</li></ul><p><a name="N101CA"><span id="wmqeeuq" class="smalltitle"><strong><font face="Arial">3.4. 持久?/font></strong></span></a></p><p>在讨Z务层之前Q我们先看一下持久层Q如下图所C:</p><br /><img height="416" alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-s-s-i/images/image007.jpg" width="320" border="0" /><br /><p>在上文中Q我们把iface包下的DAO接口归ؓ业务层,在这里不需要做修改。ibatis的sql配置文g也不需要改。要改的是DAO实现c,q在spring的配|文件中配置h?/p><p>1、修改基c?/p><p>所有的DAO实现c都l承于BaseSqlMapDaocR修改BaseSqlMapDaocd下:</p><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">public class BaseSqlMapDao extends SqlMapClientDaoSupport { protected static final int PAGE_SIZE = 4; protected SqlMapClientTemplate smcTemplate = this.getSqlMapClientTemplate(); public BaseSqlMapDao() { } } </font></code></pre></td></tr></tbody></table><br /><p>使BaseSqlMapDaocL为承于Spring提供的SqlMapClientDaoSupportc,q定义了一个保护属性smcTemplateQ其cd为SqlMapClientTemplate。关于SqlMapClientTemplatecȝ详细说明请参照附录中?Spring中文参考手?</p><p>2、修改DAO实现c?/p><p>所有的DAO实现c还是承于BaseSqlMapDaoc,实现相应的DAO接口Q但其相应的DAO操作委托SqlMapClientTemplate来执行,以AccountSqlMapDaocMؓ例,部分代码如下Q?/p><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console"> public List getUsernameList() { return smcTemplate.queryForList("getUsernameList", null); } public Account getAccount(String username, String password) { Account account = new Account(); account.setUsername(username); account.setPassword(password); return (Account) smcTemplate.queryForObject("getAccountByUsernameAndPassword", account); } public void insertAccount(Account account) { smcTemplate.update("insertAccount", account); smcTemplate.update("insertProfile", account); smcTemplate.update("insertSignon", account); } </font></code></pre></td></tr></tbody></table><br /><p>p么简单,所有函数的{֐都是一LQ只需要查找替换就可以了!</p><p>3、除d厂类以及相应的配|文?/p><p>除去DaoConfig.javaq个DAO工厂cd相应的配|文件dao.xmlQ因为DAO的获取现在要用spring来管理?/p><p>4、DAO在Spring中的配置QapplicationContext.xmlQ?/p><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>org.hsqldb.jdbcDriver</value> </property> <property name="url"> <value>jdbc:hsqldb:hsql://localhost/xdb</value> </property> <property name="username"> <value>sa</value> </property> <property name="password"> <value></value> </property> </bean> <!-- ibatis sqlMapClient config --> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation"> <value> classpath:com\ibatis\jpetstore\persistence\sqlmapdao\sql\sql-map-config.xml </value> </property> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <!-- Transactions --> <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <!-- persistence layer --> <bean id="AccountDao" class="com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDao"> <property name="sqlMapClient"> <ref local="sqlMapClient"/> </property> </bean> </font></code></pre></td></tr></tbody></table><br /><p>具体的语法请参照附录中的"Spring中文参考手?。在q里只简单解释一下:</p><p>1. 我们首先创徏一个数据源dataSourceQ在q里配置的是hsqldb数据库。如果是ORACLE数据库,driverClassName的值是"oracle.jdbc.driver.OracleDriver"QURL的值类g"jdbc:oracle:thin:@wugfMobile:1521:cdcf"。数据源现在由spring来管理,那么现在我们可以去掉properties目录下database.propertiesq个配置文g了;q有不要忘记修改sql-map-config.xmlQ去?lt;properties resource="properties/database.properties"/>对它的引用?/p><p>2. sqlMapClient节点。这个是针对ibatis SqlMap的SqlMapClientFactoryBean配置。实际上配置了一个sqlMapClient的创建工厂类。configLocation属性配|了ibatis映射文g的名U。dataSource属性指向了使用的数据源Q这h有用sqlMapClient的DAO都默认用了该数据源Q除非在DAO的配|中另外昑ּ指定?/p><p>3. TransactionManager节点。定义了事务Q用的是DataSourceTransactionManager?/p><p>4. 下面可以定义DAO节点了,如AccountDaoQ它的实现类是com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDaoQ用的SQL配置从sqlMapClient中读取,数据库连接没有特别列出,那么是默认使用sqlMapClient配置的数据源datasource?/p><p>q样Q我们就把持久层攚w完了,其他的DAO配置cM于AccountDao。怎么P单吧。这ơ有接口了:Q?AccountDao接口Q?gt;AccountSqlMapDao实现?/p><p><a name="N1022F"><span id="wmqeeuq" class="smalltitle"><strong><font face="Arial">3.5. 业务?/font></strong></span></a></p><p>业务层的位置以及相关c,如下图所C:</p><br /><img height="240" alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-s-s-i/images/image009.jpg" width="259" border="0" /><br /><p>在这个例子中只有3个业务类Q我们以OrderServicecMؓ例来攚w,q个cL最复杂的,其中涉及了事务?/p><p>1、在ApplicationContext配置文g中增加bean的配|:</p><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console"> <bean id="OrderService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref local="TransactionManager"></ref> </property> <property name="target"> <bean class="com.ibatis.jpetstore.service.OrderService"> <property name="itemDao"> <ref bean="ItemDao"/> </property> <property name="orderDao"> <ref bean="OrderDao"/> </property> <property name="sequenceDao"> <ref bean="SequenceDao"/> </property> </bean> </property> <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </font></code></pre></td></tr></tbody></table><br /><p>定义了一个OrderServiceQ还是很Ҏ懂的。ؓ了简单v见,使用了嵌套beanQ其实现cLcom.ibatis.jpetstore.service.OrderServiceQ分别引用了ItemDaoQOrderDaoQSequenceDao。该bean的insert*实现了事务管?AOP方式)。TransactionProxyFactoryBean自动创徏一个事务advisorQ?该advisor包括一个基于事务属性的pointcut,因此只有事务性的Ҏ被拦截?/p><p>2、业务类的修?/p><p>以OrderServiceZQ?/p><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">public class OrderService { /* Private Fields */ private ItemDao itemDao; private OrderDao orderDao; private SequenceDao sequenceDao; /* Constructors */ public OrderService() { } /** * @param itemDao 要设|的 itemDao? */ public final void setItemDao(ItemDao itemDao) { this.itemDao = itemDao; } /** * @param orderDao 要设|的 orderDao? */ public final void setOrderDao(OrderDao orderDao) { this.orderDao = orderDao; } /** * @param sequenceDao 要设|的 sequenceDao? */ public final void setSequenceDao(SequenceDao sequenceDao) { this.sequenceDao = sequenceDao; } //剩下的部? …? } </font></code></pre></td></tr></tbody></table><br /><p>U色部分Z攚w分。Spring采用的是Type2的设|依赖注入,所以我们只需要定义属性和相应的设值函数就可以了,ItemDaoQOrderDaoQSequenceDao的值由spring在运行期间注入。构造函数就可以为空了,另外也不需要自q写代码处理事务了Q事务在配置中声明)QdaoManager.startTransaction();{与事务相关的语句也可以L了。和原来的代码比较一下,是不是处理精了很多!可以更关注业务的实现?/p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" ><b><font color="#996699">回页?/font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="N1026D"><span id="wmqeeuq" class="atitle"><font face="Arial" size="4">4. l束?/font></span></a></p><p>ibatis是一个功能强大实用的SQL Map工具Q可以直接控制SQL,为系l设计提供了更大的自q间。其提供的最新示例程序JpetStore 4.0,设计优雅Q应用了q今为止很多最佛_践和设计模式Q非帔R于学习以及在此基础上创量的J2EE WEB应用E序。JpetStore 4.0是基于struts的,本文在此基础上,最大程度保持了原有设计的精华以及最的代码改动量,在业务层和持久化层引入了Spring。在您阅M本文以及攚w后的源代码后,会深切的感受到Spring带来的种U好处:自然的面向接口的~程Q业务对象的依赖注入Q一致的数据存取框架和声明式的事务处理,l一的配|文件…更重要的是Spring既是全面的又是模块化的,Spring有分层的体系l构Q这意味着您能选择仅仅使用它Q何一个独立的部分Q就像本文,而它的架构又是内部一致?/p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" ><b><font color="#996699">回页?/font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="resources"><span id="wmqeeuq" class="atitle"><font face="Arial" size="4">参考资?</font></span></a></p><ul><li>jpetstore相关各种资料和源E序 <a ><font color="#5c81a7">http://www.ibatis.com/jpetstore/jpetstore.html</font></a><br /><br /></li><li>Spring中文参考手?a ><font color="#5c81a7">http://www.jactiongroup.net/reference/html/index.html</font></a><br /><br /></li><li>Spring 开发指?夏昕<br /><br /></li><li>Struts <a ><font color="#5c81a7">http://struts.apache.org/</font></a><br /></li></ul><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><font color="#5c81a7"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></font></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><font color="#5c81a7"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /></font><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><font color="#5c81a7"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></font></td><td valign="top" align="right"><a class="fbox" ><b><font color="#996699">回页?/font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="author"><span id="wmqeeuq" class="atitle"><font face="Arial" size="4">关于作?/font></span></a></p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td colspan="3"><font face="Arial" size="4"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /></font></td></tr><tr valign="top" align="left"><td><p><font face="Arial" size="4"></font></p></td><td><font face="Arial" size="4"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" /></font></td><td width="100%"><p>吴高峎ͼ原一直在中兴通讯从事q营支撑产品的研发工作,对J2EE以及各种开源项目感兴趣。现在常德卷烟厂信息技术部从事EAI的徏设。联pL式:<a href="mailto:shuwgf@21cn.com"><font color="#5c81a7">shuwgf@21cn.com</font></a></p></td></tr></tbody></table><img src ="http://www.aygfsteel.com/ytfei/aggbug/44329.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/ytfei/" target="_blank">飞翔</a> 2006-05-02 02:53 <a href="http://www.aygfsteel.com/ytfei/archive/2006/05/02/44329.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>动态编译JAVAE序http://www.aygfsteel.com/ytfei/archive/2005/09/06/12194.html飞翔飞翔Tue, 06 Sep 2005 03:53:00 GMThttp://www.aygfsteel.com/ytfei/archive/2005/09/06/12194.htmlhttp://www.aygfsteel.com/ytfei/comments/12194.htmlhttp://www.aygfsteel.com/ytfei/archive/2005/09/06/12194.html#Feedback0http://www.aygfsteel.com/ytfei/comments/commentRss/12194.htmlhttp://www.aygfsteel.com/ytfei/services/trackbacks/12194.html[转脓者注]对于很多应用pȝQ常帔R要动态装载和执行cd代码片断Q这有利于部|的易性和pȝ设计上的灉|性。本文给Z一个比较全面的介绍Q值得参考?BR>
    在Sun JDK 1.2及后l版本中Q包含了一l可在程序运行时ȝ译和执行Java代码的API。这些API被包含在tools.jarcd中。这个功能允许JavaE序在运行时动态编译、执行小的代码块Q在有些情况下这个功能会让Java应用E序的架构更加灵zR开放?nbsp;

  本文假定读者已l在计算Z安装q|好了Sun JDK 1.2或更高的版本Qƈ对javac~译器命令有所了解?nbsp;

  在JavaE序中用编译器 
  假定要用javac命o~译 /home/mytest目录下Test.java文gQƈ讑֮class文g存放?home/mytest/classes路径下,输入下面命oQ?nbsp;

  javac -d /home/mytest/classes Test.java 

  辑ֈ同样的目的,也可以用Sun提供的一个Java~译器的API来实现。它的用也很简单,核心代码D如下: 

  1.   ?nbsp;
  2.   String[] args = new String[] {?d”, ?homemytestclasses”, “Test.java”}; 
  3.   Int status = javac.compile(args); 
  4.   ?nbsp;



  javac~译工具被安装在JDK根目录的/bin目录下,负责源代码~译成运行于JVM的字节码。事实上Q我们经怋?bin目录下的javac~译工具来编译Java源文件。如果在JavaE序中动态编译Q意制定的Java语句Q用这个外部的javac~译器就昑־不够灉|了。虽然有时可使用RuntimecL执行一个外部命令,但如果想知道代码是否被编译通过、编译时发生了什么错误,用Runtimecȝexec()Ҏ很隑֮C?nbsp;

  在Sun的JDK 1.2及后l版本中QJDK安装路径?lib路径下包含了一个tools.jar文gQ这个类库包含了一个完整的~译器包。com.sun.tools.javac.Main是编译器的主cd口,如果已经熟悉了javac~译器命令行的用方法,很容易理解这个类的用方法。方法compile(String[] p)执行~译动作Q参数p是一个String数组Q用来存放javac命o的参数选项Q编译后的状态返回一个Int|其对应值参考如下表所C: 

  ?nbsp;状态参C对应?nbsp;

  EXIT_OK 0 

  EXIT_ERROR 1 

  EXIT_CMDERR 2 

  EXIT_SYSERR 3 

  EXIT_ABNORMAL 4 

   

  在程序执行时~译和执行Java语句 

  从上面一D中Q我们已l基本了解了动态编译一个Java文g的方法。那么,如何q行时动态编译指定的Java语句呢?q里需要一个技巧?nbsp;

  假设要动态编译的Java条语句如下: 

  1.   System.out.println(“HelloQThis runtime codeQ?; 



  ~译器不支持~译单个Java语句Q被~译的对象必L一个以.java为后~的、结构合法的cLE序文gQ所以需要对q个语句q行攚w,变成一个完整的c,q把q条语句|入mainҎ中,便于试?nbsp;

  1.   public class <临时cL件名> { 
  2.   public static void main(String[] args) throws Exception { 
  3.   System.out.println(“HelloQThis runtime codeQ?; 
  4.   } 
  5.   } 



  q样Q欲动态编译的代码已经被程序动态拼装成了上面那D代码,准备工作q没有结束,不过看v来工作在向E微的复杂化。因ZqC码当前还存放在内存中Q编译器g对一个硬盘文件更感兴。我们需要引用java.io.Filec(JDK 1.2以上Q,创徏一个时的文g来存放上qC码的内容。java.io.Filecȝ静态方法createTempFile()Ҏ保证所创徏的文件名是不重复的,q样会增大这D늨序的灉|性。灵zL取决于真正应用到系l架构中的策略?nbsp;

  System.getProperty(“user.dir?用来获得当前路径Q在q里作ؓ临时文g的存攄录?nbsp;

  1.   File file; 
  2.   file = File.createTempFile(“JavaRuntime”, ?java”, new File(System.getProperty(“user.dir?)); 
  3.   String filename = file.getName(); 
  4.   String classname = getClassName(filename); 
  5.   //代码输出到文g 
  6.   PrintWriter out = new PrintWriter(new FileOutputStream(file)); 
  7.   out.println(?B>public class?nbsp;+ classname + ?nbsp;{”}; 
  8.   out.println(?.代码..?; 
  9.   out.println(“}?; 
  10.   //关闭文g?nbsp;
  11.   out.flush(); 
  12.   out.close(); 


  我们U定被创建的临时文g名以“JavaRuntime”ؓ头缀Q可L命名Q,后缀名以?java”结。一个待~译的Java源文件已被动态生成。下一步要从com.sun.tools.javac包中创徏一个Main实例Q调用javac.compile()Ҏ~译q个临时文gQ?nbsp;

  1.   Private static com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main(); 
  2.   String[] args = new String[] {?d”, System.getProperty(“user.dir?Qfilename }; 
  3.   Int status = javac.compile(args); 



  假定临时文g通过了编译器文法验证{验证,~译成功Qstatus值等?Q参看前表)Q在当前E序的运行目录下׃多了一个JavacL件。我们将通过执行q个Java cLӞ来模拟执行欲动态编译代码的l果?nbsp;

  Java提供在运行时d载类的特性,可动态识别和调用cL造方法、类字段和类Ҏ。java.lang.reflect.Method实现了Member接口Q可以调用接口的Ҏ来获得方法类的名U、修饰词{。方法getRuturnType()、getParameterTypes()、getExeptionTypess(){返回被表示Ҏ的构造信息。Method另一个重要的Ҏ是可以调用invoke()执行q个ҎQ详l用方法可以查看java.lang.reflect包文档)。下面这D代码中创徏一个java.lang.reflect.MethodcL法,调用getMethod()Ҏ获得被拼装的mainҎ的映,q段代码如下Q?nbsp;

  1.   try { 
  2.   // 讉Kq个c?nbsp;
  3.   Class cls = Class.forName(classname); 
  4.   //调用mainҎ 
  5.   Method main = cls.getMethod(“main”, new Class[] { String[].class }); 
  6.   main.invoke(nullQ?nbsp;new Object[] { new String[0] }); 
  7.   }catch (SecurityException se) { 
  8.   debug(“access to the information is denied:?nbsp;+ se.toString()); 
  9.   }catch (NoSuchMethodException nme) { 
  10.   debug(“a matching method is not found or if then name is or : 
  11.   ?nbsp;+ nme.toString()); 
  12.   }catch (InvocationTargetException ite) { 
  13.   debug(?B>Exception in main: ?nbsp;+ ite.getTargetException()); 
  14.   }catch (Exception e){ 
  15.   debug(e.toString()); 
  16.   } 


  q行l果参如下: 

  HelloQThis runtime codeQ?nbsp;

   

  CE序 

  下面l出了一个简单的JavaE序Q这个程序说明了如何利用Sun的javac~译器完成动态编译Java语句。运行该E序需要计机安装JDK 1.2以上版本Qƈ在classpath中或q行时指定tools.jar文g位置?nbsp;

  E序l构Q?nbsp;

  ?nbsp;compile() ~译Java代码Q返回生成的临时文gQ?nbsp;

  ?nbsp;run()q行~译的class文gQ?nbsp;

  ?nbsp;debug()输出调试信息Q?nbsp;

  ?nbsp;getClassName()从一个Java源文件获得类名; 

  ?nbsp;readLine()从控制台d用户输入的Java Code?nbsp;

  1.   Import java.io.File
  2.   ?nbsp;
  3.   Public class RuntimeCode{ 
  4.   /**~译?/ 
  5.   private static com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main(); 
  6.   /**{待用户输入JavaCodeQ然后编译、执?/ 
  7.   public static void main(String[] args) throws Exception
  8.   ?nbsp;
  9.   run(compile(code)); 
  10.   } 
  11.   /**~译JavaCodeQ返回时文件对?/ 
  12.   private synchronized static File compile(String code) 
  13.   throws IOExceptionQ?B>Exception { 
  14.   File file; 
  15.   //在用户当前文件目录创Z个时代码文?nbsp;
  16.   file = File.createTempFile(“JavaRuntime”, ?java”, 
  17.   new File(System.getProperty(“user.dir?)); 
  18.   //当虚拟机退出时Q删除此临时java源文?nbsp;
  19.   file.deleteOnExit(); 
  20.   //获得文g名和cd?nbsp;
  21.   String filename = file.getName(); 
  22.   String classname = getClassName(filename); 
  23.   //代码输出到文g 
  24.   PrintWriter out = new PrintWriter(new FileOutputStream(file)); 
  25.   out.println(?I>/**?; 
  26.   ?nbsp;
  27.   //关闭文g?nbsp;
  28.   out.flush(); 
  29.   out.close(); 
  30.   //~译代码文g 
  31.   String[] args = new String[] {?d”, System.getProperty(“user.dir?Qfilename }; 
  32.   //q回~译的状态代?nbsp;
  33.   int status = javac.compile(args); 
  34.   //处理~译状?nbsp;
  35.   ?nbsp;
  36.   } 
  37.   /**执行刚刚~译的类文g*/ 
  38.   private static synchronized void run(File file) 
  39.   ?nbsp;
  40.   //当虚拟机退出时Q删除此临时~译的类文g 
  41.   new File(file.getParent()Q?nbsp;classname + ?class?.deleteOnExit(); 
  42.   try { 
  43.   // 讉Kq个c?nbsp;
  44.   Class cls = Class.forName(classname); 
  45.   //映射mainҎ 
  46.   Method main = cls.getMethod(“main”, new Class[] { String[].class }); 
  47.   //执行mainҎ 
  48.   main.invoke(nullQ?nbsp;new Object[] { new String[0] }); 
  49.   }catch (SecurityException se) { 
  50.   ?nbsp;
  51.   } 
  52.   } 
  53.   /**打印调试信息*/ 
  54.   private static void debug(String msg) { 
  55.   System.err.println(msg); 
  56.   } 
  57.   /**Ҏ一个java源文件名获得cd*/ 
  58.   private static String getClassName(String filename){ 
  59.   return filename.substring(0Qfilename.length()-5); 
  60.   } 
  61.   /**从控制台获得用户输入的Java代码D?/ 
  62.   ?nbsp;
  63.   } 


  ~译q行上述代码Q在please input java code提示下输入以下代码: 

  for(int i=0;i<10;i++){System.out.println(“this is:?i);} 

  q行l果如下所C: 

  Please input java codeQ?nbsp;

  for(int i=0;i<10;i++){System.out.println(“this is:?i);} 

  wait.... 

  -------------------- 

  this is:0 

  this is:1 

  this is:2 

  this is:3 

  this is:4 

  this is:5 

  this is:6 

  this is:7 

  this is:8 

  this is:9 

   

  ȝ 

  在大中型企业应用pȝq_中,使用代码动态编译技术结合OO~程模型Q可在系l不菪机条g下保证系l的可扩展性和伸羃性。如果你是一个JavaE序员,E加调整以上代码Q还可以帮助调试段的Java代码Q?nbsp;



飞翔 2005-09-06 11:53 发表评论
]]>
Hibernate In Action Notehttp://www.aygfsteel.com/ytfei/archive/2005/09/05/12119.html飞翔飞翔Mon, 05 Sep 2005 14:14:00 GMThttp://www.aygfsteel.com/ytfei/archive/2005/09/05/12119.htmlhttp://www.aygfsteel.com/ytfei/comments/12119.htmlhttp://www.aygfsteel.com/ytfei/archive/2005/09/05/12119.html#Feedback0http://www.aygfsteel.com/ytfei/comments/commentRss/12119.htmlhttp://www.aygfsteel.com/ytfei/services/trackbacks/12119.htmlUsing hibernate.properties for C3P0 connection pool settings:
hibernate.connection.driver_class = org.postgresql.Driver
hibernate.connection.url = jdbc:postgresql://localhost/auctiondb
hibernate.connection.username = auctionuser
hibernate.connection.password = secret
hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect
hibernate.c3p0.min_size=5
hibernate.c3p0.max_size=20
hibernate.c3p0.timeout=300
hibernate.c3p0.max_statements=50
hibernate.c3p0.idle_test_period=3000
2.(Hibernate in a managed environment with an application server)
hibernate.connection.datasource = java:/comp/env/jdbc/AuctionDB
hibernate.transaction.factory_class = \
net.sf.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class = \
net.sf.hibernate.transaction.JBossTransactionManagerLookup
hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect

l合spring 后就不是q样了,可以右spring 控制transaction & datasource &...

飞翔 2005-09-05 22:14 发表评论
]]>
Hibernate In Actionhttp://www.aygfsteel.com/ytfei/archive/2005/09/01/11794.html飞翔飞翔Thu, 01 Sep 2005 15:59:00 GMThttp://www.aygfsteel.com/ytfei/archive/2005/09/01/11794.htmlhttp://www.aygfsteel.com/ytfei/comments/11794.htmlhttp://www.aygfsteel.com/ytfei/archive/2005/09/01/11794.html#Feedback0http://www.aygfsteel.com/ytfei/comments/commentRss/11794.htmlhttp://www.aygfsteel.com/ytfei/services/trackbacks/11794.htmllog4j.logger.net.sf.hibernate.ps.PreparedStatementCache=info

inverse="false" 则本cMؓL方,hibernate会自动查扑օ联的对象Q即使不l完全的对象Q?BR>inverse = "true" 控方

cascade = "delete"或其?nbsp;  必须是个完整的对象hibernate才会d除关联的对象Q否则不予删?BR>
<set name="members" table="USERGROUP" lazy="true"
   inverse="false" cascade="delete" sort="unsorted">
   <key column="groupid"></key>
   <many-to-many class="Sysuser" column="userid">
   </many-to-many>
  </set>

现对inverse 和cascade 属性在不同条g下测试,l果如下Q?BR>(   全ؓ完整对象Q否则cascade delete 不会U联删除,除非inverse= false 而不出错Q?BR>
inverse        cascade        result
-------------------------------------
true               delete           delete sysgroup,sysuser
true                none             delete sysgroup
false               delete            delete sysgroup,sysuser,user-group
false                none             delete sysgroup,user-group
<set name="permissions" table="group_permissions" lazy="true"
   inverse="true" cascade="delete" sort="unsorted">
   <key column="GROUPID"></key>
   <one-to-many class="GroupPermissions" />
  </set>
(完整的对象)
one-to-many :
inverse   cascade            result
------------------------------------
true       delete                  删除    sysgroup,grouppermissions
true       none                   只删?sysgroup
false      两种情况           出错Q不能更新grouppermissions=null

飞翔 2005-09-01 23:59 发表评论
]]>
Struts menu的用(动态数据)http://www.aygfsteel.com/ytfei/archive/2005/08/30/12079.html飞翔飞翔Tue, 30 Aug 2005 03:42:00 GMThttp://www.aygfsteel.com/ytfei/archive/2005/08/30/12079.htmlhttp://www.aygfsteel.com/ytfei/comments/12079.htmlhttp://www.aygfsteel.com/ytfei/archive/2005/08/30/12079.html#Feedback0http://www.aygfsteel.com/ytfei/comments/commentRss/12079.htmlhttp://www.aygfsteel.com/ytfei/services/trackbacks/12079.html1.
   拯必要?/SPAN>jar库文件到dbMenu\WEB-INF\lib
log4j-1.2.8.jar
struts-menu-2.3.jar 
拯menu-config.xml ,struts-menu.tld 到WEB-INF目录?
及一些必ȝ囄和jscript,css到相应的目录
Images/*.*       
Scripts/    menuExpandable.js xtree.js
Styles/     menuExpandable.css   global.css   xtree.css
2.
修改struts-config.xml 文g,增加如下部分
   <plug-in className="net.sf.navigator.menu.MenuPlugIn">
      
<set-property property="menuConfig"
        value
="/WEB-INF/menu-config.xml"/>
   
plug-in>

3.获得数据,构造树状菜单:
  无论何种Ҏ获得数据QJDBCQHIBERNATE。。。)Q?BR>    我们要构造的树状菜单需?个必d? 1.昄的名U?name) 2.q接到的目标位置(location)
  TreeBuilder.java
===========================================================================

/*
 * 创徏日期 2005-8-6
 */
import ....

import net.sf.navigator.menu.MenuComponent;
import net.sf.navigator.menu.MenuRepository;

/**
 * @author yutengfei
 */
public class TreeBuilder {
 protected final static Log log = LogFactory.getLog("TreeBuilder"); 
 public TreeBuilder() { }

 // 以下代码生成树结构,q以"treerepository"名储存在session ?BR>   public static void initialTree(HttpServletRequest request, HttpServlet servlet){
  try {
   MenuRepository repository = new MenuRepository();
   ServletContext context = servlet.getServletContext()
   // Get the repository from the application scope - and copy the
   // DisplayerMappings from it.
   MenuRepository defaultRepository = (MenuRepository) context.getAttribute(MenuRepository.MENU_REPOSITORY_KEY);

   repository.setDisplayers(defaultRepository.getDisplayers()); 

//your code begin
List tree = getTreeDataFromDataBase(); //
// 产生从数据库中获得的数据列表(一个树节点是一?FONT size=2>NoteBean 对象) Q获得数据的Ҏq里没有提供
for(Iterator it = tree.iterator();it.hasNext();)
{
     MenuComponent mc = new MenuComponent();   //建立一个menu对象(树节?Q然后设|他的属?BR>            NoteBean row =(NoteBean) it.next()
            String name 
= (String) row.getName()
;
            mc.setName(name);  //讄昄的节点名U?BR>            String parent 
= (String) row.getParentname()
;
            System.
out.println(name + ", parent is: " +
 parent);
            
if (parent != null
) {//存在“父”节?BR>                MenuComponent parentMenu = repository.getMenu(parent);
                
if (parentMenu == null
) {
                    System.
out.println("parentMenu '" + parent + "' doesn't exist!"
);
                    
// create a temporary parentMenu

                    parentMenu = new MenuComponent();
                    parentMenu.setName(parent);
                    repository.addMenu(parentMenu);
                }

                mc.setParent(parentMenu);   //讄“父”节?BR>            }
            String title 
= (String) row.getTitle()
;
            mc.setTitle(title);
            String location 
= (String) row.getLocation()
;
            mc.setLocation(location);
            repository.addMenu(mc);
        }
   
request.getSession().setAttribute("treerepository", repository);   //整个树结构储存在session?BR>  } catch (Exception ex) {
   ex.printStackTrace();
  }

 }

}

4.前台jsp昄树菜?BR>
<%try{%>
<%
if (session.getAttribute("treerepository") == null) {
TreeBuilder .initialTree(request, (HttpServlet) pageContext.getPage());
 }%>
<%List list = new ArrayList();
MenuRepository repository = (MenuRepository) session.getAttribute("treerepository");
list = repository.getTopMenus();
String name = "";%>


 <%for (Iterator it = list.iterator(); it.hasNext();) {
 name = ((MenuComponent) it.next()).getName();%>
 
 <%}%>

<%}catch(Exception e){
e.printStackTrace();
}
%>

5 over



飞翔 2005-08-30 11:42

文章来源:http://ytfei.cnblogs.com/archive/2005/08/30/225943.html


飞翔 2005-08-30 11:42 发表评论
]]>
Struts Validator验证器用指?/title><link>http://www.aygfsteel.com/ytfei/archive/2005/08/04/12081.html</link><dc:creator>飞翔</dc:creator><author>飞翔</author><pubDate>Thu, 04 Aug 2005 08:20:00 GMT</pubDate><guid>http://www.aygfsteel.com/ytfei/archive/2005/08/04/12081.html</guid><wfw:comment>http://www.aygfsteel.com/ytfei/comments/12081.html</wfw:comment><comments>http://www.aygfsteel.com/ytfei/archive/2005/08/04/12081.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/ytfei/comments/commentRss/12081.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/ytfei/services/trackbacks/12081.html</trackback:ping><description><![CDATA[     摘要: Struts Validator验证器用指南(ҎStruts Validator GuideQ?作者: David Winterfeldt大卫 James Turner詹姆?Rob Leland|伯?译Q?       侯思超 验证器:?.5版,验证器在一些form中就已经实现了,他最初包含在开发h员包中,后来核心代码挪到Jakarta Commons包中和Struts?..  <a href='http://www.aygfsteel.com/ytfei/archive/2005/08/04/12081.html'>阅读全文</a><img src ="http://www.aygfsteel.com/ytfei/aggbug/12081.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/ytfei/" target="_blank">飞翔</a> 2005-08-04 16:20 <a href="http://www.aygfsteel.com/ytfei/archive/2005/08/04/12081.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <a href="http://www.aygfsteel.com/" title="狠狠久久亚洲欧美专区_中文字幕亚洲综合久久202_国产精品亚洲第五区在线_日本免费网站视频">狠狠久久亚洲欧美专区_中文字幕亚洲综合久久202_国产精品亚洲第五区在线_日本免费网站视频</a> </div> </footer> վ֩ģ壺 <a href="http://" target="_blank">̨</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">Ϫ</a>| <a href="http://" target="_blank">ξ</a>| <a href="http://" target="_blank">żҿ</a>| <a href="http://" target="_blank">̳</a>| <a href="http://" target="_blank">ڶ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">Դ</a>| <a href="http://" target="_blank">ף</a>| <a href="http://" target="_blank">â</a>| <a href="http://" target="_blank">ͨ</a>| <a href="http://" target="_blank">ƺ</a>| <a href="http://" target="_blank">ƽ</a>| <a href="http://" target="_blank">ζ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ɽ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ԭ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ٰ</a>| <a href="http://" target="_blank">¡</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">佭</a>| <a href="http://" target="_blank">ī</a>| <a href="http://" target="_blank">޻</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">J</a>| <a href="http://" target="_blank">̫</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>