<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>远 -- 学然后知不足</title>
    <description></description>
    <link>http://andyao.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>kchmviewer-&quot;failed to load the chm file&quot;</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/217379" style="color:red;">http://andyao.javaeye.com/blog/217379</a>&nbsp;
          发表时间: 2008年07月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          sudo vi /usr/share/applications/kchmviewer.desktop<br /><br />[Desktop Entry]<br />Categories=Qt;KDE;Utility;<br />Encoding=UTF-8<br />Type=Application<br />Exec=kchmviewer %U<br />Icon=kchmviewer.png<br />Comment=<br />Terminal=0<br />Name=KchmViewer<br />Comment=A viewer of CHM (MS-Help) files<br />MimeType=application/chm<br /><br />找到Exec=kchmviewer %U 这行<br /><br />将 %U 改成 %f 或 去掉 %U。
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/217379#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, 21 Jul 2008 23:03:11 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/217379</link>
        <guid>http://andyao.javaeye.com/blog/217379</guid>
      </item>
      <item>
        <title>Firestarter the device eth0 is not ready</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/212011" style="color:red;">http://andyao.javaeye.com/blog/212011</a>&nbsp;
          发表时间: 2008年07月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>一种解决方法</strong><br />该方法的思路是: firestarter使用ifconfig获取IP地址，如果ifconfig的输出时使用了非en_US的locale，那么firstarter就取不到ip的信息（比较傻，匹配的时候不支持中文），所以可以在使用ifconfig之前设置locale<br /><pre name="code" class="java">
sudo gedit /etc/firestarter/firestarter.sh
找个合适的地方，例如在
# External network interface data
这行注释的上面，添加一行：
LANG=en_US
保存后
sudo firestarter
</pre><br /><strong>另外一种解决方法</strong><br />设定固定ip, 原理不祥<br /><pre name="code" class="java">
auto eth0
iface eth0 inet static
address 192.168.0.10
netmask 255.255.255.0
gateway 192.168.0.1
</pre><br /><br />我在使用中第一种方法不起作用，第二种方法可以。
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/212011#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>Sun, 06 Jul 2008 11:25:35 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/212011</link>
        <guid>http://andyao.javaeye.com/blog/212011</guid>
      </item>
      <item>
        <title>ubuntu设置默认声卡</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/210761" style="color:red;">http://andyao.javaeye.com/blog/210761</a>&nbsp;
          发表时间: 2008年07月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          sudo asoundconf list<br /><br />Names of available sound cards:<br />Live<br />V8237<br /><br />then did:<br /><br />sudo asoundconf set-default-card Live<br /><br />followed by a reboot to test.
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/210761#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, 02 Jul 2008 20:05:41 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/210761</link>
        <guid>http://andyao.javaeye.com/blog/210761</guid>
      </item>
      <item>
        <title>MySQL部分新特性将闭源</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/184087" style="color:red;">http://andyao.javaeye.com/blog/184087</a>&nbsp;
          发表时间: 2008年04月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          SUN真是开源的毒药，收购mysql以后，竟然宣布一些特性将闭源，只提供给企业版。<br />原帖见：注意Marten Mickos是Mysql CEO<br /><a href="http://jcole.us/blog/archives/2008/04/14/just-announced-mysql-to-launch-new-features-only-in-mysql-enterprise/" target="_blank">http://jcole.us/blog/archives/2008/04/14/just-announced-mysql-to-launch-new-features-only-in-mysql-enterprise/</a><br /><br />Vadim的评论<br /><a href="http://www.mysqlperformanceblog.com/2008/04/15/partially-opensourced/" target="_blank">http://www.mysqlperformanceblog.com/2008/04/15/partially-opensourced/</a>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/184087#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>Fri, 18 Apr 2008 14:05:51 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/184087</link>
        <guid>http://andyao.javaeye.com/blog/184087</guid>
      </item>
      <item>
        <title>Git --- The stupid content tracker</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/183657" style="color:red;">http://andyao.javaeye.com/blog/183657</a>&nbsp;
          发表时间: 2008年04月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Git --- The stupid content tracker。<br />Linux内核开发使用的版本控制系统，只能在Linux系统下运行。rails目前已经采用git作为版本控制系统。<br />中文教程地址：<br /><a href="http://www.bitsun.com/documents/gittutorcn.htm" target="_blank">http://www.bitsun.com/documents/gittutorcn.htm</a>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/183657#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>Thu, 17 Apr 2008 09:58:10 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/183657</link>
        <guid>http://andyao.javaeye.com/blog/183657</guid>
      </item>
      <item>
        <title>The smallwig theory of optimization</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/183614" style="color:red;">http://andyao.javaeye.com/blog/183614</a>&nbsp;
          发表时间: 2008年04月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          There are three kinds of optimization.<br /><br />   1. Optimization by using a more sensible overall approach.<br />   2. Optimization by making the code less weird.<br />   3. Optimization by making the code more weird.<br /><br />You've probably heard, and maybe even spouted yourself, the phrase "premature optimization is the root of all evil." It's exclusively "Type 3 optimization" that this aphorism applies to. Types 1 and 2 are quite fine to engage in pre-emptively.<br /><br />To make a type 3 optimization, your burdens are six:<br /><br />   1. Thou shalt have excellent, comprehensive unit tests.<br />   2. Thou shalt have a reliable benchmark, based on representative inputs.<br />   3. Thou shalt demonstrate that your change improves the benchmark.<br />   4. Thou shalt successfully argue that this improvement really matters.<br />   5. Thou shalt comment the code.<br />   6. In nontrivial cases, thou shalt also preserve the clear-but-slow implementation, to use in parity tests with your optimized implementation.<br /><br />In all things, remember these truths:<br /><br />   1. Your brain is a terrible profiler.<br />   2. Hotspot will outsmart you.<br />   3. It just doesn't matter, until it matters.<br /><br />If you believe this post, please spread the word!
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/183614#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>Thu, 17 Apr 2008 09:49:21 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/183614</link>
        <guid>http://andyao.javaeye.com/blog/183614</guid>
      </item>
      <item>
        <title>Unitils 1.1rc</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/181155" style="color:red;">http://andyao.javaeye.com/blog/181155</a>&nbsp;
          发表时间: 2008年04月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          2008-03-28 - Release of Unitils 1.1 rc 1<br /><br />We've been working hard to implement following features: JPA integration, support for hierarchically organized database scripts, support for Oracle PL/SQL in database scripts. Also, Unitils' transaction management and hibernate integration have been re-implemented on top of spring's abstraction layer for ORM and transactions. Unitils' JPA integration is also built on top of spring's JPA abstraction layer. Therefore, spring (2.0 or later) has become a depencency of unitils for all persistence-layer testing features. This doesn't mean your application code necessarily has to use spring: spring is used by unitils under the hoods, transparantly.<br /><br />Unitils 1.1-rc-1 bundles all these new features. Read the tutorial for more information on Unitils' new features. Take a look at the migration page to see what you must do to migrate to unitils 1.1-rc-1.
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/181155#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>Thu, 10 Apr 2008 11:25:09 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/181155</link>
        <guid>http://andyao.javaeye.com/blog/181155</guid>
      </item>
      <item>
        <title>ElementType.LOCAL_VARIABLE目前基本没用</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/180240" style="color:red;">http://andyao.javaeye.com/blog/180240</a>&nbsp;
          发表时间: 2008年04月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          jdk5.0引入Annotation语法，@Target中的ElementType包括<br /><pre name="code" class="java">ElementType.ANNOTATION_TYPE	应用于其他注解的元注解
