<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>Feiing <视觉错误></title>
    <description></description>
    <link>http://feiing.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>Air 使用感受</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/187816" style="color:red;">http://feiing.javaeye.com/blog/187816</a>&nbsp;
          发表时间: 2008年04月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          昨天晚上刚拿到的机器， 今天上班抽空用了大概三个小时， 说一下感受<br /><br />整台机器是非常漂亮的， 应该符合大部分人的审美观<br /><br />LED 显示屏很好 很自然<br /><br />Mac 的字体看起来非常舒服 很柔和<br /><br />Spotlight 找东西很方便， 跟 google desktop 很像<br /><br />全尺寸键盘很开阔  用惯笔记本紧凑布局的人一开始可能不适应 （我就是）<br /><br />网上说电池可以用 5 个小时以上， 我第一次充满后大概使用了 4 个半小时， 可能因为 cpu 负载比较高的缘故<br /><br />机器左上部位比较烫  散热可能是个问题<br /><br />mac jdk6 好像不太稳定， 第一次运行项目就  jvm error,  可能因为线程太多， 或者有参数需要优化<br /><br />跑  java 多线程程序很慢， 应该还是要设置一些参数<br /><br />中文输入法自带的只有智能 abc 和五笔， 听说有不少 mac 下的输入法都不错还没来得及装， 可怜只会用微软拼音的我 <img src="/images/smiles/icon_sad.gif"/><br /><br />总体来说使用下来基本满意，  等熟悉一段时间应该会好起来
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/187816#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>Tue, 29 Apr 2008 02:04:46 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/187816</link>
        <guid>http://feiing.javaeye.com/blog/187816</guid>
      </item>
      <item>
        <title>费德勒两盘横扫小德</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/187203" style="color:red;">http://feiing.javaeye.com/blog/187203</a>&nbsp;
          发表时间: 2008年04月26日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          很开心那个无敌的费德勒又回来了  并且请了新教练后红土表现让人耳目一新 感觉这次有点像绝代双骄里的嫁衣神功 "欲用其利，先挫其锋"  明天晚上九点蒙特卡洛第三次费纳对决  是球迷的千万不要错过 gg gl
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/187203#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, 26 Apr 2008 23:32:00 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/187203</link>
        <guid>http://feiing.javaeye.com/blog/187203</guid>
      </item>
      <item>
        <title>其实写 blog 是个好习惯</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/166644" style="color:red;">http://feiing.javaeye.com/blog/166644</a>&nbsp;
          发表时间: 2008年03月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          很多时候因为忙很少到这来, 然后慢慢荒凉起来了<br /><br />今天很偶尔翻到以前的一篇 <br /><br /><a href="http://starcraft.blogdriver.com/starcraft/655774.html" target="_blank">http://starcraft.blogdriver.com/starcraft/655774.html</a><br /><br />生活需要留下些印记, 不然记忆很快就消逝于这滚滚红尘了
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/166644#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, 02 Mar 2008 14:57:12 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/166644</link>
        <guid>http://feiing.javaeye.com/blog/166644</guid>
      </item>
      <item>
        <title>幸福 --- 哪怕只是一瞬间</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/161885" style="color:red;">http://feiing.javaeye.com/blog/161885</a>&nbsp;
          发表时间: 2008年02月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: large; font-family: 隶书; color: #339966">那一刻<br />感受到你的<span style="color: #ff00ff">温柔</span><br />那一刻<br />时间也为你停留<br />浪漫的天空<br />可爱的笑容<br />醉人的发香<br />温暖的双手<br />拥你入怀 <br />在寂静的夜</span>
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/161885#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, 11 Feb 2008 18:39:37 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/161885</link>
        <guid>http://feiing.javaeye.com/blog/161885</guid>
      </item>
      <item>
        <title>最是那一低头的温柔</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/161883" style="color:red;">http://feiing.javaeye.com/blog/161883</a>&nbsp;
          发表时间: 2008年02月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书"><span style="font-size: x-large">最是那一低头的<span style="color: #ff00ff"><span style="color: #ff00ff">温柔</span></span></span></span></p><p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书">&nbsp;</span></p><p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书"><span style="font-size: x-large">像一朵<span style="color: #ff00ff"><span style="color: #ff00ff">水莲花</span></span></span></span></p><p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书">&nbsp;</span></p><p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书"><span style="font-size: x-large">不胜凉风的<span style="color: #ff00ff"><span style="color: #ff00ff">娇羞</span></span></span></span></p><p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书">&nbsp;</span></p><p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书"><span style="font-size: x-large">道一声珍重, 道一声珍重</span></span></p><p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书">&nbsp;</span></p><p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书"><span style="font-size: x-large">那一声珍重里有蜜甜的忧愁----</span></span></p><p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书">&nbsp;</span></p><p style="font-family: 隶书; color: #3366ff"><span style="font-size: x-large; font-family: 隶书"><span style="font-size: x-large">莎扬那拉!</span></span></p>
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/161883#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, 11 Feb 2008 17:55:03 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/161883</link>
        <guid>http://feiing.javaeye.com/blog/161883</guid>
      </item>
      <item>
        <title>Atomikos JTA for Hibernate3</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/142663" style="color:red;">http://feiing.javaeye.com/blog/142663</a>&nbsp;
          发表时间: 2007年11月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://wiki.atomikos.org/bin/view.pl/Main/HibernateIntegration#With_Spring" target="_blank">http://wiki.atomikos.org/bin/view.pl/Main/HibernateIntegration#With_Spring</a><br /><br />http://www.hibernate.org/hib_docs/v3/reference/en/html/transactions.html#transactions-connection-release<br /><br />经过多次试验, 下面的配置是最合适的 (OpenSessionInView Works fine)<br /><br />jta.properties<br /><pre name="code" class="java">
com.atomikos.icatch.service = com.atomikos.icatch.standalone.UserTransactionServiceFactory

#Do you want transaction logging to be enabled or not?
#If set to false, then no logging overhead will be done
#at the risk of losing data after restart or crash.
#Note: this setting may be overridden depending on your license!
com.atomikos.icatch.enable_logging = false
com.atomikos.icatch.automatic_resource_registration = true
</pre><br /><br />xa datasource and jta transaction manager<br /><pre name="code" class="java">
	&lt;bean id="system.datasource"
		class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">
		&lt;property name="uniqueResourceName">&lt;value>XADBMS&lt;/value>&lt;/property>
		&lt;property name="xaDataSourceClassName">
			&lt;value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource&lt;/value>
		&lt;/property>
		&lt;property name="xaDataSourceProperties">
			&lt;value>url=${jdbc.url};user=${jdbc.username};password=${jdbc.password};pinGlobalTxToPhysicalConnection=true&lt;/value>
		&lt;/property>
		&lt;property name="exclusiveConnectionMode">&lt;value>false&lt;/value>&lt;/property>
		&lt;property name="connectionPoolSize">&lt;value>10&lt;/value>&lt;/property>
	&lt;/bean>


	&lt;bean id="system.sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		&lt;property name="dataSource" ref="system.datasource"/>
		&lt;property name="jtaTransactionManager" ref="atomikosTransactionManager" />
		&lt;property name="mappingDirectoryLocations">
            &lt;list>
            &lt;value>classpath:/com/coheg/&lt;/value>
            &lt;/list>
        &lt;/property>
		&lt;property name="hibernateProperties">
			&lt;props>
				&lt;prop key="hibernate.query.substitutions">true ${boolean.true.value}, false ${boolean.false.value}, yes ${boolean.true.value}, no ${boolean.false.value}&lt;/prop>
				&lt;prop key="hibernate.dialect">${hibernate.dialect}&lt;/prop>
				&lt;prop key="hibernate.show_sql">${hibernate.show_sql}&lt;/prop>
				&lt;prop key="hibernate.format_sql">true&lt;/prop>
				&lt;!-- for jta compatilibility -->
				&lt;prop key="current_session_context_class">jta&lt;/prop>		
				&lt;prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory&lt;/prop>	
				&lt;prop key="hibernate.connection.release_mode">after_statement&lt;/prop>
			&lt;/props>
		&lt;/property>
	&lt;/bean>
	

	&lt;!-- Construct Atomikos UserTransactionManager, needed to configure Spring -->
	&lt;bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
		&lt;!--  when close is called, should we force transactions to terminate or not? -->
		&lt;property name="forceShutdown">&lt;value>true&lt;/value>&lt;/property>
	&lt;/bean>
	
	&lt;!-- Also use Atomikos UserTransactionImp, needed to configure Spring  --> 
	&lt;bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
	    &lt;property name="transactionTimeout">&lt;value>120000&lt;/value>&lt;/property>
	&lt;/bean>
	
	&lt;!-- Configure the Spring framework to use JTA transactions from Atomikos -->
	&lt;bean id="system.platformTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
		&lt;property name="transactionManager">&lt;ref bean="atomikosTransactionManager"  />&lt;/property>
		&lt;property name="userTransaction">&lt;ref bean="atomikosUserTransaction"  />&lt;/property>
	&lt;/bean>
	

