﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;明月松间照 清泉石上流-文章分类-J2EE</title><link>http://www.blogjava.net/xuechen0721/category/18009.html</link><description>&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;———&amp;nbsp;兵临城下&amp;nbsp;&amp;nbsp;猫科动物</description><language>zh-cn</language><lastBuildDate>Wed, 04 Jul 2007 21:41:38 GMT</lastBuildDate><pubDate>Wed, 04 Jul 2007 21:41:38 GMT</pubDate><ttl>60</ttl><item><title>ANT的安装/配置笔记</title><link>http://www.blogjava.net/xuechen0721/articles/128202.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Wed, 04 Jul 2007 14:53:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/128202.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/128202.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/128202.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/128202.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/128202.html</trackback:ping><description><![CDATA[<div class=entry-body>
<p>内容摘要：<br>ANT是一个基于JAVA的自动化脚本引擎，脚本格式为XML。除了做JAVA编译相关任务外，ANT还可以通过插件实现很多应用的调用。<br></p>
<ol>
    <li>ANT的基本概念：
    <li>ANT的安装：解包，设置路径
    <li>ANT的使用：最好的学习只不过是一个简单实用的例子起步&#8230;&#8230; </li>
</ol>
</div>
<div class=entry-more id=more>
<h2>ANT的基本概念：Java的Makefile</h2>
<p>当一个代码项目大了以后，每次重新编译，打包，测试等都会变得非常复杂而且重复，因此c语言中有make脚本来帮助这些工作的批量完成。在Java 中应用是平台无关性的，当然不会用平台相关的make脚本来完成这些批处理任务了，ANT本身就是这样一个流程脚本引擎，用于自动化调用程序完成项目的编译，打包，测试等。除了基于JAVA是平台无关的外，脚本的格式是基于XML的，比make脚本来说还要好维护一些。<br></p>
<p>每个ant脚本（缺省叫build.xml）中设置了一系列任务(target)：比如对于一个一般的项目可能需要有以下任务。</p>
<ul>
    <li>任务1：usage 打印本脚本的帮助信息（缺省）
    <li>任务2：clean &lt;-- init 清空初始化环境
    <li>任务3：javadoc &lt;-- build &lt;-- init 生成JAVADOC
    <li>任务4：jar &lt;-- build &lt;-- init 生成JAR
    <li>任务5：all &lt;-- jar + javadoc &lt;-- build &lt;-- init 完成以上所有任务：jar javadoc </li>
</ul>
而多个任务之间往往又包含了一定了依赖关系：比如把整个应用打包任务(jar)的这个依赖于编译任务(build)，而编译任务又依赖于整个环境初始化任务(init)等。<br><br>注：我看到很多项目的ant脚本中的命名基本上都是一致的，比如：编译一般叫build或者compile；打包一般叫jar或war；生成文档一般命名为javadoc或javadocs；执行全部任务all。在每个任务的中，ANT会根据配置调用一些外部应用并配以相应参数执行。虽然ANT可调用的外部应用种类非常丰富，但其实最常用的就2，3个：比如javac javadoc jar等。
<h2>ANT的安装</h2>
解包后在系统可执行路径中加入指向ant的bin的路径就可以了，比如可以在GNU/Linux上把以下配置加入/etc/profile中：<br>export ANT_HOME=/home/ant<br>export JAVA_HOME=/usr/java/j2sdk1.4.1<br>export PATH=$PATH:$JAVA_HOME/bin:$ANT_HOME/bin<br><br>这样执行ant 后，如果不指定配置文件ant会缺省找build.xml这个配置文件，并根据配置文件执行任务，缺省的任务设置可以指向最常用的任务，比如： build，或指向打印帮助信息：usage，告诉用户有那些脚本选项可以使用。<br><br>
<h2>ANT的使用<br></h2>
<div style="TEXT-ALIGN: left">最好的学习过程就是看懂那些open source项目中的build.xml脚本，然后根据自己的需要简化成一个更简单的，ANT和APACHE上很多非常工程派的项目：简单易用，而且适应性非常强，因为这些项目的建立往往来源于开发人员日常最直接的需求。<br>以下是的一个<a href="http://sourceforge.net/projects/weblucene/"><font color=#0082ff><u>WebLucene</u></font></a>应用的例子：修改自<a href="http://www.jdom.org/"><font color=#0082ff><u>JDOM</u></font></a>的build.xml：<br><br>&lt;project default="usage" basedir="."&gt;<br><br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Initialization target&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="init"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;tstamp/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property file="${basedir}/build.properties" /&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property name="Name" value="ProjectFullName"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property name="name" value="project_name"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property name="version" value="0.2"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property name="year" value="2003"/&gt;<br><br>&nbsp;&nbsp;&nbsp; &lt;echo message="----------- ${Name} ${version} [${year}] ------------"/&gt;<br><br>&nbsp;&nbsp;&nbsp; &lt;property name="debug" value="off"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property name="optimize" value="on"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property name="deprecation" value="on"/&gt;<br><br>&nbsp;&nbsp;&nbsp; &lt;property name="src.dir" value="./src/WEB-INF/src"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property name="lib.dir" value="./src/WEB-INF/lib"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property name="packages" value="com.chedong.*,org.apache.lucene.*"/&gt;<br><br>&nbsp;&nbsp;&nbsp; &lt;property name="build.src" value="./src/WEB-INF/build"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property name="build.dest" value="./src/WEB-INF/classes"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;property name="build.javadocs" value="./src/doc"/&gt;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &lt;path id="classpath"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;pathelement path="${jsdk_jar}"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="${lib.dir}"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="**/*.jar"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/fileset&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/path&gt;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &lt;filter token="year" value="${year}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;filter token="version" value="${version}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;filter token="date" value="${TODAY}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;filter token="log" value="true"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;filter token="verbose" value="true"/&gt;<br>&nbsp; &lt;/target&gt;<br><br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Help on usage&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="usage" depends="init"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message="${Name} Build file"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message="-------------------------------------------------------------"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message=""/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message=" available targets are:"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message=""/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message="&nbsp;&nbsp; jar&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt; generates the ${name}.jar file"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message="&nbsp;&nbsp; build&nbsp;&nbsp;&nbsp; --&gt; compiles the source code"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message="&nbsp;&nbsp; javadoc&nbsp; --&gt; generates the API documentation"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message="&nbsp;&nbsp; clean&nbsp;&nbsp;&nbsp; --&gt; cleans up the directory"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message=""/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message=" Please rename build.properties.default to build.properties"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message=" and edit build.properties to specify JSDK 2.3 classpath."/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message=""/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message=" See the comments inside the build.xml file for more details."/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message="-------------------------------------------------------------"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message=""/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;echo message=""/&gt;<br>&nbsp; &lt;/target&gt;<br><br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Prepares the source code&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="prepare-src" depends="init"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;!-- create directories --&gt;<br>&nbsp;&nbsp;&nbsp; &lt;mkdir dir="${build.src}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;mkdir dir="${build.dest}"/&gt;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &lt;!-- copy src files --&gt;<br>&nbsp;&nbsp;&nbsp; &lt;copy todir="${build.src}"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="${src.dir}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/copy&gt;<br>&nbsp; &lt;/target&gt;<br><br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Compiles the source directory&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="build" depends="prepare-src"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;javac srcdir="${build.src}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; destdir="${build.dest}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; debug="${debug}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; optimize="${optimize}"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;classpath refid="classpath"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/javac&gt;<br>&nbsp; &lt;/target&gt;<br><br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Creates the class package&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="jar" depends="build"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;jar jarfile="${lib.dir}/${name}.jar"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; basedir="${build.dest}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; includes="**"/&gt;<br>&nbsp; &lt;/target&gt;<br><br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Creates the API documentation&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="javadoc" depends="build"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;mkdir dir="${build.javadocs}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;javadoc packagenames="${packages}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sourcepath="${build.src}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; destdir="${build.javadocs}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; author="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; version="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; use="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; splitindex="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; windowtitle="${Name} API"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; doctitle="${Name}"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;classpath refid="classpath"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/javadoc&gt;<br>&nbsp; &lt;/target&gt;<br><br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Clean targets&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="clean" depends="init"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;delete dir="${build.src}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;delete dir="${build.dest}/org"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;delete dir="${build.dest}/com"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;delete&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="${build.dest}" includes="**/*.class"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/delete&gt;<br>&nbsp; &lt;/target&gt;<br>&lt;/project&gt;<br>&lt;!-- End of file --&gt;<br><br>缺省任务：usage 打印帮助文档，告诉有那些任务选项：可用的有build, jar, javadoc和clean.<br><br>初始化环境变量：init<br>所有任务都基于一些基本环境变量的设置初始化完成，是后续其他任务的基础，在环境初始化过程中，有2点比较可以方便设置：<br><br>1 除了使用却缺省的property设置了JAVA源路径和输出路径外，引用了一个外部的build.properties文件中的设置，<br>&lt;property file="${basedir}/build.properties" /&gt;<br>这样大部分简单配置用户只要会看懂build.properties就可以了，毕竟XML比起key value的属性文件还是要可读性差一些。用build.properties也可以方便其他用户从编译的细节中解放出来。<br><br>2 CLASSPATH设置：使用了其中的：<br>&nbsp;&nbsp;&nbsp; &lt;path id="classpath"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;pathelement path="${jsdk_jar}"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="${lib.dir}"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="**/*.jar"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/fileset&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/path&gt;<br>则相当于设置了：CLASSPATH=/path/to/resin/lib/jsdk23.jar; /path/to/project/lib/*.jar;<br><br>文件复制：prepare-src<br>创建临时SRC存放目录和输出目录。<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Prepares the source code&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="prepare-src" depends="init"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;!-- create directories --&gt;<br>&nbsp;&nbsp;&nbsp; &lt;mkdir dir="${build.src}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;mkdir dir="${build.dest}"/&gt;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &lt;!-- copy src files --&gt;<br>&nbsp;&nbsp;&nbsp; &lt;copy todir="${build.src}"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="${src.dir}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/copy&gt;<br>&nbsp; &lt;/target&gt;<br><br>编译任务：build<br>编译时的CLASSPATH环境通过一下方式找到引用一个path对象<br>&lt;classpath refid="classpath"/&gt;<br><br>打包任务：jar<br>对应用打包生成项目所写名的.jar文件<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Creates the class package&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="jar" depends="build"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;jar jarfile="${lib.dir}/${name}.jar"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; basedir="${build.dest}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; includes="**"/&gt;<br>&nbsp; &lt;/target&gt;<br><br>生成JAVADOC文档任务: javadoc<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Creates the API documentation&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="javadoc" depends="build"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;mkdir dir="${build.javadocs}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;javadoc packagenames="${packages}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sourcepath="${build.src}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; destdir="${build.javadocs}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; author="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; version="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; use="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; splitindex="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; windowtitle="${Name} API"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; doctitle="${Name}"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;classpath refid="classpath"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/javadoc&gt;<br>&nbsp; &lt;/target&gt;<br><br>清空临时编译文件：clean<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;!-- Clean targets&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp; &lt;!-- =================================================================== --&gt;<br>&nbsp; &lt;target name="clean" depends="init"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;delete dir="${build.src}"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;delete dir="${build.dest}/org"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;delete dir="${build.dest}/com"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;delete&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="${build.dest}" includes="**/*.class"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/delete&gt;<br>&nbsp; &lt;/target&gt;<br><br>TODO：<br>更多任务/扩展：（样例）<br></div>
<ul>
    <li>测试任务：JUnit测试
    <li>代码风格检查任务：CheckStyle，Jalopy等
    <li>邮件警报任务：可以把以上这些任务的输出警告发送到制定的用户列表中，这个任务可以设置每天自动运行。&nbsp;&nbsp;</li>