ElementType.CONSTRUCTOR	构造函数
ElementType.FIELD	字段
ElementType.LOCAL_VARIABLE	方法中的本地变量
ElementType.METHOD	方法
ElementType.PACKAGE	包
ElementType.PARAMETER	方法的参数
ElementType.TYPE	类，接口或者枚举声明</pre><br />ElementType.LOCAL_VARIABLE是方法中的本地变量。但是目前的javac不会在bytecode中的local variable中保存annotation信息，所以就无法在runtime时获取该annotaion。也就是说ElementType.LOCAL_VARIABLE只能用在RetentionPolicy.SOURCE情况下。<br /><br />google了一下，JSR308会解决该问题<br /><a href="http://jcp.org/en/jsr/detail?id=308" target="_blank">http://jcp.org/en/jsr/detail?id=308</a><br /><a href="http://groups.csail.mit.edu/pag/jsr308/" target="_blank">http://groups.csail.mit.edu/pag/jsr308/</a>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/180240#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, 07 Apr 2008 18:30:24 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/180240</link>
        <guid>http://andyao.javaeye.com/blog/180240</guid>
      </item>
      <item>
        <title>Unitils结合Dbdeploy管理测试数据库</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/180231" style="color:red;">http://andyao.javaeye.com/blog/180231</a>&nbsp;
          发表时间: 2008年04月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Unitils是单元测试的一组工具集，介绍见<a href="../../../topic/160004" target="_blank">http://www.javaeye.com/topic/160004</a></p><p>Dbdeploy是Thoughtworks公司开发的数据库工具，获得Jolt大奖。&nbsp;</p><p>Unitils有自己的一个管理类似于dbdeploy的管理数据库的module -- DBMaintainer，其要求的sql文件名称格式如下所示，要求使用下划线分隔</p><pre name="code" class="java">001_create_users_table.sql</pre><p>Dbdeploy要求的sql文件格式使用空格分隔</p><pre name="code" class="java">1 create users table.sql</pre><p>如果在项目中同时使用了dbdeploy和unitils（dbdeploy比dbmaintainer更强大，同时支持undo），由于两种格式不匹配，就需要维护两套sql文件，比较麻烦。目前在dbdeploy中没有发现可以配置文件名的方法，只能从unitils入手，查看unitils的文档找到解决方法。</p><p>使用unitils时可以自定义ScriptSource(获取sql file source的一个接口)，在unitils.properties中加入</p><pre name="code" class="java">org.unitils.dbmaintainer.script.ScriptSource.implClassName=com.andyao.unitils.DbDeployScriptSource</pre><p>&nbsp;DbDeployScriptSource为</p><pre name="code" class="java">public class DbDeployScriptSource extends FileScriptSource {

    public static final String SEPARATOR = &quot; &quot;;


    /**
     * Indicates if the given file is regarded as a script file
     *
     * @param file                    The file
     * @param scriptFileSpecification Specification describing the files that can be regarded as a script file
     * @return True if the given file is regarded as a script file.
     */
    @Override
    protected boolean isScriptFile(File file, ScriptFilesSpecification scriptFileSpecification) {
        String name = file.getName();
        boolean fileExtensionSupported = false;
        for (String fileExtension : scriptFileSpecification.getFileExtensions()) {
            if (name.endsWith(fileExtension)) {
                fileExtensionSupported = true;
                break;
            }
        }
        if (!fileExtensionSupported) {
            return false;
        }

        if (scriptFileSpecification.isExcludeFilesWithoutIndex()) {
            if (!StringUtils.contains(name, SEPARATOR)) {
                return false;
            }
            String indexNrStr = StringUtils.substringBefore(name, SEPARATOR);
            if (!StringUtils.isNumeric(indexNrStr)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns the version index of the given script file
     *
     * @param scriptFile The file containing a script
     * @return The version of the script file
     */
    @Override
    protected Long getIndex(File scriptFile) {
        if (StringUtils.contains(scriptFile.getName(), SEPARATOR)) {
            try {
                return new Long(StringUtils.substringBefore(scriptFile.getName(), SEPARATOR));
            } catch (NumberFormatException e) {
                return -1L;
            }
        } else {
            return -1L;
        }
    }
}</pre>&nbsp;这样就可以同时使用dbdeploy和unitils，做到自动化维护测试数据库。<p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/180231#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, 07 Apr 2008 18:01:45 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/180231</link>
        <guid>http://andyao.javaeye.com/blog/180231</guid>
      </item>
      <item>
        <title>Java Persistence with Hibernate</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/179755" style="color:red;">http://andyao.javaeye.com/blog/179755</a>&nbsp;
          发表时间: 2008年04月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><img src="http://lh3.google.com/iamin83567/R-oc--J9cDI/AAAAAAAABHo/qu0PC-syvKc/JavaPersistenceWithHibernateCoverBig.jpg?imgmax=512" alt="" />
<br />
<br />图灵出版社官方Hibernate实战（第2版）链接为：
<br /><a href="http://www.turingbook.com/Books/ShowBook.aspx?BookID=260" target="_blank">http://www.turingbook.com/Books/ShowBook.aspx?BookID=260</a>
<br />
<br />书　　名： Hibernate实战（第2版） 
<br />评论星级：	****
<br />书　　号： 978-7-115-17448-2 
<br />原 书 名： Java Persistence with Hibernate 
<br />原出版社：	Manning Publications 
<br />丛 书 名：	图灵程序设计丛书 
<br />分　　类： 计算机 &gt;&gt; 程序设计 &gt;&gt; Java 
<br />作　　者：	Christian Bauer, Gavin King 
<br />译　　者：	杨春花 彭永康 俞黎敏 
<br />出版日期：	2008年3月 
<br />语　　种：	简体中文 
<br />开　　本：	16开 
<br />页　　数：	640 
<br />定　　价：	99.00 元人民币</p><p>&nbsp;</p><p>&nbsp;</p><p>之前英文原版当手册查看过一部分。既然有中文版，而且是<a href="http://yulimin.javaeye.com/blog/176487" target="_blank">YuLimin</a> 参与翻译的，翻译质量应该没有问题，所以就收了一本。总体翻译很到位，缺点就是封面太难看，纸张不是太好，有些概念英文原文没加。</p>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/179755#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>Sat, 05 Apr 2008 21:56:20 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/179755</link>
        <guid>http://andyao.javaeye.com/blog/179755</guid>
      </item>
      <item>
        <title>Memcached java client 2.01发布</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/179753" style="color:red;">http://andyao.javaeye.com/blog/179753</a>&nbsp;
          发表时间: 2008年04月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          com.danga.MemCached 发布2.0.1包括许多重要更新，建议升级至2.0.1<br /><div class="quote_title">引用</div><div class="quote_div">Released version 2.0.1 today. Many bug fixes on the 2.0 branch. You should use this and *not* use the 2.0 release.</div>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/179753#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>Sat, 05 Apr 2008 21:39:15 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/179753</link>
        <guid>http://andyao.javaeye.com/blog/179753</guid>
      </item>
      <item>
        <title>Struts2中使用Stream Result Type</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/179708" style="color:red;">http://andyao.javaeye.com/blog/179708</a>&nbsp;
          发表时间: 2008年04月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Stream result type是Struts2中比较有用的一个feature。特别是在动态生成图片和文档的情况下；例如动态验证码，各种报表图片生成等。鉴于网上使用struts2生成动态验证码，struts2+jfreechart的例子中很少使用到该feature，这里以生成动态验证码为例解释stream result的使用： </p><ol><li><div>Action类，action主要要提供一个获取InputStrem的方法<pre name="code" class="java">public class CheckCodeAction extends ActionSupport implements SessionAware {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    private InputStream imageStream;
    private Map session;

    public String getCheckCodeImage(String str, int show, ByteArrayOutputStream output) {
        Random random = new Random();
        BufferedImage image = new BufferedImage(80, 30, BufferedImage.TYPE_3BYTE_BGR);
        Font font = new Font(&quot;Arial&quot;, Font.PLAIN, 24);
        int distance = 18;
        Graphics d = image.getGraphics();
        d.setColor(Color.WHITE);
        d.fillRect(0, 0, image.getWidth(), image.getHeight());
        d.setColor(new Color(random.nextInt(100) + 100, random.nextInt(100) + 100, random.nextInt(100) + 100));
        for (int i = 0; i &lt; 10; i++) {
            d.drawLine(random.nextInt(image.getWidth()), random.nextInt(image.getHeight()), random.nextInt(image.getWidth()),
                    random.nextInt(image.getHeight()));
        }
        d.setColor(Color.BLACK);
        d.setFont(font);
        String checkCode = &quot;&quot;;
        char tmp;
        int x = -distance;
        for (int i = 0; i &lt; show; i++) {
            tmp = str.charAt(random.nextInt(str.length() - 1));
            checkCode = checkCode + tmp;
            x = x + distance;
            d.setColor(new Color(random.nextInt(100) + 50, random.nextInt(100) + 50, random.nextInt(100) + 50));
            d.drawString(tmp + &quot;&quot;, x, random.nextInt(image.getHeight() - (font.getSize())) + (font.getSize()));
        }
        d.dispose();
        try {
            ImageIO.write(image, &quot;jpg&quot;, output);
        } catch (IOException e) {
            log.warn(&quot;生成验证码错误.&quot;, e);
        }
        return checkCode;
    }

    public String execute() throws Exception {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        String checkCode = getCheckCodeImage(&quot;ABCDEFGHJKLMNPQRSTUVWXYZ123456789&quot;, 4, output);
        this.session.put(Constants.CHECK_CODE_KEY, checkCode);
        //这里将output stream转化为 inputstream
        this.imageStream = new ByteArrayInputStream(output.toByteArray());
        output.close();
        return SUCCESS;
    }

    public InputStream getImageStream() {
        return imageStream;
    }

    public void setSession(Map session) {
        this.session = session;
    }</pre>&nbsp;</div></li><li><div>struts配置文件<pre name="code" class="java">&lt;action name=&quot;checkCode&quot; class=&quot;CheckCodeAction&quot;&gt;
      &lt;result name=&quot;success&quot; type=&quot;stream&quot;&gt;
            &lt;param name=&quot;contentType&quot;&gt;image/jpeg&lt;/param&gt;
            &lt;!-- 指定提供InputStream的filed name --&gt;
            &lt;param name=&quot;inputName&quot;&gt;imageStream&lt;/param&gt;
            &lt;param name=&quot;bufferSize&quot;&gt;1024&lt;/param&gt;
        &lt;/result&gt;
        &lt;interceptor-ref name=&quot;defaultStack&quot;/&gt;
&lt;/action&gt;</pre>&nbsp;</div></li><li><div>可以看出使用Stream result type非常简单。在该例子中使用了一个小技巧将OutputStream转化为InputStrem<pre name="code" class="java">ByteArrayOutputStream output = new ByteArrayOutputStream();
//省略填充output的代码
...   
InputStrem
in = new ByteArrayInputStream(output.toByteArray());&nbsp;</pre></div></li></ol>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/179708#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>Sat, 05 Apr 2008 18:25:11 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/179708</link>
        <guid>http://andyao.javaeye.com/blog/179708</guid>
      </item>
      <item>
        <title>NotSerializableException: EnhancerByCGLIB</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/179125" style="color:red;">http://andyao.javaeye.com/blog/179125</a>&nbsp;
          发表时间: 2008年04月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>使用Ibatis时，为了获得更好的性能，我们一般都会将enhancementEnabled设为true，使用cglib。如果从ibatis获取的domain object需要序列化，这时就需要注意有可能出现如下异常：</p><pre name="code" class="java">java.io.NotSerializableException: $java.util.List$$EnhancerByCGLIB$$a80b8ab4
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
	at &times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;&times;
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at org.unitils.UnitilsJUnit3.runTest(UnitilsJUnit3.java:171)
	at org.unitils.UnitilsJUnit3.runBare(UnitilsJUnit3.java:138)
	at org.unitils.UnitilsJUnit3.run(UnitilsJUnit3.java:101)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)</pre><p>最近遇到这个问题，<a href="../../../topic/76364" target="_blank">http://www.javaeye.com/topic/7636</a> 也出现该问题。</p><p>经过测试，在使用IBatis &quot;Complex Collection Properties&quot;时会出现该问题（例子来源于ibatis文档）</p><pre name="code" class="xml">&lt;resultMap id=&rdquo;get-category-result&rdquo; class=&rdquo;com.ibatis.example.Category&rdquo;&gt;
&lt;result property=&rdquo;id&rdquo; column=&rdquo;CAT_ID&rdquo;/&gt;
&lt;result property=&rdquo;description&rdquo; column=&rdquo;CAT_DESCRIPTION&rdquo;/&gt;
&lt;result property=&rdquo;productList&rdquo; column=&rdquo;CAT_ID&rdquo; select=&rdquo; getProductsByCatId&rdquo;/&gt;
&lt;/resultMap&gt;
&lt;resultMap id=&rdquo;get-product-result&rdquo; class=&rdquo;com.ibatis.example.Product&rdquo;&gt;
&lt;result property=&rdquo;id&rdquo; column=&rdquo;PRD_ID&rdquo;/&gt;
&lt;result property=&rdquo;description&rdquo; column=&rdquo;PRD_DESCRIPTION&rdquo;/&gt;
&lt;/resultMap&gt;
&lt;select id=&rdquo;getCategory&rdquo; parameterClass=&rdquo;int&rdquo; resultMap=&rdquo;get-category-result&rdquo;&gt;
select * from CATEGORY where CAT_ID = #value#
&lt;/select&gt;
&lt;select id=&rdquo;getProductsByCatId&rdquo; parameterClass=&rdquo;int&rdquo; resultMap=&rdquo;get-product-result&rdquo;&gt;
select * from PRODUCT where PRD_CAT_ID = #value#
&lt;/select&gt;
</pre>&nbsp;<ol><li><span style="font-weight: normal">productList采用ArrayList；Product， Category类实现Serializable接口。从ibatis获取Category后（<span style="font-weight: bold">productList经过cglib增强</span>)，再序列化Category会产生上述异常。</span>&nbsp;</li><li>直接通过getProductsByCatId获取Product List（<span style="font-weight: bold">同样经过cglib增强</span>），然后序列化该List并不会出现上述异常。</li></ol><p>因为对cglib还不是太熟悉，个人猜测原因在于：第一种情况是直接create了一个新的List实现，而这个实现没有实现<span style="font-weight: normal">Serializable接口；第二种情况是增强了ArrayList，而ArrayList实现了Serializable接口。</span> </p><p>目前的解决方法是，在序列化之前new 一个新的collection 自己做一次转换。</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><br /><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/179125#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>Thu, 03 Apr 2008 12:23:23 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/179125</link>
        <guid>http://andyao.javaeye.com/blog/179125</guid>
      </item>
      <item>
        <title>从LiveJournal后台发展看大规模网站性能优化方法</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/167035" style="color:red;">http://andyao.javaeye.com/blog/167035</a>&nbsp;
          发表时间: 2008年03月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div><address>作者: 于敦德 2006-3-16</address>
</div>
<div><address>原文:&nbsp; http://www.example.net.cn/archives/2006/03/olivejournaloio.html</address>
</div>
<h2>一、LiveJournal发展历程</h2>
<p>
<a href="http://www.livejournal.com/">
LiveJournal</a>
是99年始于校园中的项目，几个人出于爱好做了这样一个应用，以实现以下功能：
</p>
<ul>
<li>博客，论坛</li>
<li>社会性网络，找到朋友</li>
<li>聚合，把朋友的文章聚合在一起</li>
</ul>
<p>
LiveJournal采用了大量的开源软件，甚至它本身也是一个开源软件。
</p>
<p>在上线后，LiveJournal实现了非常快速的增长：</p>
<ul>
<li>2004年4月份：280万注册用户。</li>
<li>2005年4月份：680万注册用户。</li>
<li>2005年8月份：790万注册用户。</li>
<li>达到了每秒钟上千次的页面请求及处理。</li>
<li>使用了大量MySQL服务器。</li>
<li>使用了大量通用组件。</li>
</ul>
<h2>二、LiveJournal架构现状概况</h2>
<p><img src="http://www.example.net.cn/archives/livejournal_backend.png" height="445" alt="livejournal_backend.png" width="600" />
<br /></p>
<h2>三、从LiveJournal发展中学习</h2>
<p>LiveJournal从1台服务器发展到100台服务器，这其中经历了无数的伤痛，但同时也摸索出了解决这些问题的方法，通过对LiveJournal的学习，可以让我们避免LJ曾经犯过的错误，并且从一开始就对系统进行良好的设计，以避免后期的痛苦。</p>
<p>下面我们一步一步看LJ发展的脚步。</p>
<h2>1、一台服务器</h2>
<p>一台别人捐助的服务器，LJ最初就跑在上面，就像Google开始时候用的破服务器一样，值得我们尊敬。这个阶段，LJ的人以惊人的速度熟悉的
Unix的操作管理，服务器性能出现过问题，不过还好，可以通过一些小修小改应付过去。在这个阶段里LJ把CGI升级到了FastCGI。</p>
<p>最终问题出现了，网站越来越慢，已经无法通过优过化来解决的地步，需要更多的服务器，这时LJ开始提供付费服务，可能是想通过这些钱来购买新的服务器，以解决当时的困境。<br />
毫无疑问，当时LJ存在巨大的单点问题，所有的东西都在那台服务器的铁皮盒子里装着。</p>
<p> <img src="http://www.example.net.cn/archives/LJ-backend-7.png" height="187" alt="LJ-backend-7.png" width="500" />
</p>
<h2>2、两台服务器</h2>
<p>用付费服务赚来的钱LJ买了两台服务器：一台叫做Kenny的Dell 6U机器用于提供Web服务，一台叫做Cartman的Dell 6U服务器用于提供数据库服务。</p>
<p> <img src="http://www.example.net.cn/archives/LJ-backend-8.png" height="279" alt="LJ-backend-8.png" width="218" />
</p>
<p>LJ有了更大的磁盘，更多的计算资源。但同时网络结构还是非常简单，每台机器两块网卡，Cartman通过内网为Kenny提供MySQL数据库服务。<br />
	<br />
暂时解决了负载的问题，新的问题又出现了：</p>
<ul>
<li>原来的一个单点变成了两个单点。</li>
<li>没有冷备份或热备份。</li>
<li>网站速度慢的问题又开始出现了，没办法，增长太快了。</li>
<li>Web服务器上CPU达到上限，需要更多的Web服务器。</li>
</ul>
<h2>3、四台服务器</h2>
<p>又买了两台，Kyle和Stan，这次都是1U的，都用于提供Web服务。目前LJ一共有3台Web服务器和一台数据库服务器。这时需要在3台Web服务器上进行负载均横。</p>
<p> <img src="http://www.example.net.cn/archives/LJ-backend-9.png" height="241" alt="LJ-backend-9.png" width="499" />
</p>
<p>LJ把Kenny用于外部的网关，使用mod_backhand进行负载均横。</p>
<p>然后问题又出现了：</p>
<ul>
<li>单点故障。数据库和用于做网关的Web服务器都是单点，一旦任何一台机器出现问题将导致所有服务不可用。虽然用于做网关的Web服务器可以通过保持心跳同步迅速切换，但还是无法解决数据库的单点，LJ当时也没做这个。</li>
<li>网站又变慢了，这次是因为IO和数据库的问题，问题是怎么往应用里面添加数据库呢？</li>
</ul>
<h2>4、五台服务器</h2>
<p>又买了一台数据库服务器。在两台数据库服务器上使用了数据库同步(Mysql支持的Master-Slave模式)，写操作全部针对主数据库（通过Binlog，主服务器上的写操作可以迅速同步到从服务器上），读操作在两个数据库上同时进行(也算是负载均横的一种吧)。</p>
<p> <img src="http://www.example.net.cn/archives/LJ-backend-10.png" height="265" alt="LJ-backend-10.png" width="500" />
</p>
<p>实现同步时要注意几个事项：</p>
<ul>
<li>读操作数据库选择算法处理，要选一个当前负载轻一点的数据库。</li>
<li>在从数据库服务器上只能进行读操作</li>
<li>准备好应对同步过程中的延迟，处理不好可能会导致数据库同步的中断。只需要对写操作进行判断即可，读操作不存在同步问题。</li>
</ul>
<h2>5、更多服务器</h2>
<p>有钱了，当然要多买些服务器。部署后快了没多久，又开始慢了。这次有更多的Web服务器，更多的数据库服务器，存在 IO与CPU争用。于是采用了BIG-IP作为负载均衡解决方案。</p>
<p> <img src="http://www.example.net.cn/archives/LJ-backend-11.png" height="483" alt="LJ-backend-11.png" width="453" />
</p>
<h2>6、现在我们在哪里：</h2>
<p><img src="http://www.example.net.cn/archives/LJ-backend-1.png" height="357" alt="LJ-backend-1.png" width="600" />
</p>
<p>现在服务器基本上够了，但性能还是有问题，原因出在架构上。</p>
<p>数据库的架构是最大的问题。由于增加的数据库都是以Slave模式添加到应用内，这样唯一的好处就是将读操作分布到了多台机器，但这样带来的后果就是写操作被大量分发，每台机器都要执行，服务器越多，浪费就越大，随着写操作的增加，用于服务读操作的资源越来越少。</p>
<p>  <img src="http://www.example.net.cn/archives/LJ-backend-2.png" height="195" alt="LJ-backend-2.png" width="500" />
</p>
<p>由一台分布到两台</p>
<p>  <img src="http://www.example.net.cn/archives/LJ-backend-3.png" height="273" alt="LJ-backend-3.png" width="500" />
</p>
<p>最终效果</p>
<p>现在我们发现，我们并不需要把这些数据在如此多的服务器上都保留一份。服务器上已经做了RAID，数据库也进行了备份，这么多的备份完全是对资源的浪费，属于冗余极端过度。那为什么不把数据分布存储呢？</p>
<p>问题发现了，开始考虑如何解决。现在要做的就是把不同用户的数据分布到不同的服务器上进行存储，以实现数据的分布式存储，让每台机器只为相对固定的用户服务，以实现平行的架构和良好的可扩展性。</p>
<p>为了实现用户分组，我们需要为每一个用户分配一个组标记，用于标记此用户的数据存放在哪一组数据库服务器中。每组数据库由一个master及几个
slave组成，并且slave的数量在2-3台，以实现系统资源的最合理分配，既保证数据读操作分布，又避免数据过度冗余以及同步操作对系统资源的过度
消耗。</p>
<p><img src="http://www.example.net.cn/archives/LJ-backend-4.png" height="324" alt="LJ-backend-4.png" width="500" />
</p>
<p>由一台（一组）中心服务器提供用户分组控制。所有用户的分组信息都存储在这台机器上，所有针对用户的操作需要先查询这台机器得到用户的组号，然后再到相应的数据库组中获取数据。</p>
<p>这样的用户架构与目前LJ的架构已经很相像了。</p>
<p>在具体的实现时需要注意几个问题：</p>
<ul>
<li>在数据库组内不要使用自增ID，以便于以后在数据库组之间迁移用户，以实现更合理的I/O，磁盘空间及负载分布。</li>
<li>将userid，postid存储在全局服务器上，可以使用自增，数据库组中的相应值必须以全局服务器上的值为准。全局服务器上使用事务型数据库InnoDB。</li>
<li>在数据库组之间迁移用户时要万分小心，当迁移时用户不能有写操作。</li>
</ul>
<h2>7、现在我们在哪里</h2>
<p><img src="http://www.example.net.cn/archives/LJ-backend-5.png" height="365" alt="LJ-backend-5.png" width="500" />
</p>
<p>问题：</p>
<ul>
<li>一个全局主服务器，挂掉的话所有用户注册及写操作就挂掉。</li>
<li>每个数据库组一个主服务器，挂掉的话这组用户的写操作就挂掉。</li>
<li>数据库组从服务器挂掉的话会导致其它服务器负载过大。</li>
</ul>
<p>对于Master-Slave模式的单点问题，LJ采取了Master-Master模式来解决。所谓Master-Master实际上是人工实现的，并不是由MySQL直接提供的，实际上也就是两台机器同时是Master，也同时是Slave，互相同步。</p>
<p>Master-Master实现时需要注意：</p>
<ul>
<li>一个Master出错后恢复同步，最好由服务器自动完成。</li>
<li>数字分配，由于同时在两台机器上写，有些ID可能会冲突。</li>
</ul>
<p>解决方案：</p>
<ul>
<li>奇偶数分配ID，一台机器上写奇数，一台机器上写偶数</li>
<li>通过全局服务器进行分配(LJ采用的做法)。</li>
</ul>
<p>Master-Master模式还有一种用法，这种方法与前一种相比，仍然保持两台机器的同步，但只有一台机器提供服务（读和写），在每天晚上的时候进行轮换，或者出现问题的时候进行切换。</p>
<h2>8、现在我们在哪里</h2>
<p><img src="http://www.example.net.cn/archives/LJ-backend-6.png" height="349" alt="LJ-backend-6.png" width="500" />
</p>
<p>现在插播一条广告，MyISAM VS InnoDB。</p>
<p>使用InnoDB：</p>
<ul>
<li>支持事务</li>
<li>需要做更多的配置，不过值得，可以更安全的存储数据，以及得到更快的速度。</li>
</ul>
<p>使用MyISAM：</p>
<ul>
<li>记录日志（LJ用它来记网络访问日志）</li>
<li>存储只读静态数据，足够快。</li>
<li>并发性很差，无法同时读写数据（添加数据可以）</li>
<li>MySQL非正常关闭或死机时会导致索引错误，需要使用myisamchk修复，而且当访问量大时出现非常频繁。</li>
</ul>
<h2>9、缓存</h2>
<p>去年我写过<a href="http://www.example.net.cn/archives/2006/01/eoamemcachedoea.html">一篇文章介绍memcached</a>
，它就是由LJ的团队开发的一款缓存工具，以key-value的方式将数据存储到分布的内存中。LJ缓存的数据：</p>
<ul>
<li>12台独立服务器（不是捐赠的）</li>
<li>28个实例</li>
<li>30GB总容量</li>
<li>90-93%的命中率（用过squid的人可能知道，squid内存加磁盘的命中率大概在70-80%）</li>
</ul>
<p>如何建立缓存策略？</p>
<p>想缓存所有的东西？那是不可能的，我们只需要缓存已经或者可能导致系统瓶颈的地方，最大程度的提交系统运行效率。通过对MySQL的日志的分析我们可以找到缓存的对象。</p>
<p>缓存的缺点？</p>
<ul>
<li>没有完美的事物，缓存也有缺点：</li>
<li>增大开发量，需要针对缓存处理编写特殊的代码。</li>
<li>管理难度增加，需要更多人参与系统维护。</li>
<li>当然大内存也需要钱。</li>
</ul>
<h2>10、Web访问负载均衡</h2>
<p>在数据包级别使用BIG-IP，但BIG-IP并不知道我们内部的处理机制，无法判断由哪台服务器对这些请求进行处理。反向代理并不能很好的起到作用，不是已经够快了，就是达不到我们想要的效果。</p>
<p>所以，LJ又开发了<a href="http://www.danga.com/perlbal/">Perlbal</a>
。特点：</p>
<ul>
<li>快，小，可管理的http web 服务器/代理</li>
<li>可以在内部进行转发</li>
<li>使用Perl开发</li>
<li>单线程，异步，基于事件，使用epoll , kqueue</li>
<li>支持Console管理与http远程管理，支持动态配置加载</li>
<li>多种模式：web服务器，反向代理，插件</li>
<li>支持插件：GIF/PNG互换？</li>
</ul>
<h2>11、MogileFS</h2>
<p>LJ使用开源的<a href="http://www.danga.com/mogilefs/">MogileFS</a>
作为分布式文件存储系统。MogileFS使用非常简单，它的主要设计思想是：</p>
<ul>
<li>文件属于类（类是最小的复制单位）</li>
<li>跟踪文件存储位置</li>
<li>在不同主机上存储</li>
<li>使用MySQL集群统一存储分布信息</li>
<li>大容易廉价磁盘</li>
</ul>
<p>到目前为止就这么多了，更多文档可以在<a href="http://www.danga.com/words/">http://www.danga.com/words/</a>
找到。<a href="http://www.danga.com/">Danga.com</a>
和<a href="http://www.livejournal.com/">LiveJournal.com</a>
的
同学们拿这个文档参加了两次MySQL Con，两次OS
Con，以及众多的其它会议，无私的把他们的经验分享出来，值得我们学习。在web2.0时代快速开发得到大家越来越多的重视，但良好的设计仍是每一个应
用的基础，希望web2.0们在成长为Top500网站的路上，不要因为架构阻碍了网站的发展。</p>
<p>参考资料：<a href="http://www.danga.com/words/2005_oscon/oscon-2005.pdf">http://www.danga.com/words/2005_oscon/oscon-2005.pdf</a>
</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/167035#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 Mar 2008 17:59:35 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/167035</link>
        <guid>http://andyao.javaeye.com/blog/167035</guid>
      </item>
      <item>
        <title>Pointcut命名有可能导致错误</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/165944" style="color:red;">http://andyao.javaeye.com/blog/165944</a>&nbsp;
          发表时间: 2008年02月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          使用Spring @AspectJ方式的AOP，代码<br /><pre name="code" class="java">@Aspect
public class AndyaoAspect {

  @Pointcut(&quot;execution(* com.andyao.Repository.get(java.lang.Integer))&amp;&amp; args(id)&quot;)
  public void get(Integer id){
  }
  
  @Around(&quot;get(id)&quot;)
  public Object fromCache(ProceedingJoinPoint pjp, Integer id) throws Throwable {
    ...
  }

}</pre><p>这段代码看上去没有什么问题，但是真正执行中，会抛出exception</p>
<pre name="code" class="java">@Around(&quot;get(id)&quot;)
java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting 'name pattern' at character position 6</pre>
<p>&nbsp;翻阅文档发现问题所在：</p>
<div class="quote_title"> 写道</div>
<div class="quote_div">其他的切入点类型<br /><br />完整的AspectJ切入点语言支持额外的切入点指定者，但是
Spring不支持这个功能。 他们分别是call, initialization, preinitialization,
staticinitialization, get, set, handler, adviceexecution, withincode,
cflow, cflowbelow, if, @this 和 @withincode。 在Spring
AOP中使用这些指定者将会导致抛出IllegalArgumentException异常。 <br /><br />Spring AOP支持的切入点指定者可能在将来的版本中得到扩展，不但支持更多的AspectJ 切入点指定者（例如&quot;if&quot;），还会支持某些Spring特有的切入点指定者，比如&quot;bean&quot;（用于匹配bean的名字）。 <br /></div>
&nbsp;所以在使用spring AOP的时候不能以上面的诸如call，get等命名，不然就会报异常。
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/165944#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>Thu, 28 Feb 2008 19:16:01 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/165944</link>
        <guid>http://andyao.javaeye.com/blog/165944</guid>
      </item>
      <item>
        <title>异常通知：面向方面的模型</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/165758" style="color:red;">http://andyao.javaeye.com/blog/165758</a>&nbsp;
          发表时间: 2008年02月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <address>原文：http://dev2dev.bea.com.cn/techdoc/2007/10/exception-advice.html</address><address>

</address><address>
作者：<a href="http://dev2dev.bea.com.cn/author/364723.html">Barry Ruzek</a> <br /></address><h3>摘要</h3>
<p>　　有效的异常处理策略是一大架构关注点，它超越了独立应用程序组件的边界。<a href="http://dev2dev.bea.com.cn/techdoc/200702364792.html">有效的Java异常</a>（Dev2Dev
中文版，2007年2月）概述了错误-意外事件（Fault-Contingency）异常模型，消除了在Java应用程序中使用已检查还是未检查异常的
迷惑。使用传统Java技术实现这种模型要求所有组件都遵循一组规则和行为。这也就暗中表明原本无关的组件间的耦合需要为意料之外的失误和故障留出空间。
在错误-意外事件异常模型中应用面向方面的技术需要首先处理这一关注点，并允许其他组件专心于其主要工作。</p><p>　　本文解释了为什么基于错误-
意外事件异常模型的异常处理方面是对传统实现的重大改进。还提供了使用AspectJ（Java的一种面向方面的扩展）创建的异常处理方面的完整示例，来
展示这种概念。文中提供的代码可在BEA WebLogic 9.2 和 Tomcat 5.0 应用服务器上运行。</p>
<h3>AOP 与架构</h3>
<p>　　作为一名应用程序架构师，您的主要职责是制订决策来监管组件之间的关系。架构决策会影响到组件的设计方法、组件用来协作的模式以及所遵循的惯
例。如果决策合理、沟通充分，并得到了项目团队的遵从，那么就会得到一个易于理解、维护和扩展的软件系统。每个人都希望得到这样的结果，但实现起来却极具
挑战。架构是跨组件的，它要求组件执行某些操作，或者避免特定的行为，以使一切在一个总体愿景下协调工作。</p><p>　　开发团队是由人构成的，而人
都是不完美的。即便最出色的开发团队也会在维护架构愿景的纯净方面遇到麻烦。团队可以利用两种传统的对策来避免架构违规。第一种对策是规定对设计和编码进
行定期审查。第二种是构建框架。审查的目标是在问题刚刚出现时发现问题。而框架提供了一种可重用的基础架构，其约束的目标是从一开始就预防问题，避免问题
出现。</p><p>　　面向方面的设计是应对架构关注点的第三种选择。它不是将架构行为分散到所有无关的组件，而是将行为封装在一个方面中，并在特定执
行点应用。面向方面编程（AOP）方面的工作自20世纪90年代就已启动，但可以公正地说，AOP的广泛采用依然有待时日。或许造成这种情况的原因之一就
是缺乏令人鼓舞的典范，表明这种技术能够带来怎样的收益。一个引人注目的AOP实例应具有如下特点：</p>
<ul><li>有价值&mdash;&mdash;解决公认的问题</li><li>没有AOP就难以解决</li><li>使用AOP可轻松解决</li></ul>
<p>　　跟踪方法执行的常用示例是展示方面功能的好办法，但并不那么令人鼓舞&mdash;&mdash;或许应该说，鼓舞的程度对于大多数人来说还不够，不足以投入人力物力去
学习这种技术，不足以成为在下一个项目中使用AOP的理由。其实存在更好的例子，但您需要详查所有记录了方法的例子，来找到所需的那些。</p><p>　
　在许多软件项目中，Java应用程序中的异常处理就是一个公认的关注点。管理不善的异常规程将导致难以理解和维护的易出错代码。一致的异常处理方法对于
多数应用程序来说都是一项重大收益。即便在团队采用了异常的架构模型时，确保每个模型都符合模型也需要不懈的努力和深入的洞察力。看上去异常处理模型似乎
是探索AOP的不错方法。这是否能成为一个鼓舞人心的例子，一切由您决定。</p>
<h3>错误-意外事件异常模型</h3>
<p>　　异常处理方面从您希望应用到整个应用程序中的一个模型或者一组行为开始。错误-意外事件异常模型提供了一种切实可行的方式来考虑执行软件时所遇
到异常。该模型将不规则的输出描述为意外事件（Contingency）或错误（Fault）。意外事件是一种可选输出，能够使用组件目标用途的词汇表加
以描述。方法或构造方法的调用方具有处理其意外事件输出的战略。另一方面，错误是无法在语义契约方面进行描述的一种故障，仅可就实现细节进行描述。举例来
说，考虑一个Account Service，它带有一个getAccount()方法，在为此方法提供一个Account
ID后，它将返回Account对象。很容易就能设想出可能出现的意外事件，例如&ldquo;No such account&rdquo;或&ldquo;Invalid
Account ID&rdquo;，是就方法的目标用途表述的。要预计可能出现的错误，您首先需要了解getAccount()
方法是怎样实现的。它是否因未连接到数据库而接收到了一个SQLException？或许存在超时，正在等待一个宕机维护的Web服务。或许一个丢失的文
件（本应存在）导致了FileNotFoundException。此处的要点在于getAccount()
的调用方不应对实现有任何了解，也不应被迫为预计到的任何错误捕捉已检查异常。</p><p>　　错误-意外事件异常模型的一个简单Java实现具有三个
基本概念：意外事件异常、错误异常、错误屏障。方法和构造方法使用意外事件异常来通知作为其契约一部分的可选输出。意外事件异常是已检查的异常，因此编译
器将帮助确保调用方考虑到所有约定的输出。错误异常用于通知特定于实现的故障。错误异常是未检查的异常，运行中的代码通常会避免捕捉这些异常，将这一责任
留给作为错误屏障的类处理。错误屏障的主要责任就是在获得错误异常时为正在处理的活动提供一个出色的出口。这种出色的出口通常包含对正在处理的故障的表
示，例如在用户界面（如果有用户界面）上显示一条道歉信息，或者通过其他方式向&ldquo;外部世界&rdquo;指出故障。</p><p>　　传统的实现使用
RuntimeException的一个子类（比如FaultExceptio）来表示错误异常。作为一个未检查的异常，FaultException可
在未在方法和构造方法的签名中被显式捕获或声明的情况下抛出。因此，这种异常完全可能在未被错误屏障捕获或处理时处于未发现的状态。意外事件异常基于
Exception的一个子类（比如ContingencyException），该子类使Java编译器可以检查此类异常。由于
ContingencyException是语义契约的完整组成部分，因此可以借助编译器来保证调用方具备处理此类异常的战略。</p><p>　　模型中
的组件需要遵循一组使一切正常工作的规范。首先，组件不能抛出FaultException 或
ContingencyException子类以外的异常。其次，组件必须避免捕获FaultException，将这一责任留给错误屏障。组件负责处理
它们所调用的外部方法抛出的异常，并在必要时将其转换为FaultException或ContingencyException。任何未捕获的
RuntimeException都被视为错误，需要错误屏障的关注。这些规则非常简单，有效地消除了应用程序代码中混乱、令人迷惑的异常序列。通过清晰
地将错误划分出来，由错误屏障负责处理，使用低级代码处理错误条件的诱惑得到了极大的消减。错误不再干预应由意外事件异常处理的情况，意外事件异常的目的
显然是在组件间传输有意义的信息。</p>
<h3>传统实现的不足之处</h3>
<p>　　错误-意外事件异常模型的传统实现是对临时异常处理的极大改进，但离理想还相去甚远。所有的组件都必须遵循规范，即便这些组件之间再无其他关
联。确保它们确实遵循了规范的惟一方法就是审查代码。可能有一个组件无意中捕获了错误异常，使之无法传递给错误屏障。如果发生这种情况，您可以顺利从那个
出色的出口退出并离开，但没有任何办法去诊断所发生的错误。</p><p>　　传统实现给错误屏障设定了两方面的责任。其固有的责任就是完美地终止处理序
列。由于其位置靠近调用堆栈的顶端，因此错误屏障了解周围环境，了解哪些内容能够构成恰当的输出响应。另外一种责任是记录与错误相关的分析信息，以使人们
了解发生了什么。它具有这种责任的惟一原因就是没有其他合适的位置来完成这个任务。如果系统需要多个错误屏障（有些系统确实需要），那么每个错误屏障都必
须包含类似的逻辑，来捕获可用信息。</p><p>　　修正一个问题的能力取决于可用信息的质量。实际上，传统实现能够提供的信息仅限于
RuntimeException能够提供的那些：堆栈跟踪和错误消息。每一名Java程序员都会乐于在没有任何与实际发生情况有关的线索的前提下，启动
一次堆栈跟踪。堆栈跟踪将显示发生了什么、在哪里发生，但不会显示为什么发生这样的情况。理想情况下，您希望了解哪些方法被调用，以及它们是怎样被调用的
&mdash;&mdash;传入各方法且导致错误的参数类型和值。将代码分散到每一个方法之中并在输入时记录其参数这种方法令人不满、不切实际、易于出错，如果未实际出现任何错
误，那么所做的一切都是白费功夫。</p>
<h3>方面、切入点和通知</h3>
<p>　　方面编程正是为解决此类问题而出现的。在我们的例子中，应用程序内的所有组件都必须关注错误和意外事件的规则。如果一个类中出现失误，会波及众
多不相关的类，导致较大的异常模型出现故障。同样，我们可以使用错误屏障来完成记录分析信息的任务，尽管其自身的角色只是了解如何为外部世界生成一般响应
并执行清除操作。</p><p>　　AOP的理念是将所需行为封装在一个实体中：方面。一个方面包含在应用程序中某些定义好的点上运行的逻辑。所运行的这
种逻辑就称为通知。应用通知的点称为连接点。可通过定义切入点来指定一组应用通知的连接点。切入点基本上就是一个表达式，过滤应用程序中所有潜在连接点，
并根据标准（如接入点的类型和各种类型的模式匹配）来选择部分连接点。如果恰当地制作了方面，它会执行一些操作，若不使用方面，这些操作将分散在应用程序
之中。将一切都集中到一处之后，应用程序中的其他组件即可集中关注其主要任务。最终得到更出色的组件内聚，这是人人都希望得到的结果。</p><p>　　
示例应用程序包含我们的异常处理方面，它是使用Java语言的一个超集AspectJ构建的。这种语言支持对于任何应用程序中的异常处理都非常重要的连接
点多样性。在Java应用程序的执行过程中，可以通过多种方式生成并捕获异常。方法执行只是其中的一种方式。异常处理方面需要考虑构造方法的执行、对象初
始化和类初始化，这些都可能导致异常。此外还要考虑显式抛出和捕获异常的位置。AspectJ的切入点语言支持实现理想模型所需的一切。</p>
<h3>ExceptionHandling方面</h3>
<p>　　ExceptionHandling方面在设计时就考虑到了最大化灵活性，因此它只有两个编译时依赖项。它们是表示错误和意外事件的两个Java类：</p>
<ul><li>FaultException：表示错误条件的RuntimeException类的一个子类。</li><li>ContingencyException：表示意外事件输出的Exception类的一个子类。子类表示具体的意外事件条件。</li><li>方
面假设（并强制）应用程序的其他部分按照模型规则使用这些类。AspectJ系统的绝妙特性之一就是能够&ldquo;为编译器编程&rdquo;，从而实施超越标准Java语言
规则的策略。在我们的例子中，我们希望鼓励开发人员以错误和意外事件为依据进行思考，并清楚地加以区分。由于我们的架构提供了一个
ContingencyException基类，我们希望确保开发人员仅使用该类的子类来表示意外事件条件。通过这种做法，就能够避免开发人员尝试声明一
个抛出（比如说）SQLException的方法，此方法应将任何意料之外的JDBC问题都视为错误。</li><li>ExceptionHandling方面使用切入点来检测声明了已检查异常而非基于ContingencyException的异常的方法和构造方法。违规将作为编译错误标出，这种方式能确保相关人员注意到问题。</li></ul>
<pre class="code">package exception;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Stack;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.CodeSignature;
public abstract aspect ExceptionHandling {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
&nbsp;&nbsp;&nbsp; pointcut methodViolation():
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; execution(* *(..) throws (Exception+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; !ContingencyException+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; !RuntimeException+));
&nbsp;&nbsp;&nbsp; declare error:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; methodViolation():
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Method throws a checked exception that is not 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a ContingencyException&quot;;
&nbsp;&nbsp;&nbsp; pointcut constructorViolation():
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; execution(*.new(..) throws (Exception+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; !ContingencyException+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; !RuntimeException+));
&nbsp;&nbsp;&nbsp; declare error:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constructorViolation():
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Constructor throws a checked exception that is not
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a ContingencyException&quot;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
}</pre>
<p>　　清单 1. 编译时异常策略实施</p><p>　　在下面的例子中，Transaction类的commit()方法声明其抛出
SQLException，而在这种环境中SQLException应视为错误，因而它违背了异常策略。编译器以ExceptionHandling方面
中的声明为依据，将这一违规作为编译错误标出。rollback()方法将意料之外的SQLException视为错误处理，符合此模型，因而不会出现任
何标记。为本文开发的示例是使用安装了AspectJ Development Tools (AJDT) 1.4.1插件的Eclipse
3.2.1开发的。</p><p>　　<a href="http://dev2dev.bea.com/images/2007/06/IDEScreenShot.JPG" target="_blank"><img src="http://dev2dev.bea.com.cn/images/20071030/image071030001.jpg" border="0" alt="Exception Policy Violation Flagged as Compile Error" /></a></p><p>　　图 1.异常策略违规将生成一个错误标记</p>
<h3>异常通知连接点</h3>
<p>　　ExceptionHandling方面使用exceptionAdvicePoints()切入点来为任何能够抛出异常的执行序列应用通知。这个方面多次使用此切入点，在可能抛出异常时注入处理。切入点包含如下连接点：</p>
<ul><li>所有方法执行</li><li>所有构造方法执行</li><li>所有对象初始化</li><li>所有对象预初始化</li><li>所有类初始化</li></ul>
<p>　　由于ExceptionHandling方面具有自己的方法、自己的构造方法，也要经历类和对象的初始化过程，上述连接点中有一些就处于这个方
面之中。通常这不是什么好事，会在方面尝试发布自己的通知时引起递归循环。为避免这样的可能性，exceptionAdvicePoints()切入点明
确地在上述连接点中排除了部分连接点：</p>
<ul><li>方面自身的词法作用域（lexical scope）内的任何连接点</li><li>其子方面的词法作用域内的任何连接点</li><li>通知执行控制流内的任何连接点</li></ul>
<pre class="code">public abstract aspect ExceptionHandling {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
&nbsp;&nbsp;&nbsp; pointcut exceptionAdvicePoints():
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (execution (* *.*(..))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; || execution (*.new(..))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; || initialization(*.new(..))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; || preinitialization(*.new(..))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; || staticinitialization(*))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; !within(ExceptionHandling+)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; !cflow(adviceexecution());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
}</pre>

<p>　　清单 2. 异常通知点</p><p>　　现在，ExceptionHandling方面能够为自身以外的所有应用程序组件应用与异常相关的通知。不会尝试为那些可能是作为执行其自身通知的结果运行的方法应用通知。</p>
<h3>运行时异常转换</h3>
<p>　　错误-意外事件模型的惯例表明，未被捕获的Throwable应视为错误条件。在传统实现中，完全不能保证此类异常在较低级别被捕获并转换，因
此错误屏障必须随时准备捕捉Throwable，而不仅仅是FaultException。在AOP实现中，我们可以更好地完成这个任务。可以确保从任一
组件抛出且未被捕获的Throwable都会自动转换成FaultException。这使错误屏障的实现更为简单，也确保了
ExceptionHandling方法内的任何其他通知都会将未被捕获的Throwable视为错误处理。</p><p>　　如果一个执行序列抛出了任
何类型的Throwable，&ldquo;抛出后&rdquo;通知将运行。如果异常是FaultException或ContingencyException，通知不会采取
任何措施。否则，通知会将违规的异常替换为FaultException的一个新实例，将未被捕获的异常作为诱因。请注意，方面的编译时异常策略实施简化
了通知必须进行的检查。</p>
<pre class="code">public abstract aspect ExceptionHandling {
&nbsp; ...
&nbsp; after() throwing(Throwable throwable):exceptionAdvicePoints(){
&nbsp;&nbsp;&nbsp; if (!(throwable instanceof FaultException || 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throwable instanceof ContingencyException)) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new FaultException(&quot;Unhandled exception: &quot;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throwable);
&nbsp;&nbsp;&nbsp; }
&nbsp; }
&nbsp; ...
}</pre>
<p>　　清单 3.运行时异常转换</p>
<h3>到达错误屏障</h3>
<p>　　ExceptionHandling
方面确保FaultException总是会到达错误屏障，无论中间参与的代码行为如何。这保证了错误屏障总是以一种有序的方式终止处理序列。它使中间参
与的代码免于担忧意外捕获FaultException。此通知使这些异常更加棘手，错误屏障以外的任何处理程序都无法捕获它们。allHandlers
()切入点应用到应用程序中的所有异常处理程序，并使包含处理程序和所处理异常的类对before()
通知逻辑可用。通知会在异常处理程序内代码执行前执行。除非异常是一个FaultException，否则通知不会采取任何措施。对于
FaultException，通知会检查处理程序是否位于指定错误屏障的类中。如果是，则允许该处理程序捕获FaultException。如果不是，
则再次抛出FaultException，忽略将捕获它的处理程序。最终，FaultException将到达指定错误屏障类中的一个处理程序处。</p>
<pre class="code">public abstract aspect ExceptionHandling {
&nbsp; ...
&nbsp; pointcut allHandlers(Object handlerType, Throwable throwable):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; handler(Throwable+) &amp;&amp; args(throwable) &amp;&amp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this(handlerType);

&nbsp; before(Object handler, Throwable throwable):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; allHandlers(handler, throwable) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (throwable instanceof FaultException) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!(isFaultBarrier(handler))) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FaultException fault = (FaultException) throwable;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw (fault);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp; }
&nbsp; abstract boolean isFaultBarrier(Object exceptionHandler);
&nbsp; ...
}</pre>
<p>　　清单 4. 仅错误屏障捕获错误</p><p>　　方面要如何知道一个处理程序是否位于指定错误屏障内呢？一种方法就是将指定错误屏障的类名称
硬编码到ExceptionHandling方面当中。但那会将方面与特定应用程序绑定在一起。为了使ExceptionHandling
方面尽可能地灵活，它声明了一个抽象方法，回答错误屏障的问题。isFaultBarrier()的实现是在了解应用程序细节且能判断一个处理程序对象是
否为错误屏障的子方面中提供的。这也就是说，ExceptionHandling必须声明为抽象方面。它必须被具体子方面扩展，之后其通知才能被激活。子
方面仅需要应用isFaultBarrier()的一个实现，加上另外一个方法，下面将讨论这个方法。</p>
<h3>更好的错误诊断</h3>
<p>　　上面介绍的ExceptionHandling方面确保了应用程序抛出且未被捕获的所有异常都会作为FaultException到达错误屏
障。这适用于来自Java库方法的意外异常、应用程序代码的bug导致的意外异常，以及在错误条件未被发现时显式抛出的FaultExceptions。
错误屏障仅需捕获FaultException，而非传统实现需要捕获的Throwable。可以通过任何看似自然的方式构造应用程序代码，无需考虑对应
用程序的错误处理能力造成的影响。</p><p>　　这是面向方面方法的一大优势。而ExceptionHandling方面实际证明了在错误发生时它能
够提供的诊断信息的质量极具价值。一个方面能够在应用程序运行时观测整个应用程序。ExceptionHandling
方面利用这种能力跟踪传递给应用程序中各方法和构造方法的参数。出现错误时，该方面为所记录的标准异常和堆栈跟踪信息附加一个特殊的应用程序跟踪
（Application
Trace）部分。应用程序跟踪中的每个条目都描述了处理的类型；类、方法或构造方法的名称；用于调用它的参数名称、类型和值。结果如下所示：</p>
<pre class="code">FATAL : exception.ServiceExceptionHandling - Application Fault Detected

