<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>hlxiong</title>
    <description>世上一庸人。。。</description>
    <link>http://hlxiong.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>Spring Framework中的面向方面编程（AOP），第一部分</title>
        <author>hlxiong</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hlxiong.javaeye.com">hlxiong</a>&nbsp;
          链接：<a href="http://hlxiong.javaeye.com/blog/145660" style="color:red;">http://hlxiong.javaeye.com/blog/145660</a>&nbsp;
          发表时间: 2007年12月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          作为这个介绍<a href="http://www.springframework.org/" target="_blank">Spring框架</a>中的面向方面编程(Aspect-Oriented Programming，AOP)的系列的第一部分，本文介绍了使您可以使用Spring中的面向方面特性进行快速开发的基础知识。使用跟踪和记录方面（面向方面领域的HelloWorld）作为例子，本文展示了如何使用Spring框架所独有的特性来声明切入点和通知以便应用方面。本系列的第二部分将更深入地介绍如何运用Spring中的所有通知类型和切入点来实现更实用的方面和面向方面设计模式。对于AOP的更一般性的介绍，请查看ONJava站点上Graham O'Regan的文章，<a href="http://www.onjava.com/pub/a/onjava/2004/01/14/aop.html" target="_blank">&ldquo;Introduction to Aspect-Oriented Programming</a>&rdquo;。
<p>　　本文的目的不是要介绍构成模块化J2EE系统&mdash;&mdash;即Spring框架&mdash;&mdash;的所有重要元素，我们将只把注意力放在Spring所提供的AOP功能上。由于Spring的模块化设计方法，我们可以只使用该框架的AOP元素，而无需对构成Spring框架的其他模块做太多考虑。</p>
<p><strong>在AOP方面，Spring提供了什么？</strong><br />
　　&ldquo;它的目标不是提供最完善的AOP实现（虽然Spring AOP非常强大）；而是要提供AOP实现与Spring IoC的紧密集成，以便企业应用中的常见问题。&rdquo;<br />
Spring Framework参考文档</p>
<p>　　为了实现这个目标，Spring框架目前支持一组AOP概念，从切入点到通知。本文将展示如何使用Spring框架中所实现的如下AOP概念：</p>
<ul>
    <li><strong>通知（Advice</strong><strong>）：</strong>如何将before通知、afterReturning通知和afterThrowing通知声明为bean。 </li>
    <li><strong>切入点（Pointcut</strong><strong>）：</strong>如何声明静态切入点逻辑以将XML Spring Bean Configuration文件中的所有内容联系在一起。 </li>
    <li><strong>Advisor</strong><strong>：</strong>关联切入点定义与通知bean的方式。 </li>
</ul>
<p><strong>设置场景：一个简单的例子应用程序</strong><br />
　　&ldquo;一般而言，Spring并不是预描述的。虽然使用好的实践非常容易，但是它避免强制推行一种特定的方法。&rdquo;<br />
Spring Framework参考文档</p>
<p>　　要试用Spring框架的AOP功能，首先我们要创建一个简单的Java应用程序。IbusinessLogic接口和BusinessLogic类为Spring框架中的bean提供了简易构件块。虽然该接口对于我们的简单应用程序逻辑来说不是必需的，但是它是Spring框架所推荐的良好实践。</p>
<pre class="code">public interface IBusinessLogic
{
    public void foo();
}

public class BusinessLogic 
    implements IBusinessLogic
{
    public void foo() 
    {
        System.out.println(
        &quot;Inside BusinessLogic.foo()&quot;);
    }
}
</pre>
<p>　　可以编写MainApplication类，借此练习BusinessLogic bean的公有方法。</p>
<pre class="code">import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class MainApplication
{
    public static void main(String [] args)
    {
        // Read the configuration file
        ApplicationContext ctx = 
          new FileSystemXmlApplicationContext(
            &quot;springconfig.xml&quot;);

        //Instantiate an object
        IBusinessLogic testObject = 
          (IBusinessLogic) ctx.getBean(&quot;businesslogicbean&quot;);

        // Execute the public 
        // method of the bean
        testObject.foo();
    }
}
</pre>
<p>　　在BusinessLogic类及其关联接口中没有什么需要注意的。但是，MainApplication类初始化BusinessLogic对象的方式很有意思。通过使用ctx.getBean(&quot;businesslogicbean&quot;)调用，MainApplication将加载和管理BusinessLogic类的bean实例的任务转交给了Spring框架。</p>
<p>　　允许Spring控制BusinessLogic bean的初始化，这使得Spring运行时有机会在bean被返回给应用程序之前执行J2EE系统所需的所有与bean相关的管理任务。然后Spring运行时配置可以决定对bean应用哪些任务和模块。该配置信息由一个XML文件提供，类似于下面所示的：<br />
</p>
<div class="code_title">xml 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-xml">
    <li class="alt"><span><span class="tag">&lt;?</span><span class="tag-name">xml</span><span>&nbsp;</span><span class="attribute">version</span><span>=</span><span class="attribute-value">&quot;1.0&quot;</span><span>&nbsp;</span><span class="attribute">encoding</span><span>=</span><span class="attribute-value">&quot;UTF-8&quot;</span><span class="tag">?&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&lt;!DOCTYPE&nbsp;beans&nbsp;PUBLIC &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&quot;-//SPRING//DTD&nbsp;BEAN//EN&quot; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&quot;http://www.springframework.org/dtd/spring-beans.dtd&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span></span><span class="tag">&lt;</span><span class="tag-name">beans</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="comments">&lt;!--&nbsp;Bean&nbsp;configuration&nbsp;--&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">&quot;businesslogicbean&quot;</span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;org.springframework.aop.framework.ProxyFactoryBean&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;proxyInterfaces&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">value</span><span class="tag">&gt;</span><span>IBusinessLogic</span><span class="tag">&lt;/</span><span class="tag-name">value</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;target&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ref</span><span>&nbsp;</span><span class="attribute">local</span><span>=</span><span class="attribute-value">&quot;beanTarget&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">bean</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="comments">&lt;!--&nbsp;Bean&nbsp;Classes&nbsp;--&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">&quot;beanTarget&quot;</span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;BusinessLogic&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span></span><span class="tag">&lt;/</span><span class="tag-name">beans</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>　　该配置文件，即springconfig.xml，指定要加载一个接口与IbusinessLogic相匹配的bean。该bean随后被关联到BusinessLogic实现类。看起来好像是费了很大力气只为了加载一个简单的bean并调用一个方法，但是您要知道，这个配置文件只是使Spring框架可以透明地对应用程序应用其组件的众多特性的一个体现。</p>
<p>　　图1显示了基本的顺序图：MainApplication原样执行，没有应用方面。</p>
<p align="center"><img src="http://dev2dev.bea.com.cn/images/image05121630001.gif" alt="Figure 1" /><br />
图1.没有对BusinessLogic bean应用方面时的顺序图</p>
<p>　　请查看本文<a href="http://www.onjava.com/pub/a/onjava/2004/07/14/springaop.html?page=2#references" target="_blank">末尾处的参考资料</a>，获取这个简单Spring应用程序的源代码。</p>
<p><strong>应用方法跟踪（Method Tracing）方面</strong><br />
　　可能最基本的方面就是方法跟踪方面了。这可能是您找得到的最简单的方面了，因此它是研究新的AOP实现的一个很好的起点。</p>
<p>　　方法跟踪方面在一个目标应用程序内捕获对所跟踪的方法的调用以及方法的返回值，并以某种方式显示这种信息。在AOP中，通知的before和after类型用于捕获这些类型的联结点，因为这两种通知可以在方法调用联结点之前或之后触发。使用Spring框架，方法跟踪方面的before通知是在TracingBeforeAdvice类中声明的。</p>
<pre class="code">import java.lang.reflect.Method;
import org.springframework.aop. MethodBeforeAdvice;

public class TracingBeforeAdvice 
   implements MethodBeforeAdvice
{
    public void before(Method m, 
                     Object[] args, 
                     Object target) 
                     throws Throwable
    {
        System.out.println(
          &quot;Hello world! (by &quot; + 
          this.getClass().getName() + 
          &quot;)&quot;);
    }
}
</pre>
<p>　　类似地，after通知可以在TracingAfterAdvice类中声明。</p>
<pre class="code">import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;

public class TracingAfterAdvice 
   implements AfterReturningAdvice
{
    public void afterReturning(Object object, 
                             Method m, 
                             Object[] args, 
                             Object target) 
                             throws Throwable
    {
        System.out.println(
          &quot;Hello world! (by &quot; + 
          this.getClass().getName() + 
          &quot;)&quot;);
    }
}
</pre>
<p>　　这两个类都通过实现Spring框架的适当通知接口而表示了特定的通知。每种类型的通知都指定实现before(..)或afterReturning(..)方法，以便使Spring运行时可以告诉通知适当的联结点会在何时出现。值得注意的是，TracingAfterAdvice实际上是从AfterReturningAdvice扩展而来的，表示只有在联结点在无异常的情况下获得返回值时才运行通知。</p>
<p>　　为了将通知与应用程序中的适当联结点关联起来，必须对springconfig.xml进行一些修改。</p>
<pre class="code"><!--l version="1.0" encoding="UTF-8--><div class="dp-highlighter"><div class="bar"></div><ol class="dp-xml"><li class="alt"><span><span class="tag"><!--sp--><span class="tag-name">xml</span><span>&nbsp;</span><span class="attribute">version</span><span>=</span><span class="attribute-value">&quot;1.0&quot;</span><span>&nbsp;</span><span class="attribute">encoding</span><span>=</span><span class="attribute-value">&quot;UTF-8&quot;</span><span class="tag">?&gt;</span><span>&nbsp;&nbsp;</span></span></span></li><li class=""><span><!--CTYPE beans PUBLIC   </sp--></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&quot;-//SPRING//DTD&nbsp;BEAN//EN&quot; &nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&quot;http://www.springframework.org/dtd/spring-beans.dtd&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span></span><span class="tag">&lt;</span><span class="tag-name">beans</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="comments"><!-- Bean configuration --></span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">&quot;businesslogicbean&quot;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;org.springframework.aop.framework.ProxyFactoryBean&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;proxyInterfaces&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">value</span><span class="tag">&gt;</span><span>IBusinessLogic</span><span class="tag"><span class="tag-name">value</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;target&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ref</span><span>&nbsp;</span><span class="attribute">local</span><span>=</span><span class="attribute-value">&quot;beanTarget&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;interceptorNames&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">list</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">value</span><span class="tag">&gt;</span><span>theTracingBeforeAdvisor</span><span class="tag"><span class="tag-name">value</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">value</span><span class="tag">&gt;</span><span>theTracingAfterAdvisor</span><span class="tag"><span class="tag-name">value</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">list</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">bean</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="comments"><!-- Bean Classes --></span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">&quot;beanTarget&quot;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;BusinessLogic&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="comments"><!-- Advisor pointcut definition for before advice --></span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">&quot;theTracingBeforeAdvisor&quot;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;org.springframework.aop.support.RegexpMethodPointcutAdvisor&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;advice&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ref</span><span>&nbsp;</span><span class="attribute">local</span><span>=</span><span class="attribute-value">&quot;theTracingBeforeAdvice&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;pattern&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">value</span><span class="tag">&gt;</span><span>.*</span><span class="tag"><span class="tag-name">value</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">bean</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="comments"><!-- Advisor pointcut definition for after advice --></span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">&quot;theTracingAfterAdvisor&quot;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;org.springframework.aop.support.RegexpMethodPointcutAdvisor&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;advice&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ref</span><span>&nbsp;</span><span class="attribute">local</span><span>=</span><span class="attribute-value">&quot;theTracingAfterAdvice&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;pattern&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">value</span><span class="tag">&gt;</span><span>.*</span><span class="tag"><span class="tag-name">value</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">bean</span><span class="tag">&lt;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="comments"><!-- Advice classes --></span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">&quot;theTracingBeforeAdvice&quot;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;TracingBeforeAdvice&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">&quot;theTracingAfterAdvice&quot;</span><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;TracingAfterAdvice&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span></span><span class="tag"><span class="tag-name">beans</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li></ol></div><div class="code_title">xml 代码</div></pre>
<p>　　theTracingBeforeAdvisor和theTracingAfterAdvisor advisor被添加到前面所声明的businesslogicbean。每个advisor都可能截获所有bean所关联到的联结点。Advisor本身就是bean，而它唯一的作用就是将切入点定义与通知bean关联起来。本例中的切入点定义是在静态对象层次结构中指定相关联结点的正则表达式。</p>
<p>　　因为本例中使用了org.springframework.aop.support.RegexpMethodPointcutAdvisor切入点advisor，切入点逻辑是使用正则表达式指定的。正则表达式用于识别公有接口对IbusinessLogici接口的联结点。下面是一些可以用来指定IBusinessLogic接口上的不同联结点集合的正则表达式例子：</p>
<ul>
    <li><strong><value></value>.*</strong><strong>：</strong>该表达式选择advisor所关联到的一个或多个bean上的所有联结点。 </li>
    <li><strong><value></value>./IBusinessLogic/.foo</strong><strong>：</strong>该表达式只选择IbusinessLogic接口上的foo()方法的联结点。如果是advisor所关联到的bean，则该表达式只选择IBusinessLogic接口上的联结点。 </li>
