<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>程序人生—路漫漫</title>
    <description>快乐编程！快乐生活！</description>
    <link>http://coolyongzi.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>Eclipse下使用Subversion</title>
        <author>coolyongzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coolyongzi.javaeye.com">coolyongzi</a>&nbsp;
          链接：<a href="http://coolyongzi.javaeye.com/blog/205285" style="color:red;">http://coolyongzi.javaeye.com/blog/205285</a>&nbsp;
          发表时间: 2008年06月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          CVS很酷，但Subversion更酷。然而，如果你在使用Eclipse进行开发，那么你可能直到近来才能利用Subversion带来的优点<br />摘要 CVS很酷，但Subversion更酷。然而，如果你在使用Eclipse进行开发，那么你可能直到近来才能利用Subversion带来的优点。随着Subclipse的发行，Subversion可能会最终在你的Eclipse IDE环境充分发挥其威力而压倒CVS。<br />一、SCM和Subversion简介<br /><br />　　软件配置管理(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 />　　二、Subclipse插件与Eclipse的集成<br /><br />　　一种良好的SCM应该与你的工作环境紧密地集成到一起。没有谁真正喜欢转到命令行以把文件添加到仓库。Eclipse很早就实现了CVS集成，但是直到最近Subversion用户仍没有被引起重视。现在，新的Subclipse插件提供了在Eclipse中的一种平滑的Subversion集成。<br /><br />　　(一) 安装Subclipse插件<br /><br />　　下面，你以通常的方法从更新站点下安装Subclipse：<br /><br />　　1. 打开"Find and install"窗口（"Help>Software Updates>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 /><br />图1.安装Subclipse插件 <br /><br />(二) 建立Repository定义<br /><br />　　现在，既然你已经安装完插件；那么，接下来，你需要告诉它你的工程仓库位于何处。你是在SVN Repository视图中实现的。打开这个视图（"Windows>Show View>Other>SVN Repository"）并且在上下文菜单中选择"New>Repository Location"以显示一个如图2所示的对话框。输入适当的URL并且点击"Finish"。<br /><br /><br />图2.添加一个仓库定义 <br /><br /><br />　　(三) 检出（Check Out）一个工程<br /><br />　　一旦建立一个仓库，你就可以在SVN Repository视图中浏览所有的内容(见图3)。我们后面将会看到，这个视图是一种与Subversion进行交互的非常方便的方式。<br /><br /><br /><br />图3.SVN Repository视图。<br /><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不能确定这些Java源码所在的位置。<br /><br />　　(四) 把一个新工程导入到仓库中<br /><br />　　如果你只是启动了一个新的工程，那么你需要把它导入到Subversion仓库。Subclipse提供了一种方便的方式来直接从你的IDE内部实现这一点。为此，只需要从Package Explorer视图下选择你的工程，并且在上下文菜单中选择"Team>Share Project"。你可以使用现有仓库之一或创建一新的仓库定义。在你指定仓库和工程名之后，你能指定你想放到仓库中的文件和目录并且提供一个初始注释(见图4)。这种方法特别有用，因为它让你有选择地导入仅由Subversion管理的文件，即使该工程还包含其它文件（例如生成的类，临时文件或其它不是必需的内容等）。 <br /><br /><br /><br />图4.把一个工程导入到一个Subversion仓库中 <br /><br />三、在Eclipse中使用Subversion<br /><br />　　现在，既然你的支持Subversion的工程已经启动并且运行起来，那么大多数必要的Subversion命令就可经由"Team"上下文菜单存取(参考图5)。你可以在Package Explorer中看到你的本地文件的状态(参考图6)，其中，任何修改了的文件都被标记上一个星号。存储在仓库中的文件都显示一个小黄桶图标(代表了一个数据库)；还没有被添加到仓库中的文件以一个问号显示。<br /><br /><br />图5.大多数Subversion命令能被经由Team菜单存取<br /><br />图6.你可以在Package Explorer中看到本地文件的状态 <br /><br />　　(一) 与Repository保持同步<br /><br />　　从仓库中更新你的文件并且把你的变化提交到仓库是相当直接的过程，这可以使用"Team>Update and Team>Commit"菜单选项来实现。在提交你的变化之前，你可能想看一下自从你的上次更新以来是否服务器上有任何文件被修改。为此，你可以使用"Team >Synchronize with Repository"。这个命令让你看到有哪些内容已经被局部地修改，有哪些内容在服务器上修改，以及这两种修改之间的任何冲突（参考图7)。你还可以以可视化方式看到冲突的版本，并且在提交你的变化之前纠正任何比较突出的冲突。<br /><br /><br />图7.与仓库保持同步 <br /><br />　　(二) 使用属性<br /><br />　　属性是Subversion具有创新性的特征之一。在Subversion中，你可以把元数据（"properties"）关联到任何文件或目录。你可以定义任何你喜欢的属性，但是Subversion也提供了一些有用的内置属性，例如下面图8中所提供的这些属性：<br /><br />　　· svn:executable属性，允许你在支持这种能力的操作系统上设置一个文件的可执行标志。<br /><br />　　· svn:need-lock属性，可以用来在文件（例如，对二进制文件非常有用）上强加排斥锁。一个定义了svn:need-lock属性的文件一次只能被一个人修改。当该文件被检出时，它是只读的。如果你想修改该文件，你需要首先使用"Team>Lock"菜单选项。之后，使用"Team>Unlock"释放该文件，或仅提交你的变化。这一行为将释放该锁并且让其它的用户也得到该文件上的一把锁。 <br /><br /><br />图8.把一个Subversion属性添加到一个文件中 <br /><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>Branch/Tag"菜单能够使你创建branch和tag(参考图9)。其中，Browse按钮提供了一种方便的方法来查看有哪些branch和tag存在于仓库中。<br /><br />　　当你使用"Team>Switch"创建成功一个新的branch或tag时，你可以非常容易地在branches之间进行切换。无论何时你切换到一个不同的branch(或返回到trunk)，Subversion将仅更新文件（它需要保持你的当前工作的副本与目的branch之间的同步）。<br /><br /><br />图9.创建一个新的branch或tag <br /><br />　　(四) 修订历史<br /><br />　　象大多数SCM系统一样，Subversion让你跟踪你的源码的变化。"Team>Show in Resource History"菜单选项能够使你查询这些变化的列表（包括对一个文件，目录或甚至整个工程的改变）(见图10)。<br /><br />　　记住，在Subversion中，提交是原子性的-一次提交由一组文件变化和一个全局注释组成。"SVN Resource History"视图向你显示每一次提交的一个简明视图，包括修改的文件和相关注释。<br /><br /><br />图10.历史资源 <br /><br />　　四、结论<br /><br />　　Subversion是一种强有力的和非常灵活的SCM工具，也是CVS的一个成功的后继者。结合Subclipse，Subversion能最终在你的Eclipse IDE环境中得到全面的发挥。
          <br/>
          <span style="color:red;">
            <a href="http://coolyongzi.javaeye.com/blog/205285#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 18 Jun 2008 18:25:12 +0800</pubDate>
        <link>http://coolyongzi.javaeye.com/blog/205285</link>
        <guid>http://coolyongzi.javaeye.com/blog/205285</guid>
      </item>
      <item>
        <title>使用spring2.5注释驱动的IOC功能</title>
        <author>coolyongzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coolyongzi.javaeye.com">coolyongzi</a>&nbsp;
          链接：<a href="http://coolyongzi.javaeye.com/blog/192957" style="color:red;">http://coolyongzi.javaeye.com/blog/192957</a>&nbsp;
          发表时间: 2008年05月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          概述 <br /><br />注释配置相对于 XML 配置具有很多的优势： <br />它可以充分利用 Java 的反射机制获取类结构信息，这些信息可以有效减少配置的工作。如使用 JPA 注释配置 ORM 映射时，我们就不需要指定 PO 的属性名、类型等信息，如果关系表字段和 PO 属性名、类型都一致，您甚至无需编写任务属性映射信息——因为这些信息都可以通过 Java 反射机制获取。 <br />注释和 Java 代码位于一个文件中，而 XML 配置采用独立的配置文件，大多数配置信息在程序开发完成后都不会调整，如果配置信息和 Java 代码放在一起，有助于增强程序的内聚性。而采用独立的 XML 配置文件，程序员在编写一个功能时，往往需要在程序文件和配置文件中不停切换，这种思维上的不连贯会降低开发效率。 <br />因此在很多情况下，注释配置比 XML 配置更受欢迎，注释配置有进一步流行的趋势。Spring 2.5 的一大增强就是引入了很多注释类，现在您已经可以使用注释配置完成大部分 XML 配置的功能。在这篇文章里，我们将向您讲述使用注释进行 Bean 定义和依赖注入的内容。<br /><br />原来我们是怎么做的 <br />在使用注释配置之前，先来回顾一下传统上是如何配置 Bean 并完成 Bean 之间依赖关系的建立。下面是 3 个类，它们分别是 Office、Car 和 Boss，这 3 个类需要在 Spring 容器中配置为 Bean： <br /><br />Office 仅有一个属性： <br />清单 1. Office.java <br /><pre name="code" class="java">package com.baobaotao;   
public class Office {   
    private String officeNo =”001”;   
  
    //省略 get/setter   
  
    @Override  
    public String toString() {   
        return "officeNo:" + officeNo;   
    }   
}  </pre><br />Car 拥有两个属性： <br />清单 2. Car.java <br /><pre name="code" class="java">package com.baobaotao;   
  
public class Car {   
    private String brand;   
    private double price;   
  
    // 省略 get/setter   
  
    @Override  
    public String toString() {   
        return "brand:" + brand + "," + "price:" + price;   
    }   
} </pre><br />Boss 拥有 Office 和 Car 类型的两个属性： <br />清单 3. Boss.java <br /><pre name="code" class="java">package com.baobaotao;   
  
public class Boss {   
    private Car car;   
    private Office office;   
  
    // 省略 get/setter   
  
    @Override  
    public String toString() {   
        return "car:" + car + "\n" + "office:" + office;   
    }   
}  </pre><br />我们在 Spring 容器中将 Office 和 Car 声明为 Bean，并注入到 Boss Bean 中：下面是使用传统 XML 完成这个工作的配置文件 beans.xml： <br />清单 4. beans.xml 将以上三个类配置成 Bean <br /><pre name="code" class="java">&lt;?xml version="1.0" encoding="UTF-8" ?>  
&lt;beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">  
    &lt;bean id="boss" class="com.baobaotao.Boss">  
        &lt;property name="car" ref="car"/>  
        &lt;property name="office" ref="office" />  
    &lt;/bean>  
    &lt;bean id="office" class="com.baobaotao.Office">  
        &lt;property name="officeNo" value="002"/>  
    &lt;/bean>  
    &lt;bean id="car" class="com.baobaotao.Car" scope="singleton">  
        &lt;property name="brand" value=" 红旗 CA72"/>  
        &lt;property name="price" value="2000"/>  
    &lt;/bean>  
&lt;/beans>  </pre><br />当我们运行以下代码时，控制台将正确打出 boss 的信息： <br />清单 5. 测试类：AnnoIoCTest.java <br /><pre name="code" class="java">import org.springframework.context.ApplicationContext;   
import org.springframework.context.support.ClassPathXmlApplicationContext;   
public class AnnoIoCTest {   
  
    public static void main(String[] args) {   
        String[] locations = {"beans.xml"};   
        ApplicationContext ctx =    
            new ClassPathXmlApplicationContext(locations);   
        Boss boss = (Boss) ctx.getBean("boss");   
        System.out.println(boss);   
    }   
} </pre><br />这说明 Spring 容器已经正确完成了 Bean 创建和装配的工作。 <br /><br /><br />使用 @Autowired 注释 <br />Spring 2.5 引入了 @Autowired 注释，它可以对类成员变量、方法及构造函数进行标注，完成自动装配的工作。来看一下使用 @Autowired 进行成员变量自动注入的代码：<br />清单 6. 使用 @Autowired 注释的 Boss.java <br /><pre name="code" class="java">package com.baobaotao;   
import org.springframework.beans.factory.annotation.Autowired;   
  
public class Boss {   
  
    @Autowired  
    private Car car;   
  
    @Autowired  
    private Office office;   
  
    …   
} </pre><br />Spring 通过一个 BeanPostProcessor 对 @Autowired 进行解析，所以要让 @Autowired 起作用必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean。 <br />清单 7. 让 @Autowired 注释工作起来 <br /><pre name="code" class="java">&lt;?xml version="1.0" encoding="UTF-8" ?>  
&lt;beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">  
  
    &lt;!-- 该 BeanPostProcessor 将自动起作用，对标注 @Autowired 的 Bean 进行自动注入 -->  
    &lt;bean class="org.springframework.beans.factory.annotation.   
        AutowiredAnnotationBeanPostProcessor"/>  
  
    &lt;!-- 移除 boss Bean 的属性注入配置的信息 -->  
    &lt;bean id="boss" class="com.baobaotao.Boss"/>  
    
    &lt;bean id="office" class="com.baobaotao.Office">  
        &lt;property name="officeNo" value="001"/>  
    &lt;/bean>  
    &lt;bean id="car" class="com.baobaotao.Car" scope="singleton">  
        &lt;property name="brand" value=" 红旗 CA72"/>  
        &lt;property name="price" value="2000"/>  
    &lt;/bean>  
&lt;/beans> </pre><br />这样，当 Spring 容器启动时，AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean，当发现 Bean 中拥有 @Autowired 注释时就找到和其匹配（默认按类型匹配）的 Bean，并注入到对应的地方中去。 <br /><br />按照上面的配置，Spring 将直接采用 Java 反射机制对 Boss 中的 car 和 office 这两个私有成员变量进行自动注入。所以对成员变量使用 @Autowired 后，您大可将它们的 setter 方法（setCar() 和 setOffice()）从 Boss 中删除。 <br /><br />当然，您也可以通过 @Autowired 对方法或构造函数进行标注，来看下面的代码<br />清单 8. 将 @Autowired 注释标注在 Setter 方法上 <br /><br /><pre name="code" class="java">package com.baobaotao;   
  
public class Boss {   
    private Car car;   
    private Office office;   
  
     @Autowired  
    public void setCar(Car car) {   
        this.car = car;   
    }   
    
    @Autowired  
    public void setOffice(Office office) {   
        this.office = office;   
    }   
    …   
}  </pre><br />这时，@Autowired 将查找被标注的方法的入参类型的 Bean，并调用方法自动注入这些 Bean。而下面的使用方法则对构造函数进行标注：<br />清单 9. 将 @Autowired 注释标注在构造函数上<br /><pre name="code" class="java">package com.baobaotao;   
  
public class Boss {   
    private Car car;   
    private Office office;   
    
    @Autowired  
    public Boss(Car car ,Office office){   
        this.car = car;   
        this.office = office ;   
    }   
    
    …   
}  </pre><br />由于 Boss() 构造函数有两个入参，分别是 car 和 office，@Autowired 将分别寻找和它们类型匹配的 Bean，将它们作为 Boss(Car car ,Office office) 的入参来创建 Boss Bean。<br />当候选 Bean 数目不为 1 时的应对方法 <br /><br />在默认情况下使用 @Autowired 注释进行自动注入时，Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时，Spring 容器将抛出 BeanCreationException 异常，并指出必须至少拥有一个匹配的 Bean。我们可以来做一个实验： <br />清单 10. 候选 Bean 数目为 0 时 <br /><pre name="code" class="java">&lt;?xml version="1.0" encoding="UTF-8" ?>  
&lt;beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
     xsi:schemaLocation="http://www.springframework.org/schema/beans    
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">  
    
    &lt;bean class="org.springframework.beans.factory.annotation.   
        AutowiredAnnotationBeanPostProcessor"/>    
  
    &lt;bean id="boss" class="com.baobaotao.Boss"/>  
  
    &lt;!-- 将 office Bean 注释掉 -->  
    &lt;!-- &lt;bean id="office" class="com.baobaotao.Office">  
    &lt;property name="officeNo" value="001"/>  
    &lt;/bean>-->  
  
    &lt;bean id="car" class="com.baobaotao.Car" scope="singleton">  
        &lt;property name="brand" value=" 红旗 CA72"/>  
        &lt;property name="price" value="2000"/>  
    &lt;/bean>  
&lt;/beans> </pre><br />由于 office Bean 被注释掉了，所以 Spring 容器中将没有类型为 Office 的 Bean 了，而 Boss 的 office 属性标注了 @Autowired，当启动 Spring 容器时，异常就产生了。 <br /><br />当不能确定 Spring 容器中一定拥有某个类的 Bean 时，可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false)，这等于告诉 Spring：在找不到匹配 Bean 时也不报错。来看一下具体的例子： <br />清单 11. 使用 @Autowired(required = false) <br /><pre name="code" class="java">package com.baobaotao;   
  