</pre><br /><br />hibernate2 不支持 Connection Release Mode 概念, 在使用 JTA + OpenSessionInView 时会出一些问题
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/142663#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, 22 Nov 2007 14:52:36 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/142663</link>
        <guid>http://feiing.javaeye.com/blog/142663</guid>
      </item>
      <item>
        <title>也说费德勒与桑普拉斯</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/142340" style="color:red;">http://feiing.javaeye.com/blog/142340</a>&nbsp;
          发表时间: 2007年11月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          从新浪上看到一个网友评论, 实在是精彩, 就转过来了<br /><br /><div class="quote_title">新浪网友 写道</div><div class="quote_div"><br /><br />奶粉：费德勒刷新连续排名第一的纪录拉，<br />桑迷：桑比费强。<br /><br />奶粉：已经连胜41场球了<br />桑迷：桑比费强。<br /><br />奶粉：草场已经连胜48场了<br />桑迷：桑比费强。<br /><br />奶粉：费德勒连续7次进入大满贯决赛<br />桑迷：桑比费强。<br /><br />奶粉：。。。。。。<br />桑迷：桑比费强。<br /><br />奶粉：对阵世界前十24连胜呀，真可怕。<br />桑迷：我们有阿加西。<br /><br />奶粉：决赛24连胜，可怕。<br />桑迷：我们有贝克尔。<br /><br />奶粉：抢7十四连胜<br />桑迷：我们有考瑞尔。<br /><br />奶粉：在北美连胜56场。<br />桑迷：我们有拉夫特。<br /><br />奶粉：.........<br />桑迷：我们有艾德博格<br /><br />奶粉：？？？？？<br />桑迷：我们有张德培。<br /><br />奶粉：！！！！！！<br />桑迷：桑普拉斯最伟大。 <br /><br /></div>
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/142340#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, 21 Nov 2007 15:48:00 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/142340</link>
        <guid>http://feiing.javaeye.com/blog/142340</guid>
      </item>
      <item>
        <title>大师杯 希尔顿 像雾像雨又像风</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/140317" style="color:red;">http://feiing.javaeye.com/blog/140317</a>&nbsp;
          发表时间: 2007年11月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="color: green">二零零七年的年终大师杯似乎比想象中来得更快, 年初澳网罗杰兵不血刃、不失一盘夺冠, 法网又一次令人扼腕输给纳达尔, 温网置于死地而后生, 美网力挽狂澜, 然后, 大师杯来了, 从遥远的大洋彼岸来到了中国, 来到了上海, 来到我生活工作的地方.<br />    <br />有比赛的日子总有很多期待, 有费德勒在咫尺之遥比赛更是魂牵梦萦, 大师们下榻的上海普陀希尔顿酒店, 更让我觉得这一切离自己这么近, 今年年初公司就开始与希尔顿合作, 自己几乎投入了全部的精力, 虽然要走的路还很长, 但希尔顿这个名字已经成为我生命中的一部分, 希尔顿酒店的雍容华贵与罗杰华丽梦幻的球技相映成辉, 恍然如梦.</span><br /><br /><span style="color: green">有网球的日子是快乐的, 在温暖和煦的春风享受生活, 在烈日炎炎的盛夏挥汗如雨, 在桂花幽香的金秋闲庭漫步, 在冷冷残阳的冬日释放青春, 奔放的正手 inside-out, 华丽的反手直线, 帅气的网前截击, 风一般的大力发球, 是视觉盛宴, 也是心灵的洗礼.</span><br /><br /><span style="color: green">费德勒又夺冠了, 第一场失利激怒了天王, 剩下的比赛如同摧枯拉朽般摧毁对手的信心, 颁奖仪式上, 顶级奔驰轿跑凸显出王者的尊贵, 今夜他是绝对的主角, 全世界为他惊叹, 完美不需要解释, 如果非要挑出一许瑕疵, 罗兰加洛斯也许是最后的遗憾, 正因为如此, 08 年才显得那么让人期待, 那片浪漫的红土地, 到底什么时候才能接纳新的王者?</span>
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/140317#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>Sun, 18 Nov 2007 21:34:00 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/140317</link>
        <guid>http://feiing.javaeye.com/blog/140317</guid>
      </item>
      <item>
        <title>Spring AOP 概览与细节</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/109075" style="color:red;">http://feiing.javaeye.com/blog/109075</a>&nbsp;
          发表时间: 2007年08月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          @王政 @2007-08-04 @转载请注明出处<br /> <br />   在这个追求速度与效率的时代,   AOP 已经成为企业应用开发重要的手段,  而 Spring AOP 无疑是其主流,  本文将从  AOP alliance 开始对 spring aop 做一个简单的概括,  同时也会对其中的重要细节做一些说明.  由于笔者对 spring2.0 尚未做过多了解, 本文将延续 spring1.0 风格.<br /><br /><span style="font-size: 18pt">主题一 : AOP 核心概念</span><br /><br />   首先来看 spring reference 对 aop 核心概念的描述 :<br />   <div class="quote_title">spring reference 写道</div><div class="quote_div"><br />   6.1.1. AOP concepts<br /><br />Let us begin by defining some central AOP concepts. These terms are not Spring-specific. Unfortunately, AOP terminology is not particularly intuitive. However, it would be even more confusing if Spring used its own terminology.<br /><br />    * Aspect: A modularization of a concern for which the implementation might otherwise cut across multiple objects. Transaction management is a good example  of a crosscutting concern in J2EE applications. Aspects are implemented using Spring as Advisors or interceptors.<br />    <br />    * Joinpoint: Point during the execution of a program, such as a method invocation or a particular exception being thrown. In Spring AOP, a joinpoint is always method invocation. Spring does not use the term joinpoint prominently; joinpoint information is accessible through methods on the MethodInvocation argument passed to interceptors, and is evaluated by implementations of the org.springframework.aop.Pointcut interface.<br />    <br />    * Advice: Action taken by the AOP framework at a particular joinpoint. Different types of advice include "around," "before" and "throws" advice. Advice types are discussed below. Many AOP frameworks, including Spring, model an advice as an interceptor, maintaining a chain of interceptors "around" the joinpoint.<br />    <br />    * Pointcut: A set of joinpoints specifying when an advice should fire. An AOP framework must allow developers to specify pointcuts: for example, using regular expressions.<br />    <br />    * Introduction: Adding methods or fields to an advised class. Spring allows you to introduce new interfaces to any advised object. For example, you could use an introduction to make any object implement an IsModified interface, to simplify caching.<br />    <br />    * Target object: Object containing the joinpoint. Also referred to as advised or proxied object.<br />    <br />    * AOP proxy: Object created by the AOP framework, including advice. In Spring, an AOP proxy will be a JDK dynamic proxy or a CGLIB proxy.<br />    <br />    * Weaving: Assembling aspects to create an advised object. This can be done at compile time (using the AspectJ compiler, for example), or at runtime. Spring, like other pure Java AOP frameworks, performs weaving at runtime.<br /><br />Different advice types include:<br /><br />    * Around advice: Advice that surrounds a joinpoint such as a method invocation. This is the most powerful kind of advice. Around advices will perform custom behavior before and after the method invocation. They are responsible for choosing whether to proceed to the joinpoint or to shortcut executing by returning their own return value or throwing an exception.<br />    <br />    * Before advice: Advice that executes before a joinpoint, but which does not have the ability to prevent execution flow proceeding to the joinpoint (unless it throws an exception).<br />    <br />    * Throws advice: Advice to be executed if a method throws an exception. Spring provides strongly typed throws advice, so you can write code that catches the exception (and subclasses) you're interested in, without needing to cast from Throwable or Exception.<br />    <br />    * After returning advice: Advice to be executed after a joinpoint completes normally: for example, if a method returns without throwing an exception.<br /><br />Around advice is the most general kind of advice. Most interception-based AOP frameworks, such as Nanning Aspects, provide only around advice.<br /><br />As Spring, like AspectJ, provides a full range of advice types, we recommend that you use the least powerful advice type that can implement the required behavior. For example, if you need only to update a cache with the return value of a method, you are better off implementing an after returning advice than an around advice, although an around advice can accomplish the same thing. Using the most specific advice type provides a simpler programming model with less potential for errors. For example, you don't need to invoke the proceed() method on the MethodInvocation used for around advice, and hence can't fail to invoke it.<br /><br />The pointcut concept is the key to AOP, distinguishing AOP from older technologies offering interception. Pointcuts enable advice to be targeted independently of the OO hierarchy. For example, an around advice providing declarative transaction management can be applied to a set of methods spanning multiple objects. Thus pointcuts provide the structural element of AOP.<br />   </div><br /><br />下图也许能更直观表述这些概念<br /><img src="http://www.javaeye.com/topics/download/ad3c8787-589c-4980-8861-bd9ecc9b177c" /><br /><br />从图中可以看出, aop alliance 定义了 Advice, JointPoint 两个核心 interface, Pointcut 接口是在 spring 中定义的, 最常用的 MethodInterceptor 是 Advice 的 sub interface, 下面简单列一下 aop alliance 各核心接口及其作用<br /><br />1. Advice :  Tag interface for Advice. Implementations can be any type of advice, such as Interceptors.<br /><br />2. Interceptor : This interface represents a generic interceptor. <br /><br />A generic interceptor can intercept runtime events that occur within a base program. <br /><br />Those events are materialized by (reified in) joinpoints. <br /><br />Runtime joinpoints can be invocations, field access, exceptions... <br />									<br />Intercetpor 的三个 sub interface :<br /><br />3. ConstructorInterceptor, FieldInterceptor, MethodInterceptor <br />   从名字看已经很直白, 分别用于拦截对象构建, 属性访问, 方法调用, <br />   对应的 JoinPoint 分别为 ConstructorInvocation, FieldAccess, MethodInvocation,<br />   其中 spring 只支持 MethodInterceptor<br />   <br />4. Joinpoint :  This interface represents a generic runtime joinpoint (in the AOP terminology). <br /><br />A runtime joinpoint is an event that occurs on a static joinpoint (i.e. a location in a the program). For instance, an invocation is the runtime joinpoint on a method (static joinpoint). <br /><br />The static part of a given joinpoint can be generically retrieved using the getStaticPart() method. <br /><br />In the context of an interception framework, a runtime joinpoint is then the reification of an access to an accessible object (a method, a constructor, a field), i.e. the static part of the joinpoint. It is passed to the interceptors that are installed on the static joinpoint. <br />								<br />Joinpoint 的三个 sub interface : <br />5. ConstructorInvocation, FieldAccess, MethodInvocation<br />   也很直白, 同时对应 3 可以知道, spring 只支持 MethodInvocation<br />   <br />aop alliance 定义的接口应该被 aop container 实现, 比如 nanning aop, spring 等<br /><br />再来看 spring 对 aop alliance 的实现和增强<br /><br />1. Pointcut : Core Spring pointcut abstraction. <br />     <br />A pointcut is composed of a ClassFilter and a MethodMatcher. <br />Both these basic terms and a Pointcut itself can be combined to build up combinations  (e.g. through org.springframework.aop.support.ComposablePointcut). <br />              <br />   笔者注 : 很奇怪 Pointcut 为什么没有出现在 aop alliance 接口定义里, 可能是为了让实现有更多的选择 ?<br />   <br />   Pointcut 就是传说中的 "切入点" 了, spring 的主要实现包括 StaticMethodMatcherPointcut, DynamicMethodMatcherPointcut 以及 AspectJExpressionPointcut, DynamicMethodMatcherPointcut 因为性能问题使用率不高, AspectJExpressionPointcut 是 spring2.0 才引入的, 本文不再讲述,StaticMethodMatcherPointcut 中最常用的是基于正则表达式的实现 JdkRegexpMethodPointcut 和 Perl5RegexpMethodPointcut,  前者需要 JDK1.4+ 的环境, 后者需要 Jakarta ORO lib, 一个典型的配置<br />   <br /><pre name="code" class="java">
&lt;bean id="settersPointcut" class="org.springframework.aop.support.Perl5RegexpMethodPointcut">
	    &lt;property name="patterns">
	        &lt;list>
	            &lt;value>.*set.*&lt;/value>
	        &lt;/list>
	    &lt;/property>
&lt;/bean>
</pre><br />   <br />   上述配置匹配所有的 set 方法, 而<br />   <br />    <pre name="code" class="java">
&lt;bean id="settersPointcut" class="org.springframework.aop.support.Perl5RegexpMethodPointcut">
		    &lt;property name="patterns">
		        &lt;list>
		            &lt;value>com.mycom.Foo.*set.*&lt;/value>
		        &lt;/list>
		    &lt;/property>
