﻿<?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>IT博客-Joinclass Inc-文章分类-JAVA专题</title><link>http://www.cnitblog.com/tilan/category/4249.html</link><description>软件开发 软件配置 项目管理 软件工程</description><language>zh-cn</language><lastBuildDate>Tue, 27 Sep 2011 10:25:23 GMT</lastBuildDate><pubDate>Tue, 27 Sep 2011 10:25:23 GMT</pubDate><ttl>60</ttl><item><title>Struts+Spring+Hibernate快速入门</title><link>http://www.cnitblog.com/tilan/articles/22676.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 05 Feb 2007 03:47:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/22676.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/22676.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/22676.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/22676.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/22676.html</trackback:ping><description><![CDATA[本文是开发基于spring的web应用的入门文章，前端采用Struts MVC框架，中间层采用spring，后台采用Hibernate。 <br /><br />　　 本文包含以下内容：<br /><br />　　　 ·配置Hibernate和事务<br /><br />　　　 ·装载Spring的applicationContext.xml文件<br /><br />　　　 ·建立业务层和DAO之间的依赖关系<br /><br />　　　 ·将Spring应用到Struts中<br /><br />　　<b>简介</b><br /><br />　　 这个例子是建立一个简单的web应用，叫MyUsers,完成用户管理操作，包含简单的数据库增，删，查，该即CRUD（新建，访问，更新，删除）操作。这是一个三层的web应用，通过Action（Struts）访问业务层，业务层访问DAO。图一简要说明了该应用的总体结构。图上的数字说明了流程顺序－从web（UserAction）到中间层（UserManager），再到数据访问层（UserDAO），然后将结果返回。<br /><br />　　 Spring层的真正强大在于它的声明型事务处理，帮定和对持久层支持（例如Hiberate和iBATIS）<br /><br />　　 以下下是完成这个例子的步骤：<br /><br />　　 1． 安装Eclipse插件<br /><br />　　 2． 数据库建表<br /><br />　　 3． 配置Hibernate和Spring<br /><br />　　 4． 建立Hibernate DAO接口的实现类<br /><br />　　 5． 运行测试类，测试DAO的CRUD操作<br /><br />　　 6． 创建一个处理类，声明事务<br /><br />　　 7． 创建web层的Action和model<br /><br />　　 8． 运行Action的测试类测试CRUD操作<br /><br />　　 9． 创建jsp文件通过浏览器进行CRUD操作<br /><br />　　 10． 通过浏览器校验jsp<br /><br />　　<b>安装eclipse插件</b><br /><br />　　 1． Hibernate插件http://www.binamics.com/hibernatesync<br /><br />　　 2． Spring插件http://springframework.sourceforge.net/spring-ide/eclipse/updatesite/<br /><br />　　 3． MyEclipse插件(破解版)<br /><br />　　 4． Tomcat插件. tanghan<br /><br />　　 5． 其他插件包括xml，jsp，<br /><b><br />　　 数据库建表</b><br /><br /><code>create table app_user(id number not null primary,firstname vchar(32),lastname vchar(32));</code><br />　　<b>新建项目</b><br /><br />　　 新建一个web project，新建后的目录结构同时包含了新建文件夹page用于放jsp文件，和源文件夹test用于放junit测试文件。同时将用到的包，包括struts，hibernate，spring都导入到lib目录下。<br /><br />　　 创建持久层O/R mapping<br /><br />　　 1． 在src/com.jandar.model下用hibernate插件从数据库导出app_user的.hbm.xml文件改名为User.hbm.xml<br /><br /><code>＜?xml version="1.0"?＞<br />＜!DOCTYPE hibernate-mapping PUBLIC<br />　　 "-//Hibernate/Hibernate Mapping DTD//EN"<br />　　 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" ＞<br />＜hibernate-mapping package="com.jandar.model"＞<br />＜class name="User" table="APP_USER"＞<br />　 ＜id<br />　　 column="ID"<br />　　 name="id"<br />　　 type="integer"<br />　 ＞<br /><br />　　 ＜generator class="assigned" /＞<br /><br />　 ＜/id＞<br /><br />　 ＜property<br />　　　 column="LASTNAME"<br />　　　 length="10"<br />　　　 name="lastname"<br />　　　 not-null="false"<br />　　　 type="string"<br />　 /＞<br /><br />　 ＜property<br />　　　 column="FIRSTNAME"<br />　　　 length="10"<br />　　　 name="firstname"<br />　　　 not-null="true"<br />　　　 type="string"<br />　 /＞<br /><br />＜/class＞<br />＜/hibernate-mapping＞ </code><br />　　 2． 通过hibernate synchronizer-＞synchronizer file生成User.java文件,User对象对应于数据库中的app_user表<br /><br />　　 注：在eclipse下自动生成的对象文件不完全相同，相同的是每个对象文件必须实现Serializable接口，必需又toString和hashCode方法；<br /><br /><code>import java.io.Serializable;<br />import org.apache.commons.lang.builder.EqualsBuilder;<br />import org.apache.commons.lang.builder.HashCodeBuilder;<br />import org.apache.commons.lang.builder.ToStringBuilder;<br />import org.apache.commons.lang.builder.ToStringStyle;<br /><br />public class BaseObject implements Serializable {<br />　 public String toString() {<br />　　 return ToStringBuilder.reflectionToString(this,<br />　　 ToStringStyle.MULTI_LINE_STYLE);<br />　 } <br /><br />　 public boolean equals(Object o) {<br />　　 return EqualsBuilder.reflectionEquals(this, o);<br />　 } <br /><br />　 public int hashCode() {<br />　　 return HashCodeBuilder.reflectionHashCode(this);<br />　 }<br />}<br /><br />public class User extends BaseObject {<br />　 private Long id;<br />　 private String firstName;<br />　 private String lastName; <br /><br />　 /**<br />　 * @return Returns the id.<br />　 */<br /><br />　 public Long getId() {<br />　　 return id;<br />　 } <br /><br />　 /**<br />　　 * @param id The id to set.<br />　 */<br /><br />　 public void setId(Long id) {<br />　　 this.id = id;<br />　 }<br /><br />　 /**<br />　 * @return Returns the firstName.<br />　 */<br /><br />　 public String getFirstName() {<br />　　 return firstName;<br />　 } <br /><br />　 /**<br />　　 * @param firstName The firstName to set.<br />　 */<br /><br />　 public void setFirstName(String firstName) {<br />　　 this.firstName = firstName;<br />　 } <br /><br />　 /**<br />　 * @return Returns the lastName.<br />　 */<br /><br />　 public String getLastName() {<br />　　 return lastName;<br />　 } <br /><br />　 /**<br />　 * @param lastName The lastName to set.<br />　 */<br /><br />　 public void setLastName(String lastName) {<br />　　 this.lastName = lastName;<br />　 }<br />}</code><code><p><strong>创建DAO访问对象</strong><br /><br />　　 1． 在src/com.jandar.service.dao新建IDAO.java接口，所有的DAO都继承该接口<br /><br /></p><p><code>package com.jandar.services.dao;<br /><br />public interface IDAO </code><br />　　 2． 在src/com.jandar.service.dao下新建IUserDAO.java接口 <br /><br /><code>public interface IUserDAO extends DAO { <br />　 List getUsers();<br />　 User getUser(Integer userid);<br />　 void saveUser(User user);<br />　 void removeUser(Integer id); <br />}</code><br />　　 该接口提供了访问对象的方法，<br /><br />　　 3． 在src/com.jandar.service.dao.hibernate下新建UserDAOHiberante.java<br /><br /><code>import java.util.List;<br />import org.apache.commons.logging.Log;<br />import org.apache.commons.logging.LogFactory;<br />import org.springframework.orm.hibernate.support.HibernateDaoSupport; <br />import com.jandar.model.User;<br />import com.jandar.service.dao.IUserDAO;<br /><br />public class UserDaoHibernate extends HibernateDaoSupport implements IUserDAO { <br /><br />　 private Log log=LogFactory.getLog(UserDaoHibernate.class);<br />　 /* （非 Javadoc）<br />　 * @see com.jandar.dao.IUserDAO#getUsers()<br />　 */<br /><br />　 public List getUsers() {<br />　　 return getHibernateTemplate().find("from User"); <br />　 } <br /><br />　 /* （非 Javadoc）<br />　 * @see com.jandar.dao.IUserDAO#getUser(java.lang.Long)<br />　 */<br /><br />　 public User getUser(Integer id) {<br />　　 // TODO 自动生成方法存根<br />　　 return (User) getHibernateTemplate().get(User.class,id);<br />　 } <br /><br />　 /* （非 Javadoc）<br />　 * @see com.jandar.dao.IUserDAO#saveUser(com.jandar.model.User)<br />　 */<br /><br />　 public void saveUser(User user) {<br />　　 log.debug("xxxxxxx");<br />　　 System.out.println("yyyy");<br />　　 getHibernateTemplate().saveOrUpdate(user);<br />　　 if(log.isDebugEnabled())<br />　　 {<br />　　　 log.debug("userId set to "+user.getId());<br />　　 } <br />　 } <br /><br />　 /* （非 Javadoc）<br />　 * @see com.jandar.dao.IUserDAO#removeUser(java.lang.Long)<br />　 */<br /><br />　 public void removeUser(Integer id) {<br />　　 Object user=getHibernateTemplate().load(User.class,id);<br />　　 getHibernateTemplate().delete(user);<br />　　 if(log.isDebugEnabled()){<br />　　　 log.debug("del user "+id);<br />　　 } <br />　 } <br />}</code><br />　　 在这个类中实现了IUserDAO接口的方法，并且继承了HibernateDAOSupport类。这个类的作用是通过hibernate访问、操作对象，进而实现对数据库的操作。</p></code><script type="text/javascript"><!--
google_ad_client = "pub-3874079075380071";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-03-19: 3G-move, 三星, 摩托罗拉, 索爱, 诺基亚
google_ad_channel = "1730597762+1709572468+0778807001+6838187825+8377398382";
//--></script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script><img src ="http://www.cnitblog.com/tilan/aggbug/22676.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2007-02-05 11:47 <a href="http://www.cnitblog.com/tilan/articles/22676.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate+Spring+Struts扩展Struts</title><link>http://www.cnitblog.com/tilan/articles/22675.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 05 Feb 2007 03:46:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/22675.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/22675.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/22675.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/22675.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/22675.html</trackback:ping><description><![CDATA[<strong>简介</strong>：<br /><br />　　我看到很多项目中，开发者实现了自己的MVC框架，并不是因为他们想做同Struts根本不同的东西，而是因为他们并没有意识到如何扩展Struts。开发自己的MVC框架可以获得全部的控制权，但是这也意味着需要很多资源来实现它（人力物力），在紧张的日程安排下，有时候这是不可能的。 <br /><br />　　Struts不仅仅是一个强大的框架，同时它也是可扩展的。你可以以三种方式来扩展Struts。<br /><br />　　1、PlugIn：如果你想在application startup或shutdown的时候做一些业务逻辑的话，那就创建你自己的PlugIn类。<br /><br />　　2、RequestProcessor：如果你想在请求被处理的过程中某个时刻做一些业务逻辑的话，那么创建你自己的RequestProcessor类。比如说，在每次请求执行之前，你可以扩展RequestProcessor来检查用户是否登陆了以及他是否有权限去执行某个特定的action。<br /><br />　　3、ActionServlet：如果你想在application startup和shutdown的时候以及请求被处理的时候做某些业务逻辑，你也可以扩张ActionServlet类。不过你应当在PlugIn和RequestProcessor都不能解决你的需求的时候来使用ActionServlet。<br /><br />　　在这篇文章中，我们将使用一个Struts应用的示例来示范如何使用这三种方式来扩展Struts。示例程序的代码可以从http://www.onjava.com/onjava/2004/11/10/examples/sample1.zip下载。两个扩展Struts成功的范例是Struts自身的Validation和Tiles框架。<br /><br />　　我们假设你已经比较熟悉Struts框架并且知道如何使用它创建一个简单的应用。如果你想知道更多关于Struts的内容，请参考官方主页。<br /><br />　　<b>PlugIn</b><br />　<br />　　PlugIn是一个接口，你可以创建一个实现该接口的类，当application startup或shutdown的时候做些事情。<br /><br />　　比方说，我创建了一个使用Hibernate作为持久层的web应用，我想当应用启动的时候就初始化Hibernate，这样子当我的web应用受到第一个请求的时候，Hibernate就已经是配置好的并且可用的。同时我们想当application关闭的时候关闭Hibernate。我们可以用一个Hibernate PlugIn来实现这个需求，通过如下的两步：<br /><br />　　1、创建一个类实现了PlugIn接口：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>public class HibernatePlugIn implements PlugIn{<br />　private String configFile;<br />　// This method will be called at application shutdown time<br />　public void destroy() {<br />　　System.out.println("Entering HibernatePlugIn.destroy()");<br />　　//Put hibernate cleanup code here<br />　　System.out.println("Exiting HibernatePlugIn.destroy()");<br />　}<br />　//This method will be called at application startup time<br />　public void init(ActionServlet actionServlet, ModuleConfig config)<br />　　throws ServletException {<br />　　　System.out.println("Entering HibernatePlugIn.init()");<br />　　　System.out.println("value of init parameter " +<br />　　　getConfigFile());<br />　　　System.out.println("Exiting HibernatePlugIn.init()");<br />　　}<br />　public String getConfigFile() {<br />　　return name;<br />　}<br />　public void setConfigFile(String string) {<br />　　configFile = string;<br />　}<br />}</td></tr></tbody></table><br />　　实现PlugIn接口的类必须完成两个方法：init()和destroy()。当application startup的时候init()方法被调用，当shutdown的时候destroy()方法被调用。Struts还允许给你的PlugIn类传递初始化参数。为了传递参数，你必须在PlugIn类中为每一个参数创建JavaBean式的setter方法。在我们的HibernatePlugIn类中，我会把configFile的name作为参数传进去，而不是硬编码到程序中。<br /><br />　　2、在struts-config.xml中添加如下的代码来通告Struts有新的PlugIn：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>＜struts-config＞<br />　...<br />　＜!-- Message Resources --＞<br />　＜message-resources parameter= "sample1.resources.ApplicationResources"/＞<br /><br />　＜!-- Declare your plugins --＞<br />　＜plug-in className="com.sample.util.HibernatePlugIn"＞<br />　　＜set-property property="configFile" value="/hibernate.cfg.xml"/＞<br />　＜/plug-in＞<br />＜/struts-config＞</td></tr></tbody></table><br />　　属性className是实现了PlugIn接口的类的全限定名。对于每一个初始化参数，可以使用＜set-property＞元素传递参数。在我们的例子中，我要把config文件的名字传进去，所以使用了一个带有配置文件路径的＜set-property＞。<br /><br />　　Struts的Tiles和Validator框架都使用PlugIn来读取配置文件进行初始化。另外两件PlugIn可以帮你做到的事情是：<br /><br />　　·如果你的application依赖于某些配置文件，那么你可以在PlugIn类中检查它们是否可用，如果不可用的话抛出一个ServletException，这样就可以使ActionServlet变为不可用。<br /><br />　　·PlugIn接口的init()方法是你可以改变ModuleConfig的最后机会，ModuleConfig是一组静态配置信息的集合，用来描述基于Struts模块。Struts将会在所有PlugIn处理完后释放ModuleConfig。<br /><br /><strong>Request是如何被处理的<br /><br /></strong>　　ActionServlet是Struts框架中唯一的Servlet，它负责处理所有request。无论何时接收到一个request，它都会先尝试为当前的request寻找一个sub-application。一旦一个sub-application被找到，ActionServlet就会为那个sub-application创建一个RequestProcessor对象，调用这个对象的process()方法并把HttpServletRequest和HttpServletResponse对象传入。<br /><br />　　RequestProcessor.process()就是大部分request被处理的地方。process()方法使用了Template Method模式实现，其中有很多独立的方法来执行请求处理的每一步骤，这些方法将会在process方法中依次被调用。比如，将会有一个独立的方法用来寻找当前request对应的ActionForm类，一个方法来检查当前用户是否有执行action mapping所必须的权限。这些给与我们极大的灵活性。在发布的Struts包中有一个RequestProcessor类提供了请求处理每一步骤的默认实现。这就意味着你可以仅仅重写你感兴趣的方法，其它的使用默认的实现。举例来说，默认地Struts调用request.isUserInRole()来检查用户是否有权限执行当前的ActionMapping；这时如果你想通过查询数据库来实现，你所要做的就是重写processRoles()方法，通过查询出的用户是否拥有必须的权限来返回true或false。<br /><br />　　首先我们将会看到缺省情况下，process()方法是如何实现的，然后我将会详细解释默认的RequestProcessor类中的每一个方法，这样你就可以决定哪一部分是你想要改变的。<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>public void process(HttpServletRequest request,HttpServletResponse response)<br />throws IOException, ServletException {<br />　// Wrap multipart requests with a special wrapper<br />　request = processMultipart(request);<br />　// Identify the path component we will<br />　// use to select a mapping<br />　String path = processPath(request, response);<br />　if (path == null) {<br />　　return;<br />　}<br />　if (log.isDebugEnabled()) {<br />　　log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'");<br />　}<br />　// Select a Locale for the current user if requested<br />　processLocale(request, response);<br />　// Set the content type and no-caching headers<br />　// if requested<br />　processContent(request, response);<br />　processNoCache(request, response);<br />　// General purpose preprocessing hook<br />　if (!processPreprocess(request, response)) {<br />　　return;<br />　}<br />　// Identify the mapping for this request<br />　ActionMapping mapping =<br />　processMapping(request, response, path);<br />　if (mapping == null) {<br />　　return;<br />　}<br />　// Check for any role required to perform this action<br />　if (!processRoles(request, response, mapping)) {<br />　　return;<br />　}<br />　// Process any ActionForm bean related to this request<br />　ActionForm form = processActionForm(request, response, mapping);<br />　processPopulate(request, response, form, mapping);<br />　if (!processValidate(request, response, form, mapping)) {<br />　　return;<br />}<br />// Process a forward or include specified by this mapping<br />if (!processForward(request, response, mapping)) {<br />　return;<br />}<br />if (!processInclude(request, response, mapping)) {<br />　return;<br />}<br />// Create or acquire the Action instance to<br />// process this request<br />Action action =<br />processActionCreate(request, response, mapping);<br />if (action == null) {<br />　return;<br />}<br />// Call the Action instance itself<br />ActionForward forward = processActionPerform(request, response,action, form, mapping);<br />// Process the returned ActionForward instance<br />processForwardConfig(request, response, forward);<br />}</td></tr></tbody></table><br />　　1、processMutipart()：在这个方法中，Struts将会读取request来检查request的contentType是否是multipart/form-data。如果是的话，将会解析request并且将之包装到HttpServletRequest中。当你创建了一个HTML FORM用来提交数据，那么request的contentType默认就是application/x-www-form-urlencoded。但是如果你的form使用了file类型的input控件允许用户上传文件的话，你就必须将contentType改为multipart/form-data。如果是这样的情况，你就不能再通过getParameter()来获取用户提交的数据；你必须将request作为一个InputStream来读取，并且自己解析它来获得参数值。<br /><br />　　2、processPath()：在这个方法中，Struts将会读取request的URI，来确定路径元素，这个元素是用来获取ActionMappint元素。<br /><br />　　3、processLocale()：在这个方法中，Struts将会为当前request取得Locale，如果配置过的话，还可以将这个对象作为HttpSession中org.apache.struts.action.LOCALE属性的值而保存。作为这个方法的副作用，HttpSession将会被创建，如果你不想创建的话，你可以在ControllerConfig中将locale属性设为false，在struts-config.xml中象如下这样：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>＜controller＞<br />　＜set-property property="locale" value="false"/＞<br />＜/controller＞</td></tr></tbody></table><br />　　4、processContent()：通过调用response.setContentType()来为response设置contentType。这个方法首先会尝试从struts-config.xml配置中得到contentType。缺省情况下使用text/html。如果想覆盖它，可以象如下这样：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>＜controller＞<br />＜set-property property="contentType" value="text/plain"/＞<br />＜/controller＞</td></tr></tbody></table><br />　　5、processNoCache()：如果配置是no-cache，Struts将会为每个response设置下面三个headers：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>requested in struts config.xml<br />response.setHeader("Pragma", "No-cache");<br />response.setHeader("Cache-Control", "no-cache");<br />response.setDateHeader("Expires", 1);</td></tr></tbody></table><br />　　如果你想设置no-cache header，在struts-config.xml中加入下面信息：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>＜controller＞<br />＜set-property property="noCache" value="true"/＞<br />＜/controller＞ </td></tr></tbody></table><br />　　6、processPreprocess()：这个方法为预处理提供一个hook，可以在子类中覆盖它。它的缺省实现没有作任何事情，总是返回true。返回false的话将会终止当前请求的处理。<br /><br />　　7、processMapping()：这个方法将会用路径信息得到一个ActionMapping对象。也就是struts-config.xml文件中的＜action＞元素：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>＜action path="/newcontact" type="com.sample.NewContactAction" name="newContactForm" scope="request"＞<br />＜forward name="sucess" path="/sucessPage.do"/＞<br />＜forward name="failure" path="/failurePage.do"/＞<br />＜/action＞</td></tr></tbody></table><br />　　ActionMapping元素包含了Action类的名称和处理请求使用的ActionForm等等信息。它还包含当前ActionMapping配置的ActionForwards信息。<br /><br />　　8、processRoles()：Struts web应用提供了一个授权方案。也就是说，一旦一个用户登入了容器，struts的processRoles()方法将会通过调用request.isUserInRole()，来检查他是否有必须的角色来运行一个给定的ActionMapping。<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>＜action path="/addUser" roles="administrator"/＞ </td></tr></tbody></table><br />　　假设你有一个AddUserAction并且你只想让administrator能够增加新的user。你所要做的就是给你的AddUserAction元素增加一个role属性，这个属性的值为administrator。这样，在运行AddUserAction之前，这个方法会确保用户拥有administraotr的角色。<br /><br />　　9、processActionForm()：每一个ActionMapping都一个相应的ActionForm类。当Struts处理一个ActionMapping的时候，它将会从＜action＞元素的name属性中找出对应的ActionForm类的名称。<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>＜form-bean name="newContactForm" type="org.apache.struts.action.DynaActionForm"＞<br />　＜form-property name="firstName" type="java.lang.String"/＞<br />　＜form-property name="lastName" type="java.lang.String"/＞<br />＜/form-bean＞</td></tr></tbody></table><br />　　在我们的例子中，它会先在request scope中检查是否有一个org.apache.struts.action.DynaActionForm类的对象存在。如果有它将会使用这个对象，如果没有它将会创建一个新的对象并把它设置在request scope。<br /><br />　　10、processPopulate()：在这个方法中，Struts将会用相匹配的request参数装配ActionForm的实例变量。<br /><br />　　11、processValidate()：Struts将会调用你的ActionForm类的validate方法。如果你从validate()返回ActionErrors，它将会将user重定向到＜action＞元素的input属性指定的页面。<br /><br />　　12、processForward()和processInclude()：在这些方法中，Struts将会检查＜action＞元素的forward或include属性，如果找到了，将会把forward或include请求放置到配置的页面中。<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>＜action forward="/Login.jsp" path="/loginInput"/＞<br />＜action include="/Login.jsp" path="/loginInput"/＞</td></tr></tbody></table><br />　　你可以从这些方法的名字上猜测它们的不同：processForward()最终调用RequestDispatcher.forward()，而processInclude()调用RequestDispatcher.include()。如果你同时配置了forward和include属性，它将会总是调用forward，因为forward先被处理。<br /><br />　　13、processActionCreate()：这个方法从＜action＞元素的type属性中获取获得Action类的名字并且创建返回它的实例。在我们的例子中，它将会创建一个com.sample.NewContactAction类的实例。<br /><br />　　14、processActionPerform()：这个方法调用你的Action类的excute()方法，你的业务逻辑也就是在excute方法中。<br /><br />　　15、processForwardConfig()：你的Action类的excute()方法将会返回一个ActionForward对象，这个对象将指出哪个页面是显示给用户的页面。因此，Struts将会为那个页面创建一个RequestDispatcher，并且调用RequestDispatcher.forward()。<br /><br />　　上面的列表说明了默认的RequestProcessor实现在处理请求时每一步作的工作，以及执行的顺序。正如你所看到的，RequestProcessor是非常灵活的，允许你通过设置＜controller＞元素的属性来配置它。举例来说，如果你的应用准备生成XML内容来代替HTML，你就可以通过设置controller元素的属性来通知Struts这些情况。<br /><br /><strong>创建你自己的RequestProcessor<br /><br /></strong>　　通过上面，我们了解到了RequestProcessor的默认实现是如何工作的。现在我们要演示一个例子来说明如何定制你自己的RequestProcessor。为了展示创建用户定制的RequestProcessor，我们将会让我们的示例实现下面两个业务需求：<br /><br />　　·我们想创建一个ContactImageAction类，它将生成图片而不是平常的HTML页面。<br /><br />　　·在每个请求处理之前，我们都想通过检查session中的userName属性来确定用户是否已经登陆。如果那个属性没有找到，我们会把用户重定向到登陆页面。<br /><br />　　我们将分两步实现这些业务需求。<br /><br />　　1、创建你的CustomRequestProcessor类，它将继承自RequestProcessor类，如下：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>public class CustomRequestProcessor<br />extends RequestProcessor {<br />　protected boolean processPreprocess (<br />　　HttpServletRequest request,HttpServletResponse response) {<br />　　　HttpSession session = request.getSession(false);<br />　　　//If user is trying to access login page<br />　　　// then don't check<br />　　　if( request.getServletPath().equals("/loginInput.do")<br />|| request.getServletPath().equals("/login.do") )<br />　　　　return true;<br />　　　//Check if userName attribute is there is session.<br />　　　//If so, it means user has allready logged in<br />　　　if( session != null &amp;&amp; session.getAttribute("userName") != null)<br />　　　　return true;<br />　　　else{<br />　　　　try{<br />　　　　　//If no redirect user to login Page<br />　　　　　request.getRequestDispatcher("/Login.jsp").forward(request,response);<br />　　　　}catch(Exception ex){<br />　　　　}<br />　　　}<br />　　　return false;<br />　　}<br /><br />　protected void processContent(HttpServletRequest request,<br />HttpServletResponse response) {<br />　　//Check if user is requesting ContactImageAction<br />　　// if yes then set image/gif as content type<br />　　if( request.getServletPath().equals("/contactimage.do")){<br />　　　response.setContentType("image/gif");<br />　　　return;<br />　　}<br />　　　super.processContent(request, response);<br />　}　<br />}</td></tr></tbody></table><br />　　在CustomRequestProcessor类的processPreprocess方法中，我们检查session的userName属性，如果没有找到，就将用户重定向到登陆页面。<br /><br />　　对于生成图片作为输出的需求，我们必须覆盖processContent方法，首先检查请求是否是/contactimage路径。如果是的话，我们就会将contentType设置为image/gif；否则设置为text/html。<br /><br />　　2、在你的struts-config.xml文件的＜action-mappint＞元素之后加入下面的文字，告诉Struts CustomRequestProcessor应当被用作RequestProcessor类：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>＜controller＞<br />＜set-property property="processorClass"value="com.sample.util.CustomRequestProcessor"/＞<br />＜/controller＞</td></tr></tbody></table><br />　　请注意，当你只有很少的action类需要生成非text/html类型的输出时，你覆写processContent()方法是OK的。如果不是这样子的话，你应该创建一个Struts的子应用来处理请求生成图片的Action，并为它们将contentType设置为image/gif。<br /><br />　　Struts的Tiles框架就是使用它自己的RequestProcessor来装饰Struts的输出。<br /><br />　　ActionServlet<br /><br />　　如果你查看你的Struts web应用的web.xml，你会看到这样的文字：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c9c9b8" border="1"><tbody><tr><td>＜web-app ＞<br />　＜servlet＞<br />　　＜servlet-name＞action=＜/servlet-name＞<br />　　＜servlet-class＞org.apache.struts.action.ActionServlet＜/servlet-class＞<br />　　＜!-- All your init-params go here--＞<br />　＜/servlet＞<br />　＜servlet-mapping＞<br />　　＜servlet-name＞action＜/servlet-name＞<br />　　＜url-pattern＞*.do＜/url-pattern＞<br />　＜/servlet-mapping＞<br />＜/web-app ＞</td></tr></tbody></table><br />　　这意味着ActionServlet负责处理你所有Struts的请求。你可以创建一个ActionServlet的子类，当应用启动，关闭，每个请求的时候做一些特定的事情。但是在继承ActionServlet类之前，你应该尽量创建一个PlugIn或RequestProcessor去解决你的问题。在Servlet1.1之前，Tiles框架是基于ActionServlet来修饰生成的响应。但是从1.1之后，它开始使用TilesRequestProcessor类。<br /><br />　　<b>总结</b><br /><br />　　决定开发你自己的MVC框架是一个非常大的决定，你必须要考虑开发和维护框架代码所花费的时间和资源。Struts是一个非常强大和稳定的框架，你可以修改它来满足你绝大多数的业务需求。<br /><br />　　但另一方面，也不要草率地做出扩展Struts的决定。如果你在RequestProcessor中写了一些性能比较低的代码，它将会在每次请求时执行，因而降低你整个应用的效率。而且还是有一些情况，开发自己的MVC框架要比扩展Struts好。<br /><script type="text/javascript"><!--
google_ad_client = "pub-3874079075380071";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-03-19: 3G-move, 三星, 摩托罗拉, 索爱, 诺基亚
google_ad_channel = "1730597762+1709572468+0778807001+6838187825+8377398382";
//--></script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script><img src ="http://www.cnitblog.com/tilan/aggbug/22675.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2007-02-05 11:46 <a href="http://www.cnitblog.com/tilan/articles/22675.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用 Spring 更好地处理 Struts 动作</title><link>http://www.cnitblog.com/tilan/articles/22674.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 05 Feb 2007 03:42:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/22674.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/22674.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/22674.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/22674.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/22674.html</trackback:ping><description><![CDATA[<blockquote>
				<i>Struts Recipes</i> 的合著者 George Franciscus 将介绍另一个重大的 Struts 整合窍门 —— 这次是将 Struts 应用程序导入 Spring 框架。请跟随 George，他将向您展示如何改变 Struts 动作，使得管理 Struts 动作就像管理 Spring beans 那样。结果是一个增强的 web 框架，这个框架可以方便地利用 Spring AOP 的优势。</blockquote>
		<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
		<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
		<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
		<p>您肯定已经听说过控制反转 (IOC) 设计模式，因为很长一段时间以来一直在流传关于它的信息。如果您在任何功能中使用过 Spring 框架，那么您就知道其原理的作用。在本文中，我利用这一原理把一个 Struts 应用程序注入 Spring 框架，您将亲身体会到 IOC 模式的强大。</p>
		<p>将一个 Struts 应用程序整合进 Spring 框架具有多方面的优点。首先，Spring 是为解决一些关于 JEE 的真实世界问题而设计的，比如复杂性、低性能和可测试性，等等。第二，Spring 框架包含一个 AOP 实现，允许您将面向方面技术应用于面向对象的代码。第三，一些人可能会说 Spring 框架只有<i>处理</i> Struts 比 Struts 处理自己好。但是这是观点问题，我演示三种将 Struts 应用程序整合到 Spring 框架的方法后，具体由您自己决定使用哪一种。</p>
		<p>我所演示的方法都是执行起来相对简单的，但是它们却具有明显不同的优点。我为每一种方法创建了一个独立而可用的例子，这样您就可以完全理解每种方法。请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#resources"><font color="#5c81a7">下载</font></a> 部分获得完整例子源代码。请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#resources"><font color="#5c81a7">参考资料</font></a>，下载 Struts MVC 和 Spring 框架。</p>
		<p>
				<a name="N10092">
						<span class="atitle">为什么 Spring 这么了不起？</span>
				</a>
		</p>
		<p>Spring 的创立者 Rod Johnson 以一种批判的眼光看待 Java™ 企业软件开发，并且提议很多企业难题都能够通过战略地使用 IOC 模式（也称作依赖注入）来解决。当 Rod 和一个具有奉献精神的开放源码开发者团队将这个理论应用于实践时，结果就产生了 Spring 框架。简言之，Spring 是一个轻型的容器，利用它可以使用一个外部 XML 配置文件方便地将对象连接在一起。每个对象都可以通过显示一个 JavaBean 属性收到一个到依赖对象的引用，留给您的简单任务就只是在一个 XML 配置文件中把它们连接好。</p>
		<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
				<tbody>
						<tr>
								<td width="10">
										<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" />
								</td>
								<td>
										<table cellspacing="0" cellpadding="5" width="100%" border="1">
												<tbody>
														<tr>
																<td bgcolor="#eeeeee">
																		<a name="N1009F">
																				<b>IOC 和 Spring</b>
																		</a>
																		<br />
																		<p>IOC 是一种使应用程序逻辑外在化的设计模式，所以它是被注入而不是被写入客户机代码中。将 IOC 与接口编程应用结合，就像 Spring 框架那样，产生了一种架构，这种架构能够减少客户机对特定实现逻辑的依赖。请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#resources"><font color="#5c81a7">参考资料</font></a> 了解更多关于 IOC 和 Spring 的信息。</p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<p>依赖注入是一个强大的特性，但是 Spring 框架能够提供更多特性。Spring 支持可插拔的事务管理器，可以给您的事务处理提供更广泛的选择范围。它集成了领先的持久性框架，并且提供一个一致的异常层次结构。Spring 还提供了一种使用面向方面代码代替正常的面向对象代码的简单机制。</p>
		<p>Spring AOP 允许您使用<i>拦截器</i> 在一个或多个执行点上拦截应用程序逻辑。加强应用程序在拦截器中的日志记录逻辑会产生一个更可读的、实用的代码基础，所以拦截器广泛用于日志记录。您很快就会看到，为了处理横切关注点，Spring AOP 发布了它自己的拦截器，您也可以编写您自己的拦截器。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N100B6">
						<span class="atitle">整合 Struts 和 Spring</span>
				</a>
		</p>
		<p>与 Struts 相似，Spring 可以作为一个 MVC 实现。这两种框架都具有自己的优点和缺点，尽管大部分人同意 Struts 在 MVC 方面仍然是最好的。很多开发团队已经学会在时间紧迫的时候利用 Struts 作为构造高品质软件的基础。Struts 具有如此大的推动力，以至于开发团队宁愿整合 Spring 框架的特性，而不愿意转换成 Spring MVC。没必要进行转换对您来说是一个好消息。Spring 架构允许您将 Struts 作为 Web 框架连接到基于 Spring 的业务和持久层。最后的结果就是现在一切条件都具备了。</p>
		<p>在接下来的小窍门中，您将会了解到三种将 Struts MVC 整合到 Spring 框架的方法。我将揭示每种方法的缺陷并且对比它们的优点。 一旦您了解到所有三种方法的作用，我将会向您展示一个令人兴奋的应用程序，这个程序使用的是这三种方法中我最喜欢的一种。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N100C2">
						<span class="atitle">三个小窍门</span>
				</a>
		</p>
		<p>接下来的每种整合技术（或者窍门）都有自己的优点和特点。我偏爱其中的一种，但是我知道这三种都能够加深您对 Struts 和 Spring 的理解。在处理各种不同情况的时候，这将给您提供一个广阔的选择范围。方法如下：</p>
		<ul>
				<li>使用 Spring 的 <code>ActionSupport</code> 类整合 Structs 
</li>
				<li>使用 Spring 的 <code>DelegatingRequestProcessor</code> 覆盖 Struts 的 <code>RequestProcessor </code></li>
				<li>将 Struts <code>Action</code> 管理委托给 Spring 框架 </li>
		</ul>
		<p>
				<a name="N100E7">
						<span class="smalltitle">
								<strong>
										<font face="Arial">装载应用程序环境</font>
								</strong>
						</span>
				</a>
		</p>
		<p>无论您使用哪种技术，都需要使用 Spring 的 <code>ContextLoaderPlugin</code> 为 Struts 的 <code>ActionServlet</code> 装载 Spring 应用程序环境。就像添加任何其他插件一样，简单地向您的 struts-config.xml 文件添加该插件，如下所示：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">&lt;plug-in className=
  "org.springframework.web.struts.ContextLoaderPlugIn"&gt;
    &lt;set-property property=
      "contextConfigLocation" value="/WEB-INF/beans.xml"/&gt;
 &lt;/plug-in&gt;
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>前面已经提到过，在 <a href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#download"><font color="#5c81a7">下载</font></a> 部分，您能够找到这三个完全可使用的例子的完整源代码。每个例子都为一个书籍搜索应用程序提供一种不同的 Struts 和 Spring 的整合方法。您可以在这里看到例子的要点，但是您也可以下载应用程序以查看所有的细节。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10104">
						<span class="atitle">窍门 1. 使用 Spring 的 ActionSupport</span>
				</a>
		</p>
		<p>手动创建一个 Spring 环境是一种整合 Struts 和 Spring 的最直观的方式。为了使它变得更简单，Spring 提供了一些帮助。为了方便地获得 Spring 环境，<code>org.springframework.web.struts.ActionSupport</code> 类提供了一个 <code>getWebApplicationContext()</code> 方法。您所做的只是从 Spring 的 <code>ActionSupport</code> 而不是 Struts <code>Action</code> 类扩展您的动作，如清单 1 所示：</p>
		<br />
		<br />
		<a name="N10122">
				<b>清单 1. 使用 ActionSupport 整合 Struts</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">package ca.nexcel.books.actions;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;
import org.springframework.context.ApplicationContext;
import org.springframework.web.struts.ActionSupport;
import ca.nexcel.books.beans.Book;
import ca.nexcel.books.business.BookService;
public class SearchSubmit extends ActionSupport {   <span class="boldcode"><strong>|(1)</strong></span>
  public ActionForward execute(
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response)
    throws IOException, ServletException {
    DynaActionForm searchForm = (DynaActionForm) form;
    String isbn = (String) searchForm.get("isbn");
		
    //the old fashion way
    //BookService bookService = new BookServiceImpl();
		
    ApplicationContext ctx = 
      getWebApplicationContext();    <span class="boldcode"><strong>|(2)</strong></span>
    BookService bookService = 
      (BookService) ctx.getBean("bookService");   <span class="boldcode"><strong>|(3)</strong></span>
        
  Book book = bookService.read(isbn.trim());
    if (null == book) {
      ActionErrors errors = new ActionErrors();
      errors.add(ActionErrors.GLOBAL_ERROR,new ActionError
        ("message.notfound"));
      saveErrors(request, errors);
      return mapping.findForward("failure") ;
  }
    request.setAttribute("book", book);
    return mapping.findForward("success");
  }
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>让我们快速思考一下这里到底发生了什么。在 (1) 处，我通过从 Spring 的 <code>ActionSupport</code> 类而不是 Struts 的 <code>Action</code> 类进行扩展，创建了一个新的 <code>Action</code>。在 (2) 处，我使用 <code>getWebApplicationContext()</code> 方法获得一个 <code>ApplicationContext</code>。为了获得业务服务，我使用在 (2) 处获得的环境在 (3) 处查找一个 Spring bean。</p>
		<p>这种技术很简单并且易于理解。不幸的是，它将 Struts 动作与 Spring 框架耦合在一起。如果您想替换掉 Spring，那么您必须重写代码。并且，由于 Struts 动作不在 Spring 的控制之下，所以它不能获得 Spring AOP 的优势。当使用多重独立的 Spring 环境时，这种技术可能有用，但是在大多数情况下，这种方法不如另外两种方法合适。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N1014C">
						<span class="atitle">窍门 2. 覆盖 RequestProcessor</span>
				</a>
		</p>
		<p>将 Spring 从 Struts 动作中分离是一个更巧妙的设计选择。分离的一种方法是使用 <code>org.springframework.web.struts.DelegatingRequestProcessor</code> 类来覆盖 Struts 的 <code>RequestProcessor</code> 处理程序，如清单 2 所示：</p>
		<br />
		<br />
		<a name="N10162">
				<b>清单 2. 通过 Spring 的 DelegatingRequestProcessor 进行整合</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
&lt;!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"&gt;
&lt;struts-config&gt;
 &lt;form-beans&gt;
    &lt;form-bean name="searchForm" 
      type="org.apache.struts.validator.DynaValidatorForm"&gt;
               &lt;form-property name="isbn"    type="java.lang.String"/&gt;
    &lt;/form-bean&gt;
  
  &lt;/form-beans&gt;
 &lt;global-forwards type="org.apache.struts.action.ActionForward"&gt;
     &lt;forward   name="welcome"                path="/welcome.do"/&gt;
     &lt;forward   name="searchEntry"            path="/searchEntry.do"/&gt;
     &lt;forward   name="searchSubmit"           path="/searchSubmit.do"/&gt;
 &lt;/global-forwards&gt;
 &lt;action-mappings&gt;
    &lt;action    path="/welcome" forward="/WEB-INF/pages/welcome.htm"/&gt;
    &lt;action    path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/&gt;
    &lt;action    path="/searchSubmit" 
               type="ca.nexcel.books.actions.SearchSubmit"
               input="/searchEntry.do"
               validate="true"
               name="searchForm"&gt;
              &lt;forward name="success" path="/WEB-INF/pages/detail.jsp"/&gt;
              &lt;forward name="failure" path="/WEB-INF/pages/search.jsp"/&gt;
    &lt;/action&gt;  
 &lt;/action-mappings&gt;
 &lt;message-resources parameter="ApplicationResources"/&gt;
 &lt;controller processorClass="org.springframework.web.struts.
   DelegatingRequestProcessor"/&gt; <span class="boldcode"><strong>|(1)</strong></span>
 &lt;plug-in className="org.apache.struts.validator.ValidatorPlugIn"&gt;
    &lt;set-property property="pathnames" 
      value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/&gt;
 &lt;/plug-in&gt;
 &lt;plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"&gt;
    &lt;set-property property="csntextConfigLocation" value="/WEB-INF/beans.xml"/&gt;
 &lt;/plug-in&gt;
 
&lt;/struts-config&gt;
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>我利用了 <code>&lt;controller&gt;</code> 标记来用 <code>DelegatingRequestProcessor</code> 覆盖默认的 Struts <code>RequestProcessor</code>。下一步是在我的 Spring 配置文件中注册该动作，如清单 3 所示：</p>
		<br />
		<br />
		<a name="N1017F">
				<b>清单 3. 在 Spring 配置文件中注册一个动作</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
  "http://www.springframework.org/dtd/spring-beans.dtd"&gt;
&lt;beans&gt;
  &lt;bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/&gt;
  &lt;bean name="/searchSubmit" 
    class="ca.nexcel.books.actions.SearchSubmit"&gt; <span class="boldcode"><strong>|(1)</strong></span>
     &lt;property name="bookService"&gt;
        &lt;ref bean="bookService"/&gt;
     &lt;/property&gt;
  &lt;/bean&gt;
&lt;/beans&gt;
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>注意：在 (1) 处，我使用名称属性注册了一个 bean，以匹配 struts-config 动作映射名称。<code>SearchSubmit</code> 动作揭示了一个 JavaBean 属性，允许 Spring 在运行时填充属性，如清单 4 所示：</p>
		<br />
		<br />
		<a name="N10194">
				<b>清单 4. 具有 JavaBean 属性的 Struts 动作</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">package ca.nexcel.books.actions;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;
import ca.nexcel.books.beans.Book;
import ca.nexcel.books.business.BookService;
public class SearchSubmit extends Action {
	
  private BookService bookService;
  public BookService getBookService() {
    return bookService;
  }
  public void setBookService(BookService bookService) { <span class="boldcode"><strong>| (1)</strong></span>
    this.bookService = bookService; 
  } 
  public ActionForward execute(
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response)
    throws IOException, ServletException {
    DynaActionForm searchForm = (DynaActionForm) form;
    String isbn = (String) searchForm.get("isbn");
		
  Book book = getBookService().read(isbn.trim());  <span class="boldcode"><strong>|(2)</strong></span>
    if (null == book) {
      ActionErrors errors = new ActionErrors();
      errors.add(ActionErrors.GLOBAL_ERROR,new ActionError("message.notfound"));
      saveErrors(request, errors);
      return mapping.findForward("failure") ;
  }
      request.setAttribute("book", book);
      return mapping.findForward("success");
  }
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在清单 4 中，您可以了解到如何创建 Struts 动作。在 (1) 处，我创建了一个 JavaBean 属性。<code>DelegatingRequestProcessor</code>自动地配置这种属性。这种设计使 Struts 动作并不知道它正被 Spring 管理，并且使您能够利用 Sping 的动作管理框架的所有优点。由于您的 Struts 动作注意不到 Spring 的存在，所以您不需要重写您的 Struts 代码就可以使用其他控制反转容器来替换掉 Spring。</p>
		<p>
				<code>DelegatingRequestProcessor</code> 方法的确比第一种方法好，但是仍然存在一些问题。如果您使用一个不同的 <code>RequestProcessor</code>，则需要手动整合 Spring 的 <code>DelegatingRequestProcessor</code>。添加的代码会造成维护的麻烦并且将来会降低您的应用程序的灵活性。此外，还有过一些使用一系列命令来代替 Struts <code>RequestProcessor</code> 的传闻。 这种改变将会对这种解决方法的使用寿命造成负面的影响。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N101BA">
						<span class="atitle">窍门 3. 将动作管理委托给 Spring</span>
				</a>
		</p>
		<p>一个更好的解决方法是将 Strut 动作管理委托给 Spring。您可以通过在 <code>struts-config</code> 动作映射中注册一个代理来实现。代理负责在 Spring 环境中查找 Struts 动作。由于动作在 Spring 的控制之下，所以它可以填充动作的 JavaBean 属性，并为应用诸如 Spring 的 AOP 拦截器之类的特性带来了可能。 </p>
		<p>清单 5 中的 <code>Action</code> 类与清单 4 中的相同。但是 struts-config 有一些不同：</p>
		<br />
		<br />
		<a name="N101D3">
				<b>清单 5. Spring 整合的委托方法</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
&lt;!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"&gt;
&lt;struts-config&gt;
 &lt;form-beans&gt;
    &lt;form-bean name="searchForm" 
      type="org.apache.struts.validator.DynaValidatorForm"&gt;
               &lt;form-property name="isbn"    type="java.lang.String"/&gt;
    &lt;/form-bean&gt;
  
  &lt;/form-beans&gt;
 &lt;global-forwards type="org.apache.struts.action.ActionForward"&gt;
     &lt;forward   name="welcome"                path="/welcome.do"/&gt;
     &lt;forward   name="searchEntry"            path="/searchEntry.do"/&gt;
     &lt;forward   name="searchSubmit"           path="/searchSubmit.do"/&gt;
 &lt;/global-forwards&gt;
 &lt;action-mappings&gt;
    &lt;action    path="/welcome" forward="/WEB-INF/pages/welcome.htm"/&gt;
    &lt;action    path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/&gt;
    &lt;action    path="/searchSubmit" 
             type="org.springframework.web.struts.DelegatingActionProxy" <span class="boldcode"><strong>|(1)</strong></span>
             input="/searchEntry.do"
             validate="true"
             name="searchForm"&gt;
             &lt;forward name="success" path="/WEB-INF/pages/detail.jsp"/&gt;
             &lt;forward name="failure" path="/WEB-INF/pages/search.jsp"/&gt;
    &lt;/action&gt;  
 &lt;/action-mappings&gt;
 &lt;message-resources parameter="ApplicationResources"/&gt;
 &lt;plug-in className="org.apache.struts.validator.ValidatorPlugIn"&gt;
    &lt;set-property 
    property="pathnames" 
    value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/&gt;
 &lt;/plug-in&gt;
 &lt;plug-in 
    className="org.springframework.web.struts.ContextLoaderPlugIn"&gt;
    &lt;set-property property="contextConfigLocation" value="/WEB-INF/beans.xml"/&gt;
 &lt;/plug-in&gt;
 
&lt;/struts-config&gt;
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>清单 5 是一个典型的 struts-config.xml 文件，只有一个小小的差别。它注册 Spring 代理类的名称，而不是声明动作的类名，如（1）处所示。DelegatingActionProxy 类使用动作映射名称查找 Spring 环境中的动作。这就是我们使用 <code>ContextLoaderPlugIn</code> 声明的环境。</p>
		<p>将一个 Struts 动作注册为一个 Spring bean 是非常直观的，如清单 6 所示。我利用动作映射使用 <code>&lt;bean&gt;</code> 标记的名称属性（在这个例子中是 "<code>/searchSubmit</code>"）简单地创建了一个 bean。这个动作的 JavaBean 属性像任何 Spring bean 一样被填充： </p>
		<br />
		<br />
		<a name="N101F3">
				<b>清单 6. 在 Spring 环境中注册一个 Struts 动作</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
 "http://www.springframework.org/dtd/spring-beans.dtd"&gt;
&lt;beans&gt;
  &lt;bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/&gt;
  &lt;bean name="/searchSubmit"   
        class="ca.nexcel.books.actions.SearchSubmit"&gt;
     &lt;property name="bookService"&gt;
        &lt;ref bean="bookService"/&gt;
     &lt;/property&gt;
  &lt;/bean&gt;
&lt;/beans&gt;
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="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.ibm.com/developerworks/cn/java/j-sr2.html#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N101FA">
						<span class="atitle">动作委托的优点</span>
				</a>
		</p>
		<p>动作委托解决方法是这三种方法中最好的。Struts 动作不了解 Spring，不对代码作任何改变就可用于非 Spring 应用程序中。<code>RequestProcessor</code> 的改变不会影响它，并且它可以利用 Spring AOP 特性的优点。 </p>
		<p>动作委托的优点不止如此。一旦让 Spring 控制您的 Struts 动作，您就可以使用 Spring 给动作补充更强的活力。例如，没有 Spring 的话，所有的 Struts 动作都必须是线程安全的。如果您设置 <code>&lt;bean&gt;</code> 标记的 singleton 属性为“false”，那么不管用何种方法，您的应用程序都将在每一个请求上有一个新生成的动作对象。您可能不需要这种特性，但是把它放在您的工具箱中也很好。您也可以利用 Spring 的生命周期方法。例如，当实例化 Struts 动作时，<code>&lt;bean&gt;</code> 标记的 init-method 属性被用于运行一个方法。类似地，在从容器中删除 bean 之前，destroy-method 属性执行一个方法。这些方法是管理昂贵对象的好办法，它们以一种与 Servlet 生命周期相同的方式进行管理。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10213">
						<span class="atitle">拦截 Struts</span>
				</a>
		</p>
		<p>前面提到过，通过将 Struts 动作委托给 Spring 框架而整合 Struts 和 Spring 的一个主要的优点是：您可以将 Spring 的 AOP 拦截器应用于您的 Struts 动作。通过将 Spring 拦截器应用于 Struts 动作，您可以用最小的代价处理横切关注点。</p>
		<p>虽然 Spring 提供很多内置拦截器，但是我将向您展示如何创建自己的拦截器并把它应用于一个 Struts 动作。为了使用拦截器，您需要做三件事：</p>
		<ol>
				<li>创建拦截器。 
</li>
				<li>注册拦截器。 
</li>
				<li>声明在何处拦截代码。 </li>
		</ol>
		<p>这看起来非常简单的几句话却非常强大。例如，在清单 7 中，我为 Struts 动作创建了一个日志记录拦截器。 这个拦截器在每个方法调用之前打印一句话：</p>
		<br />
		<br />
		<a name="N10233">
				<b>清单 7. 一个简单的日志记录拦截器</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">package ca.nexcel.books.interceptors;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class LoggingInterceptor implements MethodBeforeAdvice {
   public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("logging before!");
    }
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这个拦截器非常简单。<code>before()</code> 方法在拦截点中每个方法之前运行。在本例中，它打印出一句话，其实它可以做您想做的任何事。下一步就是在 Spring 配置文件中注册这个拦截器，如清单 8 所示：</p>
		<br />
		<br />
		<a name="N10245">
				<b>清单 8. 在 Spring 配置文件中注册拦截器</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
  "http://www.springframework.org/dtd/spring-beans.dtd"&gt;
&lt;beans&gt;
  &lt;bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/&gt;
  &lt;bean name="/searchSubmit" 
        class="ca.nexcel.books.actions.SearchSubmit"&gt;
     &lt;property name="bookService"&gt;
        &lt;ref bean="bookService"/&gt;
     &lt;/property&gt;
  &lt;/bean&gt;
  &lt;!--  Interceptors --&gt; 
  &lt;bean name="logger"    
    class="ca.nexcel.books.interceptors.LoggingInterceptor"/&gt; <span class="boldcode"><strong>|(1)</strong></span>
  &lt;!-- AutoProxies --&gt;
  &lt;bean name="loggingAutoProxy" 
        class="org.springframework.aop.framework.autoproxy.
          BeanNameAutoProxyCreator"&gt; <span class="boldcode"><strong>|(2)</strong></span>
    &lt;property name="beanNames"&gt;
          &lt;value&gt;/searchSubmit&lt;/valuesgt; <span class="boldcode"><strong>|(3)</strong></span>
    &lt;/property&gt;
    &lt;property name="interceptorNames"&gt;
        &lt;list&gt;
          &lt;value&gt;logger&lt;/value&gt; <span class="boldcode"><strong>|(4)</strong></span>
        &lt;/list&gt;
    &lt;/property&gt;
   &lt;/bean&gt;
&lt;/beans&gt;
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>您可能已经注意到了，清单 8 扩展了 <a href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#code6"><font color="#5c81a7">清单 6</font></a> 中所示的应用程序以包含一个拦截器。具体细节如下：</p>
		<ul>
				<li>在 (1) 处，我注册了这个拦截器。 
</li>
				<li>在 (2) 处，我创建了一个 bean 名称自动代理，它描述如何应用拦截器。还有其他的方法定义拦截点，但是这种方法常见而简便。 
</li>
				<li>在 (3) 处，我将 Struts 动作注册为将被拦截的 bean。如果您想要拦截其他的 Struts 动作，则只需要在 "beanNames" 下面创建附加的 <code>&lt;value&gt;</code> 标记。 
</li>
				<li>在 (4) 处，当拦截发生时，我执行了在 (1) 处创建的拦截器 bean 的名称。这里列出的所有拦截器都应用于“beanNames”。 </li>
		</ul>
		<p>就是这样。就像这个例子所展示的，将您的 Struts 动作置于 Spring 框架的控制之下，为处理您的 Struts 应用程序提供了一系列全新的选择。在本例中，使用动作委托可以轻松地利用 Spring 拦截器提高 Struts 应用程序中的日志记录能力。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10275">
						<span class="atitle">结束语</span>
				</a>
		</p>
		<p>在本文中，您已经学习了将 Struts 动作整合到 Spring 框架中的三种窍门。使用 Spring 的 <code>ActionSupport</code> 来整合 Struts（第一种窍门中就是这样做的）简单而快捷，但是会将 Struts 动作与 Spring 框架耦合在一起。如果您需要将应用程序移植到一个不同的框架，则需要重写代码。第二种解决方法通过委托 <code>RequestProcessor</code> 巧妙地解开代码的耦合，但是它的可扩展性不强，并且当 Struts 的 <code>RequestProcessor</code> 变成一系列命令时，这种方法就持续不了很长时间。第三种方法是这三种方法中最好的：将 Struts 动作委托给 Spring 框架可以使代码解耦，从而使您可以在您的 Struts 应用程序中利用 Spring 的特性（比如日志记录拦截器）。</p>
		<p>三种 Struts-Spring 整合窍门中的每一种都被实现成一个完整可用的应用程序。请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-sr2.html#download"><font color="#5c81a7">下载</font></a> 部分仔细研究它们。</p>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="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.ibm.com/developerworks/cn/java/j-sr2.html#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<span class="atitle">
						<a name="download">下载</a>
				</span>
		</p>
		<table class="data-table-1" cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<th scope="col">描述</th>
								<th scope="col">名字</th>
								<th scope="col" align="right">大小</th>
								<th scope="col">下载方法</th>
						</tr>
						<tr>
								<th class="tb-row" scope="row">ActionSupport sample code</th>
								<td nowrap="">j-sr2-actionsupport.zip</td>
								<td nowrap="" align="right">5 MB</td>
								<td nowrap=""> <a class="fbox" href="ftp://www6.software.ibm.com/software/developer/library/j-sr2-actionsupport.zip"><b><font color="#5c81a7">FTP</font></b></a></td>
						</tr>
						<tr>
								<th class="tb-row" scope="row">RequestProcessor sample code</th>
								<td nowrap="">j-sr2-requestprocessor.zip</td>
								<td nowrap="" align="right">5 MB</td>
								<td nowrap=""> <a class="fbox" href="ftp://www6.software.ibm.com/software/developer/library/j-sr2-requestprocessor.zip"><b><font color="#5c81a7">FTP</font></b></a></td>
						</tr>
						<tr>
								<th class="tb-row" scope="row">Delegate sample code</th>
								<td nowrap="">j-sr2-delegate.zip</td>
								<td nowrap="" align="right">5 MB</td>
								<td nowrap=""> <a class="fbox" href="ftp://www6.software.ibm.com/software/developer/library/j-sr2-delegate.zip"><b><font color="#5c81a7">FTP</font></b></a></td>
						</tr>
				</tbody>
		</table>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td colspan="5">
										<font color="#5c81a7">
												<img height="12" alt="" src="http://www.ibm.com/i/c.gif" width="12" border="0" />
										</font>
								</td>
						</tr>
						<tr>
								<td>
										<font color="#5c81a7">
												<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/fw.gif" width="16" />
										</font>
								</td>
								<td>
										<a class="fbox" href="http://www.ibm.com/developerworks/cn/whichmethod.html">
												<font color="#5c81a7">关于下载方法的信息</font>
										</a>
								</td>
								<td>
										<font color="#5c81a7">
												<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="50" />
										</font>
								</td>
								<td>
										<font color="#5c81a7">
												<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/sout.gif" width="16" />
										</font>
								</td>
								<td>
										<a class="fbox" href="http://www.adobe.com/products/acrobat/readstep2.html">
												<font color="#5c81a7">Get Adobe® Reader®</font>
										</a>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="resources">
						<span class="atitle">参考资料 </span>
				</a>
		</p>
		<b>学习</b>
		<br />
		<ul>
				<li>
						<a href="http://www.ibm.com/developerworks/library/wa-spring1/?S_TACT=105AGX52&amp;S_CMP=cn-a-j">
								<font color="#5c81a7">Spring 系列</font>
						</a>（Naveen Balani，developerWorks，2005 年 6 到 9 月）：对 Spring 框架的组件和特性（包括 Spring AOP）的分为四部分的介绍。<br /><br /></li>
				<li>
						<a href="http://www.ibm.com/developerworks/web/library/wa-struts/index.html?S_TACT=105AGX52&amp;S_CMP=cn-a-j">
								<font color="#5c81a7">Best practices in Struts development</font>
						</a>（Palaniyappan Thiagarajan 和 Pagadala Suresh，developerWorks，2004 年 6 月）：概述了 Struts <code>Action</code> 组件。<br /><br /></li>
				<li>
						<a href="http://www.martinfowler.com/articles/injection.html">
								<font color="#5c81a7">Inversion of Control Containers and the Dependency Injection pattern</font>
						</a>（Martin Fowler，2004 年 1 月）：介绍 IOC 容器。<br /><br /></li>
				<li>
						<a href="http://www.theserverside.com/articles/article.tss?l=SpringFramework">
								<font color="#5c81a7">Introduction to the Spring Framework</font>
						</a>（Rod Johnson，The ServerSide，2005 年 5 月）：Spring 的创立者解释了为什么 Spring 框架很重要。<br /><br /></li>
				<li>
						<a href="http://www.manning.com/franciscus">
								<font color="#5c81a7">Struts Recipes</font>
						</a>（George Franciscus 和 Danilo Gurovich；Manning，2004 年）：关于 Struts 窍门和最佳实践的一个流行的纲要。<br /><br /></li>
				<li>
						<a href="http://www.manning.com/husted">
								<font color="#5c81a7">Struts In Action</font>
						</a>（Ted Husted、Cedric Dumoulin、George Franciscus、David Winterfeldt；Manning，2002 年）：针对专业 Struts 开发人员的综合资源。<br /><br /></li>
				<li>
						<a href="http://www.manning.com/walls2">
								<font color="#5c81a7">Spring In Action</font>
						</a>（Craig Walls 和 Ryan Breidenbach；Manning，2005 年）：一本用于开发人员的 Spring 资源书籍。<br /><br /></li>
				<li>
						<a href="http://www.ibm.com/developerworks/java/?S_TACT=105AGX52&amp;S_CMP=cn-a-j">
								<font color="#5c81a7">Java 技术专区</font>
						</a>：找到关于 Java 编程各个方面的文章。<br /><br /></li>
		</ul>
		<br />
		<b>获得产品和技术</b>
		<br />
		<ul>
				<li>
						<a href="http://struts.apache.org/">
								<font color="#5c81a7">Struts 框架</font>
						</a>：Apache Software Foundation 的一个项目。<br /><br /></li>
				<li>
						<a href="http://www.springframework.org/">
								<font color="#5c81a7">Spring 框架</font>
						</a>：一个分层的 Java 企业应用程序框架。<br /><br /></li>
		</ul>
		<br />
		<b>讨论</b>
		<br />
		<ul>
				<li>
						<a href="http://www.ibm.com/developerworks/community/">
								<font color="#5c81a7">参与论坛讨论</font>
						</a>。<br /><br /></li>
				<li>
						<a href="http://www.ibm.com/developerworks/blogs/?S_TACT=105AGX52&amp;S_CMP=cn-a-j">
								<font color="#5c81a7">developerWorks blogs</font>
						</a>：加入 developerWorks 社区。 </li>
		</ul>
		<br />
		<br />
		<p>
				<a name="author">
						<span class="atitle">关于作者</span>
				</a>
		</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td colspan="3">
										<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
								</td>
						</tr>
						<tr valign="top" align="left">
								<td>
										<p>
										</p>
								</td>
								<td>
										<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" />
								</td>
								<td width="100%">
										<p>George Franciscus 是 Java 公司的一名顾问和 Struts 方面的权威。他是 Manning 出版的 <a href="http://www.manning.com/franciscus"><font color="#5c81a7">Struts Recipes</font></a> 和 <a href="http://www.manning.com/husted"><font color="#5c81a7">Struts in Action</font></a> 的合著者。 George 通过 <a href="http://www.nexcel.ca/"><font color="#5c81a7">nexcel.ca</font></a> 提供有关技术和管理方面的咨询服务。</p>
								</td>
						</tr>
				</tbody>
		</table>
		<script type="text/javascript">
				<!--
google_ad_client = "pub-3874079075380071";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-03-19: 3G-move, 三星, 摩托罗拉, 索爱, 诺基亚
google_ad_channel = "1730597762+1709572468+0778807001+6838187825+8377398382";
//-->
		</script>
		<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
		</script><img src ="http://www.cnitblog.com/tilan/aggbug/22674.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2007-02-05 11:42 <a href="http://www.cnitblog.com/tilan/articles/22674.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 在ant中使用cvs功能自动完成每日构建</title><link>http://www.cnitblog.com/tilan/articles/21907.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Tue, 16 Jan 2007 02:58:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21907.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21907.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21907.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21907.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21907.html</trackback:ping><description><![CDATA[<p>实现的主要功能是：自动从cvs中check out模块，然后编译，把编译后的class打成jar，再commit到cvs服务器的指定位置。 <br />build.xml <br /></p>
		<div class="code_title">代码</div>
		<div class="code_div">
				<div class="dp-highlighter">
						<div class="bar">
						</div>
						<ol class="dp-xml">
								<li class="alt">
										<span>
												<span class="tag">&lt;?</span>
												<span class="tag-name">xml</span>
												<span> </span>
												<span class="attribute">version</span>
												<span>=</span>
												<span class="attribute-value">"1.0"</span>
												<span class="tag">?&gt;</span>
												<span>  </span>
										</span>
								</li>
								<li class="">
										<span>
										</span>
										<span class="tag">&lt;</span>
										<span class="tag-name">project</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"gnt Auto build"</span>
										<span> </span>
										<span class="attribute">basedir</span>
										<span>=</span>
										<span class="attribute-value">"."</span>
										<span> </span>
										<span class="attribute">default</span>
										<span>=</span>
										<span class="attribute-value">"build"</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="comments">&lt;!-- The CVSROOT value --&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"cvsroot"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">":pserver:dhf:@192.168.0.200:D:/cvs_repository_z"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"cvs.password"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">""</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>     </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt.dir"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">"${basedir}/ywzcpt"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt.module.name"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>       </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"zfyw.dir"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">"${basedir}/zfyw"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"zfyw.module.name"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">"zfyw"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"external.dir"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">"${basedir}/external"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"external.module.name"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">"external"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>       </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"cvs-op"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">"co "</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="comments">&lt;!-- Initializing --&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">target</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"init"</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">tstamp</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>            </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">format</span>
										<span> </span>
										<span class="attribute">property</span>
										<span>=</span>
										<span class="attribute-value">"today"</span>
										<span> </span>
										<span class="attribute">pattern</span>
										<span>=</span>
										<span class="attribute-value">"yyyy-MM-dd hh:mm:ss"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">tstamp</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"${today}"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">target</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>       </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">target</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"prepare"</span>
										<span> </span>
										<span class="attribute">depends</span>
										<span>=</span>
										<span class="attribute-value">"init"</span>
										<span> </span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">cvspass</span>
										<span> </span>
										<span class="attribute">cvsroot</span>
										<span>=</span>
										<span class="attribute-value">"${cvsroot}"</span>
										<span> </span>
										<span class="attribute">password</span>
										<span>=</span>
										<span class="attribute-value">"${cvs.password}"</span>
										<span> </span>
										<span class="attribute">passfile</span>
										<span>=</span>
										<span class="attribute-value">"ant-cvs.cvspass"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">target</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>       </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">target</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"external-check-out"</span>
										<span> </span>
										<span class="attribute">depends</span>
										<span>=</span>
										<span class="attribute-value">"prepare"</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">cvs</span>
										<span> </span>
										<span class="attribute">cvsRoot</span>
										<span>=</span>
										<span class="attribute-value">"${cvsroot}"</span>
										<span> </span>
										<span class="attribute">package</span>
										<span>=</span>
										<span class="attribute-value">"${external.module.name}"</span>
										<span>    </span>
								</li>
								<li class="">
										<span>             </span>
										<span class="attribute">passfile</span>
										<span>=</span>
										<span class="attribute-value">"ant-cvs.cvspass"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">target</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>       </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="comments">&lt;!-- Retrieve the ywzcpt module --&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">target</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt-check-out"</span>
										<span> </span>
										<span class="attribute">depends</span>
										<span>=</span>
										<span class="attribute-value">"external-check-out"</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">delete</span>
										<span> </span>
										<span class="attribute">dir</span>
										<span>=</span>
										<span class="attribute-value">"${ywzcpt.module.name}"</span>
										<span class="tag">/&gt;</span>
										<span>    </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">cvs</span>
										<span> </span>
										<span class="attribute">cvsRoot</span>
										<span>=</span>
										<span class="attribute-value">"${cvsroot}"</span>
										<span> </span>
										<span class="attribute">package</span>
										<span>=</span>
										<span class="attribute-value">"${ywzcpt.module.name}"</span>
										<span>    </span>
								</li>
								<li class="alt">
										<span>             </span>
										<span class="attribute">passfile</span>
										<span>=</span>
										<span class="attribute-value">"ant-cvs.cvspass"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">target</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">target</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"zfyw-check-out"</span>
										<span> </span>
										<span class="attribute">depends</span>
										<span>=</span>
										<span class="attribute-value">"external-check-out"</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">delete</span>
										<span> </span>
										<span class="attribute">dir</span>
										<span>=</span>
										<span class="attribute-value">"${zfyw.module.name}"</span>
										<span class="tag">/&gt;</span>
										<span>    </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">cvs</span>
										<span> </span>
										<span class="attribute">cvsRoot</span>
										<span>=</span>
										<span class="attribute-value">"${cvsroot}"</span>
										<span> </span>
										<span class="attribute">package</span>
										<span>=</span>
										<span class="attribute-value">"${zfyw.module.name}"</span>
										<span>    </span>
								</li>
								<li class="alt">
										<span>             </span>
										<span class="attribute">passfile</span>
										<span>=</span>
										<span class="attribute-value">"ant-cvs.cvspass"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">target</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="comments">&lt;!-- cvs checkout --&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>       </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">target</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"check-out"</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">antcall</span>
										<span> </span>
										<span class="attribute">target</span>
										<span>=</span>
										<span class="attribute-value">"external-check-out"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">antcall</span>
										<span> </span>
										<span class="attribute">target</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt-check-out"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">antcall</span>
										<span> </span>
										<span class="attribute">target</span>
										<span>=</span>
										<span class="attribute-value">"zfyw-check-out"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">target</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>       </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="comments">&lt;!-- build XSP framework --&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">target</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"build"</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+=============================================+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"|     Start Building GNT for compilation      |"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+=============================================+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>           </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">antcall</span>
										<span> </span>
										<span class="attribute">target</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt-build"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>           </span>
								</li>
								<li class="">
										<span>           </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+=============================================+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"|      End Building GNT for compilation       |"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+=============================================+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>           </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">target</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>       </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">target</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt-build"</span>
										<span> </span>
										<span class="attribute">depends</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt-check-out"</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+---------------------------------------------+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"|    Start Building ywzcpt for compilation    |"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+---------------------------------------------+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">ant</span>
										<span> </span>
										<span class="attribute">antfile</span>
										<span>=</span>
										<span class="attribute-value">"build.xml"</span>
										<span> </span>
										<span class="attribute">dir</span>
										<span>=</span>
										<span class="attribute-value">"${ywzcpt.module.name}"</span>
										<span> </span>
										<span class="attribute">output</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt.log"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt.add"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">"add ./build/log/*.log ./build/*.jar ./build/*.war"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"ywzcpt.commit"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>="commit -m '${today}' ./build/log/*.log ./build/*.jar    </span>
								</li>
								<li class="">
										<span>  </span>
								</li>
								<li class="alt">
										<span>./build/*.war"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>           </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">ant</span>
										<span> </span>
										<span class="attribute">antfile</span>
										<span>=</span>
										<span class="attribute-value">"build.xml"</span>
										<span> </span>
										<span class="attribute">dir</span>
										<span>=</span>
										<span class="attribute-value">"${ywzcpt.module.name}"</span>
										<span> </span>
										<span class="attribute">target</span>
										<span>=</span>
										<span class="attribute-value">"commit-build"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>           </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+---------------------------------------------+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+     End Building ywzcpt for compilation     |"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+---------------------------------------------+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">target</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>       </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">target</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"zfyw-build"</span>
										<span> </span>
										<span class="attribute">depends</span>
										<span>=</span>
										<span class="attribute-value">"zfyw-check-out, ywzcpt-build"</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+---------------------------------------------+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"|    Start Building ywzcpt for compilation    |"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+---------------------------------------------+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">ant</span>
										<span> </span>
										<span class="attribute">antfile</span>
										<span>=</span>
										<span class="attribute-value">"build.xml"</span>
										<span> </span>
										<span class="attribute">dir</span>
										<span>=</span>
										<span class="attribute-value">"${zfyw.module.name}"</span>
										<span> </span>
										<span class="attribute">output</span>
										<span>=</span>
										<span class="attribute-value">"zfyw.log"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"zfyw.add"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>=</span>
										<span class="attribute-value">"add ./build/log/*.log ./build/*.jar ./build/*.war"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">property</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"zfyw.commit"</span>
										<span> </span>
										<span class="attribute">value</span>
										<span>="commit -m '${today}' ./build/log/*.log ./build/*.jar    </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>./build/*.war"</span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>           </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">ant</span>
										<span> </span>
										<span class="attribute">antfile</span>
										<span>=</span>
										<span class="attribute-value">"build.xml"</span>
										<span> </span>
										<span class="attribute">dir</span>
										<span>=</span>
										<span class="attribute-value">"${zfyw.module.name}"</span>
										<span> </span>
										<span class="attribute">target</span>
										<span>=</span>
										<span class="attribute-value">"commit-build"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>           </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+---------------------------------------------+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+     End Building ywzcpt for compilation     |"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">echo</span>
										<span> </span>
										<span class="attribute">message</span>
										<span>=</span>
										<span class="attribute-value">"+---------------------------------------------+"</span>
										<span> </span>
										<span class="tag">/&gt;</span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">target</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>  </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">target</span>
										<span> </span>
										<span class="attribute">name</span>
										<span>=</span>
										<span class="attribute-value">"clean"</span>
										<span> </span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">delete</span>
										<span> </span>
										<span class="attribute">dir</span>
										<span>=</span>
										<span class="attribute-value">"${ywzcpt.module.name}"</span>
										<span class="tag">/&gt;</span>
										<span>    </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">target</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
								<li class="">
										<span>     </span>
								</li>
								<li class="alt">
										<span>
										</span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">project</span>
										<span class="tag">&gt;</span>
										<span>  </span>
								</li>
						</ol>
				</div>
		</div>
		<script><![CDATA[ender_code();]]&gt;</script>
		<br />ywzcpt/build.xml片断： <br /><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-xml"><li class="alt"><span><span>主要实现commit功能   </span></span></li><li class=""><span></span><span class="tag">&lt;</span><span class="tag-name">target</span><span> </span><span class="attribute">name</span><span>=</span><span class="attribute-value">"commit-build"</span><span class="tag">&gt;</span><span>  </span></li><li class="alt"><span>    </span><span class="tag">&lt;</span><span class="tag-name">cvs</span><span> </span><span class="attribute">cvsRoot</span><span>=</span><span class="attribute-value">"${cvsroot}"</span><span> </span><span class="attribute">passfile</span><span>=</span><span class="attribute-value">"${root.dir}/ant-cvs.cvspass"</span><span>  </span></li><li class=""><span>         </span><span class="attribute">command</span><span>=</span><span class="attribute-value">"${ywzcpt.add}"</span><span class="tag">/&gt;</span><span>  </span></li><li class="alt"><span>    </span><span class="tag">&lt;</span><span class="tag-name">cvs</span><span> </span><span class="attribute">cvsRoot</span><span>=</span><span class="attribute-value">"${cvsroot}"</span><span> </span><span class="attribute">passfile</span><span>=</span><span class="attribute-value">"${root.dir}/ant-cvs.cvspass"</span><span>  </span></li><li class=""><span>         </span><span class="attribute">command</span><span>=</span><span class="attribute-value">"${ywzcpt.commit}"</span><span class="tag">/&gt;</span><span>  </span></li><li class="alt"><span></span><span class="tag">&lt;/</span><span class="tag-name">target</span><span class="tag">&gt;</span><span>  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><br />最后，在win2k中制定一个计划任务，就可以了。


<script type="text/javascript"><!--
google_ad_client = "pub-3874079075380071";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-03-19: 3G-move, 三星, 摩托罗拉, 索爱, 诺基亚
google_ad_channel = "1730597762+1709572468+0778807001+6838187825+8377398382";
//--></script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script><img src ="http://www.cnitblog.com/tilan/aggbug/21907.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2007-01-16 10:58 <a href="http://www.cnitblog.com/tilan/articles/21907.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>软件工程</title><link>http://www.cnitblog.com/tilan/articles/21893.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 15 Jan 2007 15:03:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21893.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21893.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21893.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21893.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21893.html</trackback:ping><description><![CDATA[<font face="Verdana"> </font>
		<a href="http://www.itisedu.com/phrase/200602281725525.html" target="_new">
				<font face="Verdana">软件工程</font>
		</a>
		<font face="Verdana">(</font>
		<a href="http://www.itisedu.com/phrase/200604240910235.html" target="_new">
				<font face="Verdana">Software Engineering</font>
		</a>
		<font face="Verdana">，简称为SE)是一门研究用工程化方法构建和维护有效的、实用的和高质量的</font>
		<a href="http://www.itisedu.com/phrase/200604232134205.html" target="_new">
				<font face="Verdana">软件</font>
		</a>
		<font face="Verdana">的学科。它涉及到</font>
		<a href="http://www.itisedu.com/phrase/200602281700255.html" target="_new">
				<font face="Verdana">程序设计语言</font>
		</a>
		<font face="Verdana">，</font>
		<a href="http://www.itisedu.com/phrase/200602271218062.html" target="_new">
				<font face="Verdana">数据库</font>
		</a>
		<font face="Verdana">，</font>
		<a href="http://www.itisedu.com/phrase/200603282233345.html" target="_new">
				<font face="Verdana">软件开发</font>
		</a>
		<font face="Verdana">工具，系统平台，标准，</font>
		<a href="http://www.itisedu.com/phrase/200603061631585.html" target="_new">
				<font face="Verdana">设计模式</font>
		</a>
		<font face="Verdana">等方面。</font>
		<p>
				<font face="Verdana">      在现代社会中，软件应用于多个方面。典型的软件比如有电子邮件，</font>
				<a href="http://www.itisedu.com/phrase/200603112246585.html" target="_new">
						<font face="Verdana">嵌入式系统</font>
				</a>
				<font face="Verdana">，人机界面，办公套件，</font>
				<a href="http://www.itisedu.com/phrase/200602281634075.html" target="_new">
						<font face="Verdana">操作系统</font>
				</a>
				<font face="Verdana">，编译器，数据库，游戏等。同时，各个行业几乎都有</font>
				<a href="http://www.itisedu.com/phrase/200602281627065.html" target="_new">
						<font face="Verdana">计算机软件</font>
				</a>
				<font face="Verdana">的应用，比如工业，农业，银行，航空，政府部门等。这些应用促进了经济和社会的发展，使得人们的工作更加高效，同时提高了生活质量。</font>
		</p>
		<p>
				<font face="Verdana">      软件工程师是对应用软件创造软件的人们的统称，软件工程师按照所处的领域不同可以分为系统分析员，软件设计师，系统<a href="http://www.itisedu.com/phrase/200604231335565.html" target="_new">架构师</a>，<a href="http://www.itisedu.com/phrase/200604232224305.html" target="_new">程序</a>员，测试员等等。人们也常常用程序员来泛指各种软件工程师。</font>
		</p>
		<p>
				<font face="Verdana">     软件工程(SoftWare Engineering)的<a href="http://www.itisedu.com/phrase/200603061723295.html" target="_new">框架</a>可概括为：目标、过程和原则。</font>
		</p>
		<p>
				<font face="Verdana">     (1)软件工程目标：生产具有正确性、可用性以及开销合宜的产品。正确性指软件产品达到预期功能的程度。可用性指软件基本结构、实现及文档为用户可用的程度。开销合宜是指软件开发、运行的整个开销满足用户要求的程度。这些目标的实现不论在理论上还是在实践中均存在很多待解决的问题，它们形成了对过程、过程模型及工程方法选取的约束。</font>
		</p>
		<p>
				<font face="Verdana">     (2)软件工程过程：生产一个最终能满足<a href="http://www.itisedu.com/phrase/200603101518295.html" target="_new">需求</a>且达到工程目标的软件产品所需要的步骤。软件工程过程主要包括开发过程、运作过程、维护过程。它们覆盖了需求、设计、实现、确认以及维护等活动。需求活动包括问题分析和<a href="http://www.itisedu.com/phrase/200603062220345.html" target="_new">需求分析</a>。问题分析获取需求定义，又称<a href="http://www.itisedu.com/phrase/200603061756235.html" target="_new">软件需求</a>规约。需求分析生成功能规约。设计活动一般包括概要设计和详细设计。概要设计建立整个<a href="http://www.itisedu.com/phrase/200602281706245.html" target="_new">软件系统</a>结构，包括<a href="http://www.itisedu.com/phrase/200604161433025.html" target="_new">子系统</a>、模块以及相关层次的说明、每一模块的接口定义。详细设计产生程序员可用的模块说明，包括每一模块中数据结构说明及加工描述。实现活动把设计结果转换为可执行的程序代码。确认活动贯穿于整个开发过程，实现完成后的确认，保证最终产品满足用户的要求。维护活动包括使用过程中的扩充、修改与完善。伴随以上过程，还有管理过程、支持过程、培训过程等。</font>
		</p>
		<p>
				<font face="Verdana">      (3)软件工程的原则是指围绕工程设计、工程支持以及工程管理在软件开发过程中必须遵循的原则。</font>
		</p>
		<p>
				<font face="Verdana">
						<strong>一、软件工程概述</strong>
				</font>
		</p>
		<p>
				<font face="Verdana">      概念：应需而生</font>
		</p>
		<p>
				<font face="Verdana">　　软件工程是一<a href="http://www.itisedu.com/phrase/200603090857555.html" target="_new">类</a>工程。工程是将理论和知识应用于实践的科学。就软件工程而言，它借鉴了传统工程的原则和方法，以求高效地开发高质量软件。其中应用了<a href="http://www.itisedu.com/phrase/200603021438435.html" target="_new">计算机</a>科学、数学和管理科学。计算机科学和数学用于构造模型与算法，工程科学用于制定规范、设计范型、评估成本及确定权衡，管理科学用于计划、资源、质量和成本的管理。</font>
		</p>
		<p>
				<font face="Verdana">      软件工程这一概念，主要是针对20世纪60年代“<a href="http://www.itisedu.com/phrase/200603112323405.html" target="_new">软件危机</a>”而提出的。它首次出现在1968年NATO（北大西洋公约组织）会议上。自这一概念提出以来，围绕软件项目，开展了有关开发模型、方法以及支持工具的研究。其主要成果有：提出了瀑布模型，开发了一些结构化<a href="http://www.itisedu.com/phrase/200602281641255.html" target="_new">程序设计</a>语言（例如PASCAL语言，Ada语言）、<a href="http://www.itisedu.com/phrase/200602281749185.html" target="_new">结构化方法</a>等。并且围绕<a href="http://www.itisedu.com/phrase/200604240825565.html" target="_new">项目管理</a>提出了费用估算、文档<a href="http://www.itisedu.com/phrase/200604161505135.html" target="_new">复审</a>等方法和工具。综观60年代末至80年代初，其主要特征是，前期着重研究系统实现技术，后期开始强调开发管理和软件质量。</font>
		</p>
		<p>
				<font face="Verdana">      70年代初，自“<a href="http://www.itisedu.com/phrase/200603061612405.html" target="_new">软件工厂</a>”这一概念提出以来，主要围绕<a href="http://www.itisedu.com/phrase/200602282140185.html" target="_new">软件过程</a>以及<a href="http://www.itisedu.com/phrase/200602281831545.html" target="_new">软件复用</a>，开展了有关软件生产技术和软件生产管理的研究与实践。其主要成果有：提出了应用广泛的<a href="http://www.itisedu.com/phrase/200603010948085.html" target="_new">面向对象语言</a>以及相关的<a href="http://www.itisedu.com/phrase/200602281513385.html" target="_new">面向对象方法</a>，大力开展了计算机辅助软件工程的研究与实践。尤其是近几年来，针对软件复用及软件生产，软件<a href="http://www.itisedu.com/phrase/200604161439595.html" target="_new">构件</a>技术以及软件质量控制技术、质量保证技术得到了广泛的应用。目前各个软件企业都十分重视资质认证，并想通过这些工作进行企业管理和技术的提升。软件工程所涉及的要素可概括如下： </font>
		</p>
		<p>
				<font face="Verdana">      根据这一框架，可以看出：软件工程涉及了软件工程的目标、软件工程原则和软件工程活动。</font>
		</p>
		<p>
				<font face="Verdana">      目标：我的眼里只有“产品”</font>
		</p>
		<p>
				<font face="Verdana">      软件工程的主要目标是：生产具有正确性、可用性以及开销合宜的产品。正确性意指软件产品达到预期功能的程度。可用性指软件基本结构、实现及文档为用户可用的程度。开销合宜性是指软件开发、运行的整个开销满足用户要求的程度。这些目标的实现不论在理论上还是在实践中均存在很多问题有待解决，它们形成了对过程、过程模型及工程方法选取的约束。</font>
		</p>
		<p>
				<font face="Verdana">      软件工程活动是“生产一个最终满足需求且达到工程目标的软件产品所需要的步骤”。主要包括需求、设计、实现、确认以及支持等活动。需求活动包括问题分析和需求分析。问题分析获取需求定义，又称软件需求规约。需求分析生成功能规约。设计活动一般包括概要设计和详细设计。概要设计建立整个<a href="http://www.itisedu.com/phrase/200603131358465.html" target="_new">软件体系结构</a>，包括子系统、模块以及相关层次的说明、每一模块接口定义。详细设计产生程序员可用的模块说明，包括每一模块中数据结构说明及加工描述。实现活动把设计结果转换为可执行的程序代码。确认活动贯穿于整个开发过程，实现完成后的确认，保证最终产品满足用户的要求。支持活动包括修改和完善。伴随以上活动，还有管理过程、支持过程、培训过程等。 </font>
		</p>
		<p>
				<font face="Verdana">      框架：四项基本原则是基石 </font>
		</p>
		<p>
				<font face="Verdana">　　软件工程围绕工程设计、工程支持以及工程管理，提出了以下四项基本原则：</font>
		</p>
		<p>
				<font face="Verdana">      第一，选取适宜开发范型。该原则与系统设计有关。在系统设计中，软件需求、硬件需求以及其他因素之间是相互制约、相互影响的，经常需要权衡。因此，必须认识需求定义的易变性，采用适宜的开发范型予以控制，以保证软件产品满足用户的要求。</font>
		</p>
		<p>
				<font face="Verdana">      第二，采用合适的设计方法。在软件设计中，通常要考虑软件的模块化、抽象与信息隐蔽、局部化、一致性以及适应性等特征。合适的设计方法有助于这些特征的实现，以达到软件工程的目标。</font>
		</p>
		<p>
				<font face="Verdana">      第三，提供高质量的工程支持。“工欲善其事，必先利其器”。在软件工程中，<a href="http://www.itisedu.com/phrase/200602282117345.html" target="_new">软件工具</a>与环境对软件过程的支持颇为重要。软件工程项目的质量与开销直接取决于对软件工程所提供的支撑质量和效用。</font>
		</p>
		<p>
				<font face="Verdana">      第四，重视开发过程的管理。软件工程的管理，直接影响可用资源的有效利用，生产满足目标的软件产品，提高软件组织的生产能力等问题。因此，仅当软件过程得以有效管理时，才能实现有效的软件工程。</font>
		</p>
		<p>
				<font face="Verdana">      这一软件工程框架告诉我们，软件工程的目标是可用性、正确性和合算性；实施一个软件工程要选取适宜的开发范型，要采用合适的设计方法，要提供高质量的工程支撑，要实行开发过程的有效管理；软件工程活动主要包括需求、设计、实现、确认和支持等活动，每一活动可根据特定的软件工程，采用合适的开发范型、设计方法、支持过程以及过程管理。根据软件工程这一框架，软件工程学科的研究内容主要包括：软件开发范型、软件开发方法、软件过程、软件工具、<a href="http://www.itisedu.com/phrase/200602282250045.html" target="_new">软件开发环境</a>、计算机辅助软件工程(CASE) 及软件经济学等。 </font>
		</p>
		<p>
				<font face="Verdana">      作用：高效开发高质量软件 </font>
		</p>
		<p>
				<font face="Verdana">　　自从软件工程概念提出以来，经过30多年的研究与实践,虽然“软件危机”没得到彻底解决，但在软件开发方法和技术方面已经有了很大的进步。尤其应该指出的是，自80年代中期，美国工业界和政府部门开始认识到，在软件开发中，最关键的问题是软件开发组织不能很好地定义和管理其软件过程，从而使一些好的开发方法和技术都起不到所期望的作用。也就是说，在没有很好定义和管理软件过程的软件开发中，开发组织不可能在好的软件方法和工具中获益。</font>
		</p>
		<p>
				<font face="Verdana">      根据调查，中国的现状几乎和美国10多年前的情况一样，软件开发过程没有明确规定，文档不完整，也不规范，软件项目的成功往往归功于软件开发组的一些杰出个人或小组的努力。这种依赖于个别人员上的成功并不能为全组织的软件生产率和质量的提高奠定有效的基础，只有通过建立全组织的过程改善，采用严格的软件工程方法和管理，并且坚持不懈地付诸实践，才能取得全组织的软件过程能力的不断提高。</font>
		</p>
		<p>
				<font face="Verdana">      这一事实告诉我们，只有坚持软件工程的四条基本原则，既重视软件技术的应用，又重视软件工程的支持和管理，并在实践中贯彻实施，才能高效地开发出高质量的软件。 </font>
		</p>
		<p>
				<font face="Verdana">
						<strong>二、软件工程的七条基本原理</strong>
				</font>
		</p>
		<p>
				<font face="Verdana">      自从1968年提出“软件工程”这一术语以来，研究软件工程的专家学者们陆续 提出了100多条关于软件工程的准则或信条。 美国著名的软件工程专家 Boehm 综合这些专家的意见，并总结了TRW公司多年的开发软件的经验，于1983年提出了软件工程的七条基本原理。 </font>
		</p>
		<p>
				<font face="Verdana">　　Boehm 认为，着七条原理是确保软件产品质量和开发效率的原理的最小集合。 <br />　　它们是相互独立的，是缺一不可的最小集合；同时，它们又是相当完备的。 </font>
		</p>
		<p>
				<font face="Verdana">　　人们当然不能用数学方法严格证明它们是一个完备的集合，但是可以证明，在此之前已经提出的100多条软件工程准则都可以有这七条原理的任意组合蕴含或派生。 </font>
		</p>
		<p>
				<font face="Verdana">　　下面简要介绍软件工程的七条原理： </font>
		</p>
		<p>
				<font face="Verdana">　　1 用分阶段的生命周期计划严格管理 <br />　　这一条是吸取前人的教训而提出来的。统计表明，50%以上的失败项目是由于计划不周而造成的。在软件开发与维护的漫长生命周期中，需要完成许多性质各异的工作。这条原理意味着，应该把<a href="http://www.itisedu.com/phrase/200603061230195.html" target="_new">软件生命周期</a>分成若干阶段，并相应制定出切实可行的计划，然后严格按照计划对软件的开发和维护进行管理。 Boehm 认为，在整个软件生命周期中应指定并严格执行6类计划：项目概要计划、里程碑计划、项目控制计划、产品控制计划、验证计划、运行维护计划。 </font>
		</p>
		<p>
				<font face="Verdana">　　2 坚持进行阶段评审 <br />　　统计结果显示： 大部分错误是在编码之前造成的，大约占63%; &lt;2&gt; 错误发现的越晚，改正它要付出的代价就越大，要差2到3个数量级。 因此，软件的质量保证工作不能等到编码结束之后再进行，应坚持进行严格的阶段评审，以便尽早发现错误。 </font>
		</p>
		<p>
				<font face="Verdana">　　3 实行严格的产品控制 <br />　　开发人员最痛恨的事情之一就是改动需求。但是实践告诉我们，需求的改动往往是不可避免的。这就要求我们要采用科学的产品控制技术来顺应这种要求。也就是要采用变动控制，又叫基准<a href="http://www.itisedu.com/phrase/200602271137552.html" target="_new">配置管理</a>。当需求变动时，其它各个阶段的文档或代码随之相应变动，以保证软件的一致性。 </font>
		</p>
		<p>
				<font face="Verdana">　　4 采纳现代程序设计技术 <br />　　从六、七时年代的结构化软件开发技术，到最近的<a href="http://www.itisedu.com/phrase/200603101726185.html" target="_new">面向对象</a>技术，从第一、第二代语言，到第四代语言，人们已经充分认识到：方法大似气力。采用先进的技术即可以提高软件开发的效率，又可以减少软件维护的成本。 </font>
		</p>
		<p>
				<font face="Verdana">　　5 结果应能清楚地审查 <br />　　软件是一种看不见、摸不着的逻辑产品。软件开发小组的工作进展情况可见性差，难于评价和管理。为更好地进行管理，应根据软件开发的总目标及完成期限， 尽量明确地规定开发小组的责任和产品标准，从而使所得到的标准能清楚地审查。 </font>
		</p>
		<font face="Verdana">
				<p>
						<br />　　6 开发小组的人员应少而精 <br />　　开发人员的素质和数量是影响软件质量和开发效率的重要因素，应该少而精。 <br />　　这一条基于两点原因：高素质开发人员的效率比低素质开发人员的效率要高几倍到几十倍，开发工作中犯的错误也要少的多； 当开发小组为N人时，可能的通讯信道为N(N-1)/2, 可见随着人数N的增大，通讯开销将急剧增大。 </p>
				<p>　　7 承认不断改进软件工程实践的必要性 <br />　　遵从上述六条基本原理，就能够较好地实现软件的工程化生产。但是，它们只是对现有的经验的总结和归纳，并不能保证赶上技术不断前进发展的步伐。因此，Boehm提出应把承认不断改进软件工程实践的必要性作为软件工程的第七条原理。根据这条原理，不仅要积极采纳新的软件开发技术，还要注意不断总结经验，收集进度和消耗等数据，进行出错<a href="http://www.itisedu.com/phrase/200603051002565.html" target="_new">类型</a>和问题报告统计。这些数据既可以用来评估新的 软件技术的效果，也可以用来指明必须着重注意的问题和应该优先进行研究的工具和技术。 </p>
				<p>      面向方面的编程（Aspect Oriented <a href="http://www.itisedu.com/phrase/200604232129205.html" target="_new">Programming</a>，简称<a href="http://www.itisedu.com/phrase/200604231341385.html" target="_new">AOP</a>）被认为是近年来软件工程的另外一个重要发展。这里的方面指的是完成一个功能的<a href="http://www.itisedu.com/phrase/200603090845215.html" target="_new">对象</a>和函数的集合。在这一方面相关的内容有泛型编程（Generic Programming）和模板。</p>
				<p>参考<br />      胡崑山，《中国软件产业发展现状与人才需求》，2003年9月1日，            http://software.ccidnet.com/pub/article/c372_a62973_p1.html </p>
				<p>
						<font face="Verdana">
								<strong>三、软件工程的目标与常用模型</strong>
						</font>
				</p>
				<p>
						<font face="Verdana">      软件工程的目标是提高软件的质量与生产率，最终实现软件的工业化生产。质量是软件需求方最关心的问题，用户即使不图物美价廉，也要求个货真价实。生产率是软件供应方最关心的问题，老板和员工都想用更少的时间挣更多的钱。质量与生产率之间有着内在的联系，高生产率必须以质量合格为前提。如果质量不合格，对供需双方都是坏事情。从短期效益看，追求高质量会延长软件开发时间并且增大费用，似乎降低了生产率。从长期效益看，高质量将保证软件开发的全过程更加规范流畅，大大降低了软件的维护代价，实质上是提高了生产率，同时可获得很好的信誉。质量与生产率之间不存在根本的对立，好的软件工程方法可以同时提高质量与生产率。 </font>
				</p>
				<p>
						<font face="Verdana">      软件供需双方的代表能在餐桌上谈笑风生，归功于第一线开发人员的辛勤工作。质量与生产率的提高就指望程序员与程序经理。对开发人员而言，如果非得在质量与生产率之间分个主次不可，那么应该是质量第一，生产率第二。这是因为：（1）质量直接体现在软件的每段程序中，高质量自然是开发人员的技术追求，也是职业道德的要求。（2）高质量对所有的用户都有价值，而高生产率只对开发方有意义。（3）如果一开始就追求高生产率，容易使人急功近利，留下隐患。宁可进度慢些，也要保证每个环节的质量，以图长远利益。 </font>
				</p>
				<p>
						<font face="Verdana">      软件的质量因素很多，如正确性，性能、可靠性、容错性、易用性、灵活性、可扩充性、可理解性、可维护性等等。有些因素相互重叠，有些则相抵触，真要提高质量可不容易啊！ </font>
				</p>
				<p>
						<font face="Verdana">      软件工程的主要环节有：人员管理、项目管理、可行性与需求分析、系统设计、程序设计、测试、维护等，如图1.1所示。 </font>
				</p>
				<p align="center">
						<img src="http://www.itisedu.com/manage/Upload/image/200632922318332.gif" border="0" />
				</p>
				<p>
						<font face="Verdana">      软件工程模型建议用一定的流程将各个环节连接起来，并可用规范的方式操作全过程，如同工厂的生产线。常见的软件工程模型有：线性模型（图1.2），渐增式模型（图1.3），螺旋模型，快速原型模型，形式化描述模型等等 [Pressmam 1999, Sommerville 1992]。 </font>
				</p>
				<font face="Verdana">
						<p align="center">
								<img src="http://www.itisedu.com/manage/Upload/image/2006329223120904.gif" border="0" />
								<br />  </p>
				</font>
				<p>
						<font face="Verdana">      最早出现的软件工程模型是线性模型（又称瀑布模型）。线性模型太理想化，太单纯，已不再适合现代的软件开发<a href="http://www.itisedu.com/phrase/200603061709535.html" target="_new">模式</a>，几乎被业界抛弃。偶而被人提起，都属于被贬对象，未被留一丝惋惜。但我们应该认识到，“线性”是人们最容易掌握并能熟练应用的思想方法。当人们碰到一个复杂的“非线性”问题时，总是千方百计地将其分解或转化为一系列简单的线性问题，然后逐个解决。一个软件系统的整体可能是复杂的，而单个子程序总是简单的，可以用线性的方式来实现，否则干活就太累了。线性是一种简洁，简洁就是美。当我们领会了线性的精神，就不要再呆板地套用线性模型的外表，而应该用活它。例如渐增式模型实质就是分段的线性模型，如图1.3所示。螺旋模型则是接连的弯曲了的线性模型。在其它模型中都能够找到线性模型的影子。 </font>
				</p>
				<p>
						<font face="Verdana">      套用固定的模型不是程序员的聪明之举。比如“程序设计”与“测试”之间的关系，习惯上总以为程序设计在先，测试在后，如图1.4（a）所示。而对于一些复杂的程序，将测试分为同步测试与总测试更有效，如图1.4（b）所示。 </font>
				</p>
				<p align="center">
						<img src="http://www.itisedu.com/manage/Upload/image/2006329223139948.gif" border="0" />
				</p>
				<font face="Verdana">
						<p>
								<br />      不论是什么软件工程模型，总是少不了图1.1中的各个环节。本书擗开具体的软件工程模型，顺序讲述人员管理、项目管理、可行性与需求分析、系统设计、程序设计、测试，以及维护与再生工程。其中程序设计部分以C++/C语言为例。 </p>
				</font>
				<p>
				</p>
		</font>
		<font face="Verdana">
				<strong>四、软件<a href="http://www.itisedu.com/phrase/200603122156385.html" target="_new">体系结构</a>和工具的选择</strong>
		</font>
		<p>
				<font face="Verdana">　　软件体系结构表示了一个软件系统的高层结构，主要特点有：1）软件系统结构是一个高层次上的抽象，它并不涉及具体的系统结构（比如<a href="http://www.itisedu.com/phrase/200604291152445.html" target="_new">B/S</a>还是<a href="http://www.itisedu.com/phrase/200604291148225.html" target="_new">C/S</a>），也不关心具体的实现。2）软件体系结构必须支持系统所要求的功能，在设计软件体系结构的时候，必须考虑系统的动态行为。3）在设计软件体系结构的时候，必须考虑有现有系统的兼容性、安全性和可靠性。同时还要考虑系统以后的扩展性和伸缩性。所以有时候必须在多个不同方向的目标中进行决策。</font>
		</p>
		<p>
				<font face="Verdana">　　当前已经有一些关于规范化软件体系结构，比如：ISO的开放系统互联模型、X Window系统等等。软件系统的结构通常被定义为两个部分：一个是计算部件。另一个就是部件之间的交互。如果把软件系统看成一幅图的话，计算部件就是其中的节点，而部件之间的交互就是节点之间的弧线。部件之间的连接可以被认为是一种连接器，比如过程调用、事件广播、数据库查询等等。正确的体系结构设计是软件系统成功的关键。</font>
		</p>
		<p>
				<font face="Verdana">　　我们理解了软件工程的重要性以后，我们没有相应的工具，我们也很难很好的完成一个系统。在需求分析和设计阶段，我们需要什么样的工具呢？</font>
		</p>
		<p>
				<font face="Verdana">　　当然最好是基于<a href="http://www.itisedu.com/phrase/200602271429302.html" target="_new">UML</a>的CASE工具。当前比较流行的就是Rose，它是一个很好的分析和建立对象和对象关系的工具。在具体编码的时候，我们需要<a href="http://www.itisedu.com/phrase/200603292355295.html" target="_new">版本控制</a>工具，MS的SourceSafe就是一个很好的版本管理工具和项目管理工具。具体的开发工具当然很多，但是如果你是一个对VC侵淫了多年的程序员，你一定会选择它，因为它会让你感到什么是真正的面向对象的编程，而你在用VB，<a href="http://www.itisedu.com/phrase/200604041117205.html" target="_new">PowerBuilder</a>，<a href="http://www.itisedu.com/phrase/200604042241255.html" target="_new">Delphi</a>时很少会有同样的感受。至于数据库模式构建，我一向是采用<a href="http://www.itisedu.com/phrase/200604040934365.html" target="_new">Sybase</a>的S-Design，更好的工具就不知道了。</font>
		</p>
		<p>
				<font face="Verdana">　　另外需要注意的是，我们需要建立文档编写的若干模板，以便开发人员按照这个模板编写规范的技术和说明文档。帮助文档可以用微软的HTML Help Workshop(hhw.exe)制作，你也可以编译成.chm格式，它打包了文本和图形，只有一个<a href="http://www.itisedu.com/phrase/200602282323195.html" target="_new">文件</a>，使用和分发比较方便。最后，如果开发人员不是集中在一个地方的话，最好建立一个邮件列表，开发人员可以通过邮件系统讨论开发中的各项事宜。 </font>
		</p>
		<p>
				<font face="Verdana">
						<strong>五、软件开发方法综述</strong>
				</font>
		</p>
		<font face="Verdana">
				<p>
						<br />　　国外大的软件公司和机构一直在研究软件开发方法这个概念性的东西，而且也提出了很多实际的开发方法，比如：生命周期法、原型化方法、面向对象方法等等。下面介绍几种流行的开发方法：</p>
				<p>　　1、结构化方法 </p>
				<p>　　结构化开发方法是由E.Yourdon 和 L.L.Constantine 提出的，即所谓的SASD 方 法， 也可称为面向功能的软件开发方法或面向数据流的软件开发方法。Yourdon方法是80年代 使用最广泛的软件开发方法。它首先用结构化分析（SA）对软件进行需求分析，然后用结构化设计（SD）方法进行总体设计，最后是结构化编程（SP）。它给出了两类典型的<a href="http://www.itisedu.com/phrase/200602282202575.html" target="_new">软件结构</a>（变换型和事务型）使软件开发的成功率大大提高。 </p>
				<p>　　2、面向数据结构的软件开发方法 </p>
				<p>　　Jackson方法是最典型的面向数据结构的软件开发方法，Jackson方法把问题分解为可由三种基本结构形式表示的各部分的层次结构。三种基本的结构形式就是顺序、选择和重复。三种数据结构可以进行组合，形成复杂的结构体系。这一方法从目标系统的输入、输出数据结构入手，导出程序框架结构，再补充其它细节，就可得到完整的程序结构图。这一方法对输入、输出数据结构明确的中小型系统特别有效，如商业应用中的文件表格处理。该方法也可与其它方法结合，用于模块的详细设计。</p>
				<p>　　3、 面向问题的分析法 </p>
				<p>　　PAM（Problem Analysis Method）是80年代末由日立公司提出的一种软件开发方法。 它的基本思想是考虑到输入、输出数据结构，指导系统的分解，在系统分析指导下逐步综 合。这一方法的具体步骤是：从输入、输出数据结构导出基本处理框；分析这些处理框之间的先后关系；按先后关系逐步综合处理框，直到画出整个系统的PAD图。这一方法本质上是综合的自底向上的方法，但在逐步综合之前已进行了有目的的分解，这个目的就是充分考虑系统的输入、输出数据结构。PAM方法的另一个优点是使用PAD图。这是一种二维树形结构图，是到目前为止最好的详细设计表示方法之一。当然由于在输入、输出数据结构与整个系统之间同样存在着鸿沟，这一方法仍只适用于中小型问题。</p>
				<p>　　4、原型化方法</p>
				<p>　　产生原型化方法的原因很多，主要随着我们系统开发经验的增多，我们也发现并非所有的需求都能够预先定义而且反复修改是不可避免的。当然能够采用原型化方法是因为开发工具的快速发展，比如用VB，DELPHI等工具我们可以迅速的开发出一个可以让用户看的见、摸的着的系统框架，这样，对于计算机不是很熟悉的用户就可以根据这个样板提出自己的需求。</p>
				<p>　　开发原型化系统一般由以下几个阶段：<br />（1） 确定用户需求<br />（2） 开发原始模型<br />（3） 征求用户对初始原型的改进意见<br />（4） 修改原型。</p>
				<p>　　原型化开发比较适合于用户需求不清、业务理论不确定、需求经常变化的情况。当系统规模不是很大也不太复杂时采用该方法是比较好的。</p>
				<p>　5、面向对象的软件开发方法</p>
				<p>　　当前计算机业界最流行的几个单词就是分布式、并行和面向对象这几个术语。由此可以看到面向对象这个概念在当前计算机业界的地位。比如当前流行的两大面向对象技术<a href="http://www.itisedu.com/phrase/200604152041495.html" target="_new">DCOM</a>和<a href="http://www.itisedu.com/phrase/200604031336425.html" target="_new">CORBA</a>就是例子。当然我们实际用到的还是面向对象的编程语言，比如C++。不可否认，面向对象技术是软件技术的一次革命，在软件开发史上具有里程碑的意义。 </p>
				<p>　　随着<a href="http://www.itisedu.com/phrase/200604240956125.html" target="_new">OOP</a>（<a href="http://www.itisedu.com/phrase/200603050951565.html" target="_new">面向对象编程</a>）向<a href="http://www.itisedu.com/phrase/200604231256315.html" target="_new">OOD</a>（<a href="http://www.itisedu.com/phrase/200603050045535.html" target="_new">面向对象设计</a>）和<a href="http://www.itisedu.com/phrase/200603050028345.html" target="_new">OOA</a>（<a href="http://www.itisedu.com/phrase/200604231254345.html" target="_new">面向对象分析</a>）的发展，最终形成面向对象的软件开发方法OMT (<a href="http://www.itisedu.com/phrase/200604231338435.html" target="_new">Object</a> Modeling Technique）。这是一种自底向上和自顶向下相结合的方法，而且它以对象建模为基础，从而不仅考虑了输入、输出数据结构，实际上也包含了所有对象的数据结构。所以OMT彻底实现了PAM没有完全实现的目标。不仅如此，<a href="http://www.itisedu.com/phrase/200604231401365.html" target="_new">OO</a>技术在需求分析、可维护性和可靠性这三个软件开发的关键环节和质量指标上有了实质性的突破，基本地解决了在这些方面存在的严重问题。</p>
				<p>　　综上所述，面向对象系统采用了自底向上的归纳、自顶向下的分解的方法，它通过对对象模型的建立，能够真正建立基于用户的需求，而且系统的可维护性大大改善。当前业界关于面向对象建模的标准是UML（<a href="http://www.itisedu.com/phrase/200604231301515.html" target="_new">Unified Modeling Language</a>）。</p>
				<p>　　这里我们需要谈一下微软的<a href="http://www.itisedu.com/phrase/200604231607095.html" target="_new">MSF</a>（Microsoft Solutions <a href="http://www.itisedu.com/phrase/200604241001145.html" target="_new">Framework</a>）的框架，它简单的把系统设计分成三个阶段：概念设计、逻辑设计和物理设计。概念设计阶段就是从用户的角度出发可以得到多少个对象，并且以对象为主体，画出业务框架。逻辑设计阶段就是对概念设计阶段的对象进行再分析、细分、整合、删除。并建立各个对象的方法属性以及对象之间的关系。而物理设计实际上就是要确定我们实际需要的<a href="http://www.itisedu.com/phrase/200603302222545.html" target="_new">组件</a>、服务和采用的框架结构、具体的编程语言等。MCF整个结构比较清楚是基于对象开发的一个比较好的可操作的框架系统。</p>
				<p>　　6、可视化开发方法 </p>
				<p>　　其实可视化开发并不能单独的作为一种开发方法，更加贴切的说可以认为它是一种辅助工具，比如用过SYBASE的S-Design的人都知道，用这个工具可以进行显示的图形化的数据库模式的建立，并可以导入到不同的数据库中去。当然用过S-Design的人不一定很多，但用过VB，DELPHI，C++ Builder等开发工具的人一定不少，实际上你就是在使用可视化开发工具。</p>
				<p>　　当然，不可否认的是，你只是在编程这个环节上用了可视化，而不是在系统分析和系统设计这个高层次上用了可视化的方法。实际上，建立系统分析和系统设计的可视化工具是一个很好的卖点，国外有很多工具都致力于这方面产品的设计。比如<a href="http://www.itisedu.com/phrase/200604240816305.html" target="_new">Business Object</a>就是一个非常好的数据库可视化分析工具。</p>
				<p>　　可视化开发使我们把注意力集中在业务逻辑和业务流程上，用户界面可以用可视化工具方便的构成。通过操作界面元素，诸如菜单、按钮、对话框、编辑框、单选框、复选框、 列表框和滚动条等，由可视开发工具自动生成应用软件。 </p>
				<p>
						<strong>六、怎样培养软件工程的思维与方法</strong>
				</p>
				<p>
						<br />　　作为软件开发人员的一个通病是在项目初期的时候，就喜欢谈论实现的细节，并且乐此不疲。我们更喜欢讨论如何用灵活而简短的代码来实现一个特定的功能，而忽略了对整个系统<a href="http://www.itisedu.com/phrase/200604241328115.html" target="_new">架构</a>的考虑。所以作为一个开发人员，尤其是一个有经验的开发人员，应该把自己从代码中解脱出来，更多的时候在我们的脑子里甚至暂时要放弃去考虑如何实现的问题，而从项目或产品的总体去考虑一个软件产品。</p>
				<p>　　以下是我个人的一些经验：</p>
				<p>　　1．考虑整个项目或者产品的市场前景。作为一个真正的系统分析人员，不仅要从技术的角度来考虑问题，而且还要从市场的角度去考虑问题。也就是说我们同时需要考虑我们产品的用户群是谁，当我们产品投放到市场上的时候，是否具有生命力。比如即使我们采用最好的技术实现了一个单进程的操作系统，其市场前景也一定是不容乐观的。</p>
				<p>　　2．从用户的角度来考虑问题。比如一些操作对于开发人员来讲是非常显而易见的问题。但是对于一般的用户来说可能就非常难于掌握，也就是说，有时候，我们不得不在灵活性和易用性方面进行折中。另外，在功能实现上，我们也需要进行综合考虑，尽管一些功能十分强大，但是如果用户几乎不怎么使用它的话，就不一定在产品的第一版的时候就推出。从用户的角度考虑，也就是说用户认可的才是好的，并不是开发人员觉的好才好。</p>
				<p>　　3．从技术的角度考虑问题。虽然技术绝对不是唯一重要的，但是技术一定是非常重要的，是成功的必要环节。在产品设计的时候，必须考虑采用先进的技术和先进的体系结构。比如，如果可以采用多<a href="http://www.itisedu.com/phrase/200603091754305.html" target="_new">线程</a>进行程序中各个部分并行处理的话，就最好采用多线程处理。在Windows下开发的时候，能够把功能封装成一个单独的COM构件就不作成一个简单的DLL或者是以源代码存在的函数库或者是对象。比如能够在<a href="http://www.itisedu.com/phrase/200604291151335.html" target="_new">B/S结构</a>下运行并且不影响系统功能的话就不一定要在C/S下实现。</p>
				<p>4．合理进行模块的分割。从多层模型角度来讲，一般系统可以分成用户层、业务层和<a href="http://www.itisedu.com/phrase/200604241152425.html" target="_new">数据库层</a>三部分。当然每以部分都还可以进行细分。所以在系统实现设计的时候，尽量进行各个部分的分割并建立各个部分之间进行交互的标准。并且在实际开发的时候，确实有需要的话再进行重新调整。这样就可以保证各个部分齐头并进，开发人员也可以各施其职。</p>
				<p>　　5．人员的组织和调度。这里很重要的一点是到考虑人员的特长，有的人喜欢做界面，有的人喜欢做核心。如果有可能要根据人员的具体的情况进行具体的配置。同时要保证每一个开发人员在开发的时候首先完成需要和其他人员进行交互的部分，并且对自己的项目进度以及其他开发人员的进度有一个清晰的了解，保证不同部分的开发人员能够经常进行交流。</p>
				<p>　　6．开发过程中文档的编写。在开发过程中会碰到各种各样的问题和困难，当然还有各种各样的创意和新的思路。应该把这些东西都记录下来并进行及时整理，对于困难和问题，如果不能短时间解决的，可以考虑采用其他的技术替代，并在事后做专门的研究。对于各种创意，可以根据进度计划安排考虑是在本版本中实现还是在下一版本中实现。</p>
				<p>　　7．充分考虑实施时可能遇到的问题。开发是一回事情，用户真正能够使用好它又是另外一回事情。比如在<a href="http://www.itisedu.com/phrase/200604241249435.html" target="_new">MIS</a>系统开发中，最简单的一个问题就是用户如果数据输入错误的时候，如何进行操作。在以流程方式工作的时候，如何让用户理解自己在流程中的位置和作用，如何让用户真正利用计算机进行协作也是成败的关键。</p>
				<p>　　以上是我个人的一点体会，实际上，作为一个软件开发人员，我也喜欢看到问题就坐在计算机前面直接编码，但是我确实认为软件工程对于我们系统开发的指导作用是巨大的。作为软件工程的拥戴者，下面我简单结合自己的开发经历介绍基于软件工程的开发方法、编程规范和工具使用等方面的问题。 </p>
				<p>
						<br />
						<strong>七、软件开发的发展变化</strong>
				</p>
				<p>
						<br />　　国外很多项目的开发都是基于一些图形化的东西来做的，他们的目的是尽量少写代码甚至不写代码。代码能够通过图形化的方式自动生成，这样的一个好处就是如果用户的需求变化或者业务逻辑发生变化，我们需要做的就是对图形表示的调整，然后重新自动生成代码，这也就是国外开发很注重对项目的概念和逻辑分析的原因。</p>
				<p>　　他们的重点是把业务规则和需求用图形化的方式表现出来，然后通过CASE工具自动生成代码。所以当国人还在不停的开发一个又一个的MIS工具的时候，国外已经把很多精力放到了CASE工具的制作上。</p>
				<p>　　我们很多公司人员忙着写具体业务过程的相关代码，而国外很多都把精力放到对不同应用，不同行业的模型的建立和共性的提取上。所以，他们做出来的东西就相对具有很强的灵活性和扩展性，而我们是用户的需求稍微有一点变化，就要忙着改代码，甚至改体系结构。</p>
				<p>　　另外，因为他们注重模型的建立，所以在建立其他应用的时候，能够借鉴原先的模型，在高层次上做调整和优化，同时能够有效的提取原有系统中可以被使用的部分。所以我们应该从以代码为核心的软件开发模式转化到以模型为中心的、基于CASE的开发上来。</p>
				<p>　　关于协作与个人英雄主义</p>
				<p>　　社会进步的一个很明显的现象就是社会分工越来越细，软件的开发也不例外。为什么在软件开发的今天已经不能出现象裘伯君这样的软件英雄的原因也在这里，单凭个人之力，我们也许穷尽有生之年也开发不出象Windows这样的操作系统。<br />因为，当前软件行业的壁垒无非就是两个，一个就是以技术创新取胜，你模仿的了其中的界面，但是你没有办法实现其中的核心功能。结果是你只能购买其技术核心，而你作一些边角工作。不举别的例子，比如VB这样的开发工具，其核心部分是它和第三方提供的COM控件或者是DLL函数库，你所做的就是一个整合的工作。</p>
				<p>　　第二个就是以细致取胜，也就是说功能很多而且做的很精致，即使技术本身不是很复杂，你真要想做出一个这样的东西来没有一两年的工夫是不可能的。而真等你做出来了，它的新版本也早已经推出。真正能够在市面上叫得想、经得起考验得产品都是具有这两方面的特点。</p>
				<p>　　这两方面的特点决定了你一个人绝对是不可能胜任的，也许你可以独立的完成技术创新，但是你绝对不可能一个人实现所有这些纷繁复杂的功能。所以，这个时代需要创新的英雄，也更需要人与人之间的协作。<br />当今的软件发展已经不是一个人可以包打天下的年代。软件开发的管理、系统体系结构的设计、模块之间的衔接、核心算法的实现、灵活界面的制定、软件再开发接口的实现都需要专门的人来做。而把这些有效的集成显然就需要有效的利用软件工程的思想和方法。所以，真正的软件英雄绝对不再是写着别人看不懂代码的程序员，而是整个体系结构的分析、设计、标准制定、协调人员。</p>
				<p>
						<strong>八、我们是否需要软件工程</strong>
				</p>
				<p>
						<br />　　有一点大家可以达成共识的就是，如果一个象Windows这样的操作系统，不进行全面的规划，不采用软件工程的思想和方法，是绝对搞不出来的。</p>
				<p>　　Windows的成功不在于它在进程管理和调度，文件系统、内存管理、界面设计等方面有多少成功的创新，它的成功最大的一点就是把所有的技术能够合理的整合起来，并集中到一个Window操作系统特有的框架结构中去。</p>
				<p>　　更为重要的是，Windows的每一项技术创新都能够有效的整合到Windows框架中去，比如COM、<a href="http://www.itisedu.com/phrase/200604231236585.html" target="_new">XML</a>等技术，通过ActiveX、DCOM等技术使Windows从桌面操作系统发展成为一个基于网络的操作系统。</p>
				<p>　　OLE2技术把整个Office中相关的软件进行了有效的整合，显然，这里我们可以把Office的设计和WPS的设计进行比较，客观的讲，WPS对中国用户来说实在也是一个很好的产品。但是从整个系统设计概念上来讲，Office显然要比WPS高一个层次，它能够把WORD，EXCEL，POWERPOINT，ACCESS有效的整合在一起，使我们所有办公相关的文档、图表、数据库、演示变成了一个一体化的东西。而且通过宏调用，用户可以自己定制用户界面并编制适当的模板，单是这个二次开发功能就不是WPS现在所能及项背的，当然限于当前用户的水平还很少有人使用二次开发的功能。</p>
				<p>　　从微软产品系列可以看到软件工程的作用，微软的所有产品都有一个整体的框架结构，比如Office软件，通过OLE技术进行有效的通讯和联系。比如Visual系列开发工具，提供了相似的开发界面使用户学会一种开发工具以后能够很容易的学习其他的开发工具。比如<a href="http://www.itisedu.com/phrase/200604040933575.html" target="_new">SQL SERVER</a>和ACCESS，尽管它们适用的范围不同，但是它们表现给用户的界面，特别是在查询和分析上表现了高度的一致性。</p>
				<p>　　更值得一提的是，因为设计结构的合理性，因为在开发前期作了很多分析和调研，考虑了扩展性和伸缩性，微软的系列产品能够很快的利用新的技术并采用统一的结构形式表现出来。比如当网络成为计算机发展的主流的时候，几乎微软所有的工具都能够快速的支持基于网络的开发和应用。</p>
				<p> </p>
				<p>　　相比之下，我们国内很多公司的产品很少具有连续性，往往是新的一个产品完全重起炉灶，和老的产品没有半点关系。这就是我们在设计产品的时候，没有很好的进行抽象和概念、逻辑设计，造成的结果是从旧的产品中提取不出一些有用的、共性的东西为后来的产品所使用。</p>
				<p>　　当然，很多开发人员从心里也承认一个大的系统确实需要软件工程的依托，但是一个小的工程项目是否就可以仓促上马呢？答案是否定的。所谓麻雀碎小，五脏俱全。无论是大项目、还是小项目。它们作为一个项目，都需要有一个需求分析、系统结构建立、设计、编码、测试等阶段。这是任何一个项目都不可缺少的。</p>
				<p>　　往往可以看到很多大公司的IT部门的人员都在不停的作各种各样的报表，当各个部门提出一种新类型的报表的时候，就从数据库中提取相应的数据并画出业务人员所需要的样式结构，很少是提供了一个通用的模板，当然提供高层<a href="http://www.itisedu.com/phrase/200604241228185.html" target="_new">API</a>接口进行这种操作的就更少了。这样不可避免的使开发人员陷入一些琐碎的报表编制工作。而造成这个局面的很重要的一个原因就是没有在系统开发的前期进行很好的调研、需求分析和系统体系结构的设计。</p>
				<p>　　这里就我们开发过的一些小型软件项目来谈一些开发的总结和体会，一般来说，小型软件项目功能比较单一，而且模块与模块之间的衔接不是很多，同时对开发周期要求比较短。</p>
				<p>　　小项目虽然看起来比较简单，所以很多开发人员容易犯一些错误，记得我们在开发一个基于Internet的有偿服务系统的时候，有三个开发人员：一个负责前端界面的编写，一个负责数据通讯协议和实现（基于TCP基础上的应用协议），一个负责对数据库数据的查询、整理和提取。我们在开发的时候没有认真地进行项目实际前途和工作量的估计。没有认真地估计项目难度，比如对于通讯中多用户并发访问时的多线程问题和缓存处理问题，用户批量请求处理的实现复杂度问题等等。三个人之间的接口也是在开发中休息的时候，口头定义一下。结果发现有不严密的地方(比如在通讯服务器端是用VC编写的，开发人员是通过stream来传送数据的，<a href="http://www.itisedu.com/phrase/200603082208195.html" target="_new">客户端</a>是用Delphi编写，在接收数据的时候发现数据不准确，后来研究发现VC利用CSocket在传送数据流的时候对数据进行了自己定义的格式化，结果服务器端数据发送模块只好重写)，而且其中关于一个接口双方的理解不同，然后又返工重新修改。最后到系统基本完成的时候没有一份较正式的文档。然后因为有人毕业离开这个项目，然后他编写的模块需要升级，新的接收的人不得不花很多时间去阅读他的源代码。</p>
				<p>所以在开发小项目的时候也必须要建立合理的模式：而所谓合理的模式就是软件工程告诉我们的在开发一个项目的时候所需要的五步曲：获取需求、需求分析、设计、编码、测试。</p>
				<p>　　1．理解用户真正的需求。在进入正式开发之前，必须先从用户处获取准确的需求。在这上面花费相当时间是很必要的。</p>
				<p>　　我们软件项目可以大致分为专用软件和通用软件两大类。对于专用软件，一般用户对于软件要完成哪些功能已经有了一个比较清楚的轮廓，而且往往在开发合同中已经大致地规定了。</p>
				<p>　　但是，开发合同上规定的只是一个大概的框架，在进入开发之前必须与用户进行比较具体的交流和讨论，了解清楚用户心目中的产品究竟是什么样子，这里最好就采用原型化的方法作出一个简单的框架给用户看。</p>
				<p>　　对于通用软件，在开发之前必须做一定的市场调查工作，一方面是从经济效益考虑，调查产品的潜在市场有多大，一方面是从技术的角度，了解清楚潜在用户对软件的各种技术上的要求，另一方面是确定我们软件的定位，即我们软件具体是为哪一些用户群体服务的。然后对该群体用户现有硬件配置，软件配置，网络使用情况，数据库使用情况，计算机熟悉程度做一定的调研，根据调查的统计结果决定即将开发的软件的一些技术指标。 <br />　　<br />　　2．需求分析。需求分析需要做的事情有：高层构思、确立系统目标、划分业务领域、现行业务分析、建立业务模型(Enterprise Model)、信息需求分析、用户<a href="http://www.itisedu.com/phrase/200603141659315.html" target="_new">视图</a>规范化、数据元素标准化与一致性控制。</p>
				<p>　　在了解用户的需求之后，将需求用一种模型来表示，就是需求分析，一般我们可以<a href="http://www.itisedu.com/phrase/200604231249365.html" target="_new">面向对象的方法</a>，通过分析用户需求，用类、类之间的各种关系来表示整个系统。</p>
				<p>　　为了讨论软件运行的流程，可以采用UML的<a href="http://www.itisedu.com/phrase/200603042249305.html" target="_new">Use Case</a>图。在系统分析的时候需要明确应用域（application domain）的范围，然后明确我们系统需要做什么。同时我们需要决定用什么方法来完成需求的获取，这在很大程度上影响了需求分析的做法。</p>
				<p>　　例如可以采用Use Case来表示用户需求，那么从各种<a href="http://www.itisedu.com/phrase/200603101549275.html" target="_new">序列图</a>中选出相互交互的各个实体，就是一个个类。另外分析需要与设计过程相衔接。分析过程的内容是用对象和对象之间的关系来表示整个系统和系统的流程的，并不设计具体实现，如采用什么编程语言，在什么操作系统平台上运行等等。这些具体实现是在设计阶段来完成的。</p>
				<p>　　面向对象方法的优点是分析、设计、编码过程表示法统一，能比较好的衔接。现在很多CASE工具并不区分分析和设计的阶段。但是，这并不意味着开发就可以对分析和设计不加区分，如何用好辅助设计(case)工具还是开发人员的事情。</p>
				<p>　　3．设计过程。设计阶段的工作包括对分析模型进行必要的修改，同时可能需要对某些类结构做一些修改，确定用户表示层(也就是通俗所说的界面定义)、用户服务层、业务逻辑层、数据库服务层和具体数据库所需要做的工作。同时需要确定使用的体系结构（比如B/S还是C/S）和开发工具（如VB，VC，VI，C++ Builder，DELPHI，PowerBuiler等等）</p>
				<p>　　4．编码。进入编码工作之后，依然可能会发现前面分析或设计阶段的某些错误，这时应返回到前面的阶段进行必要的修改。同时在编码前规定编码的风格并在开发过程中保持一致的风格。</p>
				<p>
						<br />　　5．测试。测试是系统投入使用前最关键的一个步骤。即使是小项目也应该严格地进行测试。就实际上就是一个把错误留给自己还是留给客户的问题。</p>
				<p>　　最后，我们知道软件项目主要是由开发人员完成的，所以对人员的合理安排和配置也很重要，一般在开发过程中，需要有一位项目负责人，负责分析、设计和协调的工作。另外需要几个程序员完成不同层的代码（比如用户服务层、业务逻辑层、数据库服务层等等）。</p>
				<p>　　同时需要有一个文档整理人员随时整理系统开发过程中相关的文档。如果条件可能的话，要配置一个测试工程师，专门进行代码的测试工作，当然如果条件不允许的话，也可以由开发人员<a href="http://www.itisedu.com/phrase/200604240926075.html" target="_new">交叉测试</a>。这里需要注意的是，对于项目负责人而言，协调几个人的工作比自己完成一段编码更重要。</p>
				<p>　　由于协调上出了漏洞，可能导致很大的问题，所以项目负责人必须随时监控各开发人员的工作，包括内容是否与要求发生偏差，进度是否滞后等等。同时必须给每个开发人员明确的任务书。具体开发时每个开发人员必须非常明确自己的任务，这些任务应该采用明确的文档来表示。每个开发人员需要清楚自己所做的工作在整个系统中处于什么地位，这样就有可能会发现<a href="http://www.itisedu.com/phrase/200604161258515.html" target="_new">设计模型</a>中的漏洞，避免了各人的代码编写完毕之后又要修改的后果。 </p>
				<p>
						<strong>九、我国软件工程发展的现状</strong>
				</p>
				<p>
						<br />      很多国内搞计算机的专家都认为：国内的软件研发过程，个人色彩比较浓。过分地依靠个人无法形成产业规模，而没有规模就谈不上产业化了。</p>
				<p>　　不管怎么样，我们大家还是先要来看一看国内软件厂商到底提供给我们多少有震撼力的软件产品，从技术和利润的角度讲，软件系统最核心的部分还是操作系统、编译系统然后就是开发平台之类的东西，接下来就是一些应用系统，比如图形开发、游戏开发、企业应用、网站建设、杀毒、网络工具等等。</p>
				<p>　　操作系统以中科院为中心，做了一个COSIX，这个本质上是一个UNIX系统，UNIX最初的源代码是公开的，尽管COSIX是一个被称为中国的操作系统并是UNIX系列的（IX就代表UNIX系列），但是其中到底有多少独创的技术成分我们暂时还不知道，但有一点可以肯定，它现在的市场覆盖率绝对不大，而且能否在上面运行各种各样的编译系统、数据库、群件和应用系统可能还需要进一步测试。然后就是对硬件平台的支持也需要进一步完善。</p>
				<p>　　然后就是轰轰烈烈的Linux系统，Linux是遵守GNU标准的操作系统，中国有很多家公司推出了自己的Linux并且还有汉化的Linux，这就有比较疑惑的一点，为什么不在Linux上构架一个类似UNICODE这样的东西，而只做汉化这么本地化的产品呢？不知道是眼光还是市场的问题了。<br />MIS系统、财务软件是中国软件行业的重头戏，它们彻底的暴露了中国软件开发无序和重复低效劳动的一面。教育软件在某一种层面上看就是电子题库，当然也有优点，比如加入了多媒体教学（可视化程度不错）和所谓寓教于乐的特点，但是从本质上说还是题库。杀毒软件据说是中国软件的骄傲，由中国权威机构评测是达到了世界领先水平，但是好象还没有得到国际权威机构的认可。游戏软件就不用提了，国内业界能够流行的游戏软件成功的秘诀众所周知，不是技术和创意，实在是归功于我们悠久的历史。字处理软件和排版软件客观的说国内的也做的不错，但是从系统的扩展性和体系结构上说和MS和Adobe相比，差距也放在那里。其实这种现状的原因很简单，一个是我们缺少创新的能力，另一个就是我们欠缺软件工程的概念，系统开发前期的需求分析、设计没有做好或者做的不够好。</p>
				<p>　　当然，我们很少怀疑自己的技术能力，我们很多时候认为这是地理环境和经济环境的原因造成了中国软件业现在的局面。当然中国软件开发人员绝对可以算是优秀的，但是想想我们软件行业龙头企业到底有多少有技术创新和专利技术呢？姑且不论这个，实际上把一个操作系统分解开来，比如文件系统、进程管理和调度、IO调度等等，也许我们可以实现其中某一块的内容，但是如何把它们合理的整合起来绝对是一个涉及到软件工程的问题。</p>
				<p>　　作为一个开发人员，我们已经习惯了自己那一套编程模式，而且我们的这种习惯也不自觉的影响着新的开发人员。所以在头脑中建立一个软件工程的作用，从某种角度上讲，要比会几种开发语言、几个编程技巧实在是重要的多。</p>
				<p>　　举一个例子来说，我们也许可以写MFC中的几个类或者是用自己的类扩展MFC，但是我们又有几个人真正去认真分析和考虑MFC架构的设计和原理呢？扪心自问，我们又有多少人能够设计出MFC这样的框架系统呢？下面就我们的题目谈一些相关的话题。 </p>
				<p>
						<strong>十、我有一个梦</strong>
				</p>
				<p>　　毋庸质疑的是，计算机的发展和人类的历史相比甚至和其他很多科技产品相比都是非常短的，从第一台计算机的研制成功到现在也没有百年的历史，但是计算机及其相关技术的发展却绝对可以说是最快的。抛开硬件的发展（硬件的发展基本上是按照摩尔定律来的，每18个月，机器的速度性能都要提高一倍），单从软件的发展来说，从体系结构来讲，我们经历了从主机结构到文件服务器结构，从客户服务器系统到基于Internet的服务器浏览器结构的体系结构的变化。从编码的角度来讲，我们经历了从最开始的机器代码到汇编代码，从高级程序语言到<a href="http://www.itisedu.com/phrase/200603011759495.html" target="_new">人工智能</a>语言，从专用的程序设计语言到通用的程序设计语言。从开发工具来讲，我们经历了从分离的开发工具（有代码编辑器，中间代码生成器和连接器）到集成的开发系统，从最简单的单行命令式调试器到方便灵活的多功能的调试器。</p>
				<p>　　但是，今天所有的软件厂商和软件开发人员依然会想起当年的黑人人权运动领袖马丁？路德？金曾经说过的一句名言我有一个梦想。是的，所有的开发人员依然怀着梦想，希望能够有一个万能的系统开发的框架和方法，只要我们沿着这个框架，我们将能开发出适合所有领域的应用系统，于是，我们在念书的时候把这个希望投到了一门课上，这么课就是软件工程。但是当我们在学完这门课的时候，我们依然没有找到这么一个框架甚至连接近这么一个框架的东西也没有碰到。</p>
				<p>　　不管我们认为软件工程可能是多么的虚无，但是所有学工科并且有逻辑头脑的人都坚信理论对实践的指导意义，因为有了爱因斯坦及其许多伟大的科学家关于能量和质量方面的理论以后，我们才造出了原子弹。但是，遗憾的是软件工程并不是一个具体的理论，它更象一门抽象的科学。软件工程是一种方法论，而不是一种具体的摸的着，看的见的产品。它告诉我们在设计一个系统的时候，我们需要进行可行性研究、计划制订、需求分析、系统设计、编码、测试、维护等等。并且对这些过程中应该做什么提出了一个指导性的东西。但是没有任何专家和标准委员会保证只要按照这些标准，我们的系统肯定会顺利完成。而且事实上，软件开发针对的领域是如此之多并不没有一种对所有领域适用的万能框架。<br />　　不管认为软件工程已经到了非常成熟的阶段还是认为软件工程依然是一个搞不懂的黑箱子，软件工程确实已经经历了三个不同的阶段。第一个阶段是软件结构化生产阶段，以结构化分析与设计、结构化评审、结构化程序设计以及结构化测试为特征。从80年代中期，软件生产开始进入以过程为中心的第二阶段，以提出过程成熟模型<a href="http://www.itisedu.com/phrase/200603051508215.html" target="_new">CMM</a>、<a href="http://www.itisedu.com/phrase/200603061000265.html" target="_new">个体软件过程</a><a href="http://www.itisedu.com/phrase/200604231319415.html" target="_new">PSP</a>和<a href="http://www.itisedu.com/phrase/200604231333525.html" target="_new">群组软件过程</a><a href="http://www.itisedu.com/phrase/200604231334415.html" target="_new">TSP</a>为标志。第三个阶段就是以软件过程、面向对象和构件重用三把斧头出现的软件工业化生产阶段。</p>
				<p>　　言归正传，我们还是回到我们的文章标题上来，我们在开发的时候是兵马未动、粮草先行还是摸着石子过河。兵马未动、粮草先行当然意味着我们在开发的时候先不忙着编写代码做程序，我们先要制订一个关于开发的方法。这点就象<a href="http://www.itisedu.com/phrase/200603141328355.html" target="_new">元数据</a>（metadata）的概念，元数据并不定义数据，它是对数据的说明，也就是通常所说的关于数据的数据。我们设计的时候也是这样，定义开发的标准，如何进行开发、怎样开发。摸着石子过河就意味着我们先不管什么理论，方法，科学的问题，我们先动手做起来，如果做的也算成功的话，那就可以按照这种模式来，实际上，在任何事情的最初，我们都是这样。从辨证唯物主义者的观点来说，就是从实践中来，然后升华到理论，再用理论来指导实践。记得一个笑话说：外国人搞软件工程是在一个黑屋子里面抓黑猫，不过到现在还是没有抓住，而中国人是在一个黑屋子里面，而里面连猫都没有，然后有人说，我已经抓到猫了。这个笑话一方面是说明直到现在，软件工程还是一个在继续探索、发展的过程，另一个侧面也说明中国搞软件工程摸不着边的局面。</p>
				<p>　　实际上，不管有没有软件工程，不管是否存在一个万能的框架系统，我们的应用系统还是要做，各种各样的软件还是要开发。说到底，软件系统是因为有需求才存在的。有了应用域才有了软件存在的意义。很多时候，我们可以看到国外有各种各样的软件和创新，而我们没有，我们更多的是模仿和一些重复的功能相近的软件的原因就是因为我们没有这方面的需求，这也正解释了为什么ERP系统能在国外开展的很好，而在国内失败多于成功的原因。一方面当然是因为我们的企业按照市场经济发展的时间还不长，另一方面是我们的企业确实也没有这方面的需求。</p>
				<p>
				</p>
				<p>
						<font face="Verdana">
						</font>
				</p>
				<font face="Verdana">
						<strong>十一、软件工程的发展方向</strong>
				</font>
				<p>
				</p>
				<p>
						<font face="Verdana">      “<a href="http://www.itisedu.com/phrase/200603291800375.html" target="_new">敏捷开发</a>”（<a href="http://www.itisedu.com/phrase/200604240917025.html" target="_new">Agile Development</a>）被认为是软件工程的一个重要的发展。它强调软件开发应当是能够对未来可能出现的变化和不确定性作出全面反应的。</font>
				</p>
				<p>
						<font face="Verdana">      敏捷开发被认为是一种“轻量级”的方法。在轻量级方法中最负盛名的应该是“<a href="http://www.itisedu.com/phrase/200603071747045.html" target="_new">极限编程</a>”（<a href="http://www.itisedu.com/phrase/200604231325295.html" target="_new">Extreme Programming</a>，简称为<a href="http://www.itisedu.com/phrase/200604231325145.html" target="_new">XP</a>）。而与轻量级方法相对应的是“重量级方法”的存在。重量级方法强调以开发过程为中心，而不是以人为中心。重量级方法的例子比如CMM/PSP/TSP。</font>
				</p>
				<p>
				</p>
		</font>
		<font face="Verdana"> </font><img src ="http://www.cnitblog.com/tilan/aggbug/21893.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2007-01-15 23:03 <a href="http://www.cnitblog.com/tilan/articles/21893.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何使用 SQL JSP 标准库</title><link>http://www.cnitblog.com/tilan/articles/21769.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Thu, 11 Jan 2007 08:54:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21769.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21769.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21769.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21769.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21769.html</trackback:ping><description><![CDATA[<p class="paragraph">完成本方法文档的学习后您应该能够使用 SQL JSTL 来： </p>
		<ul>
				<li>
						<p>
								<span class="list">查询 </span>
								<span class="list">Oracle 数据库。</span>
						</p>
				</li>
		</ul>
		<ul>
				<li>
						<p>
								<span class="list">向 Oracle 数据库中插入一行数据 </span>
						</p>
				</li>
				<li>
						<p>
								<span class="list">更新一列数据 </span>
						</p>
				</li>
		</ul>
		<h3 class="heading3">简介 </h3>
		<p class="paragraph">本文档演示如何使用 SQL JSP 标准标记库 (JSTL) 访问 Oracle 数据库。JSTL 的目的是为那些不熟悉或不适应脚本语言（如 Java）的 JSP 页面作者提供方便。过去，我们在 JSP 页面中使用 scriptlet 来处理动态数据。而 JSTL 的目的就是使用 JSTL 标记来代替 scriptlet。在本方法文档中，我们将学习如何使用 JSTL 来访问数据库。http://java.sun.com/jstl/sql 页面上提供了所有的 SQL 标记。</p>
		<h3 class="heading3">软件需求 </h3>
		<ul class="list">
				<li class="list">
						<p>
								<span class="list">Oracle9<i class="ITALICiBodycopy">i</i> Database version 9.0.1 或更新版本。您可从 Oracle 技术网<a href="http://www.oracle.com/technology/global/cn/software/products/oracle9i/index.html">下载</a> Oracle9<i class="ITALICiBodycopy">i</i> Database。</span>
								<span class="paragraph">
								</span>
						</p>
				</li>
				<li class="list">
						<p>
								<span class="paragraph">JDK1.2.x 或更高版本。可从<a href="http://java.sun.com/products">此处</a>下载。</span>
								<span class="paragraph">
								</span>
								<span class="paragraph">
								</span>
						</p>
				</li>
				<li class="list">
						<p>
								<span class="paragraph">Oracle9</span>
								<span class="list">
										<i class="ITALICiBodycopy">i</i>
								</span>
								<span class="paragraph">AS Container for Java(OC4J) version 9.0.3 或更新版本。可以在 OTN <a href="http://www.oracle.com/technology/global/cn/software/products/ias/index.html">下载</a>。</span>
						</p>
				</li>
				<li class="list">JSP 标准标记库。您可以使用 Apache.org 提供的<a href="http://jakarta.apache.org/builds/jakarta-taglibs/releases/standard/">标准标记库 1.0 Reference Implementation</a></li>
		</ul>
		<p class="heading3">使用数据库 JSTL 标记</p>
		<p class="paragraph" span="">JSTL 的数据库的库支持数据库查询、更新和事务处理。JSP 页面能够使用下面的指令导入这个库。<br /><code><span class="code">&lt;%@ taglib prefix="sql" uri="http://java.sun.com/jstl/sql" %</span><span class="nocolor">&gt;</span></code><span class="tabletext"><br /></span></p>
		<h3 class="heading3">准备数据库</h3>
		<p class="heading3">对于没有默认数据库的 JSP 页面，<code class="nocolor">&lt;sql:setDataSource&gt;</code> 能够准备一个数据库以供使用。<br />下面的代码展示了如何创建一个数据源。</p>
		<center>
				<table cellspacing="0" bgcolor="#86a5c3" border="0">
						<tbody>
								<tr>
										<td>
												<table cellspacing="0" cellpadding="3" width="100%" bgcolor="#ffffff" border="0">
														<tbody>
																<tr>
																		<td class="CODE" valign="top" bgcolor="#eaead5">
																				<pre>&lt;sql:setDataSource<br />  var="example"<br />  driver="oracle.jdbc.driver.OracleDriver"<br />  url="jdbc:oracle:thin:@localhost:1521:ORCL"<br />  user="scott"<br />  password="tiger"<br />/&gt;</pre>
																		</td>
																</tr>
														</tbody>
												</table>
										</td>
								</tr>
						</tbody>
				</table>
		</center>
		<p class="paragraph">
				<code class="nocolor">&lt;sql:setDataSource&gt;</code> 标签有如下属性：</p>
		<table width="90%" border="1">
				<tbody>
						<tr>
								<td class="tabletext">
										<b>属性</b>
								</td>
								<td class="tabletext">
										<b>说明</b>
								</td>
								<td class="tabletext">
										<b>是否需要</b>
								</td>
								<td class="tabletext">
										<b>默认</b>
								</td>
						</tr>
						<tr>
								<td class="tabletext">driver</td>
								<td class="tabletext">需要注册的 JDBC 驱动程序类的名称</td>
								<td class="tabletext">不</td>
								<td class="tabletext">无</td>
						</tr>
						<tr>
								<td class="tabletext">url</td>
								<td class="tabletext">用于数据库连接的 JDBC URL</td>
								<td class="tabletext">不</td>
								<td class="tabletext">无</td>
						</tr>
						<tr>
								<td class="tabletext">user</td>
								<td class="tabletext">数据库用户名</td>
								<td class="tabletext">不</td>
								<td class="tabletext">无</td>
						</tr>
						<tr>
								<td class="tabletext">password</td>
								<td class="tabletext">数据库密码 </td>
								<td class="tabletext">不</td>
								<td class="tabletext">无</td>
						</tr>
						<tr>
								<td class="tabletext">dataSource</td>
								<td class="tabletext">预先准备的数据库（字符串或<br /><code class="nocolor">javax.sql.DataSource</code>）</td>
								<td class="tabletext">不</td>
								<td class="tabletext">无</td>
						</tr>
						<tr>
								<td class="tabletext">var</td>
								<td class="tabletext">代表数据库的变量名</td>
								<td class="tabletext">不</td>
								<td class="tabletext">设置为默认</td>
						</tr>
						<tr>
								<td class="tabletext">scope</td>
								<td class="tabletext">代表数据库的变量的作用域</td>
								<td class="tabletext">不</td>
								<td class="tabletext">页面</td>
						</tr>
				</tbody>
		</table>
		<h3 class="heading3">
				<span class="heading3">查询与更新</span>
		</h3>
		<p class="paragraph">JSTL 可以使用 <code class="nocolor">&lt;sql:query&gt;</code> 从数据库读取数据并使用 <code class="nocolor">&lt;sql:update&gt;</code> 向数据库写入数据。这些标记可以通过 ? 占位符支持 SQL 命令，<span class="nocolor"><code>&lt;sql:param&gt;</code></span> 和 <code class="nocolor">&lt;sql:dateParam&gt;</code> 可以填充到这些占位符中。<br /></p>
		<p class="paragraph" align="center">
				<b>从数据库查询</b>
		</p>
		<center>
				<table cellspacing="0" bgcolor="#86a5c3" border="0">
						<tbody>
								<tr>
										<td>
												<table cellspacing="0" cellpadding="3" width="100%" bgcolor="#ffffff" border="0">
														<tbody>
																<tr>
																		<td class="CODE" valign="top" bgcolor="#eaead5">
																				<pre>
																						<span class="bodycopy">&lt;sql:query var="deejays"&gt;<br />  SELECT * FROM mytable<br />&lt;/sql:query&gt;</span>

&lt;%-- Get the column names for the header of the table --%&gt;
&lt;c:forEach var="columnName" items="${deejays.columnNames}"&gt;<br />  &lt;th&gt;&lt;c:out value="${columnName}"/&gt;&lt;/th&gt;<br />&lt;/c:forEach&gt;

&lt;%-- Get the value of each column while iterating over rows --%&gt;
&lt;c:forEach var="row" items="${deejays.rows}"&gt;<br />  &lt;tr&gt;<br />    &lt;c:forEach var="column" items="${row}"&gt;<br />      &lt;td&gt;&lt;c:out value="${column.value}"/&gt;&lt;/td&gt;<br />    &lt;/c:forEach&gt;<br />  &lt;/tr&gt;<br />&lt;/c:forEach&gt;</pre>
																		</td>
																</tr>
														</tbody>
												</table>
										</td>
								</tr>
						</tbody>
				</table>
		</center>
		<p class="paragraph" align="left">
				<code class="nocolor">&lt;sql:query&gt;</code> 标记可用来查询数据库，核心标记 <code class="nocolor">&lt;c:forEach&gt;</code> 用于遍历结果集。<code class="nocolor">&lt;c:forEach&gt;</code> 标记读取查询中的每一行。您可以使用列名来获取行中每一列的值。核心标记 <code class="nocolor">&lt;c:out&gt;</code> 用于输出值。</p>
		<p class="paragraph" align="center">
				<b>更新表中的一列</b>
		</p>
		<center>
				<table cellspacing="0" bgcolor="#86a5c3" border="0">
						<tbody>
								<tr>
										<td>
												<table cellspacing="0" cellpadding="3" width="100%" bgcolor="#ffffff" border="0">
														<tbody>
																<tr>
																		<td class="CODE" valign="top" bgcolor="#eaead5">
																				<pre>&lt;sql:update var="updateCount" dataSource="${example}"&gt;
  UPDATE mytable SET name=? 

    &lt;sql:param value="Scott Tiger"/&gt; WHERE nameid=1<br />&lt;/sql:update&gt;</pre>
																		</td>
																</tr>
														</tbody>
												</table>
										</td>
								</tr>
						</tbody>
				</table>
		</center>
		<p class="paragraph">
				<code class="nocolor">&lt;sql:update&gt;</code> 标记用于 DML 操作。在更新查询中可以有 '?'。然后您可以使用 <code class="nocolor">&lt;sql:parm&gt;</code> 把一个值与 ? 相关联。<code class="nocolor">&lt;sql:parm&gt;</code> 的值可以从一个变量中获取（如 HTTP 参数）。</p>
		<p class="paragraph">您可以查看完整的<a href="http://www.oracle.com/technology/sample_code/tech/java/codesnippet/jsps/JstlSql.jsp.html">源代码</a>以了解如何创建一个表以及其撤消方法。 </p>
		<h3 class="heading3">安装</h3>
		<p class="paragraph">为了运行此代码，您需要配置 OC4J 以使用 JSTL。</p>
		<ul>
				<li>
						<span class="list">把从 Apache 下载的文件 <code class="nocolor">jakarta-taglibs-standard-1.0.zip</code> 解压缩到一个目录中，比如 <code class="nocolor">D:\mydir</code>。</span>
				</li>
		</ul>
		<ul>
				<li>
						<span class="list">把目录 <code class="nocolor">D:\mydir\jakarta-taglibs\jstl-1.0\lib</code> 中的文件复制到 <code class="nocolor">&lt;J2EE_HOME&gt;\default-web-app\WEB-INF\lib</code>。如果不存在目录 <code class="nocolor">&lt;J2EE_HOME&gt;\default-web-app\WEB-INF\lib</code>，则创建它。</span>
				</li>
		</ul>
		<ul>
				<li>
						<span class="list">把文件 <code class="nocolor">JstlSql.jsp</code> 复制到目录 <code class="nocolor">&lt;J2EE_HOME&gt;\default-web-app\examples\jsp</code></span>
				</li>
		</ul>
		<ul>
				<li>
						<span class="list">从 http://&lt;host-name&gt;:&lt;port&gt;/examples/jsp/JstlSql.jsp 运行它</span>
				</li>
		</ul>
		<h3 class="heading3">参考</h3>
		<ul>
				<li>
						<a href="http://download-west.oracle.com/docs/cd/A97688_09/generic.903/a97678/toc.htm">
								<span class="list">Oracle9iAS Containers for J2EE JSP 标记库与实用工具参考</span>
						</a>
				</li>
		</ul>
		<ul>
				<li class="list">
						<a href="http://java.sun.com/products/jsp/jstl/">http://java.sun.com/products/jsp/jstl/</a>
				</li>
		</ul>
		<h3 class="heading3">总结</h3>
		<p class="paragraph">在阅读完本文档后，您应该已经理解了如何使用 JSTL 标记来访问数据库。</p><img src ="http://www.cnitblog.com/tilan/aggbug/21769.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2007-01-11 16:54 <a href="http://www.cnitblog.com/tilan/articles/21769.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对企业级Java应用程序及其部署进行建模</title><link>http://www.cnitblog.com/tilan/articles/21768.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Thu, 11 Jan 2007 08:52:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21768.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21768.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21768.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21768.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21768.html</trackback:ping><description><![CDATA[<h3>摘要</h3>
		<p>　　如今，UML用于对软件系统进行建模已有多年时间。然而，我极少看到有关对现代软件系统建模和技术的详细讨论或实例。例如，对应用程序及其部署建模需要开发各类原型系统，并需要使用有组织的方法来设计图的作用范围和布局，使其真正发挥作用。在复杂的环境中，建模显得尤为重要，它不仅能为编写代码的软件工程师带来好处，而且负责正确配置和部署软件系统的软件配置管理团队和生产服务团队也能从中获益良多。本文演示了对现代软件建模的几种方法，这些方法可用于精确而简明地交流架构方面的细节。</p>
		<h3>简介</h3>
		<p>　　不久以前，有用的企业应用程序还是由少数实体bean、较多的会话bean和一些JSP构建而成。EJB被打包在JAR文件中，而JSP则被简单地存储在Web服务器的类路径中。如果软件业还能有什么让您所称道的，那就是软件在大小、功能和复杂性方面呈几何指数增长。软件大小已经增长到每个企业应用程序的各个部分都被存储在压缩格式的WAR和EAR文件中的程度。软件系统的复杂性需要高效的建模，以便帮助管理设计和相互关系。软件功能现在已经处于这样一个级别：需要定义一个完整的新范型——即服务，才能对其进行管理。</p>
		<p>　　必须对软件复杂性进行管理，因为它对企业的影响很大。这种管理可以同时采取规划和通信的形式。现在，可以使用UML来帮助规划软件的架构、设计和部署。还可以使用它把这些规划发送到企业中必须创建、安装和维护企业软件的各个部门中。</p>
		<p>　　如果是对代码进行建模，UML 1 X就已经很不错了。而当对软件系统的打包和部署进行建模时，它就显得不够了。随着UML 2.0的出现，UML对核心软件系统建模的能力大为增强。然而，UML 2.0真正耀眼的地方是在对软件打包和部署建模时。</p>
		<p>　　本文的目的是演示对现代软件系统进行建模的几种有效方法，这些方法可用于把架构、设计和部署方面的细节精确而简明地传达给企业中的相关负责部门。我并没有声明这是建模现代软件的惟一方法。UML的建模语言相当丰富。然而，如果您不确定如何使用UML.2.0进行快速建模，尤其是对特定于BEA WebLogic Platform建模的信息，本文将会为您提供帮助。</p>
		<h3>建模难题——少就是多</h3>
		<p>　　我喜欢更加敏捷的建模方法。对软件系统进行建模不会使公司直接获益，但可执行软件却可从中受益。然而，建模是一种有效的通信工具，有助于让整个公司就如何构建、部署和操作软件达成一致的认识。</p>
		<p>　　在对软件建模时，其实少就是多。代码随着时间而增多，而模型则是静止的，它是某个时刻设计思想的快照。因此，对软件系统的每一个细节都建模并没有太大用处。软件系统的细节是在有机变化的。最佳方法是对软件的核心部分进行建模。这些模型往往能够在相当长的时间内发挥作用。</p>
		<h3>对代码进行建模</h3>
		<p>　　我将从一个特定于代码的模型开始，然后再逐渐转到软件打包和部署图上。在此过程中，我认为您可以很好地理解什么样的细节级别适合于您的企业。有一点很重要，即，您要不断地询问自己，“这个图可以帮助大家理解设计吗？”。</p>
		<p>　　首先，让我们看一看我称之为“over-modeling”的建模方式。图1给出了一个使用了bean托管持久性的实体bean的UML图。在这个图中，您可以看到3个主接口和它们的根接口，以及实现类。</p>
		<p align="center">
				<a href="http://dev2dev.bea.com/images/2006/05/modeling-figure1-lg.jpg" target="_blank">
						<img height="295" alt="图1. 对一个EJB进行Over-modeling" src="http://dev2dev.bea.com.cn/images/image060712001.jpg" width="450" vspace="4" border="0" />
				</a>
				<br />图1. 对一个EJB进行Over-modeling（单击图像查看大图）</p>
		<p>　　乍一看，图中的内容似乎很多。其实，这个模型不过是较为详细而已，实际上内容很少。该图实际上显示了一个实体bean的基本结构。稍微了解一点EJB知识的工程师就会发现这个图其实很简单。如果您提供一个充满了这种“没有价值的东西”的完整设计文档，工程师们很快就会感到厌烦，并拒绝接受这种“无用的”设计文档。</p>
		<p>　　提示！——对系统的重要部分而不是无关紧要的部分进行建模。</p>
		<p>　　让我们看一看同一个图经过修改后的版本。它删除了无价值的内容，并将重点放在重要的内容上。样板代码和构件几乎从不需要建模，除非它们能给图带来特别的好处（比如提供上下文）。例如，表示一个像ejbActivate()这样的函数不能为图增加清晰度或内容，因此也就无需对其进行建模。EJB规范中说方法必须出现在实现类中，并不意味着它需要出现在模型中。</p>
		<p align="center">
				<img height="241" alt="图2. 一个简明的EJB模型" src="http://dev2dev.bea.com.cn/images/image060712002.jpg" width="353" border="0" />
				<br />图2. 一个简明的EJB模型</p>
		<p>　　除了在建模上显而易见的区别之外，两张图之间还有一处基本的区别：stereotype的自由使用。Stereotype是一种传达有关任意模型元素的不相关信息的强大方式。使用stereotype的另一个有利之处是，定义的信息是由对象而不是图来传达的。在图1中，JNDI信息表示在图中的一条注释中。这可以使JNDI信息特定于图。在图2中，我为捕捉JNDI信息的&lt;&lt;EntityBean&gt;&gt; stereotype定义了两个标记值。标记定义是stereotype的属性。通过为stereotype定义标记定义，实现了下面两个目标：为架构师和设计师提供一些有关每个stereotype中应该包含何种信息的提示，并为企业引入了一些建模标准。通过使用stereotype并填充相关的标记定义值，可以让包含该元素的每个图都能使用这些信息。大多数UML工具都允许有选择性地隐藏stereotype及其标记定义，您可以定制化每个图，同时无需修改任何重要的模型数据。在本文后面，我将提供一个示例的stereotype类别。</p>
		<p>　　提示！——使用stereotype对不相关信息进行建模。</p>
		<h3>对可部署代码进行建模</h3>
		<p>　　现在，我将使用功能代码，并对应该如何把它打包到一个可部署模块中进行建模。在软件建模项目中，这通常是会被彻底忽视的重要一步。然而，在这个领域中，误解也是很常见的，而这些误解通常会增加公司的成本，因为它会导致项目出现延期并重新编码以纠正问题。</p>
		<p>　　J2EE项目通常被打包为一个EAR或WAR文件。JAR文件仍然在使用，但是现在通常作为EAR或WAR文件的元素而存在。在UML中，文件被表示为Artifact。Artifact用于对节点上的内容进行建模。（节点通常表示一种设备，通常为计算机，但是它也可以表示一种虚拟设备。）例如，在UML中，文档、文件、可执行文件和库都被建模为Artifact。可部署的软件也在这种定义的范围之内，所以也可以把它建模为Artifact。</p>
		<p>　　在整个领域内，这些类型的图相当少见。我仅看见过两家公司能够实现这种级别的建模。创建这些模型的人通常会掉入到对可部署模块的“定义”建模的陷阱中去。看看图3您就会明白我的意思了。</p>
		<p align="center">
				<a href="http://dev2dev.bea.com/images/2006/05/modeling-figure3-lg.jpg" target="_blank">
						<img height="301" alt="图3. 常见的可部署建模" src="http://dev2dev.bea.com.cn/images/image060712003.jpg" width="450" vspace="4" border="0" />
				</a>
				<br />图3. 常见的可部署建模（单击图像查看大图）</p>
		<p>　　这个图的第一个错误之处就是“对显而易见的内容进行建模”。“App-Inf”、“webapp”和“meta-inf”目录没有为图增加任何价值。类似地，对“自定义属性”文件的建模也很值得商榷。这只是图中的无用信息。图3的第二个错误之处是，它仅对模块的“定义”进行了建模。它仍然以软件为重点，展示了位于com.bea.customer Java包结构中的Customer EJB。对包结构建模可以增加价值，这取决于企业的需要，但是这个图确实还有疏漏之处。它应该包含比软件工程师需要的还多的信息，还应该包括对于软件配置管理团队来说重要的信息。这些“缺失的”信息就是部署模块的“上下文”。必须运行软件的团队需要知道如何部署它，因此上下文对于他们来说很重要。</p>
		<p>　　提示！——部署模型需要说明“定义”和“上下文”。</p>
		<p>　　看一看图4就很清楚了。这个图既说明了部署模块的定义，也说明了模块必须存在于其中的上下文。上下文提供了模块正确发挥功用所需的资源。这就告诉了SCM和生产团队要确保提供这些资源，否则软件就不会运行。</p>
		<p align="center">
				<a href="http://dev2dev.bea.com/images/2006/05/modeling-figure4-lg.jpg" target="_blank">
						<img height="234" alt="图4. 较好的可部署模块图" src="http://dev2dev.bea.com.cn/images/image060712004.jpg" width="450" vspace="4" border="0" />
				</a>
				<br />图4. 较好的可部署模块图（单击图像查看大图）</p>
		<p>　　现在，对于任何必须为应用程序创建可执行环境的人，这个图都可以派上用场。现在，他们知道需要在环境中配置一个JMS队列和一个JDBC驱动程序，因为这是应用程序的需要。有关JMS队列和JDBC驱动程序的关键信息也包含在内。虽然我在这里没有对每一个细节都进行建模，但是通过对环境依赖性进行建模，我为SCM团队提供了询问其他问题的机会。此外，数据库管理员可能会喜欢看到这个图。他们很可能会提出有关客户数据库、索引、键之类的一些问题。</p>
		<p>　　一个优秀的模型不只回答问题，它还会让人们思考和询问其他的问题。一开始，当您想建模来回答问题和记录决策时，这似乎有些奇怪。这没错，但是UML的设计目的不是对一切内容进行建模。某些内容，比如数据库设计，最好使用更加专业的工具进行建模。某些信息最好以纯文本格式进行定义。一个优秀的模型可作为企业其他部门进行具体思考和设计的出发点。</p>
		<p>　　提示！——优秀的模型可以回答一些问题并提出一些其他的问题。</p>
		<p>　　您可能已经注意到了，这些图中有些内容是相同的。每个图都有其特定的目的，并包含针对特定受众的消息。不能为了图本身而创建图，创建图是为了与预定的受众进行交流。每个图都应该包含一条有针对性的消息。这条消息可能是“下面是这个[概念|类|对象|节点]与其直接邻居的关系”，也可能是“下面是方法的行为”。就像所有的交流形式一样，如果没有特定的消息要传达，很可能是该消息的接收方根本无法理解您的消息。</p>
		<p>　　提示！——每个图都应该有其特定的用途。</p>
		<p>　　还要注意，在图4中，我避免对JMS队列和JDBC驱动程序的详细细节进行建模。JMS队列可以有另外的标记值，表示缓存大小、分页目录、启动时暂停的插入，等等。通常不会对这些数据建模，因为这类内容是随环境不同（也就是说，当把软件从测试环境转移到生产环境时）而不同的，而且这些值在应用程序调优过程中也会变化。提前指定它们的值通常不会带来什么好处。不过有一个例外：如果您的公司有这样一个策略——在进行性能调优之后采用最后的生产配置值（即，应用程序配置值），那么我将在模型中包含这些值。</p>
		<h3>部署图</h3>
		<p>　　出于某种原因，在大多数UML书籍中，部署图通常会受到轻视。在很多书中，有关部署图的章节只不过2到6页内容，而且只给出最简单的示例图。而我认为，这些图恰恰是UML中最重要的部分。这些图允许您表达一个软件系统或者甚至是整个IT部门的架构，而且在找出生产环境中性能问题的根源时，它们可能是一个关键因素。</p>
		<p>　　一家典型的公司有很多环境：开发环境（至少一个）、测试环境（可能多于一个）、性能环境、登台环境，当然还有生产环境。许多企业还需要维护一个灾难恢复环境，以防自然或人为的灾难。但是，没有必要为所有这些环境维护部署图。开发、测试和性能环境通常变化得太快，以至于无法为部署在这些环境中的软件进行认真的建模。登台、生产和灾难恢复环境在本质上是（或者说应该是）类似的，因此只对生产环境建模通常就足以捕捉到部署空间的本质。</p>
		<p>　　这正是UML 2.0真正的用武之地。从UML 2.0中的变化来看，显然很多人都觉得部署图相当重要。在项目的早期阶段，我建议在逻辑级别上对部署进行建模。我还建议在项目的最早期创建这些图。部署是项目规划的一个关键部分，而不是事后考虑。</p>
		<p>　　我所看到的该领域中的部署图大多数止于逻辑模型。重要的是对所有系统部署进行建模，而不是单独对每个软件系统进行建模。筒仓应用程序大行其道的时代已经一去不复返了。我们现在生活在一个互连系统的世界，而这些互连需要进行规划和映射。</p>
		<p>　　事实是，优秀的实用部署图能帮助您找出生产中的问题。系统是否是在投入到生产中时还运转良好，而现在却出了问题呢？有时候，显示征兆的系统并不是问题的根源所在。相反，有可能是上游或下游的系统没有按照期望运行，而性能问题表现在系统的完全不同的部分中了。</p>
		<p>　　有一个古老的寓言是这么说的，有一个国王要求3个天生的盲人通过触摸大象的一个部分来描述大象的样子。摸到大象耳朵的盲人认为大象像一个簸箕，摸到大象尾巴的盲人认为大象像一把刷子，抱着大象腿的盲人认为大象像一棵树。生产环境就像这头大象一样，由许多部分组成。如果您无法窥见全貌，那么就很可能像寓言中的盲人一样看法片面。您对任何问题真正本质的看法都可能是错误的。然而，对一个大型系统进行建模不是一件简单的事情。如果试图在一幅图中表示过多的生态系统，那么结果很可能是在墙上贴满了许多无用的图。</p>
		<p>　　因此，我推荐使用层次结构来组织具体的部署图。该层次结构的顶部是整个软件生态系统的全貌。由于这类图的本质，它将包含很少的具体部署细节，而将重点放在大型系统的逻辑关系上。</p>
		<p>　　图5是一个生产环境的简单视图。从这幅简单的图中，可以看到整个架构是中心辐射型的。还可以看到，整个企业在逻辑上被划分为7个不同的组，还可以从中心节点的名称“服务基础架构”（Service Infrastructure）上猜到这些逻辑、功能性区域是通过一个服务层连接起来的。从这个图出发可以深入研究获得更多的细节。让我们来仔细看一下客户关系管理(Customer Relations Management，CRM)系统。</p>
		<p align="center">
				<img height="327" alt="图5.部署概览图" src="http://dev2dev.bea.com.cn/images/image060712005.jpg" width="450" border="0" />
				<br />图5.部署概览图</p>
		<p>　　在图6中，我修改了图的范围，以便聚焦于CRM系统。在这个图中，您可以看到子系统中包含的3个商业应用程序：Siebel、Salesforce.com和ACTI。Salesforce.com实际上就是一个基于Web的应用程序，驻留在Salesforce.com服务器上，但是从企业的角度来看，它被认为是企业的一部分。</p>
		<p align="center">
				<img height="267" alt="图6. CRM域" src="http://dev2dev.bea.com.cn/images/image060712006.jpg" width="450" border="0" />
				<br />图6. CRM域</p>
		<p>　　从上图中您还可以看到，有2个自主开发的子系统。第一个子系统允许客户查看他们的帐户状态，定购产品，并使用Siebel系统提供的其他“自助”活动。第二个是CSR Command Center，它是一个自定义的应用程序，允许公司的客户服务代表代表客户执行任何功能。此外，它为客户提供通常不可用的功能，比如在联系人成为客户之前跟踪他们的前导信息。</p>
		<p>　　接下来，我将聚焦于客户自助（Customer Self Service，CSS）子系统，以便更加详细地了解这个系统。</p>
		<p align="center">
				<a href="http://dev2dev.bea.com/images/2006/05/modeling-figure7-lg.jpg" target="_blank">
						<img height="303" alt="Figure 7" src="http://dev2dev.bea.com.cn/images/image060712007.jpg" width="450" vspace="4" border="0" />
				</a>
				<br />图7. CSS物理部署细节（单击图像查看大图）</p>
		<p>　　图7给出了一幅非常详细的实际部署图。事实上，这个图已经是密密麻麻的了。注意Domain实例中的嵌套元素（图中心的大方框）。一般来说，我发现嵌套的最大深度是3层。如果超过3层，图就开始超出其消息的范围。这个图说明了如下内容：</p>
		<ul>
				<li>这个部署中总共有4台物理机器：2台是Sun Fire v40z机器，另外2台数据库服务器是Dell PowerEdge 6850。显示了PowerEdge的IP地址，但是SunFire机器的IP地址则没有显示。这是因为SunFire机器支持虚拟服务器。对这个图来说，重要的是虚拟服务器的IP地址，而不是物理主机的IP。 
</li>
				<li>总共在SunFire机器中创建了7台虚拟机器。这些虚拟机器与Java VM无关。它们是功能完整的机器，具有自己的IP地址。Java VM可以运行在这些虚拟机器中。 
</li>
				<li>一个WebLogic Platform域包含了一台管理服务器和两个集群。 
</li>
				<li>WebPortal集群包括4台服务器。还可以看到将每台服务器连接到虚拟机器的部署线。这使得SCM如何建立集群的过程变得很清楚（至少是在结构化的级别上）。 
</li>
				<li>名为BackEnd的第二个集群，它包含2台WebLogic服务器，还可看到这两台服务器部署到虚拟机器上的位置。 
</li>
				<li>一个包含客户模式的Oracle数据库，运行在Dell PowerEdge 6850机器上。 
</li>
				<li>一个硬件负载均衡器，它把URL“customer.bea.com”转换为WebLogic Portal服务器所使用的相应IP地址。 
</li>
				<li>最后，我在底部为实际部署映射定义了一个图例，它会提供有关已部署的实例类型的附加细节。 </li>
		</ul>
		<p>　　在这幅图中，我提供了大量对开发、SCM和生产服务团队有用的信息。</p>
		<p>　　提示！——三层的嵌套通常就够用了。</p>
		<p>　　然而，还需要另一幅聚焦特定于WebLogic Server的细节的图。这幅图主要面向开发人员和SCM团队。图8就是一个例子。</p>
		<p align="center">
				<a href="http://dev2dev.bea.com/images/2006/05/modeling-figure8-lg.jpg" target="_blank">
						<img height="411" alt="图8. 聚焦WebLogic Server的图" src="http://dev2dev.bea.com.cn/images/image060712008.jpg" width="450" vspace="4" border="0" />
				</a>
				<br />图8. 聚焦WebLogic Server的图（单击图像查看大图）</p>
		<p>　　下面，我将展示另一些特定于WebLogic Server的配置信息。您可以看到集群地址、多播地址和多播端口。您还可以看到创建了一个名为customerDS的数据源，其目标是BackEnd集群，这意味着该集群中的2台服务器都可以访问这个数据源。我可以使用类似的结构对JMS主题和队列、持久性存储等建模。</p>
		<p>　　此外，您还可以看到，这里还显示了前面图中定义的可部署模块，以及它们如何关联到WebLogic集群。BackEnd集群（及其所有服务器）上都部署了customer.ear文件。类似地，WebPortal集群及其服务器上也部署了customer.ear文件。通过引用可部署模块图，可以快速关闭对这些信息的循环。</p>
		<h3>WebLogic Platform Stereotype分类</h3>
		<p>　　正如早先提到的那样，UML stereotype是一种功能强大的注释工具，它允许捕捉关于模型中元素的附加信息。在大多数建模工具中，都对J2EE、Web services和其他技术有着不同的UML分类。如果您的工具没有提供这些预定义的分类，或者如果预定义的分类不能满足您的特定要求，您可以快速创建自己的分类。图9说明了本文中使用的分类。您可以对这个例子进行定制化，来满足您的特定要求。</p>
		<p align="center">
				<a href="http://dev2dev.bea.com/images/2006/05/modeling-figure9-lg.jpg" target="_blank">
						<img height="450" alt="图9. WebLogic Platform模型的一种Stereotype分类" src="http://dev2dev.bea.com.cn/images/image060712009.jpg" width="331" vspace="4" border="0" />
				</a>
				<br />图9. WebLogic Platform模型的一种Stereotype分类（单击图像查看大图）</p>
		<p>　　对于Stereotype，您要记住的重要一点就是，要以一致的方式使用它们。模型必须经过一个复审过程，以确保它们满足您所在公司所设立的标准。这可以帮助您创建标准化的模型，从而有助于让IT部门之间和子团队实现清楚的交流。独立地（即，在它自己的模型中）维护stereotype模型，然后把stereotype导入到项目模型中。这将为您节省时间，并确保您在当前项目中使用的是最新的stereotype分类。</p>
		<h3>结束语</h3>
		<p>　　在本文中，我涵盖了大量的细节。尽管进行高效的建模需要学习的东西肯定比我在这里讲的要多，本文应该可以帮助您创建更好的模型和图，改进IT部门的不同团队之间对于这些细节的交流，并提供一些标准和方法来帮助您更加有效地管理软件系统。记住，建模只是一种辅助性工作，真正重要的还是可以执行的软件系统。</p>
		<h3>参考资料</h3>
		<ul>
				<li>
						<a href="http://www.agilemodeling.com/" target="_blank">Agile Modeling</a>——Agile Modeling组织的主页 
</li>
				<li>Dev2Dev站点上的<a href="http://dev2dev.bea.com/role/architect/" target="_blank">Arch2Arch资源中心</a>——架构师的参考资源 </li>
		</ul>
		<p>
				<b>原文出处:</b>
				<a href="http://dev2dev.bea.com/pub/a/2006/05/modeling-enterprise.html" target="_blank">http://dev2dev.bea.com/pub/a/2006/05/modeling-enterprise.html</a>
		</p>
		<!--文章其他信息--><img src ="http://www.cnitblog.com/tilan/aggbug/21768.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2007-01-11 16:52 <a href="http://www.cnitblog.com/tilan/articles/21768.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解决MVC下分页显示的问题</title><link>http://www.cnitblog.com/tilan/articles/21694.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Tue, 09 Jan 2007 15:09:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21694.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21694.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21694.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21694.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21694.html</trackback:ping><description><![CDATA[前几天做一个系统，用到的是Tomcat+struts+Mysql的MVC框架。由于很多模块都需要分页，想写一个分页的方法。常见的方法每次翻页都查询一次数据库，从ResultSet中只取出一页数据（使用rs.last();rs.getRow()获得总计录条数，使用rs.absolute()定位到本页起始记录）。这种方式在某些数据库(如oracle)的JDBC实现中差不多也是需要遍历所有记录，实验证明在记录数很大时速度非常慢。
<p>　　至于缓存结果集ResultSet的方法则完全是一种错误的做法。因为ResultSet在Statement或Connection关闭时也会被关闭，如果要使ResultSet有效势必长时间占用数据库连接。</p><p>　　因此比较好的分页做法应该是每次翻页的时候只从数据库里检索页面大小的块区的数据。这样虽然每次翻页都需要查询数据库，但查询出的记录数很少，网络传输数据量不大，如果使用连接池更可以略过最耗时的建立数据库连接过程。而在数据库端有各种成熟的优化技术用于提高查询速度，比在应用服务器层做缓存有效多了。<br /><br />　　所以我用到了从数据库中查询当前页面记录的办法。由于设计到公司核心代码的缘故，我就简单的用一个图书的例子介绍一下是我的思路：<br /><br /><strong>开发思路</strong>：<br />　　我是用Struts的，当然就要使用到MVC这个模式，分页的时候也是这样的。<br />　　首先要有一个和数据库链接的bean,我们暂时叫DBUtil吧，这里面封装了很多和数据库有关的东西，比如有查询，修改，插入等方法，这是一个基本的类，这里我们用到的是查询，这个方法返回的数据类型是Object[][]。这里大家要注意一下，你也可以返回别的类型，但是一定要注意把后面对应的程序也修改一下。一下是这个类的部分代码：<br /></p><p class="code">package com.model;<br /><br />import com.attribute.Constants;<br /><br />import java.sql.*;<br />import java.util.*;<br /><br />/**<br />* Created by IntelliJ IDEA.<br />* User: 7612CE<br />* Date: 2005-6-2<br />* Time: 21:41:38<br />* To change this template use Options | File Templates.<br />*/<br />public class DBUtil {<br /><br />String sDBDriver=Constants.DBDriver;<br />String url=Constants.DBUrl;<br />String dbUser=Constants.DBUser;<br />String dbPassword=Constants.DBPassword;<br />Connection conn=null;<br />PreparedStatement stmt=null;<br />ResultSet rs=null;<br /><br />public DBUtil()throws ClassNotFoundException{<br />try{<br />Class.forName(sDBDriver);<br />conn=DriverManager.getConnection(url,dbUser,dbPassword);<br />}catch(Exception e){<br />System.out.println("DBUtil():"+e.getMessage());<br />}<br />}<br />/**<br />* Search some record in object table<br />* @param sql sql segment<br />* @ param map values for match<br />* @return Collection<br />*/<br />public Object[][] doSearch(String sql,Object [] data)throws SQLException{<br />PreparedStatement stmt = conn.prepareStatement(sql);<br />for(int i=0;data!=null&amp;&amp;i System.out.print("the aql is ="+sql);<br />System.out.println("data is " + data[i]);<br />stmt.setObject(i+1,data[i]);<br />}<br />ResultSet rset = stmt.executeQuery();<br />ResultSetMetaData rsm = rset.getMetaData();<br />int col = rsm.getColumnCount();<br />ArrayList list = new ArrayList();<br />//Each element of list contains a record of resultset<br />while(rset.next()){<br />list.add(getLine(rset,col));<br />}<br /><br />if(list.size()==0||col==0){<br />closePrepStmt();<br />return null;<br />}<br /><br />closePrepStmt();<br />//Construct box as a data matrix<br />Object[][] box = new Object[list.size()][col];<br />for(int i=0;i for(int j=0;j {<br />box[i][j] =((Object[])list.get(i))[j];<br />}<br />return box;<br />}<br />　　由于是写分页的，当然也是要有一个page的类，具体代码如下，由于有很多注释，不用一一介绍了：<br /></p><p class="code">package com.util;<br /><br />/**<br />* Title: 分页对象<br /><br />* Description: 用于包含数据及分页信息的对象<br /><br />*Page类实现了用于显示分页信息的基本方法，但未指定所含数据的类型，<br />*可根据需要实现以特定方式组织数据的子类，<br /><br />*如RowSetPage以RowSet封装数据，ListPage以List封装数据<br /><br />* Copyright: Copyright (c) 2002 <br /><br />* @author evan_zhao@hotmail.com <br /><br />* @version 1.0<br />*/<br />public class Page implements java.io.Serializable {<br />public static final Page EMPTY_PAGE = new Page();<br />public static final int DEFAULT_PAGE_SIZE = 2;<br />public static final int MAX_PAGE_SIZE = 2;<br /><br />private int myPageSize = DEFAULT_PAGE_SIZE;<br /><br />private int start;<br />private int avaCount,totalSize;<br />private Object[][] data=null;<br /><br />private int currentPageno;<br />private int totalPageCount;<br /><br />/**<br />* 默认构造方法，只构造空页<br />*/<br />public Page(){<br />this.init(0,0,0,DEFAULT_PAGE_SIZE,null);<br />}<br />/**<br />*构造分页对象<br />*@param crs 包含一页数据的OracleCachedRowSet<br />*@param start 该页数据在数据库中的起始位置<br />*@param totalSize 数据库中包含的记录总数<br />*@param pageSize 本页能容纳的记录数<br />*/<br />public Page(Object[][] crs, int start, int totalSize, int pageSize) {<br />try{<br />int avaCount=0;<br />if (crs!=null) {<br />avaCount = crs.length;<br />}<br />data = crs;<br />this.init(start,avaCount,totalSize,pageSize,data);<br />}catch(Exception ex){<br />throw new RuntimeException(ex.toString());<br />}<br />}<br /><br />/**<br />* 分页数据初始方法<br />* @param start 本页数据在数据库中的起始位置<br />* @param avaCount 本页包含的数据条数<br />* @param totalSize 数据库中总记录条数<br />* @param pageSize 本页容量<br />* @param data 本页包含的数据<br />*/<br />protected void init(int start, int avaCount, int totalSize, int pageSize, Object[][] data){<br /><br />this.avaCount =avaCount;<br />this.myPageSize = pageSize;<br /><br />this.start = start;<br />this.totalSize = totalSize;<br /><br />this.data=data;<br /><br />//System.out.println("avaCount:"+avaCount);<br />//System.out.println("totalSize:"+totalSize);<br />if (avaCount&gt;totalSize) {<br />//throw new RuntimeException("记录条数大于总条数？！");<br />}<br /><br />this.currentPageno = (start -1)/pageSize +1;<br />this.totalPageCount = (totalSize + pageSize -1) / pageSize;<br /><br />if (totalSize==0 &amp;&amp; avaCount==0){<br />this.currentPageno = 1;<br />this.totalPageCount = 1;<br />}<br />//System.out.println("Start Index to Page No: " + start + "-" + currentPageno);<br />}<br /><br />public Object[][] getData(){<br />return this.data;<br />}<br /><br />/**<br />* 取本页数据容量（本页能包含的记录数）<br />* @return 本页能包含的记录数<br />*/<br />public int getPageSize(){<br />return this.myPageSize;<br />}<br /><br />/**<br />* 是否有下一页<br />* @return 是否有下一页<br />*/<br />public boolean hasNextPage() {<br />/*<br />if (avaCount==0 &amp;&amp; totalSize==0){<br />return false;<br />}<br />return (start + avaCount -1) &lt; totalSize;<br />*/<br />return (this.getCurrentPageNo() }<br /><br />/**<br />* 是否有上一页<br />* @return 是否有上一页<br />*/<br />public boolean hasPreviousPage() {<br />/*<br />return start &gt; 1;<br />*/<br />return (this.getCurrentPageNo()&gt;1);<br />}<br /><br />/**<br />* 获取当前页第一条数据在数据库中的位置<br />* @return<br />*/<br />public int getStart(){<br />return start;<br />}<br /><br />/**<br />* 获取当前页最后一条数据在数据库中的位置<br />* @return<br />*/<br />public int getEnd(){<br />int end = this.getStart() + this.getSize() -1;<br />if (end&lt;0) {<br />end = 0;<br />}<br />return end;<br />}<br /><br />/**<br />* 获取上一页第一条数据在数据库中的位置<br />* @return 记录对应的rownum<br />*/<br />public int getStartOfPreviousPage() {<br />return Math.max(start-myPageSize, 1);<br />}<br /><br /><br />/**<br />* 获取下一页第一条数据在数据库中的位置<br />* @return 记录对应的rownum<br />*/<br />public int getStartOfNextPage() {<br />return start + avaCount;<br />}<br /><br />/**<br />* 获取任一页第一条数据在数据库中的位置，每页条数使用默认值<br />* @param pageNo 页号<br />* @return 记录对应的rownum<br />*/<br />public static int getStartOfAnyPage(int pageNo){<br />return getStartOfAnyPage(pageNo, DEFAULT_PAGE_SIZE);<br />}<br /><br />/**<br />* 获取任一页第一条数据在数据库中的位置<br />* @param pageNo 页号<br />* @param pageSize 每页包含的记录数<br />* @return 记录对应的rownum<br />*/<br />public static int getStartOfAnyPage(int pageNo, int pageSize){<br />int startIndex = (pageNo-1) * pageSize + 1;<br />if ( startIndex &lt; 1) startIndex = 1;<br />//System.out.println("Page No to Start Index: " + pageNo + "-" + startIndex);<br />return startIndex;<br />}<br /><br />/**<br />* 取本页包含的记录数<br />* @return 本页包含的记录数<br />*/<br />public int getSize() {<br />return avaCount;<br />}<br /><br />/**<br />* 取数据库中包含的总记录数<br />* @return 数据库中包含的总记录数<br />*/<br />public int getTotalSize() {<br />return this.totalSize;<br />}<br /><br />/**<br />* 取当前页码<br />* @return 当前页码<br />*/<br />public int getCurrentPageNo(){<br />return this.currentPageno;<br />}<br /><br />/**<br />* 取总页码<br />* @return 总页码<br />*/<br />public int getTotalPageCount(){<br />return this.totalPageCount;<br />}<br /><br />}</p><p class="code"> </p><p class="code"> </p><p class="code">　　由于是用mvc这样的框架，所以在c这一层仅仅起到一个转向和收集页面信息的作用，所以这里我有写了一个分页查询的bean，暂时命名是PageCtBean，代码如下：<br /></p><p> </p><p class="code">package com.util;<br /><br />import com.model.DBUtil;<br />import java.sql.*;<br />/**<br />* Created by IntelliJ IDEA.<br />* User: 7612ce<br />* Date: 2005-6-23<br />* Time: 10:36:57<br />* To change this template use Options | File Templates.<br />*/<br />public class PageCtBean {<br />public final static int MAX_PAGE_SIZE = Page.MAX_PAGE_SIZE;<br />protected String countSQL, querySQL;<br />protected int pageNo,pageSize,startIndex,totalCount;<br />protected javax.sql.RowSet rowSet;<br />protected Page setPage;<br />protected Object[][] objTables=null;<br />protected Object[][] objCount=null;<br />protected Object[] obj=null;<br /><br />public PageCtBean(){<br /><br />}<br />/**<br />* 构造一查询出所有数据的PageStatement<br />* @param sql query sql<br />*/<br />public PageCtBean(String sql){<br />this(sql,1,MAX_PAGE_SIZE);<br />}<br />/**<br />* 构造一查询出当页数据的PageStatement<br />* @param sql query sql<br />* @param pageNo 页码<br />*/<br />public PageCtBean(String sql, int pageNo){<br />this(sql, pageNo, Page.DEFAULT_PAGE_SIZE);<br />}<br /><br />/**<br />* 构造一查询出当页数据的PageStatement，并指定每页显示记录条数<br />* @param sql query sql<br />* @param pageNo 页码<br />* @param pageSize 每页容量<br />*/<br />public PageCtBean(String sql, int pageNo, int pageSize){<br />this.pageNo = pageNo;<br />this.pageSize = pageSize;<br />this.startIndex = Page.getStartOfAnyPage(pageNo, pageSize);<br />this.querySQL = intiQuerySQL(sql, this.startIndex, pageSize);<br />}<br /><br /><br />/**<br />*生成查询一页数据的sql语句<br />*@param sql 原查询语句<br />*@ startIndex 开始记录位置<br />*@ size 需要获取的记录数<br />*/<br />protected String intiQuerySQL(String sql, int startIndex, int size){<br />StringBuffer querySQL = new StringBuffer();<br />querySQL.append(sql)<br />.append(" limit ")<br />.append(startIndex-1 )<br />.append(",").append(size);<br />return querySQL.toString();<br />}<br /><br />/**<br />*使用给出的对象设置指定参数的值<br />*@param obj 包含参数值的对象<br />*/<br />public void setObject(Object obj[]) throws SQLException{<br />this.obj=obj;<br />}<br /><br />public void setCountSql(String sql){<br />this.countSQL=sql;<br />}<br />/**<br />* 执行查询取得一页数据，执行结束后关闭数据库连接<br />* @return RowSetPage<br />* @throws SQLException<br />*/<br />public Page executeQuery() throws ClassNotFoundException{<br />System.out.println("executeQueryUsingPreparedStatement");<br />DBUtil DBean=new DBUtil();<br />try{<br />objCount = DBean.doSearch(this.countSQL,obj);<br />if (!objCount.equals(null)){<br />System.out.println("the count is ="+objCount[0][0].toString());<br />totalCount =Integer.parseInt(objCount[0][0].toString()) ;<br />System.out.println("the count is ="+totalCount);<br />} else {<br />totalCount = 0;<br />}<br />if (totalCount &lt; 1 )<br />return null;<br />objTables= DBean.doSearch(this.querySQL,obj);<br />this.setPage = new Page(this.objTables,startIndex,totalCount,pageSize);<br />return this.setPage;<br />}catch(SQLException sqle){<br />//System.out.println("executeQuery SQLException");<br />sqle.printStackTrace();<br /><br />}catch(Exception e){<br />e.printStackTrace();<br />throw new RuntimeException(e.toString());<br />}<br />return null;<br />}<br />/**<br />*取封装成Page的查询结果<br />*@return Page<br />*/<br />public Page getPage() {<br />return this.setPage;<br />}<br />}</p><p class="code"><br clear="all" />　　接下来是Action里面的代码，暂时定义这个Action 是ComputerAction，代码如下：<br /></p><p class="code">package com.action;<br /><br />import org.apache.struts.action.Action;<br />import org.apache.struts.action.ActionForward;<br />import org.apache.struts.action.ActionMapping;<br />import org.apache.struts.action.ActionForm;<br />import org.apache.commons.logging.Log;<br />import org.apache.commons.logging.LogFactory;<br /><br />import javax.servlet.http.HttpServletRequest;<br />import javax.servlet.http.HttpServletResponse;<br />import javax.servlet.http.HttpSession;<br /><br />import com.form.LoginForm;<br />import com.util.LoginBean;<br />import com.util.ComputerBean;<br />import com.util.BaseView;<br />import com.util.Page;<br />import com.model.FunctionManager;<br />import com.attribute.Constants;<br />import com.attribute.SQLBook;<br /><br />import java.sql.ResultSet;<br /><br />/**<br />* Created by IntelliJ IDEA.<br />* User: 7612CE<br />* Date: 2005-6-14<br />* Time: 13:31:34<br />* To change this template use Options | File Templates.<br />*/<br />public class ComputerAction extends BaseAction {<br />private Log log=LogFactory.getLog(this.getClass().getName());<br /><br />public ActionForward execute(ActionMapping mapping,<br />ActionForm Form,<br />HttpServletRequest request,<br />HttpServletResponse response){<br />boolean flag=false;<br />Object[][] obj=null;<br />Page page=new Page();<br />Integer id=new Integer(Constants.id);<br />String sql=SQLBook.Computer_select_SQL;<br />BaseView view=new BaseView();<br />String pageNo = request.getParameter("pageNo");<br />if (pageNo == null || pageNo.equals("null") || pageNo.length() &lt;= 0) {<br />pageNo = "1";<br />}<br />try{<br />Object[] table={id};<br />ComputerBean computerBean=new ComputerBean();<br />computerBean.setBeanDate(sql,table);<br />computerBean.setPageNo(pageNo);<br />page=computerBean.getResult();<br />obj=page.getData();<br />if(!obj.equals(null)){<br />flag=true;<br />view.setObject(obj);<br />request.setAttribute(Constants.QUERY_RESULT,view);<br />request.setAttribute("page",page);<br />}<br /><br />}catch(Exception ex){<br />ex.printStackTrace();<br />}<br />log.info("system print the flag ="+flag);<br />if(flag){<br />return(mapping.findForward(Constants.FORWARD_SUCCESS));<br />}else{<br />return(mapping.findForward(Constants.FORWARD_FAILURE));<br />}<br />}<br />}</p><p class="code">　　由于Action里面用到了查询的SQL语句，所有SQL语句写在一个特定的类中，这个类名定义为SQLBook,代码如下:<br /></p><p class="code">public class SQLBook {<br /><br />public SQLBook(){}<br />/**<br />* computer sql<br />*/<br /><br />public static final String Computer_select_SQL=<br />"select a.id,a.bookname,a.bookclass,b.classname,"+<br />"a.author,a.publish,a.bookno,a.content,a.prince,a.amount,"+<br />"a.Leav_number,a.regtime,a.picture from book a,bookclass b"+<br />" where a.Bookclass = b.Id and a.bookclass=? "+<br />" order by a.Id desc ";<br />public static final String Computer_select_count_sql=<br />"select count(*) from book a,bookclass b"+<br />" where a.Bookclass = b.Id and a.bookclass=? "+<br />" order by a.Id desc ";<br /><br />}</p><p class="code"><br />　　到此为止，基本上分页的代码基本完成，为了使得分页的代码共用，我把他封装成了一个标签，这个自定义的标签命名为PaginatorTag，代码如下：<br /></p><p class="code">package com.util;<br /><br />import java.io.IOException;<br />import javax.servlet.jsp.JspException;<br />import javax.servlet.jsp.tagext.BodyTagSupport;<br />import org.apache.commons.logging.Log;<br />import org.apache.commons.logging.LogFactory;<br />public class PaginatorTag extends BodyTagSupport {<br />protected Log log = LogFactory.getLog(this.getClass());<br />//以下是一标签中的一些属性，后面有较详细的介绍<br />int currentPage = 1;//当前页码<br />String url = "";//转向的地址<br />int totalSize = 0;//总的记录数<br />int perPage = 20;//每页显示的记录数目<br />boolean showTotal = true;//是否显示总数量<br />boolean showAllPages = false;//是否显示总页码<br />String strUnit ="";//计数单位<br /><br />//得到当前页码<br />public int getCurrentPage() {<br />return currentPage;<br />}<br />//设置当前页码<br />public void setCurrentPage(int currentPage) {<br />this.currentPage = currentPage;<br />}<br />//得到每页显示记录的数目<br />public int getMaxPerPage() {<br />return perPage;<br />}<br />//设置每页显示的记录数目<br />public void setMaxPerPage(int perPage) {<br />this.perPage = perPage;<br />}<br />//判断是否显示总的页码数目<br />public boolean isShowAllPages() {<br />return showAllPages;<br />}<br />//设置是否显示总的页码数目<br />public void setShowAllPages(boolean showAllPages) {<br />this.showAllPages = showAllPages;<br />}<br />//判断是否显示总的记录数目<br />public boolean isShowTotal() {<br />return showTotal;<br />}<br />//设置是否显示总的记录数目<br />public void setShowTotal(boolean showTotal) {<br />this.showTotal = showTotal;<br />}<br />//得到计数单位<br />public String getStrUnit() {<br />return strUnit;<br />}<br />//设置计数单位<br />public void setStrUnit(String strUnit) {<br />this.strUnit = strUnit;<br />}<br />//得到总的记录数目<br />public int getTotalPut() {<br />return totalSize;<br />}<br />//设置总的记录数目<br />public void setTotalPut(int totalSize) {<br />this.totalSize = totalSize;<br />}<br />//得到转向的链接地址<br />public String getUrl() {<br />return url;<br />}<br />//设置链接地址<br />public void setUrl(String url) {<br />this.url = url;<br />}<br />public int doStartTag() throws JspException {<br />return SKIP_BODY;<br />}<br /><br /><br />public int doEndTag() throws JspException {<br />String out = showPage(currentPage, url, totalSize, perPage, showTotal, showAllPages, strUnit);<br />try {<br />pageContext.getOut().print(out);<br />} catch (IOException e) {<br />e.printStackTrace();<br />}<br />return EVAL_PAGE;<br />}<br /><br /><br />/**<br />* 作 用：显示“上一页 下一页”等信息<br />*<br />* @param url<br />* ----链接地址<br />* @ param totalSize<br />* ----总数量<br />* @ param perPage<br />* ----每页数量<br />* @param showTotal<br />* ----是否显示总数量<br />* @param showAllPages<br />* ---是否用下拉列表显示所有页面以供跳转。有某些页面不能使用，否则会出现JS错误。<br />* @param strUnit<br />* ----计数单位<br />* @return .<br />* @ throws IOException<br />*/<br />protected String showPage(int currentPage,String url, int totalSize, int perPage,<br /><br />boolean showTotal, boolean showAllPages, String strUnit){<br /><br />int n = 0;<br /><br />StringBuffer buf = new StringBuffer();<br /><br />String strUrl;<br /><br />n = (totalSize + perPage -1) / perPage;<br /><br />buf.append("&lt;table align='center'&gt;&lt;tr&gt;&lt;td&gt;");<br /><br />if (showTotal == true)<br /><br />buf.append("共 &lt;b&gt;" + totalSize + "&lt;/b&gt; " + strUnit<br /><br />+ " ");<br /><br />strUrl = JoinChar(url);<br /><br />if (currentPage &lt; 2) {<br /><br />buf.append("首页 上一页 ");<br /><br />} else {<br /><br />buf.append("&lt;a href='" + strUrl + "pageNo=1' title='首页'&gt;首页&lt;/a&gt; <br />");<br /><br />buf.append("&lt;a href='" + strUrl + "pageNo=" + (currentPage <br />- 1)<br /><br />+ "' title='上一页'&gt;上一页&lt;/a&gt; ");<br /><br />}<br /><br />if (n - currentPage &lt; 1)<br /><br />buf.append("下一页 尾页");<br /><br />else {<br /><br />buf.append("&lt;a href='" + strUrl + "pageNo=" + (currentPage <br />+ 1)<br /><br />+ "' title='下一页'&gt;下一页&lt;/a&gt; ");<br /><br />buf.append("&lt;a href='" + strUrl + "pageNo=" + n + "' <br />title='尾页'&gt;尾页&lt;/a&gt;");<br /><br />}<br /><br />buf.append(" 页次：&lt;strong&gt;&lt;font color=red&gt;" + currentPage<br /><br />+ "&lt;/font&gt;/" + n + "&lt;/strong&gt;页 ");<br /><br />buf.append(" &lt;b&gt;" + perPage + "&lt;/b&gt;" + strUnit <br />+ "/页");<br /><br />if (showAllPages == true) {<br /><br />buf<br /><br />.append(" 转到：&lt;select name='page' size='1' onchange=\"javascript:window.location='"<br /><br />+ strUrl<br /><br />+ "pageNo="<br /><br />+ "'+this.options[this.selectedIndex].value;\"&gt;");<br /><br />for (int i = 1; i &lt;= n; i++) {<br /><br />buf.append("&lt;option value='" + i + "'");<br /><br />if(currentPage == i)<br /><br />buf.append(" selected ");<br /><br />buf.append("&gt;第" + i + "页&lt;/option&gt;");<br /><br />}<br /><br />buf.append("&lt;/select&gt;");<br /><br />}<br /><br />buf.append("&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;");<br /><br />return (buf.toString());<br /><br />}<br /><br /><br />/**<br /><br />* 向地址中加入 ? 或 &amp;<br /><br />* @param strUrl<br /><br />* ----网址.<br /><br />* @return 加了 ? 或 &amp; 的网址.<br /><br />*/<br /><br />protected String JoinChar(String strUrl) {<br /><br />String result = "";<br /><br />if (strUrl.equals("") || strUrl.length() &lt;= 0) {<br /><br />return result;<br /><br />}<br /><br />if (strUrl.indexOf("?") &lt; strUrl.length()) {<br /><br />if (strUrl.indexOf("?") &gt; -1) {<br /><br />if (strUrl.indexOf("&amp;") &lt; strUrl.length()) {<br /><br />result = strUrl + "&amp;";<br /><br />} else {<br /><br />result = strUrl;<br /><br />}<br /><br />} else {<br /><br />result = strUrl + "?";<br /><br />}<br /><br />} else {<br /><br />result = strUrl;<br /><br />}<br /><br />return result;<br /><br />}<br />}<br /></p><p class="code"> </p><p class="code"> </p><p class="code"> </p><p class="code">　　有了自定义标签，当然少不了用于处理标签的tld，我们定义一个swsoft-struts.tld，代码如下： <br /></p><p> </p><p class="code">&lt;?xml version="1.0" encoding="UTF-8"?&gt; <br /><br />&lt;!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library <br />1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"&gt; <br /><br /><br />&lt;taglib&gt; <br /><br />&lt;tlibversion&gt;1.0&lt;/tlibversion&gt; <br /><br />&lt;jspversion&gt;1.1&lt;/jspversion&gt; <br /><br />&lt;shortname&gt;out&lt;/shortname&gt; <br /><br />&lt;uri&gt;http://www.swsoftware.com/&lt;/uri&gt; <br /><br />&lt;info&gt;Tab Library for PaginatorTag&lt;/info&gt; <br /><br />&lt;tag&gt; <br /><br />&lt;name&gt;paginator&lt;/name&gt; <br /><br />&lt;tagclass&gt;com.swsoftware.tags.PaginatorTag&lt;/tagclass&gt; <br /><br />&lt;bodycontent&gt;JSP&lt;/bodycontent&gt; <br /><br />&lt;info&gt;Returns a paginator&lt;/info&gt; <br /><br />&lt;attribute&gt; <br /><br />&lt;name&gt;currentPage&lt;/name&gt; <br /><br />&lt;required&gt;true&lt;/required&gt; <br /><br />&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt; <br /><br />&lt;/attribute&gt; <br /><br />&lt;attribute&gt; <br /><br />&lt;name&gt;url&lt;/name&gt; <br /><br />&lt;required&gt;true&lt;/required&gt; <br /><br />&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt; <br /><br />&lt;/attribute&gt; <br /><br />&lt;attribute&gt; <br /><br />&lt;name&gt;totalPut&lt;/name&gt; <br /><br />&lt;required&gt;true&lt;/required&gt; <br /><br />&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt; <br /><br />&lt;/attribute&gt; <br /><br />&lt;attribute&gt; <br /><br />&lt;name&gt;maxPerPage&lt;/name&gt; <br /><br />&lt;required&gt;true&lt;/required&gt; <br /><br />&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt; <br /><br />&lt;/attribute&gt; <br /><br />&lt;attribute&gt; <br /><br />&lt;name&gt;showTotal&lt;/name&gt; <br /><br />&lt;required&gt;true&lt;/required&gt; <br /><br />&lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt; <br /><br />&lt;/attribute&gt; <br /><br />&lt;attribute&gt; <br /><br />&lt;name&gt;showAllPages&lt;/name&gt; <br /><br />&lt;required&gt;true&lt;/required&gt; <br /><br />&lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt; <br /><br />&lt;/attribute&gt; <br /><br />&lt;attribute&gt; <br /><br />&lt;name&gt;strUnit&lt;/name&gt; <br /><br />&lt;required&gt;true&lt;/required&gt; <br /><br />&lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt; <br /><br />&lt;/attribute&gt; <br /><br />&lt;/tag&gt; <br /><br />&lt;/taglib&gt;</p><p class="code"><br clear="all" />　　好了，到现在我们开始来真正看一下jsp中的处理，我们写一个computer.jsp，代码如下：<br /></p><p class="code">&lt;%@ page import="com.util.BaseView,<br /><br />com.attribute.Constants,<br /><br />com.util.Page"%&gt;<br /><br />&lt;%@ page contentType="text/html;charset=GB2312" language="java" <br />errorPage=""%&gt;<br /><br />&lt;%@ taglib uri="/WEB-INF/swsoft-struts.tld"prefix="swtag"%&gt;<br /><br />&lt;html&gt;<br /><br />&lt;head&gt;<br /><br />&lt;title&gt;&lt;/title&gt;<br />&lt;/head&gt;<br /><br />&lt;body bgcolor="#e3edfc"&gt;<br /><br />&lt;div id="Layer1" style="position:absolute; left:1px; top:2px; <br />width:780px; height:406px; z-index:1; background-color: #e3edfc; layer-background-color: <br />#e3edfc; border: 1px none #000000;"&gt;<br /><br />&lt;table width="100%" cellpadding="1"&gt;<br /><br />&lt;tr&gt;<br /><br />&lt;td colspan="6"&gt;<br /><br />&lt;table width="100%" cellpadding="1"&gt;<br /><br />&lt;tr&gt;<br /><br />&lt;td width="20%" align="center"&gt;BOOKNAME&lt;/td&gt;<br /><br />&lt;td width="10%" align="center"&gt;AUTHOR&lt;/td&gt;<br /><br />&lt;td width="10%" align="center"&gt;TYPE&lt;/td&gt;<br /><br />&lt;td width="30%" align="center"&gt;PUBLISH&lt;/td&gt;<br /><br />&lt;td width="10%" align="center"&gt;PRINCE&lt;/td&gt;<br /><br />&lt;td colspan="2" align="center"&gt;SELECT&lt;/td&gt;<br /><br />&lt;/tr&gt;<br /><br />&lt;%<br /><br />String contextPath = request.getContextPath();<br /><br />String url=contextPath+"/computer.do";<br /><br />BaseView view=(BaseView)request.getAttribute(Constants.QUERY_RESULT);<br /><br />Page setpage=(Page)request.getAttribute("page");<br /><br />int currentPage=setpage.getCurrentPageNo();<br /><br />System.out.println("this is currentPage="+currentPage);<br /><br />int totalPut=setpage.getTotalSize();<br /><br />System.out.println("this is totalPut="+totalPut);<br /><br />int maxPerPage=setpage.getPageSize();<br /><br />System.out.println("this is maxPerPage="+maxPerPage);<br /><br />if(view.haveRecord()){<br /><br />String sBgcolor="";<br /><br />int length=view.getRecordCount();<br /><br />for(int i=0;i&lt;length;i++){<br /><br />String type =view.getValue(i,2);<br /><br />if(type.equals("1")){<br /><br />type="computer";<br /><br />}<br /><br />if(i%2!=0){<br /><br />sBgcolor="#A5C6EB";<br /><br />}<br /><br />else{<br /><br />sBgcolor="#B7D7EF";<br /><br />}<br /><br />%&gt;<br /><br />&lt;tr bgcolor=&lt;%=sBgcolor%&gt; height="10"&gt;<br /><br />&lt;td align="center" &gt;&lt;%=view.getValue(i,1)%&gt;&lt;/td&gt;<br /><br />&lt;td align="center"&gt;&lt;%=view.getValue(i,4)%&gt;&lt;/td&gt;<br /><br />&lt;td align="center"&gt;&lt;%=type%&gt;&lt;/td&gt;<br /><br />&lt;td align="center"&gt;&lt;%=view.getValue(i,5)%&gt;&lt;/td&gt;<br /><br />&lt;td align="center"&gt;&lt;%=view.getValue(i,8)%&gt;&lt;/td&gt;<br /><br />&lt;td width="20%" align="center"&gt;BUY PARTICULAR&lt;/td&gt;<br />&lt;/tr&gt;<br /><br />&lt;%}}%&gt;<br /><br />&lt;/table&gt;<br /><br />&lt;/td&gt;<br /><br />&lt;/tr&gt;<br />&lt;tr&gt;<br /><br />&lt;swtag:paginator url="&lt;%=url%&gt;"<br /><br />currentPage="&lt;%=currentPage%&gt;"<br /><br />totalPut="&lt;%=totalPut%&gt;"<br /><br />maxPerPage="&lt;%=maxPerPage%&gt;"<br /><br />showTotal="true"<br /><br />showAllPages="true"<br /><br />strUnit="页" /&gt;<br /><br />&lt;/tr&gt;<br /><br />&lt;/table&gt;<br /><br />&lt;/div&gt;<br />&lt;/body&gt;<br /><br />&lt;/html&gt;</p><p class="code"><br clear="all" />　　到此为止，分页的类基本完成，这样的话可以在别的模块都可以用这个标签，同时在开发别的系统的时候这个标签也可以使用，具有比较好的可移植性。这个数据库是mysql的，要是oracle的，仅仅在PageCtBean类中的intiQuerySQL方法里面改成<br /></p><p class="code">protected String intiQuerySQL(String sql, int startIndex, int size){<br />StringBuffer querySQL = new StringBuffer();<br />if (size != this.MAX_PAGE_SIZE) {<br />querySQL.append("select * from (select my_table.*,rownum as my_rownum from(")<br />.append( sql)<br />.append(") my_table where rownum&lt;").append(startIndex + size)<br />.append(") where my_rownum&gt;=").append(startIndex);<br />} else {<br />querySQL.append("select * from (select my_table.*,rownum as my_rownum from(")<br />.append(sql)<br />.append(") my_table ")<br />.append(") where my_rownum&gt;=").append(startIndex);<br />}<br />return querySQL.toString();<br />}</p><p class="code"><br />　　就可以了。<br /><br />同时在数据库中，返回当前页需要显示的数据，主要有以下方法:<br /><u><strong>a</strong>.使用mysql控制：</u><br /></p><p class="code">　　select * from user<br />　　order by Host<br />　　limit m, n</p><p class="code"><br />　　结果返回的是第m+1行到第n行的数据集。比如:<br /></p><p class="code">　　select * from user<br />　　order by Host<br />　　limit 1, 5</p><p class="code"><br />　　返回的是第2行到第5行的数据集<br /><br /><u><strong>b</strong>.使用sqlserver</u><br /></p><p class="code">　　SELECT *<br />　　FROM (SELECT TOP m *<br />　　FROM (SELECT TOP n *<br />　　FROM Customers) A<br />　　ORDER BY CustomerID DESC) B<br />　　ORDER BY CustomerID</p><p class="code"><br />　　获得的结果集数据为第n-m+1行到第n行。<br /><br />　　对整个过程的解释：<br />　　首先按照升序得到前n行的结果集A，然后按照降序从A中得到后m行的结果集B，最后按照升序对B进行重新排序，返回结果集。其中CustomerID为主键,比如：<br /></p><p class="code">　　SELECT *<br />　　FROM (SELECT TOP 5 *<br />　　FROM (SELECT TOP 10 *<br />　　FROM Customers) A<br />　　ORDER BY CustomerID DESC) B<br />　　ORDER BY CustomerID</p><p class="code"><br />　　的意思就是返回包含第6行到第10行的数据结果集。<br /><br /><u><strong>c</strong>.使用Oracle:</u><br /></p><p class="code">　　select * from (select rownum r ,* from test) tt <br />　　where tt.r &gt; 50 and tt.r &lt;= 100;</p><p class="code"><br />希望大家有好方法的话能说一下，大家共同进步。 <br clear="all" /></p><img src ="http://www.cnitblog.com/tilan/aggbug/21694.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2007-01-09 23:09 <a href="http://www.cnitblog.com/tilan/articles/21694.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Apache与JBOSS的整合,并支持SSL,整理 </title><link>http://www.cnitblog.com/tilan/articles/21121.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Tue, 26 Dec 2006 05:55:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21121.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21121.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21121.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21121.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21121.html</trackback:ping><description><![CDATA[系统:Red Hat Enterprise AS 4<br />用到的软件:<br />      &lt;1&gt;.httpd-2.2.3.tar.gz<br />      &lt;2&gt;.jboss-4.0.5.GA.zip<br />      &lt;3&gt;.openssl-0.9.8d.tar.gz<br />      &lt;4&gt;.sign.sh(mod_ssl-2.8.28-1.3.37.tar.gz里面)<br />      &lt;5&gt;.java_app_platform_sdk-5_01-linux.bin<br /><br />下载地址:<br />        <a href="http://httpd.apache.org/download.cgi" target="_blank"><font color="#666666">http://httpd.apache.org/download.cgi</font></a><br />        <a href="http://prdownloads.sourceforge.net/jboss/jboss-4.0.5.GA.zip?download" target="_blank"><font color="#666666">http://prdownloads.sourceforge.net/jboss/jboss-4.0.5.GA.zip?download</font></a><br />        <a href="http://www.openssl.org/source/openssl-0.9.8d.tar.gz" target="_blank"><font color="#666666">http://www.openssl.org/source/openssl-0.9.8d.tar.gz</font></a><br />        <a href="http://www.modssl.org/source/" target="_blank"><font color="#666666">http://www.modssl.org/source/</font></a><br />      <a href="https://sdlc3b.sun.com/ECom/EComActionServlet/DownloadPage:~:com.sun.sunit.sdlc.content.DownloadPageInfo;jsessionid=B26C97DB223331C1592519069677284D;jsessionid=B26C97DB223331C1592519069677284D" target="_blank"><font color="#666666">https://sdlc3b.sun.com/ECom/EComActionServlet/DownloadPage:~:com.sun.sunit.sdlc.content.DownloadPageInfo;jsessionid=B26C97DB223331C1592519069677284D;jsessionid=B26C97DB223331C1592519069677284D</font></a><br /><br />(一)安装openssl 2.8.28-2.3.37<br />  #tar -zxvf openssl-0.9.8d.tar.gz -C openssl<br />  #cd openssl<br />  #./config –-prefix=/usr/local/openssl<br />  #make<br />  #make install<br /><br />(二)安装apaphe2.2.3<br />  #tar -zxvf httpd-2.2.3.tar.gz<br />  #cd httpd-2.2.3<br />  #./configure --prefix=/usr/local/apache --enable-proxy=share --enable-proxy-ajp=share --enable-ssl=static --with-ssl=/usr/local/openssl <br />  #make<br />  #make install<br />  <br />(三)配置apache,支持SSL<br />  #cd /usr/local/apache/conf<br />  #vi httpd.conf<br />  <br />  #添加<br />  ProxyPass / ajp://127.0.0.1:8009/<br />  ProxyPassReverse / ajp://127.0.0.1:8009/<br />  #这就实现了最简单的apache＋jboss整合。<br /><br />  #加入SSL支持部分，这里指定使用SSL v2协议<br /><br />  SSLProtocol -all +SSLv2<br />  SSLCipherSuite SSLv2:+HIGH:+MEDIUM:+LOW:+EXP<br />  Include conf/extra/httpd-ssl.conf<br />  <br />  #保存退出<br />  <br />  生成认证证书<br />先建立一个 CA 的证书， <br />首先为 CA 创建一个 RSA 私用密钥， <br />[S-1] <br />openssl genrsa -des3 -out ca.key 1024 <br />系统提示输入 PEM pass phrase，也就是密码，输入后牢记它。 <br />生成 ca.key 文件，将文件属性改为400，并放在安全的地方。 <br />[S-2] <br />chmod 400 ca.key <br />你可以用下列命令查看它的内容， <br />[S-3] <br />openssl rsa -noout -text -in ca.key <br />利用 CA 的 RSA 密钥创建一个自签署的 CA 证书（X.509结构） <br />[S-4] <br />openssl req -new -x509 -days 3650 -key ca.key -out ca.crt <br />然后需要输入下列信息： <br />Country Name: cn 两个字母的国家代号 <br />State or Province Name: An Hui 省份名称 <br />Locality Name: Bengbu 城市名称 <br />Organization Name: Family Network 公司名称 <br />Organizational Unit Name: Home 部门名称 <br />Common Name: Chen Yang 你的姓名 <br />Email Address: <a href="mailto:yemeiqiang@gmail.com"><font color="#666666">yemeiqiang@gmail.com</font></a> Email地址 <br />生成 ca.crt 文件，将文件属性改为400，并放在安全的地方。 <br />[S-5] <br />chmod 400 ca.crt <br />你可以用下列命令查看它的内容， <br />[S-6] <br />openssl x509 -noout -text -in ca.crt <br />下面要创建服务器证书签署请求， <br />首先为你的 Apache 创建一个 RSA 私用密钥： <br />[S-7] <br />openssl genrsa -des3 -out server.key 1024 <br />这里也要设定pass phrase。 <br />生成 server.key 文件，将文件属性改为400，并放在安全的地方。 <br />[S-8] <br />chmod 400 server.key <br />你可以用下列命令查看它的内容， <br />[S-9] <br />openssl rsa -noout -text -in server.key <br />用 server.key 生成证书签署请求 CSR. <br />[S-10] <br />openssl req -new -key server.key -out server.csr <br />这里也要输入一些信息，和[S-4]中的内容类似。 <br />至于 extra attributes 不用输入。 <br />你可以查看 CSR 的细节 <br />[S-11] <br />openssl req -noout -text -in server.csr <br />下面可以签署证书了，需要用到脚本 sign.sh <br />[S-12] <br />sign.sh server.csr <br />就可以得到server.crt。 <br />将文件属性改为400，并放在安全的地方。 <br />[S-13] <br />chmod 400 server.crt <br />删除CSR <br />[S-14] <br />rm server.csr<br />Sign.sh在openssl目录中没有。我是在mod_ssl第三方模块中提取的。<br />把生成好的文件放在/usr/local/apache/conf目录下。<br />启动apache<br />/usr/local/apache/bin/apachectl start<br />有可能不能访问,原因可能是jboss没有开启.<br /><br />(四)安装JBOSS4.0.5<br />  先安装java环境<br />  #./java_app_platform_sdk-5_01-linux.bin<br />  #rpm -ivh java_app_platform_sdk-5_01-linux.rpm<br />  配置环境变量<br />  #vi /etc/profile<br />  #添加<br />  JAVA_HOME=/usr/local/java/SDK/jdk<br />JRE=/usr/local/java/SDK/jdk/jre<br />CLASSPATH=/usr/local/java/SDK/jdk/lib/tools.jar:/usr/local/java/SDK/jdk/lib/dt.jar:/usr/local/java/SDK/jdk/jre/lib/rt.jar<br />PATH=$PATH:$JAVA_HOME/bin:$JRE/bin<br />export PATH JAVA_HOME JRE CLASSPATH<br />#保存退出<br /><br />把系统重启一下,让环境变量生效<br />#reboot<br />现在开始安装jboss<br />#unzip jboss-4.0.5.GA.zip<br />#cd jboss-4.0.5.GA<br />#cp -r jboss-4.0.5GA /usr/local/jboss<br />#cd /usr/local/jboss/bin<br />#./run.sh 启动jboss<br /><br />打开Firefox ,输入<a href="https://localhost/" target="_blank"><font color="#666666">https://localhost</font></a>看看,是不是可以了,呵呵<br /><img src ="http://www.cnitblog.com/tilan/aggbug/21121.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-26 13:55 <a href="http://www.cnitblog.com/tilan/articles/21121.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>]**你如何使用javaBean操作数据库？**高效Bean封装</title><link>http://www.cnitblog.com/tilan/articles/21094.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 25 Dec 2006 08:23:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21094.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21094.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21094.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21094.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21094.html</trackback:ping><description><![CDATA[<table width="92%" border="0">
				<tbody>
						<tr>
								<td colspan="3" height="15">
								</td>
						</tr>
						<tr>
								<td align="left" colspan="3">你平时是如何使用JSP操作数据库呢？对于jsp+javaBean模式，想必大家都已经很熟悉了，我们可以将获取数据库连接，查询，更新甚至将其它的功能都封装进javaBean----<br /><br />好了--下面让我们来好好弄清楚一个问题：**你如何在JSP页中取得DB中的数据？从javaBean中返回ResultSet，然后在JSP中枚举吗？如果是这样的话，那我强烈建议你把这篇文章读完。*^_^*<br /><br />用javaBean封装数据库操作谁不会？--对啊，大家都会，但是--如果构建一个高扩展性的“结构”？这就要用到java的相关知识了。废话少说，我们先在Tomcat中创建一个DataSource- jdbc/Panabia，然后再创建一个java“基类”，这个类封装了数据库连接和连接的释放：[程式中有相应的注解]<br /><br />CODE:<br />--------------------------------------------------------------------------------<br /><br />package Panabia.db;<br /><br />import javax.sql.DataSource;<br />import javax.naming.*;<br />import java.sql.*;<br /><br />public class SQLFactory<br />{<br />    <br /> private static DataSource ds=null;<br /> private static Object Lock=new Object();<br /><br />//生成DataSource**<br />public static DataSource gainDataSource(){<br />try{<br /> if(ds==null){<br />    synchronized(Lock){<br />     if(ds==null){<br />        Context ctx=new InitialContext();<br />          ds=(DataSource)ctx.lookup(\"java:comp/env/jdbc/Panabia\");<br />       }<br />     }<br />   }<br /> }<br /> catch(NamingException e){e.printStackTrace();}<br /> return ds;<br />} <br /><br />//生成SQL连接**<br />public static synchronized Connection gainConnection(){<br /> Connection con=null;<br /> try{<br />  if(ds==null){<br />   gainDataSource();<br />   }<br />   con=ds.getConnection();<br /> }<br /> catch(SQLException e){e.printStackTrace();}<br /> return con;    <br />}<br /><br />//释放SQL连接**<br />public static void releaseConnection(ResultSet rs,PreparedStatement ps,Statement sql,Connection con){<br />try{<br />   if(rs!=null)<br />   rs.close();<br />   }<br />   catch(SQLException e){e.printStackTrace();}<br />try{<br />    if(ps!=null)<br />    ps.close(); <br />   }<br />   catch(SQLException e){e.printStackTrace();}<br />try{<br />    if(sql!=null)<br />    sql.close();<br />   }<br />   catch(SQLException e){e.printStackTrace();} <br />try{ <br />   if(con!=null&amp;&amp;!con.isClosed()) <br />   con.close();<br />  }<br />  catch(SQLException e){e.printStackTrace();}<br /> }<br />}<br /><br /><br />--------------------------------------------------------------------------------<br /><br /><br />大家都应该注意到了，这个类的所有的方法全部是static的，之所以这样，主要是为了方便其它“扩展类”的调用，当然，还有其它好处--- ：）<br /><br />好了，这个类就封装完毕了，现在我们就可以针对不同的应用要求单独写javaBean了，比如一个简单的：在JSP中列出verify表中的所有用户名与密码列表-<br /><br />该怎么做？--使用SQLFactory生成Connection，再生成Statement，再生成ResultSet--然后枚举吗？好象不错，哦，等等......这样做你难道没有一种“非常亲切”的感觉吗？---对了，ASP，PHP中就是如此-Faint~我们怎么又回到“原始社会”了....<br /><br />有没有更好的方式？答案是肯定的，JAVA的能力是“通天”的强大，只要你能想得到，仔细看看它的API Document，就不难找出解决办法。<br />答案出来了：<br />我们在查询类中返回Iterator到JSP枚举，而不是ResultSet。<br />好了，我们的UserQuery类就产生了：<br /><br />CODE:<br />--------------------------------------------------------------------------------<br /><br />package Panabia.operate;<br /><br />import Panabia.db.SQLFactory;<br />import java.util.*;<br />import java.sql.*;<br /><br />public class UserQuery{<br /> <br /> private ArrayList list=null;<br /> private Connection con=null;<br /> private Statement sql=null;<br /> private ResultSet rs=null;<br /> <br /> public Iterator getResult(){<br />   try{<br />     con=SQLFactory.gainConnection();<br />     sql=con.createStatement();<br />     rs=sql.executeQuery(\"select * from verify\");<br />                          //verify表只有两个字段：username,password;<br />      list=new ArrayList();<br />       while(rs.next()){<br />         list.add(rs.getString(1));<br />         list.add(rs.getString(2));<br />           }<br />     }<br />     catch(SQLException e){e.printStackTrace();}<br />     finally{SQLFactory.releaseConnection(rs,null,sql,con);} <br />      return list.iterator();<br />     }<br />}<br /><br /><br />--------------------------------------------------------------------------------<br /><br /><br /><br />然后，就是在JSP页中进行数据的枚举：因为发现cnjbb不支持html标签的显示，所以，只贴出了JSP中的全部java代码片--<br /><br />........<br />Iterator it=UserQuery.getResult();<br /> while(it.hasNext()){<br />  out.print((String)it.next());<br />  }<br />..........<br /><br />就是这么简单，一个循环就搞定了。<br /><br /><br />我承认，就这样把数据“裸列”出来实是“不雅”，想美化？---如果你会，那就开始做；如果觉得自己水平不行，你就需要找一个网页美工来帮你了；和网页美工配合并不是难事儿，只要将html标签简单的插入Java scriptlet中就OK了--很明显，这个比在JSP中枚举ResultSet的情况好多了，不会“牵一发而动全身”。<br /></td>
						</tr>
				</tbody>
		</table><img src ="http://www.cnitblog.com/tilan/aggbug/21094.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-25 16:23 <a href="http://www.cnitblog.com/tilan/articles/21094.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何成为一个成功的Jsp程序员？</title><link>http://www.cnitblog.com/tilan/articles/21092.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 25 Dec 2006 08:21:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21092.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21092.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21092.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21092.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21092.html</trackback:ping><description><![CDATA[<table width="92%" border="0">
				<tbody>
						<tr>
								<td align="left" colspan="3">一个普通的错误是把JSP当作简化的 Java。它不是，（事实上, JSP 是简化的 servlets 。）程序员通常试着没有学习要求的支持技巧而 直接学习 JSP 。JSP 是一个衔接技术，并且成功地连接你需要理解的另外的技术。如果你已经知道 Java，HTML 和 Javascript，这意味着 JSP 将确实是简单的。 <br /><br />需要成为一个成功的 JSP 程序员可以参考这个时间表。请注意下列： <br /><br />*忽略你已经熟悉的步骤。 <br />*训练的时间只是代表学习好足够的基础时间，这样才能转移到下一步。 <br /><br />１、建立并且理解你的Web Server。 <br />因为Apache 是免费的并且在大多数平台上工作，为训练目的推荐 Apache。 <br />安装时间：2 天。 <br /><br /><br />２、保证你理解 HTML / XHTML 。 <br />你将需要了解html基础, 特别是 HTML 布局中的table的使用。XHTML 不久将代替 HTML ，学习 XHTML 的基础是一个好主意。许多程序员通过 HTML IDE 学习 HTML ( 集成开发环境 ) 。因为大多数 HTML IDE产生混乱的HTMl语法，所以花时间学习手工写作html是很有必要的。因 为你将会使用 JSP 和 HTML 混合编程，精通HTML语法是重要的。所以，你必须能流利地写 HTML 。 <br />训练时间：2 ～ 4 个星期。 <br /><br /><br />３、开始学习 Java 。 <br />开始学习 Java 1.4 理解 Java 基础是很重要的。不用担心学习Swing或 Java 的图形方面，因为在JSP 中你不会使用这些特征。集中精力 在 Java 工作的细节，学习 Java 的逻辑，也在 Java Bean上花时间。学习Applet是好的, 但是就象Swing， JSP 的大多数应用将不使用小程 序。 <br />训练时间：3 ～ 6 个星期。 <br /><br /><br />３、学习 JavaScript <br />学习怎么将 JavaScript在HTML中验证输入的Form元素。也学习 JavaScript怎么能在一 HTML 页以内修改Form的元素。最后要求你能从一 <br />HTML 页内的事件中触发 JavaScript Function。 <br />训练时间：一～ 2 个星期。 <br /><br /><br />４、学习并且理解你的Web Server的更好的细节。 <br />熟悉Web Server的特征，这是很重要的。 <br />训练时间：2 天。 <br /><br /><br />５、建立你的 JSP Server <br />我推荐以Tomcat开始。它可以很好地运行JSP程序。当你不能在生产使用Tomcat时，学习尽可能多的知识以便于更好的运行程序。另外, 许多 JSP 程序员使用Tomcat。因此当你遇到一个问题时，你将容易发现帮助。 <br />安装时间：一～ 2 天。 <br /><br /><br />６、开始学习 JSP 。 <br />基本的 JSP 学习通过的步骤 1到步骤6可以完成, 然后使用 JSP 对象和脚本写 JSP 程序来联系。学习 JSP 的另外一个方面可以学习怎么创建一个分布式的应用程序。 <br />训练时间：4 ～ 6 个星期。 <br /><br /><br />７、学习更多的 JSP server。 <br />没有关于更多的 JSP Server当然也可以运行jsp程序。然而, 许多 JSP server都由自己特殊的特征，可以让你更好的理解你的JSP 工程。 <br />学习更多的Jsp server如何处理jsp程序是有必要的。同样也可以优化你的 JSP 应用程序，并且使之运行得更快而不出任何问题。 <br />训练时间：2 ～ 7 天。 <br /><br /><br />８、 学习 JDBC 。 <br />JSP 大多数应用将使用数据库，JDBC 被用于数据库连接。经常忽略的一个事实就是，每个 JDBC Driver 所支持的东西是相当不同的。了 解并熟悉在jsp工程上被使用的 JDBC driver的细节是很重要的。（有时这部分的学习被包含在前面 Java 或JSP的学习中了 。） <br />训练时间：1～ 2 个星期。 <br /><br />到现在，你已经成为了熟练的 JSP 程序员。仍然有很多需要学习，你可以考虑扩展你的知识比如 DHTML ， XML ，java证书， JSP Tag Libraries 或 Servlets , 看你想要造什么类型的网站而决定了。 <br /><br />这些训练是JSP 的核心。你不必都学习上面所有的, 取决于你在工程中分配到什么任务和你已经有什么知识。但是这是我成功地训练程序员 的时间表。关键的单元是时间。平均的说, 5 个月时间确实能够训练一个人 ( 从开始到完成 ) 成为一个对jsp熟悉程序员。5 个月时间似乎很长，但要成为一个资深的WEB程序员所学的东西远远不止这一些。 <br /><br />也许你认为这样学习一种语言花费的时间太长了，因为学 ASP 会更快、时间会更短。 但是学习 ASP 不需要学习java的。</td>
						</tr>
						<tr>
								<td colspan="3" height="15"> </td>
						</tr>
				</tbody>
		</table><img src ="http://www.cnitblog.com/tilan/aggbug/21092.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-25 16:21 <a href="http://www.cnitblog.com/tilan/articles/21092.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jsp设计模式</title><link>http://www.cnitblog.com/tilan/articles/21093.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 25 Dec 2006 08:21:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21093.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21093.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21093.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21093.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21093.html</trackback:ping><description><![CDATA[jsp设计模式<br />一：介绍<br />    记得初学jsp的时候，总喜欢把他和asp,php去比较，也习惯于使用asp的开发模式去写jsp，后来才发现这真是很傻的做法，其实jsp一出了来就已经用MVC模式了。下面我就简要说说jsp设计使用MVC来设计。<br /><br />二：MVC介绍<br />    MVC其实是模、视图、控制的缩写，也就是说在使用jsp时，有相应的文件去实现相应的操作。通常jsp只负责view也就是只负责显示页面。业务逻辑等有bean(ejb)去实现。下面讨论的是不使用ejb的情况下实现。如果使用ejb,ejb负责M.C通常会由servlet去实现。或者使用struts.<br />    struts的介绍你可是去http://jakarta.apache.org/struts去看。以后的文章中我会介绍。<br /><br />三：设计思路<br />    当你构建一个Application时。你必须考虑界面的问题，而界面修改是非常平常的。如果你在jsp中实现所有的操作，一旦修改界面你麻烦大了。美工不懂jsp的，你必须自己去修改无数个文件，头不要太大，而此时一般时间都很紧，使用MVC则可以减少你的一些麻烦。<br />    在设计时候jsp只负责显示页面，也就是说jsp调用Bean(Struts,servlet)传过来的数据然后显示而Bean(Struts,servlet)负责收集jsp需要的数据，用ArrayList(Arttibute)传给jsp。假如需要提交表单，一般也直接提交给Struts,servlet,处理后以后再返回处理信息。而其中相应的业务逻辑由Bean去实现。<br /><br />四：Bean的设计<br />    在我使用时候Bean通常有三类，分别manager,entry,database目录下面。<br />    manager下面的bean做商业逻辑<br />    entry下的bean就是封装数据，其实就是每个数据库表对应会有一个bean。jsp得到的也全部是类。<br />    database下的bean就是操作数据库，执行如insert,update,delete,load(查询一条记录),<br />    batchload(查询多条记录)。<br />    他们之间的关系是entry负责封装数据，作为database的调用参数，返回结果。<br />    manager调用database处理的结果。manager和jsp通信。jsp得到的结果从manager来， jsp需要做操作调用manager,即使是一个insert 在database存在这样的方法但在manager中你还是需要再封装一次。这样做的目的是使结构尽量简单。database只负责操作数据库。manager只做逻辑（取相应的数据）处理相应的逻辑，而entry则只把database取到的数据封装，或则把页面得到的参数封装，做为参数传给相应的Bean.<br /><br />五：设计实例<br />    下面我把留言板作为实例来讨论：<br />    entry/Guestbook.java(留言板对象)<br />    database/GuestbookMap.java(更新，删除，修改留言板)<br />    manager/GuestbookManager.java(处理所有的事务)<br />    数据表结构(postgresql)<br />    create sequence seq_guestbook increment 1 ;<br />    /**序列号生成器**/<br />    create table guestbook(<br />        id        int8  default nextval('seq_guestbook'),    /**主键**/<br />        title    varchar(64),/**主题**/<br />        body    text,/**内容**/<br />        sayid    int8,/**发言人**/<br />        toid    int8,/**接受人**/<br />        saytime    datetime    default now(),/**留言时间**/<br />        newflg    smallint    default 1/**是否查看过**/<br />    );<br />    <br />    Guestbook.java<br />    =======================<br />    import java.util.*;<br />    public class Guestbook(){<br />        private int id;<br />        private String title;<br />        private body title;<br />        private int sayid;<br />        private int sayid;<br />        private Date saytime;<br />        private short newflg;<br />        <br />        public Guestbook(){<br />        <br />        }<br />        <br />        public int getId(){<br />            return    this.id;<br />        }<br />        <br />        public void setId(int _id){<br />            this.id=_id;<br />        }<br />        ........<br />        (全是get/set方法)<br />    }<br />    <br />    GuestbookMap.java<br />    ==============================<br />    import Guestbook;<br />    public class GuestbookMap(){<br />        public GuestbookMap(){<br />        <br />        }<br />        <br />        public Guestbook load(int id){<br />            //取到一条guestbook<br />        }<br />        //sqlstr    查询条件<br />        //orderstr    排序条件<br />        //rcdbegin    记录开始<br />        //rcdend    记录结束<br />        //<br />        public ArrayList batchLoad(String sqlstr,String orderstr,int rcdbegin,int rcdend){<br />            //ArrayList 里面封装了Guestbook<br />        }<br />        <br />        public void insert(Guestbook info){<br />        <br />        }<br />        <br />        public void update(Guestbook info){<br />        <br />        }<br />        <br />        public void delete(int id){<br />            //取到一条guestbook<br />        }<br />        <br />        public int getRcdNums(String sqlstr){<br />            //取记录条数<br />        }<br />    }<br />    <br />    GuestbookManager.java<br />    根据需要封装需要的方法，这部分才是你要写的<br />    =============================<br />    上面的方式entry,database的文件可以自动生成，这个工具我已经开发了，如果需要联系我。你需要写的就是GuestbookManager里面的方法，你也许会觉得工作量比你所有的操作都在jsp中大，但是这样结构非常清晰。你还需要的就是写一个数据库连接池，你所有的数据库操作都从一个地方取，每一次都去连接数据库开销很大的。<br /><br />六：技术优势<br />1：结构清晰<br />2：维护方便<br />3：保护代码比较好。<br />..........<br />七：结束语<br />    我只是简单的介绍一下，具体的使用你需要在实际运用中积累，如果需要我的代码参看，联系我，如果你觉得写的和狗屎，非常不好意思，浪费了你宝贵的时间。<img src ="http://www.cnitblog.com/tilan/aggbug/21093.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-25 16:21 <a href="http://www.cnitblog.com/tilan/articles/21093.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts 中 Html 标签库 的用法(第二组标签,与Form相关的标签 )</title><link>http://www.cnitblog.com/tilan/articles/21089.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 25 Dec 2006 08:06:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21089.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21089.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21089.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21089.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21089.html</trackback:ping><description><![CDATA[<table width="92%" border="0">
				<tbody>
						<tr>
								<td align="left" colspan="3">继续学习Struts提供的HTML标签库。<br />by Budi Kurniawan <br />在本文系列的第一部分中我讲述了如何使用Struts提供的HTML标签库来配置一个Struts应用程序。我还介绍了该标签库中的一类标签：独立使用的标签。在第二部分中我将继续介绍第二类标签：与form相关的标签。<br /><br />与form相关的标签包括 标签本身以及所有必须包含在其中的标签。比如，和标签就是和form相关的标签，因为如果不把它们放到一个form中它们就没有意义。<br /><br />标签<br />标签用于生成一个HTML form。使用该标签时必须遵循许多规则。<br /><br />首先，标签中必须包含一个action属性，它是这个标签中唯一必需的属性。如果不具备该属性则JSP页面会抛出一个异常。之后你必须给这个action属性指定一个有效值。一个有效值是指应用程序的Struts配置文件中元素里的任何一个子元素的访问路径。而且相应的元素中必须有一个name属性，它的值是form bean的名称。<br /><br />例如，如果你有这样一个标签：  &lt;html:form action=\"/login\" &gt; <br /> <br /><br /><br />那么你的Struts配置文件的元素中必须有一个如下显示为粗体的元素：     &lt;action-mappings&gt; <br />     &lt;action path=\"/login\" <br />      type=\"com.javapro.struts.LoginAction\"  <br />      name=\"loginForm\"<br />      scope=\"request\"<br />      input=\"/login.jsp\"&gt;<br />      &lt;forward name=\"success\" path=\"/mainMenu.jsp\"/&gt;<br />    &lt;/action&gt;<br />    .<br />    .<br />    .<br />  &lt;/action-mappings&gt;<br /><br /><br />这就是说一个form标签是和form bean相关联的。<br /><br />另一个要遵循的规则是：任何包含在&lt;form&gt;中用来接收用户输入的标签（&lt;text&gt;、&lt;password&gt;、&lt;hidden&gt;、&lt;textarea&gt;、&lt;radio&gt;、&lt;checkbox&gt;、&lt;select&gt;）必须在相关的form bean中有一个指定的属性值。比如，如果你有一个属性值被指定为“username”的&lt;text&gt;标签，那么相关的form bean中也必须有一个名为“username”的属性。输入&lt;text&gt;标签中的值会被用于生成form bean的userName属性。<br /><br />除了上面所讲的属性之外，&lt;form&gt;标签还有一些不是必须但加上更好的“次要”的属性。比如，你可以用focus属性来生成JavaScript，它会“定焦”（focus）到该form所包含的一个元素上。使用focus属性时你需要给它指定元素的名称。比如，以下代码是定焦在第二个Text元素上的： &lt;body&gt;<br />&lt;html:form action=\"/login\" focus=\"password\"&gt;<br />User Name: &lt;html:text property=\"userName\"/&gt;<br />&lt;br&gt;Password: &lt;html:text property=\"password\"/&gt;<br />&lt;br&gt;&lt;html:submit/&gt;<br />&lt;/html:form&gt;<br />&lt;/body&gt;<br /><br />该段代码会被转换成： &lt;body&gt;<br />&lt;form name=\"loginForm\" method=\"post\" <br />   action=\"/myStrutsApp6/login.do\"&gt;<br />User Name: &lt;input type=\"text\" name=\"userName\" <br />   value=\"\"&gt;<br />&lt;br&gt;Password: &lt;input type=\"text\" <br />   name=\"password\" value=\"\"&gt;<br />&lt;br&gt;&lt;input type=\"submit\" <br />   value=\"Submit\"&gt;<br />&lt;/form&gt;<br />&lt;script language=\"JavaScript\" <br />   type=\"text/javascript\"&gt;<br />  &lt;!--<br /> if (document.forms[\"loginForm\"].elements[<br />      \"password\"].type != \"hidden\") <br />    document.forms[\"loginForm\"].elements[<br />      \"password\"].focus()<br />  // --&gt;<br />&lt;/script&gt;<br /><br />&lt;/body&gt;<br /><br />注意，&lt;form&gt;标签中method属性的缺省值是POST。另外，有没有看到这个标签库是如何建立JavaScript来定焦到password元素上的? 这也是该库让人着迷的地方之一。你不用担心如何在客户端进行编程，它会帮你自动生成。<br /><br />在运行前面的例子时还要注意，你必须有一个包含userName和password属性的相关form。 <br /><br />&lt;text&gt;标签<br />&lt;text&gt;标签用于生成一个文本的输入区域。它必须包含和相关form bean中的相同属性对应的“property”属性。该标签只有在嵌入到一个&lt;form&gt;标签中时才有效。<br /><br />例如： &lt;html:text property=\"userName\"/&gt;<br />会被转换成： &lt;input type=\"text\" name=\"userName\" value=\"\"&gt;<br /><br />&lt;password&gt;标签<br />&lt;password&gt;标签用于生成一个口令字（type password）的输入区域。它必须包含和相关form bean中的相同属性对应的“property”属性。该标签只有在嵌入到一个&lt;form&gt;标签中时才有效。该标签中的一个很重要的属性是“redisplay”，它用于重新显示以前输入到这个区域中的值。该属性的缺省值为true。然而，为了使password不能被重新显示，你或许希望将该属性的值设为false。<br /><br />例如： &lt;html:password property=\"password\" <br />   redisplay=\"false\"/&gt;<br />会被转换成: &lt;input type=\"password\" name=\"password\" <br />   value=\"\"&gt;<br /><br />&lt;hidden&gt;标签<br />&lt;hidden&gt;标签用于生成一个隐藏文本的输入区域。它必须包含和相关form bean中的相同属性对应的“property”属性。该标签只有在嵌入到一个&lt;form&gt;标签中时才有效：<br /><br />例如： &lt;html:hidden property=\"userName\"/&gt;<br /><br />会被转换成: &lt;input type=\"hidden\" name=\"userName\" value=\"\"&gt;<br /><br />&lt;textarea&gt;标签<br />&lt;textarea&gt;标签用于生成一个文本区元素（text area element）。它必须包含和相关form bean中的相同属性对应的“property”属性。<br /><br />比如： &lt;html:textarea property=\"details\" <br />  cols=\"80\"<br />  rows=\"20\"<br />  value=\"Enter details here\"/&gt;<br />会被转换成： &lt;textarea name=\"details\" cols=\"80\" <br />  rows=\"20\"&gt;Enter details here&lt;/textarea&gt;<br /><br />&lt;radio&gt;标签<br />&lt;radio&gt;标签用于显示一个单选按钮（radio button）。它必须包含“value”属性。比如这段代码： &lt;html:radio property=\"title\" value=\"1\"/&gt;Mr.<br />&lt;html:radio property=\"title\" value=\"2\"/&gt;Ms.<br />&lt;html:radio property=\"title\" value=\"3\"/&gt;Dr.<br /><br />会被转换成这样的HTML： &lt;input type=\"radio\" name=\"title\" <br />   value=\"1\"&gt;Mr.<br />&lt;input type=\"radio\" name=\"title\" <br />   value=\"2\"&gt;Ms.<br />&lt;input type=\"radio\" name=\"title\" <br />   value=\"3\"&gt;Dr.<br />&lt;checkbox&gt;标签<br />&lt;checkbox&gt;标签用于显示checkbox类型的输入区域。比如： &lt;html:checkbox property=<br />   \"notify\"/&gt;Please send me notification<br />会被转换成： &lt;input type=\"checkbox\" name=\"notify\" <br />   value=\"on\"&gt;Please send me notification<br /><br />&lt;submit&gt;标签<br />&lt;submit&gt;标签用于生成提交类型（type submit）的输入区域。比如： &lt;html:submit value=\"Login\"/&gt;<br />会被转换成： &lt;input type=\"submit\" value=\"Login\"&gt;<br /><br />&lt;reset&gt;标签<br />&lt;reset&gt;标签用于生成重置类型（type reset）的输入区域。比如： &lt;html:reset/&gt;<br />会被转换成： &lt;input type=\"reset\" value=\"Reset\"&gt;<br /><br />&lt;option&gt;标签<br />&lt;option&gt;标签用于显示select box中的一个选项。参照下面的&lt;select&gt;标签。<br /><br />&lt;select&gt;标签 <br />&lt;select&gt;标签用于显示一个包含零个或更多选项的HTML选择元素。它必须被嵌入一个&lt;form&gt;标签中才有效。下面这个例子将说明如何使用该标签： &lt;html:select property=\"color\" size=\"3\"&gt;<br />  &lt;html:option value=<br />      \"r\"&gt;red&lt;/html:option&gt;<br />  &lt;html:option value=<br />      \"g\"&gt;green&lt;/html:option&gt;<br />  &lt;html:option value=<br />      \"b\"&gt;blue&lt;/html:option&gt;<br />&lt;/html:select&gt;<br /><br />会被转换成： &lt;select name=\"color\" size=\"3\"&gt;&lt;option <br />      value=\"r\"&gt;red&lt;/option&gt;<br />  &lt;option value=\"g\"&gt;green&lt;/option&gt;<br />  &lt;option value=\"b\"&gt;blue&lt;/option&gt;<br />&lt;/select&gt;<br /><br />在本文系列的第二部分，你学到了一些更重要的和form相关的标签。在使用这些标签之前，你必须依照一些规则以便正确地运用它们。第三部分将讲述如何在真正的程序中使用这些标签。<br /></td>
						</tr>
						<tr>
								<td colspan="3" height="15"> </td>
						</tr>
				</tbody>
		</table><img src ="http://www.cnitblog.com/tilan/aggbug/21089.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-25 16:06 <a href="http://www.cnitblog.com/tilan/articles/21089.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts原理与实践（2）</title><link>http://www.cnitblog.com/tilan/articles/21078.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 25 Dec 2006 06:20:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21078.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21078.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21078.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21078.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21078.html</trackback:ping><description><![CDATA[下面，我们就从一个最简单的登录例子入手，以对Struts的主要部分有一些直观而清晰的认识。这个例子功能非常简单，假设有一个名为lhb的用户，其密码是awave，程序要完成的任务是，呈现一个登录界面给用户，如果用户输入的名称和密码都正确返回一个欢迎页面给用户，否则，就返回登录页面要求用户重新登录并显示相应的出错信息。这个例子在我们讲述Struts的基础部分时会反复用到。之所以选用这个简单的程序作为例子是因为不想让过于复杂的业务逻辑来冲淡我们的主题。 <br /><br />因为Struts是建立在MVC设计模式上的框架，你可以遵从标准的开发步骤来开发你的Struts Web应用程序，这些步骤大致可以描述如下：<br />1定义并生成所有代表应用程序的用户接口的Views，同时生成这些Views所用到的所有ActionForms并将它们添加到struts-config.xml文件中。<br />2在ApplicationResource.properties文件中添加必要的MessageResources项目<br />3生成应用程序的控制器。<br />4在struts-config.xml文件中定义Views与 Controller的关系。<br />5生成应用程序所需要的model组件<br />6编译、运行你的应用程序. <br /><br />下面，我们就一步步按照上面所说的步骤来完成我们的应用程序： <br /><br />第一步，我们的应用程序的Views部分包含两个.jsp页面：一个是登录页面logon.jsp，另一个是用户登录成功后的用户功能页main.jsp，暂时这个页面只是个简单的欢迎页面。 <br /><br />其中，logon.jsp的代码清单如下： <br /><blockquote><font size="-1">quote:</font><hr /><br />&lt;%@ page contentType=\"text/html; charset=UTF-8\" %&gt;<br />&lt;%@ taglib uri=\"/WEB-INF/struts-bean.tld\" prefix=\"bean\" %&gt;<br />&lt;%@ taglib uri=\"/WEB-INF/struts-html.tld\" prefix=\"html\" %&gt;<br />&lt;HTML&gt;<br />&lt;HEAD&gt;<br />&lt;TITLE&gt;&lt;bean:message key=\"logon.jsp.title\"/&gt;&lt;/TITLE&gt;<br />&lt;html:base/&gt;<br />&lt;/HEAD&gt;<br />&lt;BODY&gt;<br />&lt;h3&gt;&lt;bean:message key=\"logon.jsp.page.heading\"/&gt;&lt;/h3&gt;<br />&lt;html:errors/&gt;<br />&lt;html:form action=\"/logonAction.do\" focus=\"username\"&gt;<br />&lt;TABLE border=\"0\" width=\"100%\"&gt;<br />&lt;TR&gt;<br />&lt;TH align=\"right\"&gt;&lt;bean:message key=\"logon.jsp.prompt.username\"/&gt;&lt;/TH&gt;<br />&lt;TD align=\"left\"&gt;&lt;html:text property=\"username\"/&gt;&lt;/TD&gt;<br />&lt;/TR&gt;<br />&lt;TR&gt;<br />&lt;TH align=\"right\"&gt;&lt;bean:message key=\"logon.jsp.prompt.password\"/&gt;&lt;/TH&gt;<br />&lt;TD align=\"left\"&gt;&lt;html:password property=\"password\"/&gt;&lt;/TD&gt;<br />&lt;/TR&gt;<br />&lt;TR&gt;<br />&lt;TD align=\"right\"&gt;<br />  &lt;html:submit&gt;&lt;bean:message key=\"logon.jsp.prompt.submit\"/&gt;&lt;/html:submit&gt;<br />&lt;/TD&gt;<br />&lt;TD align=\"left\"&gt;<br />  &lt;html:reset&gt;&lt;bean:message key=\"logon.jsp.prompt.reset\"/&gt;&lt;/html:reset&gt;<br />&lt;/TD&gt;<br />&lt;/TR&gt;<br />&lt;/TABLE&gt;<br />&lt;/html:form&gt;<br />&lt;/BODY&gt;<br />&lt;/HTML&gt;<br />  
<hr /></blockquote><br /><br /><br />main.jsp的代码清单如下： <br /><blockquote><font size="-1">quote:</font><hr /><br />&lt;%@ page contentType=\"text/html; charset=UTF-8\" %&gt;<br />&lt;%@ taglib uri=\"/WEB-INF/struts-bean.tld\" prefix=\"bean\" %&gt;<br />&lt;%@ taglib uri=\"/WEB-INF/struts-logic.tld\" prefix=\"logic\" %&gt;<br /><br />&lt;HTML&gt;<br />&lt;HEAD&gt;<br />&lt;TITLE&gt;&lt;bean:message key=\"main.jsp.title\"/&gt;&lt;/TITLE&gt;<br />&lt;html:base/&gt;<br />&lt;/HEAD&gt;<br />&lt;BODY&gt;<br />&lt;logic:present name=\"userInfoForm\"&gt;<br />&lt;H3&gt;<br />  &lt;bean:message key=\"main.jsp.welcome\"/&gt; <br />  &lt;bean:write name=\"userInfoForm\" property=\"username\"/&gt;!<br />&lt;/H3&gt;<br />&lt;/logic:present&gt;<br />&lt;/BODY&gt;<br />&lt;/HTML&gt;<br />  
<hr /></blockquote><br /><br /><br />首先，我们看一下logon.jsp文件，会发现它有这么两个鲜明的特点：一是文件头部有诸如：<br /><br /><br /><br />这样的指令代码，他们的作用就是指示页面要用到struts的自定义标签，标签库uri是一个逻辑引用，标签库的描述符（tld）的位置在web.xml文件中给出，见上篇文章的配置部分。struts的标签库主要由四组标签组成,它们分别是：<br /><br />bean标签，作用是在jsp中操纵bean<br /><br />logic标签，作用是在jsp中进行流程控制<br /><br />html标签，作用是显示表单等组件<br /><br />template标签，作用是生成动态模板 <br /><br />关于每类标签的具体作用及语法，因受篇幅限制，不在这里详细讨论，大家可参考struts手册之类的资料。只是心里要明白所谓标签其后面的东西就是一些类，这点与bean有些相似，它们在后端运行，生成标准的html标签返回给浏览器。 <br /><br />要使用它们显然要把它们的标签库描述文件引入到我们的系统中，这是些以.tld为扩展名的文件，我们要把它们放在/webapps/mystruts/WEB-INF/目录下。引入struts标签后原来普通的html标签如文本框的标签变成了这样的形式。 <br /><br />Jsp文件的第二个特点，就是页面上根本没有直接写用于显示的文字如：username,password等东西，而是用这种形式出现。这个特点为国际化编程打下了坚实的基础，关于国际化编程后面的文章还会专门讨论。 <br /><br />这个简单的应用所用到的ActionForm为UserInfoForm,代码清单如下： <br /><blockquote><font size="-1">quote:</font><hr /><br />package entity;<br />import org.apache.struts.action.ActionForm;<br />import org.apache.struts.action.ActionMapping;<br />import javax.servlet.http.HttpServletRequest;<br /><br />public class UserInfoForm extends ActionForm{<br /><br />  private String username;<br />  private String password;<br /><br /><br />  public String getUsername() {<br />    return (this.username);<br />  }<br />  public void setUsername(String username) {<br />    this.username = username;<br />  }<br /><br />  public String getPassword() {<br />    return (this.password);<br />  }<br />  public void setPassword(String password) {<br />    this.password = password;<br />  }<br />}<br /> <br /><hr /></blockquote><br /><br />在你的应用程序的WEB-INF目录下再建一个classes目录，在新建的这个classes目录下再建如下几个目录entity(用于存放ActionForm类)、action目录（用于存放Action类）、bussness目录（用于存放作为Model的业务对象类）。Classes目录下的子目录就是所谓的包，以后，还会根据需要增加相应的包。 <br /><br />现在，将UserInfoForm.java保存到entity目录中。 <br /><br />把如下代码添加到/webapps/mystruts/WEB-INF/struts-config.xml文件中 <br /><blockquote><font size="-1">quote:</font><hr /><br />&lt;form-beans&gt;<br />    &lt;form-bean name=\"userInfoForm\" type=\"entity.UserInfoForm\" /&gt;<br />  &lt;/form-beans&gt;<br />  
<hr /></blockquote><br /><br /><br />特别要提醒一下的是：关于ActionForm的大小写，一定要按照上面的写，以免造成不必要的麻烦。 <br /><br />到此，我们完成了第一步工作。 <br /><br />第二步，我们建一个名为ApplicationResource.properties的文件，并把它放在/webapps/mystruts/WEB-INF/classes目录下。它在struts-config.xml的配置信息我们已在第一篇文章的末尾说了，就是：<br /><br /><br />目前我们在ApplicationResource.properties文件中加入的内容是： <br /><blockquote><font size="-1">quote:</font><hr /><br />#Application Resource for the logon.jsp<br />logon.jsp.title=The logon page<br />logon.jsp.page.heading=Welcome World!<br />logon.jsp.prompt.username=Username:<br />logon.jsp.prompt.password=Password:<br />logon.jsp.prompt.submit=Submit<br />logon.jsp.prompt.reset=Reset<br /><br />#Application Resource for the main.jsp<br />main.jsp.title=The main page<br />main.jsp.welcome=Welcome:<br />  
<hr /></blockquote><br /><br /><br />到此，我们已完成了第二个步骤。 <br /><br />第三步，我们开始生成和配置Controller组件。 <br /><br />在前面我们已经提到，Struts应用程序的控制器由org.apache.struts.action.ActionServlet和org.apache.struts.action.Action类组成，其中，前者已由Struts准备好了，后者Struts只是为我们提供了个骨架，我们要做的是为实现应用程序的特定功能而扩展Action类，下面是实现我们登录程序的Action类的代码清单： <br /><blockquote><font size="-1">quote:</font><hr /><br />package action;<br />import java.io.IOException;<br />import javax.servlet.ServletException;<br />import javax.servlet.http.HttpServletRequest;<br />import javax.servlet.http.HttpSession;<br />import javax.servlet.http.HttpServletResponse;<br />import org.apache.struts.action.Action;<br />import org.apache.struts.action.ActionError;<br />import org.apache.struts.action.ActionErrors;<br />import org.apache.struts.action.ActionForm;<br />import org.apache.struts.action.ActionForward;<br />import org.apache.struts.action.ActionMapping;<br />import org.apache.struts.action.ActionServlet;<br />import bussness.UserInfoBo;<br />import entity.UserInfoForm;<br />public final class LogonAction extends Action {<br />  <br />  public ActionForward execute(ActionMapping mapping,<br />         ActionForm form,<br />         HttpServletRequest request,<br />         HttpServletResponse response)<br />         throws IOException, ServletException {<br />    UserInfoForm userInfoForm = (UserInfoForm) form;         <br />    //从web层获得用户名和口令<br />    String username = userInfoForm.getUsername().trim();<br />    String password = userInfoForm.getPassword().trim();<br />    //声明错误集对象<br />    ActionErrors errors = new ActionErrors();<br />    //校验输入<br />    if(username.equals(\"\")){<br />      ActionError error=new ActionError(\"error.missing.username\");<br />      errors.add(ActionErrors.GLOBAL_ERROR,error);<br />    }<br />    if(password.equals(\"\")){<br />      ActionError error=new ActionError(\"error.missing.password\");<br />      errors.add(ActionErrors.GLOBAL_ERROR,error);<br />    }<br />    <br />    //调用业务逻辑<br />    if(errors.size()==0){<br />      String validated = \"\";<br />      try{<br />        UserInfoBo userInfoBo=new UserInfoBo();<br />        validated =userInfoBo.validatePwd(username,password);<br />        if(validated.equals(\"match\")){<br />          //一切正常就保存用户信息并转向成功的页面    <br />          HttpSession session = request.getSession();<br />          session.setAttribute(\"userInfoForm\", form);          <br />            return mapping.findForward(\"success\");<br />        } <br />      }<br />      <br />      catch(Throwable e){<br />        //处理可能出现的错误<br />        e.printStackTrace();<br />        ActionError error=new ActionError(e.getMessage());<br />        errors.add(ActionErrors.GLOBAL_ERROR,error);<br />      }<br />    }  <br />    //如出错就转向输入页面，并显示相应的错误信息<br />    saveErrors(request, errors);    <br />    return new ActionForward(mapping.getInput());    <br />  } <br />}<br /> <br /><hr /></blockquote><br /><br />这个action类中有两个错误消息键要加到ApplicationResource.properties文件中，清单如下： <br /><blockquote><font size="-1">quote:</font><hr /><br />#Application Resource for the LogonAction.java<br />error.missing.username=&lt;li&gt;&lt;font color=\"red\"&gt;missing username&lt;/font&gt;&lt;/li&gt;<br />error.missing.password=&lt;li&gt;&lt;font color=\"red\"&gt;missing password&lt;/font&gt;&lt;/li&gt;&gt;<br />  
<hr /></blockquote><br /><br /><br />第四步：在struts-config.xml文件中定义Views与 Controller的关系，也就是配置所谓的ActionMapping。它们在struts-config.xml中的位置是排在… 标签后，我们的登录程序的配置清单如下： <br /><blockquote><font size="-1">quote:</font><hr /><br />&lt;action-mappings&gt;<br />    &lt;action input=\"/logon.jsp\" name=\"userInfoForm\" path=\"/logonAction\" scope=\"session\" <br />    type=\"action.LogonAction\" validate=\"false\"&gt;<br />      &lt;forward name=\"success\" path=\"/main.jsp\" /&gt;      <br />    &lt;/action&gt;<br />  &lt;/action-mappings&gt;<br />  
<hr /></blockquote><br /><br /><br />第五步：生成应用程序所需要的model组件，该组件是完成应用程序业务逻辑的地方，现在我的登录程序的业务逻辑很简单，就是判断用户是不是lhb并且其口令是不是awave如果是就返回一个表示匹配的字符串\"match\"，否则，就抛出出错信息。其代码清单如下： <br /><blockquote><font size="-1">quote:</font><hr /><br />package bussness;<br /><br />import entity.UserInfoForm;<br /><br />public class UserInfoBo {<br /><br />  public UserInfoBo(){<br />    <br />  }      <br /><br />  public String validatePwd(String username,String password){<br />        <br />    String validateResult=\"\"; <br />       <br />    if(username.equals(\"lhb\")&amp;&amp;password.equals(\"awave\")){<br />      validateResult=\"match\";<br />    }<br />    else{<br />      <br />      throw new RuntimeException(\"error.noMatch\");<br />    }        <br />    <br />    return validateResult;   <br />    <br />  }<br />}<br /> <br /><hr /></blockquote><br /><br />将其放在bussness包中。 <br /><br />我们同样要将其表示错误信息的键值设置在ApplicationResource.properties文件中，清单如下： <br /><blockquote><font size="-1">quote:</font><hr /><br />#Application Resource for the UserInfoBo.java<br />error.noMatch=&lt;li&gt;&lt;font color=\"red\"&gt;no matched user&lt;/font&gt;&lt;/li&gt;<br />  
<hr /></blockquote><br /><br /><br />到此为止，我们已经完成了这个简单登录程序的所有组件。下面就可以享受我们的劳动成果了。 <br /><br />第六步、编译运行应用程序。 <br /><br />常规的做法是用Ant来装配和部署Struts应用程序，如果按这个套路，这篇文章就会显得十分冗长乏味，同时也没有太大的必要，因为，用一个IDE一般可以很方便地生成一个应用。因此，我们采用简便的方法，直接编译我们的.java文件。不过这里要注意一点的是：实践证明，要使得编译过程不出错，还必须将struts.jar文件放一份拷贝到/common/lib目录中，并在环境变量中设置CLASSPATH 其值是/common/lib/struts.jar;配置好后就可以分别编译entity、bussness及action目录下的.java文件了。编译完成后：打开/conf目录下的server.xml文件，在前加上如下语句为我们的应用程序建一个虚拟目录： <br /><blockquote><font size="-1">quote:</font><hr /><br />&lt;Context path=\"/mystruts\" docBase=\"mystruts\" debug=\"0\"<br />                 reloadable=\"true\"&gt;                 <br />&lt;/Context&gt;<br />  
<hr /></blockquote><br />启动，tomcat。在浏览器中输入：http://localhost:8080/mystruts/logon.jsp<br />如果前面的步骤没有纰漏的话，一个如图所示的登录画面就会出现在你的眼前<br /><a href="http://tech.ccidnet.com/pub/attachment/2004/8/320304.gif" target="_blank"><img title="open in new window" alt="" src="http://tech.ccidnet.com/pub/attachment/2004/8/320304.gif" border="0" /></a><br />如果，不输入任何内容直接点击Submit按钮，就会返回到logon.jsp并显示missing username和missing password错误信息；如果输入其他内容，则会返回no matched user的错误;如果输入的用户名是lhb且口令是awave则会显示表示登录成功的欢迎页面。 <br /><br />上面虽然是一个功能很简单的应用程序，但麻雀虽小，五脏俱全，基本涉及到了struts的主要组成部分。下面我们就来分析一下程序的特点和基本的工作原理。 <br /><br />首先，我们在浏览器中输入.jsp文件时，后台将struts的自定义标签\"翻译\"成普通的html标签返回给浏览器，而一些提示信息如作为输入框label的username、password还有按钮上提示信息还有错误信息等都来自MessageResources即ApplicationResource.properties文件中对应的键值。当我们点击Submit按钮时，从web.xml的配置可以看出，请求将被ActionServlet截获。它通过表单中提供的action参数在struts-config.xml文件中查找对应的项目，如果有对应的ActionForm，它就用表单中数据填充ActionForm的对应属性，本例中的ActionForm为userInfoForm，相应的属性是username和password，这就是所谓的实例化ActionForm。然后，将控制交给对应的Action，本例中是LogonAction，它做的主要工作是对ActionForm中取出的username和password做了一下校验，这里只是简单检验它们是否为空（这些简单的格式化方面的校验应该放在客户端进行，而且struts也为我们提供了一个很好的模式，后面如果有可能会详细介绍）。如果不为空则调用判断用户及口令是否正确的业务逻辑模块UserInfoBo，同时，它会捕获可能出现的错误，然后根据业务逻辑返回的结果将程序导向不同的页面，本例中如果业务逻辑返回的结果是\"match\"则依据中的返回main.jsp页面给浏览器同时在session对象中保存了用户的登录信息；否则，返回输入页面并显示相应的出错信息，完成了上篇文章所说的它的四个主要职责。 <br /><br />大家一定注意到了，在本例的业务逻辑模块UserInfoBo中，将用户与密码是写死在程序中的，在一个真实的应用程序中是不会这样做的，那些需要永久保存的信息如，username及口令等都会保存在数据库文件之类的永久介质中，下一篇文章我们将介绍在struts中如何访问数据库。 <br /><img src ="http://www.cnitblog.com/tilan/aggbug/21078.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-25 14:20 <a href="http://www.cnitblog.com/tilan/articles/21078.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts原理与实践（1）</title><link>http://www.cnitblog.com/tilan/articles/21077.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 25 Dec 2006 06:19:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/21077.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/21077.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/21077.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/21077.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/21077.html</trackback:ping><description><![CDATA[<strong>一、 什么是Struts <br /><br /></strong>框架（Framework）是可重用的，半完成的应用程序，可以用来产生专门的定制程序。 <br /><br />您只要细心地研究真实的应用程序，就会发现程序大致上由两类性质不同的组件组成，一类与程序要处理的具体事务密切相关，我们不妨把它们叫做业务组件；另一类是应用服务。比如说：一个税务征管系统和一个图书管理系统会在处理它们的业务方面存在很大的差异，这些直接处理业务的组件由于业务性质的不同不大可能在不同的系统中重用，而另一些组件如决定程序流向的控制、输入的校验、错误处理及标签库等这些只与程序相关的组件在不同的系统中可以很好地得到重用。人们自然会想要是把这些在不同应用程序中有共性的一些东西抽取出来，做成一个半成品程序，这样的半成品就是所谓的程序框架，再做一个新的东西时就不必白手起家，而是可以在这个基础上开始搭建。实际上，有些大型软件企业选择自己搭建这样的框架。但大多数中小型软件企业或者其他组织，没有条件自己建立框架。 <br /><br />Struts作为一个开放原代码的应用框架，在最近几年得到了飞速的发展，在JSP Web应用开发中应用得非常广泛，有的文献上说它已经成为JSP Web应用框架的事实上的标准。那么，究竟什么是Struts呢？ <br /><br />要回答这个问题还得从JSP Web应用的两种基本的结构模式:Model 1和Model 2说起，为了给读者一些实实在在的帮助，并力图让学习曲线变得平坦一些，我想采用实例驱动的方法来逐步深入地回答有关问题，因为，学一门技术的最好方法莫过于在实践中学习、在实践中体会，逐步加深对其精神实质的理解和把握，而不是一上来就引入一大堆新概念让大家觉得无所适从，或者死记硬背一大堆概念而面对一个真正的实际需求束手无策。正如，一个人即使在书本上学成了游泳博士，只要他不下水，我想他也是不大可能真正会游泳的。 <br /><br />Model 1结构如图1所示： <br /><br /><a href="http://tech.ccidnet.com/pub/attachment/2004/8/320186.jpg" target="_blank"><img title="open in new window" alt="" src="http://tech.ccidnet.com/pub/attachment/2004/8/320186.jpg" border="0" /></a><br />图1 <br /><br />mode1 1是一个以JSP文件为中心的模式，在这种模式中JSP页面不仅负责表现逻辑，也负责控制逻辑。专业书籍上称之为逻辑耦合在页面中，这种处理方式，对一些规模很小的项目如：一个简单的留言簿，也没什么太大的坏处，实际上，人们开始接触一些对自己来说是新的东西的时候，比如，用JSP访问数据库时，往往喜欢别人能提供一个包含这一切的单个JSP页面，因为这样在一个页面上他就可以把握全局，便于理解。但是，用Model 1模式开发大型时，程序流向由一些互相能够感知的页面决定，当页面很多时要清楚地把握其流向将是很复杂的事情，当您修改一页时可能会影响相关的很多页面，大有牵一发而动全身的感觉，使得程序的修改与维护变得异常困难；还有一个问题就是程序逻辑开发与页面设计纠缠在一起，既不便于分工合作也不利于代码的重用，这样的程序其健壮性和可伸缩性都不好。 <br /><br />Grady Booch等人在UML用户指南一书中，强调建模的重要性时，打了一个制作狗窝、私人住宅、和大厦的形象比喻来说明人们处理不同规模的事物时应该采用的合理方法一样，人们对不同规模的应用程序也应该采用不同的模式。 <br /><br />为了克服Model 1的缺陷，人们引入了Model 2，如图2所示：<br /><br /><a href="http://tech.ccidnet.com/pub/attachment/2004/8/320188.jpg" target="_blank"><img title="open in new window" alt="" src="http://tech.ccidnet.com/pub/attachment/2004/8/320188.jpg" border="0" /></a><br />图2 <br /><br />它引入了\"控制器\"这个概念，控制器一般由servlet来担任，客户端的请求不再直接送给一个处理业务逻辑的JSP页面，而是送给这个控制器，再由控制器根据具体的请求调用不同的事务逻辑，并将处理结果返回到合适的页面。因此，这个servlet控制器为应用程序提供了一个进行前-后端处理的中枢。一方面为输入数据的验证、身份认证、日志及实现国际化编程提供了一个合适的切入点；另一方面也提供了将业务逻辑从JSP文件剥离的可能。业务逻辑从JSP页面分离后，JSP文件蜕变成一个单纯完成显示任务的东西，这就是常说的View。而独立出来的事务逻辑变成人们常说的Model,再加上控制器Control本身，就构成了MVC模式。实践证明，MVC模式为大型程序的开发及维护提供了巨大的便利。 <br /><br />其实，MVC开始并不是为Web应用程序提出的模式，传统的MVC要求M将其状态变化通报给V，但由于Web浏览器工作在典型的拉模式而非推模式，很难做到这一点。因此有些人又将用于Web应用的MVC称之为MVC2。正如上面所提到的MVC是一种模式，当然可以有各种不同的具体实现，包括您自己就可以实现一个体现MVC思想的程序框架，Struts就是一种具体实现MVC2的程序框架。它的大致结构如图三所示<br /><a href="http://tech.ccidnet.com/pub/attachment/2004/8/320190.gif" target="_blank"><img title="open in new window" alt="" src="http://tech.ccidnet.com/pub/attachment/2004/8/320190.gif" border="0" /></a><br />图三 <br /><br />图三基本勾勒出了一个基于Struts的应用程序的结构，从左到右，分别是其表示层（view）、控制层(controller)、和模型层(Model)。其表示层使用Struts标签库构建。来自客户的所有需要通过框架的请求统一由叫ActionServlet的servlet接收（ActionServlet Struts已经为我们写好了，只要您应用没有什么特别的要求，它基本上都能满足您的要求），根据接收的请求参数和Struts配置(struts-config.xml)中ActionMapping，将请求送给合适的Action去处理，解决由谁做的问题，它们共同构成Struts的控制器。Action则是Struts应用中真正干活的组件，开发人员一般都要在这里耗费大量的时间，它解决的是做什么的问题，它通过调用需要的业务组件（模型）来完成应用的业务，业务组件解决的是如何做的问题，并将执行的结果返回一个代表所需的描绘响应的JSP（或Action）的ActionForward对象给ActionServlet以将响应呈现给客户。 <br /><br />过程如图四所示： <br /><a href="http://tech.ccidnet.com/pub/attachment/2004/8/320192.jpg" target="_blank"><img title="open in new window" alt="" src="http://tech.ccidnet.com/pub/attachment/2004/8/320192.jpg" border="0" /></a><br />这里要特别说明一下的是：就是Action这个类，上面已经说到了它是Struts中真正干活的地方，也是值得我们高度关注的地方。可是，关于它到底是属于控制层还是属于模型层，存在两种不同的意见，一种认为它属于模型层，如：《JSP Web编程指南》；另一些则认为它属于控制层如：《Programming Jakarta Struts》、《Mastering Jakarta Struts》和《Struts Kick Start》等认为它是控制器的一部分，还有其他一些书如《Struts in Action》也建议要避免将业务逻辑放在Action类中，也就是说，图3中Action后的括号中的内容应该从中移出，但实际中确有一些系统将比较简单的且不打算重用的业务逻辑放在Action中，所以在图中还是这样表示。显然，将业务对象从Action分离出来后有利于它的重用，同时也增强了应用程序的健壮性和设计的灵活性。因此，它实际上可以看作是Controller与Model的适配器，如果硬要把它归于那一部分，笔者更倾向于后一种看法，即它是Controller的一部分，换句话说，它不应该包含过多的业务逻辑，而应该只是简单地收集业务方法所需要的数据并传递给业务对象。实际上，它的主要职责是：<br /><br />校验前提条件或者声明<br /><br />调用需要的业务逻辑方法<br /><br />检测或处理其他错误<br /><br />路由控制到相关视图 <br /><br />上面这样简单的描述，初学者可能会感到有些难以接受，下面举个比较具体的例子来进一步帮助我们理解。如：假设，我们做的是个电子商务程序，现在程序要完成的操作任务是提交定单并返回定单号给客户，这就是关于做什么的问题，应该由Action类完成，但具体怎么获得数据库连接，插入定单数据到数据库表中，又怎么从数据库表中取得这个定单号（一般是自增数据列的数据），这一系列复杂的问题，这都是解决怎么做的问题，则应该由一个（假设名为orderBo）业务对象即Model来完成。orderBo可能用一个返回整型值的名为submitOrder的方法来做这件事，Action则是先校验定单数据是否正确，以免常说的垃圾进垃圾出；如果正确则简单地调用orderBo的submitOrder方法来得到定单号；它还要处理在调用过程中可能出现任何错误；最后根据不同的情况返回不同的结果给客户。 <br /><br />二、为什么要使用Struts框架 <br /><br />既然本文的开始就说了，自己可以建这种框架，为什么要使用Struts呢？我想下面列举的这些理由是显而易见的：首先，它是建立在MVC这种公认的好的模式上的，Struts在M、V和C上都有涉及，但它主要是提供一个好的控制器和一套定制的标签库上，也就是说它的着力点在C和V上，因此，它天生就有MVC所带来的一系列优点，如：结构层次分明，高可重用性，增加了程序的健壮性和可伸缩性，便于开发与设计分工，提供集中统一的权限控制、校验、国际化、日志等等；其次，它是个开源项目得到了包括它的发明者Craig R.McClanahan在内的一些程序大师和高手持续而细心的呵护，并且经受了实战的检验，使其功能越来越强大，体系也日臻完善；最后，是它对其他技术和框架显示出很好的融合性。如，现在，它已经与tiles融为一体，可以展望，它很快就会与JSF等融会在一起。当然，和其他任何技术一样，它也不是十全十美的，如：它对类和一些属性、参数的命名显得有些随意，给使用带来一些不便；还有如Action类execute方法的只能接收一个ActionForm参数等。但瑕不掩瑜，这些没有影响它被广泛使用。 <br /><br />三、Struts的安装与基本配置 <br /><br />我们主要针对Struts1.1版本进行讲解，这里假定读者已经配置好java运行环境和相应的Web容器，本文例子所使用的是j2sdk和Tomcat4.1.27。下面，将采用类似于step by step的方式介绍其基础部分。 <br /><br />安装Struts<br />到http://jakarta.apache.org/ 下载Struts的安装文件，本文例子使用的是1.1版。 <br /><br />接下来您要进行如下几个步骤来完成安装：<br />1、解压下载的安装文件到您的本地硬盘<br />2、生成一个新的Web应用，假设我们生成的应用程序的根目录在/Webapps/mystruts目录。在server.xml文件中为该应用新建一个别名如/mystruts<br />3、从第1步解压的文件中拷贝下列jar文件到/Webapps/mystruts/WEB-INF/lib目录，主要文件有如下一些.<br /><blockquote><font size="-1">quote:</font><hr />
struts.jar<br />commons－beanutils.jar<br />commons－collections.jar<br />commons－dbcp.jar<br />commons－digester.jar<br />commons－logging.jar<br />commons－pool.jar<br />commons－services.jar<br />commons－validator.jar<br /><hr /></blockquote><br />4、创建一个web.xml文件，这是一个基于servlet的Web应用程序都需要的部署描述文件，一个Struts Web应用，在本质上也是一个基于servlet的Web应用，它也不能例外。 <br /><br />Struts有两个组件要在该文件中进行配置，它们是：ActionServlet和标签库。下面是一个配置清单：<br /><blockquote><font size="-1">quote:</font><hr /><br />&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;<br />&lt;!DOCTYPE web-app PUBLIC \"-//Sun Microsystems, Inc.//DTD Web Application 2.3<br />//EN\" \"http://java.sun.com/dtd/web-app_2_3.dtd\"&gt;<br />&lt;web-app&gt;<br />  &lt;servlet&gt;<br />    &lt;servlet-name&gt;action&lt;/servlet-name&gt;<br />    &lt;servlet-class&gt;org.apache.struts.action.ActionServlet&lt;/servlet-class&gt;<br />    &lt;init-param&gt;<br />      &lt;param-name&gt;config&lt;/param-name&gt;<br />      &lt;param-value&gt;/WEB-INF/struts-config.xml&lt;/param-value&gt;<br />    &lt;/init-param&gt;<br />    &lt;init-param&gt;<br />      &lt;param-name&gt;debug&lt;/param-name&gt;<br />      &lt;param-value&gt;2&lt;/param-value&gt;<br />    &lt;/init-param&gt;<br />    &lt;load-on-startup&gt;2&lt;/load-on-startup&gt;<br />  &lt;/servlet&gt;<br />  &lt;servlet-mapping&gt;<br />    &lt;servlet-name&gt;action&lt;/servlet-name&gt;<br />    &lt;url-pattern&gt;*.do&lt;/url-pattern&gt;<br />  &lt;/servlet-mapping&gt;<br />  &lt;taglib&gt;<br />    &lt;taglib-uri&gt;/WEB-INF/struts-bean.tld&lt;/taglib-uri&gt;<br />    &lt;taglib-location&gt;/WEB-INF/struts-bean.tld&lt;/taglib-location&gt;<br />  &lt;/taglib&gt;<br />  &lt;taglib&gt;<br />    &lt;taglib-uri&gt;/WEB-INF/struts-html.tld&lt;/taglib-uri&gt;<br />    &lt;taglib-location&gt;/WEB-INF/struts-html.tld&lt;/taglib-location&gt;<br />  &lt;/taglib&gt;<br />  &lt;taglib&gt;<br />    &lt;taglib-uri&gt;/WEB-INF/struts-logic.tld&lt;/taglib-uri&gt;<br />    &lt;taglib-location&gt;/WEB-INF/struts-logic.tld&lt;/taglib-location&gt;<br />  &lt;/taglib&gt;<br />&lt;/web-app&gt;<br /><hr /></blockquote><br />上面我们在web.xml中完成了对servlet和标签库的基本配置，而更多的框架组件要在struts-config.xml中进行配置： <br /><br />5、创建一个基本的struts-config.xml文件，并把它放在/Webapps/mystruts/WEB-INF/目录中，该文件是基于Struts应用程序的配置描述文件，它将MVC结构中的各组件结合在一起，开发的过程中会不断对它进行充实和更改。在Struts1.0时，一个应用只能有一个这样的文件，给分工开发带来了一些不便，在Struts1.1时，可以有多个这样的文件，将上述缺点克服了。需在该文件中配置的组件有：data-sources <br /><blockquote><font size="-1">quote:</font><hr /><br />global-execptions<br />form-beans<br />global-forwards<br />action-mappings<br />controller<br />message-resources<br />plug-in<br /><hr /></blockquote><br />配置清单如下： <br /><blockquote><font size="-1">quote:</font><hr /><br />&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;<br />&lt;!DOCTYPE struts-config PUBLIC \"-//Apache Software Foundation//DTD Struts Configuration 1.1<br />//EN\" \"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd\"&gt;<br />&lt;struts-config&gt;<br />  &lt;message-resources parameter=\"ApplicationResources\" /&gt;<br />&lt;/struts-config&gt;<br /> <br /><hr /></blockquote><br /><br />到此为止，我们已经具备了完成一个最简单Struts应用的所需的各种组件。前面已经提到，在开发过程中我们会不断充实和修改上面两个配置描述文件。下面我们将实际做一个非常简单的应用程序来体验一下Struts应用开发的真实过程，以期对其有一个真实的认识。在完成基础部分的介绍后，笔者会给出一些在实际开发中经常用到而又让初学者感到有些难度的实例。最后，会介绍Struts与其他框架的关系及结合它们生成应用程序的例子。 <br /><img src ="http://www.cnitblog.com/tilan/aggbug/21077.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-25 14:19 <a href="http://www.cnitblog.com/tilan/articles/21077.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用 WebSphere Studio V5 编写一个简单的 Struts 应用程序</title><link>http://www.cnitblog.com/tilan/articles/20905.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Thu, 21 Dec 2006 05:13:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/20905.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/20905.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/20905.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/20905.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/20905.html</trackback:ping><description><![CDATA[<p>
				<a name="N10061">
						<span class="atitle">
								<font face="Arial" size="4">引言</font>
						</span>
				</a>
		</p>
		<p>Struts 是 Apache 软件基金会（Apache Software Foundation）资助的一个开放源代码框架。您可以用它来维护和扩展 Web 应用程序。 <a href="http://www-128.ibm.com/developerworks/cn/websphere/zones/studio/index.html"><font color="#5c81a7">IBM® WebSphere® Studio Application Developer</font></a> 版本 5.0（以下称 WebSphere Studio）对 Struts 有内建支持，支持 Struts 1.02 和 1.1（beta 2）。WebSphere Studio 中的 Struts Configuration 编辑器使您可以很容易地修改 <code>struts-config.xml</code> 文件。这篇文章描述了怎样用 WebSphere Studio 中的内建支持创建一个 Struts 示例。 </p>
		<a name="sec1">
				<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/websphere/techjournal/0302_fung/fung.html#main">
																						<b>
																								<font color="#5c81a7">回页首</font>
																						</b>
																				</a>
																		</td>
																</tr>
														</tbody>
												</table>
										</td>
								</tr>
						</tbody>
				</table>
				<br />
				<br />
				<p>
						<a name="N10075">
								<span class="atitle">
										<font face="Arial" size="4">Struts</font>
								</span>
						</a>
				</p>
				<p>Struts 框架完全是用 Java 写的，写时用了标准 J2EE API。另外，它还采用了几种著名的 J2EE 设计模式，比如模型-视图-控制器（Model-view-controller）和 FrontController。</p>
				<p>
						<b>模型-视图-控制器（MVC）</b>
				</p>
				<p>模型-视图-控制器（MVC）是一种设计模式，它在定义下面三个应用层时作了明确的区分。</p>
				<ul>
						<li>
								<i>模型（model）</i>是应用程序的数据和业务规则集合。通常被称作应用程序的业务逻辑。 
</li>
						<li>
								<i>视图（view）</i>是应用程序的用户界面。 
</li>
						<li>
								<i>控制器（controller）</i>定义了应用程序与用户输入及模型进行交互的方式。它被称作应用程序逻辑。 </li>
				</ul>
				<p>通过明确区分各个层，MVC 允许组成每个层的各个组件间松散地耦合。这使它更加灵活，并且可以重用代码。例如，如果您为一个应用程序开发了几个用户界面，那么就需要开发视图组件，因为各应用层间是松散耦合的。</p>
				<p>Struts 框架是 MVC 的视图和控制器组件。下面展示了 Struts 怎样映射为 MVC 框架。Struts 有三个主要组成部分：</p>
				<ul>
						<li>
								<i>Action</i>bean 
</li>
						<li>
								<i>ActionServlet</i>
						</li>
						<li>
								<i>ActionForm</i>bean 和定制的标记。 </li>
				</ul>
				<p>图 1. MVC 和 Struts 框架 <br /><img height="491" alt="MVC 和 Struts 框架" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image1.jpg" width="600" /></p>
				<p>
						<b>
								<i>Action</i>bean 和 <i>ActionServlet</i></b>
						<br />Struts 提供了一个单独的 <i>ActionServlet</i>（ <i>org.apache.struts.action.ActionServlet</i>）来处理所有的浏览器请求。这种类型的框架我们称之为 FrontController 模式。每个浏览器请求都由 Struts 的 <i>Action</i>子类（ <i>org.apache.struts.action.Action</i> 的子类）来处理。每个浏览器请求都被映射为 <code>struts-config.xml</code> 文件内的一个 <i>Action</i>子类。 <i>ActionServlet</i>在初始化期间加载这种映射。要配置 Web 项目使其把所有的浏览器请求都传递给 <i>ActionServlet</i>，请把所有以 .do 结尾的 URI（例如 *.do）都映射为 Web 部署描述符中的 <i>ActionServlet</i>。然后您可以在 Struts 配置文件中为各个请求 URI（比如 /submit.do）提供实际的 <i>Action</i>子类映射。 <br /><br /><b><i>ActionForm</i>bean </b><br />浏览器请求可带参数。当用户提交 HTML 表单后，Struts 框架就把参数放在一个 <i>org.apache.struts.action.ActionForm</i>bean 中。您也可以用 <i>ActionForm</i>bean 把缺省值预先植入一个表单，这些缺省值可从数据库或其他的后端系统获得。如果用户在表单中输入了不正确的值， <i>ActionForm</i>可以进行验证。您可以用以前的输入重新显示表单。 <br /><br /><b>定制的标记</b><br />Struts 提供了许多支持 <i>ActionForm</i>bean 的 JSP 定制标记。这些定制的标记支持： </p>
				<ul>
						<li>把从 <i>ActionForm</i>子类中获得的值预先植入 HTML 表单。 
</li>
						<li>国际化，比如提供由用户语言环境决定的文本。 
</li>
						<li>逻辑，比如根据人们对页面的使用方式为其显示不同的标题。 </li>
				</ul>
				<p>Struts 是一个通用的框架，您可以很容易地把它和 WebSphere Studio 一起使用。下面我们来开始第一个 Struts 示例。</p>
				<a name="sec2">
						<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/websphere/techjournal/0302_fung/fung.html#main">
																								<b>
																										<font color="#5c81a7">回页首</font>
																								</b>
																						</a>
																				</td>
																		</tr>
																</tbody>
														</table>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
						<br />
						<p>
								<a name="N10125">
										<span class="atitle">
												<font face="Arial" size="4">编写一个简单的 Struts 应用程序</font>
										</span>
								</a>
						</p>
						<p>
								<b>先决条件：</b>
						</p>
						<p>启动 WebSphere Studio 版本 5.0：</p>
						<ol>
								<li>转到 Window Start 菜单。 
</li>
								<li>选择 <b>Programs =&gt;IBM WebSphere Studio =&gt;Application Developer 5.0。</b></li>
						</ol>
						<a name="sec2-1">
								<p>
										<a name="N10143">
												<span class="smalltitle">
														<strong>
																<font face="Arial">第 1 步：开始一个新的 Struts Web 项目</font>
														</strong>
												</span>
										</a>
								</p>
								<p>创建一个 1.3 EAR 项目（不包含 EJB / Client 或 Web 项目）：</p>
								<ol>
										<li>选择 <b>New =&gt;Projects =&gt;Enterprise Application Project</b>。 
</li>
										<li>选择 <b>Create 1.3 J2EE Enterprise Application Project</b>。按 <b>Next</b>。 
</li>
										<li>取消对全部三个子项目（Application Client Project、EJB Project 和 Web Project）的选择。 
</li>
										<li>输入 <code>StrutsEAR</code> 作为项目名。 
</li>
										<li>单击 <b>Finish</b>。 </li>
								</ol>
								<p>创建一个 Web 项目并添加 Struts 支持：</p>
								<ol>
										<li>选择 <b>New =&gt;Projects =&gt;Web =&gt;Web Project</b>。 
</li>
										<li>输入 <code>StrutsSampleWeb</code> 作为 Web 项目。 
</li>
										<li>选择 <b>Add Struts support</b>复选框。单击 <b>Next</b>。 
</li>
										<li>选择 <b>Existing</b>Enterprise Application Project。 
</li>
										<li>浏览查找新建的 EAR 项目。两次单击 <b>Next</b>。 
</li>
										<li>在 Struts Setting 页面中选择 <b>Override default settings</b>，并在下拉框中选择 <b>1.1 (beta 2)</b>，如图 2 所示。前面已经提到过，表单被提交时，HTML 表单数据被自动植入 Struts <i>ActionForm</i>。Struts 1.0.1 只支持简单的 Java 类型。而 Struts 1.1（beta 2）还支持 <i>java.util.HashMap</i>或其他的 Collection 类型。这一点我们将在 <a href="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/fung.html#sec2-2"><font color="#5c81a7">本文</font></a>的后面部分讨论。 
</li>
										<li>单击 <b>Finish</b>。 </li>
								</ol>
								<p>图 2 .覆盖 Web 项目创建向导中的缺省设置 <br /><img height="534" alt="覆盖 Web 项目创建向导中的缺省设置" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image2.jpg" width="592" /></p>
								<p>检查图 3 中的 Struts 文件结构。</p>
								<p>图 3 .支持 Struts 的 web 项目 <br /><img height="470" alt="支持 Struts 的 WEB 项目" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image3.jpg" width="369" /></p>
								<p>您将稍后修改下面的文件：</p>
								<ul>
										<li>
												<code>ApplicationResources.properties</code> 是 Struts 应用程序的资源绑定。语言环境的详细信息和错误消息都放在这个属性文件中。 
</li>
										<li>
												<code>struts-config.xml</code> 是 Struts 的 xml 配置文件。WebSphere Studio 为这个文件提供了一个 GUI 编辑器。 </li>
								</ul>
								<p>现在，请执行下列操作：</p>
								<ol>
										<li>检查 <code>web.xml</code> 文件。 
</li>
										<li>展开 StrutsSampleWeb 项目并双击 <b>Web Deployment Descriptor</b>来打开编辑器。 
</li>
										<li>转到 <b>Servlets</b>页面。请注意下面两点： 
<ul><li>名为 <i>action</i>的 Struts <i>org.apache.struts.action.ActionServlet</i>的定义。 
</li><li>到这个 servlet 的 URL 映射， <i>*.do</i>。 </li></ul></li>
								</ol>
								<p>请注意，在 <i>Initialization</i>部分， <i>validate</i>被设为 true。 <i>ActionServlet</i>用 XML 解析器来验证和处理配置文件。它与表单验证无关，稍后在 <a href="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/fung.html#sec2-6"><font color="#5c81a7">本文</font></a>中您将看到这一点。 </p>
								<p>
										<b>*.do 怎样获得正确的 <i>Action</i>类？ </b>
								</p>
								<p>前面已经提到过， <i>ActionServlet</i>和 <i>Action</i>类是 MVC 模型中控制器层的核心。该控制器负责处理用户的请求，把请求路由到业务逻辑，并选择视图来响应用户（请参阅 <a href="http://jakarta.apache.org/struts/userGuide/index.html"><font color="#5c81a7">Struts 用户指南，第 4.4 节</font></a>）。表单提交给 <i>submit.do</i>后，Struts <i>ActionServlet</i> 会根据 struts-config.xml 文件中的 <code>&lt;action-mapping&gt;</code> 选择正确的 <i>Action</i>类来用。 </p>
								<p>Struts <i>Action</i> 子类负责处理用户数据。在这个示例中，创建一个名为 <code>SubmitAction</code> 的 Struts <i>Action</i>子类。它由诸如读和处理表单数据之类的操作组成。每一个表单都和该 Struts <i>ActionForm</i>子类的一个实例关联在一起。请创建这个继承 <i>ActionForm</i>的表单类。SubmitForm 是 <i>ActionForm</i>的一个子类，它是用域的 getter 和 setter 方法创建的。getter 和 setter 方法在 <i>ActionForm</i>子类中都是必须有的。 </p>
								<p>
										<b>SubmitForm 怎样发挥作用？</b>
								</p>
								<p>每一个 Struts <i>Action</i>类都必须和一个 Struts <i>ActionForm</i>类关联在一起。您可以在 WebSphere Studio 中的 struts-config.xml 编辑器的 FormBean 页面中定义 SubmitForm 类。然后可以把它与 struts-config.xml 文件中的 SubmitAction 映射关联在一起。一个请求提交后， <i>ActionServlet</i> 把从 Web 浏览器上的实际表单中得到的数据自动植入 SubmitForm。在 SubmitAction 类中，用 <code>SubmitForm f = (SubmitForm) form</code> 来访问表单数据。 </p>
								<a name="sec2-2">
										<p>
												<a name="N1026C">
														<span class="smalltitle">
																<strong>
																		<font face="Arial">第 2 步：用 Struts taglib 构建一个 JSP 表单</font>
																</strong>
														</span>
												</a>
										</p>
										<p>Struts 为输入域提供了许多 HTML 标记并为 JSP 表单提供了许多超链接。下面列出了常用的几个：</p>
										<ul>
												<li>复选框 - <code>&lt;html:checkbox property="name"/&gt;</code></li>
												<li>隐藏域 - <code>&lt;html:hidden property="name"/&gt;</code></li>
												<li>密码输入域 - <code>&lt;html:password property="name"/&gt;</code></li>
												<li>单选按钮 - <code>&lt;html:radio property="name"/&gt;</code></li>
												<li>重设按钮 - <code>&lt;html:reset/&gt;</code></li>
												<li>选项（下拉框） <br /><code>&lt;html:select property="name"/&gt;</code><br /><code>&lt;html:option value="a"/&gt;choice1&lt;/html:option&gt;</code><br /><code>&lt;/html:select&gt;</code><br /></li>
												<li>提交按钮 - <code>&lt;html:submit/&gt;</code></li>
												<li>文本输入域 - <code>&lt;html:text property="name"/&gt;</code></li>
												<li>文本区输入域 - <code>&lt;html:textarea property="name"/&gt;</code></li>
										</ul>
										<p>大多数 HTML 标记都支持 Javascript 事件，如 onmouseclick、onmouseover 等事件。关于更多信息，请参阅 <a href="http://jakarta.apache.org/struts/userGuide/struts-html.html"><font color="#5c81a7">HTML Bean API</font></a>。 </p>
										<p>下面我们来为这个示例创建一张 JSP 页面。在 Web Perspective 中，用 Struts 模型创建一张 JSP 页面：</p>
										<ol>
												<li>展开 <b>StrutsSampleWeb</b>项目直到看到 <b>/Web Content</b>文件夹。右键单击 <b>/Web Content</b>。 
</li>
												<li>选择 <b>New =&gt;JSP File</b>。 
</li>
												<li>在 <b>Name</b> 中输入 <code>submitpage.jsp</code> 。 
</li>
												<li>从下拉框中选择 <b>Struts JSP</b>作为 <b>Model</b>。 
</li>
												<li>单击 <b>Next</b>，注意只添加了 HTML 和 Bean 的 taglib。如果您想使用其他标记库中的 taglib，如 Logic taglib，请选择 <b>Add Tag Libraries</b>，然后选择 <b>/WEB-INF/struts-logic.tld</b>。 
</li>
												<li>单击 <b>Finish</b>。 </li>
										</ol>
										<p>图 4.JSP 创建向导 － Struts 标记库 <br /><img height="609" alt="JSP 创建向导 － Struts 标记库" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image4.jpg" width="525" /></p>
										<p>用源代码编辑器中的下面这些代码修改 <code>submitpage.jsp</code> 页并保存： </p>
										<pre>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;
&lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %&gt;

&lt;html:html locale="true"&gt;

&lt;HEAD&gt;
&lt;%@ page
language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"
%&gt;
&lt;META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"&gt;
&lt;META name="GENERATOR" content="IBM WebSphere Studio"&gt;
&lt;META http-equiv="Content-Style-Type" content="text/css"&gt;
&lt;LINK href="theme/Master.css" rel="stylesheet"
type="text/css"&gt;


&lt;TITLE&gt;Pizza Order Page&lt;/TITLE&gt;
&lt;/HEAD&gt;

&lt;BODY&gt;
&lt;P&gt;&lt;h1&gt;Pizza Order Page &lt;/h1&gt;&lt;/P&gt;

&lt;html:form action="/submit.do"&gt;

Name: &lt;html:text property="customer.name"/&gt;&lt;br&gt;
Address: &lt;html:text property="customer.address"/&gt;&lt;br&gt;

Size: &lt;html:radio property ="size" value="S"/&gt;Small
&lt;html:radio property ="size" value="M"/&gt;Medium
&lt;html:radio property ="size" value="L"/&gt;Large

Toppings: &lt;br&gt;
Pepperoni&lt;html:checkbox property="topping(Pepperoni)"/&gt;&lt;br&gt;
Onion&lt;html:checkbox property="topping(Onion)"/&gt;&lt;br&gt;
Mushroom&lt;html:checkbox property="topping(Mushroom)"/&gt;&lt;br&gt;
Hot Pepper&lt;html:checkbox property="topping(Hot Pepper)"/&gt;&lt;br&gt;
Bacon&lt;html:checkbox property="topping(Bacon)"/&gt;&lt;br&gt;

&lt;html:select property ="type"&gt;
&lt;html:option value="a"&gt;Delivery&lt;/html:option&gt;
&lt;html:option value="b"&gt;Pickup&lt;/html:option&gt;
&lt;/html:select&gt;

&lt;html:submit/&gt;
&lt;html:reset/&gt;
&lt;/html:form&gt;
&lt;/BODY&gt;
&lt;/html:html&gt;
</pre>
										<p>忽略任务列表中关于 submit.do 不存在的警告消息。为 Struts <i>Action</i> 类创建一张 <code>/confirm.jsp</code> 页面用来进行转发。 </p>
										<p>在 Web Perspective 中，创建 confirm.jsp 页面：</p>
										<ol>
												<li>右键单击 <b>/Web Content</b>。 
</li>
												<li>选择 <b>New =&gt;JSP File</b>。 
</li>
												<li>在 <b>Name</b> 域中输入 <code>confirm.jsp</code> 。 
</li>
												<li>单击 <b>Finish</b>。 </li>
										</ol>
										<p>用下面的代码修改 JSP 文件：</p>
										<pre>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;
&lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %&gt;
&lt;html:html&gt;
&lt;HEAD&gt;
&lt;%@ page
language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"
%&gt;
&lt;META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"&gt;
&lt;META name="GENERATOR" content="IBM WebSphere Studio"&gt;
&lt;META http-equiv="Content-Style-Type" content="text/css"&gt;
&lt;LINK href="theme/Master.css" rel="stylesheet"
type="text/css"&gt;
&lt;TITLE&gt;&lt;/TITLE&gt;
&lt;/HEAD&gt;

&lt;BODY&gt;
&lt;P&gt;Thank you &lt;%=request.getAttribute("name")%&gt;&lt;/P&gt;
&lt;/BODY&gt;
&lt;/html:html&gt;
</pre>
										<p>在 submitpage.jsp 中， <code>customer.name</code> 属性引用对象内的一个域。 <code>topping(Pepperoni)</code> 属性是 <code>java.util.HashMap</code> 的一个键／值对。Struts 1.1（beta 2）HTML taglib 支持嵌套的属性。 </p>
										<a name="sec2-3">
												<p>
														<a name="N10364">
																<span class="smalltitle">
																		<strong>
																				<font face="Arial">第 3 步：创建一个 Struts ActionForm SubmitForm 类</font>
																		</strong>
																</span>
														</a>
												</p>
												<p>您必须定义一个继承 Struts <i>ActionForm</i>类的子类。它必须有用于所有表单域的 setter 和 getter。前面已经提到过，提交表单后， <i>ActionForm</i>类被预先植入表单数据。 <i>ActionForm</i>类有验证方法和重设方法（可选）分别用来验证表单输入和重新设置表单。稍后将在 <a href="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/fung.html#sec2-6"><font color="#5c81a7">本文</font></a>中讨论它们。 </p>
												<p>如果您的应用程序有一个跨多个 Web 页面的表单，那么就可以只用一个 <i>ActionForm</i>。定义一个包含所有域的属性的 <i>ActionForm</i>bean，不管域实际显示在哪一张页面上。同样，可以把同一个表单的各个页面提交给同一个 <i>Action</i>类。使用这种设计，Web 站点设计者不必改动业务逻辑就可以重新安排域。 </p>
												<p>创建一个 Java 类，它继承 Web 项目下 /Java Source 文件夹中的 <i>org.apache.struts.action.ActionForm</i>。 </p>
												<ol>
														<li>在 Web Perspective 中，右键单击 <b>StrutsSampleWeb</b>项目下的 <b>/Java Source</b>文件夹。 
</li>
														<li>选择 <b>New =&gt;Other =&gt;Web</b>。展开 <b>Web =&gt;Struts</b>。选择 <b>ActionForm Class</b>并单击 <b>Next</b>。 
<p>图 5.启动 Struts ActionForm 类向导 <br /><img height="500" alt="启动 Struts ActionForm 类向导" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image5.jpg" width="525" /></p></li>
														<li>输入 <code>com.ibm.sample.struts</code> 作为 <b>Java Package</b>的名称。 
</li>
														<li>输入 <code>SubmitForm</code> 作为类的 <b>Name</b>。单击 <b>Next</b>。 
</li>
														<li>这张页面让您选择想为 setter 和 getter 选择的域。像图 6 那样展开目录树。选择这些项： <code>customer.name、size、topping(Pepperoni) 和 type</code> 。 
<p>图 6. 为 HTML 表单选择新的访问程序（setter 和 getter） <br /><img height="555" alt="选择新的访问程序" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image6.jpg" width="525" /></p></li>
														<li>单击 <b>Next</b>。 
</li>
														<li>将 <code>customer.name</code> 改为 <b>customer</b>（不用引号）并把它的类型改为 <b>Customer</b>。 
</li>
														<li>把 <code>topping(Pepperoni)</code> 改为 <b>toppings</b>（注意复数形式），并把其类型改为 <b>java.util.HashMap</b>。 
</li>
														<li>把 <code>String[]</code> 改为 <b>String</b>类型。 
</li>
														<li>添加 <code>name</code> ，类型为 <b>String</b>。 
</li>
														<li>添加 <code>address</code> ，类型为 <b>String</b>。 
<p>图 7. 修改访问程序 <br /><img height="373" alt="修改访问程序" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image7.jpg" width="525" /></p></li>
														<li>单击 <b>Next</b>。 <i>ActionServlet</i>用 struts-config.xml 文件来决定把控制权转交给哪个 <i>Action</i>类及与之相关联的 <i>ActionForm</i>类。这张页面自动在 Struts 配置 xml 文件中定义 <i>submitForm ActionForm</i>bean。 
<p>图 8. 在 struts-config.xml 中为 ActionForm 配置映射。 <br /><img height="273" alt="配置映射" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image8.jpg" width="525" /></p></li>
														<li>单击 <b>Finish</b>。 </li>
												</ol>
												<p>
														<i>ActionForm</i>包含表单中所有的域，并且每个域必须有一个 getter 和一个 setter。您需要手工更改 <i>Customer</i>对象以及 <i>toppings</i>HashMap 的 getter 和 setter 的实现。请照下面这样修改您刚创建的 SubmitForm 中的黑体代码： </p>
												<pre>package com.ibm.sample.struts;

import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;

public class SubmitForm extends ActionForm {

        
        <b>private Customer customer = new Customer();</b>
private String size = null;

        
        <b>private java.util.HashMap toppings = new java.util.HashMap();</b>
private String type = null;
private String name = null;
private String address = null;

public String getSize() {
return size;
}
public HashMap getToppings() {
return toppings;
}
public void setSize(String size) {
this.size = size;
}
public void setToppings(HashMap toppings) {
this.toppings = toppings;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}

        
        <b>public String getAddress() {
return customer.getAddress();
}
public String getName() {
return customer.getName();
}
public void setAddress(String address) {
customer.setAddress(address);
}
public void setName(String name) {
customer.setName(name);
}
public void setTopping(String key, Object value) {  //*new* method
toppings.put(key, value);
}
public Object getTopping(String key) {  //*new* method
return toppings.get(key);
}</b>
public void reset(ActionMapping mapping, HttpServletRequest request) {

        
        <b>this.customer = new Customer();
name = new String();
address = new String();
size = new String();
toppings.clear();
type = new String();</b>
}
}

      
      </pre>
												<p>因为 SubmitForm 要用 Customer bean，请在 <code>com.ibm.sample.struts</code> 包下创建一个名为 <i>Customer</i>的 java 类： </p>
												<ol>
														<li>在 Web Perspective 中，右键单击 Web 项目下的 <b>/Java Source</b>文件夹。 
</li>
														<li>选择 <b>New =&gt;Class</b>。 
</li>
														<li>输入或浏览选择 <code>com.ibm.sample.struts</code> 作为 <b>Package</b>的名称。 
</li>
														<li>输入 <code>Customer</code> 作为类的 <b>Name</b>。 
</li>
														<li>单击 <b>Finish</b>。 
</li>
														<li>为该类输入下面的代码： </li>
												</ol>
												<pre>package com.ibm.sample.struts;

public class Customer {

private String name;
private String address;

public String getAddress() {
return address;
}
public String getName() {
return name;
}
public void setAddress(String address) {
this.address = address;
}
public void setName(String name) {
this.name = name;
}
}

</pre>
												<p>要把表单中的 input 文本域映射为 customer 对象的 name 域，请使用 HTML 标记中的 <code>&lt;html:text property="customer.name"/&gt;</code> 。要访问 HashMap 的键／值对或任何 Collections，请使用 HTML 标记中的 <code>property="toppings(key)"</code> ，其中 toppings 是示例中 HashMap 的名称。 <b>注意</b>：只有 Struts 1.1（beta 2）支持使用 HashMap 或其他 Collections 属性。 </p>
												<p>按下 reset 按钮后，就调用了 reset() 方法。</p>
												<a name="sec2-4">
														<p>
																<a name="N104AF">
																		<span class="smalltitle">
																				<strong>
																						<font face="Arial">第 4 步：创建一个 Action SubmitAction 类</font>
																				</strong>
																		</span>
																</a>
														</p>
														<p>Struts <i>Action</i>类是应用程序逻辑。它进行 JDBC 调用、调用其他的业务 bean 并调用 EJB 等等。我们建议把业务逻辑与其他的 bean 分开，而不要把它嵌入这个 <i>Action</i>类。这个类调用有业务逻辑的 bean。您应该实现 execute() 方法。它返回一个标识下一张页面（比如一张 JSP 页面）的 <i>ActionForward</i>对象。 </p>
														<pre>public ActionForward execute (ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response)
</pre>
														<p>execute() 方法能够访问您可以使用的表单数据。因为所有的请求都被路由到 <i>Action</i>类的一个实例，所以请确保您的代码可以在多线程环境中正确执行。当返回一个标识 JSP 页面的 <i>ActionForward</i>时，在 struts-config.xml 编辑器中为该 JSP 页面定义一个逻辑名。例如，为 <i>/confirm.jsp</i>定义逻辑名 <i>success</i>。在 execute() 方法中， <i>return (mapping.findForward("success"))；</i>这行代码把控制权转交给 <i>/confirm.jsp</i>页面。 </p>
														<p>在 /Java Source 文件夹下创建一个名为 <i>SubmitAction</i>的 <i>Action</i>类： </p>
														<ol>
																<li>在 Web Perspective 中，右键单击 Web 项目下的 <b>/Java Source</b>文件夹。 
</li>
																<li>选择 <b>New =&gt;Other =&gt;Web =&gt;Struts =&gt;Action Class</b>。 
</li>
																<li>输入 <code>com.ibm.sample.struts</code> 作为 Package 的名称。 
</li>
																<li>输入 <code>SubmitAction</code> 作为类的 Name。按下 <b>Next</b>。 
</li>
																<li>在 Forwards 部分，按下 <b>Add</b>来添加转交（forwards），这次转交的 <b>Name</b>为 <i>success</i>，Path 为 <i>/confirm.jsp</i>。 
</li>
																<li>选择 <b>submitForm</b>作为 Form Bean Name 域的值。 
</li>
																<li>选择 <b>session</b>作为 Form Bean Scope 域的值。 
</li>
																<li>单击 <b>Finish</b>。 </li>
														</ol>
														<p>图 9. 创建 Action 类向导。 <br /><img height="716" alt="创建 Action 类向导" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image9.jpg" width="525" /></p>
														<p>在 SubmitAction 类中输入下列代码：</p>
														<pre>package com.ibm.sample.struts;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

/**
* @version 	1.0
* @author
*/
public class SubmitAction extends Action {

/**
* Constructor
*/
public SubmitAction() {

super();

}
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {

ActionErrors errors = new ActionErrors();
ActionForward forward = new ActionForward();
// return value
SubmitForm submitForm = (SubmitForm) form;

try {

// Getting the value from the form
&lt;B&gt;String name = submitForm.getCustomer().getName();
System.out.println ("The name is: " + name);
request.setAttribute("name",name);&lt;/B&gt;

} catch (Exception e) {

// Report the error using the appropriate name and ID.
errors.add("name", new ActionError("id"));

}

// If a message is required, save the specified key(s)
// into the request for use by the &lt;struts:errors&gt; tag.

if (!errors.empty()) {
saveErrors(request, errors);
}
// Write logic determining how the user should be forwarded.
forward = mapping.findForward("success");

// Finish with
return (forward);

}
}
</pre>
														<a name="sec2-5">
																<p>
																		<a name="N10537">
																				<span class="smalltitle">
																						<strong>
																								<font face="Arial">第 5 步：运行样本</font>
																						</strong>
																				</span>
																		</a>
																</p>
																<p>至此，您已经完成了这个示例，现在可以运行它了。要在 WebSphere Test Environment V5 中运行 submitpage.jsp：</p>
																<ol>
																		<li>右键单击 <b>submitpage.jsp =&gt;Run on Server</b>。 
</li>
																		<li>确保选中了 <b>WebSphere Test Environment v5 Server</b>。单击 <b>OK</b>。 </li>
																</ol>
																<a name="sec2-6">
																		<p>
																				<a name="N10555">
																						<span class="smalltitle">
																								<strong>
																										<font face="Arial">第 6 步：验证</font>
																								</strong>
																						</span>
																				</a>
																		</p>
																		<p>您可以在 Struts <i>ActionForm</i>中进行验证。一个表单被提交后， <i>SubmitForm</i>中的 validate() 方法被实现。请把下面的 validate() 方法添加到 <i>SubmitForm</i>中： </p>
																		<pre>public ActionErrors validate(
ActionMapping mapping,
HttpServletRequest request) {

ActionErrors errors = new ActionErrors();

if ((customer.getName()==null)|| (customer.getName().equals(""))) {
// if the name is empty --&gt; errors
errors.add("Name", new ActionError ("error.customer.name"));
System.out.println("errors....");
}
return errors;
}
</pre>
																		<p>保存了 SubmitForm 类后，请注意 ActionError 编译错误并没有解决。右键单击 <b>ActionError =&gt; Source =&gt; Organize imports。</b></p>
																		<p>error.customer.name 属性是从资源绑定中获得的。在一个编辑器中从 /Java Source 文件夹打开 <b>ApplicationResources.properties</b>。把下面的代码行添加到这个属性文件中： </p>
																		<pre># Optional header and footer for &lt;errors/&gt; tag.
title=Pizza Store
errors.header=&lt;hr&gt;&lt;h3&gt;Errors&lt;/h3&gt;&lt;ul&gt;
errors.footer=&lt;/ul&gt;&lt;hr&gt;
error.customer.name=&lt;li&gt;Name is empty
</pre>
																		<p>当错误在 JSP 页面中显示出来时，是用 <code>&lt;hr&gt;&lt;h3&gt;Errors&lt;/h3&gt;&lt;ul&gt;</code> 和 <code>&lt;/ul&gt;</code> 括起来的。您必须用 errors.header 和 errors.footer 为错误消息定义头（header）和脚（footer）。错误被检测出后，它们被返回表单，同时输入值被保存。 </p>
																		<p>您需要在 JSP 文件中添加代码行 <code>&lt;html:errors/&gt;</code> ，这样错误才能显示出来。在 submitpage.jsp 文件的开头处（就在&lt;html:form&gt; 之前），输入 <code>&lt;html:errors/&gt;</code> 。 </p>
																		<p>在 struts-config.xml 编辑器中修改 /input 域：</p>
																		<ol>
																				<li>在编辑器中打开 struts-config.xml 文件。这个文件在 /Web Content 文件夹中。 
</li>
																				<li>
																						<i>Action</i>和 <i>ActionForm</i> bean 由 Struts 创建向导来配置，但 Actions 页面中的 input 域除外。为使验证能正常进行，这个域是必需的。 
</li>
																				<li>在 struts-config.xml 编辑器的 Actions 页面下，在 Input 域中选择 <b>/submit</b> 并输入 <code>/submitpage.jsp</code> 。保存所做的修改。 </li>
																		</ol>
																		<p>您可以把错误消息和其他消息放在 ApplicationResources.properties 文件中。例如，您可以用 <i>&lt;bean:message key="title"/&gt;</i>在 JSP 中显示标题，内容可以从资源绑定中获得。 </p>
																		<p>资源绑定还支持国际化（i18n －在 <i>i</i>和 <i>n</i>之间有 18 个字符）。您可以为 ISO 语言代码为 xx 的其他语言创建 ApplicationResources_xx.properties。 </p>
																		<p>
																				<b>再次运行样本并进行验证</b>
																		</p>
																		<p>既然我们已经修改了 Web 项目，那么就来重新启动一下服务器并再次尝试运行该样本。要重新启动服务器，请转到服务器视图，右键单击并选择 <b>Restart</b>。 </p>
																		<p>如果提交后名称为空，将返回到表单页面，属性文件中列出错误消息。 </p>
																		<p>图 10. 运行样本并进行验证 <br /><img height="638" alt="运行样本并进行验证" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image10.jpg" width="431" /></p>
																		<a name="sec3">
																				<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/websphere/techjournal/0302_fung/fung.html#main">
																																						<b>
																																								<font color="#5c81a7">回页首</font>
																																						</b>
																																				</a>
																																		</td>
																																</tr>
																														</tbody>
																												</table>
																										</td>
																								</tr>
																						</tbody>
																				</table>
																				<br />
																				<br />
																				<p>
																						<a name="N105D7">
																								<span class="atitle">
																										<font face="Arial" size="4">Struts 应用程序的图形表示</font>
																								</span>
																						</a>
																				</p>
																				<p>WebSphere Studio 允许您创建 web 图表来用图形方式表示 Struts 应用程序。</p>
																				<ol>
																						<li>在 Web Perspective 中，右键单击 Web 项目下的 <b>/Java Source</b>文件夹。 
</li>
																						<li>选择 <b>File =&gt; New =&gt; Other =&gt; Web =&gt; Struts =&gt; Web Diagram</b>。 
</li>
																						<li>输入 <code>submitDiagram</code> 作为 <b>File name</b>。 
</li>
																						<li>按下 <b>Finish</b>。这样就在 /Java Source 文件夹下创建了一个名为 <i>submitDiagram.gpg</i> 的文件。 
</li>
																						<li>转到 <b>Web Structure</b>视图。这个视图和 Outline 视图位于同一堆栈上。 
</li>
																						<li>展开 <b>&lt;default module&gt; =&gt; Actions =&gt; /submit</b>。 
</li>
																						<li>把带 /submit 的图标拖到图表上。 
</li>
																						<li>右键单击图表上的图标 =&gt; <b>Draw</b>=&gt; <b>Draw All From</b>。 
</li>
																						<li>双击图标进入编辑器。例如，双击 /submit 图标可以进入 struts-config.xml 编辑器。 </li>
																				</ol>
																				<p>图 11. Struts Web 图表 <br /><img height="390" alt="Struts Web 图表" src="http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0302_fung/images/image11.jpg" width="506" /></p>
																		</a>
																</a>
														</a>
												</a>
										</a>
								</a>
						</a>
				</a>
		</a><img src ="http://www.cnitblog.com/tilan/aggbug/20905.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-21 13:13 <a href="http://www.cnitblog.com/tilan/articles/20905.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>建筑和软件中模式之异同</title><link>http://www.cnitblog.com/tilan/articles/20618.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Sat, 16 Dec 2006 07:24:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/20618.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/20618.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/20618.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/20618.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/20618.html</trackback:ping><description><![CDATA[<p align="center">
				<a href="http://www.jdon.com/aboutme.htm">板桥里人</a> http://www.jdon.com 2002年6月26日</p>
		<p>CSDN的透明特别推崇《<a href="http://www.china-pub.com/computers/common/info.asp?id=6374" target="_blank">建筑的永恒之道</a>》,认为从中探寻到软件的永恒之道,并就"设计模式"写了专门文章《<a href="http://www.csdn.net/develop/article/14/14238.shtm" target="_blank">探寻软件的永恒之道</a> 》,其中很多观点我看了很受启发,以前我也将"设计模式" 看成一个简单的解决方案,没有从一种高度来看待"设计模式"在软件中地位,下面是我自己的一些想法:</p>
		<p>建筑和软件某些地方是可以来比喻的</p>
		<p>特别是中国传统建筑,那是很讲模式的,这些都是传统文化使然,比如京剧 一招一式都有套路;中国画,也有套路,树应该怎么画法?有几种画法?艺术大家通常是创造出自己的套路,比如明末清初,水墨画法开始成熟,这时画树就不用勾勒这个模式了,而是一笔下去,浓淡几个叶子,待毛笔的水墨要干枯时,画一下树干,这样,一个活生写意的树就画出来.</p>
		<p>我上面这些描述其实都是一种模式,创建模式的人是大师,但是拘泥于模式的人永远是工匠.</p>
		<p>再回到传统建筑中,中国的传统建筑是过分注重模式了,所以建筑风格发展不大,基本分南北两派,大家有个感觉,旅游时,到南方,你发现古代名居建筑都差不多;北方由于受满人等少数民族的影响,在建筑色彩上有些与南方迥异,但是很多细节地方都差不多.这些都是模式的体现.</p>
		<p>由于建筑受材料和功用以及费用的影响,所用模式种类不多,这点是和软件很大的不同.</p>
		<p>正因为这点不同,导致建筑的管理模式和软件的管理模式就有很多不同, 有些人认识不到这点,就产生了可以大量使用"软件蓝领"的想法,因为他羡慕建筑中"民工"的低成本.</p>
		<p>要知道软件还有一个与建筑截然相反的责任和用途,那就是:现代社会中,计划感不上变化,竞争激烈,所有一切变幻莫测,要应付所有这些变化,首推信息技术中的软件,只有软件能够帮助人类去应付各种变化.而这点正好与建筑想反,建筑是不能帮助人类去应付变化的,(它自己反而要求稳固,老老实实帮助人遮风避雨,总不能叫人类在露天或树叶下打开电脑编软件吧).</p>
		<p>软件要帮助人类去应付变化,这是软件的首要责任,所以,软件中模式产生的目的就和建筑不一样了,建筑中的模式产生可以因为很多原因:建筑大师的创意;材料的革新等;建筑中这些模式一旦产生,容易发生另外一个缺点,就是有时会阻碍建筑本身的发展,因为很多人会不思创造,反复使用老的模式进行设计,阻碍建筑的发展.</p>
		<p>但是在软件中,这点正好相反,软件模式的产生是因为变化的东西太多,为减轻人类的负担,将一些不变的东西先用模式固化,这样让人类可以更加集中精力对付变化的东西,所以在软件中大量反复使用模式(我个人认为这样的软件就叫框架软件了,比如J2EE),不但没阻碍软件的发展,反而是推动了软件的发展.因为其他使用这套软件的人就可以将更多精力集中在对付那些无法用模式的应用上来.</p>
		<p>可以关于建筑和软件中的模式作用可以总结如下:</p>
		<p>在软件中,模式是帮助人类向"变化"战斗,但是在软件中还需要和'变化'直接面对面战斗的武器:人的思维,特别是创造 分析思维等等，这些是软件真正的灵魂，这种思维可以说只要有实践需求(如有新项目)就要求发生，发生频度高，人类的创造或分析思维决定了软件的质量和特点。</p>
		<p>而在建筑中,模式可以构成建筑全部知识，当有新的需求(如有新项目)，一般使用旧的模式都可以完成，因此对人类的创造以及分析思维不是每个项目都必须的，也不是非常重要的，对创造性的思维的需求只是属于锦上添花（除非人类以后离开地球居住了〕。<br /></p>
		<p>
		</p><img src ="http://www.cnitblog.com/tilan/aggbug/20618.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-16 15:24 <a href="http://www.cnitblog.com/tilan/articles/20618.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>你还在用if else吗？</title><link>http://www.cnitblog.com/tilan/articles/20619.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Sat, 16 Dec 2006 07:24:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/20619.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/20619.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/20619.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/20619.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/20619.html</trackback:ping><description><![CDATA[<p align="center">
				<a href="http://www.jdon.com/aboutme.htm">板桥里人</a> http://www.jdon.com 2006/1/11（转载请保留） </p>
		<p>　　面向过程设计和面向对象设计的主要区别是：是否在业务逻辑层使用冗长的if else判断。如果你还在大量使用if else，当然，界面表现层除外，即使你使用Java/C#这样完全面向对象的语言，也只能说明你的思维停留在传统的面向过程语言上。</p>
		<p>
				<strong>传统思维习惯分析</strong>
		</p>
		<p>　　为什么会业务逻辑层使用if else，其实使用者的目的也是为了重用，但是这是面向过程编程的重用，程序员只看到代码重用，因为他看到if else几种情况下大部分代码都是重复的，只有个别不同，因此使用if else可以避免重复代码，并且认为这是模板Template模式。</p>
		<p>　　他范的错误是：程序员只从代码运行顺序这个方向来看待它的代码，这种思维类似水管或串行电路，水沿着水管流动（代码运行次序），当遇到几个分管（子管），就分到这几个分管子在流动，这里就相当于碰到代码的if else处了。</p>
		<p>　　而使用OO，则首先打破这个代码由上向下顺序等同于运行时的先后循序这个规律，代码结构不由执行循序决定，由什么决定呢？由OO设计；设计模式会取代这些if else，但是最后总是由一个Service等总类按照运行顺序组装这些OO模块，只有一处，这处可包含事务，一般就是Service，EJB中是Session bean。</p>
		<p>　　一旦需求变化，我们更多的可能是Service中各个OO模块，甚至是只改动Service中的OO模块执行顺序就能符合需求。</p>
		<p>　　这里我们也看到OO分离的思路，将以前过程语言的一个Main函数彻底分解，将运行顺序与代码其他逻辑分离开来，而不是象面向过程那样混乱在一起。所以有人感慨，OO也是要顺序的，这是肯定的，关键是运行顺序要单独分离出来。</p>
		<p>　　是否有if else可以看出你有没有将运行顺序分离到家。</p>
		<p>
				<strong>设计模式的切入口</strong>
		</p>
		<p>　　经常有人反映，设计模式是不错，但是我很难用到，其实如果你使用if else来写代码时（除显示控制以外），就是在写业务逻辑，只不过使用简单的判断语句来作为现实情况的替代者。</p>
		<p>　　 还是以大家熟悉的论坛帖子为例子，如ForumMessage是一个模型，但是实际中帖子分两种性质：主题贴（第一个根贴）和回帖（回以前帖子的帖子），这里有一个朴素的解决方案：<br />建立一个ForumMessage，然后在ForumMessage加入isTopic这样判断语句，注意，你这里一个简单属性的判断引入，可能导致你的程序其他地方到处存在if else 的判断。</p>
		<p>　　如果我们改用另外一种分析实现思路，以对象化概念看待，实际中有主题贴和回帖，就是两种对象，但是这两种对象大部分是一致的，因此，我将ForumMessage设为表达主题贴；然后创建一个继承ForumMessage的子类ForumMessageReply作为回帖，这样，我在程序地方，如Service中，我已经确定这个Model是回帖了，我就直接下溯为ForumMessageReply即可，这个有点类似向Collection放入对象和取出时的强制类型转换。通过这个手段我消灭了以后程序中if else的判断语句出现可能。</p>
		<p>　　从这里体现了，如果分析方向错误，也会导致误用模式。</p>
		<p>　　讨论设计模式举例，不能没有业务上下文场景的案例，否则无法决定是否该用模式，下面举两个对比的例子：</p>
		<p>　　第一.<a href="http://www.jdon.com/jive/thread.jsp?forum=91&amp;thread=24681" target="_blank"> 这个帖子</a>中举例的第一个代码案例是没有上下文的，文中只说明有一段代码：</p>
		<table cellspacing="1" cellpadding="1" width="100%" bgcolor="#cccccc" border="0">
				<tbody>
						<tr>
								<td>main() { 
<p>if（case A）{ </p><p>//do with strategy A </p><p>}else(case B){</p><p>//do with strategy B </p><p>}else(case C){ </p><p>//do with strategy C </p><p>}</p><p>} </p></td>
								<td> </td>
						</tr>
				</tbody>
		</table>
		<p>　　这段代码只是纯粹的代码，没有业务功能，所以，在这种情况下，我们就很难确定使用什么模式，就是一定用策略模式等，也逃不过还是使用if else的命运，设计模式不是魔法，不能将一段毫无意义的代码变得简单了，只能将其体现的业务功能更加容易可拓展了。</p>
		<p>　　第二.在<a href="http://www.jdon.com/jive/thread.jsp?forum=91&amp;thread=24333&amp;message=14484871#14484871" target="_blank">这个帖子</a>中，作者举了一个PacketParser业务案例，这段代码是体现业务功能的，是一个数据包的分析，作者也比较了各种模式使用的不同，所以我们还是使用动态代理模式或Command模式来消灭那些可能存在的if else</p>
		<p>　　由以上两个案例表明：业务逻辑是我们使用设计模式的切入点，而在分解业务逻辑时，我们习惯则可能使用if else来实现，当你有这种企图或者已经实现代码了，那么就应该考虑是否需要重构Refactoring了。<br /></p>
		<p>
				<strong>if else替代者</strong>
		</p>
		<p>　　那么实战中，哪些设计模式可以替代if else呢？其实GoF设计模式都可以用来替代if else，我们分别描述如下：</p>
		<li>状态模式　<br />　　当数据对象存在各种可能性的状态，而且这种状态将会影响到不同业务结果时，那么我们就应该考虑是否使用状态模式，当然，使用状态模式之前，你必须首先有内存状态这个概念，而不是数据库概念，因为在传统的面向过程的/面向数据库的系统中，你很难发现状态的，从数据库中读取某个值，然后根据这个值进行代码运行分流，这是很多初学者常干的事情。参考文章:<a href="http://www.jdon.com/artichect/state.htm" target="_blank">状态对象：数据库的替代者</a><br />　　使用传统语言思维的情况还有：使用一个类整数变量标识状态：<br /><table cellspacing="1" cellpadding="1" width="100%" bgcolor="#cccccc" border="0"><tbody><tr><td><p> </p><p>public class Order{</p><p>private int status;</p><p>//说明： </p><p>//status=1 表示订货但为查看 ；</p><p>//status=2 表示已经查看未处理；</p><p>//status=3 表示已经处理未付款</p><p>//status=4 表示已经付款未发货</p><p>//status=5 表示已经发货</p><p>} </p></td></tr></tbody></table><br />　　上述类设计，无疑是将类作为传统语言的函数来使用，这样导致程序代码中存在大量的if else。<br /><br /></li>
		<li>策略模式　<br />　　当你面临几种算法或者公式选择时，可以考虑策略模式，传统过程语言情况是：从数据库中读取算法数值，数值1表示策略1，例如保存到数据库；数值为2表示策略2，例如保存到XMl文件中。这里使用if else作为策略选择的开关。 <br /><br /></li>
		<li>command模式　<br />　　传统过程的思维情况是：如果客户端发出代号是1或"A"，那么我调用A.java这个对象来处理；如果代号是2或"B"，我就调用B.java来处理，通过if else来判断客户端发送过来的代码，然后按事先约定的对应表，调用相应的类来处理。<br /><br /></li>
		<li>MVC模式　<br />　　MVC模式的传统语言误用和Command模式类似，在一个Action类中，使用if else进行前后台调度，如果客户端传送什么命令；我就调用后台什么结果；如果后台处理什么结构，再决定推什么页面，不过，现在我们使用Struts/JSF这样MVC模式的框架实现者就不必范这种低级错误。<br /><br /></li>
		<li>职责链模式　<br />　　职责链模式和Command模式是可选的，如果你实在不知道客户端会发出什么代号；也没有一个事先定义好的对照表，那么你只能编写一个个类去碰运气一样打开这个包看一下就可以。与Command是不同在<a href="http://www.jdon.com/AOPdesign/decorator.htm" target="_blank">AOP vs Decorator</a>一文中有分析。<br /><br /></li>
		<li>代理或动态代理模式　<br />　　代理对象可以是符合某种条件的代表者，比如，权限检验，传统面向过程思维是：当一个用户登陆后，访问某资源时，使用if else进行判断，只有某种条件符合时，才能允许访问，这样权限判断和业务数据逻辑混乱在一起，使用代理模式可以清晰分离，如果嫌不太好，使用动态代理，或者下面AOP等方式。<br /><br /></li>
		<li>AOP或Decorator模式<br />　　<br />　　其实使用filter过滤器也可以替代我们业务中的if else，过滤器起到一种过滤和筛选作用，将符合本过滤器条件的对象拦截下来做某件事情，这就是一个过滤器的功能，多个过滤器组合在一起实际就是if else的组合。<br />　　所以，如果你实在想不出什么办法，可以使用过滤器，将过滤器看成防火墙就比较好理解，当客户端有一个请求时，经过不同性质的防火墙，这个防火墙是拦截端口的；那个防火墙是安全检查拦截等等。过滤器也如同红蓝白各种光滤镜；红色滤镜只能将通过光线中的红色拦截了；蓝色滤镜将光线中的蓝色拦截下来，这实际上是对光线使用if else进行分解。<br /><br /><img height="182" src="http://www.jdon.com/artichect/images/ifelse.gif" width="285" /><br />　　如图，通过一个个条件过滤器我们立体地实现了对信号的分离，如果你使用if else，说明你是将图中的条件1/2/3/4合并在一起，在同一个地方实现条件判断。<br />　　需要深入了解过滤器的实现细节和微小区别，请参考文章：<a href="http://www.jdon.com/AOPdesign/decorator.htm" target="_blank">AOP vs Decorator</a><br /><p><strong>OO设计的总结</strong>　　 </p><p>　　还有一种伪模式，虽然使用了状态等模式，但是在模式内部实质还是使用if else或switch进行状态切换或重要条件判断，那么无疑说明还需要进一步努力。更重要的是，不能以模式自居，而且出书示人。</p><p>　　真正掌握面向对象这些思想是一件困难的事情，目前有各种属于揪着自己头发向上拔的解说，都是误人子弟的，所以我觉得初学者读Thinking in Java（Java编程思想）是没有用，它试图从语言层次来讲OO编程思想，非常失败，作为语言参考书可以，但是作为Java体现的OO思想的学习资料，就错了。</p><p>　　OO编程思想是一种方法论，方法论如果没有应用比较，是无法体会这个方法论的特点的，禅是古代一个方法论，悟禅是靠挑水砍柴这些应用才能体会。</p><p>　　那么OO思想靠什么应用能够体会到了？是GoF设计模式，GoF设计模式是等于软件人员的挑水砍柴等基本活，所以，如果一个程序员连基本活都不会，他何以自居OO程序员？从事OO专业设计编程这个工作，如果不掌握设计模式基本功，就象一个做和尚的人不愿意挑水砍柴，他何以立足这个行业？早就被师傅赶下山。</p><p>　　最后总结：将if else用在小地方还可以，如简单的数值判断；但是如果按照你的传统习惯思维，在实现业务功能时也使用if else，那么说明你的思维可能需要重塑，你的编程经验越丰富，传统过程思维模式就容易根深蒂固，想靠自己改变很困难；建议接受<a href="http://www.jdon.com/trainning/j2eearchitect.htm" target="_blank">专业头脑风暴培训</a>。</p><p>　　用一句话总结：如果你做了不少系统，很久没有使用if else了，那么说明你可能真正进入OO设计的境地了。（这是本人自己发明的实战性的衡量考核标准）。</p></li><img src ="http://www.cnitblog.com/tilan/aggbug/20619.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-16 15:24 <a href="http://www.cnitblog.com/tilan/articles/20619.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学习GoF设计模式的重要性</title><link>http://www.cnitblog.com/tilan/articles/20615.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Sat, 16 Dec 2006 07:21:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/20615.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/20615.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/20615.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/20615.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/20615.html</trackback:ping><description><![CDATA[<p align="center">
				<a href="http://www.jdon.com/aboutme.htm">板桥里人</a> http://www.jdon.com 2002/05/24</p>
		<p>著名的EJB领域顶尖的专家Richard Monson-Haefel在其个人网站:<a href="http://www.jdon.com/designpatterns/www.EJBNow.com" target="_blank">www.EJBNow.com</a>中极力推荐的GoF的《设计模式》,原文如下:</p>
		<p>
				<a href="http://www.amazon.com/exec/obidos/ASIN/0201633612/ref=nosim/aresoursiteforej" target="_blank">Design Patterns</a>
				<br />Most developers claim to experience an epiphany reading this book. If you've never read the Design Patterns book then you have suffered a very serious gap in your programming education that should be remedied immediately. </p>
		<p>翻译: 很多程序员在读完这本书,宣布自己相当于经历了一次"主显节"(纪念那稣降生和受洗的双重节日),如果你从来没有读过这本书,你会在你的程序教育生涯里存在一个严重裂沟,所以你应该立即挽救弥补!</p>
		<p>可以这么说：GoF设计模式是程序员真正掌握面向对象核心思想的必修课。虽然你可能已经通过了SUN的很多令人炫目的技术认证，但是如果你没有学习掌握GoF设计模式，只能说明你还是一个技工。</p>
		<p>在浏览《Thingking in Java》(第一版)时，你是不是觉得好象这还是一本Java基础语言书籍？但又不纯粹是，因为这本书的作者将面向对象的思想巧妙的融合在Java的具体技术上，潜移默化的让你感觉到了一种新的语言和新的思想方式的诞生。</p>
		<p>但是读完这本书，你对书中这些蕴含的思想也许需要一种更明晰更系统更透彻的了解和掌握，那么你就需要研读GoF的《设计模式》了。</p>
		<p>《Thingking in Java》(第一版中文)是这样描述设计模式的：他在由Gamma, Helm和Johnson Vlissides简称Gang of Four(四人帮),缩写GoF编著的《Design Patterns》一书中被定义成一个“里程碑”。事实上，那本书现在已成为几乎所有OOP(面向对象程序设计)程序员都必备的参考书。(在国外是如此)。<br /><br />GoF的《设计模式》是所有面向对象语言(C++ Java C#)的基础，只不过不同的语言将之实现得更方便地使用。</p>
		<p>
				<b>GOF的设计模式是一座"桥"</b>
				<br />就Java语言体系来说，GOF的设计模式是Java基础知识和J2EE框架知识之间一座隐性的"桥"。</p>
		<p>会Java的人越来越多，但是一直徘徊在语言层次的程序员不在少数，真正掌握Java中接口或抽象类的应用不是很多，大家经常以那些技术只适合大型项目为由，避开或忽略它们，实际中，Java的接口或抽象类是真正体现Java思想的核心所在，这些你都将在GoF的设计模式里领略到它们变幻无穷的魔力。</p>
		<p>GoF的设计模式表面上好象也是一种具体的"技术"，而且新的设计模式不断在出现，设计模式自有其自己的发展轨道，而这些好象和J2EE .Net等技术也无关！</p>
		<p>实际上，GoF的设计模式并不是一种具体"技术",它讲述的是思想，它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧，让你能够真正掌握接口或抽象类的应用，从而在原来的Java语言基础上跃进一步，更重要的是，GoF的设计模式反复向你强调一个宗旨：要让你的程序尽可能的可重用。</p>
		<p>这其实在向一个极限挑战：软件需求变幻无穷，计划没有变化快，但是我们还是要寻找出不变的东西，并将它和变化的东西分离开来，这需要非常的智慧和经验。</p>
		<p>而GoF的设计模式是在这方面开始探索的一块里程碑。</p>
		<p>J2EE等属于一种框架软件，什么是框架软件？它不同于我们以前接触的Java API等，那些属于Toolkist(工具箱)，它不再被动的被使用，被调用，而是深刻的介入到一个领域中去，J2EE等框架软件设计的目的是将一个领域中不变的东西先定义好，比如整体结构和一些主要职责(如数据库操作 事务跟踪 安全等)，剩余的就是变化的东西，针对这个领域中具体应用产生的具体不同的变化需求，而这些变化东西就是J2EE程序员所要做的。</p>
		<p>由此可见，设计模式和J2EE在思想和动机上是一脉相承，只不过</p>
		<p>1.设计模式更抽象，J2EE是具体的产品代码，我们可以接触到，而设计模式在对每个应用时才会产生具体代码。</p>
		<p>2.设计模式是比J2EE等框架软件更小的体系结构，J2EE中许多具体程序都是应用设计模式来完成的，当你深入到J2EE的内部代码研究时，这点尤其明显，因此，如果你不具备设计模式的基础知识(GoF的设计模式)，你很难快速的理解J2EE。不能理解J2EE,如何能灵活应用？</p>
		<p>3.J2EE只是适合企业计算应用的框架软件，但是GoF的设计模式几乎可以用于任何应用！因此GoF的设计模式应该是J2EE的重要理论基础之一。</p>
		<p>所以说，GoF的设计模式是Java基础知识和J2EE框架知识之间一座隐性的"桥"。为什么说隐性的？</p>
		<p>
				<b>GOF的设计模式是一座隐性的"桥"</b>
				<br />因为很多人没有注意到这点，学完Java基础语言就直接去学J2EE,有的甚至鸭子赶架，直接使用起Weblogic等具体J2EE软件，一段时间下来，发现不过如此，挺简单好用，但是你真正理解J2EE了吗？你在具体案例中的应用是否也是在延伸J2EE的思想？</p>
		<p>如果你不能很好的延伸J2EE的思想，那你岂非是大炮轰蚊子，认识到J2EE不是适合所有场合的人至少是明智的，但我们更需要将J2EE用对地方，那么只有理解J2EE此类框架软件的精髓，那么你才能真正灵活应用Java解决你的问题，甚至构架出你自己企业的框架来。(我们不能总是使用别人设定好的框架，为什么不能有我们自己的框架？)</p>
		<p>因此，首先你必须掌握GoF的设计模式。虽然它是隐性，但不是可以越过的。</p>
		<p> </p>
		<p>
				<strong>关于本站“设计模式”</strong>
		</p>
		<p>Java提供了丰富的API，同时又有强大的数据库系统作底层支持，那么我们的编程似乎变成了类似积木的简单"拼凑"和调用，甚至有人提倡"蓝领程序员",这些都是对现代编程技术的不了解所至. </p>
		<p>在真正可复用的面向对象编程中,GoF的《设计模式》为我们提供了一套可复用的面向对象技术,再配合Refactoring(重构方法),所以很少存在简单重复的工作,加上Java代码的精炼性和面向对象纯洁性(设计模式是java的灵魂),编程工作将变成一个让你时刻体验创造快感的激动人心的过程. 
</p>
		<p>为能和大家能共同探讨"设计模式",我将自己在学习中的心得写下来,只是想帮助更多人更容易理解GoF的《设计模式》。由于原著都是以C++为例, 以Java为例的设计模式基本又都以图形应用为例,而我们更关心Java在中间件等服务器方面的应用,因此,本站所有实例都是非图形应用,并且顺带剖析Jive论坛系统.同时为降低理解难度，尽量避免使用UML图. 
</p>
		<p>如果你有一定的面向对象编程经验,你会发现其中某些设计模式你已经无意识的使用过了;如果你是一个新手,那么从开始就培养自己良好的编程习惯(让你的的程序使用通用的模式,便于他人理解;让你自己减少重复性的编程工作),这无疑是成为一个优秀程序员的必备条件. 
</p>
		<p>整个设计模式贯穿一个原理:面对接口编程，而不是面对实现.目标原则是:降低耦合,增强灵活性</p><img src ="http://www.cnitblog.com/tilan/aggbug/20615.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-16 15:21 <a href="http://www.cnitblog.com/tilan/articles/20615.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Eclipse下使用Subversion （补）</title><link>http://www.cnitblog.com/tilan/articles/20102.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Tue, 05 Dec 2006 07:50:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/20102.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/20102.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/20102.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/20102.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/20102.html</trackback:ping><description><![CDATA[<div class="left">
				<span class="span">作者：朱先忠编译 转自天极<a href="http://dev.yesky.com/356/2578856.shtml">http://dev.yesky.com/356/2578856.shtml</a></span>
		</div>
		<div class="left">
				<span class="span">
				</span>
		</div>
		<div class="left">
				<span class="span">
				</span>
		</div>
		<div class="fontclear">
		</div>
		<div class="left fontsize3">CVS很酷，但Subversion更酷。然而，如果你在使用Eclipse进行开发，那么你可能直到近来才能利用Subversion带来的优点</div>
		<div class="left fontsize3">
		</div>
		<div class="left fontsize3">
				<strong>摘要</strong> CVS很酷，但Subversion更酷。然而，如果你在使用Eclipse进行开发，那么你可能直到近来才能利用Subversion带来的优点。随着Subclipse的发行，Subversion可能会最终在你的Eclipse IDE环境充分发挥其威力而压倒CVS。</div>
		<strong>一、SCM和Subversion简介<br /><br /></strong>　　软件配置管理(SCM)是管理源码并保持其安全的良好艺术，它能实现源码与其他团队成员之间保持共享，并且能够对之加以保护。良好地利用SCM，你能够容易地跟踪软件的发行和新的开发分支；这样以来，可以更为容易地标识和修正发行产品中的错误。<br /><br />　　其实，有大量的SCM工具可用，既有开源的和也有商业化的，例如StarTeam，Perforce，BitKeeper和ClearCase。在开源世界里，事实上的SCM标准是并发版本管理系统（CVS），它被广泛应用于世界范围内的成百上千的开源和商业工程。然而，CVS也存在下列许多固有的缺陷，这使得它无法非常完美地适合于现代工程开发：<br /><br />　　· 实质上针对文本文件的设计使得CVS处理二进制文件能力比较差。在每一次提交时，二进制文件被以整体形式传输和存储，这将带来带宽和磁盘空间的浪费。<br /><br />　　· 在CVS中，你不能移动文件和目录。你唯一的选择基本上就是删除并且重新添加它们，从而失去了整个过程中的所有的文件历史信息。<br /><br />　　· CVS中没有实现原子提交的概念。比方说，你要把10个文件提交到服务器，而该提交操作往往在整个过程的中途停了下来。(这很可能会发生，如果某人同时提交一个文件，或甚至如果你的网络失败或你的PC重新启动的话。)在这种情况下，服务器将仅记录下你的修正的一半信息，这可能会使代码基部分处于一种潜在地不稳定的状态。<br /><br />　　Subversion是一种比较新的开源SCM工具，其设计目的是力图从根本上克服原CVS所具有的限制。它是一种良好设计的工具，具有适合于现代开发的许多新特征：<br /><br />　　· 提交是原子化的。提交的文件都能够被正确加入到一个新的修订当中，否则仓库不会被更新；并且每一个新的修订仅由一次提交中的变化部分组成。<br /><br />　　· Subversion对文本和二进制文件使用一种巧妙的二进制技术，这既优化了网络流量也优化了仓库磁盘空间。<br /><br />　　· 在Subversion中，每一次修订都代表了一个特定时间内完整的目录树拷贝。文件和目录可以不加限制地进行移动。<br /><br />　　· Subversion仅存储两个版本之间的修改内容，这不仅节约了磁盘空间，并且意味着标识一个新版本或创建一种新的子内容几乎可以立即实现。<br /><br />　　· 你可以以多种途径来存取一个Subversion仓库，具体则依赖于你的需要：使用HTTP或HTTPS（与WebDAV一起使用），使用快速的专利性svn:协议，或直接经由本地文件，等等。 <br /><br />　　<b>二、Subclipse插件与Eclipse的集成</b><br /><br />　　一种良好的SCM应该与你的工作环境紧密地集成到一起。没有谁真正喜欢转到命令行以把文件添加到仓库。Eclipse很早就实现了CVS集成，但是直到最近Subversion用户仍没有被引起重视。现在，新的Subclipse插件提供了在Eclipse中的一种平滑的Subversion集成。<br /><br />　　(一) 安装Subclipse插件<br /><br />　　下面，你以通常的方法从更新站点下安装Subclipse：<br /><br />　　1. 打开"Find and install"窗口（"Help&gt;Software Updates&gt;Find and Install"）。<br /><br />　　2. 选择"Search for new features to install"选项并点击Next。<br /><br />　　3. 点击"New Remote Site"并且创建一远程站点，使用名字Subclipse和URL http://subclipse.tigris.org/update_1.0.x(参考图1)。<br /><br />　　4. 在结果安装窗口中，把"Subeclipse in the Features"选择到安装列表中，并且通过向导来开始安装插件。<br /><br />　　5. 完成这些之后，重新启动Eclipse。现在，你可以继续往下进行！<br /><br /><table width="90%" align="center"><tbody><tr><td><div align="center"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" height="297" hspace="0" src="http://www.subversion.org.cn/images/stories//edu/t1.jpg" width="335" border="1" /><br />图1.安装Subclipse插件</div></td></tr></tbody></table><br />(二) 建立Repository定义<br /><br />　　现在，既然你已经安装完插件；那么，接下来，你需要告诉它你的工程仓库位于何处。你是在SVN Repository视图中实现的。打开这个视图（"Windows&gt;Show View&gt;Other&gt;SVN Repository"）并且在上下文菜单中选择"New&gt;Repository Location"以显示一个如图2所示的对话框。输入适当的URL并且点击"Finish"。<br /><br /><table width="90%" align="center"><tbody><tr><td><div align="center"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" height="279" hspace="0" src="http://www.subversion.org.cn/images/stories/edu/t2.jpg" width="300" border="1" /><br />图2.添加一个仓库定义</div></td></tr></tbody></table><p><br />　　(三) 检出（Check Out）一个工程<br /><br />　　一旦建立一个仓库，你就可以在SVN Repository视图中浏览所有的内容(见图3)。我们后面将会看到，这个视图是一种与Subversion进行交互的非常方便的方式。</p><p align="center"><br /><br /><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" height="186" hspace="0" src="http://www.subversion.org.cn/images/stories/edu/t3.jpg" width="276" border="1" /><br />图3.SVN Repository视图。</p><p><br /><br />　　现在，让我们把一个工程检出到你的Eclipse工作区中。这只需选择你需要的Subversion仓库，打开上下文菜单，并且选择"Checkout"即可。这将打开一个具有两个选项的向导：<br /><br />　　· Check out as a Project configured using the New Project Wizard-这个选项打开新工程向导，这可以让你使用内建的Eclipse工程类型配置工程。这个选项通常是最好用的，因为它让你使用相同的工程模板和配置屏幕，而当你创建一个常规工程时你经常使用它们。<br /><br />　　· Check out as a Project in the Workspace-这个选项简单地在你的包含检出源码的工作区中创建一个Eclipse工程。<br /><br />　　在以上两种情况下，你仍然需要更新工程的构建路径，因为在检出该工程源码之前，Eclipse不能确定这些<a class="bluekey" href="http://dev.yesky.com/devjava/" target="_blank"><u><font color="#0000ff">Java</font></u></a>源码所在的位置。<br /><br />　　(四) 把一个新工程导入到仓库中<br /><br />　　如果你只是启动了一个新的工程，那么你需要把它导入到Subversion仓库。Subclipse提供了一种方便的方式来直接从你的IDE内部实现这一点。为此，只需要从Package Explorer视图下选择你的工程，并且在上下文菜单中选择"Team&gt;Share Project"。你可以使用现有仓库之一或创建一新的仓库定义。在你指定仓库和工程名之后，你能指定你想放到仓库中的文件和目录并且提供一个初始注释(见图4)。这种方法特别有用，因为它让你有选择地导入仅由Subversion管理的文件，即使该工程还包含其它文件（例如生成的类，临时文件或其它不是必需的内容等）。 <br /><br /></p><table width="90%" align="center"><tbody><tr><td><div align="center"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" height="355" hspace="0" src="http://www.subversion.org.cn/images/stories/edu/t4.jpg" width="372" border="0" /><br />图4.把一个工程导入到一个Subversion仓库中</div></td></tr></tbody></table><br /><strong>三、在Eclipse中使用Subversion<br /><br /></strong>　　现在，既然你的支持Subversion的工程已经启动并且运行起来，那么大多数必要的Subversion命令就可经由"Team"上下文菜单存取(参考图5)。你可以在Package Explorer中看到你的本地文件的状态(参考图6)，其中，任何修改了的文件都被标记上一个星号。存储在仓库中的文件都显示一个小黄桶图标(代表了一个数据库)；还没有被添加到仓库中的文件以一个问号显示。<br /><br /><table width="90%" align="center"><tbody><tr><td><div align="center"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" height="243" hspace="0" src="http://www.subversion.org.cn/images/stories/edu/t5.jpg" width="324" border="1" /><br />图5.大多数Subversion命令能被经由Team菜单存取<br /><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" height="279" hspace="0" src="http://www.subversion.org.cn/images/stories/edu/t6.jpg" width="407" border="1" /><br />图6.你可以在Package Explorer中看到本地文件的状态</div></td></tr></tbody></table><br />　　(一) 与Repository保持同步<br /><br />　　从仓库中更新你的文件并且把你的变化提交到仓库是相当直接的过程，这可以使用"Team&gt;Update and Team&gt;Commit"菜单选项来实现。在提交你的变化之前，你可能想看一下自从你的上次更新以来是否服务器上有任何文件被修改。为此，你可以使用"Team &gt;Synchronize with Repository"。这个命令让你看到有哪些内容已经被局部地修改，有哪些内容在服务器上修改，以及这两种修改之间的任何冲突（参考图7)。你还可以以可视化方式看到冲突的版本，并且在提交你的变化之前纠正任何比较突出的冲突。<br /><br /><table width="90%" align="center"><tbody><tr><td><div align="center"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" height="247" hspace="0" src="http://www.subversion.org.cn/images/stories/edu/t7.jpg" width="324" border="1" /><br />图7.与仓库保持同步</div></td></tr></tbody></table><br />　　(二) 使用属性<br /><br />　　属性是Subversion具有创新性的特征之一。在Subversion中，你可以把元数据（"properties"）关联到任何文件或目录。你可以定义任何你喜欢的属性，但是Subversion也提供了一些有用的内置属性，例如下面图8中所提供的这些属性：<br /><br />　　· svn:executable属性，允许你在支持这种能力的操作系统上设置一个文件的可执行标志。<br /><br />　　· svn:need-lock属性，可以用来在文件（例如，对二进制文件非常有用）上强加排斥锁。一个定义了svn:need-lock属性的文件一次只能被一个人修改。当该文件被检出时，它是只读的。如果你想修改该文件，你需要首先使用"Team&gt;Lock"菜单选项。之后，使用"Team&gt;Unlock"释放该文件，或仅提交你的变化。这一行为将释放该锁并且让其它的用户也得到该文件上的一把锁。 <br /><br /><table width="90%" align="center"><tbody><tr><td><div align="center"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" height="206" hspace="0" src="http://www.subversion.org.cn/images/stories/edu/t8.jpg" width="288" border="1" /><br />图8.把一个Subversion属性添加到一个文件中</div></td></tr></tbody></table><br />三) Tag和Branch<br /><br />　　在Subversion中，很容易创建新的tag和branch。你可以使用tag来标识一个特定的版本（使用一种可读的名字，例如"Release 1.0"）。；而一个branch用于新的开发工作而不影响主源码基(称作trunk)。在一个branch上的开发仍会继续进行，直到开发者已经为把变化集成回主trunk作好准备。<br /><br />　　在Subversion中，branch和tag都是通过制作给定修订的一个虚拟副本（以另一个名字和/或另一个目录）创建的。在常规情况下，branch存储在branches目录下，tag位于tags目录下，尽管在实践中为了满足你的工程你可以使用自己的任何定制。<br /><br />　　从Eclipse中，"Team&gt;Branch/Tag"菜单能够使你创建branch和tag(参考图9)。其中，Browse按钮提供了一种方便的方法来查看有哪些branch和tag存在于仓库中。<br /><br />　　当你使用"Team&gt;Switch"创建成功一个新的branch或tag时，你可以非常容易地在branches之间进行切换。无论何时你切换到一个不同的branch(或返回到trunk)，Subversion将仅更新文件（它需要保持你的当前工作的副本与目的branch之间的同步）。<br /><br /><table width="90%" align="center"><tbody><tr><td><div align="center"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" height="291" hspace="0" src="http://www.subversion.org.cn/images/stories/edu/t9.jpg" width="311" border="1" /><br />图9.创建一个新的branch或tag</div></td></tr></tbody></table><br />　　(四) 修订历史<br /><br />　　象大多数SCM系统一样，Subversion让你跟踪你的源码的变化。"Team&gt;Show in Resource History"菜单选项能够使你查询这些变化的列表（包括对一个文件，目录或甚至整个工程的改变）(见图10)。<br /><br />　　记住，在Subversion中，提交是原子性的-一次提交由一组文件变化和一个全局注释组成。"SVN Resource History"视图向你显示每一次提交的一个简明视图，包括修改的文件和相关注释。<br /><br /><table width="90%" align="center"><tbody><tr><td><div align="center"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" height="134" hspace="0" src="http://www.subversion.org.cn/images/stories/edu/t10.jpg" width="407" border="1" /><br />图10.历史资源</div></td></tr></tbody></table><br />　　<b>四、结论</b><br /><br />　　Subversion是一种强有力的和非常灵活的SCM工具，也是CVS的一个成功的后继者。结合Subclipse，Subversion能最终在你的Eclipse IDE环境中得到全面的发挥。<img src ="http://www.cnitblog.com/tilan/aggbug/20102.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-12-05 15:50 <a href="http://www.cnitblog.com/tilan/articles/20102.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ANT安装、配置</title><link>http://www.cnitblog.com/tilan/articles/19775.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Thu, 30 Nov 2006 10:11:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/19775.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/19775.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/19775.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/19775.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/19775.html</trackback:ping><description><![CDATA[<p>关键词：ant build.xml javac<br /></p>
		<p>内容摘要：<br />ant是一个基于JAVA的自动化脚本引擎，脚本格式为XML。除了做JAVA编译相关任务外，ANT还可以通过插件实现很多应用的调用。<br /></p>
		<ol>
				<li>ANT的基本概念： 
</li>
				<li>ANT的安装：解包，设置路径 
</li>
				<li>ANT的使用：最好的学习只不过是一个简单实用的例子起步…… </li>
		</ol>
		<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>
				<li>任务2：clean &lt;-- init 清空初始化环境 
</li>
				<li>任务3：javadoc &lt;-- build &lt;-- init 生成JAVADOC 
</li>
				<li>任务4：jar &lt;-- build &lt;-- init 生成JAR 
</li>
				<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/">WebLucene</a>应用的例子：修改自<a href="http://www.jdom.org/">JDOM</a>的build.xml：<br /><br />&lt;project default="usage" basedir="."&gt;<br /><br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Initialization target                                               --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="init"&gt;<br />    &lt;tstamp/&gt;<br />    &lt;property file="${basedir}/build.properties" /&gt;<br />    &lt;property name="Name" value="ProjectFullName"/&gt;<br />    &lt;property name="name" value="project_name"/&gt;<br />    &lt;property name="version" value="0.2"/&gt;<br />    &lt;property name="year" value="2003"/&gt;<br /><br />    &lt;echo message="----------- ${Name} ${version} [${year}] ------------"/&gt;<br /><br />    &lt;property name="debug" value="off"/&gt;<br />    &lt;property name="optimize" value="on"/&gt;<br />    &lt;property name="deprecation" value="on"/&gt;<br /><br />    &lt;property name="src.dir" value="./src/WEB-INF/src"/&gt;<br />    &lt;property name="lib.dir" value="./src/WEB-INF/lib"/&gt;<br />    &lt;property name="packages" value="com.chedong.*,org.apache.lucene.*"/&gt;<br /><br />    &lt;property name="build.src" value="./src/WEB-INF/build"/&gt;<br />    &lt;property name="build.dest" value="./src/WEB-INF/classes"/&gt;<br />    &lt;property name="build.javadocs" value="./src/doc"/&gt;<br />    <br />    &lt;path id="classpath"&gt;<br />        &lt;pathelement path="${jsdk_jar}"/&gt;<br />        &lt;fileset dir="${lib.dir}"&gt;<br />           &lt;include name="**/*.jar"/&gt;<br />        &lt;/fileset&gt;<br />    &lt;/path&gt;<br />    <br />    &lt;filter token="year" value="${year}"/&gt;<br />    &lt;filter token="version" value="${version}"/&gt;<br />    &lt;filter token="date" value="${TODAY}"/&gt;<br />    &lt;filter token="log" value="true"/&gt;<br />    &lt;filter token="verbose" value="true"/&gt;<br />  &lt;/target&gt;<br /><br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Help on usage                                                       --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="usage" depends="init"&gt;<br />    &lt;echo message="${Name} Build file"/&gt;<br />    &lt;echo message="-------------------------------------------------------------"/&gt;<br />    &lt;echo message=""/&gt;<br />    &lt;echo message=" available targets are:"/&gt;<br />    &lt;echo message=""/&gt;<br />    &lt;echo message="   jar      --&gt; generates the ${name}.jar file"/&gt;<br />    &lt;echo message="   build    --&gt; compiles the source code"/&gt;<br />    &lt;echo message="   javadoc  --&gt; generates the API documentation"/&gt;<br />    &lt;echo message="   clean    --&gt; cleans up the directory"/&gt;<br />    &lt;echo message=""/&gt;<br />    &lt;echo message=" Please rename build.properties.default to build.properties"/&gt;<br />    &lt;echo message=" and edit build.properties to specify JSDK 2.3 classpath."/&gt;<br />    &lt;echo message=""/&gt;<br />    &lt;echo message=" See the comments inside the build.xml file for more details."/&gt;<br />    &lt;echo message="-------------------------------------------------------------"/&gt;<br />    &lt;echo message=""/&gt;<br />    &lt;echo message=""/&gt;<br />  &lt;/target&gt;<br /><br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Prepares the source code                                            --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="prepare-src" depends="init"&gt;<br />    &lt;!-- create directories --&gt;<br />    &lt;mkdir dir="${build.src}"/&gt;<br />    &lt;mkdir dir="${build.dest}"/&gt;<br />    <br />    &lt;!-- copy src files --&gt;<br />    &lt;copy todir="${build.src}"&gt;<br />      &lt;fileset dir="${src.dir}"/&gt;<br />    &lt;/copy&gt;<br />  &lt;/target&gt;<br /><br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Compiles the source directory                                       --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="build" depends="prepare-src"&gt;<br />    &lt;javac srcdir="${build.src}"<br />           destdir="${build.dest}"<br />           debug="${debug}"<br />           optimize="${optimize}"&gt;<br />      &lt;classpath refid="classpath"/&gt;<br />    &lt;/javac&gt;<br />  &lt;/target&gt;<br /><br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Creates the class package                                           --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="jar" depends="build"&gt;<br />    &lt;jar jarfile="${lib.dir}/${name}.jar"<br />         basedir="${build.dest}"<br />         includes="**"/&gt;<br />  &lt;/target&gt;<br /><br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Creates the API documentation                                       --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="javadoc" depends="build"&gt;<br />    &lt;mkdir dir="${build.javadocs}"/&gt;<br />    &lt;javadoc packagenames="${packages}"<br />             sourcepath="${build.src}"<br />             destdir="${build.javadocs}"<br />             author="true"<br />             version="true"<br />             use="true"<br />             splitindex="true"<br />             windowtitle="${Name} API"<br />             doctitle="${Name}"&gt;<br />      &lt;classpath refid="classpath"/&gt;<br />    &lt;/javadoc&gt;<br />  &lt;/target&gt;<br /><br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Clean targets                                                       --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="clean" depends="init"&gt;<br />    &lt;delete dir="${build.src}"/&gt;<br />    &lt;delete dir="${build.dest}/org"/&gt;<br />    &lt;delete dir="${build.dest}/com"/&gt;<br />    &lt;delete&gt;<br />      &lt;fileset dir="${build.dest}" includes="**/*.class"/&gt;<br />    &lt;/delete&gt;<br />  &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 />    &lt;path id="classpath"&gt;<br />        &lt;pathelement path="${jsdk_jar}"/&gt;<br />        &lt;fileset dir="${lib.dir}"&gt;<br />           &lt;include name="**/*.jar"/&gt;<br />        &lt;/fileset&gt;<br />    &lt;/path&gt;<br />则相当于设置了：CLASSPATH=/path/to/resin/lib/jsdk23.jar; /path/to/project/lib/*.jar;<br /><br />文件复制：prepare-src<br />创建临时SRC存放目录和输出目录。<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Prepares the source code                                            --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="prepare-src" depends="init"&gt;<br />    &lt;!-- create directories --&gt;<br />    &lt;mkdir dir="${build.src}"/&gt;<br />    &lt;mkdir dir="${build.dest}"/&gt;<br />    <br />    &lt;!-- copy src files --&gt;<br />    &lt;copy todir="${build.src}"&gt;<br />      &lt;fileset dir="${src.dir}"/&gt;<br />    &lt;/copy&gt;<br />  &lt;/target&gt;<br /><br />编译任务：build<br />编译时的CLASSPATH环境通过一下方式找到引用一个path对象<br />&lt;classpath refid="classpath"/&gt;<br /><br />打包任务：jar<br />对应用打包生成项目所写名的.jar文件<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Creates the class package                                           --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="jar" depends="build"&gt;<br />    &lt;jar jarfile="${lib.dir}/${name}.jar"<br />         basedir="${build.dest}"<br />         includes="**"/&gt;<br />  &lt;/target&gt;<br /><br />生成JAVADOC文档任务: javadoc<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Creates the API documentation                                       --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="javadoc" depends="build"&gt;<br />    &lt;mkdir dir="${build.javadocs}"/&gt;<br />    &lt;javadoc packagenames="${packages}"<br />             sourcepath="${build.src}"<br />             destdir="${build.javadocs}"<br />             author="true"<br />             version="true"<br />             use="true"<br />             splitindex="true"<br />             windowtitle="${Name} API"<br />             doctitle="${Name}"&gt;<br />      &lt;classpath refid="classpath"/&gt;<br />    &lt;/javadoc&gt;<br />  &lt;/target&gt;<br /><br />清空临时编译文件：clean<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;!-- Clean targets                                                       --&gt;<br />  &lt;!-- =================================================================== --&gt;<br />  &lt;target name="clean" depends="init"&gt;<br />    &lt;delete dir="${build.src}"/&gt;<br />    &lt;delete dir="${build.dest}/org"/&gt;<br />    &lt;delete dir="${build.dest}/com"/&gt;<br />    &lt;delete&gt;<br />      &lt;fileset dir="${build.dest}" includes="**/*.class"/&gt;<br />    &lt;/delete&gt;<br />  &lt;/target&gt;<br /><br />TODO：<br />更多任务/扩展：（样例）<br /></div><ul><li>测试任务：JUnit测试 
</li><li>代码风格检查任务：CheckStyle，Jalopy等 
</li><li>邮件警报任务：可以把以上这些任务的输出警告发送到制定的用户列表中，这个任务可以设置每天自动运行。 </li></ul><br />参考资料：<br /><p>Jakarta ANT:<br /><a href="http://ant.apache.org/">http://ant.apache.org</a></p><img src ="http://www.cnitblog.com/tilan/aggbug/19775.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-11-30 18:11 <a href="http://www.cnitblog.com/tilan/articles/19775.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ANT十五大最佳实践</title><link>http://www.cnitblog.com/tilan/articles/19774.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Thu, 30 Nov 2006 10:10:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/19774.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/19774.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/19774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/19774.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/19774.html</trackback:ping><description><![CDATA[<p>作者：<a href="http://www.onjava.com/pub/au/110">Eric M. Burke</a>, coauthor of <a href="http://www.oreilly.com/catalog/jextprockbk/index.html?CMP=IL7015">Java Extreme Programming Cookbook</a></p>
		<p>原文：<a href="http://www.onjava.com/pub/a/onjava/2003/12/17/ant_bestpractices.html">http://www.onjava.com/pub/a/onjava/2003/12/17/ant_bestpractices.html</a></p>
		<p>译者：徐彤<a href="msn:xt121@hotmail.com">MSN:xt121@hotmail.com</a></p>
		<p> </p>
		<p>在Ant出现之前，构建和部署Java应用需要使用包括特定平台的脚本、Make文件、各种版本的IDE甚至手工操作的“大杂烩”。现在，几乎所有的开源Java项目都在使用Ant，大多数公司的内部项目也在使用Ant。Ant在这些项目中的广泛使用自然导致了读者对一整套Ant最佳实践的迫切需求。</p>
		<p>本文总结了我喜爱的Ant技巧或最佳实践，多数是从我亲身经历的项目错误或我听说的其他人经历的 “恐怖”故事中得到灵感的。比如，有人告诉我有个项目把XDoclet 生成的代码放入带有锁定文件功能的版本控制工具中。当开发者修改源代码时，他必须记住手工检出（Check out）并锁定所有将要重新生成的文件。然后，手工运行代码生成器，只到这时他才能够让Ant编译代码，这一方法还存在如下一些问题：</p>
		<ul>
				<li>生成的代码无法存储在版本控制系统中。 
</li>
				<li>Ant（本案例中是Xdoclet）应该自动确定下一次构建涉及的源文件，而不应由程序员手工确定。 
</li>
				<li>Ant的构建文件应该定义好正确的任务依赖关系，这样程序员就不必为了完成构建而不得不按照特定顺序调用任务。 </li>
		</ul>
		<p>当我开始一个新项目时，我首先编写Ant构建文件。Ant文件明确地定义构建的过程，并被团队中的每个程序员使用。本文所列的技巧基于这样的假定：Ant构建文件是一个必须仔细编写的重要文件，它应在版本控制系统中得到维护，并被定期进行重构。下面是我的十五大Ant最佳实践。</p>
		<h3>1. 采用一致的编码规范</h3>
		<p>Ant用户有的喜欢有的痛恨其构建文件的XML语法。与其跳进这一令人迷惑的争论中，不如让我们先看一些能保持XML构建文件简洁的方法。</p>
		<p>首先也是最重要的，花费时间格式化你的XML让它看上去很清晰。不论XML是否美观，Ant都可以工作。但是丑陋的XML很难令人读懂。倘若你在任务之间留出空行，有规则的缩进，每行文字不超过90列左右，那么XML令人惊讶地易读。再加上使用能够高亮XML语法的优秀编辑器或IDE工具，你就不会有阅读的麻烦。</p>
		<p>同样，精选含意明确、容易读懂的词汇来命名任务和属性。比如，<em>dir.reports</em>就比<em>rpts</em>好<em>。</em>特定的编码规范并不重要，只要拿出一套规范并坚持使用就行。</p>
		<h3>2. 将<em>build.xml</em>放在项目根目录中</h3>
		<p>Ant构建文件<em>build.xml</em>可以放在任何位置，但是放在项目顶级目录中可以保持项目简洁。这是最常用的规范，开发者能够在顶级目录中找到预期的<em>build.xml</em>。把构建文件放在根目录中，也能够使人容易了解项目目录树中不同目录之间的逻辑关系。以下是一个典型的项目目录层次：</p>
		<p>[root dir]<br />  | build.xml  <br />  +--src <br />  +--lib (包含第三方 JAR包) <br />  +--build (由 build任务生成)  <br />  +--dist (由 build任务生成) </p>
		<p>当<em>build.xml</em>在顶级目录时，假设你处于项目某个子目录中，只要输入：ant -find compile 命令，不需要改变工作目录就能够以命令行方式编译代码。参数-find告诉Ant寻找存在于上级目录中的<em>build.xml</em>并执行。</p>
		<h3>3. 使用单一的构建文件</h3>
		<p>有人喜欢将一个大项目分解成几个小的构建文件，每个构建文件分担整个构建过程的一小部分工作。这确实是看法不同的问题，但是应该认识到，将构建文件分割会增加对整体构建过程的理解难度。要注意在单一构建文件能够清楚表现构建层次的情况下不要过工程化(over-engineer)。</p>
		<p>即使你把项目划分为多个构建文件，也应使程序员能够在项目根目录下找到核心<em>build.xml</em>。尽管该文件只是将实际构建工作委派给下级构建文件，也应保证该文件可用。</p>
		<h3>4. 提供良好的帮助说明</h3>
		<p>应尽量使构建文件自文档化。增加任务描述是最简单的方法。当你输入ant -projecthelp时，你就可以看到带有描述的任务清单。比如，你可以这样定义任务：</p>&lt;target name="compile"  <br />   description="Compiles code, output goes to the build dir."&gt; 
<p>最简单的规则是把所有你想让程序员通过命令行就可以调用的任务都加上描述。对于一般用来执行中间处理过程的内部任务，比如生成代码或建立输出目录等，就无法使用描述属性。</p><p>这时，可以通过在构建文件中加入XML注释来处理。或者专门定义一个help任务，当程序员输入ant help时来显示详细的使用说明。</p>&lt;target name="help" description="Display detailed usage information"&gt;<br />  &lt;echo&gt;Detailed help...&lt;/echo&gt;&lt;/target&gt; 
<h3>5. 提供清除任务</h3><p>每个构建文件都应包含一个清除任务，用来删除所有生成的文件和目录，使系统回到构建文件执行前的初始状态。执行清空任务后还存在的文件都应处在版本控制系统的管理之下。比如：</p>&lt;target name="clean" <br />    description="Destroys all generated files and dirs."&gt;<br />  &lt;delete dir="${dir.build}"/&gt;<br />  &lt;delete dir="${dir.dist}"/&gt;<br />&lt;/target&gt; 
<p>除非是在产生整个系统版本的特殊任务中，否则不要自动调用clean任务。当程序员仅仅执行编译任务或其他任务时，他们不需要构建文件事先执行既令人讨厌又没有必要的清空任务。要相信程序员能够确定何时需要清空所有文件。</p><h3>6. 使用ANT管理任务从属关系</h3><p>假设你的应用由Swing GUI组件、Web界面、EJB层和公共应用代码组成。在大型系统中，你需要清晰地定义每个Java包属于系统的哪一层。否则任何一点修改都要被迫重新编译成百上千个文件。糟糕的任务从属关系管理会导致过度复杂而脆弱的系统。改变GUI面板的设计不应造成Servlet和EJB的重编译。</p><p>当系统变得庞大后，稍不注意就可能将依赖于客户端的代码引入到服务端。这是因为典型的IDE项目文件编译任何文件都使用单一的classpath。而Ant能让你更有效地控制构建活动。</p><p>设计你的Ant构建文件编译大型项目的步骤：首先，编译公共应用代码，将编译结果打成JAR包文件。然后，编译上一层的项目代码，编译时依靠第一步产生的JAR文件。不断重复这一过程，直到最高层的代码编译完成。</p><p>分步构建强化了任务从属关系管理。如果你工作在底层Java框架上，偶然引用到高层的GUI模板组件，这时代码不需要编译。这是由于构建文件在编译底层框架时在源路径中没有包含高层GUI面板组件的代码。</p><h3>7. 定义并重用文件路径</h3><p>如果文件路径在一个地方一次性集中定义，并在整个构建文件中得到重用，那么构建文件更易于理解。以下是这样做的一个例子：</p>&lt;project name="sample" default="compile" basedir="."&gt;<br />  &lt;path id="classpath.common"&gt;<br />    &lt;pathelement location="${jdom.jar.withpath}"/&gt;<br />    ...etc  &lt;/path&gt;<br />  &lt;path id="classpath.client"&gt;<br />    &lt;pathelement location="${guistuff.jar.withpath}"/&gt;<br />    &lt;pathelement location="${another.jar.withpath}"/&gt;<br />    &lt;!-- reuse the common classpath --&gt;<br />    &lt;path refid="classpath.common"/&gt;<br />  &lt;/path&gt;<br />  &lt;target name="compile.common" depends="prepare"&gt;<br />    &lt;javac destdir="${dir.build}" srcdir="${dir.src}"&gt;<br />          &lt;classpath refid="classpath.common"/&gt;<br />          &lt;include name="com/oreilly/common/**"/&gt;<br />    &lt;/javac&gt;<br />  &lt;/target&gt;<br />&lt;/project&gt; 
<p>当项目不断增长构建日益复杂时，这一技术越发体现出其价值。你可能需要为编译不同层次的应用定义各自的文件路径，比如运行单元测试的、运行应用程序的、运行Xdoclet的、生成JavaDocs的等等不同路径。这种组件化路径定义的方法比为每个任务单独定义路径要优越得多。否则，很容易丢失任务从属关系的轨迹。</p><h3>8. 定义恰当的任务从属关系</h3><p>假设dist任务从属于jar任务，那么哪个任务从属于compile任务哪个任务从属于prepare任务呢？Ant构建文件最终定义了任务的从属关系图，它必须被仔细地定义和维护。</p><p>应该定期检查任务的从属关系以保证构建工作得到正确执行。大的构建文件随着时间推移趋向于增加更多的任务，所以到最后可能由于不必要的从属关系导致构建工作非常困难。比如，你可能发现在程序员只需编译一些没有使用EJB的GUI代码时又重新生成了EJB代码。</p><p>以“优化”的名义忽略任务的从属关系是另一种常见的错误。这种错误迫使程序员为了得到恰当的结果必须记住并按照特定的顺序调用一串任务。更好的做法是：提供描述清晰的公共任务，这些任务包含正确的任务从属关系；另外提供一套“专家”任务让你能够手工执行个别的构建步骤，这些任务不提供完整的构建过程，但是让那些专家用户在快速而恼人的编码期间能够跳过某些步骤。</p><h3>9.使用属性</h3><p>任何需要配置或可能发生变化的信息都应作为Ant属性定义下来。对于在构建文件中多次出现的值也同样处理。属性既可以在构建文件头部定义，也可以为了更好的灵活性而在单独的属性文件中定义。以下是在构建文件中定义属性的样式：</p>&lt;project name="sample" default="compile" basedir="."&gt;<br />  &lt;property name="dir.build" value="build"/&gt;<br />  &lt;property name="dir.src" value="src"/&gt;<br />  &lt;property name="jdom.home" value="../java-tools/jdom-b8"/&gt;<br />  &lt;property name="jdom.jar" value="jdom.jar"/&gt;<br />  &lt;property name="jdom.jar.withpath"<br />                    value="${jdom.home}/build/${jdom.jar}"/&gt;<br />    etc...<br />&lt;/project&gt; 
<p>或者你可以使用属性文件：</p>&lt;project name="sample" default="compile" basedir="."&gt;<br />  &lt;property file="sample.properties"/&gt;<br />   etc...<br />&lt;/project&gt; 
<p>在属性文件 <em>sample.properties</em>中:</p>dir.build=build<br />dir.src=src<br />jdom.home=../java-tools/jdom-b8<br />jdom.jar=jdom.jarjdom.jar.withpath=${jdom.home}/build/${jdom.jar} 
<p>用一个独立的文件定义属性是有好处的，它可以清晰地定义构建中的可配置部分。另外，在开发者工作在不同操作系统的情况下，你可以在不同的平台上提供该文件的不同版本。</p><h3>10. 保持构建过程独立</h3><p>为了最大限度的扩展性，不要应用外部路径和库文件。最重要的是不要依赖于程序员的CLASSPATH设置。取而代之的是，在构建文件中使用相对路径并定义自己的路径。如果你引用了绝对路径如<em>C:\java\tools</em>，其他开发者未必使用与你相同的目录结构，所以就无法使用你的构建文件。</p><p>如果你部署开放源码项目，应该提供包含编译代码所需的所有JAR文件的发行版本。当然，这是在遵守许可协议的基础上。对于内部项目，相关的JAR文件都应在版本控制系统的管理中，并捡出（check out）到大家都知道的位置。</p><p>当你必须引用外部路径时，应将路径定义为属性。使程序员能够用适合他们自己的机器环境的参数重载这些属性。你也可以使用以下语法引用环境变量：</p>&lt;property environment="env"/&gt;<br />&lt;property name="dir.jboss" value="${env.JBOSS_HOME}"/&gt; 
<h3>11. 使用版本控制系统</h3><p>构建文件是一个重要的制品，应该像代码一样进行版本控制。当你标记你的代码时，也应用同样的标签标记构建文件。这样当你需要回溯到旧版本并进行构建时，能够使用相应版本的构建文件。</p><p>除构建文件之外，你还应在版本控制中维护第三方JAR文件。同样，这使你能够重新构建旧版本的软件。这也能够更容易保证所有开发者拥有一致的JAR文件，因为他们都是同构建文件一起从版本控制系统中捡出的。</p><p>通常应避免在版本控制系统中存放构建成果。倘若你的源代码很好地得到了版本控制，那么通过构建过程你能够重新生成任何版本的产品。</p><h3>12. 把Ant作为“最小公分母”</h3><p>假设你的开发团队使用IDE工具，当程序员通过点击图标就能够构建整个应用时为什么还要为Ant而烦恼呢？</p><p>IDE的问题是一个关于团队一致性和重现性的问题。几乎所有的IDE设计初衷都是为了提高程序员的个人生产率，而不是开发团队的持续构建。典型的IDE要求每个程序员定义自己的项目文件。程序员可能拥有不同的目录结构，可能使用不同版本的库文件，还可能工作在不同的平台上。这将导致出现这种情况：在Bob那里运行良好的代码，到Sally那里就无法运行。</p><p>不管你的开发团队使用何种IDE，一定要建立所有程序员都能够使用的Ant构建文件。要建立一个程序员在将新代码提交版本控制系统前必须执行Ant构建文件的规则。这将确保代码是经过同一个Ant构建文件构建的。当出现问题时，要使用项目标准的Ant构建文件，而不是通过某个IDE来执行一个干净的构建。</p><p>程序员可以自由选择任何他们习惯使用的IDE工具或编辑器。但是Ant应作为公共基线以保证代码永远是可构建的。</p><h3>13. 使用zipfileset属性</h3><p>人们经常使用Ant产生WAR、JAR、ZIP和 EAR文件。这些文件通常都要求有一个特定的内部目录结构，但其往往与你的源代码和编译环境的目录结构不匹配。</p><p>一个最常用的方法是写一个Ant任务，按照期望的目录结构把一大堆文件拷贝到临时目录中，然后生成压缩文件。这不是最有效的方法。使用zipfileset属性是更好的解决方案。它让你从任何位置选择文件，然后把它们按照不同目录结构放进压缩文件中。以下是一个例子：</p>&lt;ear earfile="${dir.dist.server}/payroll.ear"<br />    appxml="${dir.resources}/application.xml"&gt;<br />  &lt;fileset dir="${dir.build}" includes="commonServer.jar"/&gt;<br />  &lt;fileset dir="${dir.build}"&gt;<br />    &lt;include name="payroll-ejb.jar"/&gt;<br />  &lt;/fileset&gt;<br />  &lt;zipfileset dir="${dir.build}" prefix="lib"&gt;<br />    &lt;include name="hr.jar"/&gt;<br />    &lt;include name="billing.jar"/&gt;<br />  &lt;/zipfileset&gt;<br />  &lt;fileset dir="."&gt;<br />    &lt;include name="lib/jdom.jar"/&gt;<br />    &lt;include name="lib/log4j.jar"/&gt;<br />    &lt;include name="lib/ojdbc14.jar"/&gt;<br />  &lt;/fileset&gt;<br />  &lt;zipfileset dir="${dir.generated.src}" prefix="META-INF"&gt;<br />    &lt;include name="jboss-app.xml"/&gt;<br />  &lt;/zipfileset&gt;<br />&lt;/ear&gt; 
<p>在这个例子中，所有JAR文件都放在EAR文件包的<em>lib</em>目录中。hr.jar和billing.jar是从构建目录拷贝过来的。因此我们使用zipfileset属性把它们移动到EAR文件包内部的<em>lib</em>目录。prefix属性指定了其在EAR文件中的目标路径。</p><h3>14. 测试Clean任务</h3><p>假设你的构建文件中有clean和compile的任务，执行以下的测试。第一步，执行ant clean；第二步，执行ant compile；第三步，再执行ant compile。第三步应该不作任何事情。如果文件再次被编译，说明你的构建文件有问题。</p><p>构建文件应该只在与输出文件相关联的输入文件发生变化时执行任务。一个构建文件在不必执行诸如编译、拷贝或其他工作任务的时候执行这些任务是低效的。当项目规模增长时，即使是小的低效工作也会成为大的问题。</p><h3>15. 避免特定平台的Ant封装</h3><p>不管什么原因，有人喜欢用简单的、名称叫做<em>compile</em>之类的批文件或脚本装载他们的产品。当你去看脚本的内容你会发现以下内容：</p><p>ant compile</p><p>其实开发人员都很熟悉Ant，并且完全能够自己键入ant compile。请不要仅仅为了调用Ant而使用特定平台的脚本。这只会使其他人在首次使用你的脚本时增加学习和理解的烦扰。除此之外，你不可能提供适用于每个操作系统的脚本，这是真正烦扰其他用户的地方。</p><h3>总结</h3><p>太多的公司依靠手工方法和特别程序来编译代码和生成软件发布版本。那些不使用Ant或类似工具定义构建过程的开发团队，花费了太多的时间来捕捉代码编译过程中出现的问题：在某些开发者那里编译成功的代码，到另一些开发者那里却失败了。</p><p>生成并维护构建脚本不是一项富有魅力的工作，但却是一项必需的工作。一个好的Ant构建文件将使你能够集中到更喜欢的工作——写代码中去！</p><h3>参考</h3><ul><li><a href="http://ant.apache.org/">Ant</a></li><li><a href="http://www.ericburke.com/">AntGraph</a>: Ant依赖性的可视化工具 
</li><li><a href="http://www.oreilly.com/catalog/anttdg/index.html?CMP=IL7015">Ant: The Definitive Guide</a>, O'Reilly 
</li><li><a href="http://www.oreilly.com/catalog/jextprockbk/index.html?CMP=IL7015">Java Extreme Programming Cookbook</a>, O'Reilly </li></ul><p> </p><img src ="http://www.cnitblog.com/tilan/aggbug/19774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-11-30 18:10 <a href="http://www.cnitblog.com/tilan/articles/19774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用 Ant 和 JUnit 进行增量开发</title><link>http://www.cnitblog.com/tilan/articles/19773.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Thu, 30 Nov 2006 10:09:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/19773.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/19773.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/19773.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/19773.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/19773.html</trackback:ping><description><![CDATA[<blockquote>软件开发习惯中一个细微更改都可能会对软件质量产生巨大改进。将单元测试合并到开发过程中，然后从长远角度来看它可以节省多少时间和精力。本文通过使用代码样本说明了单元测试的种种好处，特别是使用 Ant 和 JUnit 带来的各种方便。</blockquote>
		<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
		<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
		<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
		<p>测试是大型开发过程中的基本原则之一。在任何职业中，验证都是一个重要部分。医生要通过验血来确诊。波音公司在研制 777 的过程中对飞机的每个组件都进行了精心测试。为什么软件开发就应该例外呢？</p>
		<p>以前，由于在应用程序中将 GUI 和商业逻辑紧密联系在一起，这就限制了创建自动测试的能力。当我们学会通过抽象层将商业逻辑从界面中分离出来时，各个单独代码模块的自动测试就替代了通过 GUI 进行的手工测试。</p>
		<p>现在，集成开发环境 (IDE) 能在您输入代码的同时显示错误，对于在类中快速查找方法具有智能探测功能，可以利用语法结构生成彩色代码，而且具有许多其它功能。因此，在编译更改过的代码之前，您已经全盘考虑了将构建的类，但您是否考虑过这样的修改会破坏某些功能呢？</p>
		<p>每个开发者都碰到过更改“臭虫”。代码修改过程可能会引入“臭虫”，而如果通过用户界面手工测试代码的话，在编译完成之前是不会发现它的。然后，您就要花费几天的时间追踪由更改所引起的错误。最近在我做的一个项目中，当我把后端数据库由 Informix 更改到 Oracle 时就遇到了这种情况。大部分更改都十分顺利，但由于数据库层或使用数据库层的系统缺少单元测试，从而导致将大量时间花费在尝试解决更改“臭虫”上。我花了两天的时间查到别人代码中的一个数据库语法更改。（当然，那个人仍是我的朋友。）</p>
		<p>尽管测试有许多好处，但一般的程序员对测试都不太感兴趣，开始时我也没有。您听到过多少次“它编译了，所以它一定能用”这种言论？但“我思，故我在”这种原则并 <i>不</i>适用于高质量软件。要鼓励程序员测试他们的代码，过程必须简单无痛。 </p>
		<p>本文从某人学习用 Java 语言编程时所写的一个简单的类开始。然后，我会告诉您我是如何为这个类编写单元测试，以及在编写完它以后又是如何将单元测试添加到构建过程中的。最后，我们将看到将“臭虫”引入代码时发生的情况。</p>
		<p>
				<a name="N1004E">
						<span class="atitle">
								<font face="Arial" size="4">从一个典型类开始</font>
						</span>
				</a>
		</p>
		<p>第一个典型的 Java 程序一般都包含一个打印 "Hello World" 的 <code>main()</code> 。在清单 1 中，我创建了一个 HelloWorld 对象的实例并调用 <code>sayHello()</code> 方法，该方法会打印这句习惯说法。 </p>
		<br />
		<a name="N1005F">
				<b>清单 1. 我的第一个 Java 应用程序 "Hello world"</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">/*
 *  HelloWorld.java
 *  My first java program
 */

class HelloWorld {
    /**
     * Print "Hello World"
     */
	void sayHello() {
          System.out.println("Hello World");
      }

    /**
     * Test
     */
    public static void main( String[] args ) {
        HelloWorld world = new HelloWorld();
        world.sayHello();
    }
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<code>main()</code> 方法是我的测试。哦噢！我将代码、文档、测试和样本代码包含在了一个模块中。保佑 Java！但随着程序越变越大，这种开发方法很快就开始显现出了缺陷： 
</p>
		<ul>
				<li>混乱 <br />类接口越大， <code>main()</code> 就越大。类可能仅仅因为正常的测试而变得非常庞大。 
</li>
				<li>代码膨胀 <br />由于加入了测试，所以产品代码比所需要的要大。但我不想交付测试，而只想交付产品。 
</li>
				<li>测试不可靠 <br />既然 <code>main()</code> 是代码的一部分， <code>main()</code> 就对其他开发者通过类接口无法访问的私有成员和方法享有访问权。出于这个原因，这种测试方法很容易出错。 
</li>
				<li>很难自动测试 <br />要进行自动测试，我仍然必须创建另一程序来将参数传递给 <code>main()</code> 。 </li>
		</ul>
		<p>
		</p>
		<p>
				<b>类开发</b>
				<br />对我来说，类开发是从编写 <code>main()</code> 方法开始的。我在编写 <code>main()</code> 的时候就定义类和类的用法，然后实现接口。它的一些明显的缺陷也开始显现出来。一个缺陷是我传递给 <code>main()</code> 来执行测试的参数个数。其次， <code>main()</code> 本身在进行调用子方法、设置代码等操作时变得很混乱。有时 <code>main()</code> 会比类实现的其余部分还要大。 </p>
		<p>
				<b>更简单的过程</b>
				<br />我原来的做法有一些很明显的缺陷。因此，让我们看看有什么别的方法可以使问题简化。我仍然通过接口设计代码并给出应用示例，正如原来的 <code>main()</code> 一样。不同的是我将代码放到了另一个单独的类中，而这个类恰好是我的“单元测试”。这种技术有以下几点好处： 
</p>
		<ul>
				<li>设计类的一种机制 <br />因为是通过接口进行开发，所以不太可能利用类的内部功能。但因为我是目标类的开发者，我有到其内部工作的“窗口”，所以测试并不是个真正的黑箱。仅凭这一点就足够推断出需要开发者本人在编写目标类的同时负责测试的开发，而不是由其他任何人代劳。 
</li>
				<li>类用法的示例 <br />通过将示例从实现中分离出来，开发者可以更快地提高速度，而且再不用在源代码上纠缠不清。这种分离还有助于防止开发者利用类的内部功能，因为这些功能将来可能已经不存在了。 
</li>
				<li>没有类混乱的 <code>main()</code><br />我不再受到 <code>main()</code> 的限制了。以前我得将多个参数传递给 <code>main()</code> 来测试不同的配置。现在我可以创建许多单独的测试类，每一个都维护各自的设置代码。 </li>
		</ul>
		<p>
		</p>
		<p>接下来我们将这个单独的单元测试对象放入构建过程中。这样，我们就可以提供自动确认过程的方法。 
</p>
		<ul>
				<li>确保所做的任何更改都不会对其他人产生不利影响。 
</li>
				<li>我们在进行源码控制之前就可以测试代码，而无需等待汇编测试或在夜晚进行的构建测试。这有助于尽早捕捉到“臭虫”，从而降低产生高质量代码的成本。 
</li>
				<li>通过提供增量测试过程，我们提供了更好的实现过程。如同 IDE 帮助我们在输入时捕捉到语法或编译“臭虫”一样，增量单元测试也帮助我们在构建时捕捉到代码更改“臭虫”。 </li>
		</ul>
		<p>
		</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-ant/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N100EC">
						<span class="atitle">
								<font face="Arial" size="4">使用 JUnit 自动化单元测试</font>
						</span>
				</a>
		</p>
		<p>要使测试自动化，您需要一个测试框架。您可以自己开发或购买，也可以使用某些开放源代码工具，例如 JUnit。我选择 JUnit 出于以下几个原因： 
</p>
		<ul>
				<li>不需要编写自己的框架。 
</li>
				<li>它是开放源代码，因此不需要购买框架。 
</li>
				<li>开放源代码社区中的其他开发者会使用它，因此可以找到许多示例。 
</li>
				<li>它可以让我将测试代码与产品代码分开。 
</li>
				<li>它易于集成到我的构建过程中。 </li>
		</ul>
		<p>
		</p>
		<p>
				<b>测试布局</b>
				<br />图 1 显示了使用样本 TestSuite 的 JUnit TestSuite 布局。每个测试都由若干单独的测试案例构成。每个测试案例都是一个单独的类，它扩展了 TestClass 类并包含了我的测试代码，即那些曾在 <code>main()</code> 中出现的代码。在该例中，我向 TestSuite 添加了两个测试：一个是 SkeletonTest，我将它用作所有新类和 HelloWorld 类的起点。 </p>
		<br />
		<a name="N10115">
				<b>图 1. TestSuite 布局</b>
		</a>
		<br />
		<img height="383" alt="TestSuite 布局" src="http://www-128.ibm.com/developerworks/cn/java/j-ant/image001.gif" width="600" />
		<br />
		<p>
				<b>测试类 HelloWorldTest.java</b>
				<br />按照约定，测试类的名称中包含我所测试的类的名称，但将 <i>Test</i> 附加到结尾。在本例中，我们的测试类是 <code>HelloWorldTest.java</code> 。我复制了 SkeletonTest 中的代码，并添加了 <code>testSayHello()</code> 来测试 <code>sayHello()</code> 。请注意 HelloWorldTest 扩展了 TestCase。JUnit 框架提供了 <code>assert</code> 和 <code>assertEquals</code> 方法，我们可以使用这些方法来进行验证。 <code>HelloWorldTest.java</code> 显示在清单 2 中。 </p>
		<br />
		<a name="N10145">
				<b>清单 2. HelloWorldTest.java</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">package test.com.company;

import com.company.HelloWorld;
import junit.framework.TestCase;
import junit.framework.AssertionFailedError;

/**
 * JUnit 3.2 testcases for HelloWorld
 */
public class HelloWorldTest extends TestCase {

    public HelloWorldTest(String name) {
        super(name);
    }

    public static void main(String args[]) {
        junit.textui.TestRunner.run(HelloWorldTest.class);
    }

    public void testSayHello() {
        HelloWorld world = new HelloWorld();
        assert( world!=null );
        assertEquals("Hello World",  world.sayHello() );
    }
}</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<code>testSayHello()</code> 看上去和 <code>HelloWorld.java</code> 中原来的 main 方法类似，但有一个主要的不同之处。它不是执行 <code>System.out.println</code> 并显示结果，而是添加了一个 <code>assertEquals()</code> 方法。如果两个值不同， <code>assertEquals</code> 将打印出两个输入的值。您可能已经注意到这个方法不起作用！HelloWorld 中的 <code>sayHello()</code> 方法不返回字符串。如果我先写过测试，就会捕捉到这一点。我将 "Hello World" 字符串与输出流联结起来。这样，按照清单 3 中显示的那样重写了 HelloWorld，去掉 <code>main()</code> ，并更改了 <code>sayHello()</code> 的返回类型。 </p>
		<br />
		<a name="N10172">
				<b>清单 3. Hello world 测试案例。</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">package com.company;

public class HelloWorld {
    public String sayHello() {
        return "Hello World";
    }
}</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>如果我保留了 <code>main()</code> 并修改了联系，代码看上去如下： 
</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">  
 public static void main( String[] args ) {
        HelloWorld world = new HelloWorld();
        System.out.println(world.sayHello());
    }
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>新的 <code>main()</code> 与我测试程序中的 <code>testSayHello()</code> 非常相似。是的，它看上去不象是一个现实世界中的问题（这是人为示例的问题），但它说明了问题。在单独的应用程序中编写 <code>main()</code> 可以改进您的设计，同时帮助您设计测试。现在我们已经创建了一个测试类，让我们使用 Ant 来将它集成到构建中。 </p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="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/j-ant/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10196">
						<span class="atitle">
								<font face="Arial" size="4">使用 Ant 将测试集成到构建中</font>
						</span>
				</a>
		</p>
		<p>Jakarta Project 将 Ant 工具说成“不带 make 缺点的 make”。Ant 正在成为开放源代码世界中实际上的标准。原因很简单：Ant 是使用 Java 语言编写的，这种语言可以让构建过程在多种平台上使用。这种特性简化了在不同 OS 平台之间的程序员的合作，而合作是开放源代码社区的一种需要。您可以在自己选择的平台上进行开发 <i>和</i>构建。Ant 的特性包括： 
</p>
		<ul>
				<li>类可扩展性 Java 类可用于扩展构建特性，而不必使用基于 shell 的命令。 
</li>
				<li>开放源代码 因为 Ant 是开放源代码，因此类扩展示例很充足。我发现通过示例来学习非常棒。 
</li>
				<li>XML 可配置 Ant 不仅是基于 Java 的，它还使用 XML 文件配置构建过程。假设构建实际上是分层的，那么使用 XML 描述 make 过程就是其逻辑层。另外，如果您了解 XML，要学习如何配置构建就更简单一些。 </li>
		</ul>
		<p>
		</p>
		<p>图 2 简要介绍了一个配置文件。配置文件由目标树构成。每个目标都包含了要执行的任务，其中任务就是可以执行的代码。在本例中， <i>mkdir</i>是目标 <i>compile</i>的任务。 <i>mkdir</i>是建立在 Ant 中的一个任务，用于创建目录。 Ant 带有一套健全的内置任务。您也可以通过扩展 Ant 任务类来添加自己的功能。 </p>
		<p>每个目标都有唯一的名称和可选的相关性。目标相关性需要在执行目标任务列表之前执行。例如图 2 所示，在执行 compile 目标中的任务之前需要先运行 JUNIT 目标。这种类型的配置可以让您在一个配置中有多个树。</p>
		<br />
		<a name="N101BF">
				<b>图 2. Ant XML 构建图</b>
		</a>
		<br />
		<img height="237" alt="Ant XML 构建图" src="http://www-128.ibm.com/developerworks/cn/java/j-ant/image002.gif" width="598" />
		<br />
		<p>与经典 make 实用程序的相似性是非常显著的。这是理所当然的，因为 make 就是 make。但也要记住有一些差异：通过 Java 实现的跨平台和可扩展性，通过 XML 实现的可配置，还有开放源代码。</p>
		<p>
				<b>下载和安装 Ant</b>
				<br />首先下载 Ant（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-ant/#resources"><font color="#996699">参考资料</font></a> ）。将 Ant 解压缩到 tools 目录，再将 Ant <code>bin</code> 目录添加到路径中。（在我的机器上是 <code>e:\tools\ant\bin</code> 。）设置 ANT_HOME 环境变量。在 NT 中，这意味着进入系统属性，然后以带有值的变量形式添加 ANT_HOME。ANT_HOME 应该设置为 Ant 根目录，即包含 <code>bin</code> 和 <code>lib</code> 目录的目录。（对我来说，是 <code>e:\tools\ant</code> 。）确保 JAVA_HOME 环境变量设置为安装了 JDK 的目录。Ant 文档有关于安装的详细信息。 </p>
		<p>
				<b>下载和安装 JUnit</b>
				<br />下载 JUnit 3.2（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-ant/#resources"><font color="#996699">参考资料</font></a> ）。解开 <code>junit.zip</code> ，并将 <code>junit.jar</code> 添加到 CLASSPATH。如果将 <code>junit.zip</code> 解包到类路径中，可以通过运行以下命令来测试安装： <code>java junit.textui.TestRunner junit.samples.AllTests</code></p>
		<p>
				<b>定义目录结构</b>
				<br />在开始我们的构建和测试过程之前，需要一个项目布局。图 3 显示了我的样本项目的布局。下面描述了布局的目录结构： 
</p>
		<ul>
				<li>
						<code>build</code> -- 类文件的临时构建位置。构建过程将创建这个目录。 
</li>
				<li>
						<code>src</code> -- 源代码的位置。 <code>Src</code> 被分为 <code>test</code> 文件夹和 <code>main</code> 文件夹，前者用于所有的测试代码，而后者包含可交付的代码。将测试代码与主要代码分离提供了几点特性。首先，使主要代码中的混乱减少。其次，它允许包对齐。我就热衷与将类和与其相关的包放置在一起。测试就应该和测试在一起。它还有助于分发过程，因为你不可能打算将单元测试分发给客户。 </li>
		</ul>
		<p>
		</p>
		<p>在实际中，我们有多个目录，例如 <code>distribution</code> 和 <code>documentation</code> 。我们还会在 <code>main</code> 下有多个用于包的目录，例如 <code>com.company.util</code> 。 </p>
		<p>因为目录结构经常变动，所以在 <code>build.xml</code> 中有这些变动的全局字符串常数是很重要的。 </p>
		<br />
		<a name="N1024C">
				<b>图 3. 项目布局图</b>
		</a>
		<br />
		<img height="300" alt="项目布局图" src="http://www-128.ibm.com/developerworks/cn/java/j-ant/image003.gif" width="600" />
		<br />
		<p>
				<b>Ant 构建配置文件示例</b>
				<br />下一步，我们要创建配置文件。清单 4 显示了一个 Ant 构建文件示例。构建文件中的关键就是名为 runtests 的目标。这个目标进行分支判断并运行外部程序，其中外部程序是前面已安装的 <code>junit.textui.TestRunner</code> 。我们指定要使用语句 <code>test.com.company.AllJUnitTests</code> 来运行哪个测试套件。 </p>
		<br />
		<a name="N10269">
				<b>清单 4. 构建文件示例</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">    &lt;property name="app.name"   value="sample" /&gt;
    &lt;property name="build.dir"  value="build/classes" /&gt;

    &lt;target name="JUNIT"&gt;
        &lt;available property="junit.present" classname="junit.framework.TestCase" /&gt;
    &lt;/target&gt;

    &lt;target name="compile" depends="JUNIT"&gt;
        &lt;mkdir dir="${build.dir}"/&gt;
        &lt;javac srcdir="src/main/" destdir="${build.dir}" &gt;
            &lt;include name="**/*.java"/&gt;
        &lt;/javac&gt;
    &lt;/target&gt;

    &lt;target name="jar" depends="compile"&gt;
        &lt;mkdir dir="build/lib"/&gt;
        &lt;jar jarfile="build/lib/${app.name}.jar"
             basedir="${build.dir}" includes="com/**"/&gt;
    &lt;/target&gt;

    &lt;target name="compiletests" depends="jar"&gt;
        &lt;mkdir dir="build/testcases"/&gt;
        &lt;javac srcdir="src/test" destdir="build/testcases"&gt;
            &lt;classpath&gt;
                &lt;pathelement location="build/lib/${app.name}.jar" /&gt;
                &lt;pathelement path="" /&gt;
            &lt;/classpath&gt;
            &lt;include name="**/*.java"/&gt;
        &lt;/javac&gt;
    &lt;/target&gt;

    &lt;target name="runtests" depends="compiletests" if="junit.present"&gt;
        &lt;java fork="yes" classname="junit.textui.TestRunner"
            taskname="junit" failonerror="true"&gt;
            &lt;arg value="test.com.company.AllJUnitTests"/&gt;
            &lt;classpath&gt;
                &lt;pathelement location="build/lib/${app.name}.jar" /&gt;
                &lt;pathelement location="build/testcases" /&gt;
                &lt;pathelement path="" /&gt;
                &lt;pathelement path="${java.class.path}" /&gt;
            &lt;/classpath&gt;
        &lt;/java&gt;
    &lt;/target&gt;
&lt;/project&gt;

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<b>运行 Ant 构建示例</b>
				<br />开发过程中的下一步是运行将创建和测试 HelloWorld 类的构建。清单 5 显示了构建的结果，其中包括了各个目标部分。最酷的那部分是 runtests 输出语句：它告诉我们整个测试套件都正确运行了。 </p>
		<p>我在图 4 和图 5 中显示了 JUnit GUI，其中所要做的就是将 runtest 目标从 <code>junit.textui.TestRunner</code> 改为 <code>junit.ui.TestRunner</code> 。当您使用 JUnit 的 GUI 部分时，您必须选择退出按钮来继续构建过程。如果使用 Junit GUI 构建包，那么它将更难与大型的构建过程相集成。另外，文本输出也与构建过程更一致，并可以定向输出到一个用于主构建记录的文本文件。这对于每天晚上都要进行的构建非常合适。 </p>
		<br />
		<a name="N10286">
				<b>清单 5. 构建输出示例</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">E:\projects\sample&gt;ant runtests
Searching for build.xml ...
Buildfile: E:\projects\sample\build.xml

JUNIT:

compile:
    [mkdir] Created dir: E:\projects\sample\build\classes
    [javac] Compiling 1 source file to E:\projects\sample\build\classes

jar:
    [mkdir] Created dir: E:\projects\sample\build\lib
      [jar] Building jar: E:\projects\sample\build\lib\sample.jar

compiletests:
    [mkdir] Created dir: E:\projects\sample\build\testcases
    [javac] Compiling 3 source files to E:\projects\sample\build\testcases

runtests:
    [junit] ..
    [junit] Time: 0.031
    [junit]
    [junit] OK (2 tests)
    [junit]

BUILD SUCCESSFUL

Total time: 1 second </font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<a name="N10292">
				<b>图 4. JUnit GUI 测试成功</b>
		</a>
		<br />
		<img height="354" alt="JUnit GUI 测试成功" src="http://www-128.ibm.com/developerworks/cn/java/j-ant/image004.gif" width="435" />
		<br />
		<br />
		<a name="6">
				<b>图 5. JUnit GUI 测试失败</b>
		</a>
		<br />
		<img height="354" alt="JUnit GUI 测试失败" src="http://www-128.ibm.com/developerworks/cn/java/j-ant/image005.gif" width="435" />
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="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/j-ant/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N102AE">
						<span class="atitle">
								<font face="Arial" size="4">了解测试的工作原理</font>
						</span>
				</a>
		</p>
		<p>让我们搞点破坏，然后看看会发生什么事。夜深了，我们决定把 "Hello World" 变成一个静态字符串。在更改期间，我们 <i>不小心</i>打错了字母，将 "o" 变成了 "0"，如清单 6 所示。 </p>
		<br />
		<a name="N102BA">
				<b>清单 6. Hello world 类更改</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">package com.company;

public class HelloWorld {
    private final static String HELLO_WORLD = "Hell0 World";

    public String sayHello() {
        return HELLO_WORLD;
    }
}</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在构建包时，我们看到了错误。清单 7 显示了 runtest 中的错误。它显示了失败的测试类和测试方法，并说明了为什么会失败。我们返回到代码中，改正错误后离开。</p>
		<br />
		<a name="N102C7">
				<b>清单 7. 构建错误示例</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">E:\projects\sample&gt;ant runtests
Searching for build.xml ...
Buildfile: E:\projects\sample\build.xml

JUNIT:

compile:

jar:

compiletests:

runtests:
    [junit] ..F
    [junit] Time: 0
    [junit]
    [junit] FAILURES!!!
    [junit] Test Results:
    [junit] Run: 2 Failures: 1 Errors: 0
    [junit] There was 1 failure:
    [junit] 1) testSayHello(test.com.company.HelloWorldTest) "expected:&lt;Hello
               World&gt; but was:&lt;Hell0 World&gt;"
    [junit]

BUILD FAILED

E:\projects\sample\build.xml:35: Java returned: -1

Total time: 0 seconds
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<font face="Lucida Console">
												<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
												<br />
												<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<font face="Lucida Console">
												<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
												<br />
										</font>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<font face="Lucida Console">
																				<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																				<br />
																		</font>
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-ant/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="6a">
						<span class="atitle">
								<font face="Arial" size="4">并非完全无痛</font>
						</span>
				</a>
		</p>
		<p>新的过程并不是完全无痛的。为使单元测试成为开发的一部分，您必须采取以下几个步骤：</p>
		<ol>
				<li>下载和安装 JUnit。 
</li>
				<li>下载和安装 Ant。 
</li>
				<li>为构建创建单独的结构。 
</li>
				<li>实现与主类分开的测试类。 
</li>
				<li>学习 Ant 构建过程。 </li>
		</ol>
		<p>但好处远远超过了痛苦。通过使单元测试成为开发过程的一部分，您可以：</p>
		<ul>
				<li>自动验证以捕捉更改“臭虫” 
</li>
				<li>从接口角度设计类 
</li>
				<li>提供干净的示例 
</li>
				<li>在发行包中避免代码混乱和类膨胀。 </li>
		</ul>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-ant/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="7">
						<span class="atitle">
								<font face="Arial" size="4">实现 24x7</font>
						</span>
				</a>
		</p>
		<p>保证产品的质量要花费很多钱，但如果质量有缺陷，花费的钱就更多。如何才能使所花的钱获得最大价值，来保证产品质量呢？</p>
		<ul>
				<li>评审设计和代码。 评审可以达到的效果是单纯测试的一半。 
</li>
				<li>通过单元测试来确认模块可以使用。 尽管测试早就存在，但随着开发实践的不断发展，单元测试逐渐成为日常开发过程的一个部分。 </li>
		</ul>
		<p>在我 10 年的开发生涯里，为 emageon.com 工作是最重要的部分之一。在 emageon.com 时，设计评审、代码评审和单元测试是每天都要做的事。这种日常开发习惯造就了最高质量的产品。软件在客户地点第一年的当机次数为零，是一个真正的 24x7 产品。单元测试就象刷牙：您不一定要做，但如果做了，生活质量就更好。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-ant/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="resources">
						<span class="atitle">
								<font face="Arial" size="4">参考资料 </font>
						</span>
				</a>
		</p>
		<ul>
				<li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/library/j-ant/index.html"><font color="#5c81a7">英文原文</font></a>. <br /><br /></li>
				<li>下载在本文中引用的 <a href="ftp://www6.software.ibm.com/software/developerworks/library/antsample.zip"><font color="#5c81a7">示例代码</font></a>。 <br /><br /></li>
				<li>从 <a href="http://jakarta.apache.org/builds/ant/release/v1.1/bin/"><font color="#5c81a7">Apache 网站</font></a>下载 Ant。如需 Ant 文档、FAQ 和其他下载，请访问 Jakarta 项目的 <a href="http://jakarta.apache.org/ant/"><font color="#5c81a7">Ant 主页</font></a>。 <br /><br /></li>
				<li>
						<a href="http://www.junit.org/">
								<font color="#5c81a7">JUnit 主页</font>
						</a>提供了额外的测试示例、文档、文章和 FAQ。您可以从 <a href="http://www.xprogramming.com/ftp/TestingFramework/JUnit/junit32.zip"><font color="#5c81a7">www.xprogramming.com</font></a>下载 JUnit 3.2。 <br /><br /></li>
				<li>Kent Beck 所写的 <a href="http://www.xprogramming.com/testfram.htm"><font color="#5c81a7">“简单的 Smalltalk 测试”(Simple Smalltalk Testing)</font></a>讨论了一个简单的测试策略和支持它的框架。 <br /><br /></li>
				<li>请参阅其它开发者的 <a href="http://www.xprogramming.com/Practices/PracUnitTest.html"><font color="#5c81a7">有关单元测试的评论 (comments on unit testing)</font></a>。 <br /><br /></li>
				<li>要了解其它有用的开发习惯，请访问 <a href="http://www.extremeprogramming.org/"><font color="#5c81a7">终极编程主页 (Extreme Programming Home page)</font></a>。 <br /></li>
				<li>
						<p>
								<a name="author">
										<span class="atitle">
												<font face="Arial" size="4">关于作者</font>
										</span>
								</a>
						</p>
						<table cellspacing="0" cellpadding="0" width="100%" border="0">
								<tbody>
										<tr>
												<td colspan="3">
														<font face="Arial" size="4">
																<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
														</font>
												</td>
										</tr>
										<tr valign="top" align="left">
												<td>
														<p>
																<font face="Arial" size="4">
																</font>
														</p>
												</td>
												<td>
														<font face="Arial" size="4">
																<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" />
														</font>
												</td>
												<td width="100%">
														<p>Malcolm G. Davis 拥有自己的咨询公司，并任公司的总裁，该公司位于美国阿拉巴马州的伯明翰 (Birmingham)。他把自己看做是个 Java 传道者。在工作之余，他喜欢跑步，以及和他的孩子们一起玩耍。您可以通过 <a href="mailto:malcolm@nuearth.com"><font color="#5c81a7">malcolm@nuearth.com</font></a> 与 Malcolm 联系。 </p>
												</td>
										</tr>
								</tbody>
						</table>
				</li>
		</ul><img src ="http://www.cnitblog.com/tilan/aggbug/19773.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-11-30 18:09 <a href="http://www.cnitblog.com/tilan/articles/19773.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Eclipse快速上手指南之使用Ant</title><link>http://www.cnitblog.com/tilan/articles/19772.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Thu, 30 Nov 2006 10:08:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/19772.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/19772.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/19772.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/19772.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/19772.html</trackback:ping><description><![CDATA[<br /><a href="http://www.javafan.net/special/ant/index.jsp" target="_blank">Ant</a> 是Java平台下非常棒的批处理命令执行程序，能自动完成编译，测试，打包，部署等等一系列任务。
<p>　　Ant是Java平台下非常棒的批处理命令执行程序，能非常方便地自动完成编译，测试，打包，部署等等一系列任务，大大提高开发效率。如果你现在还没有开始使用Ant，那就要赶快开始学习使用，使自己的开发水平上一个新台阶。</p><p>　　<a href="http://www.javafan.net/special/eclipse/index.jsp" target="_blank">Eclipse</a> 中已经集成了Ant，我们可以直接在Eclipse中运行Ant。</p><p>　　以前面建立的Hello工程为例，创建以下目录结构：</p><p align="center"><img height="315" src="http://www.javafan.net/uploadfiles/20041220112701100.jpg" width="236" /></p><p>　　新建一个build.xml，放在工程根目录下。build.xml定义了Ant要执行的批处理命令。虽然Ant也可以使用其它文件名，但是遵循标准能更使开发更规范，同时易于与别人交流。</p><p>　　通常，src存放Java源文件，classes存放编译后的class文件，lib存放编译和运行用到的所有jar文件，web存放JSP等web文件，dist存放打包后的jar文件，doc存放API文档。</p><p>　　然后在根目录下创建build.xml文件，输入以下内容： </p><p style="BACKGROUND: #eeeeee">&lt;xml version="1.0"?&gt;<br />&lt;roject name="Hello world" default="doc"&gt;<br /><br />&lt;-- properies --&gt;<br />&lt;roperty name="src.dir" value="src" /&gt;<br />&lt;roperty name="report.dir" value="report" /&gt;<br />&lt;roperty name="classes.dir" value="classes" /&gt;<br />&lt;roperty name="lib.dir" value="lib" /&gt;<br />&lt;roperty name="dist.dir" value="dist" /&gt;<br />&lt;roperty name="doc.dir" value="doc"/&gt;<br /><br />&lt;-- 定义classpath --&gt;<br />&lt;ath id="master-classpath"&gt;<br />&lt;ileset file="${lib.dir}/*.jar" /&gt;<br />&lt;athelement path="${classes.dir}"/&gt;<br />&lt;path&gt;<br /><br />&lt;-- 初始化任务 --&gt;<br />&lt;arget name="init"&gt;<br />&lt;target&gt;<br /><br />&lt;-- 编译 --&gt;<br />&lt;arget name="compile" depends="init" description="compile the source files"&gt;<br />&lt;kdir dir="${classes.dir}"/&gt;<br />&lt;avac srcdir="${src.dir}" destdir="${classes.dir}" target="1.4"&gt;<br />&lt;lasspath refid="master-classpath"/&gt;<br />&lt;javac&gt;<br />&lt;target&gt;<br /><br />&lt;-- 测试 --&gt;<br />&lt;arget name="test" depends="compile" description="run junit test"&gt;<br />&lt;kdir dir="${report.dir}"/&gt;<br />&lt;unit printsummary="on"<br />haltonfailure="false"<br />failureproperty="tests.failed"<br />showoutput="true"&gt;<br />&lt;lasspath refid="master-classpath" /&gt;<br />&lt;ormatter type="plain"/&gt;<br />&lt;atchtest todir="${report.dir}"&gt;<br />&lt;ileset dir="${classes.dir}"&gt;<br />&lt;nclude name="**/*Test.*"/&gt;<br />&lt;fileset&gt;<br />&lt;batchtest&gt;<br />&lt;junit&gt;<br />&lt;ail if="tests.failed"&gt;<br />***********************************************************<br />**** One or more tests failed! Check the output ... ****<br />***********************************************************<br />&lt;fail&gt;<br />&lt;target&gt;<br /><br />&lt;-- 打包成jar --&gt;<br />&lt;arget name="pack" depends="test" description="make .jar file"&gt;<br />&lt;kdir dir="${dist.dir}" /&gt;<br />&lt;ar destfile="${dist.dir}/hello.jar" basedir="${classes.dir}"&gt;<br />&lt;xclude name="**/*Test.*" /&gt;<br />&lt;xclude name="**/Test*.*" /&gt;<br />&lt;jar&gt;<br />&lt;target&gt;<br /><br />&lt;-- 输出api文档 --&gt;<br />&lt;arget name="doc" depends="pack" description="create api doc"&gt;<br />&lt;kdir dir="${doc.dir}" /&gt;<br />&lt;avadoc destdir="${doc.dir}"<br />author="true"<br />version="true"<br />use="true"<br />windowtitle="Test API"&gt;<br />&lt;ackageset dir="${src.dir}" defaultexcludes="yes"&gt;<br />&lt;nclude name="example/**" /&gt;<br />&lt;packageset&gt;<br />&lt;octitle&gt;[CDATA[&lt;1&gt;ello, test&lt;h1&gt;]&gt;doctitle&gt;<br />&lt;ottom&gt;[CDATA[&lt;&gt;ll Rights Reserved.&lt;i&gt;]&gt;bottom&gt;<br />&lt;ag name="todo" scope="all" description="To do:" /&gt;<br />&lt;javadoc&gt;<br />&lt;target&gt;<br />&lt;project&gt;</p><p>　　以上xml依次定义了init（初始化），compile（编译），test（测试），doc（生成文档），pack（打包）任务，可以作为模板。</p><p>　　选中Hello工程，然后选择“Project”，“Properties”，“Builders”，“New…”，选择“Ant Build”：</p><p align="center"><img height="546" src="http://www.javafan.net/uploadfiles/20041220112701200.jpg" width="604" /></p><p>　　填入Name：Ant_Builder；Buildfile：build.xml；Base Directory：${workspace_loc:/Hello}（按“Browse Workspace”选择工程根目录），由于用到了junit.jar包，搜索Eclipse目录，找到junit.jar，把它复制到Hello/lib目录下，并添加到Ant的Classpath中：</p><p align="center"><img height="546" src="http://www.javafan.net/uploadfiles/20041220112701300.jpg" width="604" /></p><p>　　然后在Builder面板中钩上Ant_Build，去掉Java Builder：</p><p align="center"><img height="283" src="http://www.javafan.net/uploadfiles/20041220112701400.jpg" width="459" /></p><p>　　再次编译，即可在控制台看到Ant的输出：</p><p style="BACKGROUND: #eeeeee">Buildfile: F:\eclipse-projects\Hello\build.xml<br /><br />init:<br /><br />compile:<br />[mkdir] Created dir: F:\eclipse-projects\Hello\classes<br />[javac] Compiling 2 source files to F:\eclipse-projects\Hello\classes<br /><br />test:<br />[mkdir] Created dir: F:\eclipse-projects\Hello\report<br />[junit] Running example.HelloTest<br />[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.02 sec<br /><br />pack:<br />[mkdir] Created dir: F:\eclipse-projects\Hello\dist<br />[jar] Building jar: F:\eclipse-projects\Hello\dist\hello.jar<br /><br />doc:<br />[mkdir] Created dir: F:\eclipse-projects\Hello\doc<br />[javadoc] Generating Javadoc<br />[javadoc] Javadoc execution<br />[javadoc] Loading source files for package example...<br />[javadoc] Constructing Javadoc information...<br />[javadoc] Standard Doclet version 1.4.2_04<br />[javadoc] Building tree for all the packages and classes...<br />[javadoc] Building index for all the packages and classes...<br />[javadoc] Building index for all classes...<br />[javadoc] Generating F:\eclipse-projects\Hello\doc\stylesheet.css...<br />[javadoc] Note: Custom tags that could override future standard tags: @todo. To avoid potential overrides, use at least one period character (.) in custom tag names.<br />[javadoc] Note: Custom tags that were not seen: @todo<br />BUILD SUCCESSFUL<br />Total time: 11 seconds </p><p>　　Ant依次执行初始化，编译，测试，打包，生成API文档一系列任务，极大地提高了开发效率。将来开发J2EE项目时，还可加入部署等任务。并且，即使脱离了Eclipse环境，只要正确安装了Ant，配置好环境变量ANT_HOME=&lt;nt解压目录&gt;Path=…;%ANT_HOME%\bin，在命令行提示符下切换到Hello目录，简单地键入ant即可。<br /><br />来自：J2ME开发网</p><img src ="http://www.cnitblog.com/tilan/aggbug/19772.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-11-30 18:08 <a href="http://www.cnitblog.com/tilan/articles/19772.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA开发环境配置</title><link>http://www.cnitblog.com/tilan/articles/19653.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 27 Nov 2006 09:17:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/19653.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/19653.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/19653.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/19653.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/19653.html</trackback:ping><description><![CDATA[最近上网逛论坛看见了许多帖子还在问关于Java环境相关的配置问题，这也是出学者的一大困惑。在这我写了一点，希望对读者有用。<br /><br />        其实我刚学习Java的时候也被各种环境配置搞得头晕脑胀，现在把自己平时用到的整理一下，希望给大家一些帮助。<br />　　<br />　　<b>安装JDK</b><br />　　<br />　　从http://Java.sun.com/下载jdk-1_5_0_04-windows-i586-p.exe<br />　　<br />　　安装到指定路径，我选择D:\jdk1.5.0<br />　　<br />　　配置环境变量：<br />　　<br />　　Java_HOME: D:\jdk1.5.0<br />　　PATH: D:\jdk1.5.0\bin;<br />　　CLASSPATH: .;D:\jdk1.5.0\lib\tools.jar;<br />　　D:\jdk1.5.0\jre\lib\rt.jar;<br />　　<br />　　<b>安装WTK</b><br />　　<br />　　从http://Java.sun.com/下载j2me_wireless_toolkit-2_2-windows.exe<br />　　<br />　　安装到指定路径，我选择D:\WTK22<br />　　<br />　　<b>安装Eclipse</b><br />　　<br />　　从http://www.eclipse.org/下载eclipse-SDK-3.0.1-win32.zip和<br />　　<br />　　NLpack-eclipse-SDK-3.0.x-win32.zip（语言包）<br />　　<br />　　解压缩eclipse-SDK-3.0.1-win32.zip即可，我的路径：D:\MyDevelopTools\eclipse<br />　　<br />　　解压缩NLpack-eclipse-SDK-3.0.x-win32.zip,得到features和plugins两个文件夹，把里面的文件分别拷入eclipse中相应的目录下即可<br />　　<br />　　<b>安装Tomcat</b><br />　　<br />　　从http://jakarta.apache.org/下载jakarta-tomcat-5.5.9.zip<br />　　<br />　　解压缩jakarta-tomcat-5.5.9.zip即可<br />　　<br />　　<b>配置环境变量：</b><br />　　<br />　　Tomcat_Home: D:\MyDevelopTools\tomcat-5.5.9<br />　　<br />　　PATH: D:\MyDevelopTools\tomcat-5.5.9;<br />　　<br />　　在eclipse中配置J2ME开发环境：<br />　　<br />　　<b>安装eclipseme:</b><br />　　<br />　　从http://www.eclipseme.org/下载eclipseme.feature_0.9.4_site.zip，在eclipse中选择帮助-〉软件更新-〉查找并安装-〉搜索要安装的新功能部件-〉新建已归档的站点。选择eclipseme.feature_0.9.4_site.zip，打开-〉选择eclipseme.feature_0.9.4_site.zip，剩下的一直下一步就可以了。安装完成会在窗口-〉首选项中出现J2ME<br />　　<br />　　修改Java-〉调试：<br />　　<br />　　选中Java-〉调试，把暂挂执行的前两项点去，通信中的调试器超时改为15000<br />　　<br />　　<b>配置WTK</b><br />　　<br />　　窗口-〉首选项-〉J2ME-〉Platform Components<br />　　<br />　　右键单击对话框右侧的Wireless Toolkit，选择Add Wireless Toolkit,<br />　　<br />　　选择WTK安装目录，eclipse会自动匹配。<br />　　<br />　　在eclipse中配置J2EE开发环境(Tomcat5.5.9)：<br />　　<br />　　<b>安装EMF-RunTime:</b><br />　　<br />　　从http://www.eclipseme.org/下载emf-sdo-runtime-2.0.1.zip<br />　　<br />　　解压缩emf-sdo-runtime-2.0.1.zip,得到features和plugins两个文件夹，把里面的文件分别拷入eclipse中相应的目录下即可。<br />　　<br />　　<b>安装Lomboz：</b><br />　　<br />　　从http://forge.objectweb.org下载org.objectweb.lomboz_3.0.1.N20050106.zip解压缩org.objectweb.lomboz_3.0.1.N20050106.zip,得到features和plugins两个文件夹，把里面的文件分别拷入eclipse中相应的目录下。如果在窗口-〉首选项中有Lomboz选项就安装正确，如果没有，在D:\eclipse\configuration\下删除org.eclipse.update这个文件夹,再重起eclipse就可以了。<br />　　<br />　　<b>配置Lomboz:</b><br />　　<br />　　在D:\eclipse\plugins\com.objectlearn.jdt.j2ee_3.0.1\servers下新建一个文件tomcat559.server，里面的内容从tomcat50x.server全部复制过来，把name="Apache Tomcat v5.0.x"替换成name="Apache Tomcat v5.5.9"，然后把所有的<br />　　<br />　　“${serverRootDirectory}/bin;${serverRootDirectory}/common/endorsed”替换成<br />　　<br />　　“${serverRootDirectory}/common/endorsed”就可以了。然后进入eclipse，窗口-〉首选项-〉Lomboz，把JDK Tools.jar改为：D:\jdk1.5.0\lib\tools.jar，窗口-〉首选项-〉Lomboz-〉Server Definitions,在Server types中选择Tomcat5.5.9在Application Server Directory和Classpath Variable的路径改为D:/MyDevelopTools/tomcat-5.5.9先应用，再确定就可以了。<img src ="http://www.cnitblog.com/tilan/aggbug/19653.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-11-27 17:17 <a href="http://www.cnitblog.com/tilan/articles/19653.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java开源JMS框架</title><link>http://www.cnitblog.com/tilan/articles/19365.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 20 Nov 2006 08:42:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/19365.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/19365.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/19365.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/19365.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/19365.html</trackback:ping><description><![CDATA[<p>
				<font color="#ff0000">
						<strong>XMOJO  </strong>
				</font>
				<br />
				<br />XMOJO免费的open source的JMX1.0规范的实现,该实现继承了AdventNet的JMX实现,XMOJO包含了各种适配器比如像HTML和RMI以及象MBean Browser工具等。<br /><img height="16" alt="主页" src="http://www.open-open.com/image/home_16.gif" width="16" border="0" /> <a href="http://www.xmojo.org/">http://www.xmojo.org</a></p>
		<p>
				<strong> </strong>
				<font color="#ff0000">
						<strong>MX4J</strong>  </font>
				<br />
				<br />MX4J是Java管理扩展技术的一个开源实现，支持JSR3 (JMX) 和JSR160 (JMX Remote API). <br /> <img height="16" alt="主页" src="http://www.open-open.com/image/home_16.gif" width="16" border="0" /> <a href="http://mx4j.sourceforge.net/">http://mx4j.sourceforge.net/</a><br /></p>
		<p>
				<font color="#ff0000"> <strong>MC4J</strong></font>
				<strong> </strong>
				<br />
				<br />这个软件允许你连接到JMX服务器，并而提供浏览现有managed beans(MBeans)并可以对其时行操作。 <br /><img height="16" alt="主页" src="http://www.open-open.com/image/home_16.gif" width="16" border="0" /> <a href="http://mc4j.sourceforge.net/">http://mc4j.sourceforge.net/</a><br /><br /><strong> <font color="#ff0000">JFoxMX</font></strong>   <br /><br />JFoxMX是国内第一个完整实现SUN JMX 1.2 规范的产品，实现了SUN JMX 最新的 1.2 规范，JMX 1.2 规范。<br /><img height="16" alt="主页" src="http://www.open-open.com/image/home_16.gif" width="16" border="0" /> <a href="http://www.huihoo.org/jfox/jfoxmx/jfoxmx.html">http://www.huihoo.org/jfox/jfoxmx/jfoxmx.html</a></p>
		<p> <font color="#ff0000"><strong>JMX4Ant </strong></font> <br /><br />JMX4Ant是ANT中一组可选的任务，允许与JMX MBeans相互作用。<br /> <img height="16" alt="主页" src="http://www.open-open.com/image/home_16.gif" width="16" border="0" /> <a href="http://jmx4ant.sourceforge.net/">http://jmx4ant.sourceforge.net/</a><br /><br /><strong> </strong><font color="#ff0000"><strong>Panoptes</strong> </font><br /><br />Panoptes是一个基于JFace/SWT的图形化JMX Management 管理界面。Panoptes能够工作在任何JMX服务器如jboss,Tomcat 或其它J2EE服务器。<br /><img height="16" alt="主页" src="http://www.open-open.com/image/home_16.gif" width="16" border="0" /> <a href="http://panoptesmgmt.sourceforge.net/">http://panoptesmgmt.sourceforge.net/</a></p>
		<p>
				<strong> <font color="#ff0000">EJTools JMX Browser</font></strong>   <br /><br />EJTools JMX Browser提供两种不同的风格来与JMX服务器相互作用:</p>
		<p>* 一个是基于Swing的应用程序通过RMI/IIOP与JMX服务器进行远程交互.  <br />* 一个是基于Web的可以运行在J2EE1.3 WEB容器的应用程序,它提供一种简单的方式来与JMX服务器相互作用.<br /> <img height="16" alt="主页" src="http://www.open-open.com/image/home_16.gif" width="16" border="0" /> <a href="http://ejtools.sourceforge.net/applications/jmx.browser/">http://ejtools.sourceforge.net/applications/jmx.browser/</a><br /><br /></p>
		<p>
				<font color="#ff0000"> <strong>jManage</strong></font>   <br /><br />jManage一个基于Web和命令行的JMX客户端。它提供统一的控制台来管理应用程序群与分布式应用程序构建的环境。jManage支持以下特性：<br />1，用来穿过防火墙基于Web的接口;<br />2，安全性：应用服务器密码将被加密；<br />3，支持各种不同的J2EE应用服务器（weblogic，websphere，jboss）；<br />4，命令行的UI；<br />5，基于ACL细粒度访问控制等。<br /> <img height="16" alt="主页" src="http://www.open-open.com/image/home_16.gif" width="16" border="0" /> <a href="http://www.jmanage.org/">http://www.jmanage.org/</a></p><img src ="http://www.cnitblog.com/tilan/aggbug/19365.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-11-20 16:42 <a href="http://www.cnitblog.com/tilan/articles/19365.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发工具--==Eclipse</title><link>http://www.cnitblog.com/tilan/articles/19364.html</link><dc:creator>Joinclass Inc</dc:creator><author>Joinclass Inc</author><pubDate>Mon, 20 Nov 2006 08:32:00 GMT</pubDate><guid>http://www.cnitblog.com/tilan/articles/19364.html</guid><wfw:comment>http://www.cnitblog.com/tilan/comments/19364.html</wfw:comment><comments>http://www.cnitblog.com/tilan/articles/19364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tilan/comments/commentRss/19364.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tilan/services/trackbacks/19364.html</trackback:ping><description><![CDATA[<span class="style7">
				<font face="宋体" size="2">Eclipse平台是IBM向开发源码社区捐赠的开发框架，它之所以出名并不是因为IBM宣称投入开发的资金总数 —4千万美元，而是因为如此巨大的投入所带来的成果：一个成熟的、精心设计的以及可扩展的体系结构。 <br /><br /></font>
		</span>
		<font color="#002c99">
				<img height="16" alt="主页" src="http://www.open-open.com/image/home_16.gif" width="16" border="0" />
				<font color="#000000"> </font>
				<a href="http://www.eclipse.org/">
						<font color="#002c99">http://www.eclipse.org</font>
				</a>
		</font><img src ="http://www.cnitblog.com/tilan/aggbug/19364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tilan/" target="_blank">Joinclass Inc</a> 2006-11-20 16:32 <a href="http://www.cnitblog.com/tilan/articles/19364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>