﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-justuszhang2009</title><link>http://www.blogjava.net/justuszhang2009/</link><description /><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 09:07:15 GMT</lastBuildDate><pubDate>Mon, 13 Apr 2026 09:07:15 GMT</pubDate><ttl>60</ttl><item><title>Struts2+Spring+Hibernate整合的例子</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/11/27/364925.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Sun, 27 Nov 2011 09:27:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/11/27/364925.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/364925.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/11/27/364925.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/364925.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/364925.html</trackback:ping><description><![CDATA[
<p>自从6月份写过一些博客之后，有将近5个月没写博客了，或者由于工作繁忙，或者由于懒于书笔，又或者最近没有研究技术上的东西，根本无从写起。虽然从事项目管理工作，但深知没有技术根底，管理好一个IT项目谈何容易。难得近期各项目走上正轨，有点闲心回忆一下技术方面的东西，那就拿ssh开刀吧，谁叫它是最流行的J2EE框架组合呢。<br /><br />花了将近3个小时终于把一个ssh整合的例子做完，感叹不如当年勇啊，虽说经验丰富了很多，但是实打实地编码工作，可真不是靠吹牛吹出来的，到处都存在问题，需要花时间一一解决。好了，废话少说，下面列一下用到的一些技术以及注意点，为了避免误导人，或致人懒惰，这里就不将所有代码一一列出，只是贴一些关键的代码段。<br /><br />用到的技术如下：<br />1）MySql，在本机上能跑的最小数据库。<br />2）Hibernate3，有了注解功能后，感觉比Hibernate2方便多了<br />3）Spring3<br />4）Struts2<br />5）Junit4，用于测试service的方法<br />6）Log4j<br />7）需要的jar包如下，<br /><img border="0" alt="" src="http://www.blogjava.net/images/blogjava_net/justuszhang2009/jar包库.png" width="819" height="576" /></p><p><br /><br />1、MySql<br />&nbsp;&nbsp;&nbsp; 这个就不多说了，安装过程非常简单。创建一个数据库：test，用grant语句创建用户test，密码test；创建一张表：ACCOUNT。<br />2、Hibernate3<br />&nbsp;&nbsp;&nbsp; 1）创建一个domain对象：Account，和表ACCOUNT对应；在上面加注解@Entity @Table(name="ACCOUNT")，这样就省去了些hbm文件<br />&nbsp;&nbsp;&nbsp; 2）创建AccountDao，实现增删改查功能；<br />3、spring3<br />&nbsp;&nbsp;&nbsp; 1）创建spring配置文件：spring.xml，定义datasource，sessionFactory等；<br />&nbsp;&nbsp;&nbsp; 2）创建AccountService，实现业务逻辑，调用AccountDao；<br />4、Struts2<br />&nbsp;&nbsp;&nbsp; 1）创建web.xml，将spring.xml放入ContextConfigLocation；<br />&nbsp;&nbsp;&nbsp; 2）创建struts.xml，定义package和action及跳转；<br />&nbsp;&nbsp;&nbsp; 3）创建LoginAction类，从页面获取用户名和密码，调用AccountService的用于验证方法<br /><br />总结，在做这个例子的过程中，出现很多问题，很大一部分都是来自于jar包的缺失和冲突，以下几点是比较难于发现的，<br />&nbsp; 1）Springframework的jar包版本不一致，会出现很奇怪的问题；<br />&nbsp; 2）缺少<span style="color: red;">struts2-spring-plugin</span>-2.2.3的jar包，会导致spring的bean无法实例化成功，总是获取到null；<br />&nbsp; 3）javaee.jar和servlet-api.jar的冲突，个人感觉后者是前者的精简版，在Tomcat容器的lib目录下存在，会和工程中的lib冲突，解决方案是把Tomcat下的servlet-api.jar换成javaee.jar；<br />&nbsp; 4）如果想通过标记的方式来注入bean，必须在spring配置文件中，添加以下代码：<br /><span style="color: red;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;context:annotation-config /&gt;</span><br /><span style="color: red;">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&lt;context:component-scan base-package="com.glen" /&gt;</span><br /><br />另外，还有两个问题未解决，望能人帮之，</p>
<div>&nbsp; 1）两表关联，PrimaryKeyJoinColumn不起作用，只能插数据到主表，不能将数据插入关联表；</div>
<div>&nbsp;&nbsp;2）mysql数据库，插入中文时报错，</div>
<div>
<div style="color: rgb(0, 0, 128);">[2011/11/27&nbsp;11:30:45:045&nbsp;CST]&nbsp;ERROR&nbsp;org.hibernate.util.JDBCExceptionReporter:(JDBCExceptionReporter.java:101):&nbsp;Incorrect&nbsp;string&nbsp;value:&nbsp;'\xE5\xBC\xA0\xE7\x9B\x8A...'&nbsp;for&nbsp;column&nbsp;'chinese_name'&nbsp;at&nbsp;row&nbsp;1</div>
<div style="color: rgb(0, 0, 128);">[2011/11/27&nbsp;11:30:46:046&nbsp;CST]&nbsp;ERROR&nbsp;org.hibernate.event.def.AbstractFlushingEventListener:(AbstractFlushingEventListener.java:324):&nbsp;Could&nbsp;not&nbsp;synchronize&nbsp;database&nbsp;state&nbsp;with&nbsp;session</div>
<div style="color: rgb(0, 0, 128);">org.hibernate.exception.GenericJDBCException:&nbsp;Could&nbsp;not&nbsp;execute&nbsp;JDBC&nbsp;batch&nbsp;update</div></div>
<p>&nbsp;</p> 
 