</ul>
<p>　　springconfig.xml文件中最后的bean声明指定实现通知bean的类。</p>
<p>　　既然已经指定了跟踪方面的正确配置，那么下一次执行MainApplication时，这些方面就会在初始化过程中被编织进去，而BusinessLogic bean中的所有方法都将被跟踪，如图2所示。</p>
<p align="center">　　<a href="http://www.onjava.com/onjava/2004/07/14/graphics/fig2_full.png" target="blank"><img src="http://dev2dev.bea.com.cn/images/image05121630002_0000.gif" alt="Click for larger view" /></a><br />
图2. 方法跟踪方面应用到BusinessLogic bean之后的顺序图（单击图像查看大图）</p>
<p>　　方法跟踪方面和例子应用程序的源代码可在<a href="http://www.onjava.com/pub/a/onjava/2004/07/14/springaop.html?page=2#references" target="_blank">本文末尾的参考资料</a>小节进行下载。</p>
<p><strong>方面的重用</strong><br />
　　可以对方法跟踪方面进行扩展，提供一个稍微复杂的记录（Logging）方面。记录方面提供了一个很不错的重用例子，因为记录方面所需的许多特性都已经包含在方法跟踪方面中了。</p>
<p>　　在本例中，记录方面扩展了方法跟踪方面，以便显示附加的与（在应用程序的执行过程中）所引发的异常有关的信息。</p>
<p>　　要完全使用记录方面，需要对应用程序做一些更改。BusinessLogicException异常类提供了一个可以由IBusinessLogicInterface接口和BusinessLogic实现类新增的void bar()方法引发的异常。</p>
<pre class="code">public class BusinessLogicException 
   extends Exception
{

}

public interface IBusinessLogic
{
    public void foo();
   
    public void bar() 
      throws BusinessLogicException;
}

public class BusinessLogic 
   implements IBusinessLogic
{
    public void foo() 
    {
        System.out.println(
          &quot;Inside BusinessLogic.foo()&quot;);
    }
     
    public void bar() 
       throws BusinessLogicException
    {
        System.out.println(
        &quot;Inside BusinessLogic.bar()&quot;);
          throw new BusinessLogicException();
    }
}
</pre>
<p>　　MainApplication类现在将对void bar()方法进行一次额外的调用，并处理选中的、可能由该方法引发的异常。</p>
<pre class="code">import org.springframeworkcontext.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class MainApplication
{
    public static void main(String [] args)
    {
        // Read the configuration file
        ApplicationContext ctx =
          new FileSystemXmlApplicationContext(
            &quot;springconfig.xml&quot;);

       //Instantiate an object
       IBusinessLogic testObject = 
          (IBusinessLogic) ctx.getBean(
            &quot;businesslogicbean&quot;);

       //Execute the public methods of the bean
       testObject.foo();
      
       try
       {
           testObject.bar();
       }
       catch(BusinessLogicException ble)
       {
           System.out.println(
             &quot;Caught BusinessLogicException&quot;);
       }
    }
}
</pre>
<p>　　来自方法跟踪方面的TracingBeforeAdvice和TracingAfterAdvice通知可以整体重用。LoggingThrowsAdvice类为新的异常记录提供了通知。</p>
<pre class="code">import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;

public class LoggingThrowsAdvice 
   implements ThrowsAdvice
{
    public void afterThrowing(Method method, 
                            Object[] args, 
                            Object target, 
                            Throwable subclass)
    {
       System.out.println(
         &quot;Logging that a &quot; + 
         subclass + 
         &quot;Exception was thrown.&quot;);
    }
}
</pre>
<p>　　应用记录方面的最后一步是修改springconfig.xml配置文件，使其包含新添加的LoggingThrowsAdvice通知。</p>
<p>　　图3显示了运行MainApplication并使用Spring框架应用了记录方面的UML顺序图。</p>
<p align="center">　　<a href="http://www.onjava.com/onjava/2004/07/14/graphics/fig3_full.png" target="_blank"><img src="http://dev2dev.bea.com.cn/images/image05121630003.gif" alt="Click for larger view" /></a><br />
图3. 记录方面应用到BusinessLogic bean之后的顺序图（单击图像查看大图）</p>
<p>　　此处的记录方面清楚地说明了如何重用现有方面以及如何在Spring框架中使用通知的throws形式。通过为before和after通知声明新的通知来重写现有的方法跟踪方面实现，可以实现更复杂的记录方面，记录到更复杂的记录框架，比如<a href="http://logging.apache.org/log4j/docs/" target="_blank">LOG4J</a>。关于记录方面和例子应用程序的源代码，请参见<a href="http://www.onjava.com/pub/a/onjava/2004/07/14/springaop.html?page=2#references" target="_blank">本文末尾的参考资料</a>小节。</p>
<p><strong>结束语<br />
</strong>　　本文展示了使用Spring框架中的基本AOP结构所应用的一些简单方面。在本系列的下一篇文章中，我们将介绍一些更实用的方面，探讨方面的生命周期，使用Spring框架的around通知，并使用Spring来应用AOP模式。</p>
<p>　　<strong>参考资料</strong></p>
<ul>
    <li><a href="http://www.onjava.com/onjava/2004/07/14/examples/source.zip" target="_blank">本文的源代码</a> </li>
    <li><a href="http://www.springframework.org/" target="_blank">Spring Java/J2EE Framework</a> </li>
    <li><a href="http://www.springframework.org/docs/wiki/Spring_AOP_Framework.html" target="_blank">Spring AOP Framework</a> </li>
    <li>&quot;<a href="http://www.onjava.com/pub/a/onjava/2004/01/14/aop.html" target="_blank">Introduction to Aspect-Oriented Programming</a>&quot; </li>
</ul>
<p>　　原文出处:An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 1<a href="http://www.onjava.com/pub/a/onjava/2004/07/14/springaop.html" target="_blank">http://www.onjava.com/pub/a/onjava/2004/07/14/springaop.html</a></p>
<!--文章其他信息-->
<div class="dot001"><img src="http://dev2dev.bea.com.cn/images/_.gif" height="1" alt="" width="100%" /></div>
<table cellspacing="0" border="0" width="100%" cellpadding="3">
    <tbody>
        <tr valign="bottom">
            <td height="20" colspan="2">&nbsp;<span class="h2b">作者简介</span></td>
        </tr>
        <tr>
            <td align="center" valign="top">&nbsp;</td>
            <td><a href="http://www.onjava.com/pub/au/1725" target="_blank">Russell Miles</a>是General Dynamics UK公司的一名软件工程师，他负责Java和分布式系统，但是他目前主要的兴趣在面向方面领域，尤其是AspectJ。</td>
        </tr>
    </tbody>
</table>
<hr />
<strong>[摘自]<strong><font face="Arial">http://dev2dev.bea.com.cn/techdoc/20051216709.html</font></strong></strong>
          <br/>
          <span style="color:red;">
            <a href="http://hlxiong.javaeye.com/blog/145660#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/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 03 Dec 2007 17:18:42 +0800</pubDate>
        <link>http://hlxiong.javaeye.com/blog/145660</link>
        <guid>http://hlxiong.javaeye.com/blog/145660</guid>
      </item>
      <item>
        <title>Spring Framework中的面向方面编程（AOP），第二部分</title>
        <author>hlxiong</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hlxiong.javaeye.com">hlxiong</a>&nbsp;
          链接：<a href="http://hlxiong.javaeye.com/blog/145657" style="color:red;">http://hlxiong.javaeye.com/blog/145657</a>&nbsp;
          发表时间: 2007年12月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在本系列的<a href="http://dev2dev.bea.com.cn/techdoc/20051216709.html" target="_blank">第一部分</a>，我介绍了如何实现面向方面领域的&ldquo;HelloWorld&rdquo;：跟踪和记录方面。利用<a href="http://www.springframework.org/" target="_blank">Spring框架</a>所提供的面向方面编程(Aspect-Oriented Programming，AOP)功能，您看到了如何使用before-、after-和基于异常的通知，以及如何使用基于正则表达式的简单切入点。跟踪和记录方面提供了非常不错的上手例子，而本文将进一步介绍一种新的通知形式：around通知。