import org.springframework.beans.factory.annotation.Autowired;   
import org.springframework.beans.factory.annotation.Required;   
  
public class Boss {   
  
    private Car car;   
    private Office office;   
  
    @Autowired  
    public void setCar(Car car) {   
        this.car = car;   
    }   
    @Autowired(required = false)   
    public void setOffice(Office office) {   
        this.office = office;   
    }   
    …   
}</pre><br />当然，一般情况下，使用 @Autowired 的地方都是需要注入 Bean 的，使用了自动注入而又允许不注入的情况一般仅会在开发期或测试期碰到（如为了快速启动 Spring 容器，仅引入一些模块的 Spring 配置文件），所以 @Autowired(required = false) 会很少用到。 <br /><br />和找不到一个类型匹配 Bean 相反的一个错误是：如果 Spring 容器中拥有多个候选 Bean，Spring 容器在启动时也会抛出 BeanCreationException 异常。来看下面的例子： <br />清单 12. 在 beans.xml 中配置两个 Office 类型的 Bean <br /><pre name="code" class="java">&lt;bean id="office" class="com.baobaotao.Office">  
    &lt;property name="officeNo" value="001"/>  
&lt;/bean>  
&lt;bean id="office2" class="com.baobaotao.Office">  
    &lt;property name="officeNo" value="001"/>  