</ul>
</div>
<img src ="http://www.blogjava.net/xuechen0721/aggbug/128202.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2007-07-04 22:53 <a href="http://www.blogjava.net/xuechen0721/articles/128202.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>项目笔记：dao，web，模块边界以及Model分类 </title><link>http://www.blogjava.net/xuechen0721/articles/128200.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Wed, 04 Jul 2007 14:45:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/128200.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/128200.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/128200.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/128200.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/128200.html</trackback:ping><description><![CDATA[<strong>先从后端的dao说起吧：<br></strong>已有项目的开发以及appfuse的开发，都属于传统的开放方式，内部有dao，外部还有service。<br><br>这样的开发方式太学院了，每次改动其实影响面很大，要改二个类，两个接口。平时不忙的时候也就算了，项目一紧的话，大家就乱来的了。相当部分都是最外层接口实现类就直接访问了数据库，而不走规范路线。<br><br>现在对外提供一个repository的service接口（加载该领域模块的root对象，以便程序利用root对象来游走，ddd推荐的做法），然后内部有一个dao接口，继承该repository接口，提供一些内部使用的额外服务，比如一些数据库查询。接着有一个类继承框架提供的如spring的HiberanteTemplate同时实现dao接口，这样来改动的规模就比较小，一个接口和一个实现类。这个思路和SS2的思路是一样的。<br><br>框架数据访问已经提供一些公共的操作简化开发，当然还利用泛型等。此外，Ibatis3.0的设计思路，将是下一步的工作目标。<br><br>另外：通过root对象游走访问领域对象，性能可能有点问题。但这是可以接受的，因为从开发的角度看，通常都root下的对象都是和root对象一起出现操作的，不可能单一出现一个root以外的对象来操作以及显示的。<br>
<p><strong>现在就说说Web层：</strong><br>由于servcie对外都是开放domian model了，不再提供VO对象，免去无谓的copy property操作。而把VO的这部分工作交给自行开发的界面设计工具，因此在设计页面的时候其实已经知道的页面的访问的元素以及对象路径，在设计页面完成后由该设计工具生成VO对象和映射对象。这样VO对象的设计产生，以及property的copy都是工具完成，无须人工干预。<br><br>另外Web层采用SWF，除了完整的抽取出流程控制流转逻辑，还完整的封装了对于Request以及Response的数据访问操作。这样一个流程中调用了那些model以及servcie都非常清楚。将来有可能通过工具像规则引擎那样可以提供给业务人员直接使用。同时没有了传统的Control类结构的存在，极大的提高了开发效率问题。<br></p>
<p><strong>模块边界的处理包括两个部分：</strong><br>一是行为的边界集成上，比如订单管理模块对财务模块的行为要求。<br>订单管理模块自行设计它所需要的接口，由一个集成模块提供adapter类，来适配到财务模块合适的service上。这样就解决了旧系统设计现有的问题，旧系统现在都是直接调用财务模块提供的API，这样的做法实际上是把集成工作放在了订单管理模块下。<br><br>二是对象的边界集成，是这样做的。<br>订单明细对象（OrderItem）会关联一个产品对象，不过产品对象是是属于产品模块而非订单模块，对于订单模块只关心id而并不使用对象，但在规则引擎或者界面设计工具这样的集成环境却是需要产品对象的。我们采用代码生成的方式，在订单明细对象上加一个annotation，比如Integration的annotation标识，使用aspectj在编译上enhancement生成的class，使得订单明细对象在集成环境上可以拿到产品对象，这算是一个集成方面。</p>
<p><strong>最后是domain model部分：</strong><br>一类是类似保险中的保单对象这样的长生命周期对象；<br><br>另一类是transaction交易过程的对象，几乎没有生命周期的；<br><br>最后一类是request/response对象。这类对象以前没有识别，通常和VO混在一起；但是在IAA中以及电信业的模型是这类对象是独立存在，并被持久化的。<br>当然他们也是几乎没有生命周期的，request对象建立在增量更新上很有用。<br><br>reponse对象目前没有特别用途，依然采用VO处理。<br><br>request对象的建立有两个好处。<br>1. 以前直接更新订单对象，虽然记录了log但是只知道减肥前，减肥后。<br>2. request对象的第二个好处，可以解决一个业务事务跨越两个物理事务的设计问题。即一个业务请求可以分多次累进完成，比如分两天来处理，每天完成一个部分，但在完成之前，所有操作数据都不生效。在没有持久化request对象前，我们只能把操作的数据写到临时表上。<br><br>此外，由于某个领域模块的增量操作通常从一个根对象开始，所依赖的criteria可以从request中加以识别并通过框架提前加载，而service对象的方法接受传递对象而不再关心对象的加载工作；同时也可以通过框架处理基本数据复制工作，这样程序只关心关联对象的操作。这个做法和SS2是一样的，只不过采用的是AOP的处理方式。</p>
<img src ="http://www.blogjava.net/xuechen0721/aggbug/128200.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2007-07-04 22:45 <a href="http://www.blogjava.net/xuechen0721/articles/128200.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>教你彻底明白Java的IO系统 </title><link>http://www.blogjava.net/xuechen0721/articles/127971.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Tue, 03 Jul 2007 15:39:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/127971.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/127971.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/127971.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/127971.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/127971.html</trackback:ping><description><![CDATA[<div class=postText><strong>1.&nbsp;stream代表的是任何有能力产出数据的数据源，或是任何有能力接收数据的接收源。在Java的IO中，所有的stream（包括Input和Out&nbsp;stream）都包括两种类型：<br><br></strong>1.1&nbsp;以字节为导向的stream<br>以字节为导向的stream，表示以字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种类型：<br>1.&nbsp;input　stream<br>1)&nbsp;ByteArrayInputStream：把内存中的一个缓冲区作为InputStream使用<br>2)&nbsp;StringBufferInputStream：把一个String对象作为InputStream<br>3)&nbsp;FileInputStream：把一个文件作为InputStream，实现对文件的读取操作<br>4)&nbsp;PipedInputStream：实现了pipe的概念，主要在线程中使用<br>5)&nbsp;SequenceInputStream：把多个InputStream合并为一个InputStream<br><br>2.&nbsp;Out　stream<br>1)&nbsp;ByteArrayOutputStream：把信息存入内存中的一个缓冲区中<br>2)&nbsp;FileOutputStream：把信息存入文件中<br>3)&nbsp;PipedOutputStream：实现了pipe的概念，主要在线程中使用<br>4)&nbsp;SequenceOutputStream：把多个OutStream合并为一个OutStream<br><br>1.2&nbsp;以Unicode字符为导向的stream<br>以Unicode字符为导向的stream，表示以Unicode字符为单位从stream中读取或往stream中写入信息。以Unicode字符为导向的stream包括下面几种类型：<br>1.&nbsp;Input　Stream<br>1)&nbsp;CharArrayReader：与ByteArrayInputStream对应<br>2)&nbsp;StringReader：与StringBufferInputStream对应<br>3)&nbsp;FileReader：与FileInputStream对应<br>4)&nbsp;PipedReader：与PipedInputStream对应<br><br>2.&nbsp;Out　Stream<br>1)&nbsp;CharArrayWrite：与ByteArrayOutputStream对应<br>2)&nbsp;StringWrite：无与之对应的以字节为导向的stream<br>3)&nbsp;FileWrite：与FileOutputStream对应<br>4)&nbsp;PipedWrite：与PipedOutputStream对应<br>以字符为导向的stream基本上对有与之相对应的以字节为导向的stream。两个对应类实现的功能相同，字是在操作时的导向不同。如CharArrayReader：和ByteArrayInputStream的作用都是把内存中的一个缓冲区作为InputStream使用，所不同的是前者每次从内存中读取一个字节的信息，而后者每次从内存中读取一个字符。<br><br>1.3&nbsp;两种不现导向的stream之间的转换<br>InputStreamReader和OutputStreamReader：把一个以字节为导向的stream转换成一个以字符为导向的stream。<br><br><strong>2.&nbsp;stream添加属性</strong><br>2.1&nbsp;&#8220;为stream添加属性&#8221;的作用<br>运用上面介绍的Java中操作IO的API，我们就可完成我们想完成的任何操作了。但通过FilterInputStream和FilterOutStream的子类，我们可以为stream添加属性。下面以一个例子来说明这种功能的作用。<br>如果我们要往一个文件中写入数据，我们可以这样操作：<br>FileOutStream&nbsp;fs&nbsp;=&nbsp;new&nbsp;FileOutStream(&#8220;test.txt&#8221;);<br>然后就可以通过产生的fs对象调用write()函数来往test.txt文件中写入数据了。但是，如果我们想实现&#8220;先把要写入文件的数据先缓存到内存中，再把缓存中的数据写入文件中&#8221;的功能时，上面的API就没有一个能满足我们的需求了。但是通过FilterInputStream和FilterOutStream的子类，为FileOutStream添加我们所需要的功能。<br><br>2.2&nbsp;FilterInputStream的各种类型<br>2.2.1&nbsp;用于封装以字节为导向的InputStream<br>1)&nbsp;DataInputStream：从stream中读取基本类型（int、char等）数据。<br>2)&nbsp;BufferedInputStream：使用缓冲区<br>3)&nbsp;LineNumberInputStream：会记录input&nbsp;stream内的行数，然后可以调用getLineNumber()和setLineNumber(int)<br>4)&nbsp;PushbackInputStream：很少用到，一般用于编译器开发<br><br>2.2.2&nbsp;用于封装以字符为导向的InputStream<br>1)&nbsp;没有与DataInputStream对应的类。除非在要使用readLine()时改用BufferedReader，否则使用DataInputStream<br>2)&nbsp;BufferedReader：与BufferedInputStream对应<br>3)&nbsp;LineNumberReader：与LineNumberInputStream对应<br>4)&nbsp;PushBackReader：与PushbackInputStream对应<br><br>2.3&nbsp;FilterOutStream的各种类型<br>2.2.3&nbsp;用于封装以字节为导向的OutputStream<br>1)&nbsp;DataIOutStream：往stream中输出基本类型（int、char等）数据。<br>2)&nbsp;BufferedOutStream：使用缓冲区<br>3)&nbsp;PrintStream：产生格式化输出<br><br>2.2.4&nbsp;用于封装以字符为导向的OutputStream<br>1)&nbsp;BufferedWrite：与对应<br>2)&nbsp;PrintWrite：与对应<br><br><strong>3.&nbsp;RandomAccessFile</strong><br>1)&nbsp;可通过RandomAccessFile对象完成对文件的读写操作<br>2)&nbsp;在产生一个对象时，可指明要打开的文件的性质：r，只读；w，只写；rw可读写<br>3)&nbsp;可以直接跳到文件中指定的位置<br><br><strong>4.&nbsp;I/O应用的一个例子</strong><br><br><br>&nbsp;程序代码<br>
<div class=UBBPanel>
<div class=UBBTitle><img style="MARGIN: 0px 2px -3px 0px" alt=程序代码 src="http://blog.hunan2s.com/images/code.gif"> 程序代码</div>
<div class=UBBContent>import&nbsp;java.io.*;<br>public&nbsp;class&nbsp;TestIO{<br>public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)<br>throws&nbsp;IOException{<br>//1.以行为单位从一个文件读取数据<br>BufferedReader&nbsp;in&nbsp;=&nbsp;<br>new&nbsp;BufferedReader(<br>new&nbsp;FileReader("F:\\nepalon\\TestIO.java"));<br>String&nbsp;s,&nbsp;s2&nbsp;=&nbsp;new&nbsp;String();<br>while((s&nbsp;=&nbsp;in.readLine())&nbsp;!=&nbsp;null)<br>s2&nbsp;+=&nbsp;s&nbsp;+&nbsp;"\n";<br>in.close();<br><br>//1b.&nbsp;接收键盘的输入<br>BufferedReader&nbsp;stdin&nbsp;=&nbsp;<br>new&nbsp;BufferedReader(<br>new&nbsp;InputStreamReader(System.in));<br>System.out.println("Enter&nbsp;a&nbsp;line:");<br>System.out.println(stdin.readLine());<br><br>//2.&nbsp;从一个String对象中读取数据<br>StringReader&nbsp;in2&nbsp;=&nbsp;new&nbsp;StringReader(s2);<br>int&nbsp;c;<br>while((c&nbsp;=&nbsp;in2.read())&nbsp;!=&nbsp;-1)<br>System.out.println((char)c);<br>in2.close();<br><br>//3.&nbsp;从内存取出格式化输入<br>try{<br>DataInputStream&nbsp;in3&nbsp;=&nbsp;<br>new&nbsp;DataInputStream(<br>new&nbsp;ByteArrayInputStream(s2.getBytes()));<br>while(true)<br>System.out.println((char)in3.readByte());&nbsp;<br>}<br>catch(EOFException&nbsp;e){<br>System.out.println("End&nbsp;of&nbsp;stream");<br>}<br><br>//4.&nbsp;输出到文件<br>try{<br>BufferedReader&nbsp;in4&nbsp;=<br>new&nbsp;BufferedReader(<br>new&nbsp;StringReader(s2));<br>PrintWriter&nbsp;out1&nbsp;=<br>new&nbsp;PrintWriter(<br>new&nbsp;BufferedWriter(<br>new&nbsp;FileWriter("F:\\nepalon\\&nbsp;TestIO.out")));<br>int&nbsp;lineCount&nbsp;=&nbsp;1;<br>while((s&nbsp;=&nbsp;in4.readLine())&nbsp;!=&nbsp;null)<br>out1.println(lineCount++&nbsp;+&nbsp;"："&nbsp;+&nbsp;s);<br>out1.close();<br>in4.close();<br>}<br>catch(EOFException&nbsp;ex){<br>System.out.println("End&nbsp;of&nbsp;stream");<br>}<br><br>//5.&nbsp;数据的存储和恢复<br>try{<br>DataOutputStream&nbsp;out2&nbsp;=&nbsp;<br>new&nbsp;DataOutputStream(<br>new&nbsp;BufferedOutputStream(<br>new&nbsp;FileOutputStream("F:\\nepalon\\&nbsp;Data.txt")));<br>out2.writeDouble(3.1415926);<br>out2.writeChars("\nThas&nbsp;was&nbsp;pi:writeChars\n");<br>out2.writeBytes("Thas&nbsp;was&nbsp;pi:writeByte\n");<br>out2.close();<br>DataInputStream&nbsp;in5&nbsp;=<br>new&nbsp;DataInputStream(<br>new&nbsp;BufferedInputStream(<br>new&nbsp;FileInputStream("F:\\nepalon\\&nbsp;Data.txt")));<br>BufferedReader&nbsp;in5br&nbsp;=<br>new&nbsp;BufferedReader(<br>new&nbsp;InputStreamReader(in5));<br>System.out.println(in5.readDouble());<br>System.out.println(in5br.readLine());<br>System.out.println(in5br.readLine());<br>}<br>catch(EOFException&nbsp;e){<br>System.out.println("End&nbsp;of&nbsp;stream");<br>}<br><br>//6.&nbsp;通过RandomAccessFile操作文件<br>RandomAccessFile&nbsp;rf&nbsp;=<br>new&nbsp;RandomAccessFile("F:\\nepalon\\&nbsp;rtest.dat",&nbsp;"rw");<br>for(int&nbsp;i=0;&nbsp;i&lt;10;&nbsp;i++)<br>rf.writeDouble(i*1.414);<br>rf.close();<br><br>rf&nbsp;=&nbsp;new&nbsp;RandomAccessFile("F:\\nepalon\\&nbsp;rtest.dat",&nbsp;"r");<br>for(int&nbsp;i=0;&nbsp;i&lt;10;&nbsp;i++)<br>System.out.println("Value&nbsp;"&nbsp;+&nbsp;i&nbsp;+&nbsp;"："&nbsp;+&nbsp;rf.readDouble());<br>rf.close();<br><br>rf&nbsp;=&nbsp;new&nbsp;RandomAccessFile("F:\\nepalon\\&nbsp;rtest.dat",&nbsp;"rw");<br>rf.seek(5*8);<br>rf.writeDouble(47.0001);<br>rf.close();<br><br>rf&nbsp;=&nbsp;new&nbsp;RandomAccessFile("F:\\nepalon\\&nbsp;rtest.dat",&nbsp;"r");<br>for(int&nbsp;i=0;&nbsp;i&lt;10;&nbsp;i++)<br>System.out.println("Value&nbsp;"&nbsp;+&nbsp;i&nbsp;+&nbsp;"："&nbsp;+&nbsp;rf.readDouble());<br>rf.close();<br>}<br>}<br></div>
</div>
<br>关于代码的解释（以区为单位）：<br>1区中，当读取文件时，先把文件内容读到缓存中，当调用in.readLine()时，再从缓存中以字符的方式读取数据（以下简称&#8220;缓存字节读取方式&#8221;）。<br>1b区中，由于想以缓存字节读取方式从标准IO（键盘）中读取数据，所以要先把标准IO（System.in）转换成字符导向的stream，再进行BufferedReader封装。<br>2区中，要以字符的形式从一个String对象中读取数据，所以要产生一个StringReader类型的stream。<br>4区中，对String对象s2读取数据时，先把对象中的数据存入缓存中，再从缓冲中进行读取；对TestIO.out文件进行操作时，先把格式化后的信息输出到缓存中，再把缓存中的信息输出到文件中。<br>5区中，对Data.txt文件进行输出时，是先把基本类型的数据输出屋缓存中，再把缓存中的数据输出到文件中；对文件进行读取操作时，先把文件中的数据读取到缓存中，再从缓存中以基本类型的形式进行读取。注意in5.readDouble()这一行。因为写入第一个writeDouble()，所以为了正确显示。也要以基本类型的形式进行读取。<br>6区是通过RandomAccessFile类对文件进行操作。 <br></div>
<img src ="http://www.blogjava.net/xuechen0721/aggbug/127971.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2007-07-03 23:39 <a href="http://www.blogjava.net/xuechen0721/articles/127971.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java抽象类和接口的区别</title><link>http://www.blogjava.net/xuechen0721/articles/95128.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Sun, 21 Jan 2007 06:34:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/95128.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/95128.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/95128.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/95128.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/95128.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<a class="postTitle2" id="viewpost1_TitleUrl" href="/chunkyo/archive/2007/01/21/95093.html">Java抽象类和接口的区别(好长时间没看这种文章了)</a>
		</div>
		<div align="left">
				<span style="FONT-WEIGHT: 400">
						<span class="unnamed4">
								<font style="FONT-SIZE: 9pt">abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制，正是由于这两种机制的存在，才赋予了Java强大的面向对象能力。abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性，甚至可以相互替换，因此很多开发者在进行抽象类定义时对于abstract class和interface的选择显得比较随意。其实，两者之间还是有很大的区别的，对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析，试图给开发者提供一个在二者之间进行选择的依据。  <br /><br />理解抽象类  <br /><br />abstract class和interface在Java语言中都是用来进行抽象类（本文中的抽象类并非从abstract class翻译而来，它表示的是一个抽象体，而abstract class为Java语言中用于定义抽象类的一种方法，请读者注意区分）定义的，那么什么是抽象类，使用抽象类能为我们带来什么好处呢？  <br /><br />在面向对象的概念中，我们知道所有的对象都是通过类来描绘的，但是反过来却不是这样。并不是所有的类都是用来描绘对象的，如果一个类中没有包含足够的信息来描绘一个具体的对象，这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念，是对一系列看上去不同，但是本质上相同的具体概念的抽象。比如：如果我们进行一个图形编辑软件的开发，就会发现问题领域存在着圆、三角形这样一些具体概念，它们是不同的，但是它们又都属于形状这样一个概念，形状这个概念在问题领域是不存在的，它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念，所以用以表征抽象概念的抽象类是不能够实例化的。  <br /><br />在面向对象领域，抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述，但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类，而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体，因此它可以是不允许修改的；同时，通过从这个抽象体派生，也可扩展此模块的行为功能。熟悉OCP的读者一定知道，为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle)，抽象类是其中的关键所在。  <br /><br /><br />从语法定义层面看abstract class和interface  <br /><br />在语法层面，Java语言对于abstract class和interface给出了不同的定义方式，下面以定义一个名为Demo的抽象类为例来说明这种不同。  <br /><br />使用abstract class的方式定义Demo抽象类的方式如下：  <br /><br />abstract class Demo ｛  <br /> abstract void method1();  <br /> abstract void method2();  <br /> …  <br />｝  <br /><br />使用interface的方式定义Demo抽象类的方式如下：  <br /><br />interface Demo {  <br /> void method1();  <br /> void method2();  <br /> …  <br />}  <br /><br />在abstract class方式中，Demo可以有自己的数据成员，也可以有非abstarct的成员方法，而在interface方式的实现中，Demo只能够有静态的不能被修改的数据成员（也就是必须是static final的，不过在interface中一般不定义数据成员），所有的成员方法都是abstract的。从某种意义上说，interface是一种特殊形式的abstract class。  <br /><br />      从编程的角度来看，abstract class和interface都可以用来实现"design by contract"的思想。但是在具体的使用上面还是有一些区别的。  <br /><br />首先，abstract class在Java语言中表示的是一种继承关系，一个类只能使用一次继承关系。但是，一个类却可以实现多个interface。也许，这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。  <br /><br />其次，在abstract class的定义中，我们可以赋予方法的默认行为。但是在interface的定义中，方法却不能拥有默认行为，为了绕过这个限制，必须使用委托，但是这会 增加一些复杂性，有时会造成很大的麻烦。  <br /><br />在抽象类中不能定义默认行为还存在另一个比较严重的问题，那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面（一般通过abstract class或者interface来表示）以适应新的情况（比如，添加新的方法或者给已用的方法中添加新的参数）时，就会非常的麻烦，可能要花费很多的时间（对于派生类很多的情况，尤为如此）。但是如果界面是通过abstract class来实现的，那么可能就只需要修改定义在abstract class中的默认行为就可以了。  <br /><br />同样，如果不能在抽象类中定义默认行为，就会导致同样的方法实现出现在该抽象类的每一个派生类中，违反了"one rule，one place"原则，造成代码重复，同样不利于以后的维护。因此，在abstract class和interface间进行选择时要非常的小心。  <br /><br /><br />从设计理念层面看abstract class和interface  <br /><br />上面主要从语法定义和编程的角度论述了abstract class和interface的区别，这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面：abstract class和interface所反映出的设计理念，来分析一下二者的区别。作者认为，从这个层面进行分析才能理解二者概念的本质所在。  <br /><br />前面已经提到过，abstarct class在Java语言中体现了一种继承关系，要想使得继承关系合理，父类和派生类之间必须存在"is a"关系，即父类和派生类在概念本质上应该是相同的（参考文献〔3〕中有关于"is a"关系的大篇幅深入的论述，有兴趣的读者可以参考）。对于interface 来说则不然，并不要求interface的实现者和interface定义在概念本质上是一致的，仅仅是实现了interface定义的契约而已。为了使论述便于理解，下面将通过一个简单的实例进行说明。  <br /><br />考虑这样一个例子，假设在我们的问题领域中有一个关于Door的抽象概念，该Door具有执行两个动作open和close，此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型，定义方式分别如下所示：  <br /><br />使用abstract class方式定义Door：  <br /><br />abstract class Door {  <br /> abstract void open();  <br /> abstract void close()；  <br />}  <br /><br />   <br />使用interface方式定义Door：  <br /><br /><br />interface Door {  <br /> void open();  <br /> void close();  <br />}  <br /><br />   <br />其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。  <br /><br />如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢（在本例中，主要是为了展示abstract class和interface反映在设计理念上的区别，其他方面无关的问题都做了简化或者忽略）？下面将罗列出可能的解决方案，并从设计理念层面对这些不同的方案进行分析。  <br /><br />解决方案一：  <br /><br />简单的在Door的定义中增加一个alarm方法，如下：  <br /><br />abstract class Door {  <br /> abstract void open();  <br /> abstract void close()；  <br /> abstract void alarm();  <br />}  <br /><br />   <br />或者  <br /><br />interface Door {  <br /> void open();  <br /> void close();  <br /> void alarm();  <br />}  <br /><br />   <br />那么具有报警功能的AlarmDoor的定义方式如下：  <br /><br />class AlarmDoor extends Door {  <br /> void open() { … }  <br /> void close() { … }  <br /> void alarm() { … }  <br />}  <br /><br />   <br />或者  <br /><br />class AlarmDoor implements Door ｛  <br /> void open() { … }  <br /> void close() { … }  <br /> void alarm() { … }  <br />｝  <br /><br />这种方法违反了面向对象设计中的一个核心原则ISP（Interface Segregation Priciple），在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变（比如：修改alarm方法的参数）而改变，反之依然。  <br /><br />解决方案二：  <br /><br />既然open、close和alarm属于两个不同的概念，根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有：这两个概念都使用abstract class方式定义；两个概念都使用interface方式定义；一个概念使用abstract class方式定义，另一个概念使用interface方式定义。  <br /><br />显然，由于Java语言不支持多重继承，所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的，但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。  <br /><br />如果两个概念都使用interface方式来定义，那么就反映出两个问题：1、我们可能没有理解清楚问题领域，AlarmDoor在概念本质上到底是Door还是报警器？2、如果我们对于问题领域的理解没有问题，比如：我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的，那么我们在实现时就没有能够正确的揭示我们的设计意图，因为在这两个概念的定义上（均使用interface方式定义）反映不出上述含义。  <br /><br />如果我们对于问题领域的理解是：AlarmDoor在概念本质上是Door，同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢？前面已经说过，abstract class在Java语言中表示一种继承关系，而继承关系在本质上是"is a"关系。所以对于Door这个概念，我们应该使用abstarct class方式来定义。另外，AlarmDoor又具有报警功能，说明它又能够完成报警概念中定义的行为，所以报警概念可以通过interface方式定义。如下所示：  <br /><br />abstract class Door {  <br /> abstract void open();  <br /> abstract void close()；  <br />}  <br />interface Alarm {  <br /> void alarm();  <br />}  <br />class AlarmDoor extends Door implements Alarm {  <br /> void open() { … }  <br /> void close() { … }  <br />    void alarm() { … }  <br />}  <br /><br />   <br />这种实现方式基本上能够明确的反映出我们对于问题领域的理解，正确的揭示我们的设计意图。其实abstract class表示的是"is a"关系，interface表示的是"like a"关系，大家在选择时可以作为一个依据，当然这是建立在对问题领域的理解上的，比如：如果我们认为AlarmDoor在概念本质上是报警器，同时又具有Door的功能，那么上述的定义方式就要反过来了。</font>
						</span>
				</span>
		</div>