<p>　　比起第一部分中介绍的那些通知类型，around形式的通知是一种更具侵入性也更强大的面向对象概念。本文将描述around通知的每个特性，以便您可以在自己的Spring AOP应用程序中正确地使用它。在本文最后，我将向您展示如何使用around通知来截获和改变应用程序中各个特性相互作用的方式，以便实现Cuckoo's Egg（杜鹃的蛋）面向方面设计模式。</p>
<p><strong>概述Spring AOP、IoC和代理</strong><br />
　　<a href="http://dev2dev.bea.com.cn/techdoc/20051216709.html" target="_blank">在第一部分</a>，我们快速浏览了Spring的一些AOP特性，而没有阐明Spring如何实现AOP的细节。要理解Spring框架如何运转，尤其是它如何实现其AOP功能，首先您要明白，Spring是一个依赖于控制反转(Inversion of Control，IoC)设计模式的轻量级框架。</p>
<p>　　注意：本文的目的不是要深入介绍IoC模式，介绍IoC只是为了使您明白该设计模式是如何影响Spring AOP实现的。有关IoC模式的更详细的介绍请参见本文末尾的<a href="http://www.onjava.com/pub/a/onjava/2004/10/20/springaop2.html?page=2#resources" target="_blank">参考资料</a>。</p>
<p>　　IoC设计模式的出现已经有一段时间了。一个最明显的例子就是<a href="http://www.onjava.com/pub/a/onjava/2004/10/20/springaop2.html#" target="_blank">J2EE</a>架构本身。随着企业开发尤其是J2EE平台的出现，应用程序开始依赖于由外部容器所提供的一些特性，比如bean创建、持久性、消息传递、会话以及事务管理。</p>
<p>　　IoC引入了一个新概念：由组件构成的框架，它与J2EE容器有许多类似之处。IoC框架分离了组件所依赖的功能，并且，根据Sam Newman文章中的说法，提供了&ldquo;连接组件的&lsquo;胶水&rsquo;&rdquo;。</p>
<p>　　对组件所依赖特性的控制 被反转 了，这样外部框架就可以尽可能透明地提供这些特性了。IoC模式真正意识到了从传统的由依赖于功能的组件来负责这些功能，到由独立的框架来配置和提供这些功能的方式转变。</p>
<p>　　图1显示了一些构成IoC模式的不同组件角色的例子。</p>
<p align="center"><img src="http://dev2dev.bea.com.cn/images/image051222002.jpg" height="216" alt="Figure 1" width="534" /><br />
图1. 没有对BusinessLogic bean应用方面时的顺序图.</p>
<p>　　图字：</p>
<p>　　Component：组件</p>
<p>　　Provides Facilities：提供功能</p>
<p>　　Relies on and conforms to：依赖于并服从</p>
<p>　　Manages the services the framework can then use to provide facilities：管理框架随后可以用来提供功能的服务</p>
<p>　　Service：服务</p>
<p>　　Your Component：您的组件</p>
<p>　　IoC Framework：IoC框架</p>
<p>　　External services：外部服务</p>
<p>　　IoC模式使用3种不同的方法来解除组件与服务控制的耦合：类型1、类型2和类型3。</p>
<ul>
    <li>类型1：接口注入<br />
    这是大部分J2EE实现所使用的方法。组件显式地服从于一组接口，带有关联的配置元数据，以便允许框架对它们进行正确的管理。 </li>
    <li>类型2：Setter注入<br />
    外部元数据被用来配置组件相互作用的方式。在<a href="http://dev2dev.bea.com.cn/techdoc/20051216709.html" target="_blank">第一部分</a>中，我们就是使用这种IoC方法利用springconfig.xml文件来配置Spring组件的。 </li>
    <li>类型3：构造函数注入<br />
    组件（包括构造组件时要用的参数）注册到框架，而框架提供组件的实例以及所有要应用的指定功能。 </li>
</ul>
<p>　　IoC在组件开发和企业开发中越来越受欢迎。IoC的实际例子包括传统的J2EE解决方案，比如：<a href="http://www.jboss.org/" target="_blank">JBoss</a>、Apache基金会的<a href="http://avalon.apache.org/" target="_blank">Avalon</a>项目以及本文的Spring框架。实际上，Spring框架构建于IoC模式的基础上是为了帮助将它的轻量级功能注入到它的相关应用程序的组件中。</p>
<p>　　那么IoC对于Spring AOP有何意义呢？Spring的IoC特性是使用IoC springconfig.xml配置文件对应用程序应用方面的推动因素之一。springconfig.xml配置文件通知Spring框架运行时有关应用程序的组件要被注入的功能类型的信息，所以自然轻量级的AOP功能就以同样的方式应用了。然后Spring使用代理模式围绕现有的类和bean实现指定的AOP功能。</p>
<p>　　图2显示了Spring及其IoC框架如何使用代理对象提供AOP功能（根据springconfig.xml文件中的IoC配置。）</p>
<p align="center"><a href="http://www.onjava.com/onjava/2004/10/20/graphics/fig2_full.png" target="_blank"><img src="http://dev2dev.bea.com.cn/images/image051222004.gif" border="0" height="292" alt="Click for larger view" width="432" /></a><br />
图2. springconfig.xml配置文件改变了Spring框架IoC，以便随后向第一部分中的一个顺序图提供AOP代理（单击图像查看大图）</p>
<p>　　在本系列下面的部分，您将不断看到现在包含在顺序图中的代理对象。这只是为了说明对于Spring AOP来说没有&ldquo;魔法&rdquo;，实际上只有一个面向对象设计模式的良好例子。</p>
<p><strong>回到AOP：使用around通知的积极方面</strong><br />
　　在<a href="http://dev2dev.bea.com.cn/techdoc/20051216709.html" target="_blank">第一部分</a>，您看到了如何使用Spring AOP来实现跟踪和记录方面。跟踪和记录都是&ldquo;消极&rdquo;方面，因为它们的出现并不会对应用程序的其他行为产生影响。它们都使用了消极的before和after形式的通知。</p>
<p>　　但是如果您希望改变应用程序的常规行为呢？例如说，您希望重写一个方法？这样的话，您就需要使用更积极的around形式的通知。</p>
<p>　　<a href="http://dev2dev.bea.com.cn/techdoc/20051216709.html" target="_blank">第一部分</a>的简单例子应用程序包括IbusinessLogic接口、BusinessLogic类和MainApplication类，如下所示：</p>
<pre class="code">public interface IBusinessLogic
{
  public void foo();
}


public class BusinessLogic 
  implements IBusinessLogic
{
  public void foo() 
  {
     System.out.println(
       &quot;Inside BusinessLogic.foo()&quot;);
  }
}

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class MainApplication
{
  public static void main(String [] args)
  {
    // Read the configuration file
    ApplicationContext ctx = 
      new FileSystemXmlApplicationContext(
        &quot;springconfig.xml&quot;);

    //Instantiate an object
    IBusinessLogic testObject = 
      (IBusinessLogic) ctx.getBean(
        &quot;businesslogicbean&quot;);

    // Execute the public 
    // method of the bean
    testObject.foo();
  }
}
</pre>
<p>　　要对一个BusinessLogic类的实例彻底重写对foo()方法的调用，需要创建around通知，如下面的AroundAdvice类所示：</p>
<pre class="code">import org.aopalliance.intercept.MethodInvocation;
import org.aopalliance.intercept.MethodInterceptor;

public class AroundAdvice 
   implements MethodInterceptor
{
   public Object invoke(
      MethodInvocation invocation)
      throws Throwable
   {
      System.out.println(
         &quot;Hello world! (by &quot; + 
         this.getClass().getName() + 
         &quot;)&quot;);

      return null;
   }
}
</pre>
<p>　　要在Spring中用作around通知，AroundAdvice类必须实现MethodInterceptor接口和它的invoke(..)方法。每当截获到方法的重写，invoke(..)方法就会被调用。最后一步是改变包含在应用程序的springconfig.xml文件中的Spring运行时配置，以便可以对应用程序应用AroundAdvice。</p>
<pre class="code">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE beans PUBLIC  &quot;-//SPRING//DTD BEAN//EN&quot;
    &quot;http://www.springframework.org/dtd/spring-beans.dtd&quot;&gt;

&lt;beans&gt;

   &lt;!-- Bean configuration --&gt;
   &lt;bean id=&quot;businesslogicbean&quot;
   class=&quot;org.springframework.aop.framework.ProxyFactoryBean&quot;&gt;
      &lt;property name=&quot;proxyInterfaces&quot;&gt;
         &lt;value&gt;IBusinessLogic&lt;/value&gt;
      &lt;/property&gt;
      &lt;property name=&quot;target&quot;&gt;
         &lt;ref local=&quot;beanTarget&quot;/&gt;
      &lt;/property&gt;
      &lt;property name=&quot;interceptorNames&quot;&gt;
         &lt;list&gt;
            &lt;value&gt;theAroundAdvisor&lt;/value&gt;
         &lt;/list&gt;
         &lt;/property&gt;
   &lt;/bean&gt;
   &lt;!-- Bean Classes --&gt;
   &lt;bean id=&quot;beanTarget&quot;
   class=&quot;BusinessLogic&quot;/&gt;

   &lt;!-- Advisor pointcut definition for around advice --&gt;
   &lt;bean id=&quot;theAroundAdvisor&quot;
      class=&quot;org.springframework.aop.support.RegexpMethodPointcutAdvisor&quot;&gt;
      &lt;property name=&quot;advice&quot;&gt;
         &lt;ref local=&quot;theAroundAdvice&quot;/&gt;
      &lt;/property&gt;
      &lt;property name=&quot;pattern&quot;&gt;
         &lt;value&gt;.*&lt;/value&gt;
      &lt;/property&gt;
   &lt;/bean&gt;
	
   &lt;!-- Advice classes --&gt;
   &lt;bean id=&quot;theAroundAdvice&quot;
      class=&quot;AroundAdvice&quot;/&gt;