&lt;/bean>
</pre> <br />   <br />   仅匹配 com.mycom.Foo 中的所有 set 方法<br />   <br />2. MethodInterceptor, BeforeAdvice, ThrowsAdvice, AfterReturningAdvice,   IntroductionAdvice<br /><br />   MethodInterceptor : 最常用的 advice, 大家最熟悉的 TransactionInterceptor 就是其实现, <br />   需要注意的地方是实现中一定要调用 invocation.proceed() 使 interceptor chain 继续执行<br />   <br />   BeforeAdvice : 不再赘述了, reference 中写的很明白<br />   <br />   ThrowsAdvice : 我认为非常有用的 advice, 强大的地方在于它只是一个标志接口, 实现可以选择需要拦截的 Throwable 类型, 当然也可以获取 MethodInvocation 中的一切信息, ThrowsAdvice 可以实现非常完善的 exception handle 机制<br />   <br />   AfterReturningAdvice : 使用率不高, 不再赘述<br />   <br />   IntroductionAdvice : 另一个非常强大的 advice, 限于文章篇幅, 也不再细数了, 使用它可以实现一些很眩的功能<br />   <br />3. Advisor :  Base interface holding AOP advice (action to take at a joinpoint) <br />   and a filter determining the applicability of the advice <br />   (such as a pointcut). <br />   <br />   This interface is not for use by Spring users, but to allow for commonality      in support for different types of advice. <br /><br />Spring AOP is based around around advice delivered via method interception, <br />compliant with the AOP Alliance interception API. <br /><br />The Advisor interface allows support for different types of advice, <br />such as before and after advice, which need not be implemented using interception. <br /><br />   <br />   Advisor hold 了 Advice, 并且决定 advice 是否执行, 比如最常用的 PointcutAdvisor, 它包含了 getPointcut() 方法,<br /> <span style="color: red">  通俗的讲, Advice 是 "做什么 ", Pointcut 是 "何时做", 而 PointcutAdvisor 是 "何时做, 做什么", </span><br />   配合下文提到的 DefaultAdvisorAutoProxyCreator, 就可以对容器中的 bean 实现灵活的拦截<br />   <br />   <br /><span style="font-size: 18pt">主题二 : 使用 Spring AOP</span><br /><br />   spring 提供了多种不同的方案实现对 bean 的 aop proxy, 包括 ProxyFactoryBean, 便利的 TransactionProxyFactoryBean 以及 AutoProxyCreator 等,<br />   下图是 proxy class diagram 以供参考<br />   <br />   <img src="http://www.javaeye.com/topics/download/fa7c5a9e-efa5-4ae1-bb1d-15b63d789093" /><br />    <br />   这里重点说一下最常用的 ProxyFactoryBean, TransactionProxyFactoryBean, BeanNameAutoProxyCreator, DefaultAdvisorAutoProxyCreator 的联系和区别<br />   <br />   1. ProxyFactoryBean : 使用率最高的 proxy 方式, 它通过配置 interceptorNames 属性决定加入哪些 advisor (method interceptor 将会被自动包装成 advisor, 下文将描述这个细节), <br />      注意是 "interceptorNames" 而不是 "interceptors", 原因是 ProxyFactoryBean 可能返回非 singleton 的 proxy 实例, 而 advisior 可能也是非 singleton 的, 因此不能通过 interceptor reference 来注入<br />      <br />   2. TransactionProxyFactoryBean : 特定用于 transaction proxy, 注意其 super class 是 AbstractSingletonProxyFactoryBean,  也就是说, <span style="color: red">TransactionProxyFactoryBean 永远无法返回非 singleton 的 proxy 实例 !!!</span> 如果你需要非 singleton 的 proxy 实例, 请考虑使用 ProxyFactoryBean.<br />      <br />   3. BeanNameAutoProxyCreator : 故名思义, 根据 bean name 进行 auto proxy, bean name 的 match 规则参见 org.springframework.util.PatternMatchUtils<br />  <br />   4. DefaultAdvisorAutoProxyCreator : 更强大的 auto proxy creator, 强大之处在于它会 cahce 容器中所有注册的 advisor, 然后搜索容器中所有的 bean , <br />      如果某个 bean 满足 advisor 中的 Pointcut, 那么将会被自动代理, 与 BeanNameAutoProxyCreator 相比, 省去了配置 beanNames 的工作, <br />      <br />      eg :<br />    <br />      <pre name="code" class="java">
      
	      &lt;bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
			  &lt;bean id="defaultPointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor" scope="prototype">
			  		&lt;property name="pointcut" ref="fooPointcut"/>
			  		&lt;property name="advice" ref="fooAdvice"/>
				&lt;/bean>	
				
				&lt;bean id="fooAdvice" class="com.mycompany.FooAdvice" scope="prototype" />
			
				&lt;bean id="fooPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">
			  		&lt;property name="patterns">
			  			&lt;list>
			  				&lt;value>com.mycompany.FooService.*&lt;/value>
			  			&lt;/list>
			  		&lt;/property>
			  	&lt;/bean>
      </pre> <br />      <br />      以上配置将自动代理容器中所有 com.mycompany.FooService 类型的 bean, 并拦截其所有方法<br />      <br />      <br /> <br /> <span style="font-size: 18pt">深度话题</span><br />      <br />      1. MethodInterceptor 如何被包装成 Advisor ?<br />         <br />         在 AdvisorAdapterRegistry#wrap(Object) 方法中实现, code as below<br />         <br />         <pre name="code" class="java">
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
  if (adviceObject instanceof Advisor) {
    return (Advisor) adviceObject;
  }
  if (!(adviceObject instanceof Advice)) {
    hrow new UnknownAdviceTypeException(adviceObject);
  }
  Advice advice = (Advice) adviceObject;
  if (advice instanceof MethodInterceptor) {
   // So well-known it doesn't even need an adapter.
   return new DefaultPointcutAdvisor(advice);
  }
  for (int i = 0; i &lt; this.adapters.size(); i++) {
   / Check that it is supported.
							   AdvisorAdapter adapter = (AdvisorAdapter) this.adapters.get(i);
if (adapter.supportsAdvice(advice)) {
								 return new DefaultPointcutAdvisor(advice);
 }
}
  throw new UnknownAdviceTypeException(advice);
					}
         </pre><br />         <br />         从代码可以看到, 如果 adviceObject(也就是 interceptorNames 对应的 bean) 不是 advisor<br />         而是 MethodInterceptor 或 Advice, 那么 spring 将其包装成 DefaultPointcutAdvisor, <br />         而 DefaultPointcutAdvisor 中定义的 Pointcut 是 TruePointcut :<br />         <br />         <pre name="code" class="java">
         class TruePointcut implements Pointcut, Serializable {
	
						public static final TruePointcut INSTANCE = new TruePointcut();
						
						/**
						 * Enforce Singleton pattern.
						 */
						private TruePointcut() {
						}
					
						public ClassFilter getClassFilter() {
							return ClassFilter.TRUE;
						}
					
						public MethodMatcher getMethodMatcher() {
							return MethodMatcher.TRUE;
						}
						
						/**
						 * Required to support serialization. Replaces with canonical
						 * instance on deserialization, protecting Singleton pattern.
						 * Alternative to overriding &lt;code>equals()&lt;/code>.
						 */
						private Object readResolve() {
							return INSTANCE;
						}
					
						public String toString() {
							return "Pointcut.TRUE";
						}
					
					}
         
         </pre><br />         <br />         也就是说, MethodInterceptor 和 Advice 被包装成的 Advisor 将会匹配容器中的所有 bean,<br /> 所以, <span style="color: red">永远不要在 DefaultAdvisorAutoProxyCreator 的 interceptorNames 中引用一个 Advice, 那将会使容器中所有的 bean 被自动代理!!! 此时应该考虑使用 BeanNameAutoProxyCreator 或者将 Advice 用特定的 Pointcut 包装成 advisor 后注入 DefaultAdvisorAutoProxyCreator.</span><br />      <br />      2. spring 中 interceptor 的执行顺序 ? TODO<br />      3. 一个 bean 可以被多次 proxy 吗 ?  TODO
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/109075#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>Sun, 05 Aug 2007 03:21:00 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/109075</link>
        <guid>http://feiing.javaeye.com/blog/109075</guid>
      </item>
      <item>
        <title>apache - 带给人的只有失望</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/108391" style="color:red;">http://feiing.javaeye.com/blog/108391</a>&nbsp;
          发表时间: 2007年08月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          &nbsp;&nbsp;&nbsp;&nbsp; 本来简单易用的&nbsp; <a href="http://xfire.codehaus.org/">xfire</a>&nbsp;&nbsp; ,&nbsp; 成为 <a href="http://incubator.apache.org/cxf/">apache CXF</a> 后,&nbsp; 变的异常复杂,&nbsp; 第一次使用 xfire 时按照 user guide 5 分钟就非常顺利把服务跑起来,&nbsp;&nbsp; 换成 CXF 却抛了无数异常,&nbsp; CXF 按照所谓的标准自做聪明做了很多附加处理,&nbsp; 使用起来极其之郁闷<br />
<br />
&nbsp; &nbsp;&nbsp; 类似的 <a href="http://www.opensymphony.com/webwork/">webwork </a>到 <a href="http://struts.apache.org/index.html">struts2</a>.0,&nbsp; 除了体积在迅速扩大,&nbsp; 实在没看出什么亮点<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 正好又在看 <a href="http://activemq.apache.org/">ActiveMq</a>,&nbsp; jdbc persistence 的实现 Statements 简陋得让人抓狂,&nbsp;&nbsp; 难道这帮没有看过 hibernate dialet ?<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; 严重怀疑 apache&nbsp; 是按照代码行数来付薪水的,&nbsp;&nbsp; 任何精巧的项目一到它旗下,&nbsp; 马上变得臃肿丑陋不堪起来<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; 以后技术选型时, 如果有别的选择,&nbsp; 俺一定远离 apache
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/108391#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>Fri, 03 Aug 2007 02:32:43 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/108391</link>
        <guid>http://feiing.javaeye.com/blog/108391</guid>
      </item>
      <item>
        <title>十米距离 四个阶层</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/100853" style="color:red;">http://feiing.javaeye.com/blog/100853</a>&nbsp;
          发表时间: 2007年07月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          早上上班遇到红灯,&nbsp; 正好一排停了四辆车,&nbsp; 最远的是一辆货运汽车,&nbsp; 露天的后车厢坐着七八个民工,&nbsp; 都是一脸茫然的表情<br />
<br />
货运汽车旁边是一辆出租车,&nbsp; 上面坐的是一个年轻少妇,&nbsp;&nbsp; 显得悠闲自得<br />
<br />
再旁边是一辆 bmw,&nbsp; 隐隐约约能看到也是一个年轻女人,&nbsp;&nbsp; 很漠然的表情<br />
<br />
然后就是我站着的公车,&nbsp;&nbsp; 有点挤,&nbsp;&nbsp; 每人都想找个站着舒服点的地方<br />
<br />
不知道为什么突然就想写这段流水账
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/100853#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, 13 Jul 2007 23:29:46 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/100853</link>
        <guid>http://feiing.javaeye.com/blog/100853</guid>
      </item>
      <item>
        <title>蒙特卡洛 * 费德勒又输了</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/73264" style="color:red;">http://feiing.javaeye.com/blog/73264</a>&nbsp;
          发表时间: 2007年04月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 纳达尔似乎已经成了梦魇<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 球王何时能走出魔咒<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 接下来还有罗马 汉堡 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当然最终的目标是罗兰 * 加洛斯<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 罗杰好运
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/73264#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, 22 Apr 2007 23:13:51 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/73264</link>
        <guid>http://feiing.javaeye.com/blog/73264</guid>
      </item>
      <item>
        <title>转帖 : 杀人运钞警该不该判死刑？</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/73204" style="color:red;">http://feiing.javaeye.com/blog/73204</a>&nbsp;
          发表时间: 2007年04月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong><font size="2">2007年4月8日，下午五点左右，一、名、中、年男子到中、国建、设银、行苏、家、屯支、行取款，取完 后正要离开时，正赶上运、钞、车来了，车上的运、钞警 卫不准顾客出入，这名男子说他有急事要先走，便和运 钞 警 卫 发生了口角，运钞警卫说：你信不信，再走我就 打 死 你。这位男子不 信 邪，向前跨了一步，这位警卫马上向外喊：进来。这时又进来一位警卫，二话没说，举 枪 就 朝 这位中年男子 脑 袋 开 了 一 枪，中 年 男 子 当 场 死 亡。 </font></strong> <br />
<strong><font size="2"> <br />
这 个 世 界 是不 是 真 的 疯 了 啊~~~~  <br />
人 命 就 这 么 不 值 钱 ???</font></strong><br />
<br />
原贴地址<br />
<br />
<a href="http://club.chinaren.com/bbs/index.jsp?boardid=13&amp;hotmsgid=97543793">club.chinaren.com/bbs/index.jsp</a><br />
<br />
大家以后碰到运钞车一定记得躲远点啊，&nbsp; 罪过
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/73204#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>Sun, 22 Apr 2007 17:18:07 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/73204</link>
        <guid>http://feiing.javaeye.com/blog/73204</guid>
      </item>
      <item>
        <title>四月 * 上海</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/69352" style="color:red;">http://feiing.javaeye.com/blog/69352</a>&nbsp;
          发表时间: 2007年04月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 天气怪的厉害,&nbsp; 三月时已经是艳阳高照,&nbsp; 这几天倒是时冷时热,&nbsp;&nbsp; 空气中似乎没有一许水的滋味,&nbsp;&nbsp; 干燥让脆弱的喉咙一直好不起来,&nbsp;&nbsp; 每天痒痒的难受.<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 周末去打球,&nbsp; 球场有些荒废了,&nbsp; 地上很厚的沙土,&nbsp; 没什么心情玩,&nbsp; 草草的就回去了,&nbsp; 需要找一个新的场地,&nbsp; 李教练一直没给消息, 不知道情况怎么样了.&nbsp; <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 半个月竟然剪了两次头发,&nbsp; 第一次从披肩长发变长发,&nbsp; 这次从长发变碎发,&nbsp; 不知道为什么突然起在乎别人的看法,&nbsp; 人家 gigix 的发型,&nbsp; 不是比自己夸张很多么,&nbsp; 年轻需要激情.<br />
<br />
&nbsp;&nbsp;&nbsp; 电视上在说 ：生如夏花, 逝如冬雪
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/69352#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, 09 Apr 2007 21:36:18 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/69352</link>
        <guid>http://feiing.javaeye.com/blog/69352</guid>
      </item>
      <item>
        <title>解惑 spring 嵌套事务 </title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/35907" style="color:red;">http://feiing.javaeye.com/blog/35907</a>&nbsp;
          发表时间: 2006年11月25日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          解惑 spring 嵌套事务 <br /><br />	 /**<br />	  * @author 王政<br />	  * @date 2006-11-24<br />	  * @note 转载请注明出处<br />	  */<br />		<br />   在所有使用 spring 的应用中, 声明式事务管理可能是使用率最高的功能了, 但是, 从我观察到的情况看, <br />绝大多数人并不能深刻理解事务声明中不同事务传播属性配置的的含义, 让我们来看一下 TransactionDefinition 接口中的定义<br /><br /><pre name="code" class="java">