&lt;/bean>  </pre><br />我们在 Spring 容器中配置了两个类型为 Office 类型的 Bean，当对 Boss 的 office 成员变量进行自动注入时，Spring 容器将无法确定到底要用哪一个 Bean，因此异常发生了。 <br /><br />Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名称，这样歧义就消除了，可以通过下面的方法解决异常：<br />清单 13. 使用 @Qualifier 注释指定注入 Bean 的名称 <br /><pre name="code" class="java">@Autowired  
public void setOffice(@Qualifier("office")Office office) {   
    this.office = office;   
}  </pre><br />@Qualifier("office") 中的 office 是 Bean 的名称，所以 @Autowired 和 @Qualifier 结合使用时，自动注入的策略就从 byType 转变成 byName 了。@Autowired 可以对成员变量、方法以及构造函数进行注释，而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同，所以 Spring 不将 @Autowired 和 @Qualifier 统一成一个注释类。下面是对成员变量和构造函数入参进行注释的代码： <br /><br />对成员变量进行注释： <br />清单 14. 对成员变量使用 @Qualifier 注释  <br /><pre name="code" class="java">public class Boss {   
    @Autowired  
    private Car car;   
    
    @Autowired  
    @Qualifier("office")   
    private Office office;   
    …   
}  </pre><br />对构造函数入参进行注释： <br />清单 15. 对构造函数变量使用 @Qualifier 注释 <br /><pre name="code" class="java">public class Boss {   
    private Car car;   
    private Office office;   
  
    @Autowired  
    public Boss(Car car , @Qualifier("office")Office office){   
        this.car = car;   
        this.office = office ;   
    }   
} </pre><br />@Qualifier 只能和 @Autowired 结合使用，是对 @Autowired 有益的补充。一般来讲，@Qualifier 对方法签名中入参进行注释会降低代码的可读性，而对成员变量注释则相对好一些。 <br />使用 JSR-250 的注释 <br />Spring 不但支持自己定义的 @Autowired 的注释，还支持几个由 JSR-250 规范定义的注释，它们分别是 @Resource、@PostConstruct 以及 @PreDestroy。 <br /><br />@Resource <br /><br />@Resource 的作用相当于 @Autowired，只不过 @Autowired 按 byType 自动注入，面 @Resource 默认按 byName 自动注入罢了。@Resource 有两个属性是比较重要的，分别是 name 和 type，Spring 将 @Resource 注释的 name 属性解析为 Bean 的名字，而 type 属性则解析为 Bean 的类型。所以如果使用 name 属性，则使用 byName 的自动注入策略，而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性，这时将通过反射机制使用 byName 自动注入策略。 <br /><br />Resource 注释类位于 Spring 发布包的 lib/j2ee/common-annotations.jar 类包中，因此在使用之前必须将其加入到项目的类库中。来看一个使用 @Resource 的例子： <br /><br /><br />清单 16. 使用 @Resource 注释的 Boss.java <br /><pre name="code" class="java">package com.baobaotao;   
  