exception.FaultException: Unexpected failure on catalog query: com.ibm.db2.jcc.b.SQLException: The string constant beginning with &quot;'&quot; does not have an ending string delimiter.

at domain.CatalogDAO.performQuery(CatalogDAO.java:86)

at domain.CatalogDAO.getCatalogEntries(CatalogDAO.java:57)

at domain.CatalogService.getCatalogEntry(CatalogService.java:16)

at domain.CartItem.&lt;init&gt;(CartItem.java:22)

at domain.ShoppingCart.addToCart(ShoppingCart.java:28)

at action.SelectItemAction.performAction(SelectItemAction.java:44)

at action.BaseAction.execute(BaseAction.java:57)

at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:484)

at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:274)

at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1482)

at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:525)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:763)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:856)

at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:225)

at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:127)

at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:283)

at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:175)

at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3214)

at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)

at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:121)

at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:1983)

at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:1890)

at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1344)

at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)

at weblogic.work.ExecuteThread.run(ExecuteThread.java:181)



Application Trace:

method-execution: domain.CatalogDAO.performQuery(query:java.lang.String=SELECT * FROM ADMINISTRATOR.CATALOG WHERE CATALOGID = 'BAD'INPUT', transaction:integration.Transaction=jdbc:db2://localhost:50000/DOCMGMT)

method-execution: domain.CatalogDAO.getCatalogEntries(catalogID:java.lang.String=BAD'INPUT, transaction:integration.Transaction=jdbc:db2://localhost:50000/DOCMGMT)

method-execution: domain.CatalogService.getCatalogEntry(catalogID:java.lang.String=BAD'INPUT)

constructor-execution: domain.CartItem.&lt;init&gt;(catalogID:java.lang.String=BAD'INPUT, quantity:int=1)

initialization: domain.CartItem.&lt;init&gt;(catalogID:java.lang.String=BAD'INPUT, quantity:int=1)

method-execution: domain.ShoppingCart.addToCart(catalogID:java.lang.String=BAD'INPUT, quantity:int=1)

method-execution: action.SelectItemAction.performAction(cart:domain.ShoppingCart=Cart0024988, action:org.apache.struts.action.ActionMapping=ActionConfig[path=/SelectItem,name=SelectItemForm,scope=session,type=action.SelectItemAction, form:org.apache.struts.action.ActionForm=CatalogID-BAD'INPUT Quantity-1, request:javax.servlet.http.HttpServletRequest=Http Request: /ShoppingServices/SelectItem.do, response:javax.servlet.http.HttpServletResponse=weblogic.servlet.internal.ServletResponseImpl@22a6c2)

method-execution: action.BaseAction.execute(action:org.apache.struts.action.ActionMapping=ActionConfig[path=/SelectItem,name=SelectItemForm,scope=session,type=action.SelectItemAction, form:org.apache.struts.action.ActionForm=CatalogID-BAD'INPUT Quantity-1, request:javax.servlet.http.HttpServletRequest=Http Request: /ShoppingServices/SelectItem.do, response:javax.servlet.http.HttpServletResponse=weblogic.servlet.internal.ServletResponseImpl@22a6c2)</pre>
<p>　　清单 5. 应用程序跟踪的结果</p><p>　　应用程序跟踪仅包含受ExceptionHandling方面影响的那些方法：作为应用程序
特定部分的方法。请注意，应用程序跟踪中的条目大致对应于堆栈跟踪顶端的项目。（堆栈跟踪的下端涵盖作为WebLogic
Server实现的具体部分的类。）这里给出的示例来自一个允许用户应用形参（BAD'INPUT）的Struts应用程序，该参数包含单个引号字符，从
而在SQL预计中导致语法错误。在诊断记录中显示参数值有助于确定错误在何处发生。这是ExceptionHandling方面中与错误记录相关的一段出
色代码。首先，观察一下方面是如何控制错误记录方式的。</p>
<pre class="code">public abstract aspect ExceptionHandling {
&nbsp; ...
&nbsp; private boolean FaultException.logged = false;
&nbsp; private boolean FaultException.isLogged() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.logged;
&nbsp; }

&nbsp; private void FaultException.setLogged() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.logged = true;
&nbsp; }
&nbsp; after() throwing(FaultException fault): exceptionAdvicePoints(){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!fault.isLogged()) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logFault(fault);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fault.setLogged();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp; }
&nbsp; ...
}</pre>
<p>　　清单 6. &nbsp;错误记录通知</p><p>　　只要应用程序的任何一点抛出FaultException，抛出后通知就会运行。它的任务是调用
方面的logFault()方法，此方法完成实际记录工作。单独一个错误在调用堆栈的所有方法上传播时可能会多次触发通知，因此通知需要找到一种方法，来
了解记录在何时完成。为此使用了另外一种AOP技术：成员引入（member
introduction）。方面将一个布尔标记引入FaultException
类型，附带一些用于访问的方法。这个标记和这些方法被合理地标为私有，仅在ExceptionHandling方面内可见。总体影响是：诊断记录在错误出
现时立即发生，而且不再重复。</p><p>　　错误可能在任何时候出现。要为可能出现的错误做好准备，ExceptionHandling
方面需要在应用程序运行的时候跟踪其活动。这样，如果出现错误，它就可以随时记录导致错误的调用序列及其参数值。为此，该方面维护了一个
JoinPoint对象引用的每线程堆栈。执行应用程序时，方面的跟踪堆栈随调用堆栈一起伸缩。AspectJ运行时利用语言构造
thisJoinPoint使JoinPoint对通知逻辑可用。JoinPoint
对象包含通知逻辑的动态上下文信息，使逻辑能够了解关于触发通知的环境的细节。</p>
<pre class="code">public abstract aspect ExceptionHandling {
&nbsp; ...
 
&nbsp;&nbsp;private static ThreadLocal&lt;Stack&lt;JoinPoint&gt;&gt; traceStack = 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new ThreadLocal&lt;Stack&lt;JoinPoint&gt;&gt;() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected Stack&lt;JoinPoint&gt; initialValue() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new Stack&lt;JoinPoint&gt;();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp; };
 
&nbsp;&nbsp;private static void pushJoinPoint(JoinPoint joinPoint) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; traceStack.get().push(joinPoint);
&nbsp; }
 
&nbsp;&nbsp;private static JoinPoint popJoinPoint() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Stack&lt;JoinPoint&gt; stack = traceStack.get();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (stack.empty()) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JoinPoint joinPoint = stack.pop();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return joinPoint;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp; }
 
&nbsp;&nbsp;private static JoinPoint[] getJoinPointTrace() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Stack&lt;JoinPoint&gt; stack = traceStack.get();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return stack.toArray(new JoinPoint[stack.size()]);
&nbsp; }
 
&nbsp;&nbsp;...
}</pre>
<p>　　清单 7. ThreadLocal 调用跟踪方法</p><p>　　有了这些方法之后，跟踪应用程序调用的通知就非常简单了。
exceptionAdvicePoints()切入点（可能因异常突然终止的任何执行序列）标识的连接点被推入堆栈。在序列开始之前，
JoinPoint对象被推入线程的跟踪堆栈。序列完成后，其JoinPoint对象从堆栈弹出。跟踪堆栈中的JoinPoint
对象永远不会被解除引用，除非出现错误。</p>
<pre class="code">public abstract aspect ExceptionHandling {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...

&nbsp;&nbsp;&nbsp; before(): exceptionAdvicePoints(){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pushJoinPoint(thisJoinPoint);
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; after(): exceptionAdvicePoints(){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; popJoinPoint();
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
}</pre>
<p>　　清单 8. 调用跟踪通知</p><p>　　发生错误时，将运行清单6中的通知，同时调用下面的方法来呈现诊断。来自堆栈跟踪的信息包含在
FaultException
中，这些信息来自方面自身的每线程连接点堆栈。formatJoinPoint()方法从各JoinPointobject对象中提取我们需要的信息：限
定的方法或构造方法名称、其形参的名称和类型、作为自变量传递给那些参数的值。</p>
<pre class="code">public abstract aspect ExceptionHandling {
&nbsp; ...
 
&nbsp;&nbsp;private void logFault(FaultException fault) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ByteArrayOutputStream traceInfo = 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new ByteArrayOutputStream();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PrintStream traceStream = new PrintStream(traceInfo);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fault.printStackTrace(traceStream);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer applicationTrace = new StringBuffer();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JoinPoint[] joinPoints = getJoinPointTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = joinPoints.length - 1; i &gt;= 0; i--) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; applicationTrace.append(&quot;\n\t&quot;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + formatJoinPoint(joinPoints[i]));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; recordFaultDiagnostics(&quot;Application Fault Detected&quot;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + &quot;\n&quot; + traceInfo.toString()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + &quot;\nApplication Trace:&quot;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + applicationTrace.toString());
&nbsp; }
 
&nbsp;&nbsp;abstract void recordFaultDiagnostics(String diagnostics);
 
&nbsp;&nbsp;private String formatJoinPoint(JoinPoint joinPoint) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CodeSignature signature = (CodeSignature) 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;joinPoint.getSignature();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] names = signature.getParameterNames();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class[] types = signature.getParameterTypes();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object[] args = joinPoint.getArgs();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer argumentList = new StringBuffer();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; args.length; i++) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (argumentList.length() != 0) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; argumentList.append(&quot;, &quot;);
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; argumentList.append(names[i]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; argumentList.append(&quot;:&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; argumentList.append(types[i].getName());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; argumentList.append(&quot;=&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; argumentList.append(args[i]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer format = new StringBuffer();
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format.append(joinPoint.getKind());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; format.append(&quot;: &quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; format.append(signature.getDeclaringTypeName());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; format.append(&quot;.&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; format.append(signature.getName());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; format.append(&quot;(&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; format.append(argumentList);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; format.append(&quot;)&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return format.toString();
&nbsp; }
 
}</pre>
<p>　　清单 9.错误记录方法</p><p>　　ExceptionHandling
方面定义了抽象方法recordFaultDiagnostics()，允许应用程序指定希望如何记录方面所产生的诊断信息。应用程序在具体子方面内提供
该方法的一个实现。这种安排使记录细节脱离基本方面，从而保证了方面的最大灵活性。</p><p>　　一个方面观测应用程序其他部分的能力使之能够在错误发生时提供全面的诊断。它能够在不了解其他应用程序组件或不与其协作的前提下完成这一任务。将实现诊断记录关注点的代码聚集在一处是面向方面方法的一大优势。</p>
<h3>结束语</h3>
<p>　　错误-意外事件异常模型对于许多Java应用程序都很有帮助。使用AOP技术实现该模型的关注点具有一些令人着迷的优势。在编译时检测模型偏差
的能力只是其中之一。将与错误处理相关的逻辑隔离起来是另外一种优势。以错误和意外事件为依据进行思考能够消除应用程序中众多令人迷惑的代码。而以方面为
已经进行思考则会使应用程序的代码更简单，减少代码中充满无心之错的机会。那么，在考虑采用AOP时，这是否足够令人鼓舞？只有您能决定。</p>
<h3>下载</h3>
<ul><li>下载本文所使用的 <a href="http://dev2dev.bea.com/2007/06/ExceptionHandling.zip">源代码</a></li></ul>
<h3>参考资料</h3>
<ul><li>Dev2Dev文章 <a href="http://dev2dev.bea.com.cn/techdoc/200702364792.html" target="_blank">有效的Java异常</a>（Dev2Dev中文版，2007年2月）提供了错误-意外事件异常模型的更详尽讨论。</li><li><a href="http://www.eclipse.org/aspectj/" target="_blank">Eclipse  AspectJ</a> 项目站点包含关于AspectJ的各个方面。</li><li><a href="http://www.eclipse.org/aspectj/doc/released/progguide/index.html" target="_blank">AspectJ  Programming Guide</a> 全面探讨了AspectJ语言的所有概念和结构。</li><li>Dev2Dev文章 <a href="http://dev2dev.bea.com.cn/techdoc/05101903.html" target="_blank">JRockit JVM对AOP的支持，第1部分</a>（Dev2Dev中文版，2005年10月）简要介绍了AOP和AspectJ。</li></ul>
<p>原文出处：<a href="http://dev2dev.bea.com/pub/a/2007/06/exception-advice.html" target="_blank">http://dev2dev.bea.com/pub/a/2007/06/exception-advice.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" width="0">&nbsp;</td>
<td><a href="http://dev2dev.bea.com/pub/au/3424" target="_blank">Barry Ruzek</a> 被Open Group组织授予Master Certified IT Architect的称号。他有30多年开发操作系统和企业应用程序的经验。</td></tr></tbody></table>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/165758#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>Thu, 28 Feb 2008 15:16:24 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/165758</link>
        <guid>http://andyao.javaeye.com/blog/165758</guid>
      </item>
      <item>
        <title>TCMalloc : Thread-Caching Malloc</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/162289" style="color:red;">http://andyao.javaeye.com/blog/162289</a>&nbsp;
          发表时间: 2008年02月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          
<p>原文<a href="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc.html" target="_blank">http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc.html</a></p>

<address>Sanjay Ghemawat</address>

<h2>Motivation</h2>

<p>TCMalloc is faster than the glibc 2.3 malloc (available as a
separate library called ptmalloc2) and other mallocs that I have
tested.  ptmalloc2 takes approximately 300 nanoseconds to execute a
malloc/free pair on a 2.8 GHz P4 (for small objects).  The TCMalloc
implementation takes approximately 50 nanoseconds for the same
operation pair.  Speed is important for a malloc implementation
because if malloc is not fast enough, application writers are inclined
to write their own custom free lists on top of malloc.  This can lead
to extra complexity, and more memory usage unless the application
writer is very careful to appropriately size the free lists and
scavenge idle objects out of the free list.</p>

<p>TCMalloc also reduces lock contention for multi-threaded programs.
For small objects, there is virtually zero contention.  For large
objects, TCMalloc tries to use fine grained and efficient spinlocks.
ptmalloc2 also reduces lock contention by using per-thread arenas but
there is a big problem with ptmalloc2's use of per-thread arenas.  In
ptmalloc2 memory can never move from one arena to another.  This can
lead to huge amounts of wasted space.  For example, in one Google
application, the first phase would allocate approximately 300MB of
memory for its URL canonicalization data structures.  When the first
phase finished, a second phase would be started in the same address
space.  If this second phase was assigned a different arena than the
one used by the first phase, this phase would not reuse any of the
memory left after the first phase and would add another 300MB to the
address space.  Similar memory blowup problems were also noticed in
other applications.</p>

<p>Another benefit of TCMalloc is space-efficient representation of
small objects.  For example, N 8-byte objects can be allocated while
using space approximately 8N * 1.01 bytes.  I.e., a
one-percent space overhead.  ptmalloc2 uses a four-byte header for
each object and (I think) rounds up the size to a multiple of 8 bytes
and ends up using 16N bytes.</p>


<h2>Usage</h2>

<p>To use TCMalloc, just link TCMalloc into your application via the
&quot;-ltcmalloc&quot; linker flag.</p>

<p>You can use TCMalloc in applications you didn't compile yourself,
by using LD_PRELOAD:</p>
<pre>   $ LD_PRELOAD=&quot;/usr/lib/libtcmalloc.so&quot; 
</pre>
<p>LD_PRELOAD is tricky, and we don't necessarily recommend this mode
of usage.</p>

<p>TCMalloc includes a <a href="http://google-perftools.googlecode.com/svn/trunk/doc/heap_checker.html">heap checker</a>
and <a href="http://google-perftools.googlecode.com/svn/trunk/doc/heapprofile.html">heap profiler</a> as well.</p>

<p>If you'd rather link in a version of TCMalloc that does not include
the heap profiler and checker (perhaps to reduce binary size for a
static binary), you can link in libtcmalloc_minimal
instead.</p>


<h2>Overview</h2>

<p>TCMalloc assigns each thread a thread-local cache.  Small
allocations are satisfied from the thread-local cache.  Objects are
moved from central data structures into a thread-local cache as
needed, and periodic garbage collections are used to migrate memory
back from a thread-local cache into the central data structures.</p>
<img src="http://google-perftools.googlecode.com/svn/trunk/doc/overview.gif" alt="" />

<p>TCMalloc treats objects with size &lt;= 32K (&quot;small&quot; objects)
differently from larger objects.  Large objects are allocated directly
from the central heap using a page-level allocator (a page is a 4K
aligned region of memory).  I.e., a large object is always
page-aligned and occupies an integral number of pages.</p>

<p>A run of pages can be carved up into a sequence of small objects,
each equally sized.  For example a run of one page (4K) can be carved
up into 32 objects of size 128 bytes each.</p>


<h2>Small Object Allocation</h2>

<p>Each small object size maps to one of approximately 170 allocatable
size-classes.  For example, all allocations in the range 961 to 1024
bytes are rounded up to 1024.  The size-classes are spaced so that
small sizes are separated by 8 bytes, larger sizes by 16 bytes, even
larger sizes by 32 bytes, and so forth.  The maximal spacing (for
sizes &gt;= ~2K) is 256 bytes.</p>

<p>A thread cache contains a singly linked list of free objects per
size-class.</p>
<img src="http://google-perftools.googlecode.com/svn/trunk/doc/threadheap.gif" alt="" />

<p>When allocating a small object: (1) We map its size to the
corresponding size-class.  (2) Look in the corresponding free list in
the thread cache for the current thread.  (3) If the free list is not
empty, we remove the first object from the list and return it.  When
following this fast path, TCMalloc acquires no locks at all.  This
helps speed-up allocation significantly because a lock/unlock pair
takes approximately 100 nanoseconds on a 2.8 GHz Xeon.</p>

<p>If the free list is empty: (1) We fetch a bunch of objects from a
central free list for this size-class (the central free list is shared
by all threads).  (2) Place them in the thread-local free list.  (3)
Return one of the newly fetched objects to the applications.</p>

<p>If the central free list is also empty: (1) We allocate a run of
pages from the central page allocator.  (2) Split the run into a set
of objects of this size-class.  (3) Place the new objects on the
central free list.  (4) As before, move some of these objects to the
thread-local free list.</p>


<h2>Large Object Allocation</h2>

<p>A large object size (&gt; 32K) is rounded up to a page size (4K)
and is handled by a central page heap.  The central page heap is again
an array of free lists.  For i &lt; 256, the
kth entry is a free list of runs that consist of
k pages.  The 256th entry is a free list of
runs that have length &gt;= 256 pages: </p>
<img src="http://google-perftools.googlecode.com/svn/trunk/doc/pageheap.gif" alt="" />

<p>An allocation for k pages is satisfied by looking in
the kth free list.  If that free list is empty, we look
in the next free list, and so forth.  Eventually, we look in the last
free list if necessary.  If that fails, we fetch memory from the
system (using sbrk, mmap, or by mapping in
portions of /dev/mem).</p>

<p>If an allocation for k pages is satisfied by a run
of pages of length &gt; k, the remainder of the
run is re-inserted back into the appropriate free list in the
page heap.</p>


<h2>Spans</h2>

<p>The heap managed by TCMalloc consists of a set of pages.  A run of
contiguous pages is represented by a Span object.  A span
can either be <em>allocated</em>, or <em>free</em>.  If free, the span
is one of the entries in a page heap linked-list.  If allocated, it is
either a large object that has been handed off to the application, or
a run of pages that have been split up into a sequence of small
objects.  If split into small objects, the size-class of the objects
is recorded in the span.</p>

<p>A central array indexed by page number can be used to find the span to
which a page belongs.  For example, span <em>a</em> below occupies 2
pages, span <em>b</em> occupies 1 page, span <em>c</em> occupies 5
pages and span <em>d</em> occupies 3 pages.</p>
<img src="http://google-perftools.googlecode.com/svn/trunk/doc/spanmap.gif" alt="" />

<p>In a 32-bit address space, the central array is represented by a a
2-level radix tree where the root contains 32 entries and each leaf
contains 2^15 entries (a 32-bit address spave has 2^20 4K pages, and
the first level of tree divides the 2^20 pages by 2^5).  This leads to
a starting memory usage of 128KB of space (2^15*4 bytes) for the
central array, which seems acceptable.</p>

<p>On 64-bit machines, we use a 3-level radix tree.</p>


<h2>Deallocation</h2>

<p>When an object is deallocated, we compute its page number and look
it up in the central array to find the corresponding span object.  The
span tells us whether or not the object is small, and its size-class
if it is small.  If the object is small, we insert it into the
appropriate free list in the current thread's thread cache.  If the
thread cache now exceeds a predetermined size (2MB by default), we run
a garbage collector that moves unused objects from the thread cache
into central free lists.</p>

<p>If the object is large, the span tells us the range of pages covered
by the object.  Suppose this range is [p,q].  We also
lookup the spans for pages p-1 and q+1.  If
either of these neighboring spans are free, we coalesce them with the
[p,q] span.  The resulting span is inserted into the
appropriate free list in the page heap.</p>


<h2>Central Free Lists for Small Objects</h2>

<p>As mentioned before, we keep a central free list for each
size-class.  Each central free list is organized as a two-level data
structure: a set of spans, and a linked list of free objects per
span.</p>

<p>An object is allocated from a central free list by removing the
first entry from the linked list of some span.  (If all spans have
empty linked lists, a suitably sized span is first allocated from the
central page heap.)</p>

<p>An object is returned to a central free list by adding it to the
linked list of its containing span.  If the linked list length now
equals the total number of small objects in the span, this span is now
completely free and is returned to the page heap.</p>


<h2>Garbage Collection of Thread Caches</h2>

<p>A thread cache is garbage collected when the combined size of all
objects in the cache exceeds 2MB.  The garbage collection threshold is
automatically decreased as the number of threads increases so that we
don't waste an inordinate amount of memory in a program with lots of
threads.</p>

<p>We walk over all free lists in the cache and move some number of
objects from the free list to the corresponding central list.</p>

<p>The number of objects to be moved from a free list is determined
using a per-list low-water-mark L.  L
records the minimum length of the list since the last garbage
collection.  Note that we could have shortened the list by
L objects at the last garbage collection without
requiring any extra accesses to the central list.  We use this past
history as a predictor of future accesses and move L/2
objects from the thread cache free list to the corresponding central
free list.  This algorithm has the nice property that if a thread
stops using a particular size, all objects of that size will quickly
move from the thread cache to the central free list where they can be
used by other threads.</p>


<h2>Performance Notes</h2>

<h3>PTMalloc2 unittest</h3>

<p>The PTMalloc2 package (now part of glibc) contains a unittest
program t-test1.c. This forks a number of threads and
performs a series of allocations and deallocations in each thread; the
threads do not communicate other than by synchronization in the memory
allocator.</p>

<p>t-test1 (included in
tests/tcmalloc/, and compiled as
ptmalloc_unittest1) was run with a varying numbers of
threads (1-20) and maximum allocation sizes (64 bytes -
32Kbytes). These tests were run on a 2.4GHz dual Xeon system with
hyper-threading enabled, using Linux glibc-2.3.2 from RedHat 9, with
one million operations per thread in each test. In each case, the test
was run once normally, and once with
LD_PRELOAD=libtcmalloc.so.

</p><p>The graphs below show the performance of TCMalloc vs PTMalloc2 for
several different metrics. Firstly, total operations (millions) per
elapsed second vs max allocation size, for varying numbers of
threads. The raw data used to generate these graphs (the output of the
time utility) is available in
t-test1.times.txt.</p>

<table border="0">
<tbody><tr>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspersec.vs.size.1.threads.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspersec.vs.size.2.threads.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspersec.vs.size.3.threads.png" alt="" /></td>
</tr>
<tr>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspersec.vs.size.4.threads.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspersec.vs.size.5.threads.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspersec.vs.size.8.threads.png" alt="" /></td>
</tr>
<tr>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspersec.vs.size.12.threads.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspersec.vs.size.16.threads.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspersec.vs.size.20.threads.png" alt="" /></td>
</tr>
</tbody></table>


<ul><li> TCMalloc is much more consistently scalable than PTMalloc2 - for
       all thread counts &gt;1 it achieves ~7-9 million ops/sec for small
       allocations, falling to ~2 million ops/sec for larger
       allocations. The single-thread case is an obvious outlier,
       since it is only able to keep a single processor busy and hence
       can achieve fewer ops/sec. PTMalloc2 has a much higher variance
       on operations/sec - peaking somewhere around 4 million ops/sec
       for small allocations and falling to &lt;1 million ops/sec for
       larger allocations.

  </li><li> TCMalloc is faster than PTMalloc2 in the vast majority of
       cases, and particularly for small allocations. Contention
       between threads is less of a problem in TCMalloc.

  </li><li> TCMalloc's performance drops off as the allocation size
       increases. This is because the per-thread cache is
       garbage-collected when it hits a threshold (defaulting to
       2MB). With larger allocation sizes, fewer objects can be stored
       in the cache before it is garbage-collected.

  </li><li> There is a noticeable drop in TCMalloc's performance at ~32K
       maximum allocation size; at larger sizes performance drops less
       quickly. This is due to the 32K maximum size of objects in the
       per-thread caches; for objects larger than this TCMalloc
       allocates from the central page heap.
</li></ul>

<p>Next, operations (millions) per second of CPU time vs number of
threads, for max allocation size 64 bytes - 128 Kbytes.</p>

<table border="0">
<tbody><tr>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspercpusec.vs.threads.64.bytes.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspercpusec.vs.threads.256.bytes.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspercpusec.vs.threads.1024.bytes.png" alt="" /></td>
</tr>
<tr>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspercpusec.vs.threads.4096.bytes.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspercpusec.vs.threads.8192.bytes.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspercpusec.vs.threads.16384.bytes.png" alt="" /></td>
</tr>
<tr>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspercpusec.vs.threads.32768.bytes.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspercpusec.vs.threads.65536.bytes.png" alt="" /></td>
  <td><img src="http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc-opspercpusec.vs.threads.131072.bytes.png" alt="" /></td>
</tr>
</tbody></table>

<p>Here we see again that TCMalloc is both more consistent and more
efficient than PTMalloc2. For max allocation sizes &lt;32K, TCMalloc
typically achieves ~2-2.5 million ops per second of CPU time with a
large number of threads, whereas PTMalloc achieves generally 0.5-1
million ops per second of CPU time, with a lot of cases achieving much
less than this figure. Above 32K max allocation size, TCMalloc drops
to 1-1.5 million ops per second of CPU time, and PTMalloc drops almost
to zero for large numbers of threads (i.e. with PTMalloc, lots of CPU
time is being burned spinning waiting for locks in the heavily
multi-threaded case).</p>


<h2>Caveats</h2>

<p>For some systems, TCMalloc may not work correctly on with
applications that aren't linked against libpthread.so (or
the equivalent on your OS). It should work on Linux using glibc 2.3,
but other OS/libc combinations have not been tested.</p>

<p>TCMalloc may be somewhat more memory hungry than other mallocs,
(but tends not to have the huge blowups that can happen with other
mallocs).  In particular, at startup TCMalloc allocates approximately
240KB of internal memory.</p>

<p>Don't try to load TCMalloc into a running binary (e.g., using JNI
in Java programs).  The binary will have allocated some objects using
the system malloc, and may try to pass them to TCMalloc for
deallocation.  TCMalloc will not be able to handle such objects.</p>

<hr />

<address>Sanjay Ghemawat, Paul Menage<br />
<!-- Created: Tue Dec 19 10:43:14 PST 2000 -->
<!-- hhmts start -->
Last modified: Sat Feb 24 13:11:38 PST 2007  (csilvers)
<!-- hhmts end -->
</address>


          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/162289#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>Thu, 14 Feb 2008 17:39:40 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/162289</link>
        <guid>http://andyao.javaeye.com/blog/162289</guid>
      </item>
      <item>
        <title>Sping容器加载xsd文件时的问题</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/160778" style="color:red;">http://andyao.javaeye.com/blog/160778</a>&nbsp;
          发表时间: 2008年01月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          今天遇到一个非常奇怪的spring容器问题，先看日志<pre name="code" class="java">]-303 Loading XML bean definitions from class path resource [com/eucita/component/activemq/applicationContext-activemq-topic.xml]
-75 Using JAXP provider [org.apache.xerces.jaxp.DocumentBuilderFactoryImpl]
r]-103 Trying to resolve XML entity with public id [null] and system id [http://www.springframework.org/schema/beans/spring-beans-2.5.xsd]
r]-114 Found XML schema [http://www.springframework.org/schema/beans/spring-beans-2.5.xsd] in classpath: org/springframework/beans/factory/xml/spring-beans-2.5.xsd
r]-103 Trying to resolve XML entity with public id [null] and system id [http://www.springframework.org/schema/context/spring-context-2.5.xsd]
r]-114 Found XML schema [http://www.springframework.org/schema/context/spring-context-2.5.xsd] in classpath: org/springframework/context/config/spring-context-2.5.xsd
r]-103 Trying to resolve XML entity with public id [null] and system id [http://activemq.apache.org/schema/core/activemq-core-5.0.0.xsd]</pre><p>环境是</p><p>tomcat6.0（中间换glassfish2.0也试过）</p><p>spring2.5</p><p>activemq5.0</p><p>使用eclipse，和IDEA调试程序</p><p>问题是：<span style="color: #ff6600">在出了上面的日志以后，CPU占用率从50%左右直接下降到2-3%，然后就一直停在这里，没有响应。</span></p><p>&nbsp;</p><p>team里面有3台同时出现这个问题，调试了一段时间找不到原因，试着重启了电脑，然后问题消失。</p><p>猜想是IDE造成的，但是同时几个人，不同IDE，出同样问题，非常诡异。</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/160778#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>Thu, 31 Jan 2008 17:56:06 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/160778</link>
        <guid>http://andyao.javaeye.com/blog/160778</guid>
      </item>
      <item>
        <title>持续集成-软件质量的改进和风险降低之道</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/160229" style="color:red;">http://andyao.javaeye.com/blog/160229</a>&nbsp;
          发表时间: 2008年01月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://www.china-pub.com/38003" target="_blank">http://www.china-pub.com/38003</a><br />机工版，纸张一如既往的差。内容主要讲的是从实践角度讲持续集成。CI领域的重要文章Martin Flower的《Continuous Integration》主要是讲一种思想和理论。这本书算是一个CI的指导手册，讲述了CI的背景，以及持续数据库集成，持续测试，持续审查，持续部署和持续反馈，算是一本比较好的CI书籍。<br /><br />最后还是要说，机工的书纸张真差，买的书中最烂属机工；以后机工的书打死也不买了。
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/160229#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>Tue, 29 Jan 2008 23:37:14 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/160229</link>
        <guid>http://andyao.javaeye.com/blog/160229</guid>
      </item>
      <item>
        <title>Unitils——简化测试</title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/160004" style="color:red;">http://andyao.javaeye.com/blog/160004</a>&nbsp;
          发表时间: 2008年01月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <address>/**</address><address>*作者:andyao,email:andyaoy@gmail.com</address><address>*http://andyao.javaeye.com/blog/160004</address>*/&nbsp;<p>Unitils（<a href="http://www.unitils.org/summary.html" target="_blank">http://www.unitils.org/summary.html</a>）是一个简化测试开发，提高测试维护性的一个开源项目，这里有开发者做的一个presentation（<a href="http://www.parleys.com/display/PARLEYS/Unitils%20-%20making%20unit%20testing%20easy?showComments=true">http://www.parleys.com/display/PARLEYS/Unitils</a>）。目前提供的功能为：</p><ol><li><em>General testing utilities ：</em>提供一些测试的辅助方法，主要是通过反射进行数据验证。</li><li><em>Database testing utilities： <br /></em><ul><li>自动维护测试数据库，并且可以自动关闭测试数据库的constranits</li><li>提供类似于dbdeploy和rails migration的数据库版本控制</li><li>简化单元测试数据库connection的获取</li><li>简化dbunit维护测试数据的操作</li><li>Hibernate支持</li><li>单元测试事务的管理，可以选择有Unitils管理，还是spring管理</li></ul></li><li><em>Mock object utilities：</em>简化Mock ojbect的creation，injection，和match</li><li><em>Spring integration：</em>可以方便的在单元测试中获取spring管理的bean。</li></ol><p>以前做数据库测试的时候，最麻烦的就是测试数据集的管理。unitils提供了非常好的数据库测试支持，下面我们看一个简单的例子：</p><ol><li>需要测试的类<pre name="code" class="java">public class User {
 private int  id;
 private String name;
 //set get略
}

//任何实现都可以，jdbc，hibernate，ibatis等等
public class UserDAO {
  public void save(User user) {  
    ...
  }     
   
  public User get(int id) {
    ...
  }
}</pre>&nbsp;</li><li>测试类<pre name="code" class="java">@SpringApplicationContext({&quot;spring-config.xml&quot;})
@DataSet
public class UserDAOTest extends UnitilsJUnit4 {
   @SpringBeanByType
   UserDao userDao;    

  @Test
  public void testGet() {
     User user = userDao.get(1);
     assertEquals(&quot;foo&quot;, user.getName());   
  }

  @Test
  public void testSave() {
      User user = new User();
      user.setName(&quot;bar&quot;); 
      userDao.save(user);
      assertNotNull(user.getId());
   }
}</pre>&nbsp;</li><li>测试数据文件。Unitils默认按testClassName.xml到相同目录下找测试文件，然后自动装载，装载前先自动清空测试文件中包含的table，然后再转载这些table的数据。<pre name="code" class="xml">&lt;?xml version='1.0' encoding='UTF-8'?&gt;
&lt;dataset&gt;         
    &lt;user id=&quot;1&quot; name=&quot;foo&quot; /&gt;     
&lt;/dataset&gt;</pre>&nbsp;</li><li>其他的设置主要是unitils在配置文件unitils.properties（<a href="http://www.unitils.org/unitils.properties" target="_blank">http://www.unitils.org/unitils.properties</a>）中修改数据库的配置，并且放到classpath下面。依赖jar可以在unitils网站上看到。</li></ol><p>这样一个UserDAO的测试就做完了。unitils可以为每个类（或者方法，建议是类）指定测试数据文件，在测试开始的时候自动装载相关表格的数据。这是我最感兴趣的功能之一，简化的测试数据的管理。同时unitils的提供的mock支持可以显著减少使用mock时create mock的代码</p><pre name="code" class="java">@Mock
private UserDao mockUserDao;</pre>&nbsp;&nbsp;<p>unitils项目还提供了一些其他功能：</p><ol><li>多数据库测试<pre name="code" class="xml">&lt;?xml version='1.0' encoding='UTF-8'?&gt;
&lt;dataset xmlns=&quot;SCHEMA_A&quot; xmlns:b=&quot;SCHEMA_B&quot;&gt;
    &lt;user id=&quot;1&quot; userName=&quot;jack&quot; /&gt;    
    &lt;b:role id=&quot;1&quot; roleName=&quot;admin&quot; /&gt;
&lt;/dataset&gt;</pre>&nbsp;</li><li>简介中提到的类似于dbdeploy和rails migration的DBMaintainer，可以通过sql文件维护测试数据库版本。这个也是比较有用的功能，感兴趣的可以去unitils网站看文档。</li><li>一个Unit Test最佳实践的guide. <a href="http://www.unitils.org/guidelines.html">http://www.unitils.org/guidelines.htm</a></li><li>可以很容易扩展unitils，实现自己的modules。</li></ol>其他的还有很多有用的功能没有介绍，大家可以查看官方文档。<br /><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://andyao.javaeye.com/blog/160004#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, 28 Jan 2008 22:05:49 +0800</pubDate>
        <link>http://andyao.javaeye.com/blog/160004</link>
        <guid>http://andyao.javaeye.com/blog/160004</guid>
      </item>
      <item>
        <title>关于数据库重构工具——dbdeploy </title>
        <author>andyao</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andyao.javaeye.com">andyao</a>&nbsp;
          链接：<a href="http://andyao.javaeye.com/blog/159788" style="color:red;">http://andyao.javaeye.com/blog/159788</a>&nbsp;
          发表时间: 2008年01月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p> 因为目前的一个项目中，数据库变动比较频繁，本控制做的有些不好，想找一个比较好的工具和最佳实践来做数据库版本控制。google到主要工具就是dbdeploy。&nbsp;</p><p> dbdeploy（<a href="dbdeploy（http://dbdeploy.com/）">http://dbdeploy.com/</a>） 获得 <a href="http://www.prnewswire.com/cgi-bin/stories.pl?ACCT=104&amp;STORY=/www/story/03-22-2007/0004551328&amp;EDATE=">第17届Jolt奖</a>，是thoughtworks公司开发的开源的，java-based数据库重构工具。</p><p>大致看了一下介绍，和Rails migration很象，通过运行一些特定格式的sql-scripts，做到对数据库的控制，所有的数据库操作都要写到scripts中。</p><p> 