&lt;/beans&gt;
</pre>
<p>　　根据该springconfig.xml配置文件，theAroundAdvisor截获所有对BusinessLogic类的方法的调用。接下来，theAroundAdvisor被关联到theAroundAdvice，表明当截获一个方法时，就应该使用在AroundAdvice类中指定的通知。既然已经指定了around通知的正确配置，下一次执行MainApplication类时，BusinessLogic bean的foo()方法就会被截获并重写，如图3所示：</p>
<p align="center"><img src="http://dev2dev.bea.com.cn/images/image051222006.jpg" height="223" alt="Figure 3" width="550" /><br />
图3. 使用around通知重写对BusinessLogic类中的foo()方法的调用</p>
<p>　　前面的例子显示，BusinessLogic类中的foo()方法可以通过AroundAdvice类中的invoke(..)方法彻底重写。原来的foo()方法完全不能被invoke(..)方法调用。如果希望从around通知内调用foo()方法，可以使用proceed()方法，可从invoke(..)方法的MethodInvocation参数中得到它。</p>
<pre class="code">public class AroundAdvice 
   implements MethodInterceptor
{
   public Object invoke(
      MethodInvocation invocation) 
      throws Throwable
   {
      System.out.println(
         &quot;Hello world! (by &quot; + 
         this.getClass().getName() + 
         &quot;)&quot;);

      invocation.proceed();

      System.out.println(&quot;Goodbye! (by &quot; + 
         this.getClass().getName() + 
         &quot;)&quot;);

      return null;
   }
}
</pre>
<p>　　图4显示了对proceed()的调用如何影响操作的顺序（与图3所示的初始around通知执行相比较）。</p>
<p align="center"><img src="http://dev2dev.bea.com.cn/images/image051222008.jpg" height="203" alt="Figure 4" width="550" /><br />
图4. 从around通知内使用proceed()调用原来的方法</p>
<p>　　当调用proceed()时，实际是在指示被截获的方法（在本例中是foo()方法）利用包含在MethodInvocation对象中的信息运行。您可以通过调用MethodInvocation类中的其他方法来改变该信息。</p>
<p>　　您可能希望更改包含在MethodInvocation类中的信息，以便在使用proceed()调用被截获的方法之前对被截获方法的参数设置新值。</p>
<p>　　通过对MethodInvocation对象调用getArguments()方法，然后在返回的数组中设置其中的一个参数对象，最初传递给被截获的方法的参数可以被更改。</p>
<p>　　如果IbusinessClass和BusinessLogic类的foo()方法被更改为使用整型参数，那么就可以将传递给被截获的调用的值由在AroundAdvice的notify(..)方法中传递改为在foo(int)中传递。</p>
<pre class="code">public class AroundAdvice 
   implements MethodInterceptor
{
   public Object invoke(
      MethodInvocation invocation) 
      throws Throwable
   {
      System.out.println(
         &quot;Hello world! (by &quot; + 
         this.getClass().getName() + 
         &quot;)&quot;);
      
      invocation.getArguments()[0] = new Integer(20);
      
      invocation.proceed();
      
      System.out.println(
         &quot;Goodbye! (by &quot; + 
         this.getClass().getName() + 
         &quot;)&quot;);
      
      return null;
   }
}
</pre>
<p>　　在本例中，被截获的方法的第一个形参被假设为int。实参本身是作为对象传递的，所以通过将其包装在Integer类实例中的方法，基本的int类型的形参被改为对应数组中的新值。如果您将该参数设置为一个非Integer对象的值，那么在运行时就会抛出IllegalArgumentException异常。</p>
<p>　　您还将注意到，invoke(..)方法必须包含一个return语句，因为该方法需要返回值。但是，被重写的foo()方法并不返回对象，所以invoke(..)方法可以以返回null结束。如果在foo()方法不需要的情况下，您仍然返回了一个对象，那么该对象将被忽略。</p>
<p>　　如果foo()方法确实需要返回值，那么需要返回一个与foo()方法的初始返回类型在同一个类或其子类中的对象。如果foo()方法返回一个简单类型，例如，一个integer，那么您需要返回一个Integer类的对象，当方法被重写时，该对象会自动由AOP代理拆箱，如图5所示：</p>
<p align="center"><img src="http://dev2dev.bea.com.cn/images/image051222010.jpg" height="294" alt="Figure 5" width="550" /><br />
图5. around通知的装箱和自动拆箱</p>
<p>　　图字：</p>
<p>　　Object invoke：对象调用</p>
<p>　　The integer return value is boxed in a Integer object in the AroundAdvice and then unboxed by the AOP Proxy：整型返回值被装箱在AroundAdvic通知的一个Integer对象中，然后由AOP代理拆箱。</p>
<p>　　面向方面编程还是一个比较新的领域，尤其是与衍生出它的面向对象编程相比。设计模式通常被认为是常见问题的通用解决方案，因为面向方面发展的时间还不长，所以已发现的面向方面设计模式比较少。</p>
<p>　　此处要介绍的是一种正在浮现的模式，即Cuckoo's Egg设计模式。该模式还有其他的叫法，它在面向对象领域的对等体包括模仿对象（Mock Object）和模仿测试（Mock Testing），甚至代理模式也与它有一些类似之处。</p>
<p>　　Cuckoo's Egg面向方面设计模式可以被定义为应用程序上下文中功能部件的透明和模块化的置换。就像杜鹃偷偷地把自己的蛋放在另一种鸟的巢中一样，Cuckoo's Egg设计模式用一个替代功能部件实现置换现有的功能部件,而使造成的干扰尽可能少。</p>
<p>　　这种置换的实现方式可以是静态的、动态的、部分的、完全的，针对一个对象的多个部分，或针对多个组件。使用面向方面的方法可以透明地实现功能部件的置换，而无需对应用程序的其余部分进行更改。要置换应用程序中现有功能部件的替代功能部件就是&ldquo;杜鹃的蛋&rdquo;。图6显示了Cuckoo's Egg设计模式中的主要组成元素。</p>
<p align="center"><img src="http://dev2dev.bea.com.cn/images/image051222012.jpg" height="278" alt="Figure 6" width="527" /><br />
图6. Cuckoo's Egg设计模式中的主要组成元素</p>
<p>　　图字：</p>
<p>　　Application：应用程序</p>
<p>　　Component：组件</p>
<p>　　Replacement Feature：替代功能部件</p>
<p>　　Component 1 and 2 together encompass a distinct feature of the software：组件1和2共同包含了软件的一个独立的功能部件</p>
<p>　　The Cuckoo's Egg pattern transparently replaces an existing feature of the software：Cuckoo's Egg模式透明地置换了软件现有的功能部件</p>
<p>　　Before the pattern is applied：应用该模式前</p>
<p>　　After the pattern is applied：应用该模式后</p>
<p>　　Cuckoo's Egg设计模式依赖于around通知的概念。您需要借助于积极的和侵入性的around通知来截获并有效置换应用程序中现有的功能部件。</p>
<p>　　有关Cuckoo's Egg设计模式的更多信息，以及AspectJ中的一个可选实现，请参见《<a href="http://www.oreilly.com/catalog/aspectjckbk/" target="_blank">AspectJ Cookbook</a>》（O'Reilly，2004年12月出版）。</p>
<p>　　要使用Spring AOP实现Cuckoo's Egg设计模式，需要声明一个around通知来截获所有对要置换的功能部件的调用。与<a href="http://www.springframework.org/docs/reference/aop.html#aop-ts-swap" target="_blank">hot-swappable target sources</a>（Spring AOP的一个功能部件，将在本系列的另一篇文章中介绍）不同，around通知的显式使用使得Cuckoo's Egg实现可以有效地跨越对象边界（因此也可以跨越bean边界）进行整个功能部件的置换，如图7所示。</p>
<p align="center"><img src="http://dev2dev.bea.com.cn/images/image051222014.jpg" height="285" alt="Figure 7" width="293" /><br />
图7. 一个跨越bean边界的组件</p>
<p>　　图字：</p>
<p>　　A feature crosses the boundaries of BusinessLogic and BusinessLogic2 by depending on behavior supplied separately by the two beans：一个功能部件通过依赖于由BusinessLogic和BusinessLogic2各自提供的行为而跨越了这两个bean的边界</p>
<p>　　下面的代码显示了一个具有两个bean的简单应用程序，其中有一个功能部件跨越了该应用程序的多个方面。要置换的功能部件可以被视为包含IBusinessLogic bean中的foo()方法和IBusinessLogic2 bean中的bar()方法。IBusinessLogic2 bean中的baz()方法不是 该功能部件的一部分，所以不进行置换。</p>
<pre class="code">public interface IBusinessLogic
{
   public void foo();
}

public interface IBusinessLogic2
{
   public void bar();
   
   public void baz();
}
</pre>
<p>　　该例子的完整源代码可在<a href="http://www.onjava.com/pub/a/onjava/2004/10/20/springaop2.html?page=2#resources#resources" target="_blank">本文末尾的参考资料</a>小节中下载。</p>
<p>　　此处，ReplacementFeature类扮演了&ldquo;杜鹃的蛋&rdquo;的角色，它提供了将被透明地引入应用程序的替代实现。ReplacementFeature类实现了所有在该类引入时要被置换的方法。</p>
<pre class="code">public class ReplacementFeature
{
   public void foo()
   {
      System.out.println(
         &quot;Inside ReplacementFeature.foo()&quot;);
   }
   
   public void bar()
   {
      System.out.println(
         &quot;Inside ReplacementFeature.bar()&quot;);
   }
}
</pre>
<p>　　现在需要声明一个around通知来截获对跨越bean的功能部件的方法调用。CuckoosEgg类提供了某种around通知来检查被截获的方法，并将适当的方法调用传递给ReplacementFeature类的实例。</p>
<pre class="code">public class CuckoosEgg implements MethodInterceptor
{
   public ReplacementFeature replacementFeature =
       new ReplacementFeature();
   
   public Object invoke(MethodInvocation invocation)
       throws Throwable
   {
      if (invocation.getMethod().getName().equals(&quot;foo&quot;))
      {
         replacementFeature.foo();
      }
      else
      {
         replacementFeature.bar();
      }
      
      return null;
   }
}
</pre>
<p>　　因为与Spring框架关系密切，Cuckoo's Egg设计的详细信息被放在springconfig.xml配置文件中。对springconfig.xml文件的更改将确保所有对IbusinessLogic和IBusinessLogic2 bean的foo()方法和bar()方法的调用都将被截获，并传递给CuckoosEgg类的around通知。</p>
<pre class="code">   ...
   
   &lt;!--CONFIG--&gt;
   &lt;bean id=&quot;businesslogicbean&quot; 
      class=&quot;org.springframework.aop.framework.ProxyFactoryBean&quot;&gt;
      &lt;property name=&quot;proxyInterfaces&quot;&gt;
         &lt;value&gt;IBusinessLogic&lt;/value&gt;
      &lt;/property&gt;
      &lt;property name=&quot;target&quot;&gt;
         &lt;ref local=&quot;beanTarget&quot;/&gt;
      &lt;/property&gt;
      &lt;property name=&quot;interceptorNames&quot;&gt;
         &lt;list&gt;
            &lt;value&gt;theCuckoosEggAdvisor&lt;/value&gt;
         &lt;/list&gt;
      &lt;/property&gt;
   &lt;/bean&gt;
   
   &lt;bean id=&quot;businesslogicbean2&quot; 
      class=&quot;org.springframework.aop.framework.ProxyFactoryBean&quot;&gt;
      &lt;property name=&quot;proxyInterfaces&quot;&gt;
         &lt;value&gt;IBusinessLogic2&lt;/value&gt;
      &lt;/property&gt;
      &lt;property name=&quot;target&quot;&gt;
         &lt;ref local=&quot;beanTarget2&quot;/&gt;
      &lt;/property&gt;
      &lt;property name=&quot;interceptorNames&quot;&gt;
         &lt;list&gt;
            &lt;value&gt;theCuckoosEgg2Advisor&lt;/value&gt;
         &lt;/list&gt;
      &lt;/property&gt;
   &lt;/bean&gt;

   &lt;!--CLASS--&gt;
   &lt;bean id=&quot;beanTarget&quot; class=&quot;BusinessLogic&quot;/&gt;
   &lt;bean id=&quot;beanTarget2&quot; class=&quot;BusinessLogic2&quot;/&gt;
   
   &lt;!--ADVISOR--&gt;
   &lt;bean id=&quot;theCuckoosEggAdvisor&quot; 
      class=&quot;org.springframework.aop.support.RegexpMethodPointcutAdvisor&quot;&gt;
      &lt;property name=&quot;advice&quot;&gt;
         &lt;ref local=&quot;theReplacementFeaturePart1Advice&quot;/&gt;
      &lt;/property&gt;
      &lt;property name=&quot;pattern&quot;&gt;
         &lt;value&gt;IBusinessLogic.*&lt;/value&gt;
      &lt;/property&gt;
   &lt;/bean&gt;
   
   &lt;bean id=&quot;theCuckoosEgg2Advisor&quot; 
      class=&quot;org.springframework.aop.support.RegexpMethodPointcutAdvisor&quot;&gt;
      &lt;property name=&quot;advice&quot;&gt;
         &lt;ref local=&quot;theReplacementFeaturePart2Advice&quot;/&gt;
      &lt;/property&gt;
      &lt;property name=&quot;pattern&quot;&gt;
         &lt;value&gt;IBusinessLogic2.bar*&lt;/value&gt;
      &lt;/property&gt;
   &lt;/bean&gt;
   
   &lt;!--ADVICE--&gt;
   &lt;bean id=&quot;theReplacementFeaturePart1Advice&quot; class=&quot;CuckoosEgg&quot;/&gt;
   &lt;bean id=&quot;theReplacementFeaturePart2Advice&quot; class=&quot;CuckoosEgg&quot;/&gt;
   
   ...