import javax.annotation.Resource;   
  
public class Boss {   
    // 自动注入类型为 Car 的 Bean   
    @Resource  
    private Car car;   
  
    // 自动注入 bean 名称为 office 的 Bean   
    @Resource(name = "office")   
    private Office office;   
}   </pre><br />一般情况下，我们无需使用类似于 @Resource(type=Car.class) 的注释方式，因为 Bean 的类型信息可以通过 Java 反射从代码中获取。 <br /><br />要让 JSR-250 的注释生效，除了在 Bean 类中标注这些注释外，还需要在 Spring 容器中注册一个负责处理这些注释的 BeanPostProcessor： <br /><pre name="code" class="java">&lt;bean    
  class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>  </pre><br />CommonAnnotationBeanPostProcessor 实现了 BeanPostProcessor 接口，它负责扫描使用了 JSR-250 注释的 Bean，并对它们进行相应的操作。 <br /><br />@PostConstruct 和 @PreDestroy <br /><br />Spring 容器中的 Bean 是有生命周期的，Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作，您既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作方法，也可以通过 &lt;bean> 元素的 init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作方法。关于 Spring 的生命周期，笔者在《精通 Spring 2.x—企业应用开发精解》第 3 章进行了详细的描述，有兴趣的读者可以查阅。 <br /><br />JSR-250 为初始化之后/销毁之前方法的指定定义了两个注释类，分别是 @PostConstruct 和 @PreDestroy，这两个注释只能应用于方法上。标注了 @PostConstruct 注释的方法将在类实例化后调用，而标注了 @PreDestroy 的方法将在类销毁之前调用。 <br /><br /><br />清单 17. 使用 @PostConstruct 和 @PreDestroy 注释的 Boss.java <br /><pre name="code" class="java">package com.baobaotao;   
  
import javax.annotation.Resource;   
import javax.annotation.PostConstruct;   
import javax.annotation.PreDestroy;   
  
public class Boss {   
    @Resource  
    private Car car;   
  
    @Resource(name = "office")   
    private Office office;   
  
    @PostConstruct  
    public void postConstruct1(){   
        System.out.println("postConstruct1");   
    }   
  
    @PreDestroy  
    public void preDestroy1(){   
        System.out.println("preDestroy1");    
    }   
    …   
}  </pre><br />您只需要在方法前标注 @PostConstruct 或 @PreDestroy，这些方法就会在 Bean 初始化后或销毁之前被 Spring 容器执行了。 <br /><br />我们知道，不管是通过实现 InitializingBean/DisposableBean 接口，还是通过 &lt;bean> 元素的 init-method/destroy-method 属性进行配置，都只能为 Bean 指定一个初始化 / 销毁的方法。但是使用 @PostConstruct 和 @PreDestroy 注释却可以指定多个初始化 / 销毁方法，那些被标注 @PostConstruct 或 @PreDestroy 注释的方法都会在初始化 / 销毁时被执行。 <br /><br />通过以下的测试代码，您将可以看到 Bean 的初始化 / 销毁方法是如何被执行的： <br /><br /><br />清单 18. 测试类代码 <br /><pre name="code" class="java">package com.baobaotao;   
  
import org.springframework.context.support.ClassPathXmlApplicationContext;   
  
public class AnnoIoCTest {   
  
    public static void main(String[] args) {   
        String[] locations = {"beans.xml"};   
        ClassPathXmlApplicationContext ctx =    
            new ClassPathXmlApplicationContext(locations);   
        Boss boss = (Boss) ctx.getBean("boss");   
        System.out.println(boss);   
        ctx.destroy();// 关闭 Spring 容器，以触发 Bean 销毁方法的执行   
    }   
}  
</pre><br />这时，您将看到标注了 @PostConstruct 的 postConstruct1() 方法将在 Spring 容器启动时，创建 Boss Bean 的时候被触发执行，而标注了 @PreDestroy 注释的 preDestroy1() 方法将在 Spring 容器关闭前销毁 Boss Bean 的时候被触发执行。 <br /><br /><br /><br /><br /><br />使用 &lt;context:annotation-config/> 简化配置 <br /><br />Spring 2.1 添加了一个新的 context 的 Schema 命名空间，该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的，它仅提供元数据信息。要使元数据信息真正起作用，必须让负责处理这些元数据的处理器工作起来。 <br /><br />而我们前面所介绍的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是处理这些注释元数据的处理器。但是直接在 Spring 配置文件中定义这些 Bean 显得比较笨拙。Spring 为我们提供了一种方便的注册这些 BeanPostProcessor 的方式，这就是 &lt;context:annotation-config/>。请看下面的配置： <br /><br /><br />清单 19. 调整 beans.xml 配置文件 <br /><pre name="code" class="java">&lt;?xml version="1.0" encoding="UTF-8" ?>  
&lt;beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
     xmlns:context="http://www.springframework.org/schema/context"  
     xsi:schemaLocation="http://www.springframework.org/schema/beans    
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
 http://www.springframework.org/schema/context    
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
    
    &lt;context:annotation-config/>    
  
    &lt;bean id="boss" class="com.baobaotao.Boss"/>  
    &lt;bean id="office" class="com.baobaotao.Office">  
        &lt;property name="officeNo" value="001"/>  
    &lt;/bean>  
    &lt;bean id="car" class="com.baobaotao.Car" scope="singleton">  
        &lt;property name="brand" value=" 红旗 CA72"/>  
        &lt;property name="price" value="2000"/>  
    &lt;/bean>  