<img src ="http://www.blogjava.net/justuszhang2009/aggbug/364925.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-11-27 17:27 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/11/27/364925.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈系统架构（一）</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/06/26/353014.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Sun, 26 Jun 2011 00:16:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/06/26/353014.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/353014.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/06/26/353014.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/353014.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/353014.html</trackback:ping><description><![CDATA[<p>目前正在实施的一个项目，由于系统架构组组长突然离职，使我有幸代理了一段时间的系统架构组组长，但我主要负责系统架构组的日常管理工作，在技术层面涉入不深，这也是我比较遗憾的地方。最近被人问到如何设计一个系统架构才能支撑高并发的要求时，我哑口了，因为我只知道用的什么系统架构，但真的没有去总结过使用这个架构的原因，今天有空稍微整理了一下思绪，浅浅地谈一下。<br /><br />先说一下我对系统架构的认识，这是我在和一个资深的系统架构师交谈时所吸收的，架构至少可以分成三种层面的架构：系统架构、软件架构、应用架构。系统架构是最高层面的一个架构，可以独立于软件架构（用什么技术框架来搭建）和应用架构（实现的什么业务需求）而存在。一个系统架构师应该考虑的是，根据系统的非功能性需求（访问量、并发量、扩展性要求等），来选择使用什么样的硬件、操作系统、数据库、中间件、编程语言。<br /><br />由于没有太多系统架构的经验，这次就先说到这里，之后会简单聊聊我所熟知的软件架构和应用架构。</p> <img src ="http://www.blogjava.net/justuszhang2009/aggbug/353014.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-06-26 08:16 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/06/26/353014.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】单元测试利器 JUnit 4</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352430.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Fri, 24 Jun 2011 05:06:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352430.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/352430.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352430.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/352430.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/352430.html</trackback:ping><description><![CDATA[<p sizset="78" sizcache="22"><a name="N1005D"><span class="atitle">引言</span></a></p>
<p>毋庸置疑，程序员要对自己编写的代码负责，您不仅要保证它能通过编译，正常地运行，而且要满足需求和设计预期的效果。单元测试正是验证代码行为是否满足预期的有效手段之一。但不可否认，做测试是件很枯燥无趣的事情，而一遍又一遍的测试则更是让人生畏的工作。幸运的是，单元测试工具 JUnit 使这一切变得简单艺术起来。</p>
<p>JUnit 是 Java 社区中知名度最高的单元测试工具。它诞生于 1997 年，由 Erich Gamma 和 Kent Beck 共同开发完成。其中 Erich Gamma 是经典著作《设计模式：可复用面向对象软件的基础》一书的作者之一，并在 Eclipse 中有很大的贡献；Kent Beck 则是一位极限编程（XP）方面的专家和先驱。</p>
<p>麻雀虽小，五脏俱全。JUnit 设计的非常小巧，但是功能却非常强大。Martin Fowler 如此评价 JUnit：在软件开发领域，从来就没有如此少的代码起到了如此重要的作用。它大大简化了开发人员执行单元测试的难度，特别是 JUnit 4 使用 Java 5 中的注解（annotation）使测试变得更加简单。</p>
<div class="ibm-alternate-rule">
<hr />
</div>
<p class="ibm-ind-link ibm-back-to-top" sizset="79" sizcache="22"><a class="ibm-anchor-up-link" href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#ibm-pcon">回页首</a></p>
<p sizset="80" sizcache="22"><a name="major2"><span class="atitle">JUnit 4 初体验</span></a></p>
<p>在开始体验 JUnit 4 之前，我们需要以下软件的支持：</p>
<ul sizset="81" sizcache="22"><li sizset="81" sizcache="22">Eclipse：最为流行的 IDE，它全面集成了 JUnit，并从版本 3.2 开始支持 JUnit 4。当然 JUnit 并不依赖于任何 IDE。您可以从 <a href="http://www.eclipse.org/">http://www.eclipse.org/</a> 上下载最新的 Eclipse 版本。</li><li sizset="82" sizcache="22">Ant：基于 Java 的开源构建工具，您可以在 <a href="http://ant.apache.org/">http://ant.apache.org/</a> 上得到最新的版本和丰富的文档。Eclipse 中已经集成了 Ant，但是在撰写本文时，Eclipse 使用的 Ant 版本较低（必需 1.7 或者以上版本），不能很好的支持 JUnit 4。</li><li sizset="83" sizcache="22">JUnit：它的官方网站是 <a href="http://www.junit.org/">http://www.junit.org/</a>。您可以从上面获取关于 JUnit 的最新消息。如果您和本文一样在 Eclipse 中使用 JUnit，就不必再下载了。 </li></ul>
<p sizset="84" sizcache="22">首先为我们的体验新建一个 Java 工程 &#8212;&#8212; coolJUnit。现在需要做的是，打开项目 coolJUnit 的属性页 -&gt; 选择&#8220;Java Build Path&#8221;子选项 -&gt; 点选&#8220;Add Library &#8230;&#8221;按钮 -&gt; 在弹出的&#8220;Add Library&#8221;对话框中选择 JUnit（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#figure001">图 1</a>），并在下一页中选择版本 4.1 后点击&#8220;Finish&#8221;按钮。这样便把 JUnit 引入到当前项目库中了。</p><br /><a name="figure001"><strong>图 1 为项目添加 JUnit 库</strong></a><br /><img alt="图 1 为项目添加 JUnit 库" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure001.jpg" width="554" height="451" /> <br />
<div class="ibm-container ibm-alt-header dw-container-sidebar">
<h2>请注意 JDK 的版本</h2>
<div class="ibm-container-body">
<p>JUnit 4.1 是基于 Java 5 的升级版本，它使用了 Tiger 中的很多新特性来简化原有的使用方式。正因为如此，它并不能直接运行在 JDK1.4.x 版本上。如果您需要在 JDK1.4.x 版本使用 JUnit 的话，请使用 3.8.1 版本。</p></div></div>
<p sizset="86" sizcache="22">可以开始编写单元测试了吗？等等&#8230;&#8230;，您打算把单元测试代码放在什么地方呢？把它和被测试代码混在一起，这显然会照成混乱，因为单元测试代码是不会出现在最终产品中的。建议您分别为单元测试代码与被测试代码创建单独的目录，并保证测试代码和被测试代码使用相同的包名。这样既保证了代码的分离，同时还保证了查找的方便。遵照这条原则，我们在项目 coolJUnit 根目录下添加一个新目录 testsrc，并把它加入到项目源代码目录中（加入方式见 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#figure002">图 2</a>）。</p><br /><a name="figure002"><strong>图 2 修改项目源代码目录</strong></a><br /><img alt="图 2 修改项目源代码目录" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure002.jpg" width="553" height="436" /> <br />
<p>现在我们得到了一条 JUnit 的最佳实践：单元测试代码和被测试代码使用一样的包，不同的目录。</p>
<p>一切准备就绪，一起开始体验如何使用 JUnit 进行单元测试吧。下面的例子来自笔者的开发实践：工具类 WordDealUtil 中的静态方法 wordFormat4DB 是专用于处理 Java 对象名称向数据库表名转换的方法（您可以在代码注释中可以得到更多详细的内容）。下面是第一次编码完成后大致情形：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> package com.ai92.cooljunit; 

 import java.util.regex.Matcher; 
 import java.util.regex.Pattern; 

 /** 
 * 对名称、地址等字符串格式的内容进行格式检查
 * 或者格式化的工具类
 * 
 * @author Ai92 
 */ 
 public class WordDealUtil { 

	 /** 
	 * 将 Java 对象名称（每个单词的头字母大写）按照
	 * 数据库命名的习惯进行格式化
	 * 格式化后的数据为小写字母，并且使用下划线分割命名单词
	 * 
	 * 例如：employeeInfo 经过格式化之后变为 employee_info 
	 * 
	 * @param name 	 Java 对象名称
	 */ 
	 public static String wordFormat4DB(String name){ 
		 Pattern p = Pattern.compile("[A-Z]"); 
		 Matcher m = p.matcher(name); 
		 StringBuffer sb = new StringBuffer(); 
		
		 while(m.find()){ 
			 m.appendReplacement(sb, "_"+m.group()); 
		 } 
		 return m.appendTail(sb).toString().toLowerCase(); 
	 } 
 } 
</pre></td></tr></tbody></table><br />
<p>它是否能按照预期的效果执行呢？尝试为它编写 JUnit 单元测试代码如下：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> package com.ai92.cooljunit; 

 import static org.junit.Assert.assertEquals; 
 import org.junit.Test; 

 public class TestWordDealUtil { 
	 // 测试 wordFormat4DB 正常运行的情况
	 @Test public void wordFormat4DBNormal(){ 
		 String target = "employeeInfo"; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertEquals("employee_info", result); 
	 } 
 } 
</pre></td></tr></tbody></table><br />
<p>很普通的一个类嘛！测试类 TestWordDealUtil 之所以使用&#8220;Test&#8221;开头，完全是为了更好的区分测试类与被测试类。测试方法 wordFormat4DBNormal 调用执行被测试方法 WordDealUtil.wordFormat4DB，以判断运行结果是否达到设计预期的效果。需要注意的是，测试方法 wordFormat4DBNormal 需要按照一定的规范书写：</p>
<ol><li>测试方法必须使用注解 org.junit.Test 修饰。</li><li>测试方法必须使用 public void 修饰，而且不能带有任何参数。 </li></ol>
<p>测试方法中要处理的字符串为&#8220;employeeInfo&#8221;，按照设计目的，处理后的结果应该为&#8220;employee_info&#8221;。assertEquals 是由 JUnit 提供的一系列判断测试结果是否正确的静态断言方法（位于类 org.junit.Assert 中）之一，我们使用它将执行结果 result 和预期值&#8220;employee_info&#8221;进行比较，来判断测试是否成功。</p>
<p sizset="88" sizcache="22">看看运行结果如何。在测试类上点击右键，在弹出菜单中选择 Run As JUnit Test。运行结果如 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#figure003">下图</a>所示：</p><br /><a name="figure003"><strong>图 3 JUnit 运行成功界面</strong></a><br /><img alt="图 3 JUnit 运行成功界面" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure003.jpg" width="251" height="368" /> <br />
<p>绿色的进度条提示我们，测试运行通过了。但现在就宣布代码通过了单元测试还为时过早。记住：您的单元测试代码不是用来证明您是对的，而是为了证明您没有错。因此单元测试的范围要全面，比如对边界值、正常值、错误值得测试；对代码可能出现的问题要全面预测，而这也正是需求分析、详细设计环节中要考虑的。显然，我们的测试才刚刚开始，继续补充一些对特殊情况的测试：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> public class TestWordDealUtil { 
	&#8230;&#8230;
	 // 测试 null 时的处理情况
	 @Test public void wordFormat4DBNull(){ 
		 String target = null; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertNull(result); 
	 } 
	
	 // 测试空字符串的处理情况
	 @Test public void wordFormat4DBEmpty(){ 
		 String target = ""; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertEquals("", result); 
	 } 

	 // 测试当首字母大写时的情况
	 @Test public void wordFormat4DBegin(){ 
		 String target = "EmployeeInfo"; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertEquals("employee_info", result); 
	 } 
	
	 // 测试当尾字母为大写时的情况
	 @Test public void wordFormat4DBEnd(){ 
		 String target = "employeeInfoA"; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertEquals("employee_info_a", result); 
	 } 
	
	 // 测试多个相连字母大写时的情况
	 @Test public void wordFormat4DBTogether(){ 
		 String target = "employeeAInfo"; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertEquals("employee_a_info", result); 
	 } 
 } 