<img src ="http://www.blogjava.net/xuechen0721/aggbug/95128.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2007-01-21 14:34 <a href="http://www.blogjava.net/xuechen0721/articles/95128.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现DAO 设计模式</title><link>http://www.blogjava.net/xuechen0721/articles/92158.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Sat, 06 Jan 2007 03:50:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/92158.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/92158.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/92158.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/92158.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/92158.html</trackback:ping><description><![CDATA[
		<div class="tit">实现DAO 设计模式</div>
		<div class="date"> </div>
		<table style="TABLE-LAYOUT: fixed">
				<tbody>
						<tr>
								<td>
										<div class="cnt">在JAVA编程的时候, 有时候看起来非常直接的实现却非要用设计模式转若干个弯去实现他, 这似乎显的很多余,但是采用一些成熟的设计模式,会使程序更加的健壮,松耦合以及好维护和扩展. <br /><br />实现DAO 设计模式<br /><br /><br />为DAO实现工厂类的策略<br /><br />1 采用工厂方法设计模式<br />如果一个DAO 工厂只为一个数据库的实现,(比如ORACLE)而创建很多的DAO的时候,实现该策略时,我们考虑采用工厂方法设计模式. 假设该工厂类创建了CustomerDAO, AccountDAO, OrderDAO 等一些对象。<br /><br />2 使用抽象工厂设计模式:<br /><br />如果考虑为三种不同类型的数据库来实现这个策略,我们可以考虑采用抽象工厂设计模式. 假设. 这个工厂创建了CustomerDAO, AccountDAO, OrderDAO的一系列的DAO, 该策略运用了在抽象工厂中产生的工厂类中的工厂方法的实现.<br /><br />代码说明：<br /><br />以下代码举例说明了DAO设计模式的具体实现:<br />我们以使用抽象工厂的设计模式来对付多种类型数据库为例,在以下的例子中只具体列出CLOUDSCAPE 数据库类型的DAO设计模式的具体实现,其他类型数据库DAO设计模式的实现大同小异.<br /><br />1 // Abstract class DAO Factory<br />public abstract class DAOFactory {<br /><br />// List of DAO types supported by the factory<br />public static final int CLOUDSCAPE = 1;<br />public static final int ORACLE = 2;<br />public static final int SYBASE = 3;<br />...<br /><br />// There will be a method for each DAO that can be <br />// created. The concrete factories will have to <br />// implement these methods.<br />// 所有实现该抽象工厂的工厂类中必须有的方法,用这些方法来创建具体的DAO类.<br />public abstract CustomerDAO getCustomerDAO();<br />public abstract AccountDAO getAccountDAO();<br />public abstract OrderDAO getOrderDAO();<br /><br />//该抽象类的静态方法,用他来创建其他具体的DAO工厂类<br />public static DAOFactory getDAOFactory(<br />int whichFactory) {<br /><br />switch (whichFactory) {<br />case CLOUDSCAPE: <br />return new CloudscapeDAOFactory();<br />case ORACLE : <br />return new OracleDAOFactory(); <br />case SYBASE : <br />return new SybaseDAOFactory();<br />...<br />default : <br />return null;<br />}<br />}<br />}<br /><br /><br />2 以下是Cloudscape DAO FACTORY 类的实现,在他里面实现了该类型数据库的连接,以及实现了他所继承的抽象工厂类中所必须实现的那些方法,在这些方法中创建具体的DAO对象.<br /><br />// Cloudscape concrete DAO Factory implementation<br />import java.sql.*;<br /><br />public class CloudscapeDAOFactory extends DAOFactory {<br />public static final String DRIVER=<br />"COM.cloudscape.core.RmiJdbcDriver";<br />public static final String DBURL=<br />"jdbc:cloudscape:rmi://localhost:1099/CoreJ2EEDB";<br /><br />// method to create Cloudscape connections<br />//建立Cloudscape 连接<br />public static Connection createConnection() {<br />// Use DRIVER and DBURL to create a connection<br />// Recommend connection pool implementation/usage<br />}<br />//创建 CustomerDAO 对象 当然返回的是一个该类实现的接口,他的好处就是实现了实现细节的隐蔽.<br />public CustomerDAO getCustomerDAO() {<br />// CloudscapeCustomerDAO implements CustomerDAO<br />return new CloudscapeCustomerDAO();<br />}<br />//创建 AccountDAO 对象 当然返回的是一个该类实现的接口,他的好处就是实现了实现细节的隐蔽.<br />public AccountDAO getAccountDAO() {<br />// CloudscapeAccountDAO implements AccountDAO<br />return new CloudscapeAccountDAO();<br />}<br />//创建 OrderDAO 对象 当然返回的是一个该类实现的接口,他的好处就是实现了实现细节的隐蔽.<br /><br />public OrderDAO getOrderDAO() {<br />// CloudscapeOrderDAO implements OrderDAO<br />return new CloudscapeOrderDAO();<br />}<br />...<br />}<br /><br />3 以下代码就是具体DAO类实现的接口也就是CloudscapeCustomerDAO()实现的接口: CustomerDAO .在该接口中定义了所有的业务方法.<br /><br /><br />// Interface that all CustomerDAOs must support<br />public interface CustomerDAO {<br />public int insertCustomer(...);<br />public boolean deleteCustomer(...);<br />public Customer findCustomer(...);<br />public boolean updateCustomer(...);<br />public RowSet selectCustomersRS(...);<br />public Collection selectCustomersTO(...);<br />...<br />}<br /><br />4 以下CloudscapeCustomerDAO类实现的具体业务细节和数据操作细节, 他是要向客户数据端隐蔽的.<br /><br />import java.sql.*;<br />public class CloudscapeCustomerDAO implements <br />CustomerDAO {<br />public CloudscapeCustomerDAO() {<br />// initialization <br />}<br />// The following methods can use<br />// CloudscapeDAOFactory.createConnection() <br />// to get a connection as required<br />public int insertCustomer(...) {<br />// Implement insert customer here.<br />// Return newly created customer number<br />// or a -1 on error<br />}<br />public boolean deleteCustomer(...) {<br />// Implement delete customer here<br />// Return true on success, false on failure<br />}<br />public Customer findCustomer(...) {<br />// Implement find a customer here using supplied<br />// argument values as search criteria<br />// Return a Transfer Object if found,<br />// return null on error or if not found<br />}<br />public boolean updateCustomer(...) {<br />// implement update record here using data<br />// from the customerData Transfer Object<br />// Return true on success, false on failure or<br />// error<br />}<br />public RowSet selectCustomersRS(...) {<br />// implement search customers here using the<br />// supplied criteria.<br />// Return a RowSet. <br />}<br />public Collection selectCustomersTO(...) {<br />// implement search customers here using the<br />// supplied criteria.<br />// Alternatively, implement to return a Collection <br />// of Transfer Objects.<br />}<br />...<br />}<br /><br />5 下面的代码是数据客户端向DAO中传输数据的, 他其实就是一个JAVABEAN;<br /><br />public class Customer implements java.io.Serializable {<br />// member variables<br />int CustomerNumber;<br />String name;<br />String streetAddress;<br />String city;<br />...<br /><br />// getter and setter methods...<br />...<br />}<br /><br /><br />6 最后就是客户数据端对这个设计的应用:<br />...<br />// create the required DAO Factory<br />DAOFactory cloudscapeFactory = <br />DAOFactory.getDAOFactory(DAOFactory.DAOCLOUDSCAPE);<br />// Create a DAO<br />CustomerDAO custDAO = <br />cloudscapeFactory.getCustomerDAO();<br />// create a new customer<br />int newCustNo = custDAO.insertCustomer(...);<br />// Find a customer object. Get the Transfer Object.<br />Customer cust = custDAO.findCustomer(...);<br />// modify the values in the Transfer Object.<br />cust.setAddress(...);<br />cust.setEmail(...);<br />// update the customer object using the DAO<br />custDAO.updateCustomer(cust);<br />// delete a customer object<br />custDAO.deleteCustomer(...);<br />// select all customers in the same city <br />Customer criteria=new Customer();<br />criteria.setCity("New York");<br />Collection customersList = <br />custDAO.selectCustomersTO(criteria);<br />// returns customersList - collection of Customer<br />// Transfer Objects. iterate through this collection to<br />// get values.<br /><br />言而简之，以下6步完成该模式的实现：<br /><br />1 创建一个抽象工厂类,他包含两个重要的部分: 第一部分是 一些抽象方法,这些方法是所有实现该抽象工厂的具体工厂类所必须实现的. 第二部分 就是一个静态方法,该方法来创建一个具体类型数据源的工厂对象,比如文中的CloudscapeDAOFactory().<br /><br />2 然后,分别创建各个类型数据源的工厂类,(本文以CloudscapeDAOFactory为例).在这个工厂类中里面也有两个重要组成部分: 第一部分就是实现在他继承的那个抽象工厂类中的左右抽象方法,在该方法中创建具体的DAO对象(这些对象的类在第4不具体定义实现),本文中三个方法分别创建了3个具体的DAO对象,当然为了实现细节的隐蔽,这些方法返回的是这些具体DAO类门实现的接口(这些接口在第3步实现).<br /><br />3 定义具体DAO类的接口,并在接口中定义所有的业务方法,和数据操作方法.<br /><br />4 定义具体的DAO类,在这个类中才是实际的业务方法,和数据的操作的实现.<br /><br />5 定义数据传输对象,他是用来在客户端和DAO之间传递数据的,他其实就是一个JAVABEAN.<br /><br />6 完成以上5步之后我们就可以在数据客户端使用以上由DAO设计模式定义好的各个类了(见最后一个代码例子块).<br /><br />以上6步大家在编程的时需具体体会,一般来说,数据库中的一个表就可以对应一个数据传递类也就是在第4步中定义的那个类,类中的属性就是表中的字段,然后加上相应的GET,SET 方法. 然后再按模式和以上步骤来定义具体的类.<br /><br />本文译自：http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html<br /><br />jegg <br /><br />JAVA-J2EE-DESIGH PATTERN<br /><br />共享就是力量！</div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/xuechen0721/aggbug/92158.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2007-01-06 11:50 <a href="http://www.blogjava.net/xuechen0721/articles/92158.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Java文件路径问题（转） </title><link>http://www.blogjava.net/xuechen0721/articles/90805.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Fri, 29 Dec 2006 12:39:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/90805.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/90805.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/90805.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/90805.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/90805.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<a id="viewpost1_TitleUrl" href="/hengheng123456789/archive/2006/12/29/90762.html">关于Java文件路径问题（转）</a>
		</div>
		<div class="postText">转自：<a href="http://blog.csdn.net/soleghost/archive/2006/04/27/679374.aspx">http://blog.csdn.net/soleghost/archive/2006/04/27/679374.aspx</a><br /><br /><div class="postText"><p><font size="3">1.如何获得当前文件路径</font></p><p><font size="3">常用：</font></p><p><font size="3">字符串类型：System.getProperty("user.dir");</font></p><p><font size="3">综合：</font></p><p><font size="3">package com.zcjl.test.base;<br />import java.io.File;<br />public class Test {<br />    public static void main(String[] args) throws Exception {<br />        System.out.println(<br /></font><font size="3"><font color="#ff0000">            Thread.currentThread().getContextClassLoader().getResource(""));<br />        System.out.println(Test.class.getClassLoader().getResource(""));<br />        System.out.println(ClassLoader.getSystemResource(""));<br /></font></font></p><p><font size="3"><font color="#ff0000">        System.out.println(Test.class.getResource(""));<br />        System.out.println(Test.class.getResource("/"));</font></font></p><p><font size="3"><font color="#ff0000"><br />        System.out.println(new File("").getAbsolutePath());<br />        System.out.println(System.getProperty("user.dir"));</font><br />    }<br />}</font></p><p><font size="3">file:/E:/workSpace/javaTest/target/classes/<br />file:/E:/workSpace/javaTest/target/classes/<br />file:/E:/workSpace/javaTest/target/classes/<br />file:/E:/workSpace/javaTest/target/classes/javaAPI/<br />file:/E:/workSpace/javaTest/target/classes/<br />E:\workSpace\javaTest<br />E:\workSpace\javaTest<br /></font></p><p><font size="3"></font> </p><p><font size="3">2.Web服务中</font></p><p><font size="3">(1).Weblogic</font></p><p><font size="3">WebApplication的系统文件根目录是你的weblogic安装所在根目录。<br />例如：如果你的weblogic安装在c:\bea\weblogic700.....<br />那么，你的文件根路径就是c:\.<br />所以，<font color="#ff0000">有两种方式能够让你访问你的服务器端的文件：<br />a.使用绝对路径：<br />比如将你的参数文件放在c:\yourconfig\yourconf.properties，<br />直接使用 new FileInputStream("/yourconfig/yourconf.properties");<br />b.使用相对路径：<br />相对路径的根目录就是你的webapplication的根路径，即WEB-INF的上一级目录，将你的参数文件放在yourwebapp\yourconfig\yourconf.properties，<br />这样使用：<br />new FileInputStream("yourconfig/yourconf.properties");<br /></font>这两种方式均可，自己选择。</font></p><p><font size="3">(2).Tomcat</font></p><p><font size="3">在类中输出System.getProperty("user.dir");显示的是%Tomcat_Home%/bin</font></p><p><font size="3">(3).Resin</font></p><p><font size="3">不是你的JSP放的相对路径,是JSP引擎执行这个JSP编译成SERVLET<br />的路径为根.比如用新建文件法测试File f = new File("a.htm");<br />这个a.htm在resin的安装目录下 </font></p><p><font color="#ff0000" size="3">(4).如何读相对路径哪？</font></p><p><font color="#ff0000" size="3">在Java文件中getResource或getResourceAsStream均可</font></p><p><font color="#ff0000" size="3">例：getClass().getResourceAsStream(filePath);//filePath可以是"/filename",这里的/代表web发布根路径下WEB-INF/classes</font></p><p><font color="#ff0000" size="3">也可以</font><font color="#ff0000" size="3">getClass().getClassLoader().getResourceAsStream(filePath)//filePath不是带“/”的</font></p><p><font size="3">(5).获得文件真实路径</font></p><p><font size="3">string  file_real_path=request.getRealPath("mypath/filename");  </font></p><p><font size="3">通常使用<font color="#ff0000">request.getRealPath("/");</font>  </font></p><p><font size="3"> 4.遗留问题</font></p><p><font size="3">目前new FileInputStream()只会使用绝对路径，相对</font></p><p><br />  InputStream in1 = new FileInputStream("abc1.properties"); // 相对路径<br />  InputStream in2 = new FileInputStream("/abc2.properties"); // 绝对路径，E盘下<br />  InputStream in3 = new FileInputStream("e://abc3.properties"); //相对路径</p><p><font size="3">5.按Java文件类型分类读取配置文件</font></p><div class="storycontent"><p><font size="3">配 置文件是应用系统中不可缺少的，可以增加程序的灵活性。java.util.Properties是从jdk1.2就有的类，一直到现在都支持load ()方法，jdk1.4以后save(output,string) -&gt;store(output,string)。如果只是单纯的读，根本不存在烦恼的问题。<font color="#ff0000">web层可以通过 Thread.currentThread().getContextClassLoader().<br />getResourceAsStream("xx.properties") 获取；</font></font></p><p><font size="3"><font color="#ff0000">Application可以通过new FileInputStream("xx.properties");直接在classes一级获取。</font>关键是有时我们需要通过web修改配置文件，我们不 能将路径写死了。经过测试觉得有以下心得：</font></p><p><font size="3">1.servlet中读写。如果运用Struts 或者Servlet可以直接在初始化参数中配置，调用时根据servlet的getRealPath("/")获取真实路径，再根据String file = this.servlet.getInitParameter("abc");获取相对的WEB-INF的相对路径。<br />例：<br />InputStream input = Thread.currentThread().getContextClassLoader().<br />getResourceAsStream("abc.properties");<br />Properties prop = new Properties();<br />prop.load(input);<br />input.close();<br />OutputStream out = new FileOutputStream(path);<br />prop.setProperty("abc", “test");<br />prop.store(out, “–test–");<br />out.close();</font></p><p><font size="3">2.直接在jsp中操作，通过jsp内置对象获取可操作的绝对地址。<br />例：<br />// jsp页面<br /><font color="#ff0000">String path = pageContext.getServletContext().getRealPath("/");<br />String realPath = path+"/WEB-INF/classes/abc.properties";</font></font></p><p><font size="3">//java 程序<br /><font color="#ff0000">InputStream in = getClass().getClassLoader().getResourceAsStream("abc.properties"); // abc.properties放在webroot/WEB-INF/classes/目录下<br /></font>prop.load(in);<br />in.close();</font></p><p><font size="3">OutputStream out = new FileOutputStream(path); // path为通过页面传入的路径<br />prop.setProperty("abc", “abcccccc");<br />prop.store(out, “–test–");<br />out.close();</font></p><p><font size="3">3.只通过Java程序操作资源文件<br />InputStream in = new FileInputStream("abc.properties"); // 相对路径,项目下的路径</font></p><p><font size="3">OutputStream out = new FileOutputStream("abc.properties");</font><br /></p></div></div></div>