&lt;/beans>  
</pre><br />&lt;context:annotationconfig/> 将隐式地向 Spring 容器注册 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 这 4 个 BeanPostProcessor。 <br /><br />在配置文件中使用 context 命名空间之前，必须在 &lt;beans> 元素中声明 context 命名空间。 <br /><br /><br /><br /><br /><br />使用 @Component <br /><br />虽然我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能，但是 Bean 还是在 XML 文件中通过 &lt;bean> 进行定义 —— 也就是说，在 XML 配置文件中定义 Bean，通过 @Autowired 或 @Resource 为 Bean 的成员变量、方法入参或构造函数入参提供自动注入的功能。能否也通过注释定义 Bean，从 XML 配置文件中完全移除 Bean 定义的配置呢？答案是肯定的，我们通过 Spring 2.5 提供的 @Component 注释就可以达到这个目标了。 <br /><br />下面，我们完全使用注释定义 Bean 并完成 Bean 之间装配： <br /><br /><br />清单 20. 使用 @Component 注释的 Car.java <br /><pre name="code" class="java">package com.baobaotao;   
  
import org.springframework.stereotype.Component;   
  
@Component  
public class Car {   
    …   
}  </pre><br />仅需要在类定义处，使用 @Component 注释就可以将一个类定义了 Spring 容器中的 Bean。下面的代码将 Office 定义为一个 Bean： <br /><br /><br />清单 21. 使用 @Component 注释的 Office.java <br /><pre name="code" class="java">package com.baobaotao;   
  
import org.springframework.stereotype.Component;   
  
@Component  
public class Office {   
    private String officeNo = "001";   
    …   
}   </pre><br />这样，我们就可以在 Boss 类中通过 @Autowired 注入前面定义的 Car 和 Office Bean 了。 <br /><br /><br />清单 22. 使用 @Component 注释的 Boss.java <br /><pre name="code" class="java">package com.baobaotao;   
  
import org.springframework.beans.factory.annotation.Autowired;   
import org.springframework.beans.factory.annotation.Required;   
import org.springframework.beans.factory.annotation.Qualifier;   
import org.springframework.stereotype.Component;   
  
@Component("boss")   
public class Boss {   
    @Autowired  
    private Car car;   
  
    @Autowired  
    private Office office;   
    …   
}  </pre><br />@Component 有一个可选的入参，用于指定 Bean 的名称，在 Boss 中，我们就将 Bean 名称定义为“boss”。一般情况下，Bean 都是 singleton 的，需要注入 Bean 的地方仅需要通过 byType 策略就可以自动注入了，所以大可不必指定 Bean 的名称。 <br /><br />在使用 @Component 注释后，Spring 容器必须启用类扫描机制以启用注释驱动 Bean 定义和注释驱动 Bean 自动注入的策略。Spring 2.5 对 context 命名空间进行了扩展，提供了这一功能，请看下面的配置： <br /><br /><br />清单 23. 简化版的 beans.xml <br /><pre name="code" class="java">&lt;?xml version="1.0" encoding="UTF-8" ?>  
&lt;beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
 http://www.springframework.org/schema/context    
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
    &lt;context:component-scan base-package="com.baobaotao"/>  
&lt;/beans>  </pre><br />这里，所有通过 &lt;bean> 元素定义 Bean 的配置内容已经被移除，仅需要添加一行 &lt;context:component-scan/> 配置就解决所有问题了——Spring XML 配置文件得到了极致的简化（当然配置元数据还是需要的，只不过以注释形式存在罢了）。&lt;context:component-scan/> 的 base-package 属性指定了需要扫描的类包，类包及其递归子包中所有的类都会被处理。 <br /><br />&lt;context:component-scan/> 还允许定义过滤器将基包下的某些类纳入或排除。Spring 支持以下 4 种类型的过滤方式，通过下表说明： <br /><br /><br />表 1. 扫描过滤方式 <br />过滤器类型 说明 <br />注释 假如 com.baobaotao.SomeAnnotation 是一个注释类，我们可以将使用该注释的类过滤出来。 <br />类名指定 通过全限定类名进行过滤，如您可以指定将 com.baobaotao.Boss 纳入扫描，而将 com.baobaotao.Car 排除在外。 <br />正则表达式 通过正则表达式定义过滤的类，如下所示： com\.baobaotao\.Default.* <br />AspectJ 表达式 通过 AspectJ 表达式定义过滤的类，如下所示： com. baobaotao..*Service+ <br /><br />下面是一个简单的例子： <br /><br /><pre name="code" class="java">&lt;context:component-scan base-package="com.baobaotao">  
    &lt;context:include-filter type="regex"    
        expression="com\.baobaotao\.service\..*"/>  
    &lt;context:exclude-filter type="aspectj"    
        expression="com.baobaotao.util..*"/>  
&lt;/context:component-scan>  </pre><br />值得注意的是 &lt;context:component-scan/> 配置项不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能，同时还启用了注释驱动自动注入的功能（即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor），因此当使用 &lt;context:component-scan/> 后，就可以将 &lt;context:annotation-config/> 移除了。 <br /><br />默认情况下通过 @Component 定义的 Bean 都是 singleton 的，如果需要使用其它作用范围的 Bean，可以通过 @Scope 注释来达到目标，如以下代码所示： <br /><br /><br />清单 24. 通过 @Scope 指定 Bean 的作用范围 <br /><br /><pre name="code" class="java">package com.baobaotao;   
import org.springframework.context.annotation.Scope;   
…   
@Scope("prototype")   
@Component("boss")   
public class Boss {   
    …   
}   
</pre><br />这样，当从 Spring 容器中获取 boss Bean 时，每次返回的都是新的实例了。<br />采用具有特殊语义的注释 <br /><br />Spring 2.5 中除了提供 @Component 注释外，还定义了几个拥有特殊语义的注释，它们分别是：@Repository、@Service 和 @Controller。在目前的 Spring 版本中，这 3 个注释和 @Component 是等效的，但是从注释类的命名上，很容易看出这 3 个注释分别和持久层、业务层和控制层（Web 层）相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意，但 Spring 将在以后的版本中为它们添加特殊的功能。所以，如果 Web 应用程序采用了经典的三层分层结构的话，最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释，而用 @Component 对那些比较中立的类进行注释。 <br /><br /><br />注释配置和 XML 配置的适用场合 <br /><br />是否有了这些 IOC 注释，我们就可以完全摒除原来 XML 配置的方式呢？答案是否定的。有以下几点原因： <br /><br />注释配置不一定在先天上优于 XML 配置。如果 Bean 的依赖关系是固定的，（如 Service 使用了哪几个 DAO 类），这种配置信息不会在部署时发生调整，那么注释配置优于 XML 配置；反之如果这种依赖关系会在部署时发生调整，XML 配置显然又优于注释配置，因为注释是对 Java 源代码的调整，您需要重新改写源代码并重新编译才可以实施调整。 <br />如果 Bean 不是自己编写的类（如 JdbcTemplate、SessionFactoryBean 等），注释配置将无法实施，此时 XML 配置是唯一可用的方式。 <br />注释配置往往是类级别的，而 XML 配置则可以表现得更加灵活。比如相比于 @Transaction 事务注释，使用 aop/tx 命名空间的事务配置更加灵活和简单。 <br />所以在实现应用中，我们往往需要同时使用注释配置和 XML 配置，对于类级别且不会发生变动的配置可以优先考虑注释配置；而对于那些第三方类以及容易发生调整的配置则应优先考虑使用 XML 配置。Spring 会在具体实施 Bean 创建和 Bean 注入之前将这两种配置方式的元信息融合在一起。 <br /><br />小结 <br /><br />Spring 在 2.1 以后对注释配置提供了强力的支持，注释配置功能成为 Spring 2.5 的最大的亮点之一。合理地使用 Spring 2.5 的注释配置，可以有效减少配置的工作量，提高程序的内聚性。但是这并不意味着传统 XML 配置将走向消亡，在第三方类 Bean 的配置，以及那些诸如数据源、缓存池、持久层操作模板类、事务管理等内容的配置上，XML 配置依然拥有不可替代的地位。
          <br/>
          <span style="color:red;">
            <a href="http://coolyongzi.javaeye.com/blog/192957#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 14 May 2008 15:29:46 +0800</pubDate>
        <link>http://coolyongzi.javaeye.com/blog/192957</link>
        <guid>http://coolyongzi.javaeye.com/blog/192957</guid>
      </item>
      <item>
        <title>用com.oroinc.net.ftp向FTP服务器上传/下载文件</title>
        <author>coolyongzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coolyongzi.javaeye.com">coolyongzi</a>&nbsp;
          链接：<a href="http://coolyongzi.javaeye.com/blog/183071" style="color:red;">http://coolyongzi.javaeye.com/blog/183071</a>&nbsp;
          发表时间: 2008年04月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          近来项目中要用java实现ftp客户端向FTP服务器上传和下载文件，总结了一个小例子，希望对大家有所帮助，同时希望大家批评指正。<br />1：AnalysisTools(用来解析Properties文件)<br />源码如下：<br /><pre name="code" class="java">package org.coolyongzi;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class AnalysisTools {
	private static Properties properties = new Properties();

	public void AnalysisProperty(String fileName) throws IOException
	{
		FileInputStream fileInputStream = new FileInputStream(fileName);
		properties.load(fileInputStream);
	}
	public String getProperty(String key)
	{
		return properties.get(key).toString();
	}
}
</pre><br />2：FtpConfig(用来封装上传和下载的相关信息)<br />源码如下：<br /><pre name="code" class="java">package org.coolyongzi;