</pre></td></tr></tbody></table><br />
<p sizset="90" sizcache="22">再次运行测试。很遗憾，JUnit 运行界面提示我们有两个测试情况未通过测试（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#figure004">图 4</a>）&#8212;&#8212;当首字母大写时得到的处理结果与预期的有偏差，造成测试失败（failure）；而当测试对 null 的处理结果时，则直接抛出了异常&#8212;&#8212;测试错误（error）。显然，被测试代码中并没有对首字母大写和 null 这两种特殊情况进行处理，修改如下：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> // 修改后的方法 wordFormat4DB 
 /** 
	 * 将 Java 对象名称（每个单词的头字母大写）按照
	 * 数据库命名的习惯进行格式化
	 * 格式化后的数据为小写字母，并且使用下划线分割命名单词
	 * 如果参数 name 为 null，则返回 null 
	 * 
	 * 例如：employeeInfo 经过格式化之后变为 employee_info 
	 * 
	 * @param name Java 对象名称
	 */ 
	 public static String wordFormat4DB(String name){ 
		
		 if(name == null){ 
			 return null; 
		 } 
		
		 Pattern p = Pattern.compile("[A-Z]"); 
		 Matcher m = p.matcher(name); 
		 StringBuffer sb = new StringBuffer(); 
		
		 while(m.find()){ 
			 if(m.start() != 0) 
				 m.appendReplacement(sb, ("_"+m.group()).toLowerCase()); 
		 } 
		 return m.appendTail(sb).toString().toLowerCase(); 
	 } 