<img src ="http://www.blogjava.net/xuechen0721/aggbug/90805.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2006-12-29 20:39 <a href="http://www.blogjava.net/xuechen0721/articles/90805.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TOMCAT配置技巧10 </title><link>http://www.blogjava.net/xuechen0721/articles/90584.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Thu, 28 Dec 2006 12:18:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/90584.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/90584.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/90584.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/90584.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/90584.html</trackback:ping><description><![CDATA[
		<h2>
				<a id="viewpost1_TitleUrl" href="/junky/archive/2006/12/27/90218.html">
						<font color="#770000">TOMCAT配置技巧10</font>
				</a>
		</h2>
		<p>
				<strong>1． 配置系统管理（Admin Web Application）</strong>
		</p>
		<p>[被屏蔽广告]　　大多数商业化的J2EE服务器都提供一个功能强大的管理界面，且大都采用易于理解的Web应用界面。Tomcat按照自己的方式，同样提供一个成熟的管理工具，并且丝毫不逊于那些商业化的竞争对手。Tomcat的Admin Web Application最初在4.1版本时出现，当时的功能包括管理context、data source、user和group等。当然也可以管理像初始化参数，user、group、role的多种数据库管理等。在后续的版本中，这些功能将得到很大的扩展，但现有的功能已经非常实用了。</p>
		<p>　　Admin Web Application被定义在自动部署文件：CATALINA_BASE/webapps/admin.xml 。<br />（译者注：CATALINA_BASE即tomcat安装目录下的server目录）</p>
		<p>　　你必须编辑这个文件，以确定Context中的docBase参数是绝对路径。也就是说，CATALINA_BASE/webapps/admin.xml 的路径是绝对路径。作为另外一种选择，你也可以删除这个自动部署文件，而在server.xml文件中建立一个Admin Web Application的context，效果是一样的。你不能管理Admin Web Application这个应用，换而言之，除了删除CATALINA_BASE/webapps/admin.xml ，你可能什么都做不了。</p>
		<p>　　如果你使用UserDatabaseRealm（默认），你将需要添加一个user以及一个role到CATALINA_BASE/conf/tomcat-users.xml 文件中。你编辑这个文件，添加一个名叫“admin”的role 到该文件中，如下：</p>
		<p>&lt;role name="admin"/&gt;</p>
		<p>　　你同样需要有一个用户，并且这个用户的角色是“admin”。象存在的用户那样，添加一个用户（改变密码使其更加安全）：</p>
		<p>&lt;user name="admin" password="deep_dark_secret" roles="admin"/&gt;</p>
		<p>　　当你完成这些步骤后，请重新启动Tomcat，访问http://localhost:8080/admin，你将看到一个登录界面。Admin Web Application采用基于容器管理的安全机制，并采用了Jakarta Struts框架。一旦你作为“admin”角色的用户登录管理界面，你将能够使用这个管理界面配置Tomcat。</p>
		<p>
				<strong>2．配置应用管理（Manager Web Application）</strong>
		</p>
		<p>　　Manager Web Application让你通过一个比Admin Web Application更为简单的用户界面，执行一些简单的Web应用任务。</p>
		<p>　　Manager Web Application被被定义在一个自动部署文件中：</p>
		<p>　　CATALINA_BASE/webapps/manager.xml 。</p>
		<p>　　你必须编辑这个文件，以确保context的docBase参数是绝对路径，也就是说CATALINA_HOME/server/webapps/manager的绝对路径。<br />　　（译者注：CATALINA_HOME即tomcat安装目录）</p>
		<p>　　如果你使用的是UserDatabaseRealm，那么你需要添加一个角色和一个用户到CATALINA_BASE/conf/tomcat-users.xml文件中。接下来，编辑这个文件，添加一个名为“manager”的角色到该文件中：</p>
		<p>　　&lt;role name=”manager”&gt;</p>
		<p>　　你同样需要有一个角色为“manager”的用户。像已经存在的用户那样，添加一个新用户（改变密码使其更加安全）：</p>
		<p>　　&lt;user name="manager" password="deep_dark_secret" roles="manager"/&gt;</p>
		<p>　　然后重新启动Tomcat，访问http://localhost/manager/list，将看到一个很朴素的文本型管理界面，或者访问http://localhost/manager/html/list，将看到一个HMTL的管理界面。不管是哪种方式都说明你的Manager Web Application现在已经启动了。</p>
		<p>　　Manager application让你可以在没有系统管理特权的基础上，安装新的Web应用，以用于测试。如果我们有一个新的web应用位于/home/user/hello下在，并且想把它安装到 /hello下，为了测试这个应用，我们可以这么做，在第一个文件框中输入“/hello”（作为访问时的path），在第二个文本框中输入“file:/home/user/hello”（作为Config URL）。</p>
		<p>　　Manager application还允许你停止、重新启动、移除以及重新部署一个web应用。停止一个应用使其无法被访问，当有用户尝试访问这个被停止的应用时，将看到一个503的错误——“503 - This application is not currently available”。</p>
		<p>　　移除一个web应用，只是指从Tomcat的运行拷贝中删除了该应用，如果你重新启动Tomcat，被删除的应用将再次出现（也就是说，移除并不是指从硬盘上删除）。<br /></p>
		<p>
				<strong>3．部署一个web应用</strong>
		</p>
		<p>　　有两个办法可以在系统中部署web服务。</p>
		<p>　　1&gt; 拷贝你的WAR文件或者你的web应用文件夹（包括该web的所有内容）到$CATALINA_BASE/webapps目录下。<br />　　2&gt; 为你的web服务建立一个只包括context内容的XML片断文件，并把该文件放到$CATALINA_BASE/webapps目录下。这个web应用本身可以存储在硬盘上的任何地方。</p>
		<p>　　如果你有一个WAR文件，你若想部署它，则只需要把该文件简单的拷贝到CATALINA_BASE/webapps目录下即可，文件必须以“.war”作为扩展名。一旦Tomcat监听到这个文件，它将（缺省的）解开该文件包作为一个子目录，并以WAR文件的文件名作为子目录的名字。接下来，Tomcat将在内存中建立一个context，就好象你在server.xml文件里建立一样。当然，其他必需的内容，将从server.xml中的DefaultContext获得。</p>
		<p>　　部署web应用的另一种方式是写一个Context XML片断文件，然后把该文件拷贝到CATALINA_BASE/webapps目录下。一个Context片断并非一个完整的XML文件，而只是一个context元素，以及对该应用的相应描述。这种片断文件就像是从server.xml中切取出来的context元素一样，所以这种片断被命名为“context片断”。</p>
		<p>　　举个例子，如果我们想部署一个名叫MyWebApp.war的应用，该应用使用realm作为访问控制方式，我们可以使用下面这个片断：</p>
		<p>&lt;!--  <br /> Context fragment for deploying MyWebApp.war  <br />--&gt;<br />&lt;Context path="/demo" docBase="webapps/MyWebApp.war" <br />        debug="0" privileged="true"&gt;<br /> &lt;Realm className="org.apache.catalina.realm.UserDatabaseRealm"                <br />        resourceName="UserDatabase"/&gt; <br />&lt;/Context&gt;</p>
		<p>　　把该片断命名为“MyWebApp.xml”，然后拷贝到CATALINA_BASE/webapps目录下。</p>
		<p>　　这种context片断提供了一种便利的方法来部署web应用，你不需要编辑server.xml，除非你想改变缺省的部署特性，安装一个新的web应用时不需要重启动Tomcat。</p>
		<p>
				<br />
				<strong>4．配置虚拟主机（Virtual Hosts）</strong>
		</p>
		<p>
				<strong>　　</strong>关于server.xml中“Host”这个元素，只有在你设置虚拟主机的才需要修改。虚拟主机是一种在一个web服务器上服务多个域名的机制，对每个域名而言，都好象独享了整个主机。实际上，大多数的小型商务网站都是采用虚拟主机实现的，这主要是因为虚拟主机能直接连接到Internet并提供相应的带宽，以保障合理的访问响应速度，另外虚拟主机还能提供一个稳定的固定IP。</p>
		<p>　　基于名字的虚拟主机可以被建立在任何web服务器上，建立的方法就是通过在域名服务器（DNS）上建立IP地址的别名，并且告诉web服务器把去往不同域名的请求分发到相应的网页目录。因为这篇文章主要是讲Tomcat，我们不准备介绍在各种操作系统上设置DNS的方法，如果你在这方面需要帮助，请参考《DNS and Bind》一书，作者是Paul Albitz and Cricket Liu (O'Reilly)。为了示范方便，我将使用一个静态的主机文件，因为这是测试别名最简单的方法。<br />在Tomcat中使用虚拟主机，你需要设置DNS或主机数据。为了测试，为本地IP设置一个IP别名就足够了，接下来，你需要在server.xml中添加几行内容，如下：</p>
		<p>&lt;Server port="8005" shutdown="SHUTDOWN" debug="0"&gt;<br /> &lt;Service name="Tomcat-Standalone"&gt;<br />   &lt;Connector className="org.apache.coyote.tomcat4.CoyoteConnector"<br />port="8080" minProcessors="5" maxProcessors="75"<br />enableLookups="true" redirectPort="8443"/&gt;<br />   &lt;Connector className="org.apache.coyote.tomcat4.CoyoteConnector"<br />port="8443" minProcessors="5" maxProcessors="75"<br />acceptCount="10" debug="0" scheme="https" secure="true"/&gt;<br />     &lt;Factory className="org.apache.coyote.tomcat4.CoyoteServerSocketFactory"<br />clientAuth="false" protocol="TLS" /&gt;<br />   &lt;/Connector&gt;<br />   &lt;Engine name="Standalone" defaultHost="localhost" debug="0"&gt;<br />     &lt;!-- This Host is the default Host --&gt;<br />     &lt;Host name="localhost" debug="0" appBase="webapps"<br />     unpackWARs="true" autoDeploy="true"&gt;<br />       &lt;Context path="" docBase="ROOT" debug="0"/&gt;<br />       &lt;Context path="/orders" docBase="/home/ian/orders" debug="0"<br />                      reloadable="true" crossContext="true"&gt;<br />       &lt;/Context&gt;<br />     &lt;/Host&gt;</p>
		<p>     &lt;!-- This Host is the first "Virtual Host": www.example.com --&gt;<br />     &lt;Host name="www.example.com" appBase="/home/example/webapp"&gt;<br />       &lt;Context path="" docBase="."/&gt;<br />     &lt;/Host&gt;</p>
		<p>   &lt;/Engine&gt;<br /> &lt;/Service&gt;<br />&lt;/Server&gt;</p>
		<p>　　Tomcat的server.xml文件，在初始状态下，只包括一个虚拟主机，但是它容易被扩充到支持多个虚拟主机。在前面的例子中展示的是一个简单的server.xml版本，其中粗体部分就是用于添加一个虚拟主机。每一个Host元素必须包括一个或多个context元素，所包含的context元素中必须有一个是默认的context，这个默认的context的显示路径应该为空（例如，path=””）。</p>
		<p>
				<strong>5．配置基础验证（Basic Authentication）</strong>
		</p>
		<p>
				<strong>　　</strong>容器管理验证方法控制着当用户访问受保护的web应用资源时，如何进行用户的身份鉴别。当一个web应用使用了Basic Authentication（BASIC参数在web.xml文件中auto-method元素中设置），而有用户访问受保护的web应用时，Tomcat将通过HTTP Basic Authentication方式，弹出一个对话框，要求用户输入用户名和密码。在这种验证方法中，所有密码将被以64位的编码方式在网络上传输。</p>
		<p>　　注意：使用Basic Authentication通过被认为是不安全的，因为它没有强健的加密方法，除非在客户端和服务器端都使用HTTPS或者其他密码加密码方式（比如，在一个虚拟私人网络中）。若没有额外的加密方法，网络管理员将能够截获（或滥用）用户的密码。但是，如果你是刚开始使用Tomcat，或者你想在你的web应用中测试一下基于容器的安全管理，Basic Authentication还是非常易于设置和使用的。只需要添加&lt;security-constraint&gt;和&lt;login-config&gt;两个元素到你的web应用的web.xml文件中，并且在CATALINA_BASE/conf/tomcat-users.xml 文件中添加适当的&lt;role&gt;和&lt;user&gt;即可，然后重新启动Tomcat。</p>
		<p>　　下面例子中的web.xml摘自一个俱乐部会员网站系统，该系统中只有member目录被保护起来，并使用Basic Authentication进行身份验证。请注意，这种方式将有效的代替Apache web服务器中的.htaccess文件。</p>
		<p>&lt;!--<br /> Define the Members-only area, by defining<br /> a "Security Constraint" on this Application, and<br /> mapping it to the subdirectory (URL) that we want<br /> to restrict.<br />--&gt;<br />&lt;security-constraint&gt;<br /> &lt;web-resource-collection&gt;<br />   &lt;web-resource-name&gt;<br />     Entire Application<br />   &lt;/web-resource-name&gt;<br />   &lt;url-pattern&gt;/members/*&lt;/url-pattern&gt;<br /> &lt;/web-resource-collection&gt;<br /> &lt;auth-constraint&gt;<br />     &lt;role-name&gt;member&lt;/role-name&gt;<br /> &lt;/auth-constraint&gt;<br />&lt;/security-constraint&gt;<br />&lt;!-- Define the Login Configuration for this Application --&gt;<br />&lt;login-config&gt;<br /> &lt;auth-method&gt;BASIC&lt;/auth-method&gt;<br /> &lt;realm-name&gt;My Club Members-only Area&lt;/realm-name&gt;<br />&lt;/login-config&gt;</p>
		<p>
				<strong>6．配置单点登录（Single Sign-On）</strong>
		</p>
		<p>　　一旦你设置了realm和验证的方法，你就需要进行实际的用户登录处理。一般说来，对用户而言登录系统是一件很麻烦的事情，你必须尽量减少用户登录验证的次数。作为缺省的情况，当用户第一次请求受保护的资源时，每一个web应用都会要求用户登录。如果你运行了多个web应用，并且每个应用都需要进行单独的用户验证，那这看起来就有点像你在与你的用户搏斗。用户们不知道怎样才能把多个分离的应用整合成一个单独的系统，所有他们也就不知道他们需要访问多少个不同的应用，只是很迷惑，为什么总要不停的登录。</p>
		<p>　　Tomcat 4的“single sign-on”特性允许用户在访问同一虚拟主机下所有web应用时，只需登录一次。为了使用这个功能，你只需要在Host上添加一个SingleSignOn Valve元素即可，如下所示：</p>
		<p>&lt;Valve className="org.apache.catalina.authenticator.SingleSignOn"<br />      debug="0"/&gt;</p>
		<p>　　在Tomcat初始安装后，server.xml的注释里面包括SingleSignOn Valve配置的例子，你只需要去掉注释，即可使用。那么，任何用户只要登录过一个应用，则对于同一虚拟主机下的所有应用同样有效。</p>
		<p>　　使用single sign-on valve有一些重要的限制：</p>
		<p>　　1&gt; value必须被配置和嵌套在相同的Host元素里，并且所有需要进行单点验证的web应用（必须通过context元素定义）都位于该Host下。<br />　　2&gt; 包括共享用户信息的realm必须被设置在同一级Host中或者嵌套之外。<br />　　3&gt; 不能被context中的realm覆盖。<br />　　4&gt; 使用单点登录的web应用最好使用一个Tomcat的内置的验证方式（被定义在web.xml中的&lt;auth-method&gt;中），这比自定义的验证方式强，Tomcat内置的的验证方式包括basic、digest、form和client-cert。<br />　　5&gt; 如果你使用单点登录，还希望集成一个第三方的web应用到你的网站中来，并且这个新的web应用使用它自己的验证方式，而不使用容器管理安全，那你基本上就没招了。你的用户每次登录原来所有应用时需要登录一次，并且在请求新的第三方应用时还得再登录一次。当然，如果你拥有这个第三方web应用的源码，而你又是一个程序员，你可以修改它，但那恐怕也不容易做。<br />　　6&gt; 单点登录需要使用cookies。</p>
		<p>
				<strong>7．配置用户定制目录（Customized User Directores）</strong>
		</p>
		<p>　　一些站点允许个别用户在服务器上发布网页。例如，一所大学的学院可能想给每一位学生一个公共区域，或者是一个ISP希望给一些web空间给他的客户，但这又不是虚拟主机。在这种情况下，一个典型的方法就是在用户名前面加一个特殊字符（~），作为每位用户的网站，比如：</p>
		<p>http://www.cs.myuniversity.edu/~username<br />http://members.mybigisp.com/~username</p>
		<p>　　Tomcat提供两种方法在主机上映射这些个人网站，主要使用一对特殊的Listener元素。Listener的className属性应该是org.apache.catalina.startup.UserConfig，userClass属性应该是几个映射类之一。如果你的系统是Unix，它将有一个标准的/etc/passwd文件，该文件中的帐号能够被运行中的Tomcat很容易的读取，该文件指定了用户的主目录，使用PasswdUserDatabase 映射类。</p>
		<p>&lt;Listener className="org.apache.catalina.startup.UserConfig" <br />directoryName="public_html" <br />userClass="org.apache.catalina.startup.PasswdUserDatabase"/&gt;</p>
		<p>　　web文件需要放置在像/home/users/ian/public_html 或者 /users/jbrittain/public_html一样的目录下面。当然你也可以改变public_html 到其他任何子目录下。</p>
		<p>　　实际上，这个用户目录根本不一定需要位于用户主目录下里面。如果你没有一个密码文件，但你又想把一个用户名映射到公共的像/home一样目录的子目录里面，则可以使用HomesUserDatabase类。</p>
		<p>&lt;Listener className="org.apache.catalina.startup.UserConfig" <br />directoryName="public_html" homeBase="/home" <br />userClass="org.apache.catalina.startup.HomesUserDatabase"/&gt;</p>
		<p>　　这样一来，web文件就可以位于像/home/ian/public_html 或者 /home/jasonb/public_html一样的目录下。这种形式对Windows而言更加有利，你可以使用一个像c:\home这样的目录。</p>
		<p>　　这些Listener元素，如果出现，则必须在Host元素里面，而不能在context元素里面，因为它们都用应用于Host本身。</p>
		<p>
				<strong>8．在Tomcat中使用CGI脚本</strong>
		</p>
		<p>　　Tomcat主要是作为Servlet/JSP容器，但它也有许多传统web服务器的性能。支持通用网关接口（Common Gateway Interface，即CGI）就是其中之一，CGI提供一组方法在响应浏览器请求时运行一些扩展程序。CGI之所以被称为通用，是因为它能在大多数程序或脚本中被调用，包括：Perl，Python，awk，Unix shell scripting等，甚至包括Java。当然，你大概不会把一个Java应用程序当作CGI来运行，毕竟这样太过原始。一般而言，开发Servlet总要比CGI具有更好的效率，因为当用户点击一个链接或一个按钮时，你不需要从操作系统层开始进行处理。</p>
		<p>　　Tomcat包括一个可选的CGI Servlet，允许你运行遗留下来的CGI脚本。</p>
		<p>　　为了使Tomcat能够运行CGI，你必须做如下几件事：</p>
		<p>　　1. 把servlets-cgi.renametojar （在CATALINA_HOME/server/lib/目录下）改名为servlets-cgi.jar。处理CGI的servlet应该位于Tomcat的CLASSPATH下。<br />　　2. 在Tomcat的CATALINA_BASE/conf/web.xml 文件中，把关于&lt;servlet-name&gt; CGI的那段的注释去掉（默认情况下，该段位于第241行）。<br />　　3. 同样，在Tomcat的CATALINA_BASE/conf/web.xml文件中，把关于对CGI进行映射的那段的注释去掉（默认情况下，该段位于第299行）。注意，这段内容指定了HTML链接到CGI脚本的访问方式。<br />　　4. 你可以把CGI脚本放置在WEB-INF/cgi 目录下（注意，WEB-INF是一个安全的地方，你可以把一些不想被用户看见或基于安全考虑不想暴露的文件放在此处），或者你也可以把CGI脚本放置在context下的其他目录下，并为CGI Servlet调整cgiPathPrefix初始化参数。这就指定的CGI Servlet的实际位置，且不能与上一步指定的URL重名。<br />　　5. 重新启动Tomcat，你的CGI就可以运行了。</p>
		<p>　　在Tomcat中，CGI程序缺省放置在WEB-INF/cgi目录下，正如前面所提示的那样，WEB-INF目录受保护的，通过客户端的浏览器无法窥探到其中内容，所以对于放置含有密码或其他敏感信息的CGI脚本而言，这是一个非常好的地方。为了兼容其他服务器，尽管你也可以把CGI脚本保存在传统的/cgi-bin目录，但要知道，在这些目录中的文件有可能被网上好奇的冲浪者看到。另外，在Unix中，请确定运行Tomcat的用户有执行CGI脚本的权限。</p>
		<p>
				<strong>9．改变Tomcat中的JSP编译器（JSP Compiler）</strong>
		</p>
		<p>　　在Tomcat 4.1（或更高版本，大概），JSP的编译由包含在Tomcat里面的Ant程序控制器直接执行。这听起来有一点点奇怪，但这正是Ant有意为之的一部分，有一个API文档指导开发者在没有启动一个新的JVM的情况下，使用Ant。这是使用Ant进行Java开发的一大优势。另外，这也意味着你现在能够在Ant中使用任何javac支持的编译方式，这里有一个关于Apache Ant使用手册的javac page列表。使用起来是容易的，因为你只需要在&lt;init-param&gt; 元素中定义一个名字叫“compiler”，并且在value中有一个支持编译的编译器名字，示例如下：</p>
		<p>&lt;servlet&gt;<br />   &lt;servlet-name&gt;jsp&lt;/servlet-name&gt;<br />   &lt;servlet-class&gt;<br />     org.apache.jasper.servlet.JspServlet<br />   &lt;/servlet-class&gt;<br />   &lt;init-param&gt;<br />     &lt;param-name&gt;logVerbosityLevel&lt;/param-name&gt;<br />     &lt;param-value&gt;WARNING&lt;/param-value&gt;<br />   &lt;/init-param&gt;<br />   &lt;init-param&gt;<br />     &lt;param-name&gt;compiler&lt;/param-name&gt;<br />     &lt;param-value&gt;jikes&lt;/param-value&gt;<br />   &lt;/init-param&gt;<br />   &lt;load-on-startup&gt;3&lt;/load-on-startup&gt;<br />&lt;/servlet&gt;</p>
		<p>　　当然，给出的编译器必须已经安装在你的系统中，并且CLASSPATH可能需要设置，那处决于你选择的是何种编译器。</p>
		<p>
				<strong>10．限制特定主机访问（Restricting Access to Specific Hosts）</strong>
		</p>
		<p>　　有时，你可能想限制对Tomcat web应用的访问，比如，你希望只有你指定的主机或IP地址可以访问你的应用。这样一来，就只有那些指定的的客户端可以访问服务的内容了。为了实现这种效果，Tomcat提供了两个参数供你配置：RemoteHostValve 和RemoteAddrValve。</p>
		<p>　　通过配置这两个参数，可以让你过滤来自请求的主机或IP地址，并允许或拒绝哪些主机/IP。与之类似的，在Apache的httpd文件里有对每个目录的允许/拒绝指定。<br />例如你可以把Admin Web application设置成只允许本地访问，设置如下：</p>
		<p>&lt;Context path="/path/to/secret_files" ...&gt;<br /> &lt;Valve className="org.apache.catalina.valves.RemoteAddrValve"<br />        allow="127.0.0.1" deny=""/&gt;<br />&lt;/Context&gt;</p>
		<p>　　如果没有给出允许主机的指定，那么与拒绝主机匹配的主机就会被拒绝，除此之外的都是允许的。与之类似，如果没有给出拒绝主机的指定，那么与允许主机匹配的主机就会被允许，除此之外的都是拒绝的。</p>
<img src ="http://www.blogjava.net/xuechen0721/aggbug/90584.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2006-12-28 20:18 <a href="http://www.blogjava.net/xuechen0721/articles/90584.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java 操作 excel（引用自IBM）</title><link>http://www.blogjava.net/xuechen0721/articles/87375.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Wed, 13 Dec 2006 00:47:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/87375.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/87375.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/87375.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/87375.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/87375.html</trackback:ping><description><![CDATA[
		<p>
				<a name="1">
						<span class="atitle">
								<font face="Arial" size="4">JAVA EXCEL API简介</font>
						</span>
				</a>
		</p>
		<p>
				<font face="Arial" size="4">
				</font>
		</p>
		<p>Java Excel是一开放源码项目，通过它Java开发人员可以读取Excel文件的内容、创建新的Excel文件、更新已经存在的Excel文件。使用该API非Windows操作系统也可以通过纯Java应用来处理Excel数据表。因为是使用Java编写的，所以我们在Web应用中可以通过JSP、Servlet来调用API实现对Excel数据表的访问。</p>
		<p>现在发布的稳定版本是V2.0，提供以下功能：</p>
		<ul>
				<li>从Excel 95、97、2000等格式的文件中读取数据； 
</li>
				<li>读取Excel公式（可以读取Excel 97以后的公式）； 
</li>
				<li>生成Excel数据表（格式为Excel 97）； 
</li>
				<li>支持字体、数字、日期的格式化； 
</li>
				<li>支持单元格的阴影操作，以及颜色操作； 
</li>
				<li>修改已经存在的数据表； </li>
		</ul>
		<p>现在还不支持以下功能，但不久就会提供了：</p>
		<ol>
				<li>不能够读取图表信息； 
</li>
				<li>可以读，但是不能生成公式，任何类型公式最后的计算值都可以读出； </li>
		</ol>
		<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" href="http://www-128.ibm.com/developerworks/cn/java/l-javaExcel/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="2">
						<span class="atitle">
								<font face="Arial" size="4">应用示例</font>
						</span>
				</a>
		</p>
		<p>
				<font face="Arial" size="4">
				</font>
		</p>
		<p>
				<a name="N1006F">
						<span class="smalltitle">
								<strong>
										<font face="Arial">1 从Excel文件读取数据表</font>
								</strong>
						</span>
				</a>
		</p>
		<p>
				<strong>
						<font face="Arial">
						</font>
				</strong>
		</p>
		<p>Java Excel API既可以从本地文件系统的一个文件(.xls)，也可以从输入流中读取Excel数据表。读取Excel数据表的第一步是创建Workbook(术语：工作薄)，下面的代码片段举例说明了应该如何操作：(完整代码见ExcelReading.java)</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">import java.io.*;
import jxl.*;
… … … …
try
{
//构建Workbook对象, 只读Workbook对象
	//直接从本地文件创建Workbook
//从输入流创建Workbook
    InputStream is = new FileInputStream(sourcefile);
    jxl.Workbook rwb = Workbook.getWorkbook(is);
}
catch (Exception e)
{
	e.printStackTrace();
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>一旦创建了Workbook，我们就可以通过它来访问Excel Sheet(术语：工作表)。参考下面的代码片段：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">//获取第一张Sheet表
Sheet rs = rwb.getSheet(0);
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>我们既可能通过Sheet的名称来访问它，也可以通过下标来访问它。如果通过下标来访问的话，要注意的一点是下标从0开始，就像数组一样。</p>
		<p>一旦得到了Sheet，我们就可以通过它来访问Excel Cell(术语：单元格)。参考下面的代码片段：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">//获取第一行，第一列的值
Cell c00 = rs.getCell(0, 0);
String strc00 = c00.getContents();

//获取第一行，第二列的值
Cell c10 = rs.getCell(1, 0);
String strc10 = c10.getContents();

//获取第二行，第二列的值
Cell c11 = rs.getCell(1, 1);
String strc11 = c11.getContents();

System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>如果仅仅是取得Cell的值，我们可以方便地通过getContents()方法，它可以将任何类型的Cell值都作为一个字符串返回。示例代码中Cell(0, 0)是文本型，Cell(1, 0)是数字型，Cell(1,1)是日期型，通过getContents()，三种类型的返回值都是字符型。</p>
		<p>如果有需要知道Cell内容的确切类型，API也提供了一系列的方法。参考下面的代码片段：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">String strc00 = null;
double strc10 = 0.00;
Date strc11 = null;

Cell c00 = rs.getCell(0, 0);
Cell c10 = rs.getCell(1, 0);
Cell c11 = rs.getCell(1, 1);

if(c00.getType() == CellType.LABEL)
{
LabelCell labelc00 = (LabelCell)c00;
strc00 = labelc00.getString();
}
if(c10.getType() == CellType.NUMBER)
{
	NmberCell numc10 = (NumberCell)c10;
strc10 = numc10.getValue();
}
if(c11.getType() == CellType.DATE)
{
DateCell datec11 = (DateCell)c11;
strc11 = datec11.getDate();
}

System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在得到Cell对象后，通过getType()方法可以获得该单元格的类型，然后与API提供的基本类型相匹配，强制转换成相应的类型，最后调用相应的取值方法getXXX()，就可以得到确定类型的值。API提供了以下基本类型，与Excel的数据格式相对应，如下图所示：</p>
		<br />
		<img height="218" alt="" src="http://www-128.ibm.com/developerworks/cn/java/l-javaExcel/image001.jpg" width="365" />
		<br />
		<p>每种类型的具体意义，请参见Java Excel API Document。</p>
		<p>当你完成对Excel电子表格数据的处理后，一定要使用close()方法来关闭先前创建的对象，以释放读取数据表的过程中所占用的内存空间，在读取大量数据时显得尤为重要。参考如下代码片段：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">//操作完成时，关闭对象，释放占用的内存空间
rwb.close();
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>Java Excel API提供了许多访问Excel数据表的方法，在这里我只简要地介绍几个常用的方法，其它的方法请参考附录中的Java Excel API Document。</p>
		<p>
				<b>Workbook类提供的方法</b>
		</p>
		<p>1. int getNumberOfSheets() <br />获得工作薄（Workbook）中工作表（Sheet）的个数，示例： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
int sheets = rwb.getNumberOfSheets();
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>2. Sheet[] getSheets() <br />返回工作薄（Workbook）中工作表（Sheet）对象数组，示例： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
Sheet[] sheets = rwb.getSheets();
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>3. String getVersion() <br />返回正在使用的API的版本号，好像是没什么太大的作用。 </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
String apiVersion = rwb.getVersion();
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<b>Sheet接口提供的方法</b>
		</p>
		<p>1) String getName() <br />获取Sheet的名称，示例： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
String sheetName = rs.getName();
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>2) int getColumns() <br />获取Sheet表中所包含的总列数，示例： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
int rsColumns = rs.getColumns();
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>3) Cell[] getColumn(int column) <br />获取某一列的所有单元格，返回的是单元格对象数组，示例： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell[] cell = rs.getColumn(0);
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>4) int getRows() <br />获取Sheet表中所包含的总行数，示例： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
int rsRows = rs.getRows();
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>5) Cell[] getRow(int row) <br />获取某一行的所有单元格，返回的是单元格对象数组，示例子： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell[] cell = rs.getRow(0);
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>6) Cell getCell(int column, int row) <br />获取指定单元格的对象引用，需要注意的是它的两个参数，第一个是列数，第二个是行数，这与通常的行、列组合有些不同。 </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell cell = rs.getCell(0, 0);
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N10114">
						<span class="smalltitle">
								<strong>
										<font face="Arial">2 生成新的Excel工作薄</font>
								</strong>
						</span>
				</a>
		</p>
		<p>
				<strong>
						<font face="Arial">
						</font>
				</strong>
		</p>
		<p>下面的代码主要是向大家介绍如何生成简单的Excel工作表，在这里单元格的内容是不带任何修饰的(如：字体，颜色等等)，所有的内容都作为字符串写入。(完整代码见ExcelWriting.java)</p>
		<p>与读取Excel工作表相似，首先要使用Workbook类的工厂方法创建一个可写入的工作薄(Workbook)对象，这里要注意的是，只能通过API提供的工厂方法来创建Workbook，而不能使用WritableWorkbook的构造函数，因为类WritableWorkbook的构造函数为protected类型。示例代码片段如下：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">import java.io.*;
import jxl.*;
import jxl.write.*;
… … … …
try
{
//构建Workbook对象, 只读Workbook对象
//Method 1：创建可写入的Excel工作薄
    jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));

//Method 2：将WritableWorkbook直接写入到输出流
/*
    OutputStream os = new FileOutputStream(targetfile);
    jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os);
*/
}
catch (Exception e)
{
	e.printStackTrace();
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>API提供了两种方式来处理可写入的输出流，一种是直接生成本地文件，如果文件名不带全路径的话，缺省的文件会定位在当前目录，如果文件名带有全路径的话，则生成的Excel文件则会定位在相应的目录；另外一种是将Excel对象直接写入到输出流，例如：用户通过浏览器来访问Web服务器，如果HTTP头设置正确的话，浏览器自动调用客户端的Excel应用程序，来显示动态生成的Excel电子表格。</p>
		<p>接下来就是要创建工作表，创建工作表的方法与创建工作薄的方法几乎一样，同样是通过工厂模式方法获得相应的对象，该方法需要两个参数，一个是工作表的名称，另一个是工作表在工作薄中的位置，参考下面的代码片段：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">//创建Excel工作表
jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>"这锅也支好了，材料也准备齐全了，可以开始下锅了！"，现在要做的只是实例化API所提供的Excel基本数据类型，并将它们添加到工作表中就可以了，参考下面的代码片段：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">//1.添加Label对象
jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell");
ws.addCell(labelC);

//添加带有字型Formatting的对象
jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true);
jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);
jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF);
ws.addCell(labelCF);