/**
	 * Support a current transaction, create a new one if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * &lt;p>This is typically the default setting of a transaction definition.
	 */
	int PROPAGATION_REQUIRED = 0;

	/**
	 * Support a current transaction, execute non-transactionally if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * &lt;p>Note: For transaction managers with transaction synchronization,
	 * PROPAGATION_SUPPORTS is slightly different from no transaction at all,
	 * as it defines a transaction scopp that synchronization will apply for.
	 * As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
	 * will be shared for the entire specified scope. Note that this depends on
	 * the actual synchronization configuration of the transaction manager.
	 * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
	 */
	int PROPAGATION_SUPPORTS = 1;

	/**
	 * Support a current transaction, throw an exception if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 */
	int PROPAGATION_MANDATORY = 2;

	/**
	 * Create a new transaction, suspend the current transaction if one exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * &lt;p>Note: Actual transaction suspension will not work on out-of-the-box
	 * on all transaction managers. This in particular applies to JtaTransactionManager,
	 * which requires the &lt;code>javax.transaction.TransactionManager&lt;/code> to be
	 * made available it to it (which is server-specific in standard J2EE).
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	int PROPAGATION_REQUIRES_NEW = 3;

	/**
	 * Execute non-transactionally, suspend the current transaction if one exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * &lt;p>Note: Actual transaction suspension will not work on out-of-the-box
	 * on all transaction managers. This in particular applies to JtaTransactionManager,
	 * which requires the &lt;code>javax.transaction.TransactionManager&lt;/code> to be
	 * made available it to it (which is server-specific in standard J2EE).
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	int PROPAGATION_NOT_SUPPORTED = 4;

	/**
	 * Execute non-transactionally, throw an exception if a transaction exists.
	 * Analogous to EJB transaction attribute of the same name.
	 */
	int PROPAGATION_NEVER = 5;

	/**
	 * Execute within a nested transaction if a current transaction exists,
	 * behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
	 * &lt;p>Note: Actual creation of a nested transaction will only work on specific
	 * transaction managers. Out of the box, this only applies to the JDBC
	 * DataSourceTransactionManager when working on a JDBC 3.0 driver.
	 * Some JTA providers might support nested transactions as well.
	 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
	 */
	int PROPAGATION_NESTED = 6;