public class FtpConfig {
	private String ftpServer;
	private String ftpUserName;
	private String ftpPwd;
	private String ftpDownDir;
	private String ftpDownLoadFileName;
	private String ftpUploadFilePath;
	private int dataTimeout;


	public int getDataTimeout() {
		return dataTimeout;
	}
	public void setDataTimeout(int dataTimeout) {
		this.dataTimeout = dataTimeout;
	}
	public String getFtpServer() {
		return ftpServer;
	}
	public void setFtpServer(String ftpServer) {
		this.ftpServer = ftpServer;
	}
	public String getFtpUserName() {
		return ftpUserName;
	}
	public void setFtpUserName(String ftpUserName) {
		this.ftpUserName = ftpUserName;
	}
	public String getFtpPwd() {
		return ftpPwd;
	}
	public void setFtpPwd(String ftpPwd) {
		this.ftpPwd = ftpPwd;
	}
	public String getFtpDownDir() {
		return ftpDownDir;
	}
	public void setFtpDownDir(String ftpDownDir) {
		this.ftpDownDir = ftpDownDir;
	}
	public String getFtpDownLoadFileName() {
		return ftpDownLoadFileName;
	}
	public void setFtpDownLoadFileName(String ftpDownLoadFileName) {
		this.ftpDownLoadFileName = ftpDownLoadFileName;
	}
	public String getFtpUploadFilePath() {
		return ftpUploadFilePath;
	}
	public void setFtpUploadFilePath(String ftpUploadFilePath) {
		this.ftpUploadFilePath = ftpUploadFilePath;
	}
	
}
</pre><br />3：FtpClient(上传和下载的实现类)<br />源码如下：<br /><pre name="code" class="java">package org.coolyongzi;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.SocketException;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import com.oroinc.net.ftp.FTPClient;

public class FtpClient {
	private FtpConfig ftpConfig;
	private FTPClient client;
	private AnalysisTools analysisTools;
	public static final Logger logger = LogManager.getLogger(FtpClient.class);
	public FtpClient() throws IOException
	{
		ftpConfig = new FtpConfig();
		this.initFtpConfit(ftpConfig);
		client = new FTPClient();
		
	}
	
	private void initFtpConfit(FtpConfig ftpConfig) throws IOException
	{
		analysisTools = new AnalysisTools();
		analysisTools.AnalysisProperty("F:/WorkSpace/MyTest/src/properties");		
		ftpConfig.setFtpServer(analysisTools.getProperty("ftpServer"));
		ftpConfig.setFtpUserName(analysisTools.getProperty("ftpUserName"));
		ftpConfig.setFtpPwd(analysisTools.getProperty("ftpUserPwd"));
		ftpConfig.setFtpDownDir(analysisTools.getProperty("ftpDownDir"));
		ftpConfig.setFtpDownLoadFileName(analysisTools.getProperty("ftpDownLoadFileNames"));
		ftpConfig.setFtpUploadFilePath(analysisTools.getProperty("ftpDownLoadFileName"));
	}
	
	/*
	 * @description:Downloaded from the file server documents
	 * @return void
	 */
	public void downLoadFileThroughFtpClient() throws IOException
	{
		connectFtp();
		//Get the file list
		String[] fileNames = client.listNames();
		File downDir = new File(ftpConfig.getFtpDownDir());
		String[] downFileNames = StringTools.SegmentateCharacters(ftpConfig.getFtpDownLoadFileName(), ",");
		File tempFile = null;
		FileOutputStream fileOutputStream = null;
		logger.debug(analysisTools.getProperty("getFileFromFtpServerStart"));
		for (int i = 0; i &lt; fileNames.length; i++) {
			for (int j = 0; j &lt; downFileNames.length; j++) {
				if((downFileNames[j].trim()).equalsIgnoreCase(fileNames[i].trim()))
				{
					tempFile = new File(downDir.getAbsolutePath()+File.separator+downFileNames[j]);
					fileOutputStream = new FileOutputStream(tempFile);
					client.retrieveFile(fileNames[i], fileOutputStream);
					fileOutputStream.close();
				}
			}
		}
		logger.debug(analysisTools.getProperty("getFileFromFtpServerEnd"));
		//Sign launched
		client.logout();
		//Disconnect
		client.disconnect();
	}
	