//添加带有字体颜色Formatting的对象
jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,
UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.RED);
jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);
jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC);
ws.addCell(labelCF);

//2.添加Number对象
jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926);
ws.addCell(labelN);

//添加带有formatting的Number对象
jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");
jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);
jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN);
ws.addCell(labelNF);

//3.添加Boolean对象
jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false);
ws.addCell(labelB);

//4.添加DateTime对象
jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date());
ws.addCell(labelDT);

//添加带有formatting的DateFormat对象
jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss");
jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);
jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF);
ws.addCell(labelDTF);
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这里有两点大家要引起大家的注意。第一点，在构造单元格时，单元格在工作表中的位置就已经确定了。一旦创建后，单元格的位置是不能够变更的，尽管单元格的内容是可以改变的。第二点，单元格的定位是按照下面这样的规律(column, row)，而且下标都是从0开始，例如，A1被存储在(0, 0)，B1被存储在(1, 0)。</p>
		<p>最后，不要忘记关闭打开的Excel工作薄对象，以释放占用的内存，参见下面的代码片段：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">//写入Exel工作表
wwb.write();

//关闭Excel工作薄对象
wwb.close();
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这可能与读取Excel文件的操作有少少不同，在关闭Excel对象之前，你必须要先调用write()方法，因为先前的操作都是存储在缓存中的，所以要通过该方法将操作的内容保存在文件中。如果你先关闭了Excel对象，那么只能得到一张空的工作薄了。</p>
		<p>
				<a name="N10144">
						<span class="smalltitle">
								<strong>
										<font face="Arial">3 拷贝、更新Excel工作薄</font>
								</strong>
						</span>
				</a>
		</p>
		<p>接下来简要介绍一下如何更新一个已经存在的工作薄，主要是下面二步操作，第一步是构造只读的Excel工作薄，第二步是利用已经创建的Excel工作薄创建新的可写入的Excel工作薄，参考下面的代码片段：(完整代码见ExcelModifying.java)</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">//创建只读的Excel工作薄的对象
jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));

//创建可写入的Excel工作薄对象
jxl.write.WritableWorkbook  wwb = Workbook.createWorkbook(new File(targetfile), rw);
            
//读取第一张工作表
jxl.write.WritableSheet ws = wwb.getSheet(0);

//获得第一个单元格对象
jxl.write.WritableCell wc = ws.getWritableCell(0, 0);
            
//判断单元格的类型, 做出相应的转化
if(wc.getType() == CellType.LABEL)
{
Label l = (Label)wc;
    l.setString("The value has been modified.");
}

//写入Excel对象
wwb.write();

//关闭可写入的Excel对象
wwb.close();

//关闭只读的Excel对象
rw.close();
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>之所以使用这种方式构建Excel对象，完全是因为效率的原因，因为上面的示例才是API的主要应用。为了提高性能，在读取工作表时，与数据相关的一些输出信息，所有的格式信息，如：字体、颜色等等，是不被处理的，因为我们的目的是获得行数据的值，既使没有了修饰，也不会对行数据的值产生什么影响。唯一的不利之处就是，在内存中会同时保存两个同样的工作表，这样当工作表体积比较大时，会占用相当大的内存，但现在好像内存的大小并不是什么关键因素了。</p>
		<p>一旦获得了可写入的工作表对象，我们就可以对单元格对象进行更新的操作了，在这里我们不必调用API提供的add()方法，因为单元格已经于工作表当中，所以我们只需要调用相应的setXXX()方法，就可以完成更新的操作了。</p>
		<p>尽单元格原有的格式化修饰是不能去掉的，我们还是可以将新的单元格修饰加上去，以使单元格的内容以不同的形式表现。</p>
		<p>新生成的工作表对象是可写入的，我们除了更新原有的单元格外，还可以添加新的单元格到工作表中，这与示例2的操作是完全一样的。</p>
		<p>最后，不要忘记调用write()方法，将更新的内容写入到文件中，然后关闭工作薄对象，这里有两个工作薄对象要关闭，一个是只读的，另外一个是可写入的。</p>