</pre><br /><br />我们可以看到, 在 spring 中一共定义了六种事务传播属性, 如果你觉得看起来不够直观, 那么我来转贴一个满大街都有的翻译<br /><br /><div class="quote_title">引用</div><div class="quote_div"><br />PROPAGATION_REQUIRED -- 支持当前事务，如果当前没有事务，就新建一个事务。这是最常见的选择。<br />PROPAGATION_SUPPORTS -- 支持当前事务，如果当前没有事务，就以非事务方式执行。<br />PROPAGATION_MANDATORY -- 支持当前事务，如果当前没有事务，就抛出异常。<br />PROPAGATION_REQUIRES_NEW -- 新建事务，如果当前存在事务，把当前事务挂起。<br />PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作，如果当前存在事务，就把当前事务挂起。<br />PROPAGATION_NEVER -- 以非事务方式执行，如果当前存在事务，则抛出异常。<br />PROPAGATION_NESTED -- 如果当前存在事务，则在嵌套事务内执行。如果当前没有事务，则进行与PROPAGATION_REQUIRED类似的操作。<br />前六个策略类似于EJB CMT，第七个（PROPAGATION_NESTED）是Spring所提供的一个特殊变量。<br />它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为（如Spring的DataSourceTransactionManager）<br /></div><br /><br />在我所见过的误解中, 最常见的是下面这种:<br /><br /><div class="quote_title">引用</div><div class="quote_div"><br />假如有两个业务接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一个方法实现如下<br /><br />/**<br /> * 事务属性配置为 PROPAGATION_REQUIRED<br /> */<br />void methodA() {<br />	// 调用 ServiceB 的方法<br />	ServiceB.methodB();<br />}<br /><br />那么如果 ServiceB 的 methodB  如果配置了事务, 就必须配置为 PROPAGATION_NESTED<br /></div><br /><br />这种想法可能害了不少人, 认为 Service 之间应该避免互相调用, 其实根本不用担心这点，PROPAGATION_REQUIRED 已经说得很明白,<br />如果当前线程中已经存在事务, 方法调用会加入此事务, 果当前没有事务，就新建一个事务, 所以 ServiceB#methodB() 的事务只要遵循最普通的规则配置为 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我们称之为内部事务, 为下文打下基础) 抛了异常, 那么 ServiceA#methodA(我们称之为外部事务) 如果没有特殊配置此异常时事务提交 (即 +MyCheckedException的用法), 那么整个事务是一定要 rollback 的, 什么 Service 只能调 Dao 之类的言论纯属无稽之谈, spring 只负责配置了事务属性方法的拦截, 它怎么知道你这个方法是在 Service 还是 Dao 里 ?<br /><br />     说了这么半天, 那到底什么是真正的事务嵌套呢, 解释之前我们来看一下  Juergen Hoeller 的原话<br /><br /><div class="quote_title">Juergen Hoeller 写道</div><div class="quote_div"><br />PROPAGATION_REQUIRES_NEW starts a new, independent "inner" transaction for the given scope. This transaction will be committed or rolled back completely independent from the outer transaction, having its own isolation scope, its own set of locks, etc. The outer transaction will get suspended at the beginning of the inner one, and resumed once the inner one has completed.<br /><br />Such independent inner transactions are for example used for id generation through manual sequences, where the access to the sequence table should happen in its own transactions, to keep the lock there as short as possible. The goal there is to avoid tying the sequence locks to the (potentially much longer running) outer transaction, with the sequence lock not getting released before completion of the outer transaction.<br /><br />PROPAGATION_NESTED on the other hand starts a "nested" transaction, which is a true subtransaction of the existing one. What will happen is that a savepoint will be taken at the start of the nested transaction. íf the nested transaction fails, we will roll back to that savepoint. The nested transaction is part of of the outer transaction, so it will only be committed at the end of of the outer transaction.<br /><br />Nested transactions essentially allow to try some execution subpaths as subtransactions: rolling back to the state at the beginning of the failed subpath, continuing with another subpath or with the main execution path there - all within one isolated transaction, and not losing any previous work done within the outer transaction.<br /><br />For example, consider parsing a very large input file consisting of account transfer blocks: The entire file should essentially be parsed within one transaction, with one single commit at the end. But if a block fails, its transfers need to be rolled back, writing a failure marker somewhere. You could either start over the entire transaction every time a block fails, remembering which blocks to skip - or you mark each block as a nested transaction, only rolling back that specific set of operations, keeping the previous work of the outer transaction. The latter is of course much more efficient, in particular when a block at the end of the file fails.<br /></div><br /><br /><div class="quote_title">Juergen Hoeller 写道</div><div class="quote_div"><br />Rolling back the entire transaction is the choice of the demarcation code/config that started the outer transaction.<br /><br />So if an inner transaction throws an exception and is supposed to be rolled back (according to the rollback rules), the transaction will get rolled back to the savepoint taken at the start of the inner transaction. The immediate calling code can then decide to catch the exception and proceed down some other path within the outer transaction.<br /><br />If the code that called the inner transaction lets the exception propagate up the call chain, the exception will eventually reach the demarcation code of the outer transaction. At that point, the rollback rules of the outer transaction decide whether to trigger a rollback. That would be a rollback of the entire outer transaction then.<br /><br />So essentially, it depends on your exception handling. If you catch the exception thrown by the inner transaction, you can proceed down some other path within the outer transaction. If you let the exception propagate up the call chain, it's eventually gonna cause a rollback of the entire outer transaction.<br /></div><br /><br />    也就是说, 最容易弄混淆的其实是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED, 那么这两种方式又有何区别呢? 我简单的翻译一下 Juergen Hoeller 的话 :<br />    <br />    <span style="color: red">PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.</span><br /> <br /><br />    <span style="color: red">另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务,  它是已经存在事务的一个真正的子事务. 潜套事务开始执行时,  它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.</span><br /><br />    <span style="color: red">由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.</span><br />    <br />    <br />    那么外部事务如何利用嵌套事务的 savepoint 特性呢, 我们用代码来说话<br />    <br /><pre name="code" class="java">
	ServiceA {
		
		/**
		 * 事务属性配置为 PROPAGATION_REQUIRED
		 */
		void methodA() {
			ServiceB.methodB();
		}
	
	}
	
	ServiceB {
		
		/**
		 * 事务属性配置为 PROPAGATION_REQUIRES_NEW
		 */	
		void methodB() {
		}
		
	}	
</pre>    <br /><br />这种情况下, 因为 ServiceB#methodB 的事务属性为 PROPAGATION_REQUIRES_NEW, 所以两者不会发生任何关系, ServiceA#methodA 和 ServiceB#methodB 不会因为对方的执行情况而影响事务的结果, 因为它们根本就是两个事务, 在 ServiceB#methodB 执行时 ServiceA#methodA 的事务已经挂起了 (关于事务挂起的内容已经超出了本文的讨论范围, 有时间我会再写一些挂起的文章) .<br /><br />那么 PROPAGATION_NESTED 又是怎么回事呢? 继续看代码<br /><br /><pre name="code" class="java">

	ServiceA {
		
		/**
		 * 事务属性配置为 PROPAGATION_REQUIRED
		 */
		void methodA() {
			ServiceB.methodB();
		}
	
	}
	
	ServiceB {
		
		/**
		 * 事务属性配置为 PROPAGATION_NESTED
		 */	
		void methodB() {
		}
		
	}	

</pre><br /><br />现在的情况就变得比较复杂了, ServiceB#methodB 的事务属性被配置为 PROPAGATION_NESTED, 此时两者之间又将如何协作呢? 从 Juergen Hoeller 的原话中我们可以找到答案, ServiceB#methodB 如果 rollback, 那么内部事务(即 ServiceB#methodB) 将回滚到它执行前的 SavePoint(注意, 这是本文中第一次提到它, 潜套事务中最核心的概念), 而外部事务(即 ServiceA#methodA) 可以有以下两种处理方式:<br /><br />1. 改写 ServiceA 如下<br /><pre name="code" class="java">

	ServiceA {
		
		/**
		 * 事务属性配置为 PROPAGATION_REQUIRED
		 */
		void methodA() {
			try {
				ServiceB.methodB();
			} catch (SomeException) {
				// 执行其他业务, 如 ServiceC.methodC();
			}
		}
	
	}
	
</pre> <br /><br />这种方式也是潜套事务最有价值的地方, 它起到了分支执行的效果, 如果 ServiceB.methodB 失败, 那么执行 ServiceC.methodC(), 而 ServiceB.methodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都没有办法做到这一点. (题外话 : 看到这种代码, 似乎似曾相识, 想起了 prototype.js 中的 Try 函数 )<br /><br />2. 代码不做任何修改, 那么如果内部事务(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滚到它执行之前的 SavePoint(在任何情况下都会如此),<br />   外部事务(即 ServiceA#methodA) 将根据具体的配置决定自己是 commit 还是 rollback (+MyCheckedException).<br />   <br />   <br />上面大致讲述了潜套事务的使用场景, 下面我们来看如何在 spring 中使用 PROPAGATION_NESTED, 首先来看 AbstractPlatformTransactionManager<br /><br /><pre name="code" class="java">

	/**
	 * Create a TransactionStatus for an existing transaction.
	 */
	private TransactionStatus handleExistingTransaction(
			TransactionDefinition definition, Object transaction, boolean debugEnabled)
			throws TransactionException {

    ... 省略

		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			if (!isNestedTransactionAllowed()) {
				throw new NestedTransactionNotSupportedException(
						"Transaction manager does not allow nested transactions by default - " +
						"specify 'nestedTransactionAllowed' property with value 'true'");
			}
			if (debugEnabled) {
				logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
			}
			if (useSavepointForNestedTransaction()) {
				// Create savepoint within existing Spring-managed transaction,
				// through the SavepointManager API implemented by TransactionStatus.
				// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
				DefaultTransactionStatus status =
						newTransactionStatus(definition, transaction, false, false, debugEnabled, null);
				status.createAndHoldSavepoint();
				return status;
			}
			else {
				// Nested transaction through nested begin and commit/rollback calls.
				// Usually only for JTA: Spring synchronization might get activated here
				// in case of a pre-existing JTA transaction.
				doBegin(transaction, definition);
				boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
				return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
			}
		}
	}

</pre><br />   <br />一目了然<br /><br /><span style="color: red">1. 我们要设置 transactionManager 的 nestedTransactionAllowed 属性为 true, 注意, 此属性默认为 false!!!</span><br /><br />再看 AbstractTransactionStatus#createAndHoldSavepoint() 方法<br /><br /><pre name="code" class="java">
	/**
	 * Create a savepoint and hold it for the transaction.
	 * @throws org.springframework.transaction.NestedTransactionNotSupportedException
	 * if the underlying transaction does not support savepoints
	 */
	public void createAndHoldSavepoint() throws TransactionException {
		setSavepoint(getSavepointManager().createSavepoint());
	}
</pre><br /><br />  可以看到 Savepoint 是 SavepointManager.createSavepoint 实现的, 再看 SavepointManager 的层次结构, 发现<br />  其 Template 实现是 JdbcTransactionObjectSupport, 常用的 DatasourceTransactionManager, HibernateTransactionManager<br />  中的 TransactonObject 都是它的子类 :<br /><br /><img src="http://www.javaeye.com/upload/attachment/pic/2754/6dfbf7bb-eb70-485c-82d6-a172e7ad8114-thumb.jpg" /><br />   <br />  JdbcTransactionObjectSupport 告诉我们必须要满足两个条件才能 createSavepoint :<br />  <br /><span style="color: red"> 2. java.sql.Savepoint 必须存在, 即 jdk 版本要 1.4+ <br /> 3. Connection.getMetaData().supportsSavepoints() 必须为 true, 即 jdbc drive 必须支持 JDBC 3.0 </span><br /> <br /> 确保以上条件都满足后, 你就可以尝试使用 PROPAGATION_NESTED 了. (全文完)
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/35907#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, 25 Nov 2006 01:03:18 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/35907</link>
        <guid>http://feiing.javaeye.com/blog/35907</guid>
      </item>
      <item>
        <title>身心疲惫</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/33224" style="color:red;">http://feiing.javaeye.com/blog/33224</a>&nbsp;
          发表时间: 2006年11月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          什么时候才能连续休假半年周游世界， 无论怎样， 程序员也不是一份轻松的职业
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/33224#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>Wed, 08 Nov 2006 16:45:32 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/33224</link>
        <guid>http://feiing.javaeye.com/blog/33224</guid>
      </item>
      <item>
        <title>预测的魔力</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/33110" style="color:red;">http://feiing.javaeye.com/blog/33110</a>&nbsp;
          发表时间: 2006年11月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          注 : 此文纯属调侃<br /><br />一不小心看到 Readonly 去年写的一篇趣文, 原文如下<br /><br /><div class="quote_title">Readonly 写道</div><div class="quote_div"><br />    <br />狗年再来看看（如果javaeye还在的话）<br /><br />1. Google推出它自己的浏览器<br /><br />2. 出现多种Open Source的分布式计算和集群架构<br /><br />3. EJB3规范落地，并且再次被吹捧上天，而生不逢时的JDO2规范升天<br /><br />4. 号称比AOP更进一步的MOP出现，MOP开始嗡嗡做响<br /><br />5. Tapestry项目逐步走向没落，与之类似的JSF规范也没有好下场<br /><br />6. 基于Flash的Rich应用市场分额上升，击败webstart, applet<br /><br />7. Apache推出common-orm可以让你随意切换EJB3, JDO2, Hibernate3<br /><br />8. Hani停止了Bile Blog<br /><br />9. .Net在市场上惨败，但是M$一如既往地否认这一点，并且在狗年推出完全不同的TNND架构，但号称是.Net的升级版本<br /><br />10. JavaEye被Jdon收购<br /></div><br /><br />不妨来看看是否应证<br /><br /><div class="quote_title">Readonly 写道</div><div class="quote_div">1. Google推出它自己的浏览器</div><br />到现在为止好像还没有， 倒是推出了一些照片， 文档管理之类的服务 ( Docs and Spreadsheets ?), 没怎么使用过， 不过 Google 总是能给人一些惊喜， 期待年底或 2007 蔫吧. 预测准确度 : 30%<br /><br /><div class="quote_title">Readonly 写道</div><div class="quote_div">2. 出现多种Open Source的分布式计算和集群架构</div><br />可能是我不太了解, 今年这方面的动向好像不太明显？ 预测准确度 : 20%<br /><br /><div class="quote_title">Readonly 写道</div><div class="quote_div">3. EJB3规范落地，并且再次被吹捧上天，而生不逢时的JDO2规范升天</div><br /><br />基本言中, 论坛上开始出现不少 spring vs ejb3 的帖子, 可怜的 JDO2, 阿门. 预测准确度 : 90%<br /><div class="quote_title">Readonly 写道</div><div class="quote_div">4. 号称比AOP更进一步的MOP出现，MOP开始嗡嗡做响</div><br /><br />这个 MOP， 可能是 annotation ? 不过这些都不重要了， ROR 的迅速崛起, 已经不能用嗡嗡作响形容, 已经快捅破天了. 预测准确度 : 50%<br /><br /><div class="quote_title">Readonly 写道</div><div class="quote_div">5. Tapestry项目逐步走向没落，与之类似的JSF规范也没有好下场</div><br />Tapestry 好像从来没火过？ JSF, 个人没什么好感,  能不能成为主流需要进一步观察. 预测准确度 : 70%<br /><br /><div class="quote_title">Readonly 写道</div><div class="quote_div">6. 基于Flash的Rich应用市场分额上升，击败webstart, applet</div><br /><br />Flex 好像一直是不温不火， ajax 和 eclipse rcp 似乎更主流一些. 预测准确度 : 40%<br /><br /><div class="quote_title">Readonly 写道</div><div class="quote_div"><br />7. Apache推出common-orm可以让你随意切换EJB3, JDO2, Hibernate3</div><br />Apache 似乎堕落了？　没听说什么大动作，　不过也不能怪它，　2006 所有的风头都被 ror 抢走了，　我等 java 开发者俨然已经落后了一个时代 :x. 预测准确度 : 0%<br /><br /><div class="quote_title">Readonly 写道</div><div class="quote_div"><br />8. Hani停止了Bile Blog</div><br />Hani 自从加入 JCP 好像变乖了不少？ 看来什么都不要跟政治扯上关系的好. <br />预测准确度 : 50%<br /><div class="quote_title">Readonly 写道</div><div class="quote_div"><br />9. .Net在市场上惨败，但是M$一如既往地否认这一点，并且在狗年推出完全不同的TNND架构，但号称是.Net的升级版本</div><br /><br />.Net 好像快出 3.0 了吧， 微软可能更忙着 vista 的事, 企业市场， 还是算了吧。 预测准确度 : 80%<br /><div class="quote_title">Readonly 写道</div><div class="quote_div">10. JavaEye被Jdon收购</div><br />敏感性话题<img src="/images/smiles/icon_rolleyes.gif"/> Readonly 应该是反语, 2006 年的 javaeye2.0 几乎成为国内 ror 的标志性作品, 至于 xdon, 尚能饭否? 预测准确度 : 100%<img src="/images/smiles/icon_lol.gif"/><br /><br />期待 Readonly 的猪年预测篇<img src="/images/smiles/icon_idea.gif"/>
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/33110#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, 07 Nov 2006 22:47:07 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/33110</link>
        <guid>http://feiing.javaeye.com/blog/33110</guid>
      </item>
      <item>
        <title>使用 FactoryBean 让你的 spring 配置动起来</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/32019" style="color:red;">http://feiing.javaeye.com/blog/32019</a>&nbsp;
          发表时间: 2006年11月01日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          看到不少朋友讨论 spring 配置时认为 spring 配置中只能静态的设置一些参数(典型情况如数据库配置, 定时器配置等)导致不方便, 其实 spring 已经提供了非常便利的方式来实现动态配置, 我们要做的只是实现一个自己的 FactoryBean , 来看一下 FactoryBean 接口的定义 <br /><pre name="code" class="java">
/**
 * Interface to be implemented by objects used within a BeanFactory
 * that are themselves factories. If a bean implements this interface,
 * it is used as a factory, not directly as a bean.
 *
 * &lt;p>&lt;b>NB: A bean that implements this interface cannot be used
 * as a normal bean.&lt;/b> A FactoryBean is defined in a bean style,
 * but the object exposed for bean references is always the object
 * that it creates.
 *
 * &lt;p>FactoryBeans can support singletons and prototypes, and can
 * either create objects lazily on demand or eagerly on startup.
 *
 * &lt;p>This interface is heavily used within the framework, for
 * example for the AOP ProxyFactoryBean or JndiObjectFactoryBean.
 * It can be used for application components, but this is not common
 * outside of infrastructure code.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 08.03.2003
 * @see org.springframework.beans.factory.BeanFactory
 * @see org.springframework.aop.framework.ProxyFactoryBean
 * @see org.springframework.jndi.JndiObjectFactoryBean
 */
public interface FactoryBean {

	/**
	 * Return an instance (possibly shared or independent) of the object
	 * managed by this factory. As with a BeanFactory, this allows
	 * support for both the Singleton and Prototype design pattern.
	 * &lt;p>If this method returns &lt;code>null&lt;/code>, the factory will consider
	 * the FactoryBean as not fully initialized and throw a corresponding
	 * FactoryBeanNotInitializedException.
	 * @return an instance of the bean (should not be &lt;code>null&lt;/code>;
	 * a &lt;code>null&lt;/code> value will be considered as an indication of
	 * incomplete initialization)
	 * @throws Exception in case of creation errors
	 * @see FactoryBeanNotInitializedException
	 */
	Object getObject() throws Exception;

	/**
	 * Return the type of object that this FactoryBean creates, or &lt;code>null&lt;/code>
	 * if not known in advance. This allows to check for specific types
	 * of beans without instantiating objects, for example on autowiring.
	 * &lt;p>For a singleton, this should try to avoid singleton creation
	 * as far as possible; it should rather estimate the type in advance.
	 * For prototypes, returning a meaningful type here is advisable too.
	 * &lt;p>This method can be called &lt;i>before&lt;/i> this FactoryBean has
	 * been fully initialized. It must not rely on state created during
	 * initialization; of course, it can still use such state if available.
	 * &lt;p>&lt;b>NOTE:&lt;/b> Autowiring will simply ignore FactoryBeans that return
	 * &lt;code>null&lt;/code> here. Therefore it is highly recommended to implement
	 * this method properly, using the current state of the FactoryBean.
	 * @return the type of object that this FactoryBean creates,
	 * or &lt;code>null&lt;/code> if not known at the time of the call
	 * @see ListableBeanFactory#getBeansOfType
	 */
	Class getObjectType();

	/**
	 * Is the bean managed by this factory a singleton or a prototype?
	 * That is, will &lt;code>getObject()&lt;/code> always return the same object
	 * (a reference that can be cached)?
	 * &lt;p>&lt;b>NOTE:&lt;/b> If a FactoryBean indicates to hold a singleton object,
	 * the object returned from &lt;code>getObject()&lt;/code> might get cached
	 * by the owning BeanFactory. Hence, do not return &lt;code>true&lt;/code>
	 * unless the FactoryBean always exposes the same reference.
	 * &lt;p>The singleton status of the FactoryBean itself will generally
	 * be provided by the owning BeanFactory; usually, it has to be
	 * defined as singleton there.
	 * @return if this bean is a singleton
	 * @see #getObject()
	 */
	boolean isSingleton();

}

</pre><br /><br />看了以后发现, FactoryBean 用于在 spring 容器中创建其他的 Bean, 我们平时用得最多的 JndiObjectFactoryBean, hibernate 的 LocalSessionFactoryBean 都是 FactoryBean 的具体实现, 既然如此, 读取动态配置就变得易如反掌了, 假如我们要实现动态读取数据库配置的功能, 拿使用率最高的 BasicDatasource 为例, 简单的实现一个  BasicDatasource FactoryBean 如下即可<br /><br /><pre name="code" class="java">
public class BasicDataSourceFactoryBean implements FactoryBean {

	public Object getObject() throws Exception {
		BasicDataSource dataSource = new BasicDataSource();
		
		// 读取外部配置, 设置到 dataSource 中 ...
		
		return dataSource;
	}

	public Class getObjectType() {
		return BasicDataSource.class;
	}

	public boolean isSingleton() {
		return true;
	}

}

</pre><br /><br />然后在 spring 中如此声明<br /><pre name="code" class="java">
&lt;bean id="dataSource" class="BasicDataSourceFactoryBean ">
   ... 你的配置来源
&lt;/bean> 
</pre><br />就这么简单
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/32019#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>Wed, 01 Nov 2006 17:43:08 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/32019</link>
        <guid>http://feiing.javaeye.com/blog/32019</guid>
      </item>
      <item>
        <title>一个可能的列级权限控制方案讨论</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/20549" style="color:red;">http://feiing.javaeye.com/blog/20549</a>&nbsp;
          发表时间: 2006年05月25日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          最近的项目需要做到列级权限控制,  大意如下<br /><br />[code:1]<br />public class DomainObject {<br />   private String attr1;<br />   private String attr2;<br />}<br />[/code:1]<br /><br />需求是 角色1 可对 attr1  进行读操作, 对 attr2 进行写操作,  而 角色2  对 attr1, attr2 都没有读权限.<br /><br />目前考虑的方案 : 主要是在 ui 上控制, 如果没有读权限, 则显示空或无权限, 如果只有读权限, 则控制输入框为只读.<br /><br />实现 : 建一张表用以纪录角色对 DomainObject   各属性的权限,  使用 webwork interceptor  拦截所有页面上的 ww 标签访问,  然后到配置表中查找权限信息,  如果查到匹配,  就将信息记录在一个集合中(类似 fieldError), 不妨称之为 fieldSecurityInfo,   最后更改标签模板,  根据 fieldSecurityInfo 中的信息做相应控制, 感觉大体可行.<br /><br />目前的问题是 :<br /><br />1. 如何拦截 &lt;ww:> 标签的访问 ?<br />2. 只在 ui 上控制是否足够安全? 如果需要后台控制,  觉得难度工作量更大.<br /><br /><br />有做过类似需求的同学,  请不吝发言 <img src="/images/smiles/icon_biggrin.gif"/>
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/20549#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, 25 May 2006 18:45:19 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/20549</link>
        <guid>http://feiing.javaeye.com/blog/20549</guid>
      </item>
      <item>
        <title>Spring 事务简化配置</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/19317" style="color:red;">http://feiing.javaeye.com/blog/19317</a>&nbsp;
          发表时间: 2006年03月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在 spring 中,  事务管理一般是通过声明一个 txProxyTemplate,  然后业务 bean 中 parent = "txProxyTemplate",  这样做未免显得有些繁琐,  并且如果业务 bean 还需要其他拦截器,  配置也不太方便,  下面贴出我的配置, 用 DefaultAdvisorAutoProxyCreator 实现自动代理<br /><br />[code:1]<br />&lt;beans><br />	&lt;!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) --><br />    &lt;bean id="transactionManager"<br />          class="org.springframework.orm.hibernate3.HibernateTransactionManager"><br />          &lt;property name="sessionFactory"><br />              &lt;ref bean="sessionFactory"/><br />          &lt;/property><br />    &lt;/bean>	<br />	<br />	&lt;bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"><br />    	&lt;property name="transactionManager" ref="transactionManager"/><br />		&lt;property name="transactionAttributeSource"><br />		  &lt;value><br />			com.skyon.user.manager.UserManager.*=PROPAGATION_REQUIRED<br />			#Add new defines here -><br />		  &lt;/value><br />		&lt;/property><br />	&lt;/bean><br />	<br />	&lt;bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"><br />		&lt;property name="interceptorNames"><br />			&lt;list><br />				&lt;value>transactionInterceptor&lt;/value><br />				&lt;!--<br />				增加新的 Interceptor<br />				--><br />			&lt;/list><br />		&lt;/property><br />	&lt;/bean><br /><br />	&lt;bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"><br />	  &lt;property name="transactionInterceptor" ref="transactionInterceptor"/><br />	&lt;/bean><br />		<br />&lt;/beans><br /><br />[/code:1]<br /><br />这里利用 DefaultAdvisorAutoProxyCreator 实现了对容器中所有 bean 的自动代理,  增加一个需要事务的业务 bean 时只要在 transactionInterceptor 增加一行即可,  增加别的 interceptor 也非常方便，<br />极大减少了配置量<img src="/images/smiles/icon_smile.gif"/>
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/19317#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, 21 Mar 2006 00:33:06 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/19317</link>
        <guid>http://feiing.javaeye.com/blog/19317</guid>
      </item>
      <item>
        <title>再论 Acegi 权限存储策略</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/18635" style="color:red;">http://feiing.javaeye.com/blog/18635</a>&nbsp;
          发表时间: 2006年02月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          本文原出处<br />http://starcraft.blogdriver.com/starcraft/1135045.html<br /><br />在我之前的一篇文章里, 说明了在 Acegi 中如何将资源权限数据存储到数据库中, 文章见 http://www.hibernate.org.cn/viewtopic.php?t=17538,<br />虽然文中方式实现了从数据库读取资源权限, 但是代码量较大, 并且重载了 SecurityEnforcementFilter, 造成比较大的侵入性, <br />这里我将提供另一种更简洁的方式实现此功能.<br /><br />入口还是 org.acegisecurity.intercept.web.FilterSecurityInterceptor, 资源权限配置来自于<br />objectDefinitionSource, 标准配置方式采用 FilterInvocationDefinitionSourceEditor 解析, 支持 Perl5 和 AntPath 两种风格, <br />为了实现从其它位置(典型如数据库), 我们要做的就是实现一个自定义的 FilterInvocationDefinitionSource, 查看类层次结构图后可以发现, <br />acegi 中已经有一个 AbstractFilterInvocationDefinitionSource 已经实现此接口, 只要实现一个抽象方法即可<br /><br />[code:1]<br />public abstract ConfigAttributeDefinition lookupAttributes(String url);<br />[/code:1]<br /><br />因此, 自定义一个 Class 如下 :<br />[code:1]<br />public class RdbmsBasedFilterInvocationDefinitionSource extends AbstractFilterInvocationDefinitionSource <br />[/code:1]<br /><br />因为 acegi 中已经提供了 Perl5 和 AntPath 的实现, 只需要集成过来即可, 因此定义接口如下<br />[code:1]<br /><br />/**<br /> * &lt;class>ConfigableFilterInvocationDefinition&lt;/class> 支持 Perl5 和 ant Path 两种风格的资源配置方式<br /> * @since 2006-1-19<br /> * @author 王政<br /> * @version $Id: ConfigableFilterInvocationDefinition.java,v 1.3 2006/01/19 09:40:37 wz Exp $<br /> */<br />public interface ConfigableFilterInvocationDefinition {<br />	<br />	/** The Perl5 expression  */<br />	String PERL5_KEY = "PATTERN_TYPE_PERL5";<br />    <br />    /** The ant path expression */<br />    String ANT_PATH_KEY = "PATTERN_TYPE_APACHE_ANT";<br />	<br />    /** 标准分隔符 */<br />    String STAND_DELIM_CHARACTER = ",";<br />    <br />    /**<br />     * Set resource expression, the value must be {@link #PERL5_KEY_REG_EXP} or {@link #ANT_PATH_KEY}<br />     * @see #REOURCE_EXPRESSION_PERL5_REG_EXP<br />     * @see #RESOURCE_EXPRESSION_ANT_PATH_KEY<br />     * @param resourceExpression the resource expression<br />     */<br />    void setResourceExpression(String resourceExpression);<br />    <br />    /**<br />     * <br />     * @return resource expression<br />     */<br />    String getResourceExpression();<br />    <br />    /**<br />     * Set whether convert url to lowercase before comparison<br />     * @param convertUrlToLowercaseBeforeComparison whether convertUrlToLowercaseBeforeComparison<br />     */<br />    void setConvertUrlToLowercaseBeforeComparison(boolean convertUrlToLowercaseBeforeComparison);<br />	<br />    /**<br />     * <br />     * @return whether convert url to lowercase before comparison<br />     */<br />    boolean isConvertUrlToLowercaseBeforeComparison();<br />   <br />}<br /><br />[/code:1]<br /><br />再让 RdbmsBasedFilterInvocationDefinitionSource 实现此接口即可, 下面是简略代码<br /><br />[code:1]<br />/**<br /> * &lt;class>RdbmsBasedFilterInvocationDefinitionSource&lt;/class> 是基于数据库的权限存储实现, 它支持两种风格的配置<br /> * @see com.skyon.uum.security.acegi.intercept.web.ConfigableFilterInvocationDefinition<br /> * @see org.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap<br /> * @see org.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap<br /> * @since 2006-1-19<br /> * @author 王政<br /> * @version $Id: RdbmsBasedFilterInvocationDefinitionSource.java,v 1.6 2006/02/13 03:20:55 wz Exp $<br /> */<br />public class RdbmsBasedFilterInvocationDefinitionSource extends AbstractFilterInvocationDefinitionSource <br />	implements ConfigableFilterInvocationDefinition, InitializingBean {<br />	<br />	<br />    //~ Static fields/initializers =============================================<br /><br />    private static final Log logger = LogFactory.getLog(RdbmsBasedFilterInvocationDefinitionSource.class);<br />	<br />    //	~ Instance fields ========================================================<br />	<br />    private String resourceExpression = PERL5_KEY;<br />    <br />    private boolean convertUrlToLowercaseBeforeComparison = false;<br />	    <br />    private ResourceMappingProvider resourceMappingProvider;<br />    <br />    //  ~ Methods ================================================================<br />    <br />	/**<br />	 * <br />	 * @see org.acegisecurity.intercept.web.AbstractFilterInvocationDefinitionSource#lookupAttributes(java.lang.String)<br />	 */<br />	public ConfigAttributeDefinition lookupAttributes(String url) {<br />		FilterInvocationDefinitionSource actualSource = populateFilterInvocationDefinitionSource();<br />		<br />		if (RegExpBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {<br />			return ((RegExpBasedFilterInvocationDefinitionMap) actualSource).lookupAttributes(url);<br />		} else if (PathBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {<br />			return ((PathBasedFilterInvocationDefinitionMap) actualSource).lookupAttributes(url);<br />		}<br />		<br />		throw new IllegalStateException("wrong type of " + actualSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class <br />				+ " or " + PathBasedFilterInvocationDefinitionMap.class);		<br />	}<br />	<br />	/**<br />	 * <br />	 * @see org.acegisecurity.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions()<br />	 */<br />	public Iterator getConfigAttributeDefinitions() {<br />		FilterInvocationDefinitionSource actualSource = populateFilterInvocationDefinitionSource();<br />		<br />		if (RegExpBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {<br />			return ((RegExpBasedFilterInvocationDefinitionMap) actualSource).getConfigAttributeDefinitions();<br />		} else if (PathBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {<br />			return ((PathBasedFilterInvocationDefinitionMap) actualSource).getConfigAttributeDefinitions();<br />		}<br />		<br />		throw new IllegalStateException("wrong type of " + actualSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class <br />				+ " or " + PathBasedFilterInvocationDefinitionMap.class);	<br />	}<br />	<br />	<br />    private FilterInvocationDefinitionSource populateFilterInvocationDefinitionSource() {            	<br />    	FilterInvocationDefinitionMap definitionSource = null;<br />    	<br />    	if (PERL5_KEY.equals(getResourceExpression())) {<br />    		definitionSource = new RegExpBasedFilterInvocationDefinitionMap();<br />    	} else if (ANT_PATH_KEY.equals(getResourceExpression())) {<br />    		definitionSource = new PathBasedFilterInvocationDefinitionMap();<br />    	} else {<br />    		throw new IllegalArgumentException("wrong resourceExpression value");<br />    	}<br />        <br />        definitionSource.setConvertUrlToLowercaseBeforeComparison(isConvertUrlToLowercaseBeforeComparison());<br />        <br />        ResourceMapping[] mappings = getResourceMappingProvider().getResourceMappings();<br />        if (mappings == null || mappings.length ==0) {<br />            return (FilterInvocationDefinitionSource) definitionSource;<br />        }<br />        <br />        for (int i = 0; i &lt; mappings.length; i++) {<br />            ResourceMapping mapping = mappings[i];<br />            String[] recipents = mapping.getRecipients();<br />            <br />            if (recipents == null || recipents.length == 0) {<br />                if (logger.isErrorEnabled()) {<br />                    logger.error("Notice, the resource : " + mapping.getResourcePath() + " hasn't no recipents, it will access by any one ! ");<br />                }<br />                continue;<br />            }<br />            <br />            StringBuffer valueBuffer = new StringBuffer();<br />            for (int j = 0; j &lt; recipents.length; j++) {<br />                valueBuffer.append(recipents[j]);<br />                if (j &lt; recipents.length - 1) {<br />                    valueBuffer.append(STAND_DELIM_CHARACTER);<br />                }<br />            }<br />            String value = valueBuffer.toString();                    <br />            addSecureUrl(definitionSource, mapping.getResourcePath(), value);<br />         }<br />     <br />        return (FilterInvocationDefinitionSource )definitionSource;<br />    }<br />	<br />    <br />    /**<br />     * @param source<br />     * @param name<br />     * @param value<br />     * @throws IllegalArgumentException<br />     */<br />    private synchronized void addSecureUrl(FilterInvocationDefinitionMap source, String name, String value) <br />        throws IllegalArgumentException {<br />        <br />        // Convert value to series of security configuration attributes<br />        ConfigAttributeEditor configAttribEd = new ConfigAttributeEditor();<br />        configAttribEd.setAsText(value);<br /><br />        ConfigAttributeDefinition attr = (ConfigAttributeDefinition) configAttribEd.getValue();<br /><br />        // Register the regular expression and its attribute<br />        source.addSecureUrl(name, attr);<br />    }<br />    <br />    省去 getter， setter....<br /><br />}<br /><br />[/code:1]<br /><br />ResourceMappingProvider <br /><br />[code:1]<br />public interface ResourceMappingProvider {<br />    <br />	String RESOURCE_PATH_PREFIX = "/";<br />	<br />    /**<br />     * Get Resource Mapping<br />     * @return resource mapping<br />     */<br />    ResourceMapping[] getResourceMappings();<br />    <br />}<br /><br />public class ResourceMapping {<br />    <br />    // url<br />    private String resourcePath;<br />    <br />    // 即角色<br />    private String[] recipients = new String[0];<br />       <br />    省去 getter， setter....<br /><br />}<br /><br />[/code:1]<br /><br />这样就很完美的既支持了从数据库的读取数据, 又可以自由选择 Perl5 和 AntPath 两种风格的配置, 最后不要忘了给 ResourceMappingProvider 加一层 cache, 我的配置如下<br /><br />[code:1]<br />&lt;bean id="resourceCache" class="com.skyon.uum.security.acegi.intercept.web.cache.EhCacheBasedResourceCache"><br />		&lt;property name="dataSource"><br />			&lt;ref bean="dataSource">&lt;/ref><br />		&lt;/property><br />		&lt;property name="cache"><br />			&lt;bean parent="cacheTemplate">				<br />				&lt;property name="cacheName">&lt;value>resourceCache&lt;/value>&lt;/property><br />			&lt;/bean><br />        &lt;/property><br />		&lt;property name="allResourcesQuery"><br />			&lt;value><br />			select distinct t.id, t.id, t.parent_id, t.url, t.title, t.layer, t.type, t.application_id<br />			from uum_resource t order by t.orderField<br />			&lt;/value><br />		&lt;/property><br />	&lt;/bean><br />	<br />	&lt;bean id="permissionCache" class="com.skyon.uum.security.acegi.intercept.web.cache.EhCacheBasedPermissionCache"><br />		&lt;property name="dataSource"><br />			&lt;ref bean="dataSource">&lt;/ref><br />		&lt;/property><br />		&lt;property name="cache"><br />			&lt;bean parent="cacheTemplate">				<br />				&lt;property name="cacheName">&lt;value>permissionCache&lt;/value>&lt;/property><br />			&lt;/bean><br />        &lt;/property><br />		&lt;property name="recipentsResourceMappingQuery"><br />			&lt;value><br />			select r.name, p.resource_id from uum_permission p left outer join uum_role r on p.role_id = r.id<br />			&lt;/value><br />		&lt;/property>	<br />	&lt;/bean><br />	<br />			<br />	&lt;bean id="resourceMappingProvider" class="com.skyon.uum.security.acegi.intercept.web.ResourceMappingProviderImpl" autowire="byType"/><br /><br />    &lt;!-- ======================== AUTHENTICATION ======================= --><br />    <br />    &lt;!-- Note the order that entries are placed against the objectDefinitionSource is critical.<br />         The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL.<br />         Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last --><br />    &lt;bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"><br />        &lt;property name="authenticationManager">&lt;ref local="authenticationManager"/>&lt;/property><br />        &lt;property name="accessDecisionManager">&lt;ref local="accessDecisionManager"/>&lt;/property><br />        &lt;property name="objectDefinitionSource">&lt;ref local="filterInvocationDefinitionSource">&lt;/ref>&lt;/property><br />    &lt;/bean><br />	<br />	&lt;bean id="filterInvocationDefinitionSource" class="com.skyon.uum.security.acegi.intercept.web.RdbmsBasedFilterInvocationDefinitionSource"><br />		&lt;property name="resourceMappingProvider"><br />			&lt;ref local="resourceMappingProvider">&lt;/ref><br />		&lt;/property><br />		&lt;property name="resourceExpression"><br />			&lt;value>PATTERN_TYPE_APACHE_ANT&lt;/value><br />		&lt;/property><br />	&lt;/bean><br /><br />[/code:1]<br /><br />这段时间看了很多人对 Acegi 的评价, 有不少观点认为 Acegi 的配置太过繁琐, 其实权限控制本来就不是一件很轻松的事, Acegi 用 AOP 实现, 配置文件的确有些繁琐,<br />但是只要一个配置文件就解决了整个系统的权限问题, 可谓一劳永逸, 相比较在 Action 中实现应该还是利远大于弊, 也有人说用 WebWork 的 Interceptor 实现, 虽然也是不错的 solution, <br />但是不要忘了, 并不是所有的项目都使用 webwork , 假如有一个 struts 的项目, 权限控制就会有移植性的问题.<br /><br />我的 msn : shartcn@msn.com, 有问题欢迎讨论
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/18635#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, 18 Feb 2006 00:17:19 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/18635</link>
        <guid>http://feiing.javaeye.com/blog/18635</guid>
      </item>
      <item>
        <title>以前写的一篇介绍 Acegi 的文档</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/17921" style="color:red;">http://feiing.javaeye.com/blog/17921</a>&nbsp;
          发表时间: 2006年01月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          半年前写的,  版本是 0.8.3, 主要是翻译了一些 reference guide, 希望更多的人了解 Acegi
          <br/>
          <span style="color:red;">
            <a href="http://feiing.javaeye.com/blog/17921#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, 05 Jan 2006 09:51:19 +0800</pubDate>
        <link>http://feiing.javaeye.com/blog/17921</link>
        <guid>http://feiing.javaeye.com/blog/17921</guid>
      </item>
      <item>
        <title>Acegi 资源配置动态扩展实现</title>
        <author>Feiing</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://feiing.javaeye.com">Feiing</a>&nbsp;
          链接：<a href="http://feiing.javaeye.com/blog/17538" style="color:red;">http://feiing.javaeye.com/blog/17538</a>&nbsp;
          发表时间: 2005年12月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          本文原出处 : <a href="http://starcraft.blogdriver.com/starcraft/1089862.html" target="_blank">http://starcraft.blogdriver.com/starcraft/1089862.html</a><br /><br /><strong>Acegi 资源配置动态扩展实现</strong><br /><br />王 政 (Feiing) 于 2005-12-11<br /><br /><strong><span style="color: blue">1. 问题提出</span></strong><br /><br />在使用 Acegi Security Framework 的过程中, 如果细心的话, 会发现其资源和角色配置是在配置文件中的, 下面是 Appfuse 中相关配置 : <br />[code:1]<br />    &lt;bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"><br />        &lt;property name="authenticationManager">&lt;ref local="authenticationManager"/>&lt;/property><br />        &lt;property name="accessDecisionManager">&lt;ref local="accessDecisionManager"/>&lt;/property><br />         &lt;property name="objectDefinitionSource"><br />            &lt;value><br />                CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br />                PATTERN_TYPE_APACHE_ANT<br />                /signup.html=ROLE_ANONYMOUS,admin,tomcat<br />  <br />                /clickstreams.jsp=admin<br />            &lt;/value><br />        &lt;/property><br />    &lt;/bean><br /><br />[/code:1]<br />上面的配置从功能上实现了资源与角色的映射, 但用户可能会提出在运行期动态改变权限分配的需求, 配置文件策略可能略显不足, 下面我将提供一种基于数据库的策略解决此问题.<br /><br /><strong><span style="color: blue">2. E-R 模型</span></strong><br /><br />下图是需要的 E-R 模型<br /><br /><img src="http://forum.javaeye.com/files/security.jpg" /><br />图1 Acegi 标准 RBAC E-R设计<br /><br />图中的用户与角色不再多做解释, 我们主要关注一下 Permission 表 和 Resource 表, 这里 Resource 表用于存储系统资源, 在 web 层一般来说就是 url, 如果使用 acl, 就是 aclClass, 此时 Permission 表中的 aclMask 用来存储对应的 acl 权限, 考虑到 acl 在 web 项目中使用率不高, 下面我将着重介绍 web 层的权限控制, 对 acl 有兴趣的读者可以自己参阅 Acegi Reference Guide.<br /><br /><br /><strong><span style="color: blue">3. 如何阻止 acegi 从配置文件读取权限配置<br /></span></strong><br /><br /><br />从 Appfuse 中的示例性配置可以看出, acegi 对权限配置的要求是 “ 资源 = 角色1, 角色2 …  角色 n ”, 看过源代码的读者应该知道,  最终这些配置将被组装为 net.sf.acegisecurity.intercept. ObjectDefinitionSource(web 层对应的实现是 net.sf.acegisecurity.intercept.web. FilterInvocationDefinitionSource), 那么我们怎么才能用数据库的数据来组装 FilterInvocationDefinitionSource ? 这里涉及到一个 PropertyEditor 问题, 在 Acegi 中, FilterInvocationDefinitionSource 是通过 net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSourceEditor 组装的, 假如我们不想让 FilterInvocationDefinitionSourceEditor 从配置文件中读取权限配置, 就需要自己实现一个 ProdertyEditor 来覆盖默认实现, 下面是我的 配置 :<br /><br /><img src="http://forum.javaeye.com/files/customeditorconfigurer.jpg" /><br />图2 customerEditorConfigurer 配置<br /><br />那么, 这个 PropertyEditor 中需要做些什么呢 ? 要做的就是使用一个比较特殊的标记, 当遇到这个特殊标记的时候直接略过解析,  我这里使用的标记是 “DONT_USE_ME”, 然后在 PropertyEditor 中简单的如下实现即可:<br /><br />[code:1]<br />/*<br /> * Copyright 2004-2005 wangz.<br /> * Project shufe_newsroom<br /> */<br />package com.skyon.um.security.acegi.intercept.web;<br /><br />import java.beans.PropertyEditorSupport;<br />import java.io.BufferedReader;<br />import java.io.IOException;<br />import java.io.StringReader;<br /><br />import net.sf.acegisecurity.ConfigAttributeDefinition;<br />import net.sf.acegisecurity.ConfigAttributeEditor;<br />import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionMap;<br />import net.sf.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap;<br />import net.sf.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap;<br /><br />import org.apache.commons.lang.StringUtils;<br />import org.apache.commons.logging.Log;<br />import org.apache.commons.logging.LogFactory;<br /><br />/**<br /> * @since 2005-8-4<br /> * @author 王政<br /> * @version $Id: FilterInvocationDefinitionSourceDynamicExtentionEditor.java,v 1.2 2005/11/04 15:55:07 wangzheng Exp $<br /> */<br />public class FilterInvocationDefinitionSourceDynamicExtentionEditor extends<br />        PropertyEditorSupport {<br /><br />    public static final String ANT_PATH_KEY = "PATTERN_TYPE_APACHE_ANT";<br />    <br />    public static final String LOWER_CASE_URL_KEY = "CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON";<br /><br />    public static final String DONT_USE_ME_KEY = "DONT_USE_ME";<br />    <br />    public static final String STAND_DELIM_CHARACTER = ",";<br /><br />    private static final Log logger = LogFactory.getLog(FilterInvocationDefinitionSourceDynamicExtentionEditor.class);<br />    <br />    /**<br />     * @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)<br />     */<br />    public void setAsText(String text) throws IllegalArgumentException {                <br />        FilterInvocationDefinitionMap source = new RegExpBasedFilterInvocationDefinitionMap();<br />               <br />        if (StringUtils.isBlank(text)) {<br />            // Leave target object empty<br />        } else {         <br />            // Check if we need to override the default definition map<br />            if (text.lastIndexOf(ANT_PATH_KEY) != -1) {<br />                source = new PathBasedFilterInvocationDefinitionMap();<br /><br />                if (logger.isDebugEnabled()) {<br />                    logger.debug(("Detected PATTERN_TYPE_APACHE_ANT directive; using Apache Ant style path expressions"));<br />                }<br />            }<br /><br />            if (text.lastIndexOf(LOWER_CASE_URL_KEY) != -1) {<br />                if (logger.isDebugEnabled()) {<br />                    logger.debug("Instructing mapper to convert URLs to lowercase before comparison");<br />                }<br /><br />                source.setConvertUrlToLowercaseBeforeComparison(true);<br />            }<br />            <br />            if (text.indexOf(DONT_USE_ME_KEY) != -1) {<br />                if (logger.isDebugEnabled()) {<br />                    logger.debug("DETECTED " + DONT_USE_ME_KEY + " directive;  skip parse, Use " + EhCacheBasedFilterInvocationDefinitionSourceCache.class + " to parse!");<br />                }<br />                addSecureUrl(source, "/dontuseme", "dontuseme");<br />            } else {<br />                BufferedReader br = new BufferedReader(new StringReader(text));<br />                int counter = 0;<br />                String line;<br /><br />                while (true) {<br />                    counter++;<br /><br />                    try {<br />                        line = br.readLine();<br />                    } catch (IOException ioe) {<br />                        throw new IllegalArgumentException(ioe.getMessage());<br />                    }<br /><br />                    if (line == null) {<br />                        break;<br />                    }<br /><br />                    line = line.trim();<br /><br />                    if (logger.isDebugEnabled()) {<br />                        logger.debug("Line " + counter + ": " + line);<br />                    }<br /><br />                    if (line.startsWith("//")) {<br />                        continue;<br />                    }<br /><br />                    if (line.equals(LOWER_CASE_URL_KEY)) {<br />                        continue;<br />                    }<br /><br />                    if (line.lastIndexOf('=') == -1) {<br />                        continue;<br />                    }<br /><br />                    // Tokenize the line into its name/value tokens<br />                    String[] nameValue = org.springframework.util.StringUtils.delimitedListToStringArray(line, "=");<br />                    String name = nameValue[0];<br />                    String value = nameValue[1];<br /><br />                    addSecureUrl(source, name, value);                <br />                }<br />            }<br />            <br /><br /><br />        }<br /><br />        setValue(source);<br />    }<br />    <br /><br />    /**<br />     * @param source<br />     * @param name<br />     * @param value<br />     * @throws IllegalArgumentException<br />     */<br />    private void addSecureUrl(FilterInvocationDefinitionMap source, String name, String value) <br />        throws IllegalArgumentException {<br />        <br />        // Convert value to series of security configuration attributes<br />        ConfigAttributeEditor configAttribEd = new ConfigAttributeEditor();<br />        configAttribEd.setAsText(value);<br /><br />        ConfigAttributeDefinition attr = (ConfigAttributeDefinition) configAttribEd.getValue();<br /><br />        // Register the regular expression and its attribute<br />        source.addSecureUrl(name, attr);<br />    }<br /><br /><br />}<br /><br />[/code:1]<br /><br />Ok, 现在 FilterInvocationDefinitionSourceDynamicExtentionEditor 遇到配置文件中的 “DONT_USE_ME” 时将直接略过, 下面是我的 filterInvocationInterceptor 配置:<br /><img src="http://forum.javaeye.com/files/filterinvocationinterceptor.jpg" /><br />图3 filterInvocationInterceptor 配置<br /><br />现在, 我们已经成功阻止 acegi 从配置文件读取权限配置, 下一个问题就是:<br /><br /><br /><strong><span style="color: blue">4. 如何从表中数据组装 FilterInvocationDefinitionSource<br /></span></strong><br /><br /><br />为了实现此功能, 需要一个自定义的资源定义接口来提供 FilterInvocationDefinitionSource,  此接口可能会是这样 :<br /><br />[code:1]<br />/*<br /> * Copyright 2005-2010 the original author or autors<br /> *  <br /> *    http://www.skyon.com.cn<br /> *<br /> * Project { SkyonFramwork }<br /> */<br />package com.skyon.um.security.acegi.intercept.web;<br /><br />import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;<br /><br />import org.springframework.beans.factory.FactoryBean;<br /><br />import com.skyon.framework.spring.ehcache.FlushableCache;<br /><br />/**<br /> * &lt;class>FilterInvocationDefinitionSourceCache&lt;/class> use to hold the global FilterInvocationDefinitionSource,<br /> * it keeps a static variable , if the source been changed(generally the database data),  the reload method should be called<br /> * <br /> * @see com.skyon.um.security.acegi.intercept.event.FilterInvocationDefinitionSourceChangedEvent<br /> * @see com.skyon.um.security.acegi.intercept.event.FilterInvocationDefinitionSourceListener<br /> * @see com.skyon.um.security.acegi.intercept.web.SecurityEnforcementDynamicExtensionFilter<br /> * @since 2005-8-7<br /> * @author 王政<br /> * @version $Id: FilterInvocationDefinitionSourceCache.java,v 1.1 2005/11/04 15:55:07 wangzheng Exp $<br /> */<br />public interface FilterInvocationDefinitionSourceCache extends FactoryBean, FlushableCache {<br />	<br />	/** The Perl5 expression  */<br />    int REOURCE_EXPRESSION_PERL5_REG_EXP = 1;<br />    <br />    /** The ant path expression */<br />    int RESOURCE_EXPRESSION_ANT_PATH_KEY = 2;<br />    <br />    /**<br />     * Set resource expression, the value must be {@link #REOURCE_EXPRESSION_PERL5_REG_EXP} or {@link #RESOURCE_EXPRESSION_ANT_PATH_KEY}<br />     * @see #REOURCE_EXPRESSION_PERL5_REG_EXP<br />     * @see #RESOURCE_EXPRESSION_ANT_PATH_KEY<br />     * @param resourceExpression the resource expression<br />     */<br />    void setResourceExpression(int resourceExpression);<br />    <br />    /**<br />     * Set whether convert url to lowercase before comparison<br />     * @param convertUrlToLowercaseBeforeComparison whether convertUrlToLowercaseBeforeComparison<br />     */<br />    void setConvertUrlToLowercaseBeforeComparison(boolean convertUrlToLowercaseBeforeComparison);<br />    <br />	/**<br />	 * Get the defination source, generally from a database schema<br />	 * @return the defination source<br />	 */<br />    FilterInvocationDefinitionSource getFilterInvocationDefinitionSource();<br />           <br /> <br />}<br />[/code:1]<br /><br />其核心方法是  FilterInvocationDefinitionSource getFilterInvocationDefinitionSource(), 此方法将代替配置文件提供资源和角色的配置, 下面是实现<br />[code:1]<br />/*<br /> * Copyright 2005-2010 the original author or autors<br /> *  <br /> *    http://www.skyon.com.cn<br /> *<br /> * Project { SkyonFramwork }<br /> */<br />package com.skyon.um.security.acegi.intercept.web;<br /><br />import net.sf.acegisecurity.ConfigAttributeDefinition;<br />import net.sf.acegisecurity.ConfigAttributeEditor;<br />import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionMap;<br />import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;<br />import net.sf.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap;<br />import net.sf.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap;<br />import net.sf.ehcache.Cache;<br />import net.sf.ehcache.Element;<br /><br />import org.apache.commons.logging.Log;<br />import org.apache.commons.logging.LogFactory;<br />import org.springframework.beans.factory.InitializingBean;<br />import org.springframework.util.Assert;<br /><br />import com.skyon.framework.spring.ehcache.CacheUtils;<br />import com.skyon.framework.spring.ehcache.SerializableObjectProvider;<br />import com.skyon.framework.spring.support.MandatorySingletonBeanSupport;<br /><br />/**<br /> * @since 2005-8-7<br /> * @author 王政<br /> * @version $Id: EhCacheBasedFilterInvocationDefinitionSourceCache.java,v 1.2 2005/11/17 09:38:25 wangzheng Exp $<br /> */<br />public class EhCacheBasedFilterInvocationDefinitionSourceCache extends MandatorySingletonBeanSupport <br />	implements FilterInvocationDefinitionSourceCache, InitializingBean {<br />    <br />    private static final Log logger = LogFactory.getLog(EhCacheBasedFilterInvocationDefinitionSourceCache.class);<br />    <br />    private int resourceExpression;<br />    <br />    private boolean convertUrlToLowercaseBeforeComparison = false;<br />        <br />    private ResourceMappingProvider resourceMappingProvider;<br />    <br />    private Cache cache;<br />    <br />    private Object lock = new Object();<br />    <br />    /**<br />     * @see com.skyon.um.security.acegi.intercept.web.FilterInvocationDefinitionSourceCache#getFilterInvocationDefinitionSource()<br />     */<br />    public FilterInvocationDefinitionSource getFilterInvocationDefinitionSource() {<br />    	synchronized (lock) {<br />        	Element element = CacheUtils.get(getCache(), "key");<br />        	<br />        	if (element == null) {<br />        		FilterInvocationDefinitionSource definitionSource = (FilterInvocationDefinitionSource) getFilterInvocationDefinitionSourceFromBackend();<br />        		element = new Element("key", new SerializableObjectProvider(definitionSource));<br />        		getCache().put(element);<br />        	}<br />        	<br />        	return (FilterInvocationDefinitionSource) ((SerializableObjectProvider) element.getValue()).getSourceObject();<br />    	}<br />    }<br />            <br />	public void flushCache() {<br />		CacheUtils.flushCache(getCache());<br />		getFilterInvocationDefinitionSource();<br />	}<br />    <br />    private FilterInvocationDefinitionMap getFilterInvocationDefinitionSourceFromBackend() {        <br />    	logger.info(" 开始加载系统资源权限数据到缓存... ");<br />    	<br />    	FilterInvocationDefinitionMap definitionSource = null;<br />        <br />    	switch (resourceExpression) {<br />            case REOURCE_EXPRESSION_PERL5_REG_EXP : {<br />                definitionSource = new RegExpBasedFilterInvocationDefinitionMap();<br />                break;<br />            }<br />            case RESOURCE_EXPRESSION_ANT_PATH_KEY : {<br />                definitionSource = new PathBasedFilterInvocationDefinitionMap();<br />                break;<br />            }<br />            default : {<br />                throwException();<br />            }<br />        }<br />        <br />        definitionSource.setConvertUrlToLowercaseBeforeComparison(isConvertUrlToLowercaseBeforeComparison());<br />        <br />        ResourceMapping[] mappings = getResourceMappingProvider().getResourceMappings();<br />        if (mappings == null || mappings.length ==0) {<br />            return definitionSource;<br />        }<br />        <br />        for (int i = 0; i &lt; mappings.length; i++) {<br />            ResourceMapping mapping = mappings[i];<br />            String[] recipents = mapping.getRecipients();<br />            <br />            if (recipents == null || recipents.length == 0) {<br />                if (logger.isErrorEnabled()) {<br />                    logger.error("Notice, the resource : " + mapping.getResourcePath() + " has no recipents, it will access by any one ! ");<br />                }<br />                continue;<br />            }<br />            <br />            StringBuffer valueBuffer = new StringBuffer();<br />            for (int j = 0; j &lt; recipents.length; j++) {<br />                valueBuffer.append(recipents[j]);<br />                if (j &lt; recipents.length - 1) {<br />                    valueBuffer.append(FilterInvocationDefinitionSourceDynamicExtentionEditor.STAND_DELIM_CHARACTER);<br />                }<br />            }<br />            String value = valueBuffer.toString();                    <br />            addSecureUrl(definitionSource, mapping.getResourcePath(), value);<br />         }<br />        <br />        logger.info(" 成功加载系统资源权限数据到缓存 ! ");<br 