</pre>
<p>　　当使用修改后的springconfig.xml文件运行例子应用程序时，要替换的、被指定为功能部件的一部分的方法调用完全被截获并传递给ReplacementFeature类。</p>
<p>　　通常，即使在同一个实现环境中，我们也可以用不同的方法来实现同一种设计模式。实现上例的另一种方法是实现两个独立的通知。</p>
<p>　　最后需要注意的是，使用Cuckoo's Egg设计模式置换的功能部件，不管它是跨越bean的还是在一个类中，它的生命周期与它所置换的功能部件的目标生命周期匹配。在上例中这没什么问题，因为只有一个功能部件实例被置换了，而且唯一的Cuckoo's Egg通知只维护一个替代功能部件。</p>
<p>　　这个例子非常简单，而在实践中，您很可能必须处理大量需要用各自的Cuckoo's Egg实例置换的功能部件实例。在这种情况下，单个的方面实例需要被关联到单个的要置换的功能部件实例。本系列的下一篇文章将会考虑方面生命周期的用法，届时将解决这个问题。</p>
<p><strong>结束语</strong><br />
　　本文介绍了如何在Spring框架内谨慎使用around形式的通知。around形式的通知常用于实现Cuckoo's Egg设计模式时，所以我们引入了一个例子来说明如何使用Spring AOP实现这种面向方面设计模式。</p>
<p>　　在本系列的第三部分中，您将看到如何使用Spring框架中其他的AOP基本概念。这些概念包括：控制方面生命周期、使用基于introduction通知的积极方面改变应用程序的静态结构，以及使用control flow切入点实现对方面编织的更细微的控制。</p>
<p>　　<strong>参考资料</strong></p>
<ul>
    <li><a href="http://javaboutique.internet.com/tutorials/spring_frame" target="_blank">Spring框架教程</a>&mdash;&mdash;向您展示Spring框架的内部运作方式 </li>
    <li><a href="http://www.onjava.com/onjava/2004/10/20/examples/springaoppart2source.zip" target="_blank">本文的完整源代码</a> </li>
    <li><a href="http://www.springframework.org/" target="_blank">Spring Java/J2EE框架</a> </li>
    <li><a href="http://martinfowler.com/articles/injection.html" target="_blank">&ldquo;Comparing the Inversion of Control and Dependency Injection Design Patterns</a>&rdquo; </li>
    <li><a href="http://today.java.net/pub/a/today/2004/02/10/ioc.html" target="_blank">IoC模式概述</a> </li>
    <li><a href="http://www.springframework.org/docs/wiki/Spring_AOP_Framework.html" target="_blank">Spring框架参考文档第5章</a>&mdash;&mdash;介绍Spring中的所有AOP特性 </li>
</ul>
<p>　　<strong>原文出处</strong><br />
An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 2 <a href="http://www.onjava.com/pub/a/onjava/2004/10/20/springaop2.html">http://www.onjava.com/pub/a/onjava/2004/10/20/springaop2.html</a></p>
<!--文章其他信息-->
<div class="dot001"><img src="http://dev2dev.bea.com.cn/images/_.gif" height="1" alt="" width="100%" /></div>
<table cellspacing="0" border="0" width="100%" cellpadding="3">
    <tbody>
        <tr valign="bottom">
            <td height="20" colspan="2">&nbsp;<span class="h2b">作者简介</span></td>
        </tr>
        <tr>
            <td align="center" valign="top">&nbsp;</td>
            <td><a href="http://www.onjava.com/pub/au/1725" target="_blank">Russell Miles</a>是General Dynamics UK公司的一名软件工程师，他负责Java和分布式系统，但是他目前主要的兴趣在面向方面领域，尤其是AspectJ。</td>
        </tr>
    </tbody>
</table>
<hr />
<strong>[摘自]<strong><font face="Arial">http://dev2dev.bea.com.cn/techdoc/20051223712.html</font></strong></strong>
          <br/>
          <span style="color:red;">
            <a href="http://hlxiong.javaeye.com/blog/145657#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 03 Dec 2007 17:16:57 +0800</pubDate>
        <link>http://hlxiong.javaeye.com/blog/145657</link>
        <guid>http://hlxiong.javaeye.com/blog/145657</guid>
      </item>
      <item>
        <title>AOP学习</title>
        <author>hlxiong</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hlxiong.javaeye.com">hlxiong</a>&nbsp;
          链接：<a href="http://hlxiong.javaeye.com/blog/145644" style="color:red;">http://hlxiong.javaeye.com/blog/145644</a>&nbsp;
          发表时间: 2007年12月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          更确切地说，我们在比较AOP的拦截器和Decorator模式，它们非常相似，一些AOP框架本身就是使用Decorator模式来实现拦截器功能的。<br /><br />　　过滤器是架构设计模式中比较常用的一种，几乎每个灵活动态系统都需要过滤器，特别是当我们的数据以内存状态出现时，过滤器无疑成为领域层的一个核心业务逻辑，当然如果你还是使用面向数据库的编程模式，过滤器功能就被你用SQL语句的where语法给替代了，那么以下你可能不必再看，请浏览这篇文章：状态对象：数据库的替代者。<br /><br />　　当我们在一个AOP框架下编程，经常会问自己，到底过滤器这个功能是应该做成拦截器还是 <br />Decorator，当我在Jdon Framework下重写JiveJdon时就不只一次的问我自己。<br /><br />　　其实这是一个分析模式中的过滤器实现问题，也是一个现实设计的问题：在Servlet Filter和职责链以及装饰模式Decorator和AOP几个方面如何选择？<br /><br />　　选择标准无外乎软件的两个终极目标：简单和高质量；高质量是反映在细粒度方面，简单则是在进行设计实现时比较容易方便。<br /><br />　　在粒度粗细方面，又有一个衡量指标：功能覆盖范围，也是一个scope，例如：如果功能需求要为某个类的方法实现实现过滤，但是，你使用一个Servlet Filter这样过滤器实现，虽然 <br />它也能够实现我们的目标，但是它对所有的Servlet请求都进行过滤，这无疑范了杀鸡取卵的错误，造成系统性能上的损失。<br /><br />　　以一个具体案例为例子： <br />　　我们在论坛显示时，希望动态过滤掉不应该出现的字眼，也就是在帖子显示之前，加载一个过滤器，对内容进行替换。<br /><br />　　这里的Model是ForumMessage,而对应的Service则是ForumMessageService； ForumMessageService的getMessage可以获得帖子内容，这里我们在getMessage之前加一个过滤器；那么这里应该如何选择呢？<br /><br />　　因为我在Jdon Framework这样的Ioc/AOP框架下实现，所以想到的是AOP，我们定义一个拦截器，拦截的对象是ForumMessageService的getMessage方法，进行配置文件配置即可。似乎很简单很酷，可是真的很美丽吗？<br /><br />　　这里，我们从原理上这几个过滤器实现进行解析一下： <br />　　我这里举个例子，你想向处于同一个城市对方送达一个信件，这时有两种选择： <br />　　1.委托高效的TNT UPS等快递公司 <br />　　2.亲自跑一趟。<br /><br />　　这两种哪个效率最高？无疑是亲自跑一趟，可能半个小时或一个小时解决问题，但是你一旦委托第三方，我想，无论快递公司如何高效，肯定没有你自己解决快速。所以，当你能够自己处理把握时，亲自动手无疑最快的。<br /><br />　　编码时也类似这样，如果你能在编码时，能够确定拦截那个类哪个方法，有什么比你直接写代码还能执行更快的呢？<br /><br />　　AOP的拦截器实现原理也是这样，当然，正是有这种考虑，所以所有AOP框架内，只有 AspectJ性能是最好的，因为它在你编译代码时就将你的拦截意图实现了，这属于静态织入 weaving，这就类似你自己写代码实现，但是与你自己写代码不同的是，你自己不但要写拦截器，而且还要自己手工将拦截器语句插入被拦截的那段代码，现在，使用AspectJ你就不用做后者了，这也是整合了AspectJ的Spring 2.0给我们带来的效能。所以，为了在编译代码时做手脚，就不能使用SUN的JDK原来的javac了，必须用他们自己特定的javac了。有得必有失吧。<br /><br />　　理解了AOP拦截器拦截的原理，你可能感慨：原来所有的动态AOP（不改换编译器的AOP框架）拦截效能没有Decorator直接指定要快啊。所以，如果你在编码设计阶段，可以知道你所要拦截的方法，那么，无疑直接使用Decorator模式组成过滤器是一种好方式。<br /><br />　　这也是我先期不怎么看好使用动态AOP实现的Spring 1.X版本，也不太热衷于同样使用 <br />动态AOP实现、虽符合EJB 3.0规范的JBoss 4.0了。可是，为什么总是有人迫不及待地告别EJB 2.x，夸张Spring，然后又狂炫EJB 3.0。在接纳他们之前，先把盒子打开看看验货一下，做一个普通的消费者应有的理性。<br /><br />　　当然，不是说动态AOP没有用武之地，它类似SOA的集成作用，可以在不用修改原来代码结构上强行加入过滤器。而且AOP不只是拦截器，还有introduction，这一神奇功能可以突破Java单继承法则，显得象儿童世界里面的神奇魔法一样。<br /><br />　　从某个方面来说，动态AOP和职责链非常类似，职责链类似击鼓传花，更像典型的打太极拳，推来推去，这也是政府效率不高的主要原因，同样，用在软件系统里，性能最差。想象一下，当这朵花传到真正主人面前时，而这个主人是传花环节中最后一个，这种效率和直接抛绣球一样，直接将花抛给真正主人相比，无疑最耗时间的，所以击鼓传花游戏就是玩的这个耗时间，在鼓声中，消耗时间，煎熬折磨你的心思，它玩的不是要把花如何快速准确地送给某人，它的目的性不确切。<br /><br />　　我们的软件系统不能这么玩吧？<br /><br />　　总结如下：过滤器实现方式在不保证功能前提下，从性能角度考虑有如下先后顺序：Decorator或Proxy模式；AOP拦截器。<br /><br />　　考虑使用AOP拦截器时，最好选择那些受众面积比较广的功能，例如一些基础通用功能：权限检查；事务机制；Pool等，这些功能不是针对某个具体类或方法（方法权限除外），而是一系列类，这样使用动态AOP拦截器，就是有些性能损耗也是值得的，而且是必要的，使用其他方法也会引起这样的损耗。<br /><br />　　如果过滤器是业务逻辑的一部分，而且在设计时，我们可以确定这些过滤器，这样我们使用Decorator模式或Proxy模式进行特定指定的拦截，当然，因为每个类/接口都需要一个附加的Decorator/Proxy，如果某个过滤功能是很多类都需要的，会形成很多Decorator/Proxy附加类，当点形成面时，这时AOP切面概念就应该浮现在你脑海，这时升级使用AOP拦截器就更好。Decorator/Proxy在点上针对性相当强，特别在这个点上有一系列过滤器需要实现时。<br /><br />　　职责链和Decorator/AOP拦截器是有些区别的，在一个动态运行系统中，有两个概念：由客户端触发的请求对象，该请求对象需要穿透一系列过滤器（防火墙），最终可能达到持久层数据库。Decorator/AOP拦截器是对过滤器管理的一种模式，也就是说：怎么设计过滤器类；过滤器类关系是怎样；而职责链不是对类关系管理定义，而是为了处理某个请求对象而实现的。他们区别在于目标对象不一样，所以职责链是一种很具体的行为。<br /><br />　　在这个层面上，Command模式和其是相竞争的，Command模式类似直接抛绣球，知道目的，能够最有效率，但是前提在设计编码阶段你必须知道你的目的地；Command模式和职责链的区别与Decorator和AOP拦截器的区别是类似的。<br />-----------------------------------------------------------------<br />[摘自]http://www.javaeye.com/topic/34339
          <br/>
          <span style="color:red;">
            <a href="http://hlxiong.javaeye.com/blog/145644#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/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 03 Dec 2007 16:47:01 +0800</pubDate>
        <link>http://hlxiong.javaeye.com/blog/145644</link>
        <guid>http://hlxiong.javaeye.com/blog/145644</guid>
      </item>
      <item>
        <title>Spring AOP应用总结--AfterReturningAdvice</title>
        <author>hlxiong</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hlxiong.javaeye.com">hlxiong</a>&nbsp;
          链接：<a href="http://hlxiong.javaeye.com/blog/144623" style="color:red;">http://hlxiong.javaeye.com/blog/144623</a>&nbsp;
          发表时间: 2007年11月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Spring AOP可以方便地监控方法执行情况，可以用来记录系统日志。以下是通过after通知来记录指定方法的执行情况。</p>