<img src ="http://www.blogjava.net/xuechen0721/aggbug/87375.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2006-12-13 08:47 <a href="http://www.blogjava.net/xuechen0721/articles/87375.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>EditPlus使用</title><link>http://www.blogjava.net/xuechen0721/articles/87130.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Tue, 12 Dec 2006 01:24:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/87130.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/87130.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/87130.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/87130.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/87130.html</trackback:ping><description><![CDATA[EditPlus是很多程序员非常熟悉的编辑工具，它以占用系统资源小、操作简便灵活、支持文件类型丰富（还能自定义文件类型）等优点而被广泛使用。<br />其实EditPlus不但有编辑程序的功能，还有灵活的编译功能，下面就简单介绍一下EditPlus对JAVA的编译设置。<br />实现对JAVA的编译<br />在配置EditPlus之前，我们先要将JAVA的运行环境安装并且调试好，这里我就不再说明了。<br />首先，从菜单“工具（Tools）”;“配置用户工具...”进入用户工具设置。<br />在类别里展开“工具”树形菜单;“用户工具”，选择“组和工具项目”中的“Group 1”，点击面板右边的“组名称...”按钮，将文本Group1”修改成“编译JAVA程序”。<br />然后选择修改的组“编译JAVA程序”，点击“添加新工具”按钮，选择程序，建立“新建程序”，选中它。<br />然后就是最重要的步骤（修改属性）：<br />1.1 添加编译功能 <br />“菜单文字”里的内容修改为“JAVAC”；<br />“命令”选择安装JDK后的BIN目录中的编译程序JAVAC.EXE，如果JDK 安装路径为“c:\jdk”，那么此路径为“c:\jdk\bin\javac.exe”；<br />“参数”选择“文件名称”，即显示为“$(FileName)”；<br />“初始目录”选择“文件目录”，显示为“$(FileDir)”；选择“捕获输出”复选框。<br />1.2 添加执行功能<br />“菜单文字”里的内容修改为“JAVA”；<br />“命令”选择安装JDK后的BIN目录中的编译程序JAVA.EXE，路径为“c:\jdk\bin\java.exe”；<br />“参数”选择“文件名（不含扩展名）”，即显示为“$(FileNameNoExt)”；<br />“初始目录”选择“文件目录”，显示为“$(FileDir)”；选择“捕获输出”复选框。<br />这样就完成了基本的配置工作，下面来试着编写一个JAVA程序来测试一下（如图），编译的所有信息都会显示在输出窗口中，双击某一行错误信息，EditPlus会自动定位到出错行 <br />转自（<a href="http://www.neeao.com/blog/article.asp?id=2400"><font color="#005880">http://www.neeao.com/blog/article.asp?id=2400</font></a>）<img src ="http://www.blogjava.net/xuechen0721/aggbug/87130.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2006-12-12 09:24 <a href="http://www.blogjava.net/xuechen0721/articles/87130.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java基础－关于session的详细解释（转载）</title><link>http://www.blogjava.net/xuechen0721/articles/44967.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Mon, 08 May 2006 03:05:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/44967.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/44967.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/44967.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/44967.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/44967.html</trackback:ping><description><![CDATA[
		<p>一、术语session <br />　　在我的经验里，session这个词被滥用的程度大概仅次于transaction，更加有趣的是transaction与session在某些语境下的含义是相同的。</p>
		<p>　　session，中文经常翻译为会话，其本来的含义是指有始有终的一系列动作/消息，比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个session。有时候我们可以看到这样的话“在一个浏览器会话期间，...”，这里的会话一词用的就是其本义，是指从一个浏览器窗口打开到关闭这个期间①。最混乱的是“用户（客户端）在一次会话期间”这样一句话，它可能指用户的一系列动作（一般情况下是同某个具体目的相关的一系列动作，比如从登录到选购商品到结账登出这样一个网上购物的过程，有时候也被称为一个transaction），然而有时候也可能仅仅是指一次连接，也有可能是指含义①，其中的差别只能靠上下文来推断②。</p>
		<p>　　然而当session一词与网络协议相关联时，它又往往隐含了“面向连接”和/或“保持状态”这样两个含义，“面向连接”指的是在通信双方在通信之前要先建立一个通信的渠道，比如打电话，直到对方接了电话通信才能开始，与此相对的是写信，在你把信发出去的时候你并不能确认对方的地址是否正确，通信渠道不一定能建立，但对发信人来说，通信已经开始了。“保持状态”则是指通信的一方能够把一系列的消息关联起来，使得消息之间可以互相依赖，比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有“一个TCP session”或者“一个POP3 session”③。</p>
		<p>　　而到了web服务器蓬勃发展的时代，session在web开发语境下的语义又有了新的扩展，它的含义是指一类用来在客户端与服务器之间保持状态的解决方案④。有时候session也用来指这种解决方案的存储结构，如“把xxx保存在session里”⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持，所以在某种特定语言的语境下，session也被用来指代该语言的解决方案，比如经常把Java里提供的javax.servlet.http.HttpSession简称为session⑥。</p>
		<p>　　鉴于这种混乱已不可改变，本文中session一词的运用也会根据上下文有不同的含义，请大家注意分辨。</p>
		<p>　　在本文中，使用中文“浏览器会话期间”来表达含义①，使用“session机制”来表达含义④，使用“session”表达含义⑤，使用具体的“HttpSession”来表达含义⑥</p>
		<p>　　二、HTTP协议与状态保持</p>
		<p>　　HTTP协议本身是无状态的，这与HTTP协议本来的目的是相符的，客户端只需要简单的向服务器请求下载某些文件，无论是客户端还是服务器都没有必要纪录彼此过去的行为，每一次请求之间都是独立的，好比一个顾客和一个自动售货机或者一个普通的（非会员制）大卖场之间的关系一样。</p>
		<p>　　然而聪明（或者贪心？）的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用，就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为，另一方面在服务器端则出现了CGI规范以响应客户端的动态请求，作为传输载体的HTTP协议也添加了文件上载、cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。</p>
		<p>　　让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠，然而一次性消费5杯咖啡的机会微乎其微，这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案：</p>
		<p>　　1、该店的店员很厉害，能记住每位顾客的消费数量，只要顾客一走进咖啡店，店员就知道该怎么对待了。这种做法就是协议本身支持状态。</p>
		<p>　　2、发给顾客一张卡片，上面记录着消费的数量，一般还有个有效期限。每次消费时，如果顾客出示这张卡片，则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。</p>
		<p>　　3、发给顾客一张会员卡，除了卡号之外什么信息也不纪录，每次消费时，如果顾客出示该卡片，则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。</p>
		<p>　　由于HTTP协议是无状态的，而出于种种考虑也不希望使之成为有状态的，因此，后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案，而session机制采用的是在服务器端保持状态的方案。同时我们也看到，由于采用服务器端保持状态的方案在客户端也需要保存一个标识，所以session机制可能需要借助于cookie机制来达到保存标识的目的，但实际上它还有其他选择。</p>
		<p>　　三、理解cookie机制 </p>
		<p>　　cookie机制的基本原理就如上面的例子一样简单，但是还有几个问题需要解决：“会员卡”如何分发；“会员卡”的内容；以及客户如何使用“会员卡”。</p>
		<p>　　正统的cookie分发是通过扩展HTTP协议来实现的，服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。</p>
		<p>　　而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie，如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置，则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示，如果某家分店还发行了自己的会员卡，那么进这家店的时候除了要出示麦当劳的会员卡，还要出示这家店的会员卡。</p>
		<p>　　cookie的内容主要包括：名字，值，过期时间，路径和域。</p>
		<p>　　其中域可以指定某一个域比如.google.com，相当于总店招牌，比如宝洁公司，也可以指定一个域下的具体某台机器比如<a href="http://www.google.com">www.google.com</a>或者froogle.google.com，可以用飘柔来做比。</p>
		<p>　　路径就是跟在域名后面的URL路径，比如/或者/foo等等，可以用某飘柔专柜做比。</p>
		<p>　　路径与域合在一起就构成了cookie的作用范围。</p>
		<p>　　如果不设置过期时间，则表示这个cookie的生命期为浏览器会话期间，只要关闭浏览器窗口，cookie就消失了。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里，当然这种行为并不是规范规定的。如果设置了过期时间，浏览器就会把cookie保存到硬盘上，关闭后再次打开浏览器，这些cookie仍然有效直到超过设定的过期时间。</p>
		<p>　　存储在硬盘上的cookie可以在不同的浏览器进程间共享，比如两个IE窗口。而对于保存在内存里的cookie，不同的浏览器有不同的处理方式。对于IE，在一个打开的窗口上按Ctrl-N（或者从文件菜单）打开的窗口可以与原窗口共享，而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie；对于Mozilla Firefox0.8，所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。</p>
		<p>　　下面就是一个goolge设置cookie的响应头的例子</p>
		<p>HTTP/1.1 302 Found<br />Location: <a href="http://www.google.com/intl/zh-CN/">http://www.google.com/intl/zh-CN/</a><br />Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com<br />Content-Type: text/html</p>
		<p>
				<br />　　这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分</p>
		<p> </p>
		<p>　　浏览器在再次访问goolge的资源时自动向外发送cookie</p>
		<p>
				<br />　　使用Firefox可以很容易的观察现有的cookie的值</p>
		<p>　　使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。</p>
		<p> </p>
		<p>　　IE也可以设置在接受cookie前询问</p>
		<p>
				<br />　　这是一个询问接受cookie的对话框。</p>
		<p>　　四、理解session机制</p>
		<p>　session机制是一种服务器端的机制，服务器使用一种类似于散列表的结构（也可能就是使用散列表）来保存信息。</p>
		<p>　　当程序需要为某个客户端的请求创建一个session的时候，服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为session id，如果已包含一个session id则说明以前已经为此客户端创建过session，服务器就按照session id把这个session检索出来使用（如果检索不到，可能会新建一个），如果客户端请求不包含session id，则为此客户端创建一个session并且生成一个与此session相关联的session id，session id的值应该是一个既不会重复，又不容易被找到规律以仿造的字符串，这个session id将被在本次响应中返回给客户端保存。</p>
		<p>　　保存这个session id的方式可以采用cookie，这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID，而。比如weblogic对于web应用程序生成的cookie，JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764，它的名字就是JSESSIONID。</p>
		<p>　　由于cookie可以被人为的禁止，必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写，就是把session id直接附加在URL路径的后面，附加方式也有两种，一种是作为URL路径的附加信息，表现形式为<a href="http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764</a>另一种是作为查询字符串附加在URL后面，表现形式为<a href="http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764</a><br />这两种方式对于用户来说是没有区别的，只是服务器在解析的时候处理的方式不同，采用第一种方式也有利于把session id的信息和正常程序参数区分开来。</p>
		<p>　　为了在整个交互过程中始终保持状态，就必须在每个客户端可能请求的路径后面都包含这个session id。</p>
		<p>　　另一种技术叫做表单隐藏字段。就是服务器会自动修改表单，添加一个隐藏字段，以便在表单提交时能够把session id传递回服务器。比如下面的表单</p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p>　　在被传递给客户端之前将被改写成</p>
		<p> </p>
		<p>
				<br /> </p>
		<p>　　这种技术现在已较少应用，笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。实际上这种技术可以简单的用对action应用URL重写来代替。</p>
		<p>　　在谈论session机制的时候，常常听到这样一种误解“只要关闭浏览器，session就消失了”。其实可以想象一下会员卡的例子，除非顾客主动对店家提出销卡，否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的，除非程序通知服务器删除一个session，否则服务器会一直保留，程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭，因此服务器根本不会有机会知道浏览器已经关闭，之所以会有这种错觉，是大部分session机制都使用会话cookie来保存session id，而关闭浏览器后这个session id就消失了，再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上，或者使用某种手段改写浏览器发出的HTTP请求头，把原来的session id发送给服务器，则再次打开浏览器仍然能够找到原来的session。</p>
		<p>　　恰恰是由于关闭浏览器不会导致session被删除，迫使服务器为seesion设置了一个失效时间，当距离客户端上一次使用session的时间超过这个失效时间时，服务器就可以认为客户端已经停止了活动，才会把session删除以节省存储空间。</p>
		<p>　　五、理解javax.servlet.http.HttpSession</p>
		<p>　　HttpSession是Java平台对session机制的实现规范，因为它仅仅是个接口，具体到每个web应用服务器的提供商，除了对规范支持之外，仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。</p>
		<p>　　首先，Weblogic Server提供了一系列的参数来控制它的HttpSession的实现，包括使用cookie的开关选项，使用URL重写的开关选项，session持久化的设置，session失效时间的设置，以及针对cookie的各种设置，比如设置cookie的名字、路径、域，cookie的生存时间等。</p>
		<p>　　一般情况下，session都是存储在内存里，当服务器进程被停止或者重启的时候，内存里的session也会被清空，如果设置了session的持久化特性，服务器就会把session保存到硬盘上，当服务器进程重新启动或这些信息将能够被再次使用，Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。</p>
		<p>　　复制严格说来不算持久化保存，因为session实际上还是保存在内存里，不过同样的信息被复制到各个cluster内的服务器进程中，这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。</p>
		<p>　　cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。</p>
		<p>　　cookie的路径对于web应用程序来说是一个非常重要的选项，Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。</p>
		<p>　　关于session的设置参考[5] <a href="http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869">http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869</a></p>
		<p>　　六、HttpSession常见问题</p>
		<p>　　（在本小节中session的含义为⑤和⑥的混合）</p>
		<p>　　1、session在何时被创建</p>
		<p>　　一个常见的误解是以为session在有客户端访问时就被创建，然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建，注意如果JSP没有显示的使用 &lt;<a href="mailto:%@page">%@page</a> session="false"%&gt;关闭session，则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。</p>
		<p>　　由于session会消耗内存资源，因此，如果不打算使用session，应该在所有的JSP中关闭它。</p>
		<p>　　2、session何时被删除</p>
		<p>　　综合前面的讨论，session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止（非持久session）</p>
		<p>　　3、如何做到在浏览器关闭时删除session</p>
		<p>　　严格的讲，做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作，然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。</p>
		<p>　　4、有个HttpSessionListener是怎么回事</p>
		<p>　　你可以创建这样的listener去监控session的创建和销毁事件，使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener，而不是相反。类似的与HttpSession有关的listener还有HttpSessionBindingListener，HttpSessionActivationListener和HttpSessionAttributeListener。 </p>
		<p>5、存放在session中的对象必须是可序列化的吗</p>
		<p>　　不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果session中有不可序列化的对象，在session销毁时会有一个Exception，很奇怪。</p>
		<p>　　6、如何才能正确的应付客户端禁止cookie的可能性</p>
		<p>　　对所有的URL使用URL重写，包括超链接，form的action，和重定向的URL，具体做法参见[6]<br /><a href="http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770">http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770</a></p>
		<p>　　7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session</p>
		<p>　　参见第三小节对cookie的讨论，对session来说是只认id不认人，因此不同的浏览器，不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。</p>
		<p>　　8、如何防止用户打开两个浏览器窗口操作导致的session混乱</p>
		<p>　　这个问题与防止表单多次提交是类似的，可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端，同时保存在session里，客户端提交表单时必须把这个id也返回服务器，程序首先比较返回的id与保存在session里的值是否一致，如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口，一般不设置这个id，或者使用单独的id，以防主窗口无法操作，建议不要再window.open打开的窗口里做修改操作，这样就可以不用设置。</p>
		<p>　　9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue<br />做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变，需要向其他服务器进程复制新的session值。</p>
		<p>　　10、为什么session不见了</p>
		<p>　　排除session正常失效的因素之外，服务器本身的可能性应该是微乎其微的，虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过；浏览器插件的可能性次之，笔者也遇到过3721插件造成的问题；理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。</p>
		<p>　　出现这一问题的大部分原因都是程序的错误，最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。</p>
		<p>　　七、跨应用程序的session共享</p>
		<p>　　常常有这样的情况，一个大项目被分割成若干小项目开发，为了能够互不干扰，要求每个小项目作为一个单独的web应用程序开发，可是到了最后突然发现某几个小项目之间需要共享一些信息，或者想使用session来实现SSO(single sign on)，在session中保存login的用户信息，最自然的要求是应用程序间能够访问彼此的session。</p>
		<p>　　然而按照Servlet规范，session的作用范围应该仅仅限于当前应用程序下，不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范，但是实现的细节却可能各有不同，因此解决跨应用程序session共享的方法也各不相同。</p>
		<p>　　首先来看一下Tomcat是如何实现web应用程序之间session的隔离的，从Tomcat设置的cookie路径来看，它对不同的应用程序设置的cookie路径是不同的，这样不同的应用程序所用的session id是不同的，因此即使在同一个浏览器窗口里访问不同的应用程序，发送给服务器的session id也可以是不同的。</p>
		<p> </p>
		<p>　　根据这个特性，我们可以推测Tomcat中session的内存结构大致如下。</p>
		<p> </p>
		<p>　　笔者以前用过的iPlanet也采用的是同样的方式，估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器，解决的思路很简单，实际实行起来也不难。要么让所有的应用程序共享一个session id，要么让应用程序能够获得其他应用程序的session id。</p>
		<p>　　iPlanet中有一种很简单的方法来实现共享一个session id，那就是把各个应用程序的cookie路径都设为/（实际上应该是/NASApp，对于应用程序来讲它的作用相当于根）。</p>
		<p>/NASApp </p>
		<p>
				<br />　　需要注意的是，操作共享的session应该遵循一些编程约定，比如在session attribute名字的前面加上应用程序的前缀，使得setAttribute("name", "neo")变成setAttribute("app1.name", "neo")，以防止命名空间冲突，导致互相覆盖。</p>
		<p>
				<br />　　在Tomcat中则没有这么方便的选择。在Tomcat版本3上，我们还可以有一些手段来共享session。对于版本4以上的Tomcat，目前笔者尚未发现简单的办法。只能借助于第三方的力量，比如使用文件、数据库、JMS或者客户端cookie，URL参数或者隐藏字段等手段。</p>
		<p>　　我们再看一下Weblogic Server是如何处理session的。</p>
		<p> </p>
		<p>
				<br />　　从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/，这是不是意味着在Weblogic Server中默认的就可以共享session了呢？然而一个小实验即可证明即使不同的应用程序使用的是同一个session，各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下</p>
		<p> </p>
		<p>　　对于这样一种结构，在session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量，比如使用文件、数据库、JMS或者客户端cookie，URL参数或者隐藏字段等手段，还有一种较为方便的做法，就是把一个应用程序的session放到ServletContext中，这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下，</p>
		<p>　　应用程序A</p>
		<p>context.setAttribute("appA", session); </p>
		<p>　　应用程序B</p>
		<p>contextA = context.getContext("/appA");<br />HttpSession sessionA = (HttpSession)contextA.getAttribute("appA"); </p>
		<p>　　值得注意的是这种用法不可移植，因为根据ServletContext的JavaDoc，应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值，以上做法在Weblogic Server 8.1中通过。</p>
		<p>　　那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢？原来是为了SSO，凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点，修改首先登录的那个应用程序的描述符weblogic.xml，把cookie路径修改为/appA访问另外一个应用程序会重新要求登录，即使是反过来，先访问cookie路径为/的应用程序，再访问修改过路径的这个，虽然不再提示登录，但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM，因为浏览器和web服务器对basic认证方式有其他的处理方式，第二次请求的认证不是通过session来实现的。具体请参看[7] secion 14.8 Authorization，你可以修改所附的示例程序来做这些试验。</p>
		<p>　　八、总结</p>
		<p>　　session机制本身并不复杂，然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器，服务器的经验当作普遍适用的经验，而是始终需要具体情况具体分析。<br /></p>