	/*
	 * @description:Upload files to the file server
	 * @return void
	 */
	public void upLoadFileThroughFtpClient() throws IOException
	{
		connectFtp();
		String[] upLoadFilePathArray = StringTools.SegmentateCharacters(ftpConfig.getFtpUploadFilePath(),",");
		File tempFile = null;
		logger.debug(analysisTools.getProperty("uploadFileStart"));
		for (int i = 0; i &lt; upLoadFilePathArray.length; i++) {
			tempFile = new File(upLoadFilePathArray[i]);
			client.storeFile(tempFile.getName(),new FileInputStream(tempFile));
		}
		logger.debug(analysisTools.getProperty("uploadFileEnd"));
		//Sign launched
		client.logout();
		//Disconnect
		client.disconnect();
	}
	
	private void connectFtp() throws IOException
	{
		//Connect FtpServer
		try {
			client.connect(ftpConfig.getFtpServer());
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			logger.debug(analysisTools.getProperty("ftpconnectError"));
			logger.debug(e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			logger.debug(e.getMessage());
			e.printStackTrace();
		}
		//Login FtpServer
		try {
			client.login(ftpConfig.getFtpUserName(),ftpConfig.getFtpPwd());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			logger.debug(analysisTools.getProperty("ftploginError"));
			logger.debug(e.getMessage());
			e.printStackTrace();
		}
		//Document in whatever form transmission
		client.setFileType(FTPClient.BINARY_FILE_TYPE);
		//Set up overtime hours
		client.setDataTimeout(ftpConfig.getDataTimeout());
	}
}
</pre><br />4：StringTools(字符串工具类)<br />源码如下：<br /><pre name="code" class="java">package org.coolyongzi;

public class StringTools {
	public static String getShortFileNameFromFilePath(String FilePath)
	{
		int position = FilePath.lastIndexOf("/");
		return FilePath.substring(position+1);
	}
	
	public static String conversionSpecialCharacters(String  convertedCharacters)
	{
		return convertedCharacters.replace("/", "\\");	
	}
	
	public static String[] SegmentateCharacters(String str,String symbol)
	{
		return str.split(symbol);
	}
}
</pre><br />5：Junit测试类<br />源码如下：<br /><pre name="code" class="java">package org.coolyongzi.testcase;

import java.io.IOException;
import java.net.SocketException;

import org.coolyongzi.FtpClient;

import junit.framework.TestCase;

public class FtpClientTest extends TestCase {

	protected void setUp() throws Exception {
		super.setUp();
	}

	protected void tearDown() throws Exception {
		super.tearDown();
	}

	public void testDownLoadFileThroughFtpClient() throws SocketException, IOException {
		FtpClient ftpClient = new FtpClient();
		ftpClient.downLoadFileThroughFtpClient();
	}
	
	public void testupLoadFileThroughFtpClient() throws IOException
	{
		FtpClient ftpClient = new FtpClient();
		ftpClient.upLoadFileThroughFtpClient();
	}
}
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://coolyongzi.javaeye.com/blog/183071#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 15 Apr 2008 23:30:58 +0800</pubDate>
        <link>http://coolyongzi.javaeye.com/blog/183071</link>
        <guid>http://coolyongzi.javaeye.com/blog/183071</guid>
      </item>
      <item>
        <title>用org.apache.tools.zip压缩/解压缩zip文件</title>
        <author>coolyongzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coolyongzi.javaeye.com">coolyongzi</a>&nbsp;
          链接：<a href="http://coolyongzi.javaeye.com/blog/182447" style="color:red;">http://coolyongzi.javaeye.com/blog/182447</a>&nbsp;
          发表时间: 2008年04月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          写了一个用org.apache.tools.zip压缩/解压缩zip文件的例子,用来解决中文乱码问题。代码如下：<br /><pre name="code" class="java">package org.coolyongzi;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;


public class ZipTools {
	public static final Logger logger = LogManager.getLogger(FileTools.class);
	public ZipTools()
	{
		
	}
	/*
	 * @description:Compressed files or folders
	 * @param compressedFilePath String,zipFileRootPath String,zipFileName String
	 * @return boolean
	 */
	public static boolean compressFloderChangeToZip(String compressedFilePath,String zipFileRootPath,String zipFileName) 
	throws IOException
	{
		File compressedFile = new File(compressedFilePath);
		if("".equalsIgnoreCase(zipFileName))
		{
			zipFileName = StringTools.getShortFileNameFromFilePath(compressedFilePath);
		}
		if(!StringTools.conversionSpecialCharacters(zipFileRootPath).endsWith(File.separator))
		{
			zipFileRootPath = zipFileRootPath + File.separator;
		}
		ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileRootPath + zipFileName));
		String base ="";
		logger.debug("compress [" + compressedFilePath + "] start!");
		boolean result = ZipTools.compressFloderChangeToZip(compressedFile,zipOutputStream,base);
		logger.debug("compress [" + compressedFilePath + "] end!");
		zipOutputStream.close();
		return result;
		
	}
	
	private static  boolean compressFloderChangeToZip(File compressedFile,ZipOutputStream zipOutputStream,String base) 
	throws IOException
	{
		FileInputStream fileInputStream = null;
		
		try{
			if(compressedFile.isDirectory())
			{
				File[] childrenCompressedFileList = compressedFile.listFiles();
				base = base.length() == 0 ? "" : base + File.separator;
				for (int i = 0; i &lt; childrenCompressedFileList.length; i++) {
					ZipTools.compressFloderChangeToZip(childrenCompressedFileList[i],
					zipOutputStream,base+childrenCompressedFileList[i].getName());
				}
			}
			else
			{
				if("".equalsIgnoreCase(base))
				{
					base = compressedFile.getName();
				}
				zipOutputStream.putNextEntry(new ZipEntry(base));
				fileInputStream = new FileInputStream(compressedFile);
				int b;
				while((b=fileInputStream.read())!=-1)
				{
					zipOutputStream.write(b);
				}
				fileInputStream.close();
			}
			return true;
		}catch(Exception e)
		{
			e.getStackTrace();
			logger.error(e.getMessage());
			return false;
		}
	}
	/*
	 * @param:zipFilePath String,releasePath String
	 * @return void
	 * @description:Decompress A File
	 */
	@SuppressWarnings("unchecked")
	public static void decompressFile(String zipFilePath,String releasePath) throws IOException
	{
		ZipFile zipFile = new ZipFile(zipFilePath);
		Enumeration&lt;ZipEntry> enumeration = zipFile.getEntries();
		InputStream inputStream = null;
		FileOutputStream fileOutputStream = null;
		ZipEntry zipEntry = null;
		String zipEntryNameStr ="";
		String[] zipEntryNameArray = null;
		while (enumeration.hasMoreElements()) {
			zipEntry = enumeration.nextElement();
			zipEntryNameStr = zipEntry.getName();
			zipEntryNameArray = zipEntryNameStr.split("/");
			String path = releasePath;
			File root = new File(releasePath);
			if(!root.exists())
			{
				root.mkdir();
			}
			for (int i = 0; i &lt; zipEntryNameArray.length; i++) {
				if(i&lt;zipEntryNameArray.length-1)
				{
					path = path + File.separator+zipEntryNameArray[i];		
					new File(StringTools.conversionSpecialCharacters(path)).mkdir();
				}				
				else
				{
					if(StringTools.conversionSpecialCharacters(zipEntryNameStr).endsWith(File.separator))
					{
						new File(releasePath + zipEntryNameStr).mkdir();
					}
					else
					{
						inputStream = zipFile.getInputStream(zipEntry);
						fileOutputStream = new FileOutputStream(new File(
								StringTools.conversionSpecialCharacters(releasePath + zipEntryNameStr)));	
			            byte[] buf = new byte[1024];
			            int len;
			            while ((len = inputStream.read(buf)) > 0)
			            {
			            	fileOutputStream.write(buf, 0, len);
			            }
			            inputStream.close();
			            fileOutputStream.close();
					}
				}
			}
		}
		zipFile.close();
	}
}