</pre></td></tr></tbody></table><br /><br /><a name="figure004"><strong>图 4 JUnit 运行失败界面</strong></a><br /><img alt="图 4 JUnit 运行失败界面" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure004.jpg" width="251" height="428" /> <br />
<p>JUnit 将测试失败的情况分为两种：failure 和 error。Failure 一般由单元测试使用的断言方法判断失败引起，它表示在测试点发现了问题；而 error 则是由代码异常引起，这是测试目的之外的发现，它可能产生于测试代码本身的错误（测试代码也是代码，同样无法保证完全没有缺陷），也可能是被测试代码中的一个隐藏的 bug。</p>
<div class="ibm-container ibm-alt-header dw-container-sidebar">
<h2>请牢记！</h2>
<div class="ibm-container-body">
<p>请牢记这一条 JUnit 最佳实践：测试任何可能的错误。单元测试不是用来证明您是对的，而是为了证明您没有错。</p></div></div>
<p>啊哈，再次运行测试，绿条又重现眼前。通过对 WordDealUtil.wordFormat4DB 比较全面的单元测试，现在的代码已经比较稳定，可以作为 API 的一部分提供给其它模块使用了。</p>
<p>不知不觉中我们已经使用 JUnit 漂亮的完成了一次单元测试。可以体会到 JUnit 是多么轻量级，多么简单，根本不需要花心思去研究，这就可以把更多的注意力放在更有意义的事情上&#8212;&#8212;编写完整全面的单元测试。</p>
<div class="ibm-alternate-rule">
<hr />
</div>
<p class="ibm-ind-link ibm-back-to-top" sizset="92" sizcache="22"><a class="ibm-anchor-up-link" href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#ibm-pcon">回页首</a></p>
<p sizset="93" sizcache="22"><a name="N10135"><span class="atitle">JUnit 深入</span></a></p>
<p>当然，JUnit 提供的功能决不仅仅如此简单，在接下来的内容中，我们会看到 JUnit 中很多有用的特性，掌握它们对您灵活的编写单元测试代码非常有帮助。</p>
<p sizset="94" sizcache="22"><a name="N1013F"><span class="smalltitle">Fixture</span></a></p>
<p>何谓 Fixture ？它是指在执行一个或者多个测试方法时需要的一系列公共资源或者数据，例如测试环境，测试数据等等。在编写单元测试的过程中，您会发现在大部分的测试方法在进行真正的测试之前都需要做大量的铺垫&#8212;&#8212;为设计准备 Fixture 而忙碌。这些铺垫过程占据的代码往往比真正测试的代码多得多，而且这个比率随着测试的复杂程度的增加而递增。当多个测试方法都需要做同样的铺垫时，重复代码的&#8220;坏味道&#8221;便在测试代码中弥漫开来。这股&#8220;坏味道&#8221;会弄脏您的代码，还会因为疏忽造成错误，应该使用一些手段来根除它。</p>
<p>JUnit 专门提供了设置公共 Fixture 的方法，同一测试类中的所有测试方法都可以共用它来初始化 Fixture 和注销 Fixture。和编写 JUnit 测试方法一样，公共 Fixture 的设置也很简单，您只需要：</p>
<ol><li>使用注解 org,junit.Before 修饰用于初始化 Fixture 的方法。</li><li>使用注解 org.junit.After 修饰用于注销 Fixture 的方法。</li><li>保证这两种方法都使用 public void 修饰，而且不能带有任何参数。 </li></ol>
<p>遵循上面的三条原则，编写出的代码大体是这个样子：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> // 初始化 Fixture 方法
 @Before public void init(){ &#8230;&#8230; } 

 // 注销 Fixture 方法
 @After public void destroy(){ &#8230;&#8230; } 
</pre></td></tr></tbody></table><br />
<p sizset="95" sizcache="22">这样，在每一个测试方法执行之前，JUnit 会保证 init 方法已经提前初始化测试环境，而当此测试方法执行完毕之后，JUnit 又会调用 destroy 方法注销测试环境。注意是每一个测试方法的执行都会触发对公共 Fixture 的设置，也就是说使用注解 Before 或者 After 修饰的公共 Fixture 设置方法是方法级别的（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#figure005">图 5</a>）。这样便可以保证各个独立的测试之间互不干扰，以免其它测试代码修改测试环境或者测试数据影响到其它测试代码的准确性。</p><br /><a name="figure005"><strong>图 5 方法级别 Fixture 执行示意图</strong></a><br /><img alt="图 5 方法级别 Fixture 执行示意图" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure005.gif" width="400" height="113" /> <br />
<p>可是，这种 Fixture 设置方式还是引来了批评，因为它效率低下，特别是在设置 Fixture 非常耗时的情况下（例如设置数据库链接）。而且对于不会发生变化的测试环境或者测试数据来说，是不会影响到测试方法的执行结果的，也就没有必要针对每一个测试方法重新设置一次 Fixture。因此在 JUnit 4 中引入了类级别的 Fixture 设置方法，编写规范如下：</p>
<ol><li>使用注解 org,junit.BeforeClass 修饰用于初始化 Fixture 的方法。</li><li>使用注解 org.junit.AfterClass 修饰用于注销 Fixture 的方法。</li><li>保证这两种方法都使用 public static void 修饰，而且不能带有任何参数。 </li></ol>
<p sizset="97" sizcache="22">类级别的 Fixture 仅会在测试类中所有测试方法执行之前执行初始化，并在全部测试方法测试完毕之后执行注销方法（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#figure006">图 6</a>）。代码范本如下：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> // 类级别 Fixture 初始化方法
 @BeforeClass public static void dbInit(){ &#8230;&#8230; } 
	
 // 类级别 Fixture 注销方法
	 @AfterClass public static void dbClose(){ &#8230;&#8230; } 
</pre></td></tr></tbody></table><br /><br /><a name="figure006"><strong>图 6 类级别 Fixture 执行示意图</strong></a><br /><img alt="图 6 类级别 Fixture 执行示意图" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure006.gif" width="502" height="36" /> <br />
<p sizset="99" sizcache="22"><a name="N101A0"><span class="smalltitle">异常以及时间测试</span></a></p>
<p>注解 org.junit.Test 中有两个非常有用的参数：expected 和 timeout。参数 expected 代表测试方法期望抛出指定的异常，如果运行测试并没有抛出这个异常，则 JUnit 会认为这个测试没有通过。这为验证被测试方法在错误的情况下是否会抛出预定的异常提供了便利。举例来说，方法 supportDBChecker 用于检查用户使用的数据库版本是否在系统的支持的范围之内，如果用户使用了不被支持的数据库版本，则会抛出运行时异常 UnsupportedDBVersionException。测试方法 supportDBChecker 在数据库版本不支持时是否会抛出指定异常的单元测试方法大体如下：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> @Test(expected=UnsupportedDBVersionException.class) 
	 public void unsupportedDBCheck(){ 
		&#8230;&#8230;
 } 
</pre></td></tr></tbody></table><br />
<p>注解 org.junit.Test 的另一个参数 timeout，指定被测试方法被允许运行的最长时间应该是多少，如果测试方法运行时间超过了指定的毫秒数，则 JUnit 认为测试失败。这个参数对于性能测试有一定的帮助。例如，如果解析一份自定义的 XML 文档花费了多于 1 秒的时间，就需要重新考虑 XML 结构的设计，那单元测试方法可以这样来写：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> @Test(timeout=1000) 
	 public void selfXMLReader(){ 
		&#8230;&#8230;
 } 
</pre></td></tr></tbody></table><br />
<p sizset="100" sizcache="22"><a name="N101B5"><span class="smalltitle">忽略测试方法</span></a></p>
<p>JUnit 提供注解 org.junit.Ignore 用于暂时忽略某个测试方法，因为有时候由于测试环境受限，并不能保证每一个测试方法都能正确运行。例如下面的代码便表示由于没有了数据库链接，提示 JUnit 忽略测试方法 unsupportedDBCheck：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> @ Ignore(&#8220;db is down&#8221;) 
 @Test(expected=UnsupportedDBVersionException.class) 
	 public void unsupportedDBCheck(){ 
		&#8230;&#8230;
 } 
</pre></td></tr></tbody></table><br />
<p>但是一定要小心。注解 org.junit.Ignore 只能用于暂时的忽略测试，如果需要永远忽略这些测试，一定要确认被测试代码不再需要这些测试方法，以免忽略必要的测试点。</p>
<p sizset="101" sizcache="22"><a name="N101C6"><span class="smalltitle">测试运行器</span></a></p>
<p>又一个新概念出现了&#8212;&#8212;测试运行器，JUnit 中所有的测试方法都是由它负责执行的。JUnit 为单元测试提供了默认的测试运行器，但 JUnit 并没有限制您必须使用默认的运行器。相反，您不仅可以定制自己的运行器（所有的运行器都继承自 org.junit.runner.Runner），而且还可以为每一个测试类指定使用某个具体的运行器。指定方法也很简单，使用注解 org.junit.runner.RunWith 在测试类上显式的声明要使用的运行器即可：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> @RunWith(CustomTestRunner.class) 
 public class TestWordDealUtil { 
&#8230;&#8230;
 } 
</pre></td></tr></tbody></table><br />
<p>显而易见，如果测试类没有显式的声明使用哪一个测试运行器，JUnit 会启动默认的测试运行器执行测试类（比如上面提及的单元测试代码）。一般情况下，默认测试运行器可以应对绝大多数的单元测试要求；当使用 JUnit 提供的一些高级特性（例如即将介绍的两个特性）或者针对特殊需求定制 JUnit 测试方式时，显式的声明测试运行器就必不可少了。</p>
<p sizset="102" sizcache="22"><a name="N101D7"><span class="smalltitle">测试套件</span></a></p>
<p>在实际项目中，随着项目进度的开展，单元测试类会越来越多，可是直到现在我们还只会一个一个的单独运行测试类，这在实际项目实践中肯定是不可行的。为了解决这个问题，JUnit 提供了一种批量运行测试类的方法，叫做测试套件。这样，每次需要验证系统功能正确性时，只执行一个或几个测试套件便可以了。测试套件的写法非常简单，您只需要遵循以下规则：</p>
<ol><li>创建一个空类作为测试套件的入口。</li><li>使用注解 org.junit.runner.RunWith 和 org.junit.runners.Suite.SuiteClasses 修饰这个空类。</li><li>将 org.junit.runners.Suite 作为参数传入注解 RunWith，以提示 JUnit 为此类使用套件运行器执行。</li><li>将需要放入此测试套件的测试类组成数组作为注解 SuiteClasses 的参数。</li><li>保证这个空类使用 public 修饰，而且存在公开的不带有任何参数的构造函数。 </li></ol>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> package com.ai92.cooljunit; 

 import org.junit.runner.RunWith; 
 import org.junit.runners.Suite; 
&#8230;&#8230;

 /** 
 * 批量测试 工具包 中测试类
 * @author Ai92 
 */ 
 @RunWith(Suite.class) 
 @Suite.SuiteClasses({TestWordDealUtil.class}) 
 public class RunAllUtilTestsSuite { 
 } 
</pre></td></tr></tbody></table><br />
<p>上例代码中，我们将前文提到的测试类 TestWordDealUtil 放入了测试套件 RunAllUtilTestsSuite 中，在 Eclipse 中运行测试套件，可以看到测试类 TestWordDealUtil 被调用执行了。测试套件中不仅可以包含基本的测试类，而且可以包含其它的测试套件，这样可以很方便的分层管理不同模块的单元测试代码。但是，您一定要保证测试套件之间没有循环包含关系，否则无尽的循环就会出现在您的面前&#8230;&#8230;。</p>
<p sizset="103" sizcache="22"><a name="N101FA"><span class="smalltitle">参数化测试</span></a></p>
<p sizset="104" sizcache="22">回顾一下我们在小节&#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#major2">JUnit 初体验</a>&#8221;中举的实例。为了保证单元测试的严谨性，我们模拟了不同类型的字符串来测试方法的处理能力，为此我们编写大量的单元测试方法。可是这些测试方法都是大同小异：代码结构都是相同的，不同的仅仅是测试数据和期望值。有没有更好的方法将测试方法中相同的代码结构提取出来，提高代码的重用度，减少复制粘贴代码的烦恼？在以前的 JUnit 版本上，并没有好的解决方法，而现在您可以使用 JUnit 提供的参数化测试方式应对这个问题。</p>
<p>参数化测试的编写稍微有点麻烦（当然这是相对于 JUnit 中其它特性而言）：</p>
<ol><li>为准备使用参数化测试的测试类指定特殊的运行器 org.junit.runners.Parameterized。</li><li>为测试类声明几个变量，分别用于存放期望值和测试所用数据。</li><li>为测试类声明一个使用注解 org.junit.runners.Parameterized.Parameters 修饰的，返回值为 java.util.Collection 的公共静态方法，并在此方法中初始化所有需要测试的参数对。</li><li>为测试类声明一个带有参数的公共构造函数，并在其中为第二个环节中声明的几个变量赋值。</li><li>编写测试方法，使用定义的变量作为参数进行测试。 </li></ol>
<p>我们按照这个标准，重新改造一番我们的单元测试代码：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">package com.ai92.cooljunit; 

import static org.junit.Assert.assertEquals; 
import java.util.Arrays; 
import java.util.Collection; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.junit.runners.Parameterized; 
import org.junit.runners.Parameterized.Parameters; 

@RunWith(Parameterized.class) 
public class TestWordDealUtilWithParam { 

        private String expected; 
    
        private String target; 
    
        @Parameters 
        public static Collection words(){ 
            return Arrays.asList(new Object[][]{ 
                {"employee_info", "employeeInfo"},      // 测试一般的处理情况
                {null, null},                           // 测试 null 时的处理情况
                {"", ""},                               // 测试空字符串时的处理情况
                {"employee_info", "EmployeeInfo"},      // 测试当首字母大写时的情况
                {"employee_info_a", "employeeInfoA"},   // 测试当尾字母为大写时的情况
                {"employee_a_info", "employeeAInfo"}    // 测试多个相连字母大写时的情况
            }); 
        } 
    
         /** 
         * 参数化测试必须的构造函数
         * @param expected     期望的测试结果，对应参数集中的第一个参数
         * @param target     测试数据，对应参数集中的第二个参数
         */ 
        public TestWordDealUtilWithParam(String expected , String target){ 
            this.expected = expected; 
            this.target = target; 
        } 
    
         /** 
         * 测试将 Java 对象名称到数据库名称的转换
         */ 
        @Test public void wordFormat4DB(){ 
            assertEquals(expected, WordDealUtil.wordFormat4DB(target)); 
        } 
} 
</pre></td></tr></tbody></table><br />
<p>很明显，代码瘦身了。在静态方法 words 中，我们使用二维数组来构建测试所需要的参数列表，其中每个数组中的元素的放置顺序并没有什么要求，只要和构造函数中的顺序保持一致就可以了。现在如果再增加一种测试情况，只需要在静态方法 words 中添加相应的数组即可，不再需要复制粘贴出一个新的方法出来了。</p>
<div class="ibm-alternate-rule">
<hr />
</div>
<p class="ibm-ind-link ibm-back-to-top" sizset="105" sizcache="22"><a class="ibm-anchor-up-link" href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#ibm-pcon">回页首</a></p>
<p sizset="106" sizcache="22"><a name="N10227"><span class="atitle">JUnit 和 Ant</span></a></p>
<p>随着项目的进展，项目的规模在不断的膨胀，为了保证项目的质量，有计划的执行全面的单元测试是非常有必要的。但单靠 JUnit 提供的测试套件很难胜任这项工作，因为项目中单元测试类的个数在不停的增加，测试套件却无法动态的识别新加入的单元测试类，需要手动修改测试套件，这是一个很容易遗忘得步骤，稍有疏忽就会影响全面单元测试的覆盖率。</p>
<p>当然解决的方法有多种多样，其中将 JUnit 与构建利器 Ant 结合使用可以很简单的解决这个问题。Ant &#8212;&#8212; 备受赞誉的 Java 构建工具。它凭借出色的易用性、平台无关性以及对项目自动测试和自动部署的支持，成为众多项目构建过程中不可或缺的独立工具，并已经成为事实上的标准。Ant 内置了对 JUnit 的支持，它提供了两个 Task：junit 和 junitreport，分别用于执行 JUnit 单元测试和生成测试结果报告。使用这两个 Task 编写构建脚本，可以很简单的完成每次全面单元测试的任务。</p>
<p sizset="107" sizcache="22">不过，在使用 Ant 运行 JUnit 之前，您需要稍作一些配置。打开 Eclipse 首选项界面，选择 Ant -&gt; Runtime 首选项（见 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#figure007">图 7</a>），将 JUnit 4.1 的 JAR 文件添加到 Classpath Tab 页中的 Global Entries 设置项里。记得检查一下 Ant Home Entries 设置项中的 Ant 版本是否在 1.7.0 之上，如果不是请替换为最新版本的 Ant JAR 文件。</p><br /><a name="figure007"><strong>图 7 Ant Runtime 首选项</strong></a><br /><img alt="图 7 Ant Runtime 首选项" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure007.jpg" width="553" height="502" /> <br />
<p>剩下的工作就是要编写 Ant 构建脚本 build.xml。虽然这个过程稍嫌繁琐，但这是一件一劳永逸的事情。现在我们就把前面编写的测试用例都放置到 Ant 构建脚本中执行，为项目 coolJUnit 的构建脚本添加一下内容：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode"> &lt;?xml version="1.0"?&gt; 
 &lt;!-- ============================================= 
     auto unittest task    
     ai92                                                                
     ========================================== --&gt; 
 &lt;project name="auto unittest task" default="junit and report" basedir="."&gt; 

		 &lt;property name="output folder" value="bin"/&gt; 

		 &lt;property name="src folder" value="src"/&gt; 
	
		 &lt;property name="test folder" value="testsrc"/&gt; 
	
		 &lt;property name="report folder" value="report" /&gt; 

		 &lt;!-- - - - - - - - - - - - - - - - - - 
          target: test report folder init                      
         - - - - - - - - - - - - - - - - - --&gt; 
		 &lt;target name="test init"&gt; 
			 &lt;mkdir dir="${report folder}"/&gt; 
		 &lt;/target&gt; 
	
		 &lt;!-- - - - - - - - - - - - - - - - - - 
          target: compile                      
         - - - - - - - - - - - - - - - - - --&gt; 
		 &lt;target name="compile"&gt; 
			 &lt;javac srcdir="${src folder}" destdir="${output folder}" /&gt; 
			 &lt;echo&gt;compilation complete!&lt;/echo&gt; 
		 &lt;/target&gt; 

		 &lt;!-- - - - - - - - - - - - - - - - - - 
          target: compile test cases                      
         - - - - - - - - - - - - - - - - - --&gt; 
		 &lt;target name="test compile" depends="test init"&gt; 
			 &lt;javac srcdir="${test folder}" destdir="${output folder}" /&gt; 
			 &lt;echo&gt;test compilation complete!&lt;/echo&gt; 
		 &lt;/target&gt; 
	
		 &lt;target name="all compile" depends="compile, test compile"&gt; 
		 &lt;/target&gt; 
	
		 &lt;!-- ======================================== 
          target: auto test all test case and output report file                      
      	 ===================================== --&gt; 
		 &lt;target name="junit and report" depends="all compile"&gt; 
			 &lt;junit printsummary="on" fork="true" showoutput="true"&gt; 
				 &lt;classpath&gt; 
					 &lt;fileset dir="lib" includes="**/*.jar"/&gt; 
					 &lt;pathelement path="${output folder}"/&gt; 
				 &lt;/classpath&gt; 
				 &lt;formatter type="xml" /&gt; 
				 &lt;batchtest todir="${report folder}"&gt; 
					 &lt;fileset dir="${output folder}"&gt; 
						 &lt;include name="**/Test*.*" /&gt; 
					 &lt;/fileset&gt; 
				 &lt;/batchtest&gt; 
			 &lt;/junit&gt; 
			 &lt;junitreport todir="${report folder}"&gt; 
				 &lt;fileset dir="${report folder}"&gt; 
					 &lt;include name="TEST-*.xml" /&gt; 
				 &lt;/fileset&gt; 
				 &lt;report format="frames" todir="${report folder}" /&gt; 
			 &lt;/junitreport&gt; 
		 &lt;/target&gt; 
 &lt;/project&gt; 
</pre></td></tr></tbody></table><br />
<p sizset="109" sizcache="22">Target junit report 是 Ant 构建脚本中的核心内容，其它 target 都是为它的执行提供前期服务。Task junit 会寻找输出目录下所有命名以&#8220;Test&#8221;开头的 class 文件，并执行它们。紧接着 Task junitreport 会将执行结果生成 HTML 格式的测试报告（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#figure008">图 8</a>）放置在&#8220;report folder&#8221;下。</p>
<p>为整个项目的单元测试类确定一种命名风格。不仅是出于区分类别的考虑，这为 Ant 批量执行单元测试也非常有帮助，比如前面例子中的测试类都已&#8220;Test&#8221;打头，而测试套件则以&#8220;Suite&#8221;结尾等等。</p><br /><a name="figure008"><strong>图 8 junitreport 生成的测试报告</strong></a><br /><img alt="图 8 junitreport 生成的测试报告" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure008.jpg" width="553" height="330" /> <br />
<p sizset="111" sizcache="22">现在执行一次全面的单元测试变得非常简单了，只需要运行一下 Ant 构建脚本，就可以走完所有流程，并能得到一份详尽的测试报告。您可以在 <a href="http://ant.apache.org/manual/index.html">Ant 在线手册</a>中获得上面提及的每一个 Ant 内置 task 的使用细节。</p>
<div class="ibm-alternate-rule">
<hr />
</div>
<p class="ibm-ind-link ibm-back-to-top" sizset="112" sizcache="22"><a class="ibm-anchor-up-link" href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/#ibm-pcon">回页首</a></p>
<p sizset="113" sizcache="22"><a name="N10273"><span class="atitle">总结</span></a></p>
<p>随着越来越多的开发人员开始认同并接受极限编程（XP）的思想，单元测试的作用在软件工程中变得越来越重要。本文旨在将最新的单元测试工具 JUnit 4 介绍给您，以及如何结合 IDE Eclipse 和构建工具 Ant 创建自动化单元测试方案。并且还期望您能够通过本文&#8220;感染&#8221;一些好的单元测试意识，因为 JUnit 本身仅仅是一份工具而已，它的真正优势来自于它的思想和技术。</p>  <img src ="http://www.blogjava.net/justuszhang2009/aggbug/352430.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-06-24 13:06 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352430.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】持续集成工具</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352182.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Fri, 24 Jun 2011 05:06:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352182.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/352182.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352182.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/352182.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/352182.html</trackback:ping><description><![CDATA[<table class="seyle4" border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="content9" valign="middle"><strong></strong></td></tr>
<tr>
<td>
<div align="right"><span class="textbox-label">[ 2009-12-23 9:25:00 | By: <span class="style3">王海鹏</span> ]</span></div></td></tr></tbody></table>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td height="3">&nbsp;</td></tr></tbody></table>
<table style="table-layout: fixed" border="0" cellspacing="0" cellpadding="0" width="100%" align="center">
<tbody>
<tr>
<td><span class="oblog_text"><span id="ob_logd41613">
<div style="text-align: center; margin: 0px 10px 5px 0px; width: 45px; display: inline;background: url(http://www.blogjava.net/Images/digg.gif) no-repeat left top; float: right; height: 55px" class="digg_list">
<div style="padding-bottom: 11px; line-height: 1; padding-left: 0px; width: 45px; padding-right: 0px; font-family: tahoma,Arial,Helvetica,sans-serif; color: #333; font-size: 18px; font-weight: 600; padding-top: 10px" class="digg_number">0</div>
<div style="padding-bottom: 0px; line-height: 1; padding-left: 6px; padding-right: 0px; letter-spacing: 6px; padding-top: 3px" class="digg_submit"><a style="line-height: 1; font-size: 12px" href="javascript:void(null)">推荐</a></div></div></span>
<p>引言</p>
<p>Thomas Carlyle说：&#8220;人类是使用工具的动物。没有工具，人什么都不是；有了工具，人无所不能。&#8221;金融家们创造了复杂的金融工具，并利用这些工具制造了财富神话，制造了著名的跨国公司，也制造了世界范围的危机。软件精英们为了让自己的工作效率更高，有更多时间去做想做的事，也创造了各式各样的工具。持续集成已经不是一个新概念，在这个概念发展的十多年间，出现了支持这一概念的众多工具。这些工具的组合使用，为软件开发提供了强大的支持。</p>
<p>持续集成工具的分类和功能</p>
<p>一般来说，持续集成工具可以分成两大类：自动化构建工具和构建计划安排工具。</p>
<p>自动化构建工具有这样一些基本功能：代码编译、组件打包、程序执行和文件操作。编译源代码是构建的主要工作之一，为了提高效率，编译应该根据相应的源代码是否发生改变而有条件地执行。组件打包是将编译的结果和其他需要包含的文件组织在一起，形成可以部署的组件。构建工具应该知道何时需要重新打包。程序执行是指构建工具能够在它支持的平台上，调用所有提供命令行接口的程序。构建工具应该支持创建、拷贝、删除文件和目录等操作。</p>
<p>某些自动化构建工具还有一些扩展功能：执行开发者测试、版本控制工具集成、文档集成、部署功能、代码品质分析、支持扩展、多平台构建、加速构建。虽然构建工具可以通过命令行执行的方式来集成构建工具和测试工具，但如果它提供更直接的集成方式，开发者就更省力。同样，如果构建工具能够直接与版本控制工具集成，开发者也会觉得更方便。文档集成是指构建工具能够自动从源代码中抽取并生成API文档。构建工具还可以将打包好的组件自动部署到目标测试环境中去。构建工具一般通过一些第三方插件，支持对代码品质进行分析。而提供插件接口，是构建工具实现可扩展性的通用方式。如果您开发的软件需要在多个平台上构建并测试，那么构建工具对多平台的支持就会带来极大的方便。对于较大的代码集，一次构建可能需要好几个小时，这为持续集成带来了一些挑战。有的构建工具支持加速构建，即在多个构建服务器的多个处理器上进行分布式构建。</p>
<p>常见的自动化构建工具包括Ant、NAnt、MSBuild、make、Maven、Rake等。</p>
<p>构建计划安排工具有这样一些基本功能：构建执行、版本控制集成、构建工具集成、提供反馈、为构建打上标签。构建计划安排工具的核心功能就是在特定时间执行自动化的构建，这可以通过轮询版本控制库、计划驱动或事件通知等方式来实现。大部分构建计划安排工具都支持大多数流行的版本控制系统，也支持大多数流行的构建工具。构建计划安排工具至少支持通过电子邮件提供反馈信息，有一些工具可以通过即时消息、手机短信或其他设备来提供反馈。大多数构建计划安排工具会提供某种类型的升序计数，作为构建版本的标签。</p>
<p>某些构建计划安排工具还有一些扩展功能：支持项目间依赖关系、提供用户界面、制品发布、安全。如果项目间存在依赖关系，您可能希望在被依赖的项目重新构建时，重新构建依赖于它的项目。设计良好的用户界面会在工作时为您节约时间。制品发布是指除了得到可部署的组件之外，一些成熟的某些构建计划安排工具可以将文档、测试结果、品质分析结构和其他测量指标数据格式化，便于查看。有一些工具提供了身份认证和授权等安全方面的功能，允许您指定谁能查看结果和修改配置。</p>
<p>常见的构建计划安排工具包括AnthillPro、Continuum、CruiseControl、CruiseControl.NET、Draco.NET、Luntbuild、Hudson等。</p>
<p>下面介绍两个颇具代表性的工具：Ant和Hudson。</p>
<p>Ant</p>
<p>Ant是Java构建工具的事实标准，一般建议，不论项目团队成员使用哪种集成开发环境，项目都要有一个可以脱离IDE执行的Ant脚本。Ant采用插件式的设计结构，通过不同的插件来实现各种任务，其任务分类如表1所示。</p>
<p>Archive Tasks<br />&nbsp;打包解包任务，支持的格式包括：BZip2、Cab、Ear、GZip、Jar、Rpm、Tar、War、Zip。<br />&nbsp;<br />Audit/Coverage Tasks<br />&nbsp;JDepend任务，调用JDepend实现代码静态分析，针对每个Java包生成设计品质指标数据。<br />&nbsp;<br />Compile Tasks<br />&nbsp;编译任务，实现对Java、JSP、NetRexx等源文件的编译。<br />&nbsp;<br />Deployment Tasks<br />&nbsp;部署任务，实现在JavaEE服务器上热部署。<br />&nbsp;<br />Documentation Tasks<br />&nbsp;文档生成任务，生成javadoc文档、Apache Stylebook文档。<br />&nbsp;<br />EJB Tasks<br />&nbsp;EJB任务，提供对1.x和2.x的EJB的支持，并支持不同供应商的应用服务器。<br />&nbsp;<br />Execution Tasks<br />&nbsp;执行任务，包括对子项目调用Ant、调用同一脚本中的另一个target、执行系统提供的命令行程序、执行Java程序、暂停和并行同步执行等功能。<br />&nbsp;<br />File Tasks<br />&nbsp;文件任务，实现对文件和目录的操作。<br />&nbsp;<br />Java2 Extensions Tasks<br />&nbsp;Java2 扩展信息任务，对jar包中的版本、供应商等扩展信息进行检查和操作。<br />&nbsp;<br />Logging Tasks<br />&nbsp;日志任务，将构建过程事件记录到文件中。<br />&nbsp;<br />Mail Tasks<br />&nbsp;邮件任务，发送SMTP邮件。<br />&nbsp;<br />Miscellaneous Tasks<br />&nbsp;其他任务，各种或许会用到的小任务，例如播放wav文件。<br />&nbsp;<br />.NET Tasks<br />&nbsp;.NET任务，支持执行.NET程序、执行NUnit测试、调用NAnt、调用MSBuild、调用WiX工具。<br />&nbsp;<br />Pre-process Tasks<br />&nbsp;预处理任务，实现编译之前的一些预处理。例如调用ANTLR、JavaCC、Native2Ascii等程序。<br />&nbsp;<br />Property Tasks<br />&nbsp;属性任务，对脚本中的属性变量进行判断和操作。<br />&nbsp;<br />Remote Tasks<br />&nbsp;远程任务，支持FTP、Rexec、Scp、SSH和Telnet。<br />&nbsp;<br />SCM Tasks<br />&nbsp;SCM任务，支持各种配置管理（版本控制）软件，包括CVS、ClearCase、Continuus、Visual SourceSafe、Perforce、PVCS、SourceOffSite和StarTeam。<br />&nbsp;<br />Testing Tasks<br />&nbsp;测试任务，支持执行JUnit测试。<br />&nbsp;<br />表1. Ant任务分类</p>
<p>以上介绍的只是Ant发行版所带的一些任务。由于Ant采用的是插件结构，所以开发者可以开发自己需要的Ant任务，支持各种工具，如FindBugs、TestNG等其他代码检查工具和测试工具。早期的Ant没有很好的依赖关系支持，后来则通过Ivy弥补了这一缺点。</p>
<p>关键是Ant为我们提供了一个跨平台的Java构建工具，为持续集成提供了根本的支持。对于Java开发者来说，如果不想采用Ant，也可以考虑采用Maven。</p>
<p>Hudson</p>
<p>Hudson是一个开放源代码的CI服务器，受到世界各地各种规模和类型的开发团队的欢迎。关键是因为它非常易于安装和使用，提供了灵活的配置方式和复杂的功能，同时支持Java项目和非Java项目，由强大的Hudson社区提供技术支持。</p>
<p>简而言之，Hudson不仅仅是一个CI服务器，它的可扩展架构使它不仅是一个构建管理系统，也成为一个通用的开发生命周期管理系统，让开发者能够完成提升基线、打标签、执行工作流、根据依赖关系追踪变更、监视并图示测试结果、查看代码覆盖率和违反编码标准的情况等任务。</p>
<p>Hudson是最活跃，成长最快的开源社区之一，目前每周下载达4000次，有超过2万个在工作的安装实例。它的开发者超过160人，贡献的工作量超过137人年，目前已发布了超过300个发行版本。Hudson实际上是现在世界上最受欢迎的开源CI服务器。</p>
<p>图1是Apache软件基金会运行Hudson的屏幕截图，您可以在<a href="http://wiki.hudson-ci.org/display/HUDSON/Meet+Hudson">http://wiki.hudson-ci.org/display/HUDSON/Meet+Hudson</a> 看到更多Hudson的使用案例。</p>
<p>图1. Apache运行的Hudson</p>
<p>Huddon的主要优点包括：</p>
<p>易于安装。只要执行&#8220;java &#8211;jar hudson.war&#8221;，或者将hudson.war部署到应用服务器上就可以了，不需要其他的安装工作，也不需要建立数据库。 <br />易于配置。所有东西都通过Web GUI界面来配置，不需要手工修改XML文件。 <br />支持分布式构建。Hudson支持将构建和测试负载分布到多台机器上，图2是Apache采用Hudson的分布式构建功能。 <br />支持环境配置矩阵。Hudson支持在不同的环境配置下执行相同的任务，例如不同的JDK版本、不同的操作系统、不同的数据库。执行的结果可以汇总在一起。 <br />支持JUnit/TestNG测试报告。测试的结果可以分标签列出、汇总，并与历史信息一同显示。历史趋势可以显示在图中。 <br />追踪依赖关系。Hudson追踪记录哪次构建生成了哪些jar，某次构建使用了哪些版本的jar，即使这些jar包来自于外部也可以。 </p>
<p>图2. Hudson支持的分布式构建</p>
<p>Hudson通过大量的插件来实现其丰富的功能，这些插件大致可以分为以下几类：</p>
<p>SCM。Hudson缺省支持CVS和Subversion，通过安装插件支持Accurev、Bitkeeper、ClearCase、Git、Mercurial、Perforce、StarTeam、Synergy等 <br />构建触发器。可以通过IRC、Ivy、Jabber、Join、Locks and Latches、Navigator来触发执行构建。 <br />构建工具。缺省支持Ant、Maven、shell s和Windows 批处理命令，通过安装插件支持batch tasks、Gant、Gradle、Grails、Groovy、Jython、Kundo、MSBuild、Phing、Powershell、Python、Rake、Ruby、SCons、SCTMExecutor,Selenium等。 <br />构建包装。对构建的方式进行一些控制，如并发同步、启停虚拟机等。包括Hudson Centralized Job Action、Hudson Distributed Workspace Clean、Locks and Latches、M2 Extra Steps、M2 Release、Release、VMware、Xvnc、Zen Timestamp等。 <br />构建通知。缺省支持电子邮件通知，通过插件支持Campfire、Google Calendar、HudsonTracker (RSS feeds)、IRC、Jabber、Nabaztag、SameTime、Status Monitor、The new Emailer、TuxDroid、Twitter等。 <br />Slave启动和控制。缺省支持JNLP和命令行，通过插件支持SSH。 <br />构建报告。缺省支持JUnit、javadoc和FindBugs，通过插件支持CCCC、Checkstyle、Clover、DRY、Emma、Gallio、Gnat、Grinder、Japaex、JavaNCS、JavaTest Report、MSTest、N Cover、NUint、Plot、PMD、PureCoverage、Ruby metrics、Selenium AES、Selenium hq、Serenitec、SLOCCount、Task Scanner、Testability Explorer、Violations、Warnings、WebTest Presenter等。图3是Sonar生成的项目报告的样例。 <br />集群管理/分布式构建。支持DistFork、Hadoop、PXE、Selenium、Swarm等。 <br />制品上传。支持FTP-Publisher、java.net uploader、SCP,SFEE、SVN等。 <br />身份认证和用户管理。支持操作审计追踪、LDAP、MySQL认证等。 </p>
<p>图3. Sonar Dashboard</p>
<p>结束语</p>
<p>工欲善其事，必先利其器。人是工具的主宰。A fool with a tool is still a fool（傻子拿着工具还是傻子）。人们总是在学习工具、使用工具、创造更好的工具，以期提高工作的效率和品质。人要有智慧，工具要先进。<br /></p></span></td></tr></tbody></table>  <img src ="http://www.blogjava.net/justuszhang2009/aggbug/352182.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-06-24 13:06 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352182.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring和CXF集成来实现webservices</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352944.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Fri, 24 Jun 2011 05:05:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352944.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/352944.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352944.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/352944.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/352944.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 最近在负责一个大系统的实施，经过需求分析之后，将系统分为5个子系统，我们采用SOA架构，分模块开发。项目组中最大的一个争议就是，子系统之间的通讯问题，大家提出了两种方案：一、如果5个子系统最后发布为5个war包，那么相互之间就不能直接调用，而是需要通过webservices等通讯方式，那会增加一些开发工作量；二、如果5个子系统合并在一个大工程中，下面放所有的模块，那子系统间的访问很简单，但是日常的...&nbsp;&nbsp;<a href='http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352944.html'>阅读全文</a><img src ="http://www.blogjava.net/justuszhang2009/aggbug/352944.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-06-24 13:05 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/06/24/352944.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>mybatis批量更新的问题</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/06/21/352698.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Mon, 20 Jun 2011 23:19:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/06/21/352698.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/352698.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/06/21/352698.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/352698.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/352698.html</trackback:ping><description><![CDATA[<p>一、问题描述<br />场景描述：有这样一个service方法，调用了两个dao中的方法。第一个方法按照传入的id批量更新用户名。第二个dao方法无数据库操作，仅仅抛出一个RuntimeException. <br />这个service方法通过xml配置由spring事务管理的。<br />两个DAO类中分别有SqlSessionTemplate类型的属性template，使用IOC的方式注入的。<br />&nbsp;public void batchUpdate() {<br />&nbsp;&nbsp;String username="newname59";</p>
<p>&nbsp;&nbsp;List&lt;Integer&gt; idList=Arrays.asList(10000,10001);<br />&nbsp;&nbsp;userDao.batchUpdateUsername(username, idList);<br />&nbsp;&nbsp;testDao.testException();<br />&nbsp;&nbsp;userDao.batchUpdateUserage(55, idList);<br />&nbsp;&nbsp;testDao.testNormal();&nbsp;<br />&nbsp;}<br />当userDao及testDao中注入的是ExcutorType.Simple类型的template时，批量更新用户名的操作会回滚。<br />当userDao及testDao中注入的是ExcutorType.Batch类型的template时，批量更新用户名的操作未回滚。</p>
<p>经过检查数据库日志，发现第二种情况的数据库执行序列如下：<br />1 set autocommit = 0<br />2 rollback<br />3 update t_user set username="newname59" where id = '10000'<br />4 update t_user set username="newname59" where id = '10001'<br />5 set autocommit = 1<br />更新操作在回滚之后执行，故回滚失败。</p>
<p>调试源代码发现有如下序列：<br />AbstractPlatformTransactionManager <br />&nbsp;processRollback （） --&gt; triggerAfterCompletion() --&gt; invokeAfterCompletion()<br />--&gt; <br />TransactionSynchronizationUtils <br />&nbsp;invokeAfterCompletion() <br />--&gt; <br />SqlSessionUtils<br />&nbsp;&nbsp;&nbsp; afterCompletion()<br />--&gt;<br />DefaultSqlSession<br />&nbsp;close()<br />--&gt;<br />BaseExecutor<br />&nbsp;close() --&gt; rollback() --&gt; flushStatement()<br />--&gt;<br />BatchExecutor<br />&nbsp;doFlushStatements()<br />这时就执行了sql语句。<br />&nbsp;<br />简单来说：抛出异常，spring事务回滚，清理资源关闭sqlSession.<br />mybatis关闭sqlsession,关闭前先flushStatements，执行未执行的sql语句，然后再rollback.<br />但是这个rollback方法里判断connection是受事务管理的，就不执行任何操作。<br />&nbsp;<br />&nbsp;public void rollback(boolean required) throws SQLException {<br />&nbsp;&nbsp;&nbsp; if (!closed) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clearLocalCache();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; flushStatements();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } finally {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (required) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transaction.rollback();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }&nbsp;</p>
<p>&nbsp;&nbsp; public void rollback() throws SQLException {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!this.isConnectionTransactional) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this.logger.isDebugEnabled()) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.logger.debug("Rolling back JDBC Connection [" + this.connection + "]");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.connection.rollback();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;<br />二、解决办法：<br />&nbsp;1、在自己的应用程序中写个拦截器。在执行完executor的close()之后，由这个拦截器再执行一遍connection.rollback()，但从代码的可读性来看，会非常的差。<br />&nbsp;2、修改mybatis的bug。修改BaseExecutor的rollback()<br />&nbsp;public void rollback(boolean required) throws SQLException {<br />&nbsp;&nbsp;&nbsp; if (!closed) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clearLocalCache();<br />&nbsp;&nbsp;if (!required) {&nbsp;<br />&nbsp;&nbsp;&nbsp;flushStatements();<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } finally {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (required) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transaction.rollback();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }&nbsp;<br /><br />不知道大家有没有碰到过类似的问题，又是通过什么方案解决的呢？</p> <img src ="http://www.blogjava.net/justuszhang2009/aggbug/352698.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-06-21 07:19 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/06/21/352698.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring 3 和mybatis 3集成，并用junit4进行测试</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/06/19/352618.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Sun, 19 Jun 2011 07:31:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/06/19/352618.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/352618.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/06/19/352618.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/352618.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/352618.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 提供了一个基于spring3＋mybatis3的例子，并用junit4进行测试。&nbsp;&nbsp;<a href='http://www.blogjava.net/justuszhang2009/archive/2011/06/19/352618.html'>阅读全文</a><img src ="http://www.blogjava.net/justuszhang2009/aggbug/352618.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-06-19 15:31 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/06/19/352618.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何激励团队</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/06/16/352451.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Thu, 16 Jun 2011 09:57:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/06/16/352451.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/352451.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/06/16/352451.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/352451.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/352451.html</trackback:ping><description><![CDATA[在谈到项目经理最重要的品质时，经常会谈到一个人的leadership，一个优秀的项目经理应该可以轻易激励项目团队的士气，即便在很困难的情况下也能按时按质完成项目。<br /><br />但是一个人有没有leadership不是靠喊口号喊出来的，也不是看那张脸是不是象leader，我觉得是从一个人的言行身教中反映出来的。我喜欢和一些非常senior的PM、Director谈论一个成功的项目经理是怎么样的，下面是令我印象最深刻的片段，是关于如何激励项目团队的，不得不佩服此人的才干：<br /><br />一、描绘宏伟蓝图。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在项目启动之初，在开项目组的kick off meeting时，项目经理说出以下这段话：&#8220;Hi 各位，大家都非常幸运，能加入到这个项目中来，这个项目是全上海最大的一个项目，是由上海市场亲自宣布启动的，这个项目的成功上线将会影响全上海2000万人的生活。做完这个项目，每个人都会得到很大的提升，就不会再有什么难得倒你们的项目了。&#8221;<br /><br />二、故意缩短工期。<br />&nbsp;&nbsp;&nbsp;&nbsp; 在描绘完项目的宏伟蓝图之后，拉上项目组的核心人员进行第二轮的激励，假设客户或者老板给项目经理的期限是6个月完成，那告诉项目组成员的时候，就说项目的周期只有3个月，问大家有没有信心完成，由于项目实在太大，大家肯定会说&#8220;不可能完成！&#8221;，项目经理接着说，&#8220;大家加一下油嘛！&#8221;，&#8220;还是无法完成！&#8221;，&#8220;给大家加钱呢？&#8221;，&#8220;有点困难！&#8221;，项目经理这时说，&#8220;既然大家都说无法完成，我跟客户再沟通沟通，争取多给一个月时间，这样有没有问题？&#8221;，大家这时候就跟减了便宜一样，会齐声说&#8220;可以！&#8221;。因此如果真能在4个月内完成，那么你还能提前2个月完成项目，你这个项目经理还不是成功的项目经理吗？ <img src ="http://www.blogjava.net/justuszhang2009/aggbug/352451.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-06-16 17:57 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/06/16/352451.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>持续集成工具Sonar的使用（二）</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/06/14/352280.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Tue, 14 Jun 2011 09:02:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/06/14/352280.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/352280.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/06/14/352280.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/352280.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/352280.html</trackback:ping><description><![CDATA[上一篇我们讲述了Sonar和Maven的结合来达到代码质量审查的效果，但是由于maven的学习成本高，并不是任何项目都适合，因此本篇我们讲述一下如何通过Sonar和ant的结合来进行代码质量审查。<br />&nbsp;<br />目前来说，Sonar和Ant的集成没有做到Sonar和Maven的集成这么好，因此在使用过程中需要多写一些脚本。我们下面来介绍一下使用步骤吧：<br />&nbsp; 1、下载Sonar并解压到任何目录，注意目录中不允许有中文；<br />&nbsp; 2、点击bin\windows-x86-32\StartSonar.bat启动Sonar；<br />&nbsp; 3、下载一个ant插件：sonar-ant-task-1.1.jar，并存放到某个路径，ant脚本需要访问这个文件。<br />&nbsp; 4、编写一个ant脚本，如下：<br />
<p>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;</p>
<p>&lt;project name="framework-client" default="sonar" basedir="."&gt;<br />&nbsp;&nbsp;<br />&nbsp;&lt;property name="project.name" value="framework-client"/&gt;<br />&nbsp;&lt;property name="src.dir" value="${basedir}/src/main/java" /&gt;<br />&nbsp;&lt;property name="lib.dir" value="${basedir}/lib"/&gt;<br />&nbsp;<br />&nbsp;&nbsp; &lt;!-- Out-of-the-box those parameters are optional --&gt;<br />&nbsp;&nbsp; &lt;!-- EXAMPLE FOR MYSQL&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp; &lt;property name="sonar.jdbc.url" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value="jdbc:mysql://localhost:3306/sonar?useUnicode=true&amp;amp;characterEncoding=utf8" /&gt;<br />&nbsp;&nbsp; &lt;property name="sonar.jdbc.driverClassName" value="com.mysql.jdbc.Driver" /&gt;<br />&nbsp;&nbsp; &lt;property name="sonar.jdbc.username" value="sonar" /&gt;<br />&nbsp;&nbsp; &lt;property name="sonar.jdbc.password" value="sonar" /&gt;<br />&nbsp;&nbsp; --&gt;<br /><br />&nbsp;&nbsp; &lt;!-- SERVER ON A REMOTE HOST --&gt;<br />&nbsp;&nbsp; &lt;!--<br />&nbsp;&nbsp; &lt;property key="sonar.host.url" value="<a href="http://myserver:1234">http://myserver:1234</a>" /&gt;<br />&nbsp;&nbsp; --&gt;<br />&nbsp;<br />&nbsp;&nbsp; &lt;!-- Define the Sonar task if this hasn't been done in a common script --&gt;<br />&nbsp;&nbsp; &lt;taskdef uri="antlib:org.sonar.ant" resource="org/sonar/ant/antlib.xml"&gt;<br />&nbsp;&nbsp; &nbsp;&nbsp;&lt;classpath&gt;<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="<span style="background-color: yellow">${lib.dir}/sonar-ant-task-1.1.jar</span>"/&gt;<br />&nbsp;&nbsp; &nbsp;&nbsp;&lt;/classpath&gt;<br />&nbsp;&nbsp; &lt;/taskdef&gt;<br />&nbsp;&nbsp; <br />&nbsp;&nbsp; &lt;!-- Add the target --&gt;<br />&nbsp;&nbsp; &lt;target name="sonar"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- list of mandatories Sonar properties --&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="sonar.sources" value="${src.dir}" /&gt;<br />&nbsp;&nbsp; &nbsp;&lt;property name="sonar.projectKey" value="org.example:${project.name}" /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- list of optional Sonar properties --&gt;<br />&nbsp;&nbsp; &nbsp;&lt;!--<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;property key="sonar.projectName" value="this value overrides the name defined in Ant root node" /&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;property key="sonar.binaries" value="list of directories which contain for example the Java bytecode" /&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;property key="sonar.tests" value="list of test source directories separated by a comma" /&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;property key="sonar.libraries" value="list of paths to libraries separated by a comma (These libraries are for example used by the Sonar Findbugs plugin)" /&gt;<br />&nbsp;&nbsp; &nbsp;--&gt;<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;sonar:sonar key="${sonar.projectKey}" version="0.9" xmlns:sonar="antlib:org.sonar.ant"/&gt;</p>
<p>&nbsp;&nbsp; &nbsp;&lt;/target&gt;<br />&lt;/project&gt;<br /><br />&nbsp; 5、运行ant脚本，看到build successfully的提示后，就可以访问：<a href="http://localhost:9000/">http://localhost:9000/</a>来查看代码质量审查结果了。<br /><br />上述是使用Sonar和ant最简单的步骤，用了Sonar自带的嵌入式数据库Derby，以及standalone的应用服务器，当然也支持使用其它数据库，比如：mysql，只要修改一下sonar.properties的配置文件，以及在ant脚本中配置一下连接数据库的方式。另外也可以使用tomcat、jboss等应用服务器来发布Sonar应用，只要运行一下Sonar自带的一个脚本：build-war.bat就可以了，这里不再详述。<br /></p>  <img src ="http://www.blogjava.net/justuszhang2009/aggbug/352280.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-06-14 17:02 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/06/14/352280.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>持续集成工具Sonar的使用（一）</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/06/13/352176.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Mon, 13 Jun 2011 03:42:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/06/13/352176.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/352176.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/06/13/352176.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/352176.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/352176.html</trackback:ping><description><![CDATA[最近由于工作需要，接触到了Sonar，使用它的出发点是因为它能统计代码注释率，而且支持drill down，可以依次看到工程、包、类的注释率。<br /><br />但Sonar作为一个很不错的持续集成工具，它的功能源不止于此，<span lang="EN-US"><strong>sonar</strong></span>&nbsp;可以持续，自动地统计并分析软件项目的相关质量数据，例如单元测试的通过率，覆盖率，代码的复杂度，代码的行数，代码注释率等等，用于评估和度量软件项目质量。<br /><br />我的Sonar的第一个应用，是Sonar和maven的配合使用，Sonar对Maven支持的比较好，网站上对于这两者结合使用的文章也非常多，因此如何用Sonar和maven来分析项目质量，在这边就不细说了，可以参考官方的网站：<a href="http://docs.codehaus.org/display/SONAR/Use+Sonar">http://docs.codehaus.org/display/SONAR/Use+Sonar</a>。这边主要说一下大致的步骤，以及在具体使用中碰到的几个问题。Sonar的使用真的非常简单，大概就包括以下几步，<br /><br />&nbsp;1、到官方网站下载<span lang="EN-US">Sonar</span>&nbsp;的压缩包，解压到任意目录，但是一定要注意：<strong style="color: red">目录中不允许有中文</strong>，不然启动Sonar后，访问：<a href="http://localhost:9000">http://localhost:9000</a>返回的界面会报错。<br />&nbsp;2、启动Sonar应用，可以直接点击bin/windows-x86-32目录下的StartSonar.bat，启动过程可能要1分多钟，请耐心等待，这里Sonar有一点不好的是，弹出的console中不打印任何信息。<br />&nbsp;3、进入你的工程目录（当前目录下面有pom.xml文件），运行：mvn clean install&nbsp; sonar:sonar，看到build successfully之后，就可以访问：<a href="http://localhost:9000/">http://localhost:9000</a>来分析你的工程的代码质量了。<br /><br />这里要说明的是，在执行maven命令时，后台会报一些乱码方面的错误，是由于java代码中出现中文字符串的问题造成的。可以通过以下方式解决：在&lt;properties&gt;中添加<strong>：&lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;</strong>，这是我试过的唯一的解决方案，在网上还提到两种解决方案，但都不管用。  <img src ="http://www.blogjava.net/justuszhang2009/aggbug/352176.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-06-13 11:42 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/06/13/352176.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>