??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲精品国产精品乱码不99按摩
,亚洲成人毛片,九九视频这里只有精品 http://www.aygfsteel.com/ytfei/category/3100.htmlzh-cn Sat, 30 Jan 2010 18:42:14 GMT Sat, 30 Jan 2010 18:42:14 GMT 60 ant 的?/title> http://www.aygfsteel.com/ytfei/archive/2006/10/20/76397.html飞翔 飞翔 Fri, 20 Oct 2006 07:48:00 GMT http://www.aygfsteel.com/ytfei/archive/2006/10/20/76397.html http://www.aygfsteel.com/ytfei/comments/76397.html http://www.aygfsteel.com/ytfei/archive/2006/10/20/76397.html#Feedback 0 http://www.aygfsteel.com/ytfei/comments/commentRss/76397.html http://www.aygfsteel.com/ytfei/services/trackbacks/76397.html 转自:http://www.csbus.com/forum/ShowDetail.asp?Ici_ID=849&ForumID=5&ForumName=%D4%B1%B9%A4%D6%AE%BC%D2 本文主要介绍 Ant 在多用户开发的情况下,如何?EclipseQƈ且根据不同的目标环境~译不同的部|包。文中首先介l一个场景,l出在开发、编译、部|过E中遇到的问题;然后介绍如何?Eclipse 来简化你?Ant 开发过E;文章的最后解释如何通过~写有效?Ant 脚本来提高你的工作效率?/blockquote>
读者定位ؓh Java ?Ant 使用l验的开发h员?/p>
读者可以学习到如何使用 Ant 解决一些多用户开发环境中Q根据不同的目标环境~译成不同部|包的问题?/p>
工作场景
现在有一?web 目Q是多hq行开发的Q通过 CVS 来管理。另外该目q有一些测试h员,他们试部v在测试服务器上的应用E序Q发?bug 后通过 bug 理pȝ通知开发h员,在开发h员修?bug q经q本地测试后Q由专门的h负责出(check outQ代码,~译打包后部|到试服务器上?/p>
该项目的成员A是负责Z码、编译打包,q|到服务器上的h。除了这个Q务之外,他还是该目的编Eh员。在目q入试阶段后,A在得到组中别的成员修复了 bug q且入(check inQ了代码的消息后Q也有可能是A自己入了代码Q,A首先更新本地的代码,先在本地做测试,认修复?bug 后打?WAR 包部|到试服务器上Qƈ通知试人员已经修复?bugQ让试人员l箋q行试?/p>
A的烦?/font>
在该目中,有一些ؓ试方便开发而写的代码和面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>
从上面的场景可以看出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>
在正式开始编写脚本之前,我们需要下载安装相应的软g?/p>
?Eclipse3.1 来创?Ant 脚本
如果你?Eclipse 来编?AntQ徏议?Eclipse 3.1 以后的版本。除了以?Ant ~辑器提供的语法高亮Q提C法错误等功能外,Eclipse3.1 版本增加了许多新的功能。比如:脚本代码的折叠;快速定位某属性或者目标(targetQ段的定义;在同一 builder 文g中重构属性名和目标名Q快h式Alt + Shift +RQ;调试 Ant 脚本{?/p>
下面我们来看看 Eclipse 3.1 中对 Ant 的支?/p>
打开“File”-“New”-“Project”-“Simple”-“Project”,点击“Next”,输入工程名“Ant”,然后点击直到“Finish?/p>
在新建的 Ant 工程中,新徏 Test.xmlQƈ且拷贝下面的脚本。该D脚本的内容׃做介l了Q我们主要看 Eclipse 提供了哪些功能。注意这时候打开的ƈ不是 Ant ~辑器,内Ҏ贝进M后,x打开的“Test.xml”,然后再重新打开它,q样 Eclipse ׃?Ant ~辑器打开它,q且也语法高亮了?/p>
<?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>
自动提示和代码折叠功能。如果是 Ant 内置的命令或者前面已l定义的变量Q编辑器会自动提C;~辑器右边的?减号可以代码折叠。如下所C:
快速定位目标(targetQ或者定义变量处。在上图中,鼠标移?default=”init?中的 init 上之后,按下 ctrl 键,鼠标变成手状Q单d可以定位到定义该目标的地?/p>
快速重构目标名或者属性名。选中目标/属性名Q按?Alt + Shift + RQ然后键入你要修改后的|所有引用到的地斚w会随之改动。如下图所C,选中 init 后,按下快捷键,Ҏ initialQ?/p>
调试 Ant 脚本。在标签?lt;target name=..”的左边讄一断点Q然后在~辑器中叛_Q出现的菜单中选“Debug As”-“Ant Build”,出现后的H口与调?Java E序的界面差不多?/p>
q是调试H口Q这里可以选择单步跟进、蟩出等Q?/p>
下面是运行时变量H口。可以看?test 变量的值是“test”:
q行l果H口Q?/p>
由此可见Q如果用好 Eclipse Ant ~辑器所提供强大的功能的话能大大提高?Ant 脚本的效率?/p>
Ant 中?propertyQ属性)文g
刚开始写 Ant 的初学者可能会把所有的信息都放?build.xml 中,下面是q样的一个例子?/p>
<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>
在上面的例子中,所有的路径信息都是写在 build.xml 中。但?Ant 脚本可能在不同的机器或者不同的pȝ上运行,也有可能一些值需要根据环境的不同而变化,?Ant 中可以把所有这些可能变化的地方都放C个配|文件中Q然后在 Ant 脚本中引用这个配|文件就可以了,针对上面的例子,如下所C:
<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
如果惛_ Ant 脚本中引用值的话,只需?W号开_在一?{}'中写入要引用的键倹{如上例中,需要引用编译的目标路径?${dest.dir}'?/p>
使用 Ant d?CVS 中检出(check outQ源代码Qƈ~译打包
Ant 中提供了 cvs dQTaskQ可以从 CVS 服务器中源(注意Q在使用 Ant ?cvs d之前Q请先将 cvs.exe 下蝲C的机器,q且它dC本地?PATH 环境变量中,然后重新启动 Eclipse。否则在执行下面脚本的时候就会得?error=2 的错误)。cvs 的可选用属性很多,在这里介l经怋用到的几个属性。从 CVS 中检源一般需要指定:
CVS 所在的服务器地址Q目?CVS 服务器地址
用户名:d?CVS 服务器你指定的用户名
密码Q登录该 CVS 服务器需要的密码
库\径(Repository PathQ:服务器中的库路径
模块名:当前需要检出的模块名,一般都是以工程的名字作为模块名
标签名:需要从 CVS 中检出哪个标{?
在介l?Ant ?cvs 之前Q先说一下本地的目录l构。在 C 盘的 temp 目录下,分别有四个目录,如下所C:
build 目录Q放~译后的cM及资源文?
dist 目录Q放生成?jar 文g或?war 文g
lib 目录Q放在编译过E中需要用到的 jar 文g
src 目录Q放?cvs 中检出的源文Ӟ包括 JSP {)
?Ant 中这样写可以从中检源:
<cvs cvsRoot=':pserver:username:pwd@cvs.server:/home/testPath'
package='TestProj' dest=' c:/temp/src/testProj ' failonerror='true' />
q段脚本片断的意思就是从?cvs.server'的服务器中,用用户名?username、密码ؓ pwd 的用h出在库\径是 /home/testPath 下的 TestProj 模块Q项目)Q检出后的资源放入本地目?c:/temp/src/testProj 中。在上面q段脚本中,可以看到有很多值可能会Ҏ不同的环境或者用户随之改变的Q比如用户名和密码等Q而且从脚本的重复可利用性来_需要把有些值抽出来攑ֈ配置文g中,如服务器的地址和库路径{。因此把q些可能需要更改的地方攑ֈ property 文g中,效果会更好。改完后的完?Ant 脚本如下所C:
<?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>
对应?TestWeb.properties 文g内容如下所C:
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=
在检Z资源后,需要对其进行编译打包。ؓ了 Ant 脚本更加h可读性和灉|性,我们需要对上面?Ant 脚本q行一些改动。首先将 Ant 脚本中进行分D,如下所C:
<?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>
上面的脚本中Qd分成?个目标(targetQ,脚本的入口点?all'Qall 按顺序调?initQcleanQcheckoutQbuild。其中:
init 是用来做初始化属性文件和讄 classpath {设|初始条件的事情
clean 用来删除上一ơ留下的没用的目录和文g
checkout 已经介绍q了Q是用来?CVS 中检?
build 用来~译源文件ƈ?WAR 包到指定的目?
详细?Ant 脚本可以参见随本文所附的 TestWeb.xml ?TestWeb.properties?/p>
~译q程与生不同目标环境的脚本分开执行
在前面介l的 Ant 脚本中,Ҏ?CVS 服务器中出的资源打成了一个默认的 war 包,q没有考虑Ҏ不同的目标环境来生成不同的包Q从下一节开始介l如何根据不同的环境来生成不同的部v包?/p>
q有一个问题是Qؓ什么需要把?CVS 中检源、编译的q程跟根据目标环境打包的q程分开Q?/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>
解开 WAR ?/font>
我们首先得徏立一个目录(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>
<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>
?init D中首先删除掉上ơ解压的目录和目标打包目录,然后重新建立目录Q在 unpack 中,首先编译好的默?war 包拷贝至 unpack 定义的目录,解压之后?unpack 下的 war 包删除。下面是q时候对应的属性文件?/p>
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
利用 Ant 提供?filter d替换属性?/font>
现在Ҏ不同环境的需要,Ҏ些配|文件的值做一些替换。在 Ant 中,提供?filter dQ得替换值很方便。当然也可以使用下面介绍的正则表辑ּ来替换属性倹{filter 主要用来在同一行内容中的替换,而正则表辑ּ一下子可以替换多行内容。filter 的用例子:
<filter token=' log4j.logger' value='INFO'/>
<copy todir='${dest.dir}' filtering='true'>
<fileset dir='${src.dir}'/>
</copy>
q段脚本的意思就是在 src.dir 目录下的所有文件中Q如果有预先定义好的'@log4j.logger@'占位W的话,在拷贝到 dest.dir 目录后,所有的占位W都被替换成?INFO'?/p>
你也可以所有被替换的值放到某个属性文件中Qfilter d属性文件中的每一个条目读出来q且讄成一?Filter。如下所C:
<filter filtersfile='deploy_env.properties'/>
<copy todir='${dest.dir}' filtering='true'>
<fileset dir='${src.dir}'/>
</copy>
上面的脚本表C所有在 deploy_env 中出现的条目被作ؓ一?filterQ在拯?dest.dir 目录后,所?src.dir 目录中存在的占位W将被替换成 deploy_env 中的倹{具体的例子可以参见随本文附带的 deploy.xmlQ?deploy_env.properties ?Test.properties?
其中 deploy.xml ?ant 脚本Qdeploy_env.properties 中包含所有要替换的|?Test.properties 中是包含有占位符的资源文件?/p>
利用正则表达式替换属性?/font>
Ant 中支持多U正则表辑ּQ在q行 Ant 的时候用哪种正则表达式可以通过讄 ant.regexp.regexpimpl 的值来切换QAnt 支持的的正则表达式有Q?/p>
java.util.regex package of JDK 1.4
jakarta-regexp
installation dependencies
正则表达式的例子Q?/p>
<replaceregexp byline='true'>
<regexp pattern='正则表达?/>
<substitution expression='要替换的?/>
<fileset dir='${unpack.war.dir}/WEB-INF' includes='web.xml'/>
</replaceregexp>
byline 属性用来确认被替换的时候是一ơ替换一行还是多行;pattern 属性用来指明正则表辑ּQsubstitution expression 中是替换的|替换的值都定义在相对应的配|文件中Qfileset 属性中?dir 用来指定被替换文件所在的目录Qincludes 用来指定要替换哪个文件。需要注意的是,如果在正则表辑ּ或者替换的g出现'<'的话Q需要用转义W?<'?/p>
?Eclipse3.1 中已l内|了Ҏ则表辑ּ的支持;但是如果你在命o行中q行需要正则表辑ּ支持的脚本的话,则需要自己将正则表达式的包下载下来加?classpath 中。在随文章的 deploy.xml 中提供了一个简单的替换属性文件的值的例子。正则表辑ּ的例子可以在本文所带的 deploy.xml 中找到?/p>
Ant 使用条g表达式,Ҏ属性值删除不需要的文g
在生成部|包的时候,q有可能会根据目标环境的不同Q删除一些不同的文g。比如在产品环境中那些作为测试需要的代码往往需要删除,但是试环境中ƈ不需要。因此就需要条件表辑ּ来做判断。如下所C:
<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>
在上面的例子中,先读?${deploy.isTestEnv} 的|在配|文?deloy.properties 中)Q根据读出的值对属?isTestEnv 讑ր|如果 ${deploy.isTestEnv} 的值是 trueQisTestEnv 的g?trueQ否则是 false。然后分别调用目标段 trueCondition ?falseCondition。在q里Q表辑ּ值的判断是用'istrue'Q在 Ant 中还提供了很多别的表辑ּQ比?not/and/orQequals {等Q具体关于条件表辑ּ的信息可以参考:http://ant.apache.org/manual/CoreTasks/condition.html Q该也可以在随下蝲下来的文档中扑ֈ?/p>
在段 trueCondition 中,判断 isTestEnvQ如果是真的话就q行Q否则不q行Q在D?falseCondition 中,也判?isTestEnvQ如果是假就q行Q否则不q行Q在D中可以Ҏ情况做相应的事情。条件判断式的例子可以在本文?deploy.xml 中找到?/p>
重新打包Qƈ拯C同的目录
在上面的替换q程完成后,调用 war ?unpack 目录下的内容重新打包?/p>
<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>
详细的例子可以参见随本文的附?deploy.xml ?deploy.properties?/p>
l论
通过本文可以看出~写好有效的 Ant 脚本能很好的解决在编译部|包的时候出现的问题Q将那些变化的内Ҏ到配|文件中Q在部v的时候切换不同的配置文g可以实现生成不同的部v包。文中也介绍了如何?Eclipse 来提高你~写/调试 Ant 脚本的效率?/p>
]]> c++学习W记 http://www.aygfsteel.com/ytfei/archive/2006/06/25/54983.html飞翔 飞翔 Sun, 25 Jun 2006 05:35:00 GMT http://www.aygfsteel.com/ytfei/archive/2006/06/25/54983.html http://www.aygfsteel.com/ytfei/comments/54983.html http://www.aygfsteel.com/ytfei/archive/2006/06/25/54983.html#Feedback 0 http://www.aygfsteel.com/ytfei/comments/commentRss/54983.html http://www.aygfsteel.com/ytfei/services/trackbacks/54983.html Teacher * 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; ]]> memcached 的?/title> http://www.aygfsteel.com/ytfei/archive/2006/06/10/51787.html飞翔 飞翔 Sat, 10 Jun 2006 02:44:00 GMT http://www.aygfsteel.com/ytfei/archive/2006/06/10/51787.html http://www.aygfsteel.com/ytfei/comments/51787.html http://www.aygfsteel.com/ytfei/archive/2006/06/10/51787.html#Feedback 2 http://www.aygfsteel.com/ytfei/comments/commentRss/51787.html http://www.aygfsteel.com/ytfei/services/trackbacks/51787.html
用户讉K|页Ӟ查看 memcached 中是否有当前用户?SESSION 数据Q?session_id() 作ؓ唯一标识W;如果数据存在Q则直接q回Q如果不存在Q再q行数据库连接,获取 SESSION 数据Qƈ此数据保存?memcached 中,供下ơ用;
当前?PHP q行l束Q或使用?session_write_close() Q时Q会调用 My_Sess::write() ҎQ将数据写入数据库,q样的话Q每ơ仍然会有数据库操作Q对于这个方法,也需要进行优化。用一个全局变量Q记录用戯入页面时?SESSION 数据Q然后在 write() Ҏ内比较此数据与想要写入的 SESSION 数据是否相同Q不同才q行数据库连接、写入数据库Q同时将 memcached 中对应的对象删除Q如果相同的话,则表C?SESSION 数据未改变,那么可以不做Q何操作,直接q回了;
那么用户 SESSION q期旉怎么解决呢?记得 memcached ?add() Ҏ有个q期旉参数 $exp 吗?把这个参数D|成于 SESSION 最大存zL间即可。另外别忘了l那些一直在U的用户延箋 SESSION 旉Q这个可以在 write() Ҏ中解冻I通过判断旉Q符合条件则更新数据库数据?
]]> ?千万U邮件系l的设计及数据库中间仉分实?/title> http://www.aygfsteel.com/ytfei/archive/2006/06/09/51742.html飞翔 飞翔 Fri, 09 Jun 2006 09:27:00 GMT http://www.aygfsteel.com/ytfei/archive/2006/06/09/51742.html http://www.aygfsteel.com/ytfei/comments/51742.html http://www.aygfsteel.com/ytfei/archive/2006/06/09/51742.html#Feedback 0 http://www.aygfsteel.com/ytfei/comments/commentRss/51742.html http://www.aygfsteel.com/ytfei/services/trackbacks/51742.html
]]> ?在JavaE序中截h制台输出 http://www.aygfsteel.com/ytfei/archive/2006/05/21/47293.html飞翔 飞翔 Sun, 21 May 2006 07:52:00 GMT http://www.aygfsteel.com/ytfei/archive/2006/05/21/47293.html http://www.aygfsteel.com/ytfei/comments/47293.html http://www.aygfsteel.com/ytfei/archive/2006/05/21/47293.html#Feedback 0 http://www.aygfsteel.com/ytfei/comments/commentRss/47293.html http://www.aygfsteel.com/ytfei/services/trackbacks/47293.html
在JavaE序中截h制台输出 文档提供者:newebug () ?2005-3-15俞良?/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>
试图从PipedInputStreamd数据Q?
PipedInputStream的缓冲区为“空”(即不存在可读取的数据Q,
最后一个向PipedOutputStream写数据的U程不再zdQ通过Thread.isAlive())? q是一个很微妙的时刻,同时也是一个极光要的时刻。假定有一个线Ew向PipedOutputStream写入数据Q另一个线Er从对应的PipedInputStreamd数据。下面一pd的事件将DrU程在试图读取PipedInputStream旉到IOException异常Q?/p>
w向PipedOutputStream写入数据?
wl束Qw.isAlive()q回falseQ?
r从PipedInputStreamdw写入的数据,清空PipedInputStream的缓冲区?
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>
写操作线Ew已经创徏Q但rU程q不存在?
w向PipedOutputStream写入数据?
ȝEr被创建,q从PipedInputStreamd数据?
rU程l束?
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>
]]> 想作个静态网는成器 http://www.aygfsteel.com/ytfei/archive/2006/05/20/47215.html飞翔 飞翔 Sat, 20 May 2006 12:48:00 GMT http://www.aygfsteel.com/ytfei/archive/2006/05/20/47215.html http://www.aygfsteel.com/ytfei/comments/47215.html http://www.aygfsteel.com/ytfei/archive/2006/05/20/47215.html#Feedback 2 http://www.aygfsteel.com/ytfei/comments/commentRss/47215.html http://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" /> ]]> 调试JSP解决TOMCAT~存问题 http://www.aygfsteel.com/ytfei/archive/2006/05/12/45799.html飞翔 飞翔 Fri, 12 May 2006 03:10:00 GMT http://www.aygfsteel.com/ytfei/archive/2006/05/12/45799.html http://www.aygfsteel.com/ytfei/comments/45799.html http://www.aygfsteel.com/ytfei/archive/2006/05/12/45799.html#Feedback 2 http://www.aygfsteel.com/ytfei/comments/commentRss/45799.html http://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>
]]> JSTL 入门: 表达式语a http://www.aygfsteel.com/ytfei/archive/2006/05/10/45550.html飞翔 飞翔 Wed, 10 May 2006 14:33:00 GMT http://www.aygfsteel.com/ytfei/archive/2006/05/10/45550.html http://www.aygfsteel.com/ytfei/comments/45550.html http://www.aygfsteel.com/ytfei/archive/2006/05/10/45550.html#Feedback 0 http://www.aygfsteel.com/ytfei/comments/commentRss/45550.html http://www.aygfsteel.com/ytfei/services/trackbacks/45550.html 阅读全文 ]]> 攚wjpetstore4.0 (struts+ibatis) ===> (struts+spring+ibatis) 的轻量J2EE开?/title> http://www.aygfsteel.com/ytfei/archive/2006/05/02/44329.html飞翔 飞翔 Mon, 01 May 2006 18:53:00 GMT http://www.aygfsteel.com/ytfei/archive/2006/05/02/44329.html http://www.aygfsteel.com/ytfei/comments/44329.html http://www.aygfsteel.com/ytfei/archive/2006/05/02/44329.html#Feedback 1 http://www.aygfsteel.com/ytfei/comments/commentRss/44329.html http://www.aygfsteel.com/ytfei/services/trackbacks/44329.html U别: 初
吴高?/font>
常dL厂信息技术部
2005 q?2 ?01 ?/p>
大多数IT l织都必解决三个主要问题:1Q帮助组l减成?2Q增加ƈ且保持客?3Q加快业务效率。完成这些问题一般都需要实现对多个业务pȝ的数据和业务逻辑的无~访问,也就是说Q要实施pȝ集成工程Q以便联l业务流E、实现数据的讉K与共享?/blockquote>
JpetStore 4.0是ibatis的最新示例程序,ZStruts MVC框架Q注Q非传统Struts开发模式)Q以ibatis作ؓ持久化层。该CZE序设计优雅Q层ơ清晎ͼ可以学习以及作ؓ一个高效率的编E模型参考。本文是在其基础上,采用Spring对其中间层(业务层)q行攚w。开发量q一步减,同时又拥有了Spring的一些好处?/p>
1. 前言
JpetStore 4.0是ibatis的最新示例程序。ibatis是开源的持久层品,包含SQL Maps 2.0 ?Data Access Objects 2.0 框架。JpetStoreCZE序很好的展CZ如何利用ibatis来开发一个典型的J2EE web应用E序。JpetStore有如下特点:
ibatis数据?
POJO业务?
POJO领域c?
Struts MVC
JSP 表示?
以下是本文用到的关键技术介l,本文假设您已l对StrutsQSpringFramewokQibatis有一定的了解Q如果不是,请首先查阅附录中的参考资料?/p>
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后具体剖析一下?
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应用最成熟的还是在事务理上?
ibatis 是一个功能强大实用的SQL Map工具Q不同于其他ORM工具Q如hibernateQ,它是SQL语句映射成Java对象Q而对于ORM工具Q它的SQL语句是根据映定义生成的。ibatis 以SQL开发的工作量和数据库移植性上的让步,为系l设计提供了更大的自q间。有ibatis代码生成的工P可以ҎDDL自动生成ibatis代码Q能减少很多工作量?
2. JpetStoreq?/font>
2.1. 背景
最初是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>
2.2. JpetStore开发运行环境的建立
1、开发环?/p>
Java SDK 1.4.2
Apache Tomcat 4.1.31
Eclipse-SDK-3.0.1-win32
HSQLDB 1.7.2
2、Eclipse插g
EMF SDK 2.0.1QEclipse建模框架Qlomboz插g需要,可以使用runtime版本?
lomboz 3.0QJ2EE插gQ用来在Eclipse中开发J2EE应用E序
Spring IDE 1.0.3QSpring Bean配置理插g
xmlbuddy_2.0.10Q编辑XMLQ用免费版功能即?
tomcatPluginV3Qtomcat理插g
Properties EditorQ编辑java的属性文?q可以预览以及自动存盘ؓUnicode格式。免M手工或者ANT调用native2ascii的麻烦?
3、示例源E序
ibatisCZE序JpetStore 4.0 http://www.ibatis.com/jpetstore/jpetstore.html
攚w后的源E序Q?springQ(源码链接Q?
2.3. 架构
? JpetStore架构?/strong>
? 是JPetStore架构图,更详l的内容请参见JPetStore的白皮书。参照这个架构图Q让我们E微剖析一下源代码Q得出JpetStore 4.0的具体实现图Q见?Q,思\一下子p然开朗了。前a中提到的非传l的struts开发模式,关键在struts Actioncdform beancM?/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>
q样重心p{Udform bean上了Q它已经不是普通意义上的form bean了。查看源代码Q可以看到它不仅仅有数据和校?重置ҎQ而且已经h了行为,从这个意义上来说Q它更像一个BO(Business Object)。这是前文讲到的,BeanActioncd用反原理,ҎURL来决定调用form bean的哪个方法(行ؓQ。form bean的这些方法的{很简单,例如Q?/p>
public String myActionMethod() {
//..work
return "success";
}
Ҏ的返回值直接就是字W串Q对应的是forward的名Uͼ而不再是ActionForward对象Q创建ActionForward对象的Q务已l由BeanActioncM劳了?/p>
另外Q程序还提供了ActionContext工具c,该工L装了request 、response、form parameters、request attributes、session attributes?application attributes中的数据存取操作Q简单而线E安全,form beancM用该工具cd以进一步从表现层框架解耦?/p>
在这里需要特别指出的是,BeanActioncL对struts扩展的一个有益尝试,虽然提供了非常好的应用开发模式,但是它还非常斎ͼ一直在发展中?/p>
? JpetStore 4.0具体实现
2.4. 代码剖析
下面p我们开始进一步分析JpetStore4.0的源代码Qؓ下面的改造铺路?/p>
BeanAction.java是唯一一个Struts actionc,位于com.ibatis.struts包下。正如上文所aQ它是一个通用的控制类Q利用反机Ӟ把控制{Udform bean的某个方法来处理。详l处理过E参考其源代码,单明晰?
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>
Struts-config.xml的配|里?U映方式,来告诉BeanAction把控制{到哪个form bean对象的哪个方法来处理?/p>
以这个请求连接ؓ例http://localhost/jpetstore4/shop/viewOrder.do
1. URL Pattern
<action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
name="orderBean" scope="session"
validate="false">
<forward name="success" path="/order/ViewOrder.jsp"/>
</action>
此种方式表示Q控制将被{发到"orderBean"q个form bean对象 ?viewOrder"ҎQ行为)来处理。方法名?path"参数的以"/"分隔的最后一部分?/p>
2. Method Parameter
<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>
此种方式表示Q控制将被{发到"orderBean"q个form bean对象?viewOrder"ҎQ行为)来处理。配|中?parameter"参数表示form beancM的方法?parameter"参数优先?path"参数?/p>
3. No Method call
<action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
name="orderBean" parameter="*" scope="session"
validate="false">
<forward name="success" path="/order/ViewOrder.jsp"/>
</action>
此种方式表示Qform bean上没有Q何方法被调用。如果存?name"属性,则struts把表单参数等数据填充到form bean对象后,把控制{发到"success"。否则,如果name为空Q则直接转发控制?success"?/p>
q就相当于struts内置的org.apache.struts.actions.ForwardAction的功?/p>
<action path="/shop/viewOrder" type="org.apache.struts.actions.ForwardAction"
parameter="/order/ViewOrder.jsp " scope="session" validate="false">
</action>
ServicecM于com.ibatis.jpetstore.service包下Q属于业务层。这些类装了业务以及相应的事务控制。Servicecȝform beancL调用?
com.ibatis.jpetstore.persistence.iface包下的类是DAO接口Q属于业务层Q其屏蔽了底层的数据库操作,供具体的ServicecL调用。DaoConfigcL工具c(DAO工厂c)QServicec通过DaoConfigcL获得相应的DAO接口Q而不用关心底层的具体数据库操作,实现了如?中{耦合2}的解耦?
com.ibatis.jpetstore.persistence.sqlmapdao包下的类是对应DAO接口的具体实玎ͼ在JpetStore4.0中采用了ibatis来实现ORM。这些实现类l承BaseSqlMapDaoc,而BaseSqlMapDaocdl承ibatis DAO 框架中的SqlMapDaoTemplatecRibatis的配|文件存攑֜com.ibatis.jpetstore.persistence.sqlmapdao.sql目录下。这些类和配|文件位于数据层
DomaincM于com.ibatis.jpetstore.domain包下Q是普通的javabean。在q里用作数据传输对象QDTOQ,贯穿视图层、业务层和数据层Q用于在不同层之间传输数据?
剩下的部分就比较单了Q请看具体的源代码,非常清晰?/p>
2.5. 需要改造的地方
JpetStore4.0的关键就在struts Actioncdform beancMQ这也是其精华之一Q虽然该实现方式是试验性,待扩充和验证Q,在此ơ改造中我们要保留下来,x制层一点不变,表现层获取相应业务类的方式变了(要加载spring环境Q,其它保持不变。要特别x的改动是业务层和持久层,q运的是JpetStore4.0设计非常好,需要改动的地方非常,而且由模式可循,如下Q?/p>
1. 业务层和数据层用Spring BeanFactory机制理?/p>
2. 业务层的事务由spring 的aop通过声明来完成?/p>
3. 表现层(form beanQ获取业务类的方法改p定义工厂cL实现Q加载spring环境Q?/p>
3. JPetStore的改?/font>
3.1. 攚w后的架?/font>
其中U色部分是要增加的部分,蓝色部分是要修改的部分。下面就让我们逐一剖析?/p>
3.2. Spring Context的加?/font>
Z在Struts中加载Spring ContextQ一般会在struts-config.xml的最后添加如下部分:
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/applicationContext.xml" />
</plug-in>
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>
具体的来_是在com.ibatis.spring包下创徏CustomBeanFactoryc,spring的配|文件applicationContext.xml也放在这个目录下。以下就是该cȝ全部代码Q很单:
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);
}
}
实际上就是封装了Spring 的XMLBeanFactory而已Qƈ且Spring的配|文件只需要加载一ơ,以后可以直接用CustomBeanFactory.getBean("someBean")来获得需要的对象?例如someBean)Q而不需要知道具体的cRCustomBeanFactorycȝ于{耦合1}的解耦?/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>
所以,很自然的Q我们就创徏了CustomBeanFactoryc,在表现层来衔接struts和spring。就q么单,实现了另一U方式的{耦合一}的解耦?/p>
3.3. 表现?/font>
?
面分析到Qstruts和spring是在表现层衔接v来的Q那么表现层p做稍微的更改Q即所需要的servicecȝ对象创徏上。以表现层的AccountBeancMؓ例:
原来的源代码如下
private static final AccountService accountService = AccountService.getInstance();
private static final CatalogService catalogService = CatalogService.getInstance();
攚w后的源代码如下
private static final AccountService accountService = (AccountService)CustomBeanFactory.getBean("AccountService");
private static final CatalogService catalogService = (CatalogService)CustomBeanFactory.getBean("CatalogService");
其他的几个presentationcM同样方式攚w。这P表现层就完成了。关于表现层的其它部分如JSP{一概不动。也许您会说Q没有看Z么特别之处的好处啊?你还是额外实C一个工厂类。别着急,帷幕刚刚开启,spring是在表现层引入,但您发没发现Q?/p>
presentationcM仅面向servicecȝ接口~程Q具?AccountService"是哪个实现类QpresentationcM知道Q是在spring的配|文仉配置。(本例中,Z最大限度的保持原来的代码不作变化,没有抽象出接口)。Spring鼓励面向接口~程Q因为是如此的方便和自然Q当然您也可以不q么做?
CustomBeanFactoryq个工厂cMؓ什么会如此单,因ؓ其直接用了Spring的BeanFactory。Spring从其核心而言Q是一个DI容器Q其设计哲学是提供一U无侵入式的高扩展性的框架。ؓ了实现这个目标,Spring 大量引入了Java 的Reflection机制Q通过动态调用的方式避免编码方式的U束Qƈ在此基础上徏立了其核心组件BeanFactoryQ以此作为其依赖注入机制的实现基。org.springframework.beans包中包括了这些核心组件的实现c,核心中的核心为BeanWrapper和BeanFactorycR? 3.4. 持久?/font>
在讨Z务层之前Q我们先看一下持久层Q如下图所C:
在上文中Q我们把iface包下的DAO接口归ؓ业务层,在这里不需要做修改。ibatis的sql配置文g也不需要改。要改的是DAO实现c,q在spring的配|文件中配置h?/p>
1、修改基c?/p>
所有的DAO实现c都l承于BaseSqlMapDaocR修改BaseSqlMapDaocd下:
public class BaseSqlMapDao extends SqlMapClientDaoSupport {
protected static final int PAGE_SIZE = 4;
protected SqlMapClientTemplate smcTemplate = this.getSqlMapClientTemplate();
public BaseSqlMapDao() {
}
}
使BaseSqlMapDaocL为承于Spring提供的SqlMapClientDaoSupportc,q定义了一个保护属性smcTemplateQ其cd为SqlMapClientTemplate。关于SqlMapClientTemplatecȝ详细说明请参照附录中?Spring中文参考手?
2、修改DAO实现c?/p>
所有的DAO实现c还是承于BaseSqlMapDaoc,实现相应的DAO接口Q但其相应的DAO操作委托SqlMapClientTemplate来执行,以AccountSqlMapDaocMؓ例,部分代码如下Q?/p>
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);
}
p么简单,所有函数的{都是一LQ只需要查找替换就可以了!
3、除d厂类以及相应的配|文?/p>
除去DaoConfig.javaq个DAO工厂cd相应的配|文件dao.xmlQ因为DAO的获取现在要用spring来管理?/p>
4、DAO在Spring中的配置QapplicationContext.xmlQ?/p>
<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>
具体的语法请参照附录中的"Spring中文参考手?。在q里只简单解释一下:
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>
2. sqlMapClient节点。这个是针对ibatis SqlMap的SqlMapClientFactoryBean配置。实际上配置了一个sqlMapClient的创建工厂类。configLocation属性配|了ibatis映射文g的名U。dataSource属性指向了使用的数据源Q这h有用sqlMapClient的DAO都默认用了该数据源Q除非在DAO的配|中另外昑ּ指定?/p>
3. TransactionManager节点。定义了事务Q用的是DataSourceTransactionManager?/p>
4. 下面可以定义DAO节点了,如AccountDaoQ它的实现类是com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDaoQ用的SQL配置从sqlMapClient中读取,数据库连接没有特别列出,那么是默认使用sqlMapClient配置的数据源datasource?/p>
q样Q我们就把持久层攚w完了,其他的DAO配置cM于AccountDao。怎么P单吧。这ơ有接口了:Q?AccountDao接口Q?gt;AccountSqlMapDao实现?/p>
3.5. 业务?/font>
业务层的位置以及相关c,如下图所C:
在这个例子中只有3个业务类Q我们以OrderServicecMؓ例来攚w,q个cL最复杂的,其中涉及了事务?/p>
1、在ApplicationContext配置文g中增加bean的配|:
<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>
定义了一个OrderServiceQ还是很Ҏ懂的。ؓ了简单v见,使用了嵌套beanQ其实现cLcom.ibatis.jpetstore.service.OrderServiceQ分别引用了ItemDaoQOrderDaoQSequenceDao。该bean的insert*实现了事务管?AOP方式)。TransactionProxyFactoryBean自动创徏一个事务advisorQ?该advisor包括一个基于事务属性的pointcut,因此只有事务性的Ҏ被拦截?/p>
2、业务类的修?/p>
以OrderServiceZQ?/p>
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;
}
//剩下的部?
…?
}
U色部分Z攚w分。Spring采用的是Type2的设|依赖注入,所以我们只需要定义属性和相应的设值函数就可以了,ItemDaoQOrderDaoQSequenceDao的值由spring在运行期间注入。构造函数就可以为空了,另外也不需要自q写代码处理事务了Q事务在配置中声明)QdaoManager.startTransaction();{与事务相关的语句也可以L了。和原来的代码比较一下,是不是处理精了很多!可以更关注业务的实现?/p>
4. l束?/font>
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>
参考资?
关于作?/font>
吴高峎ͼ原一直在中兴通讯从事q营支撑产品的研发工作,对J2EE以及各种开源项目感兴趣。现在常德卷烟厂信息技术部从事EAI的徏设。联pL式:shuwgf@21cn.com
]]>动态编译JAVAE序 http://www.aygfsteel.com/ytfei/archive/2005/09/06/12194.html飞翔 飞翔 Tue, 06 Sep 2005 03:53:00 GMT http://www.aygfsteel.com/ytfei/archive/2005/09/06/12194.html http://www.aygfsteel.com/ytfei/comments/12194.html http://www.aygfsteel.com/ytfei/archive/2005/09/06/12194.html#Feedback 0 http://www.aygfsteel.com/ytfei/comments/commentRss/12194.html http://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如下:
?nbsp;
String [] args = new String [] {?d”, ?homemytestclasses”, “Test.java”};
Int status = javac.compile(args);
?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条语句如下:
System .out.println(“HelloQThis runtime codeQ?;
~译器不支持~译单个Java语句Q被~译的对象必L一个以.java为后~的、结构合法的cLE序文gQ所以需要对q个语句q行攚w,变成一个完整的c,q把q条语句|入mainҎ中,便于试?nbsp;
public class <临时cL件名> {
public static void main(String [] args) throws Exception {
System .out.println(“HelloQThis runtime codeQ?;
}
}
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;
File file;
file = File .createTempFile(“JavaRuntime”, ?java”, new File (System .getProperty(“user.dir?));
String filename = file.getName();
String classname = getClassName(filename);
//代码输出到文g
PrintWriter out = new PrintWriter (new FileOutputStream (file));
out.println(?B>public class ?nbsp;+ classname + ?nbsp;{”};
out.println(?.代码..?;
out.println(“}?;
//关闭文g?nbsp;
out.flush();
out.close();
我们U定被创建的临时文g名以“JavaRuntime”ؓ头缀Q可L命名Q,后缀名以?java”结。一个待~译的Java源文件已被动态生成。下一步要从com.sun.tools.javac包中创徏一个Main实例Q调用javac.compile()Ҏ~译q个临时文gQ?nbsp;
Private static com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main();
String [] args = new String [] {?d”, System .getProperty(“user.dir?Qfilename };
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;
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;
~译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;
]]>Hibernate In Action Note http://www.aygfsteel.com/ytfei/archive/2005/09/05/12119.html飞翔 飞翔 Mon, 05 Sep 2005 14:14:00 GMT http://www.aygfsteel.com/ytfei/archive/2005/09/05/12119.html http://www.aygfsteel.com/ytfei/comments/12119.html http://www.aygfsteel.com/ytfei/archive/2005/09/05/12119.html#Feedback 0 http://www.aygfsteel.com/ytfei/comments/commentRss/12119.html http://www.aygfsteel.com/ytfei/services/trackbacks/12119.html Using 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 &... ]]> Hibernate In Action http://www.aygfsteel.com/ytfei/archive/2005/09/01/11794.html飞翔 飞翔 Thu, 01 Sep 2005 15:59:00 GMT http://www.aygfsteel.com/ytfei/archive/2005/09/01/11794.html http://www.aygfsteel.com/ytfei/comments/11794.html http://www.aygfsteel.com/ytfei/archive/2005/09/01/11794.html#Feedback 0 http://www.aygfsteel.com/ytfei/comments/commentRss/11794.html http://www.aygfsteel.com/ytfei/services/trackbacks/11794.html log4j.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 ]]> Struts menu的用(动态数据) http://www.aygfsteel.com/ytfei/archive/2005/08/30/12079.html飞翔 飞翔 Tue, 30 Aug 2005 03:42:00 GMT http://www.aygfsteel.com/ytfei/archive/2005/08/30/12079.html http://www.aygfsteel.com/ytfei/comments/12079.html http://www.aygfsteel.com/ytfei/archive/2005/08/30/12079.html#Feedback 0 http://www.aygfsteel.com/ytfei/comments/commentRss/12079.html http://www.aygfsteel.com/ytfei/services/trackbacks/12079.html 1. 拯必要?/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.jsStyles/ 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" /> < SPAN>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. getN ame() ; mc.setName(name); //讄昄的节点名U?BR> String parent = (String) row. getP arentname() ; 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. getT itle() ; mc.setTitle(title); String location = (String) row. getL ocation() ; 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 = "";%>
<%}catch(Exception e){ e.printStackTrace(); } %> 5 over
飞翔 2005-08-30 11:42
文章来源:http://ytfei.cnblogs.com/archive/2005/08/30/225943.html ]]> Struts Validator验证器用指?/title> http://www.aygfsteel.com/ytfei/archive/2005/08/04/12081.html飞翔 飞翔 Thu, 04 Aug 2005 08:20:00 GMT http://www.aygfsteel.com/ytfei/archive/2005/08/04/12081.html http://www.aygfsteel.com/ytfei/comments/12081.html http://www.aygfsteel.com/ytfei/archive/2005/08/04/12081.html#Feedback 0 http://www.aygfsteel.com/ytfei/comments/commentRss/12081.html http://www.aygfsteel.com/ytfei/services/trackbacks/12081.html 阅读全文 ]]>
վ֩ģ壺
̨ |
|
|
|
|
|
|
Ϫ |
ξ |
żҿ |
̳ |
ڶ |
|
Դ |
ף |
â |
ͨ |
ƺ |
ƽ |
ζ |
|
ɽ |
|
|
ԭ |
|
|
|
|
|
ٰ |
¡ |
|
|
|
佭 |
ī |
|
|
J |
̫ |