<img src ="http://www.blogjava.net/xuechen0721/aggbug/44967.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2006-05-08 11:05 <a href="http://www.blogjava.net/xuechen0721/articles/44967.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>论J2EE程序员的武功修为 （转载）</title><link>http://www.blogjava.net/xuechen0721/articles/43629.html</link><dc:creator>兵临城下</dc:creator><author>兵临城下</author><pubDate>Thu, 27 Apr 2006 09:49:00 GMT</pubDate><guid>http://www.blogjava.net/xuechen0721/articles/43629.html</guid><wfw:comment>http://www.blogjava.net/xuechen0721/comments/43629.html</wfw:comment><comments>http://www.blogjava.net/xuechen0721/articles/43629.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xuechen0721/comments/commentRss/43629.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xuechen0721/services/trackbacks/43629.html</trackback:ping><description><![CDATA[
		<a href="http://www.easyjf.com/">
				<font face="Arial" size="2">EasyJF开源团队</font>
		</a>
		<font face="Arial" size="2">(www.easyjf.com) 大峡<br /><br />　　经常会跟一些朋友讨论怎么样才能学好Java,学到什么程度才算撑握了Java的问题。其中有一个J2EE程序员层次及武功修为的问题，有点意思。这里就把讨论的内容大致整理一下发出来，大家继续讨论。<br /><br />　　纵观国内的软件行业，靠Java吃饭的程序员还真不是少，而且Java程序员是有很大优越感的，毕竟对于很多用b/s搞开发的业内朋友来说，Java技术意味着难度大、门槛高，因此相对来说Java程序员比其它的程序员(如php、.net)收入高就理所当然。然而J2EE所涉及到的范畴是很广的，不能一个Java程序员就概括了事，而应该具有层次及水平之分，很多时候经常需要进行分类或评级，有时他评、有时自评。<br /><br />　　谈到国内J2EE领域的程序员层次水平，当前流行的称谓及评级不外乎就下面几种：<br />　　第一种是精通掌握记事本、Dreamweaver等工具来写JSP+JavaBean数据库应用的是J2EE程序员；<br />　　第二种是用JBuilder、Eclipse等专用Java开发工具写着一堆一堆过程式Java Bean，而且还能精通Struts+Spring+Hibernate等应用框架的高级J2EE程序员；<br />　　第三种是用Together建模，然后生成一堆Java接口或代码，开口闭口都是设计模式的资深Java程序同及高级系统分析、构架师；<br />　　最后还有一种是整天在BlogJava或JavaEye上谈经论道的大师们，这些大师技术水平难以触摸，武功门派也各具特色，不好归类，有时不好称为程序员（因为有的时候他们甚至不写或者写不出程序），但又做着与J2EE程序员密切相关的事情，我们暂且就归为“牛牛”或“大师”。<br /><br />　　称谓毕竟只是称谓，带有点主观或者功利色彩，有时很难鉴定一个人应该属于什么，因此，我们再从纯技术的角度，也即武功修为的角度，作了一个简单的分析及归类，把2EE领域程序员大致分成以下几个层次，可以作为大家自评的一个参考标准：<br /><br />　　第一个层次：精通掌握Java语法、能调试基本的程序错误，精通掌握JSP+Java Bean写一些N年前ASP、PHP翻版的Java Web应用程序（如论坛、网站新闻发布系统、OA、网上商城等），精通JDBC使用、精通SQL语句、精通XML等。<br /><br />　　第二个层次：掌握设计模式原理及应用，掌握基于OO的分析及设计方法,并能精通熟练使用几种Java专业设计及开发工具，精通掌握流行的J2EE框架如Hibernate、EJB、Webwork、Spring的原理及应用，精通J2EE中一两个组成部分(如Servlet、EJB等)的工作原理及细节。<br /><br />　　第三个层次：少林的高僧有两种，禅僧及武僧。J2EE程序员的第三个层次也同样有禅、武两个分支，这里我们重点分析一下：<br /><br />　　第一个分支属于走的禅僧线路。在练完第二个层次中的各种武功基础上，结合实际项目中的千奇百怪的用户需求，游刃有余的选择适合的技术方案为客户解决问题，并形成自己的一套解决方案。达到这一个层次的J2EE程序员已经不在乎使用任何工具、任何框架了，而是根据不同的对手，使用不同的武器或招式来应对。好比小李飞刀一样，只有达到了“手中无刀、心中有刀”的境界，才能达到“出手一刀，例不虚发”的效果。这一层次的武功属于一个熟练度问题，刀练得多了、遇到的对手多了，再加上前面的武功修为，就算做不到例不虚发，也可达到十发九中。<br /><br />　　第二个分支属于走的武僧线路，在撑握熟悉第一二个层次中涉及到的内容后，进一步专研并撑握J2EE底层开发，J2EE规范制订、规范实现、Java虚拟机的工作原理、各种常见的J2EE服务器内核工作机制、内存管理、进程机制、源代码等。因为涉及的很多东西都比较抽象，代码也很多，练这一层的武功需要有很好的资质及耐性、并具还得有一定的环境及条件。好比神雕大侠杨过拿起“玄铁剑”，并练成“暗然销魂掌”的成长过程，需要前面的武功修为作基础，更需那只威力神武神雕的帮助指点及他处处为民、惩奸除恶的侠之心态。<br />　　<br />　　胡侃了这么多，现在来根据自己情况测算一下自己的份量，结果如下：<br />　　第一层　练到8成；<br />　　第二层　练到5成；<br />　　第三层　准备走禅僧线路，当前算是练到1成；<br />　　<br />　　唉，后面的武功提升越来越难，真不知道要到何年何月才能达到10成啊。你的武功练到哪一个层次了，不防亮出来大家切磋切磋。嘿嘿，要是有一天，咱们中国的Java程序员人手一把“玄铁剑”、人人会使“暗然销魂掌”，那还了得!汗...，写着写着居然做起白日梦了，不好意思，就此打住。</font>
<img src ="http://www.blogjava.net/xuechen0721/aggbug/43629.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xuechen0721/" target="_blank">兵临城下</a> 2006-04-27 17:49 <a href="http://www.blogjava.net/xuechen0721/articles/43629.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>