</pre><br />junit测试类<br /><pre name="code" class="java">package org.coolyongzi.testcase;

import java.io.IOException;

import org.coolyongzi.ZipTools;

import junit.framework.TestCase;

public class ZipToolsTest extends TestCase {

	protected void setUp() throws Exception {
		super.setUp();
	}

	protected void tearDown() throws Exception {
		super.tearDown();
	}

	public void testCompressFloderChangeToZip(){
		try {
			ZipTools.compressFloderChangeToZip("f:/iDocumentBanner2.gif", "f:", "test.zip");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void testDecompressFile(){
		try {
			ZipTools.decompressFile("f:/java对解压Zip格式的文件.zip","f:/test/");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println(e.getMessage());
		}
	}
}
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://coolyongzi.javaeye.com/blog/182447#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 13 Apr 2008 23:34:53 +0800</pubDate>
        <link>http://coolyongzi.javaeye.com/blog/182447</link>
        <guid>http://coolyongzi.javaeye.com/blog/182447</guid>
      </item>
      <item>
        <title>Java文件操作</title>
        <author>coolyongzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coolyongzi.javaeye.com">coolyongzi</a>&nbsp;
          链接：<a href="http://coolyongzi.javaeye.com/blog/182179" style="color:red;">http://coolyongzi.javaeye.com/blog/182179</a>&nbsp;
          发表时间: 2008年04月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          [align=left]写了个Java操作文件的简单例子，希望给需要帮助的兄弟提供点儿帮助，同时也希望大家给予批评指正。<br />文件操作类，代码如下：<br /><pre name="code" class="java">package org.coolyongzi;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;



public class FileTools {
	private static final String SPECIAL_FILE_STYLE_PICTURECACHE = "Thumbs.db";
	public static final Logger logger = LogManager.getLogger(FileTools.class);
	
	public FileTools()
	{
		
	}
	/*
	 * @discription:Delete a folder
	 * @param:dirPath String
	 * @return boolean
	 */
	public static boolean deleteDirectory(String dirPath,boolean deteDirRoot)
	{
		File parentFile = new File(dirPath);
		File[] chirenFile = null;
		if(parentFile.exists() && parentFile.isDirectory())
		{
				chirenFile = parentFile.listFiles();
				for (int i = 0; i &lt; chirenFile.length; i++) {
					if(!chirenFile[i].isDirectory())
					{
						chirenFile[i].delete();
					}
					else
					{
						deleteDirectory(chirenFile[i].getAbsolutePath(),true);					
						chirenFile[i].delete();
					}
				}
				if(deteDirRoot)
				{
					parentFile.delete();
				}
				
				return true;		
		}
		else
		{
			FileTools.logger.error("The Floder Is Not Exist!");
			return false;
		}
	}
	
	/*
	 * @discription:Copy Folder
	 * @param:sourceFilePath String,targetFilePath String
	 * @return boolean
	 */
	public static boolean copyDirectory(String sourceFilePath,String targetFilePath) throws IOException
	{
		File sourceFile = new File(sourceFilePath);
		File targetFile = new File(targetFilePath);
		
		if(!targetFile.exists())
		{
			targetFile.mkdir();
		}
		
		File[] sourceChirenFile = null;
		FileInputStream fileInputStream = null;
		FileOutputStream fileOutputStream = null;
		FileTools.logger.debug("Copy "+sourceFile.getName()+" Floder Start......");
		if(sourceFile.exists() && sourceFile.isDirectory())
		{
			sourceChirenFile = sourceFile.listFiles();
			for (int i = 0; i &lt; sourceChirenFile.length; i++) {
				File tempFile = null;
				
				if(!sourceFilePath.endsWith(File.separator))
				{
					tempFile = new File(targetFilePath+File.separator+sourceChirenFile[i].getName());
				}
				else
				{
					tempFile = new File(targetFilePath+sourceChirenFile[i].getName());
				}
				if(!FileTools.SPECIAL_FILE_STYLE_PICTURECACHE.equalsIgnoreCase(sourceChirenFile[i].getName()))
				{
					if(!sourceChirenFile[i].isDirectory())
					{
						fileInputStream = new FileInputStream(sourceChirenFile[i]);
						fileOutputStream = new FileOutputStream(tempFile);
						byte[] b = new byte[1024];
						FileTools.logger.debug("Copy "+sourceChirenFile[i].getName()+" File Start......");
					    while(fileInputStream.read(b)!=-1)
					    {
					    	fileOutputStream.write(b);
					    }
					    FileTools.logger.debug("Copy "+sourceChirenFile[i].getName()+"  File End!");
					    fileInputStream.close();
					    fileOutputStream.close();	
					}
					else
					{
						copyDirectory(sourceFilePath+File.separator+sourceChirenFile[i].getName(),
								      targetFilePath+File.separator+sourceChirenFile[i].getName());
					}
				}
				
			}
			FileTools.logger.debug("Copy "+sourceFile.getName()+" Floder End!");
			return true;
		}
		else
		{
			FileTools.logger.error("The Floder Is Not Exist!");
			return false;
		}
	}
}
</pre><br />Junit测试类<br /><pre name="code" class="java">package org.coolyongzi.testcase;

import java.io.IOException;

import org.coolyongzi.FileTools;

import junit.framework.TestCase;

public class FileToolsTest extends TestCase {

	protected void setUp() throws Exception {
		super.setUp();
	}

	protected void tearDown() throws Exception {
		super.tearDown();
	}

	public void testDeleteDirectory() 
	{
		FileTools.deleteDirectory("F:/复件 图片",false);
	}
	
	public void testCopyDirectory()
	{
		try {
			FileTools.copyDirectory("F:/Game","F:/Game1");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://coolyongzi.javaeye.com/blog/182179#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 12 Apr 2008 22:20:58 +0800</pubDate>
        <link>http://coolyongzi.javaeye.com/blog/182179</link>
        <guid>http://coolyongzi.javaeye.com/blog/182179</guid>
      </item>
  </channel>
</rss>