<p>通知类：<font face="Arial">SysLogAfterAdvice</font></p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;SysLogAfterAdvice&nbsp;</span><span class="keyword">implements</span><span>&nbsp;AfterReturningAdvice&nbsp;{ &nbsp;&nbsp;</span></span> </li>
    <li class=""><span></span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;afterReturning(Object&nbsp;arg0,&nbsp;Method&nbsp;arg1,&nbsp;Object[]&nbsp;arg2,&nbsp;Object&nbsp;arg3)&nbsp;</span><span class="keyword">throws</span><span>&nbsp;Throwable&nbsp;{ &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//实现代码 </span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>在该类中记录需要监听的方法执行情况，形成系统日志。</p>
<p>关键在于配置文件。</p>
<div class="code_title">xml 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-xml">
    <li class="alt"><span><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">&quot;id1_Service&quot;</span><span>&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;org.springframework.aop.framework.ProxyFactoryBean&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;target&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ref</span><span>&nbsp;</span><span class="attribute">local</span><span>=</span><span class="attribute-value">&quot;beanTarge&quot;</span><span>&nbsp;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;interceptorNames&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">list</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">value</span><span class="tag">&gt;</span><span>logAfterAdvisor</span><span class="tag"><span class="tag-name">value</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">list</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">bean</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span></span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">&quot;logAfterAdvisor&quot;</span><span>&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;org.springframework.aop.support.RegexpMethodPointcutAdvisor&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;advice&quot;</span><span class="tag">&gt;</span><span class="tag">&lt;</span><span class="tag-name">ref</span><span>&nbsp;</span><span class="attribute">local</span><span>=</span><span class="attribute-value">&quot;sysLogAfterAdvice&quot;</span><span>&nbsp;</span><span class="tag">/&gt;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;pattern&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">value</span><span class="tag">&gt;</span><span>.*do.*</span><span class="tag"><span class="tag-name">value</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">bean</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>&nbsp;=</span><span class="attribute-value">&quot;sysLogAfterAdvice&quot;</span><span>&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">&quot;com.jiuqi.mcasv2.logic.sysmng.SysLogAfterAdvice&quot;</span><span>&nbsp;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;logMngBO&quot;</span><span class="tag">&gt;</span><span class="tag">&lt;</span><span class="tag-name">ref</span><span>&nbsp;</span><span class="attribute">bean</span><span>=</span><span class="attribute-value">&quot;logMngBO&quot;</span><span class="tag">/&gt;</span><span class="tag"><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">bean</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;&nbsp;</span> </span></li>
</ol>
</div>
<p>id1_Service中属性interceptorNames指定进行监听器列表，其次序是有影响的。</p>
<p>监听器logAfterAdvisor将通知及切入点组合在一起，指明对以do开头的方法发送通知sysLogAfterAdvice进行拦截处理。</p>
<p>其它类型的通知，配置方式同上，只要改变相应通知类就行了。其它可用通知类型包括before/around/after throwing等类型。</p>
<p>around通知是万金油型，对方法的执行前后情况都可拦截，通用。</p>
<p>但是建议用最适合的通知类型，而不是任何情况都用around通知，虽然它也能解决。</p>
          <br/>
          <span style="color:red;">
            <a href="http://hlxiong.javaeye.com/blog/144623#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 28 Nov 2007 20:22:34 +0800</pubDate>
        <link>http://hlxiong.javaeye.com/blog/144623</link>
        <guid>http://hlxiong.javaeye.com/blog/144623</guid>
      </item>
      <item>
        <title>单元测试之初步实践</title>
        <author>hlxiong</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hlxiong.javaeye.com">hlxiong</a>&nbsp;
          链接：<a href="http://hlxiong.javaeye.com/blog/134537" style="color:red;">http://hlxiong.javaeye.com/blog/134537</a>&nbsp;
          发表时间: 2007年10月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><font face="Arial">&nbsp;断断续续地学习了一些单元测试的知识，在最近的编码过程中有意识地进行了实践，勉强能达到一点测试的既定目的，但感觉疑惑仍然不少。</font></p>
<p><font face="Arial">在javaeye上也拜读了诸多高人们关于单元测试、TDD方面的文章，获益良多，但是感觉很多文章起点有些高，像我这样比较笨的人读多次都不一定能领悟，适合入门一级的测试文章不太多。因此我想将自己实施单元测试的一些实践整理出来，尽量表述出我的想法，尽量提供比较详细的代码，希望初次接触单元测试的朋友能从中受益，从而少走一些弯路。另外，我在学习和实施单元测试的过程中也有很多不解和困惑，希望可以得到大家的指点。</font></p>
<p><font face="Arial">&nbsp;先列出一个测试代码实例吧。<br />
&nbsp;业务逻辑：对员工信息的增删改查；<br />
&nbsp;业务对象：EmpBO；<br />
&nbsp;业务对象接口代码（部分）： </font></p>
<font face="Arial">
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="comment">/**&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;*&nbsp;Title:员工BO接口&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;*&nbsp;Description:维护员工信息&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;*&nbsp;@author&nbsp;xhl&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;*&nbsp;@version&nbsp;1.0&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;*&nbsp;@date&nbsp;2007-9-18&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;*/</span><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class=""><span></span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">interface</span><span>&nbsp;EmpBOI&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;</span><span class="comment">/**新增一条员工信息&nbsp;&nbsp;&nbsp; </span>&nbsp; </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;@param&nbsp;fmCau&nbsp;员工form&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;@return&nbsp;ArrayList&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;0&nbsp;String&nbsp;true/false&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;1&nbsp;String&nbsp;成功/失败信息&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;@author&nbsp;xhl&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;@date&nbsp;2007-9-29&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class="alt"><span>&nbsp;</span><span class="keyword">public</span><span>&nbsp;ArrayList&nbsp;saveNewEmp(CcpAclsUserExtForm&nbsp;fmCau);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;</span><span class="comment">/**修改员工信息&nbsp;&nbsp;&nbsp; </span>&nbsp; </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;@param&nbsp;fmCau&nbsp;员工form&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;@return&nbsp;ArrayList&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;0&nbsp;String&nbsp;true/false&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;1&nbsp;String&nbsp;成功/失败信息&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;@author&nbsp;xhl&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;@date&nbsp;2007-9-29&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class="alt"><span>&nbsp;</span><span class="keyword">public</span><span>&nbsp;ArrayList&nbsp;saveEmp(CcpAclsUserExtForm&nbsp;fmCau);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;</span><span class="comment">/**删除员工信息&nbsp;&nbsp;&nbsp; </span>&nbsp; </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;@param&nbsp;ccpUserGuid&nbsp;员工guid&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;@return&nbsp;ArrayList&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;0&nbsp;String&nbsp;true/false&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;1&nbsp;String&nbsp;成功/失败信息&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;@author&nbsp;xhl&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;@date&nbsp;2007-9-18&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class="alt"><span>&nbsp;</span><span class="keyword">public</span><span>&nbsp;ArrayList&nbsp;removeEmp(String&nbsp;ccpUserGuid);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;</span><span class="comment">/**根据员工guid取员工form&nbsp;&nbsp;&nbsp; </span>&nbsp; </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;@param&nbsp;ccpUserGuid&nbsp;员工guid&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;@return&nbsp;ArrayList&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;CcpAclsUserExtForm&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*&nbsp;@author&nbsp;xhl&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;*&nbsp;@date&nbsp;2007-9-29&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class=""><span>&nbsp;</span><span class="keyword">public</span><span>&nbsp;ArrayList&nbsp;getEmpById(String&nbsp;ccpUserGuid);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>&nbsp;</p>
<p><font face="Arial">&nbsp;这段代码反映了典型的CRUD业务逻辑操作，对实现类的测试基本说明了我在实践单元测试时遇到的问题。<br />
&nbsp;对于写代码的顺序，我一般是先根据概设文档定义出业务逻辑操作类接口，测试类，业务逻辑类，然后针对每一个接口方法，先写该方法的测试用例，然后在业务逻辑类中实现该方法，运行测试，修改或重构实现代码。<br />
&nbsp;下面来看测试代码。<br />
&nbsp;首先定义了一个测试基类，扩展自Spring提供的JUnit封装类AbstractDependencyInjectionSpringContextTests。<br />
&nbsp;测试基类代码：</font><font face="Arial">&nbsp;&nbsp; </font></p>
<p>&nbsp;</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">abstract</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;SpringUnitTest&nbsp;</span><span class="keyword">extends</span><span>&nbsp;AbstractDependencyInjectionSpringContextTests{&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="comment">/*&nbsp;(non-Javadoc)&nbsp;&nbsp;&nbsp; </span>&nbsp; </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;&nbsp;*&nbsp;@see&nbsp;org.springframework.test.AbstractSingleSpringContextTests#getConfigLocations()&nbsp;&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class=""><span>&nbsp;&nbsp;</span><span class="keyword">protected</span><span>&nbsp;String[]&nbsp;getConfigLocations()&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;TODO&nbsp;Auto-generated&nbsp;method&nbsp;stub&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;setAutowireMode(AUTOWIRE_BY_NAME);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;String[]{Constants.DEFAULT_SPRING_CONTEXT_HIB,&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Constants.DEFAULT_SPRING_CONTEXT_SER,&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Constants.DEFAULT_SPRING_CONTEXT_RES,&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Constants.DEFAULT_TEST_SPRING_CONTEXT_CCP};&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;}&nbsp;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>&nbsp;SpringUnitTest类扩展AbstractDependencyInjectionSpringContextTests，实现了其抽象方法getConfigLocations，将Spring对bean的匹配方式设为AUTOWIRE_BY_NAME，根据名称而非type查找bean，然后返回Spring相关 配置文件的路径。配置文件中定义了DataSource数据源，测试代码运行时连接数据库。<br />
&nbsp;对员工管理BO的测试类EmpBOTest定义:</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;EmpBOTest&nbsp;</span><span class="keyword">extends</span><span>&nbsp;SpringUnitTest&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class=""><span>&nbsp;&nbsp;</span><span class="keyword">protected</span><span>&nbsp;EmpBOI&nbsp;empBO;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;setEmpBO(EmpBOI&nbsp;empBO)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>.empBO&nbsp;=&nbsp;empBO;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>&nbsp;</p>
<p>&nbsp;测试类中注入了待测试的业务类。</p>
<p>首先，写新增员工方法saveNewEmp的测试代码：</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;testSaveNewEmp(){&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;CcpAclsUserExtForm&nbsp;fmCau&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;CcpAclsUserExtForm);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setCauGuid(</span><span class="string">&quot;9DDC036A177088F0FAE833CEA0971DF0&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setName(</span><span class="string">&quot;吕南&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setSex(</span><span class="string">&quot;02&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setBirth(</span><span class="string">&quot;1989-11-24&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setAddress(</span><span class="string">&quot;北京市大兴区&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setMobile(</span><span class="string">&quot;13810384254&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setWorkUnit(</span><span class="string">&quot;威天软件&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setTel(</span><span class="string">&quot;01055669235&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ArrayList&nbsp;retlist&nbsp;=&nbsp;</span><span class="keyword">this</span><span>.ccpUserBO.saveNewEmp(fmCau);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span class="string">&quot;true&quot;</span><span>,(String)retlist.get(</span><span class="number">0</span><span>));&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>}&nbsp;&nbsp;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>&nbsp; </p>
<p>然后在业务实现类EmpBO中具体实现该方法的业务逻辑。</p>
<p>说明一下，CcpAclsUserExtForm是对员工pojo对象的封装，员工对象的主键是cauGuid，这是一个由程序依据一定规则随机产生的32位字符串。</p>
<p>OK，现在业务代码有了，测试代码也已就位，数据库已运行，配置文件也在正确路径上，一切准备工作均已就绪，可以运行测试了。</p>
<p>Run测试代码，发现bug，修改业务代码，再次run，直到绿条出现，一个方法测试完成，一路有惊无险，很顺利。</p>
<p>当然上面的测试代码只测试了程序正常执行的分支，没有覆盖其它可能出现异常的分支，完全可以加入对异常分支的测试代码，不过这个业务比较简单，对正常分支测试通过基本就OK了。个人觉得，单元测试没有必要太看重测试覆盖率，够用就行了。</p>
<p>这样，测试类中就有了一个测试方法了：</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;EmpBOTest&nbsp;</span><span class="keyword">extends</span><span>&nbsp;SpringUnitTest&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">protected</span><span>&nbsp;EmpBOI&nbsp;empBO;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;setEmpBO(EmpBOI&nbsp;empBO)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>.empBO&nbsp;=&nbsp;empBO;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;testSaveNewEmp(){&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;CcpAclsUserExtForm&nbsp;fmCau&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;CcpAclsUserExtForm);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setCauGuid(</span><span class="string">&quot;9DDC036A177088F0FAE833CEA0971DF0&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setName(</span><span class="string">&quot;吕南&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setSex(</span><span class="string">&quot;02&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setBirth(</span><span class="string">&quot;1989-11-24&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setAddress(</span><span class="string">&quot;北京市大兴区&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setMobile(</span><span class="string">&quot;13810384254&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setWorkUnit(</span><span class="string">&quot;威天软件&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setTel(</span><span class="string">&quot;01055669235&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;ArrayList&nbsp;retlist&nbsp;=&nbsp;</span><span class="keyword">this</span><span>.ccpUserBO.saveNewEmp(fmCau);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span class="string">&quot;true&quot;</span><span>,(String)retlist.get(</span><span class="number">0</span><span>));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>&nbsp;</p>
<p>测试代码运行后，数据库中就有了员工信息记录。接下来是实现员工信息的修改逻辑。测试代码如下：</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;testSaveEmp(){&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;CcpAclsUserExtForm&nbsp;fmCau&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;CcpAclsUserExtForm();&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setCauGuid(</span><span class="string">&quot;FD705E2FA08E95956241040BE3D83D69&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setName(</span><span class="string">&quot;吕南修改&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setSex(</span><span class="string">&quot;02&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setBirth(</span><span class="string">&quot;1968-10-15&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setAddress(</span><span class="string">&quot;北京市昌平区&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setMobile(</span><span class="string">&quot;13999999999&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setWorkUnit(</span><span class="string">&quot;join-cheer&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setTel(</span><span class="string">&quot;01058561199&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ArrayList&nbsp;retlist&nbsp;=&nbsp;</span><span class="keyword">this</span><span>.ccpUserBO.saveEmp(fmCau);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span class="string">&quot;true&quot;</span><span>,(String)retlist.get(</span><span class="number">0</span><span>));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>然后实现具体业务逻辑代码，运行测试，OK，绿条出现，测试通过。完事大吉了吗？</p>
<p>貌似没有问题，但是上面的测试代码实际是很脆弱。请注意这一句：</p>
<p>fmCau.setCauGuid(&quot;FD705E2FA08E95956241040BE3D83D69&quot;);</p>
<p>这是设置待修改的员工主键，问题就在这儿。主键是随机产生的，每个员工的主键值都是不同的。此处我将主键写死，该主键代表运行新增员工的测试代码testSaveNewEmp得到的员工记录。那么，我下次运行新增员工方法testSaveNewEmp时，得到的员工记录主键发生了变化，为了使修改员工的测试方法testSaveEmp正确运行，我发布修改测试方法中的代码，将待修改的员工对象主键值设为这次得到的员工记录的主键值。</p>
<p>如此一来，我的测试类无法运行，只能每次运行其中的一个测试方法。这种笨方法在业务逻辑开发阶段还能承受，但测试的自动化运行就没办法了。测试结果无法再现，每次运行都要修改测试代码，太恐怖了！</p>
<p>经过N久的痛苦之后，我决定改变测试基类。现在的基类扩展自AbstractDependencyInjectionSpringContextTests，我将其改为继承自AbstractTransactionalDataSourceSpringContextTests，这样我的测试类就自动具有事务功能，即在每个测试方法执行后Spring会自动回滚事务，将数据库还原为初始状态。然后在我的测试类里定义一个私有方法，用于向数据库中插入测试数据，每个测试方法运行时都要调用这个方法产生数据，因为测试类具有自动回滚功能，每个方法运行完成后，数据库会还原，将测试数据清空，以待下次运行时插入。这样一来，每次测试类运行时，可以保证测试数据都是相同的。这样我就可以随时运行测试而不用担心修改测试代码了。</p>
<p>修改后的测试基类如下：</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;SpringTransactUnitTest&nbsp;</span><span class="keyword">extends</span><span>&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AbstractTransactionalDataSourceSpringContextTests&nbsp;{&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/*&nbsp;(non-Javadoc)&nbsp;&nbsp; </span>&nbsp; </li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@see&nbsp;org.springframework.test.AbstractSingleSpringContextTests#getConfigLocations()&nbsp;&nbsp; </span>&nbsp;</span> </li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">protected</span><span>&nbsp;String[]&nbsp;getConfigLocations()&nbsp;{&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;TODO&nbsp;Auto-generated&nbsp;method&nbsp;stub&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAutowireMode(AUTOWIRE_BY_NAME);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;String[]{Constants.DEFAULT_SPRING_CONTEXT_HIB,&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Constants.DEFAULT_SPRING_CONTEXT_SER,&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Constants.DEFAULT_SPRING_CONTEXT_RES,&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Constants.DEFAULT_TEST_SPRING_CONTEXT_CCP,&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Constants.DEFAULT_TEST_SPRING_CONTEXT_APP&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>}&nbsp;&nbsp;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>&nbsp;</p>
<p>修改后的测试类如下：</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;EmpBOTest&nbsp;</span><span class="keyword">extends</span><span>&nbsp;SpringTransactUnitTest&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">protected</span><span>&nbsp;EmpBOI&nbsp;empBO;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;setEmpBO(EmpBOI&nbsp;empBO)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>.empBO&nbsp;=&nbsp;empBO;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;ArrayList&nbsp;add(){&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;CcpAclsUserExtForm&nbsp;fmCau&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;CcpAclsUserExtForm);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setCauGuid(</span><span class="string">&quot;9DDC036A177088F0FAE833CEA0971DF0&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setName(</span><span class="string">&quot;吕南&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setSex(</span><span class="string">&quot;02&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setBirth(</span><span class="string">&quot;1989-11-24&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setAddress(</span><span class="string">&quot;北京市大兴区&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setMobile(</span><span class="string">&quot;13810384254&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setWorkUnit(</span><span class="string">&quot;威天软件&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setTel(</span><span class="string">&quot;01055669235&quot;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;ArrayList&nbsp;retlist&nbsp;=&nbsp;</span><span class="keyword">this</span><span>.ccpUserBO.saveNewEmp(fmCau);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;retlist; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;testSaveNewEmp(){&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ArrayList&nbsp;retlist&nbsp;=&nbsp;add();&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span class="string">&quot;true&quot;</span><span>,(String)retlist.get(</span><span class="number">0</span><span>));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;testSaveEmp(){&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;add();&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;CcpAclsUserExtForm&nbsp;fmCau&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;CcpAclsUserExtForm();&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setCauGuid(</span><span class="string">&quot;9DDC036A177088F0FAE833CEA0971DF0&quot;</span><span>);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;fmCau.setName(</span><span class="string">&quot;王小二&quot;</span><span>);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;ArrayList&nbsp;retlist&nbsp;=&nbsp;</span><span class="keyword">this</span><span>.ccpUserBO.saveEmp(fmCau);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span class="string">&quot;true&quot;</span><span>,(String)retlist.get(</span><span class="number">0</span><span>));&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>} &nbsp;&nbsp;</span> </li>
</ol>
</div>
<p><br />
业务类中的删除与查询方法就不在此赘述了。<br />
一个小技艺：私有方法add用于产生测试数据，其实也可能通过AbstractTransactionalDataSourceSpringContextTests提供的jdbcTemplate执行原生SQL语句来向数据库中插入数据。这在测试业务类没有提供新增方法时非常实用。</p>
<p>另外，我认为，测试时最好使用一个单独的测试库，不要用项目组公用的开发库，否则会产生很多麻烦。</p>
<p>以上是我在具体项目中实施单元测试的一点心得体会，贴出来给大家参考，也欢迎大家提出批评意见。<br />
</p>
</font>
          <br/>
          <span style="color:red;">
            <a href="http://hlxiong.javaeye.com/blog/134537#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/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 22 Oct 2007 18:57:57 +0800</pubDate>
        <link>http://hlxiong.javaeye.com/blog/134537</link>
        <guid>http://hlxiong.javaeye.com/blog/134537</guid>
      </item>
      <item>
        <title>单元测试总结（持续更新）</title>
        <author>hlxiong</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hlxiong.javaeye.com">hlxiong</a>&nbsp;
          链接：<a href="http://hlxiong.javaeye.com/blog/134395" style="color:red;">http://hlxiong.javaeye.com/blog/134395</a>&nbsp;
          发表时间: 2007年10月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><font face="Arial">A、单元测试原理归纳</font></p>
<p><font face="Arial">优良的单元测试具有以下的特点：简称为 A-TRIP。 </font></p>
<font face="Arial">
<p><br />
自动性(Automatic)</p>
<p>完备性(Thorough)</p>
<p>可重复性(Repeatable)</p>
<p>独立性(Independent)</p>
<p>专业性(Professional)<br />
下面让我们逐一理解它们的含义。 </p>
<p>自动性<br />
单元测试是自动执行的，这里的自动指两个方面：1执行测试，2检查测试结果 <br />
执行测试应该是足够简单的，这样，我们就可以随时随地的进行测试。因此，执行相应测试就应该像在IDE中点击一个按钮，或者在命令行中打一个命令那么简单。一些IDE甚至会在后台连续的进行测试。 <br />
维护这个环境很关键，不要引入哪些会破坏自动测试模型，需要手工进行干预的单元测试。如果测试需要一些环境（网络，数据库，等等），那就把它做为测试的一部分。Mock对象可以有效的隔离对外部的依赖。 <br />
运行测试的不光你一个人，还应该有一台机器对所有提交的代码持续的运行所有的测试。这种自动、无人职守的检查的作用就像一个定位杆，这种安全机制保证所有提交的东西不会破坏任何测试。理想情况下并不必须这样子，因为你可以依靠每一个开发人员都会自行运行所有必需的测试。回到现实，可能某个家伙再某个遥远的地方并没有执行必需的测试。也许在他的机器上有一些代码能够保证一切没问题，可他们却没有提交代码。这样虽然在本地可以执行，其他地方就会出问题。 <br />
最后，自动性的含义还有测试必须能够自行判断成功还是失败。让一个人（你或者其他倒霉蛋）通过检查测试产生的数据结果来判断代码的正确性，这是让项目失败不错的方式。测试的自检查是一致回归的重要特性。人类并不擅长这种重复性的工作，另外，在项目里，我们还有很多更重要的事情去做。 <br />
测试的自动执行和自动检查是非常关键的，这意味着你不用花太多的心思再这上面，它已经成为了项目的一部分。测试是项目安全保护网的重要组成部分。它在你掉下来时接住你，且不会挡道，这样你就可以集中精力走&quot;钢丝&quot;了。 </p>
<p>彻底性<br />
良好的测试应该是彻底的，所有可能会出错的地方都应该测到。如何能够做到这一点呢？一种极端情况，测试每一行代码、每一个分支、每一个抛出的异常，诸如此类；另一个极端，只测试最可能的情况：边界条件、数据丢失、数据格式无效，等等。这就需要根据项目情况进行区分了。 <br />
如果追求较高的测试覆盖度，那就需要寻求代码覆盖工具来帮忙了（例如：免费的nounit,quilt，商用的Clover)，用这些工具可以知道到底有多少代码是被测试覆盖的。 <br />
有一个事实需要注意，Bug在代码中的分布情况是不均匀的，而是喜欢聚集在有问题的地方（很多昆虫都这样，例如:苍蝇）。 <br />
这种现象引出了一段非常著名的呼声&ldquo;别打补丁，重写&rdquo;。通常，从头重写一段有一堆问题的代码的代价和痛苦程度要低得多。当然，有了单元测试，从头重写代码也安全多了，单元测试可以保证新代码能够按照预定的执行。 </p>
<p><br />
可重复性<br />
测试用例之间是独立的同时，也应该是独立于环境的。目标就是保持每个测试能够以任意顺序、不断的重复执行，且得到同样的结果。这意味着测试不能依赖于不可控的任何外部环境。 <br />
使用Mock对象来隔离测试，保证测试不依赖于外部环境。如果必须依赖一些条件（例如：数据库），那就要保证这个条件不受其他开发人员的干扰。每个开发人员都应该有自己的sandbox，不同的数据库实例啦，web服务器啦。 <br />
没有可重复性保证，可能会在最糟糕的时侯遇到一些让你奇怪的问题，更糟糕的是，这些奇怪的问题通常都是虚幻的，并不是一个真正的bug，只是测试自身的问题。真不值得为这些虚幻的问题浪费时间。 <br />
每个测试用例，每次执行都应该得到同样的结果。如果测试结果是失败，那就说明是在代码里面存在bug，而不是有其他问题引起的错误。 </p>
<p><br />
独立性<br />
测试代码必须保持精简、整洁，这就要求测试是专注的、与环境和其他测试隔离的（要记住，别的开发人员可能同时在运行这些测试）。 <br />
在书写测试时，保证每个测试只做一件事情。这不是说在一个测试方法里只写一个assert，而是说在一个测试方法里应该只专注于一个、或者几个共同提供某些功能的方法产品代码。有些时候一个测试方法只能够测试了一个复杂的产品代码方法的一部分，就需要一套测试方法来完整的测试产品代码的方法了。 <br />
理想情况下，我们希望在测试代码和潜在的bug之间建立起关联。也就是说，当一个测试方法fail的时候，能够定位到对应代码中的bug。独立性也意味着test之间没有相互依赖。每一个test都是可以独立运行的，而不需要必须在其他test之后才能运行。每一个test应该是一个孤岛。 </p>
<p>专业性<br />
为单元测试所写的代码是货真价实的，甚至有些人会争辩说，比提交个客户的源代码还要货真价实。这意味着，测试代码的书写和维护应该保持和生产代码一样的专业水准。产品代码中必须遵循的常用的设计准则，如：数据封装、DRY原则、高内聚、低耦合等等，在测试代码中也一样要遵守。 <br />
测试代码很容易就会写成linear的样式，代码里面充次着同样的内容，不断的重复同样的代码，却鲜见方法和对象。这样很糟糕，测试代码必须和生产代码按同等对待来书写。这就要把哪些常用的、重复部分的代码抽取出来成为一个方法，这样就可以在多处调用。 <br />
这样就会慢慢积累出一些测试的方法，可以封装在一个类里面了。别争论什么，就算这个类只是用来测试的，就创建一个好了。这样做不进没问题，而且是应该得到鼓励的：测试代码是货真价实的代码。某些情况下，我们也许需要创建一个更大的框架，或者创建一个数据驱动的测试工具。 <br />
不要浪费时间测试那些不必要的方面，我们不是为了测试而创建test。测试的完整性要求测试方法的每一个可能产生bug的方面。如果没有产生bug的可能，就不要做测试。比如get set方法，就没必要做测试了。但如果这些get set方法中包含了业务逻辑，哪就有必要进行测试了。 <br />
最后，测试代码应该和生产代码是同一规模量级的。是的，你绝对没有看错。如果产品代码有20000行，那么测试代码至少也应该是20000行，甚至更多。测试代码的量也很大，就必须保持整洁、精简，有良好的设计，结构良好的，同生产代码一样的专业。 <br />
[摘自]http://www.javaeye.com/topic/30932</p>
<p><br />
--------------------------------------------------------------------------------</p>
<p>B、单元测试目的<br />
（1）单元测试目的：<br />
首先保证代码质量。<br />
其次保证代码的可维护。<br />
再此保证代码的可扩展。</p>
<p>（2）单元测试的优点<br />
1、它是一种验证行为。<br />
&nbsp;&nbsp;&nbsp; 程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支缓。就算是开发后期，我们也可以轻松的增加功能或更改程序结构，而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样，我们就可以更自由的对程序进行改进。<br />
2、它是一种设计行为。<br />
&nbsp;&nbsp;&nbsp; 编写单元测试将使我们从调用者观察、思考。特别是先写测试（test-first），迫使我们把程序设计成易于调用和可测试的，即迫使我们解除软件中的耦合。<br />
3、它是一种编写文档的行为。<br />
&nbsp;&nbsp;&nbsp; 单元测试是一种无价的文档，它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的，并且它保持最新，永远与代码同步。<br />
4、它具有回归性。<br />
&nbsp;&nbsp;&nbsp; 自动化的单元测试避免了代码出现回归，编写完成之后，可以随时随地的快速运行测试。 </p>
<p>（3）单元测试的范畴<br />
&nbsp;&nbsp;&nbsp; 如果要给单元测试定义一个明确的范畴，指出哪些功能是属于单元测试，这似乎很难。但下面讨论的四个问题，基本上可以说明单元测试的范畴，单元测试所要做的工作。<br />
1、 它的行为和我期望的一致吗？<br />
&nbsp;&nbsp;&nbsp; 这是单元测试最根本的目的，我们就是用单元测试的代码来证明它所做的就是我们所期望的。<br />
2、 它的行为一直和我期望的一致吗？<br />
&nbsp;&nbsp;&nbsp; 编写单元测试，如果只测试代码的一条正确路径，让它正确走一遍，并不算是真正的完成。软件开发是一个项复杂的工程，在测试某段代码的行为是否和你的期望一 致时，你需要确认：在任何情况下，这段代码是否都和你的期望一致；譬如参数很可疑、硬盘没有剩余空间、缓冲区溢出、网络掉线的时候。<br />
3、 我可以依赖单元测试吗？<br />
