﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-空间站-文章分类-Spring</title><link>http://www.blogjava.net/i369/category/16872.html</link><description>北极心空</description><language>zh-cn</language><lastBuildDate>Fri, 14 Mar 2008 03:37:13 GMT</lastBuildDate><pubDate>Fri, 14 Mar 2008 03:37:13 GMT</pubDate><ttl>60</ttl><item><title>谈谈Spring 2.x中简化配置的问题</title><link>http://www.blogjava.net/i369/articles/186191.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Fri, 14 Mar 2008 01:44:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/186191.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/186191.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/186191.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/186191.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/186191.html</trackback:ping><description><![CDATA[Spring 2.x在配置文件的简化的方面做了很多工作，原来1.x中比较麻烦的配置都已经拥有了比较完美的解决方案。最近刚看完《精通Spring 2.x --企业应用开发精解》的书，结合自己的经验整理一下简化配置的内容。 <br />
<br />
<strong>一、关于集合的配置</strong> <br />
<em>1.List</em> <br />
&gt;1.x版本的 <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;bean&nbsp;id=</span><span class="string">"parentBoss"</span><span>&nbsp;</span><span class="keyword">abstract</span><span>=</span><span class="string">"true"</span><span class="keyword">class</span><span>=</span><span class="string">"com.baobaotao.attr.Boss"</span><span>&gt;&nbsp;&lt;--父&lt;bean&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name=</span><span class="string">"favorites"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;set&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;看报&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;赛车&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;高尔夫&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/set&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;/bean&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;bean id="parentBoss" abstract="true"class="com.baobaotao.attr.Boss"&gt; &lt;--父&lt;bean&gt;
&lt;property name="favorites"&gt;
&lt;set&gt;
&lt;value&gt;看报&lt;/value&gt;
&lt;value&gt;赛车&lt;/value&gt;
&lt;value&gt;高尔夫&lt;/value&gt;
&lt;/set&gt;
&lt;/property&gt;
&lt;/bean&gt;
</pre>
<br />
<br />
&gt;2.x版本的 <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;util:list&nbsp;id=</span><span class="string">"favoriteList1"</span><span>&nbsp;list-</span><span class="keyword">class</span><span>=</span><span class="string">"java.util.LinkedList"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;看报&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;赛车&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;高尔夫&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;/util:list&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;util:list id="favoriteList1" list-class="java.util.LinkedList"&gt;
&lt;value&gt;看报&lt;/value&gt;
&lt;value&gt;赛车&lt;/value&gt;
&lt;value&gt;高尔夫&lt;/value&gt;
&lt;/util:list&gt;</pre>
<br />
<br />
<em>2.Set</em> <br />
&gt; 1.x <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;bean&nbsp;id=</span><span class="string">"boss1"</span><span>&nbsp;</span><span class="keyword">class</span><span>=</span><span class="string">"com.baobaotao.attr.Boss"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name=</span><span class="string">"favorites"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;set&gt;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;看报&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;赛车&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;高尔夫&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/set&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;/bean&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;bean id="boss1" class="com.baobaotao.attr.Boss"&gt;
&lt;property name="favorites"&gt;
&lt;set&gt;
&lt;value&gt;看报&lt;/value&gt;
&lt;value&gt;赛车&lt;/value&gt;
&lt;value&gt;高尔夫&lt;/value&gt;
&lt;/set&gt;
&lt;/property&gt;
&lt;/bean&gt;</pre>
<br />
&gt; 2.x <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;util:set&nbsp;id=</span><span class="string">"favoriteSet1"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&lt;value&gt;看报&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&lt;value&gt;赛车&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&lt;value&gt;高尔夫&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;/util:set&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">
&lt;util:set id="favoriteSet1"&gt;
&lt;value&gt;看报&lt;/value&gt;
&lt;value&gt;赛车&lt;/value&gt;
&lt;value&gt;高尔夫&lt;/value&gt;
&lt;/util:set&gt;
</pre>
<br />
<em>3.Map</em> <br />
&gt; 1.x <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;bean&nbsp;id=</span><span class="string">"boss1"</span><span>&nbsp;</span><span class="keyword">class</span><span>=</span><span class="string">"com.baobaotao.attr.Boss"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name=</span><span class="string">"jobs"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;map&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--Map第一个元素--&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry&gt;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;key&gt;&lt;value&gt;AM&lt;/value&gt;&lt;/key&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;会见客户&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/entry&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--Map第二个元素--&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry&gt;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;key&gt;&lt;value&gt;PM&lt;/value&gt;&lt;/key&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;公司内部会议&lt;/value&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/entry&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/map&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;/bean&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;bean id="boss1" class="com.baobaotao.attr.Boss"&gt;
&lt;property name="jobs"&gt;
&lt;map&gt;
&lt;!--Map第一个元素--&gt;
&lt;entry&gt;
&lt;key&gt;&lt;value&gt;AM&lt;/value&gt;&lt;/key&gt;
&lt;value&gt;会见客户&lt;/value&gt;
&lt;/entry&gt;
&lt;!--Map第二个元素--&gt;
&lt;entry&gt;
&lt;key&gt;&lt;value&gt;PM&lt;/value&gt;&lt;/key&gt;
&lt;value&gt;公司内部会议&lt;/value&gt;
&lt;/entry&gt;
&lt;/map&gt;
&lt;/property&gt;
&lt;/bean&gt;</pre>
<br />
&gt; 2.x <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;util:map&nbsp;id=</span><span class="string">"emails1"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry&nbsp;key=</span><span class="string">"AM"</span><span>&nbsp;value=</span><span class="string">"会见客户"</span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry&nbsp;key=</span><span class="string">"PM"</span><span>&nbsp;value=</span><span class="string">"公司内部会议"</span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;/util:map&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;util:map id="emails1"&gt;
&lt;entry key="AM" value="会见客户" /&gt;
&lt;entry key="PM" value="公司内部会议" /&gt;
&lt;/util:map&gt;
</pre>
<br />
<br />
<em>4. Properties</em> <br />
<br />
&gt; 1.x <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;bean&nbsp;id=</span><span class="string">"boss1"</span><span>&nbsp;</span><span class="keyword">class</span><span>=</span><span class="string">"com.baobaotao.attr.Boss"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name=</span><span class="string">"mails"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;props&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;prop&nbsp;key=</span><span class="string">"jobMail"</span><span>&gt;john-office</span><span class="annotation">@baobaotao</span><span>.com&lt;/prop&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;prop&nbsp;key=</span><span class="string">"lifeMail"</span><span>&gt;john-life</span><span class="annotation">@baobaotao</span><span>.com&lt;/prop&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/props&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;/bean&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;bean id="boss1" class="com.baobaotao.attr.Boss"&gt;
&lt;property name="mails"&gt;
&lt;props&gt;
&lt;prop key="jobMail"&gt;john-office@baobaotao.com&lt;/prop&gt;
&lt;prop key="lifeMail"&gt;john-life@baobaotao.com&lt;/prop&gt;
&lt;/props&gt;
&lt;/property&gt;
&lt;/bean&gt;
</pre>
<br />
<br />
&gt; 2.x <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;util:properties&nbsp;id=</span><span class="string">"emailProps1"</span><span>&nbsp;location=</span><span class="string">"classpath:com/baobaotao/fb/mails.properties"</span><span>/&gt;&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">  &lt;util:properties id="emailProps1" location="classpath:com/baobaotao/fb/mails.properties"/&gt;
</pre>
<br />
可以在一个属性文件中直接配置属性，这比较符合一般的项目习惯。 <br />
<br />
<strong>二、 关于事务配置</strong> <br />
<br />
<br />
1.1.x <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&nbsp;&lt;bean&nbsp;id=</span><span class="string">"bbtForum"</span><span>&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span class="keyword">class</span><span>=</span><span class="string">"org.springframework.transaction.interceptor.TransactionProxyFactoryBean"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;property&nbsp;name=</span><span class="string">"transactionManager"</span><span>&nbsp;ref=</span><span class="string">"txManager"</span><span>&nbsp;/&gt;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;property&nbsp;name=</span><span class="string">"target"</span><span>&nbsp;ref=</span><span class="string">"bbtForumTarget"</span><span>/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;property&nbsp;name=</span><span class="string">"transactionAttributes"</span><span>&gt;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;props&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;prop&nbsp;key=</span><span class="string">"get*"</span><span>&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;prop&nbsp;key=</span><span class="string">"*"</span><span>&gt;PROPAGATION_REQUIRED&lt;/prop&gt;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/props&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;/property&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&lt;/bean&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">  &lt;bean id="bbtForum"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt;
&lt;property name="transactionManager" ref="txManager" /&gt;
&lt;property name="target" ref="bbtForumTarget"/&gt;
&lt;property name="transactionAttributes"&gt;
&lt;props&gt;
&lt;prop key="get*"&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;
&lt;prop key="*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;
&lt;/props&gt;
&lt;/property&gt;
&lt;/bean&gt;
</pre>
<br />
<br />
2.2.x <br />
<br />
有两种新方法 <br />
a)使用@Transactional注解 <br />
在需要的服务类或服务方法处直接打上@Transactional注解，然后在Spring配置文件中启用注解事务驱动就可以了： <br />
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span class="annotation">@Transactional</span><span>&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;BbtForumImpl&nbsp;</span><span class="keyword">implements</span><span>&nbsp;BbtForum&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="annotation">@Transactional</span><span>(readOnly=</span><span class="keyword">true</span><span>)&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;Forum&nbsp;getForum(</span><span class="keyword">int</span><span>&nbsp;forumId)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;forumDao.getForum(forumId); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;。。。。 &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">@Transactional
public class BbtForumImpl implements BbtForum {
@Transactional(readOnly=true)
public Forum getForum(int forumId) {
return forumDao.getForum(forumId);
}
。。。。
}
</pre>
<br />
在Spring配置文件中相应添加上： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;bean&nbsp;id=</span><span class="string">"txManager"</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">class</span><span>=</span><span class="string">"org.springframework.jdbc.datasource.DataSourceTransactionManager"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name=</span><span class="string">"dataSource"</span><span>&nbsp;ref=</span><span class="string">"dataSource"</span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;/bean&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;tx:annotation-driven&nbsp;transaction-manager=</span><span class="string">"txManager"</span><span>/&gt;&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;
&lt;property name="dataSource" ref="dataSource" /&gt;
&lt;/bean&gt;
&lt;tx:annotation-driven transaction-manager="txManager"/&gt;
</pre>
<br />
这样就OK了，简单吧：） <br />
<br />
b)使用aop/tx <br />
<br />
如果你的Service服务类都很规范，我觉得使用aop/tx更方面，因为不用到处打注解，在一处集中配置就OK了，可谓运筹帷幄之中，决胜于千里之外：） <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;aop:config&gt;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>t;aop:pointcut&nbsp;id=</span><span class="string">"serviceMethod"</span><span>&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;expression=</span><span class="string">"execution(*&nbsp;com.baobaotao.service.*Forum.*(..))"</span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>t;aop:advisor&nbsp;pointcut-ref=</span><span class="string">"serviceMethod"</span><span>&nbsp;advice-ref=</span><span class="string">"txAdvice"</span><span>&nbsp;/&gt;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&lt;/aop:config&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;tx:advice&nbsp;id=</span><span class="string">"txAdvice"</span><span>&nbsp;transaction-manager=</span><span class="string">"txManager"</span><span>&gt;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&lt;tx:attributes&gt;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tx:method&nbsp;name=</span><span class="string">"get*"</span><span>&nbsp;read-only=</span><span class="string">"false"</span><span>/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tx:method&nbsp;name=</span><span class="string">"add*"</span><span>&nbsp;rollback-</span><span class="keyword">for</span><span>=</span><span class="string">"Exception"</span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tx:method&nbsp;name=</span><span class="string">"update*"</span><span>/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&lt;/tx:attributes&gt; &nbsp;&nbsp;</span></li>
    <li><span>lt;/tx:advice&gt; &nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">
&lt;aop:config&gt;
&lt;aop:pointcut id="serviceMethod"
expression="execution(* com.baobaotao.service.*Forum.*(..))" /&gt;
&lt;aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" /&gt;
&lt;/aop:config&gt;
&lt;tx:advice id="txAdvice" transaction-manager="txManager"&gt;
&lt;tx:attributes&gt;
&lt;tx:method name="get*" read-only="false"/&gt;
&lt;tx:method name="add*" rollback-for="Exception" /&gt;
&lt;tx:method name="update*"/&gt;
&lt;/tx:attributes&gt;
&lt;/tx:advice&gt;
</pre>
<br />
<br />
<strong>三、关于AOP配置</strong> <br />
<br />
原来1.x的AOP太麻烦了，提都不想提，直接说一下2.x的AOP。 <br />
Spring 2.x使用@AspectJ来描述切面，由于@AspectJ的语法描述能力超强，因此在Spring 2.x中使用AOP真的非常方便。 <br />
<br />
在使用@AspectJ之前，首先你得保证你所使用的JDK的版本是5.0及以上版本，否则无法使用注解技术。 <br />
Spring在处理@Aspect注解表达式时，需要使用位于spring/lib/asm下asm关联类库，将该类库的三个类包加入到类路径中：asm-2.2.2.jar、asm-commons-2.2.2.jar和asm-util-2.2.2.jar。我们在第一章中了解了asm类库的用途，它是轻量级的字节码处理框架，因为Java的反射机制无法获取入参名，Spring就利用asm处理@AspectJ中所描述的方法入参名。 <br />
<br />
此外，Spring采用AspectJ提供的@AspectJ注解类库及相应的解析类库，它位于spring/lib/aspectj目录下，将目录下的aspectjrt.jar和aspectjweaver.jar类包加入类路径中。 <br />
在做好上节中所提到的前置工作后，我们就可以开始编写一个基于@AspectJ的切面了，首先来看一个简单的例子，以便对@AspectJ有一个切身的认识。 <br />
<br />
@AspectJ采用不同的方式对AOP进行描述， 我们使用NaiveWaiter的例子来说明，这是一个希望引入切面的目标类： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword">package</span><span>&nbsp;com.baobaotao; &nbsp;&nbsp;</span></span></li>
    <li><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;NaiveWaiter&nbsp;</span><span class="keyword">implements</span><span>&nbsp;Waiter&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;greetTo(String&nbsp;clientName)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string">"NaiveWaiter:greet&nbsp;to&nbsp;"</span><span>+clientName+</span><span class="string">"..."</span><span>); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;serveTo(String&nbsp;clientName){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string">"NaiveWaiter:serving&nbsp;"</span><span>+clientName+</span><span class="string">"..."</span><span>); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">package com.baobaotao;
public class NaiveWaiter implements Waiter {
public void greetTo(String clientName) {
System.out.println("NaiveWaiter:greet to "+clientName+"...");
}
public void serveTo(String clientName){
System.out.println("NaiveWaiter:serving "+clientName+"...");
}
}
</pre>
<br />
<br />
下面使用@AspectJ来定义一下切面： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword">package</span><span>&nbsp;com.baobaotao.aspectj.aspectj; &nbsp;&nbsp;</span></span></li>
    <li><span class="keyword">import</span><span>&nbsp;org.aspectj.lang.annotation.Aspect; &nbsp;&nbsp;</span></span></li>
    <li><span class="keyword">import</span><span>&nbsp;org.aspectj.lang.annotation.Before; &nbsp;&nbsp;</span></span></li>
    <li><span class="comment">//通过该注解将PreGreetingAspect标识为一个切面 </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span class="annotation">@Aspect</span><span>&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;PreGreetingAspect{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="annotation">@Before</span><span>(</span><span class="string">"execution(*&nbsp;greetTo(..))"</span><span>)&nbsp;</span><span class="comment">//&lt;---定义切点和增强类型 </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;beforeGreeting(){&nbsp;</span><span class="comment">//&lt;----增强的横切逻辑 </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string">"How&nbsp;are&nbsp;you"</span><span>); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">package com.baobaotao.aspectj.aspectj;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//通过该注解将PreGreetingAspect标识为一个切面
@Aspect
public class PreGreetingAspect{
@Before("execution(* greetTo(..))") //&lt;---定义切点和增强类型
public void beforeGreeting(){ //&lt;----增强的横切逻辑
System.out.println("How are you");
}
}
</pre>
<br />
<br />
然后启动@AspectJ的注解切面驱动就可以了！ <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml&nbsp;version=</span><span class="string">"1.0"</span><span>&nbsp;encoding=</span><span class="string">"UTF-8"</span><span>&nbsp;?&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;beans&nbsp;xmlns=</span><span class="string">"http://www.springframework.org/schema/beans"</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;xmlns:xsi=</span><span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;xmlns:aop=</span><span class="string">"http://www.springframework.org/schema/aop"</span><span>&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;xsi:schemaLocation="http:</span><span class="comment">//www.springframework.org/schema/beans </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&lt;A&nbsp;href=</span><span class="string">"http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"</span><span>&nbsp;target=_blank&gt;http://www.springframework.org/schema/beans/spring-beans-</span><span class="number">2.0</span><span>.xsd&lt;/A&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http:</span><span class="comment">//www.springframework.org/schema/aop&nbsp; </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&lt;A&nbsp;href=</span><span class="string">"http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"</span><span>&nbsp;target=_blank&gt;http://www.springframework.org/schema/aop/spring-aop-</span><span class="number">2.0</span><span>.xsd&lt;/A&gt;"&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--基于</span><span class="annotation">@AspectJ</span><span>切面的驱动器--&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;aop:aspectj-autoproxy&nbsp;/&gt;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean&nbsp;id=</span><span class="string">"waiter"</span><span>&nbsp;</span><span class="keyword">class</span><span>=</span><span class="string">"com.baobaotao.NaiveWaiter"</span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean&nbsp;</span><span class="keyword">class</span><span>=</span><span class="string">"com.baobaotao.aspectj.example.PreGreetingAspect"</span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;/beans&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
<a href="http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans-2.0.xsd</a>
http://www.springframework.org/schema/aop
<a href="http://www.springframework.org/schema/aop/spring-aop-2.0.xsd" target="_blank">http://www.springframework.org/schema/aop/spring-aop-2.0.xsd</a>"&gt;
&lt;!--基于@AspectJ切面的驱动器--&gt;
&lt;aop:aspectj-autoproxy /&gt;
&lt;bean id="waiter" class="com.baobaotao.NaiveWaiter" /&gt;
&lt;bean class="com.baobaotao.aspectj.example.PreGreetingAspect" /&gt;
&lt;/beans&gt;
</pre>
<br />
<br />
<strong>四、关于Spring 2.1添加的新功能</strong> <br />
<br />
1.原来引入一个外面属性配置文件需要使用以下的方式： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;bean&nbsp;</span><span class="keyword">class</span><span>=</span><span class="string">"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name=</span><span class="string">"locations"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--指定属性文件地址，可以在这里定义多个属性文件--&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;classpath:com/baobaotao/place/car.properties&lt;/value&gt;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/list&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name=</span><span class="string">"fileEncoding"</span><span>&nbsp;value=</span><span class="string">"utf-8"</span><span>/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;/bean&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;</span></li>
    <li><span>&lt;!--引用外部属性的值，对car属性进行配置--&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;bean&nbsp;id=</span><span class="string">"car"</span><span>&nbsp;</span><span class="keyword">class</span><span>=</span><span class="string">"com.baobaotao.place.Car"</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name=</span><span class="string">"brand"</span><span>&nbsp;value=</span><span class="string">"${brand}"</span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name=</span><span class="string">"maxSpeed"</span><span>&nbsp;value=</span><span class="string">"${maxSpeed}"</span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name=</span><span class="string">"price"</span><span>&nbsp;value=</span><span class="string">"${price}"</span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;/bean&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&gt;
&lt;property name="locations"&gt;
&lt;list&gt;
&lt;!--指定属性文件地址，可以在这里定义多个属性文件--&gt;
&lt;value&gt;classpath:com/baobaotao/place/car.properties&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="fileEncoding" value="utf-8"/&gt;
&lt;/bean&gt;
&lt;!--引用外部属性的值，对car属性进行配置--&gt;
&lt;bean id="car" class="com.baobaotao.place.Car"&gt;
&lt;property name="brand" value="${brand}" /&gt;
&lt;property name="maxSpeed" value="${maxSpeed}" /&gt;
&lt;property name="price" value="${price}" /&gt;
&lt;/bean&gt;
</pre>
<br />
<br />
使用Spring 2.1后，你只需要象下面这样配置就可以了： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/148519#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;context:property-placeholder&nbsp;location=</span><span class="string">"&nbsp;classpath:com/baobaotao/place/car.properties&nbsp;"</span><span>/&gt;&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;context:property-placeholder location=" classpath:com/baobaotao/place/car.properties "/&gt;
</pre>
<br />
<br />
3.注解驱动的Bean注入 <br />
<br />
大家看看这段E文就OK了， <br />
&lt;context:component-scan&gt;: scans classpath using one or more "include"/"exclude" filters, and automatically registers beans <br />
In essence, this is a third way to define beans (1: classic xml, 2:javaconfig (code), 3:&lt;context:component-scan&gt;, matching on annotations or types) <br />
Default naming strategy is based on short classname of discovered bean <br />
@Component and @Repository annotations, when used, can optionally specify a bean name to use <br />
For filter type "annotation", the value of "expression" attribute should resolve to a Java annotation type <br />
For filter type "assignable", the value of "expression" attribute should resolve to a Java type <br />
For filter type "aspectj", the value of "expression" should be an "type expression" (in Pointcut language, perhaps it could be injected?) <br />
Relevant documentation can be found in preliminary spring 2.1 manual, sections 3.10 and 3.11 <br />
In addition, this last JIRA comment for http://www.jetbrains.net/jira/browse/IDEADEV-16886#action_163502 contains two links to articles showing example usage of &lt;context:component-scan&gt; <br />
&lt;context:annotation-config&gt; (described in 3.10 in spring 2.1 manual linked above): allows autowiring to be defined using @Resource or @Autowired annotations. <br />
<br />
<br />
<strong>五、关于Spring 2.5添加的新功能</strong> <br />
Spring 2.5继续对context命名空间进行了扩充，添加了好用而强大的context:load-time-weaver，可以让我们更方便地应用AspectJ。大家可以看TSS上的这篇文章，它全面讲解了Spring 2.5的新特性。 <br />
<a href="http://www.theserverside.com/tt/articles/article.tss?l=IntrotoSpring25" target="_blank">http://www.theserverside.com/tt/articles/article.tss?l=IntrotoSpring25</a> <br />
<br />
<span style="color: red">注：以上大部分代码来直接引用自 《精通Spring 2.x--企业应用开发精解》</span> <br />
文章来源于:javaeye的stamen
<img src ="http://www.blogjava.net/i369/aggbug/186191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2008-03-14 09:44 <a href="http://www.blogjava.net/i369/articles/186191.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACEGI杂项记录[全部转载]</title><link>http://www.blogjava.net/i369/articles/170224.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Tue, 25 Dec 2007 01:35:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/170224.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/170224.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/170224.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/170224.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/170224.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 对ACEGI中FilterChainProxy进行性能调优一般情况下，在ACEGI中队filterChainProxy如下配置 &nbsp;&nbsp;&nbsp; &lt;bean id="filterChainProxy" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.acegisec...&nbsp;&nbsp;<a href='http://www.blogjava.net/i369/articles/170224.html'>阅读全文</a><img src ="http://www.blogjava.net/i369/aggbug/170224.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-12-25 09:35 <a href="http://www.blogjava.net/i369/articles/170224.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi安全系统扩展 </title><link>http://www.blogjava.net/i369/articles/165458.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Wed, 05 Dec 2007 04:51:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/165458.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/165458.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/165458.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/165458.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/165458.html</trackback:ping><description><![CDATA[相信side对Acegi的扩展会给你耳目一新的感觉,提供完整的扩展功能,管理界面,中文注释和靠近企业的安全策略。side只对Acegi不符合企业应用需要的功能进行扩展,尽量不改动其余部分来实现全套权限管理功能,以求能更好地适应Acegi升级。
<p>&nbsp;</p>
<h2>3.1 基于角色的权限控制(RBAC)</h2>
<p>&nbsp;&nbsp;&nbsp; Acegi 自带的 sample 表设计很简单: users表{username,password,enabled} authorities表{username,authority},这样简单的设计无法适应复杂的权限需求,故SpringSide选用RBAC模型对 权限控制数据库表进行扩展。&nbsp;RBAC引入了ROLE的概念,使User(用户)和Permission(权限)分离,一个用户拥有多个角色,一个角色拥 有有多个相应的权限,从而减少了权限管理的复杂度,可更灵活地支持安全策略。 </p>
<p><img alt="" src="http://www.springside.org.cn/docs/reference/images/acegi/RBAC.jpg" /></p>
<p>&nbsp;&nbsp;&nbsp; 同时,我们也引入了resource(资源)的概念,一个资源对应多个权限，资源分为ACL,URL,和FUNTION三种。注意，URL和FUNTION的权限命名需要以AUTH_开头才会有资格参加投票, 同样的ACL权限命名需要ACL_开头。</p>
<br />
<h2>3.2&nbsp;管理和使用EhCache</h2>
<h3>3.2.1 设立缓存</h3>
<p>在SpringSide里的 Acegi 扩展使用 <a href="http://ehcache.sourceforge.net/">EhCache</a><span style="font-family: 宋体">&nbsp;就作为一种缓存解决方案,以缓存用户和资源的信息和相对应的权限信息。</span></p>
<p><span style="font-family: 宋体">首先需要一个在classpath的<span style="font-family: Arial"> <span lang="EN-US">ehcache.xml </span></span><span style="font-family: 宋体">文件，用于配置</span><span style="font-family: Arial"> <span lang="EN-US">EhCache。</span></span></span></p>
<pre><span style="font-family: 宋体"><span style="font-family: Arial"><span lang="EN-US">&lt;ehcache&gt;<br />
</span></span></span><span style="font-family: 宋体"><span style="font-family: Arial"><span lang="EN-US">&nbsp;&nbsp;&nbsp;</span></span></span><span style="font-family: 宋体"><span style="font-family: Arial"><span lang="EN-US">&nbsp;&nbsp;&nbsp; &lt;defaultCache<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxElementsInMemory="10000"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eternal="false"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; overflowToDisk="true"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeToIdleSeconds="0"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeToLiveSeconds="0"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; diskPersistent="false"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;diskExpiryThreadIntervalSeconds= "120"/&gt; <br />
&nbsp;&nbsp;&nbsp; &lt;!-- acegi cache--&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;cache name="<font color="#000080">userCache</font>"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxElementsInMemory="10000"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eternal="true"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;overflowToDisk= "true"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;!-- acegi cache--&gt;   <br />
&nbsp;&nbsp;&nbsp; &lt;cache name="<font color="#000080">resourceCache</font>"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxElementsInMemory="10000"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eternal="true"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; overflowToDisk="true"/&gt;<br />
&lt;/ehcache&gt;</span></span></span></pre>
<p><span lang="EN-US" style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp; maxElementsInMemory设定了允许在Cache中存放的数据数目，eternal设定Cache是否会过期， overflowToDisk设定内存不足的时候缓存到硬盘，timeToIdleSeconds和timeToLiveSeconds设定缓存游离时间 和生存时间，diskExpiryThreadIntervalSeconds设定缓存在硬盘上的生存时间，注意当eternal="true"时， timeToIdleSeconds，timeToLiveSeconds和diskExpiryThreadIntervalSeconds都是无效 的。</span></p>
<p><span lang="EN-US" style="font-family: Arial">&lt;defaultCache&gt;是除制定的Cache外其余所有Cache的设置，针对Acegi 的情况, 专门设置了userCache和resourceCache，都设为永不过期。在applicationContext-acegi-security.xml中相应的调用是</span></p>
<pre><span lang="EN-US" style="font-family: Arial">&lt;bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cacheManager" ref="cacheManager"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="<font color="#000080">cacheName</font>" value="<font color="#000080">  userCache</font>"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache" autowire="byName"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cache" ref="userCacheBackend"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="resourceCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cacheManager" ref="cacheManager"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="<font color="#000080">cacheName</font>" value="<font color="#000080">  resourceCache</font>"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="resourceCache" class="org.springside.modules.security.service.acegi.cache.ResourceCache" autowire="byName"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cache" ref="resourceCacheBackend"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&lt;bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/&gt;<br />
</span></pre>
<p><span lang="EN-US" style="font-family: Arial">"cacheName" 就是设定在<span lang="EN-US">ehcache.xml </span>中相应Cache的名称。</span></p>
<p><span lang="EN-US" style="font-family: Arial">userCache使用的是Acegi 的EhCacheBasedUserCache(实现了UserCache接口), resourceCache是SpringSide的扩展类</span></p>
<pre><span lang="EN-US" style="font-family: Arial">public interface <font color="#000080">UserCache</font>   {</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public UserDetails <font color="#800080">getUserFromCache</font>   (String username);</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public void <font color="#800080">putUserInCache</font>   (UserDetails user);</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public void <font color="#800080">removeUserFromCache</font>   (String username);<br />
}</span></pre>
<pre><span lang="EN-US" style="font-family: Arial">public class <font color="#000080">ResourceCache</font>   {<br />
</span><span lang="EN-US" style="font-family: Arial">&nbsp;&nbsp;&nbsp; public ResourceDetails <font color="#800080">getAuthorityFromCache</font>   (String resString) {...</span><span lang="EN-US" style="font-family: Arial">&nbsp;&nbsp; }<br />
</span><span lang="EN-US" style="font-family: Arial">&nbsp;&nbsp;&nbsp; public void <font color="#800080">putAuthorityInCache</font>   (ResourceDetails resourceDetails) {...&nbsp; }<br />
&nbsp;</span><span lang="EN-US" style="font-family: Arial">&nbsp;&nbsp; public void <font color="#800080">removeAuthorityFromCache</font>   (String resString) {... }<br />
</span><span lang="EN-US" style="font-family: Arial">&nbsp;&nbsp;&nbsp; public List getUrlResStrings() {... }<br />
</span><span lang="EN-US" style="font-family: Arial">&nbsp;&nbsp;&nbsp; public List getFunctions() {.. }</span><span lang="EN-US" style="font-family: Arial"><br />
}</span></pre>
<p><span lang="EN-US" style="font-family: Arial">UserCache 就是通过EhCache对UserDetails 进行缓存管理, 而ResourceCache 是对ResourceDetails 类进行缓存管理</span></p>
<pre><span lang="EN-US" style="font-family: Arial">public interface <font color="#000080">UserDetails</font>   extends Serializable {<br />
</span><span lang="EN-US" style="font-family: Arial">&nbsp;&nbsp;&nbsp; public boolean isAccountNonExpired();</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public boolean isAccountNonLocked();</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public GrantedAuthority[] <font color="#800080">getAuthorities</font>();</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public boolean isCredentialsNonExpired();</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public boolean isEnabled();</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public String getPassword();</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public String getUsername();<br />
}</span></pre>
<pre><span lang="EN-US" style="font-family: Arial">public interface <font color="#000080">ResourceDetails</font>   extends Serializable {</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public String getResString();</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public String getResType();</span><span lang="EN-US" style="font-family: Arial"><br />
&nbsp;&nbsp;&nbsp; public GrantedAuthority[] <font color="#800080">getAuthorities</font>();<br />
</span><span lang="EN-US" style="font-family: Arial">}</span></pre>
<p><span lang="EN-US" style="font-family: Arial">UserDetails 包含用户信息和相应的权限，ResourceDetails 包含资源信息和相应的权限。</span></p>
<pre><span lang="EN-US" style="font-family: Arial">public interface <font color="#000080">GrantedAuthority</font>     {<br />
&nbsp;&nbsp;&nbsp; public String <font color="#800080">getAuthority</font>   ();<br />
}</span></pre>
<p><span lang="EN-US" style="font-family: Arial"><font face="Tahoma">&nbsp;&nbsp;&nbsp;&nbsp; </font>GrantedAuthority 就是权限信息，在Acegi 的 sample 里<span lang="EN-US" style="font-family: Arial">GrantedAuthority 的信息如</span>ROLE_USER, ROLE_SUPERVISOR, ACL_CONTACT_DELETE, ACL_CONTACT_ADMIN等等，网上也有很多例子把角色作为<span lang="EN-US" style="font-family: Arial">GrantedAuthority ，但事实上看看ACL 就知道， Acegi本身根本就没有角色这个概念，<span lang="EN-US" style="font-family: Arial">GrantedAuthority 包含的信息应该是权限，对于非ACL的权限用 AUTH_ 开头更为合理, 如SpringSide里的 AUTH_ADMIN_LOGIN, AUTH_BOOK_MANAGE 等等。</span></span> </span></p>
<h3>3.2.2 管理缓存</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 使用AcegiCacheManager对<span lang="EN-US" style="font-family: Arial">userCache和resourceCache进行统一缓存管理。<font face="Tahoma">当在后台对用户信息进行修改或赋权的时候, 在更新数据库同时就会调用acegiCacheManager相应方法, 从数据库中读取数据并替换cache中相应部分,使cache与数据库同步。</font></span></p>
<pre><span lang="EN-US" style="font-family: Arial">public class AcegiCacheManager extends BaseService {<br />
</span><span lang="EN-US" style="font-family: Arial">&nbsp;&nbsp;&nbsp; private ResourceCache <font color="#800080">resourceCache</font>   ;<br />
</span><span lang="EN-US" style="font-family: Arial">&nbsp;&nbsp;&nbsp; private UserCache <font color="#800080">userCache</font>   ;</span><span lang="EN-US" style="font-family: Arial"><br />
<font color="#800000">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 修改User时更改userCache  <br />
&nbsp;&nbsp;&nbsp;&nbsp; */</font> <br />
&nbsp;&nbsp;&nbsp; public void <font color="#800080">modifyUserInCache</font>   (User user, String orgUsername) {...&nbsp;&nbsp;&nbsp; }<br />
<font color="#800000">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 修改Resource时更改resourceCache  <br />
&nbsp;&nbsp;&nbsp;&nbsp; */</font> <br />
&nbsp;&nbsp;&nbsp; public void <font color="#800080">modifyResourceInCache</font>   (Resource resource, String orgResourcename) {...&nbsp;&nbsp;&nbsp; }<br />
<font color="#800000">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; *   修改权限时同时修改userCache和resourceCache<br />
&nbsp;&nbsp;&nbsp;&nbsp; */</font> <br />
&nbsp;&nbsp;&nbsp; public void <font color="#800080">modifyPermiInCache</font>   (Permission permi, String orgPerminame) {...&nbsp; }<br />
&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * <font color="#800000">User授予角色时更改userCache</font>  <br />
&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public void <font color="#800080">authRoleInCache</font>   (User user) {...&nbsp;&nbsp;&nbsp; }<br />
<font color="#800000">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * Role授予权限时更改userCache和resourceCache  <br />
&nbsp;&nbsp;&nbsp;&nbsp; */</font> <br />
&nbsp;&nbsp;&nbsp; public void <font color="#800080">authPermissionInCache</font>   (Role role) {...&nbsp; }<br />
&nbsp;<font color="#800000">&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * Permissioni授予资源时更改resourceCache  <br />
&nbsp;&nbsp;&nbsp;&nbsp; */</font> <br />
&nbsp;&nbsp;&nbsp; public void <font color="#800080">authResourceInCache</font>   (Permission permi) {...&nbsp; }<br />
<font color="#800000">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; *   初始化userCache<br />
&nbsp;&nbsp;&nbsp;&nbsp; */</font> <br />
&nbsp;&nbsp;&nbsp; public void <font color="#800080">initUserCache</font>   () {...&nbsp; }<br />
<font color="#800000">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; *   初始化resourceCache<br />
&nbsp;&nbsp;&nbsp;&nbsp; */</font> <br />
&nbsp;&nbsp;&nbsp; public void <font color="#800080">initResourceCache</font>   () {... }<br />
<font color="#800000">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; *  获取所有的url资源<br />
&nbsp;&nbsp;&nbsp;&nbsp; */</font> <br />
&nbsp;&nbsp;&nbsp; public List <font color="#800080">getUrlResStrings</font>   () {... &nbsp;}<br />
<font color="#800000">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 获取所有的Funtion资源  <br />
&nbsp;&nbsp;&nbsp;&nbsp; */</font> <br />
&nbsp;&nbsp;&nbsp; public List <font color="#800080">getFunctions</font>   () {...&nbsp; }<br />
<font color="#800000">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 根据资源串获取资源  <br />
&nbsp;&nbsp;&nbsp;&nbsp; */</font> <br />
&nbsp;&nbsp;&nbsp; public ResourceDetails <font color="#800080">getAuthorityFromCache</font>   (String resString) {...&nbsp; }<br />
&nbsp;&nbsp;<br />
&nbsp;......<br />
<br />
<br />
}</span></pre>
<p>&nbsp;</p>
<h2>3.3 资源权限定义扩展</h2>
<p><span lang="EN-US" style="font-family: Arial"><font face="Tahoma">&nbsp;&nbsp;&nbsp;&nbsp; </font></span>Acegi给出的sample里,资源权限对照关系是配置在xml中的,试想一下如果你的企业安全应用有500个用户,100个角色权限的时候,维护这个xml将是个繁重无比的工作,如何动态更改用户权限更是个头痛的问题。</p>
<pre>&nbsp;&nbsp; &lt;bean id="contactManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="accessDecisionManager"&gt;&lt;ref local="businessAccessDecisionManager"/&gt;&lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="afterInvocationManager"&gt;&lt;ref local="afterInvocationManager"/&gt;&lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="objectDefinitionSource"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.create=ROLE_USER<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.getAllRecipients=ROLE_USER<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.getAll=ROLE_USER,AFTER_ACL_COLLECTION_READ<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.getById=ROLE_USER,AFTER_ACL_READ<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.delete=ACL_CONTACT_DELETE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp; &lt;/bean&gt;</pre>
<pre>&nbsp;&nbsp;&lt;bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="accessDecisionManager"&gt;&lt;ref local="httpRequestAccessDecisionManager"/&gt;&lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="objectDefinitionSource"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PATTERN_TYPE_APACHE_ANT<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /index.jsp=ROLE_ANONYMOUS,ROLE_USER<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /hello.htm=ROLE_ANONYMOUS,ROLE_USER<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /switchuser.jsp=ROLE_SUPERVISOR<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /j_acegi_switch_user=ROLE_SUPERVISOR<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER<br />
&nbsp;&nbsp;&nbsp;&nbsp;   /**=ROLE_USER<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p>&nbsp;对如此不Pragmatic的做法,SpringSide进行了扩展, 让Acegi 能动态读取数据库中的权限资源关系。</p>
<h3>3.3.1 Aop Invocation Authorization</h3>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="objectDefinitionSource" ref="methodDefinitionSource"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="methodDefinitionSource" class="org.springside.security.service.acegi.DBMethodDefinitionSource"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="acegiCacheManager" ref="acegiCacheManager"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p><span lang="EN-US" style="font-family: Arial"><font face="Tahoma">&nbsp;&nbsp;&nbsp;&nbsp; </font></span>研究下Aceig的源码,<strong>ObjectDefinitionSource</strong>的实际作用是返回一个<strong>ConfigAttributeDefinition</strong>对象，而Acegi Sample 的方式是用<strong>MethodDefinitionSourceEditor</strong>把xml中的文本Function资源权限对应关系信息加载到<strong>MethodDefinitionMap </strong>( MethodDefinitionSource&nbsp;的实现类 )中, 再组成ConfigAttributeDefinition，而我们的扩展目标是从缓存中读取信息来组成ConfigAttributeDefinition。</p>
<p><span lang="EN-US" style="font-family: Arial"><font face="Tahoma">&nbsp;&nbsp;&nbsp;&nbsp; </font></span><strong>MethodSecurityInterceptor</strong>是通过调用AbstractMethodDefinitionSource的<strong>lookupAttributes(method)</strong>方法获取ConfigAttributeDefinition。所以我们需要实现自己的O<span lang="EN-US" style="font-family: Arial"><font face="Tahoma">bjectDefinitionSource，</font></span>继承<strong>AbstractMethodDefinitionSource</strong>并实现其lookupAttributes方法,从缓存中读取资源权限对应关系组成并返回ConfigAttributeDefinition即可。SpringSide中的<strong>DBMethodDefinitionSource</strong>类的部分实现如下 :</p>
<pre>public class DBMethodDefinitionSource <font color="#000080">extends</font> <font color="#000080">AbstractMethodDefinitionSource</font> {<br />
......<br />
&nbsp;&nbsp;&nbsp; protected <font color="#008080">ConfigAttributeDefinition</font> <font color="#800080">lookupAttributes</font>(Method mi) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.notNull(mi, "lookupAttrubutes in the DBMethodDefinitionSource is null");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String methodString = mi.getDeclaringClass().getName() + "." + mi.getName();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!acegiCacheManager.isCacheInitialized()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#800000">//初始化Cache<br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; acegiCacheManager.initResourceCache();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#800000">//获取所有的function<br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List methodStrings = acegiCacheManager.getFunctions();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Set auths = new HashSet();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#800000">//取权限的合集</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Iterator iter = methodStrings.iterator(); iter.hasNext();) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String mappedName = (String) iter.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (methodString.equals(mappedName)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; || <font color="#800080">isMatch</font>(methodString, mappedName)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResourceDetails resourceDetails = acegiCacheManager.getAuthorityFromCache(mappedName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (resourceDetails == null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GrantedAuthority[] authorities = resourceDetails.getAuthorities();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (authorities == null || authorities.length == 0) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; auths.addAll(Arrays.asList(authorities));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (auths.size() == 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String authoritiesStr = " ";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Iterator iter = auths.iterator(); iter.hasNext();) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GrantedAuthority authority = (GrantedAuthority) iter.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; authoritiesStr += authority.getAuthority() + ",";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String authStr = authoritiesStr.substring(0, authoritiesStr.length() - 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; configAttrEditor.setAsText(authStr);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#800000">//组装并返回ConfigAttributeDefinition</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (ConfigAttributeDefinition) configAttrEditor.getValue();<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; ......<br />
}</pre>
<p>要注意几点的是: <br />
1) 初始化Cache是比较浪费资源的，所以SpringSide中除第一次访问外的Cache的更新是针对性更新。</p>
<p>2) 因为method采用了匹配方式(详见 isMatch() 方法) , 即对于*Book和save*这两个资源来说，只要当前访问方法是Book结尾或以save开头都算匹配得上，所以应该取这些能匹配上的资源的相对应的权限的合集。</p>
<p>3) 使用ConfigAttributeEditor 能更方便地组装ConfigAttributeDefinition。 </p>
<h3>3.3.2 Filter Invocation Authorization</h3>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="objectDefinitionSource" ref="filterDefinitionSource"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="filterDefinitionSource" class="org.springside.security.service.acegi.DBFilterInvocationDefinitionSource"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="convertUrlToLowercaseBeforeComparison" value="true"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="useAntPath" value="true"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="acegiCacheManager" ref="acegiCacheManager"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p><span lang="EN-US" style="font-family: Arial"><font face="Tahoma">&nbsp;&nbsp;&nbsp;&nbsp; </font></span>PathBasedFilterInvocationDefinitionMap和RegExpBasedFilterInvocationDefinitionMap都是 <strong>FilterInvocationDefinitionSource</strong>的实现类,当PATTERN_TYPE_APACHE_ANT字符串匹配上时时,<strong>FilterInvocationDefinitionSourceEditor </strong>选用PathBasedFilterInvocationDefinitionMap 把xml中的文本URL资源权限对应关系信息加载。</p>
<p><span lang="EN-US" style="font-family: Arial"><font face="Tahoma">&nbsp;&nbsp;&nbsp;&nbsp; </font></span><strong>FilterSecurityInterceptor</strong>通过FilterInvocationDefinitionSource的<strong>lookupAttributes(url)</strong>方法获取ConfigAttributeDefinition。 所以，我们可以通过继承FilterInvocationDefinitionSource的抽象类<strong>AbstractFilterInvocationDefinitionSource</strong>，并实现其lookupAttributes方法,从缓存中读取URL资源权限对应关系即可。SpringSide的<strong>DBFilterInvocationDefinitionSource</strong>类部分实现如下:</p>
<pre>public class DBFilterInvocationDefinitionSource <font color="#000080">extends&nbsp;AbstractFilterInvocationDefinitionSource</font> {<br />
<br />
......<br />
&nbsp;&nbsp;&nbsp; public ConfigAttributeDefinition lookupAttributes(String url) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!acegiCacheManager.isCacheInitialized()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; acegiCacheManager.initResourceCache();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isUseAntPath()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Strip anything after a question mark symbol, as per SEC-161.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int firstQuestionMarkIndex = url.lastIndexOf("?");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (firstQuestionMarkIndex != -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; url = url.substring(0, firstQuestionMarkIndex);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List urls = acegiCacheManager.getUrlResStrings();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#800000">//URL资源倒叙排序</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collections.sort(urls);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collections.reverse(urls);<br />
<font color="#800000">//是否先全部转为小写再比较</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (convertUrlToLowercaseBeforeComparison) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; url = url.toLowerCase();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GrantedAuthority[] authorities = new GrantedAuthority[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (Iterator iterator = urls.iterator(); iterator.hasNext();) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String resString = (String) iterator.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean matched = false;<br />
<font color="#800000">              //可选择使用AntPath和Perl5两种不同匹配模式  </font> <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isUseAntPath()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; matched = pathMatcher.match(resString, url);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Pattern compiledPattern;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Perl5Compiler compiler = new Perl5Compiler();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; compiledPattern = compiler.compile(resString,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Perl5Compiler.READ_ONLY_MASK);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MalformedPatternException mpe) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new IllegalArgumentException(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Malformed regular expression: " + resString);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; matched = matcher.matches(url, compiledPattern);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (matched) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResourceDetails rd = acegiCacheManager.getAuthorityFromCache(resString);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; authorities = rd.getAuthorities();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (authorities.length &gt; 0) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String authoritiesStr = " ";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; authorities.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; authoritiesStr += authorities[i].getAuthority() + ",";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String authStr = authoritiesStr.substring(0, authoritiesStr<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .length() - 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; configAttrEditor.setAsText(authStr);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (ConfigAttributeDefinition) configAttrEditor.getValue();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
......<br />
&nbsp;}</pre>
<p>继承<font color="#000000">AbstractFilterInvocationDefinitionSource</font>注意几点：<br />
1)&nbsp; 需要先把获取回来的URL资源按倒序派序，以达到 a/b/c/d.* 在 a/.* 之前的效果(详见 Acegi sample 的applicationContext-acegi-security.xml 中的filterInvocationInterceptor的注释)，为的是更具体的URL可以先匹配上，而获取具体URL的权限，如a/b/c/d.*权限AUTH_a, AUTH_b 才可查看,&nbsp; a/.* 需要权限AUTH_a 才可查看，则如果当前用户只拥有权限AUTH_b,则他只可以查看a/b/c/d.jsp 而不能察看a/d.jsp。</p>
<p>2)&nbsp;基于上面的原因，故第一次匹配上的就是当前所需权限，而不是取权限的合集。</p>
<p>3) 可以选用AntPath 或 Perl5 的资源匹配方式，感觉AntPath匹配方式基本足够。</p>
<p>4) Filter 权限控制比较适合于较粗颗粒度的权限，如设定某个模块下的页面是否能访问等，对于具体某个操作如增删修改，是否能执行，用Method&nbsp;&nbsp;Invocation 会更佳些，所以注意两个方面一起控制效果更好</p>
<p>&nbsp;</p>
<h2>3.4 授权操作</h2>
<p><span lang="EN-US" style="font-family: Arial"><font face="Tahoma">&nbsp;&nbsp;&nbsp;&nbsp; </font></span>RBAC模型中有不少多对多的关系，这些关系都能以一个中间表的形式来存放，而Hibernate中可以不建这中间表对应的hbm.xml , 以资源与权限的配置为例，如下:</p>
<pre>&lt;hibernate-mapping package="org.springside.modules.security.domain"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;class name="Permission" table="PERMISSIONS" dynamic-insert="true" dynamic-update="true"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;cache usage="nonstrict-read-write"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;id name="id" column="ID"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;generator class="native"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/id&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="name" column="NAME" not-null="true"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="descn" column="DESCN"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="operation" column="OPERATION"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="status" column="STATUS"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;set name="roles" table="ROLE_PERMIS" lazy="true" inverse="true" cascade="save-update" batch-size="5"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;key&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;column name="PERMIS_ID" not-null="true"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/key&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;many-to-many class="Role" column="ROLE_ID" outer-join="auto"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/set&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;set name="resources" table="PERMIS_RESC" lazy="true" inverse="false" cascade="save-update" batch-size="5"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;key&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;column name="PERMIS_ID" not-null="true"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/key&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;many-to-many class="Resource" column="RESC_ID"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/set&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/class&gt;<br />
&lt;/hibernate-mapping&gt;</pre>
<pre>&lt;hibernate-mapping package="org.springside.modules.security.domain"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;class name="Resource" table="RESOURCES" dynamic-insert="true" dynamic-update="true"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;cache usage="nonstrict-read-write"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;id name="id" column="ID"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;generator class="native"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/id&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="name" column="NAME" not-null="true"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="resType" column="RES_TYPE" not-null="true"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="resString" column="RES_STRING" not-null="true"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="descn" column="DESCN"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;set name="permissions" table="PERMIS_RESC" lazy="true" inverse="true" cascade="save-update" batch-size="5"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;key&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;column name="RESC_ID" not-null="true"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/key&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;many-to-many class="Permission" column="PERMIS_ID" outer-join="auto"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/set&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/class&gt;<br />
&lt;/hibernate-mapping&gt;</pre>
<p>配置时注意几点:</p>
<p>1) 因为是分配某个权限的资源，所以权限是主控方，把inverse设为false，资源是被控方inverse设为true</p>
<p>2) cascade是"save-update"，千万别配成delete</p>
<p>3) 只需要 permission.getResources().add(resource)， permission.getResources()..remove(resource) 即可很方便地完成授权和取消授权操作</p>
&nbsp;<br />
<br />
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1753827</p>
<img src ="http://www.blogjava.net/i369/aggbug/165458.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-12-05 12:51 <a href="http://www.blogjava.net/i369/articles/165458.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACEGI标签及其扩展</title><link>http://www.blogjava.net/i369/articles/165457.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Wed, 05 Dec 2007 04:50:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/165457.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/165457.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/165457.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/165457.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/165457.html</trackback:ping><description><![CDATA[ACEGI标签
<p style="text-indent: 21pt">虽然Acegi的安全强制过滤器能够阻止用户浏览他们没有权限看到的页面，但最好的做法是从一开始就不提供指向受限制页面的链接。&lt;authz:authorize&gt;标签能够根据当前用户是否拥有恰当权限来决定显示或隐藏Web页面的内容。</p>
<p style="text-indent: 21pt">&lt;authz:authorize&gt;是一个流程控制标签，能够在满足特定安全需求的条件下显示它的内容体。它有三个互斥的参数：</p>
<p>n&nbsp;&nbsp; ifAllGranted——是一个由逗号分隔的权限列表，用户必须拥有所有列出的权限才能渲染标签体；</p>
<p>n&nbsp;&nbsp; ifAnyGranted——是一个由逗号分隔的权限列表，用户必须至少拥有其中的一个才能渲染标签体；</p>
<p>n&nbsp;&nbsp; ifNotGranted——是一个由逗号分隔的权限列表，用户必须不拥有其中的任何一个才能渲染标签体。</p>
<p style="text-indent: 21pt">你可以轻松地想像在JSP中如何使用&lt; authz:authorize&gt;标签根据用户的权限来限制他们的行为。例如，Spring培训应用有一个向用户显示课程有关信息的课程明细页面。 对管理员来说，如果能够从课程明细页面直接跳转到课程编辑页面从而可以更新课程信息是很方便的。但你不希望这个链接对除了管理员之外的其他用户可见。</p>
<p style="text-indent: 21pt">使用&lt;authz:authorize&gt;标签，在用户没有管理员权限的情况下，你可以避免渲染到课程编辑页面的链接：</p>
<p>&nbsp; &lt;authz:authorize ifAllGranted="ROLE_ADMINISTRATOR"&gt;</p>
<p>&nbsp;&nbsp;&nbsp; &lt;a href="admin/editCourse.htm?courseId=${course.id}"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Edit Course</p>
<p>&nbsp;&nbsp;&nbsp; &lt;/a&gt;</p>
<p>&nbsp; &lt;/authz:authorize&gt;</p>
<p style="text-indent: 21pt">这里，我们使用了ifAllGranted参数，由于这里只需要检查一个授权，所以ifAllGranted标签也是可以使用的。Web应用的安全性只是Acegi功能的一个方面。现在让我们考察它的另一面——保护方法调用。</p>
<br />
<h2>扩展acegi标签实现动态定位方法对应的权限列表</h2>
<p style="text-indent: 21pt">实现如下： <br />
<br />
package com.wonder.cdc.oa.webapp.taglib;<br />
<br />
import java.util.List;<br />
import javax.servlet.ServletContext;<br />
import javax.servlet.jsp.JspException;<br />
import org.acegisecurity.GrantedAuthority;<br />
import org.acegisecurity.taglibs.authz.AuthorizeTag;<br />
import org.apache.commons.lang.StringUtils;<br />
import org.springframework.context.ApplicationContext;<br />
import org.springframework.web.context.support.WebApplicationContextUtils;<br />
import com.wonder.cdc.oa.service.security.AcegiCacheManager;<br />
import com.wonder.cdc.oa.service.security.ResourceDetails;<br />
/**<br />
* 根据当前指定方法名称，判断当前用户是否有权限访问该标签方法体<br />
* @author sudi<br />
*/<br />
public class AuthorizeFuncTag extends AuthorizeTag {<br />
/**<br />
&nbsp; * 扩展acegi标签，实现指定方法名称自动根据名称生成对应的权限字符序列传递给acegi对应的标签<br />
&nbsp; */<br />
private static final long serialVersionUID = 1L;<br />
private String funcString;<br />
public String getFuncString() {<br />
&nbsp; return funcString;<br />
}<br />
public void setFuncString(String funcString) {<br />
&nbsp; this.funcString = funcString;<br />
}<br />
<br />
&nbsp;&nbsp;&nbsp; public int doStartTag() throws JspException {<br />
&nbsp;&nbsp;&nbsp;&nbsp; //如果设置的funcString为空则显示标签体<br />
&nbsp;&nbsp;&nbsp;&nbsp; if(StringUtils.isBlank(getFuncString())){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return EVAL_BODY_INCLUDE;<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp; ServletContext context = pageContext.getServletContext();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(context); <br />
&nbsp;&nbsp;&nbsp;&nbsp; AcegiCacheManager acegiCacheManager = (AcegiCacheManager) ctx.getBean("acegiCacheManager");<br />
&nbsp;&nbsp;&nbsp;&nbsp; GrantedAuthority[] authorities = new GrantedAuthority[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp; String roles = " ";<br />
&nbsp;&nbsp;&nbsp;&nbsp; List functions = acegiCacheManager.getFunctions();<br />
&nbsp;&nbsp;&nbsp;&nbsp; String funcString;<br />
&nbsp;&nbsp;&nbsp;&nbsp; boolean findFlag = false;<br />
&nbsp;&nbsp;&nbsp;&nbsp; for(int i = 0; i &lt; functions.size(); i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; funcString = ((String)functions.get(i)).trim();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(funcString.equals(getFuncString())){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; findFlag = true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; //如果设置的funcString没有在acegi的权限控制范围内则显示标签体<br />
&nbsp;&nbsp;&nbsp;&nbsp; if(!findFlag){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return EVAL_BODY_INCLUDE;<br />
&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; //如果在acegi的权限控制范围则取出该资源相对应的权限设置到setIfAnyGranted()方法中<br />
&nbsp;&nbsp;&nbsp;&nbsp; ResourceDetails rd = acegiCacheManager.getAuthorityFromCache(getFuncString());&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; if(rd != null){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; authorities = rd.getAuthorities();&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (authorities.length &gt; 0) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; authorities.length; i++) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; roles += authorities.getAuthority() + ",";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; roles = roles.substring(0, roles.length() - 1);&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; this.setIfAnyGranted(roles);<br />
&nbsp;&nbsp;&nbsp;&nbsp; return super.doStartTag();<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
标签描述文件：<br />
&nbsp;&nbsp; &lt;tag&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;name&gt;isVisualable&lt;/name&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tag-class&gt;com.wonder.cdc.oa.webapp.taglib.AuthorizeFuncTag&lt;/tag-class&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;info&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根据当前指定方法名称，判断当前用户是否有权限访问该标签方法体<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/info&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;attribute&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;name&gt;funcString&lt;/name&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;required&gt;true&lt;/required&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/attribute&gt;<br />
&nbsp;&nbsp; &lt;/tag&gt;<br />
<br />
其中的acegiCacheManager,是由spring来维护的，用来管理用户，角色，权限，资源的缓存管理类，<br />
其与持久层是同步更新的，用以获得尽可能的性能提升。<br />
acegi的url和method的访问控制都是基于数据库的，这样要比在xml中配置更灵活。<br />
<br />
扩展前：<br />
&lt;authz:authorize ifAnyGranted="AUTH_ADMIN,AUTH_USER"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;input type="button" style="margin-right: 5px"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onclick="location.href='&lt;c:url value="/editRole.html?method=Add&amp;from=list"/&gt;'"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value="&lt;fmt:message key="button.add"/&gt;"/&gt;<br />
&lt;/authz:authorize&gt;<br />
扩展后：<br />
&lt;CDC:isVisualable funcString="RoleManager.saveRole"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;input type="button" style="margin-right: 5px"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onclick="location.href='&lt;c:url value="/editRole.html?method=Add&amp;from=list"/&gt;'"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value="&lt;fmt:message key="button.add"/&gt;"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/CDC:isVisualable&gt;<br />
<br />
可以看到，扩展前必须通过硬编码的方式指定当前用户是否有权限看到该按钮，而且一旦增加新的权限，就要对应修改所有的相关页面，要死人的阿...</p>
<br />
<p style="text-indent: 21pt"><br />
</p>
<br />
<br />
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1752951</p>
<img src ="http://www.blogjava.net/i369/aggbug/165457.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-12-05 12:50 <a href="http://www.blogjava.net/i369/articles/165457.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>幼学琼林-对比Spring 1.0与2.0的事务配置方式 </title><link>http://www.blogjava.net/i369/articles/157731.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Fri, 02 Nov 2007 04:58:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/157731.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/157731.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/157731.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/157731.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/157731.html</trackback:ping><description><![CDATA[<table width="100%">
    <tbody>
        <tr>
            <td>
            <p>Spring 2.0 的重头戏之一就是AspectJ 式 AOP 配置。 但是一定要通过对比，才能看到2.0式的AOP配置是如何跳出一片新天空的。</p>
            <p><strong>1. 对比</strong> <br />
            先看1.0的标准事务配置:</p>
            <p>Spring 2.0 的重头戏之一就是AspectJ 式 AOP 配置。 但是一定要通过对比，才能看到2.0式的AOP配置是如何跳出一片新天空的。</p>
            <p>1. 对比 <br />
            <strong>先看1.0的标准事务配置:</strong></p>
            <p>
            <div class="code_title">代码</div>
            <div class="code_div">
            <div class="dp-highlighter">
            <div class="bar"></div>
            <ol class="dp-xml">
                <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">"baseTxService"</span><span>&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">"org.springframework.transaction.interceptor.TransactionProxyFactoryBean"</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">abstract</span><span>=</span><span class="attribute-value">"true"</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">"transactionManager"</span><span>&nbsp;</span><span class="attribute">ref</span><span>=</span><span class="attribute-value">"transactionManager"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">"proxyTargetClass"</span><span>&nbsp;</span><span class="attribute">value</span><span>=</span><span class="attribute-value">"true"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">"transactionAttributes"</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">props</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">prop</span><span>&nbsp;</span><span class="attribute">key</span><span>=</span><span class="attribute-value">"get*"</span><span class="tag">&gt;</span><span>PROPAGATION_REQUIRED,readOnly</span><span class="tag">&lt;/</span><span class="tag-name">prop</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">prop</span><span>&nbsp;</span><span class="attribute">key</span><span>=</span><span class="attribute-value">"find*"</span><span class="tag">&gt;</span><span>PROPAGATION_REQUIRED,readOnly</span><span class="tag">&lt;/</span><span class="tag-name">prop</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">prop</span><span>&nbsp;</span><span class="attribute">key</span><span>=</span><span class="attribute-value">"save*"</span><span class="tag">&gt;</span><span>PROPAGATION_REQUIRED</span><span class="tag">&lt;/</span><span class="tag-name">prop</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">prop</span><span>&nbsp;</span><span class="attribute">key</span><span>=</span><span class="attribute-value">"remove*"</span><span class="tag">&gt;</span><span>PROPAGATION_REQUIRED</span><span class="tag">&lt;/</span><span class="tag-name">prop</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">props</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">"preInterceptors"</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">list</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ref</span><span>&nbsp;</span><span class="attribute">bean</span><span>=</span><span class="attribute-value">"methodSecurityInterceptor"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">list</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">bean</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">"bookManager"</span><span>&nbsp;</span><span class="attribute">parent</span><span>=</span><span class="attribute-value">"baseTxService"</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">"target"</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">"org.springside.bookstore.admin.manager.BookManager"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">bean</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
            </ol>
            </div>
            </div>
            <script>render_code();</script>
            <p>&nbsp;</p>
            <p><strong>再看2.0的新配置：</strong></p>
            <p>
            <div class="code_title">代码</div>
            <div class="code_div">
            <div class="dp-highlighter">
            <div class="bar"></div>
            <ol class="dp-xml">
                <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">aop:config</span><span>&nbsp;</span><span class="attribute">proxy-target-class</span><span>=</span><span class="attribute-value">"true"</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">aop:advisor</span><span>&nbsp;</span><span class="attribute">pointcut</span><span>=</span><span class="attribute-value">"execution(*&nbsp;yourpackagename..*Manager.*(..))"</span><span>&nbsp;</span><span class="attribute">advice-ref</span><span>=</span><span class="attribute-value">"txAdvice"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">aop:advisor</span><span>&nbsp;</span><span class="attribute">pointcut</span><span>=</span><span class="attribute-value">"execution(*&nbsp;yourpackagename..*Manager.save(..))"</span><span>&nbsp;</span><span class="attribute">advice-ref</span><span>=</span><span class="attribute-value">"fooAdvice"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span class="tag">&lt;/</span><span class="tag-name">aop:config</span><span class="tag">&gt;</span><span class="tag">&lt;</span><span class="tag-name">tx:advice</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">"txAdvice"</span><span>&nbsp;</span><span class="attribute">transaction-manager</span><span>=</span><span class="attribute-value">"transactionManager"</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">tx:attributes</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">tx:method</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">"save*"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">tx:method</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">"remove*"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">tx:method</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">"*"</span><span>&nbsp;</span><span class="attribute">read-only</span><span>=</span><span class="attribute-value">"true"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;/</span><span class="tag-name">tx:attributes</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class=""><span class="tag">&lt;/</span><span class="tag-name">tx:advice</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
                <li class="alt"><span>&nbsp;&nbsp;</span></li>
                <li class=""><span class="tag">&lt;</span><span class="tag-name">bean</span><span>&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">"bookManager"</span><span>&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">"org.springside.bookstore.commons.service.BookManager"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
            </ol>
            </div>
            </div>
            <script>render_code();</script>
            <p>&nbsp;</p>
            <p><strong>2.进步</strong> <br />
            <strong>1. AOP的配置方式也AOP了。</strong> <br />
            对比1.0的配置文件，因为下面2提到的限制，事关安全acegi methodSecurityInterceptor 拦截器要配置在关于事务的TransactionProxyFactoryBean的preInterceptors属性里，这样子就一点不AOP了，而2.0使用ponintcut expression，很AOP的配置一切Aspect。 <br />
            <br />
            <strong>2. 1.0时，一个已经AOP过的object不能再次被AOP。</strong> <br />
            在Spring 1.0的文档里Rod说，比如&lt;bean id="bookManager" parent="baseTxService"&gt;已经进行了一次AOP，如果想在这个Bean上再配一层AOP，比如要对方法执行结果缓存，无论以1.0 还是2.0的方式定义，cglib方式是会报错的，而基于接口的方式，结果不确定。 <br />
            <br />
            <strong>3. BookManager能直接定义自己，而不是像1.0那样作匿名内部target。</strong></p>
            <p>虽然在1.0时代的BeanNameAutoProxyCreator 达到类似作用，但只能用BeanName来匹配比较危险，没有AspectJ的pointcut语法细致。</p>
            <p><strong>3. 语法</strong> <br />
            满江红翻译的Spring参考文档 6.3 schema-based AOP support 提供了aspect,advisor,advide三种组装方法的解释，其中aspect是aspectJ原装，但稍复杂.</p>
            <p>唯一有点难懂的是pointcut里的语法，其实也很好学，Spring<a href="http://www.redsaga.com/spring_ref/2.0RC2/html/aop.html#d0e6939" target="blank">参考文档6.2.3.4里</a>有完整说明 ，其实一排子过去是</p>
            <p>
            <div class="code_title">代码</div>
            <div class="code_div">
            <div class="dp-highlighter">
            <div class="bar"></div>
            <ol class="dp-j">
                <li class="alt"><span><span>execution(modifiers-pattern?&nbsp;ret-type-pattern&nbsp;declaring-type-pattern?&nbsp;name-pattern(param-pattern)&nbsp;</span><span class="keyword">throws</span><span>-pattern?)&nbsp;&nbsp;</span></span></li>
            </ol>
            </div>
            </div>
            <script>render_code();</script>其中带问号的modifiers-pattern?(public/protected) 和 declaring-type-pattern? throws-pattern? 可以不填
            <p>&nbsp;</p>
            <p>可见execution(* *..BookManager.save(..))</p>
            <p>第一颗* 代表ret-type-pattern 返回值可任意， <br />
            *..BookManager 代表任意Pacakge里的BookManager类。 <br />
            如果写成com.xyz.service.* 则代表com.xyz.service下的任意类 <br />
            com.xyz.service..* com.xyz.service则代表com.xyz.service及其子package下的任意类 <br />
            save代表save方法，也可以写save* 代表saveBook()等方法 <br />
            (..) 匹配0个参数或者多个参数的，任意类型 <br />
            (x,..) 第一个参数的类型必须是X <br />
            (x,,,s,..) 匹配至少4个参数，第一个参数必须是x类型，第二个和第三个参数可以任意，第四个必须是s类型。 </p>
            <p>注意name-pattern千万不要写成*..*Manager ，这样子的话会把所有第三方类库的Manager比如Spring的PlatformTranstationManager 也加入aop，非常危险。所以最好还是加上项目的package前缀，如org.springside <br />
            </p>
            </td>
        </tr>
    </tbody>
</table>
<br />
<span class="graytag">声明：JavaEye文章版权属于作者，受法律保护。没有作者书面许可不得转载。若作者同意转载，必须以超链接形式标明文章原始出处和作者。</span> <br />
<hr />
<span class="graytag">相关文章：</span></a> &nbsp;&nbsp;<a href="http://www.javaeye.com/topic/24425">selenium参考手册中文翻译</a> <br />
<br />
转载自:JavaEye文章(http://www.javaeye.com/topic/27099)
<img src ="http://www.blogjava.net/i369/aggbug/157731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-11-02 12:58 <a href="http://www.blogjava.net/i369/articles/157731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于spring 2.0自定义xml 标记 </title><link>http://www.blogjava.net/i369/articles/157730.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Fri, 02 Nov 2007 04:56:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/157730.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/157730.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/157730.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/157730.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/157730.html</trackback:ping><description><![CDATA[<span style="color: red">关于spring 2.0自定义xml 标记 （一 主要的相关类）</span> <br />
<br />
<p>在spring 2.0 中，增加了自定义xml标记这一重大的功能。下面主要看一下spring 2.0实现这一功能的主要相关类：</p>
<p><strong>NamespaceHandlerResolver（接口）</strong> <br />
由DefaultBeanDefinitionDocumentReader使用，用于定位NamespaceHandler，指定特定的命名空间uri</p>
<p>实现类： <br />
<strong>DefaultNamespaceHandlerResolver</strong> <br />
<br />
通过map 保存所有的对应关系 <br />
默认使用spring.handlers文件来保存所有的handlers <br />
可以定义其他的location 如：</p>
<p>String location = "org/springframework/beans/factory/xml/support/customNamespace.properties"; <br />
NamespaceHandlerResolver resolver = new DefaultNamespaceHandlerResolver(getClass().getClassLoader(), location); <br />
<br />
<strong>NamespaceHandler（接口）</strong> <br />
<br />
基础接口，用于DefaultBeanDefinitionDocumentReader处理自定义命名空间。 <br />
方法： <br />
void init(); <br />
由DefaultBeanDefinitionDocumentReader调用在构造完后但在解析自定义元素前。 <br />
<br />
BeanDefinition parse(Element element, ParserContext parserContext); <br />
解析指定的元素。 <br />
<br />
BeanDefinitionHolder decorate(Node element, <br />
BeanDefinitionHolder definition, <br />
ParserContext parserContext); <br />
执行相应的修饰。</p>
<p>实现类： <br />
<br />
<strong>NamespaceHandlerSupport（抽象类）</strong> <br />
<br />
主要的三个方法： <br />
protected final void registerBeanDefinitionDecorator( <br />
String elementName, <br />
BeanDefinitionDecorator decorator) <br />
注册decorator，通过element <br />
<br />
protected final void registerBeanDefinitionDecoratorForAttribute( <br />
String attributeName, <br />
BeanDefinitionDecorator decorator) <br />
注册decorator，通过attr <br />
<br />
protected final void registerBeanDefinitionParser( <br />
String elementName, <br />
BeanDefinitionParser parser) <br />
注册BeanDefinitionParser，通过element</p>
<p>实际的操作由具体的BeanDefinitionDecorator 或者BeanDefinitionParser 执行</p>
<p><strong>BeanDefinitionDecorator(接口)</strong></p>
<p>装饰相关的自定义属性。</p>
<p>AbstractInterceptorDrivenBeanDefinitionDecorator <br />
用于注册相应的Interceptor bean 定义，使用aop代理</p>
<p><strong>其他类：</strong></p>
<p>PluggableSchemaResolver，用于自定义相关的schema,默认的schema 保存于spring.schemas文件中</p>
<p>可以通过覆盖resolveEntity方法来装载相应的自定义xsd文件</p>
<p><strong>主要的执行类：</strong> <br />
<br />
XmlBeanDefinitionReader <br />
<br />
用于处理相应的读取工作，其实主要的工作委派给BeanDefinitionDocumentReader</p>
<p>实际的类，就介绍到这，下一节通过实例来说明如何定义自定义xml 元素</p>
<br />
<span style="color: red">关于spring 2.0自定义xml 标记 （二 如何实现）</span> <br />
<p>看了spring test 用例，其实实现这一功能还算比较简单，主要分以下的步骤，具体的实例可以去参考spring 自带的testcase</p>
<p>首先定义相关xsd文件，用于验证相应的行为： <br />
<br />
主要增加了4个自定义元素和1个属性：</p>
<p>&lt;?xml version="1.0" encoding="UTF-8" standalone="no"?&gt;</p>
<p>&lt;xsd:schema xmlns="http://www.springframework.org/schema/beans/test" <br />
xmlns:xsd="http://www.w3.org/2001/XMLSchema" <br />
targetNamespace="http://www.springframework.org/schema/beans/test" <br />
elementFormDefault="qualified"&gt;</p>
<p>&lt;xsd:element name="testBean"&gt; <br />
&lt;xsd:complexType&gt; <br />
&lt;xsd:attribute name="id" type="xsd:string" use="required" form="unqualified"/&gt; <br />
&lt;xsd:attribute name="name" type="xsd:string" use="required" form="unqualified"/&gt; <br />
&lt;xsd:attribute name="age" type="xsd:integer" use="required" form="unqualified"/&gt; <br />
&lt;/xsd:complexType&gt; <br />
&lt;/xsd:element&gt;</p>
<p>&lt;xsd:element name="set"&gt; <br />
&lt;xsd:complexType&gt; <br />
&lt;xsd:attribute name="name" type="xsd:string" use="required" form="unqualified"/&gt; <br />
&lt;xsd:attribute name="age" type="xsd:integer" use="required" form="unqualified"/&gt; <br />
&lt;/xsd:complexType&gt; <br />
&lt;/xsd:element&gt;</p>
<p>&lt;xsd:element name="debug"/&gt; <br />
&lt;xsd:element name="nop"/&gt;</p>
<p>&lt;xsd:attribute name="object-name" type="xsd:string"/&gt;</p>
<p>&lt;/xsd:schema&gt;</p>
<p>接着定义handler映射文件：customNamespace.properties</p>
<p><a href="http://www.javaeye.com/topic/http/://www.springframework.org/schema/beans/test=org.springframework.beans.factory.xml.support.TestNamespaceHandler" target="blank">http\://www.springframework.org/schema/beans/test=org.springframework.beans.factory.xml.support.TestNamespaceHandler</a></p>
<p>定义Handler:</p>
<p>主要注册相应的解析类和装饰类</p>
<p>&nbsp;</p>
<p>publicclass TestNamespaceHandler extends NamespaceHandlerSupport {</p>
<p>publicvoid init() {</p>
<p>//相对于每个xsd中定义的元素</p>
<p>registerBeanDefinitionParser("testBean", new TestBeanDefinitionParser());</p>
<p>registerBeanDefinitionDecorator("set", new PropertyModifyingBeanDefinitionDecorator());</p>
<p>registerBeanDefinitionDecorator("debug", new DebugBeanDefinitionDecorator());</p>
<p>registerBeanDefinitionDecorator("nop", new NopInterceptorBeanDefinitionDecorator());</p>
<p>registerBeanDefinitionDecoratorForAttribute("object-name", new ObjectNameBeanDefinitionDecorator());</p>
<p>}</p>
<p>}</p>
<p>&nbsp;</p>
<p>定义各个解析类：</p>
<p>privatestaticclass TestBeanDefinitionParser implements BeanDefinitionParser {</p>
<p>public BeanDefinition parse(Element element, ParserContext parserContext) {</p>
<p>RootBeanDefinition definition = new RootBeanDefinition();</p>
<p>definition.setBeanClass(TestBean.class);</p>
<p>&nbsp;</p>
<p>MutablePropertyValues mpvs = new MutablePropertyValues();</p>
<p>mpvs.addPropertyValue("name", element.getAttribute("name"));</p>
<p>mpvs.addPropertyValue("age", element.getAttribute("age"));</p>
<p>definition.setPropertyValues(mpvs);</p>
<p>&nbsp;</p>
<p>parserContext.getRegistry().registerBeanDefinition(element.getAttribute("id"), definition);</p>
<p>&nbsp;</p>
<p>returnnull;</p>
<p>}</p>
<p>}</p>
<p>privatestaticclassPropertyModifyingBeanDefinitionDecorator implements BeanDefinitionDecorator {</p>
<p>public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition,</p>
<p>ParserContext parserContext) {</p>
<p>Element element = (Element)node;</p>
<p>BeanDefinition def = definition.getBeanDefinition();</p>
<p>&nbsp;</p>
<p>MutablePropertyValues mpvs = (def.getPropertyValues() == null) ?</p>
<p>new MutablePropertyValues() : def.getPropertyValues();</p>
<p>mpvs.addPropertyValue("name", element.getAttribute("name"));</p>
<p>mpvs.addPropertyValue("age", element.getAttribute("age"));</p>
<p>&nbsp;</p>
<p>((AbstractBeanDefinition) def).setPropertyValues(mpvs);</p>
<p>return definition;</p>
<p>}</p>
<p>}</p>
<p>privatestaticclassDebugBeanDefinitionDecorator extends AbstractInterceptorDrivenBeanDefinitionDecorator {</p>
<p>&nbsp;</p>
<p>protected BeanDefinition createInterceptorDefinition(Node node) {</p>
<p>returnnew RootBeanDefinition(DebugInterceptor.class);</p>
<p>}</p>
<p>}</p>
<p>privatestaticclassNopInterceptorBeanDefinitionDecorator extends</p>
<p>AbstractInterceptorDrivenBeanDefinitionDecorator {</p>
<p>&nbsp;</p>
<p>protected BeanDefinition createInterceptorDefinition(Node node) {</p>
<p>returnnew RootBeanDefinition(NopInterceptor.class);</p>
<p>}</p>
<p>}</p>
<p>privatestaticclassObjectNameBeanDefinitionDecorator implements BeanDefinitionDecorator {</p>
<p>public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition,</p>
<p>ParserContext parserContext) {</p>
<p>Attr objectNameAttribute = (Attr)node;</p>
<p>definition.getBeanDefinition().setAttribute("objectName", objectNameAttribute.getValue());</p>
<p>return definition;</p>
<p>}</p>
<p>}</p>
<p>&nbsp;</p>
<p>可以定义EntityResolver，用于验证相应的xsd</p>
<p>privateclass DummySchemaResolver extends PluggableSchemaResolver {</p>
<p>&nbsp;</p>
<p>public DummySchemaResolver() {</p>
<p>super(CustomNamespaceHandlerTests.this.getClass().getClassLoader());</p>
<p>}</p>
<p>&nbsp;</p>
<p>public InputSource resolveEntity(String publicId, String systemId) throws IOException {</p>
<p>InputSource source = super.resolveEntity(publicId, systemId);</p>
<p>if (source == null) {</p>
<p>Resource resource = <br />
new ClassPathResource("org/springframework/beans/factory/xml/support/spring-test.xsd");</p>
<p>source = new InputSource(resource.getInputStream());</p>
<p>source.setPublicId(publicId);</p>
<p>source.setSystemId(systemId);</p>
<p>}</p>
<p>return source;</p>
<p>}</p>
<p>}</p>
<p>关键的一步，如何生效：</p>
<p>String location = "org/springframework/beans/factory/xml/support/customNamespace.properties";</p>
<p>NamespaceHandlerResolver resolver = new DefaultNamespaceHandlerResolver( <br />
getClass().getClassLoader(), location);</p>
<p>DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();</p>
<p>XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);</p>
<p>reader.setNamespaceHandlerResolver(resolver);</p>
<p>reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);</p>
<p>reader.setEntityResolver(new DummySchemaResolver());</p>
<p>reader.loadBeanDefinitions(getResource());</p>
<p>写一个测试xml文件：</p>
<p>&lt;?xmlversion="1.0"encoding="UTF-8"?&gt;</p>
<p>&lt;beansxmlns="http://www.springframework.org/schema/beans"</p>
<p>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"</p>
<p>xmlns:test="http://www.springframework.org/schema/beans/test"</p>
<p>xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd</p>
<p><a href='http://www.springframework.org/schema/beans/testhttp://www.springframework.org/schema/beans/factory/xml/support/spring-test.xsd">' target=blank href_cetemp='http://www.springframework.org/schema/beans/testhttp://www.springframework.org/schema/beans/factory/xml/support/spring-test.xsd">'>http://www.springframework.org/schema/beans/testhttp://www.springframework.org/schema/beans/factory/xml/support/spring-test.xsd"&gt;</a></p>
<p>&lt;test:testBeanid="testBean"name="Rob Harrop"age="23"/&gt;</p>
<p>&lt;beanid="customisedTestBean"class="org.springframework.beans.TestBean"&gt;</p>
<p>&lt;test:setname="Rob Harrop"age="23"/&gt;</p>
<p>&lt;/bean&gt;</p>
<p>&lt;beanid="debuggingTestBean"class="org.springframework.beans.TestBean"&gt;</p>
<p>&lt;test:debug/&gt;</p>
<p>&lt;propertyname="name"value="Rob Harrop"/&gt;</p>
<p>&lt;propertyname="age"value="23"/&gt;</p>
<p>&lt;/bean&gt;</p>
<p>&lt;beanid="chainedTestBean"class="org.springframework.beans.TestBean"&gt;</p>
<p>&lt;test:debug/&gt;</p>
<p>&lt;test:nop/&gt;</p>
<p>&lt;propertyname="name"value="Rob Harrop"/&gt;</p>
<p>&lt;propertyname="age"value="23"/&gt;</p>
<p>&lt;/bean&gt;</p>
<p>&lt;beanid="decorateWithAttribute"class="org.springframework.beans.TestBean"test:object-name="foo"/&gt;</p>
<p>&lt;/beans&gt;</p>
<p>相关的测试方法：</p>
<p>publicvoid testSimpleParser() throws Exception {</p>
<p>TestBean bean = (TestBean) this.beanFactory.getBean("testBean");</p>
<p>assetTestBean(bean);</p>
<p>}</p>
<p>publicvoid testSimpleDecorator() throws Exception {</p>
<p>TestBean bean = (TestBean) this.beanFactory.getBean("customisedTestBean");</p>
<p>assetTestBean(bean);</p>
<p>}</p>
<p>publicvoid testProxyingDecorator() throws Exception {</p>
<p>ITestBean bean = (ITestBean) this.beanFactory.getBean("debuggingTestBean");</p>
<p>assetTestBean(bean);</p>
<p>assertTrue(AopUtils.isAopProxy(bean));</p>
<p>Advisor[] advisors = ((Advised) bean).getAdvisors();</p>
<p>assertEquals("Incorrect number of advisors", 1, advisors.length);</p>
<p>assertEquals("Incorrect advice class.", DebugInterceptor.class, advisors[0].getAdvice().getClass());</p>
<p>}</p>
<p>publicvoid testChainedDecorators() throws Exception {</p>
<p>ITestBean bean = (ITestBean) this.beanFactory.getBean("chainedTestBean");</p>
<p>assetTestBean(bean);</p>
<p>assertTrue(AopUtils.isAopProxy(bean));</p>
<p>Advisor[] advisors = ((Advised) bean).getAdvisors();</p>
<p>assertEquals("Incorrect number of advisors", 2, advisors.length);</p>
<p>assertEquals("Incorrect advice class.", DebugInterceptor.class, advisors[0].getAdvice().getClass());</p>
<p>assertEquals("Incorrect advice class.", NopInterceptor.class, advisors[1].getAdvice().getClass());</p>
<p>}</p>
<p>publicvoid testDecorationViaAttribute() throws Exception {</p>
<p>RootBeanDefinition beanDefinition <br />
= (RootBeanDefinition)this.beanFactory.getBeanDefinition("decorateWithAttribute");</p>
<p>assertEquals("foo", beanDefinition.getAttribute("objectName"));</p>
<p>}</p>
<p>privatevoid assetTestBean(ITestBean bean) {</p>
<p>assertEquals("Invalid name", "Rob Harrop", bean.getName());</p>
<p>assertEquals("Invalid age", 23, bean.getAge());</p>
<p>}</p>
<br />
<img src ="http://www.blogjava.net/i369/aggbug/157730.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-11-02 12:56 <a href="http://www.blogjava.net/i369/articles/157730.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]CAS及客户端Acegi的安装配置指南</title><link>http://www.blogjava.net/i369/articles/155776.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 25 Oct 2007 01:19:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/155776.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/155776.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/155776.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/155776.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/155776.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: CAS及客户端Acegi的安装配置指南（上）作者：龙智&nbsp;(Dragon)时间：2006-07-09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CAS（Central Authentication Service）是耶鲁大学开发的一个开源的SSO（single sign on，单点登录）系统。它提供了丰富的客...&nbsp;&nbsp;<a href='http://www.blogjava.net/i369/articles/155776.html'>阅读全文</a><img src ="http://www.blogjava.net/i369/aggbug/155776.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-10-25 09:19 <a href="http://www.blogjava.net/i369/articles/155776.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]浅谈Acegi配置</title><link>http://www.blogjava.net/i369/articles/155769.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 25 Oct 2007 01:06:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/155769.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/155769.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/155769.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/155769.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/155769.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Acegi配置文档 作者：javafish(likunkun) Email:javafish@sunxin.org Acegi是基于Spring的一个开源的安全认证框架，现在的最新版本是1.04。Acegi的特点就是有很多的过滤器：不过我们也用不到这么多的过滤器，只是可以把它们看作为一个个的模块，在用的时候加上自己用的着的即可，由于认证的流程的方面比较复杂导致它的配置很复杂，如果能摸清...&nbsp;&nbsp;<a href='http://www.blogjava.net/i369/articles/155769.html'>阅读全文</a><img src ="http://www.blogjava.net/i369/aggbug/155769.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-10-25 09:06 <a href="http://www.blogjava.net/i369/articles/155769.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring源码学习</title><link>http://www.blogjava.net/i369/articles/154995.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Mon, 22 Oct 2007 06:41:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/154995.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/154995.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/154995.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/154995.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/154995.html</trackback:ping><description><![CDATA[<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt; tab-stops: list 21.0pt"><strong>一、<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></strong><strong><span style="font-family: 宋体">概述</span> </strong></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt; tab-stops: list 21.0pt"><strong>二、<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></strong><strong>Spring </strong><strong><span style="font-family: 宋体">初始化之旅</span> </strong></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt">a)<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Spring <span style="font-family: 宋体">初始化的时候首先要运行的类为：</span> org.springframework.web.context. ContextLoaderListener <span style="font-family: 宋体">或</span> org.springframework.web.context. ContextLoaderServlet <span style="font-family: 宋体">。</span> </p>
<p style="margin: 0cm 0cm 0pt 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">它们在初始化函数里无一例外地实例化了</span> ContextLoader <span style="font-family: 宋体">类</span> , <span style="font-family: 宋体">然后调用了它的函数</span> &nbsp;&nbsp;&nbsp;&nbsp; public WebApplicationContext initWebApplicationContext(ServletContext ) <span style="font-family: 宋体">。</span> </p>
<p style="margin: 0cm 0cm 0pt 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">接下来看一下在这个方法里干了写什么</span> </p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt">b)<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">在他的方法体内，关键是&#8220;</span> this.context = createWebApplicationContext(servletContext, parent); <span style="font-family: 宋体">&#8221;新建了一个&#8220;</span> ConfigurableWebApplicationContext <span style="font-family: 宋体">&#8221;类型的对象，在这一步实例化中几乎完成了所有的</span> spring <span style="font-family: 宋体">初始化工作。读取了所有的</span> spring <span style="font-family: 宋体">配置文件。它的工作步骤如下所述。</span> </p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt">c)<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">首先，在将&#8220;</span> ConfigurableWebApplicationContext <span style="font-family: 宋体">&#8221;类型的对象实例化以后（这个对象实际的类型是这个包内的</span> XmlWebApplicationContext <span style="font-family: 宋体">），然后又给这个实例设置了三个属性，&#8220;</span> wac.setParent(parent); <span style="font-family: 宋体">&#8221;在默认的初始化过程中这一步设置了一个</span> null <span style="font-family: 宋体">值，然后又设置了一个&#8220;</span> wac.setServletContext(servletContext); <span style="font-family: 宋体">&#8221;，将系统默认的上下文设置进来，比较重要的是下面这一段：</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p style="margin: 0cm 0cm 0pt 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (configLocation != null) { </p>
<p style="margin: 0cm 0cm 0pt 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // <span style="font-family: 宋体">读取</span> spring <span style="font-family: 宋体">的应用配置文件</span> </p>
<p style="margin: 0cm 0cm 0pt 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,Configurabl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eWebApplicationContext.CONFIG_LOCATION_DELIMITERS)); </p>
<p style="margin: 0cm 0cm 0pt 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </p>
<p style="margin: 0cm 0cm 0pt 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">它将我们的配置文件名放置到</span> wac <span style="font-family: 宋体">变量中，以待在后续的操作中使用。然后调用</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">&#8220;</span> wac.refresh(); <span style="font-family: 宋体">&#8221;完成主要的初始化</span> BeanFactory <span style="font-family: 宋体">的操作。如下。</span> </p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt">d)<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">首先我们应该看一下我们实例化的对象</span> </p>
<p style="margin: 0cm 0cm 0pt 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; org.springframework.web.context.support.XmlWebApplicationContext <span style="font-family: 宋体">的类图：</span> </p>
<p style="margin: 0cm 0cm 0pt 21pt"><img height="639" alt="xx.jpg" src="http://www.blogjava.net/images/blogjava_net/javajohn/xx.jpg" width="1183" border="0" /> </p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt">e)<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">&#8220;</span> wac.refresh(); <span style="font-family: 宋体">&#8221;从类结构里我们能找到这个方法来自它的父类：</span> AbstractApplicationContext <span style="font-family: 宋体">在它的</span> refresh() <span style="font-family: 宋体">方法内我们可以看到</span> spring <span style="font-family: 宋体">的复杂逻辑。</span> </p>
<p style="margin: 0cm 0cm 0pt 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">首先执行了</span> refreshBeanFactory(); <span style="font-family: 宋体">（来自</span> AbstractRefreshableApplicationContext <span style="font-family: 宋体">）见</span> f), </p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt">f)<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>refreshBeanFactory(); <span style="font-family: 宋体">这个方法由负责维护变量</span> beanFactory <span style="font-family: 宋体">的子类</span> AbstractRefreshableApplicationContext <span style="font-family: 宋体">实现，默认情况下这个方法直接实例化一个新的</span> DefaultListableBeanFactory <span style="font-family: 宋体">类型的</span> BeanFacorty, <span style="font-family: 宋体">然后调用一个起缓冲作用的配置函数生成一个将</span> beanFacroty <span style="font-family: 宋体">包装起来的对象</span> beanDefinitionReader <span style="font-family: 宋体">，然后对这个对象进行属性配置，实际上该方法主要负责生成一个临时的操作对象，对应调用的函数为&#8220;</span> loadBeanDefinitions(beanFactory); <span style="font-family: 宋体">&#8221;该方法为初始化期间较为重要的一个。</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">该方法来自其子类：</span> AbstractRefreshableWebApplicationContext <span style="font-family: 宋体">对应的函数：</span> </p>
<p style="margin: 0cm 0cm 0pt 42pt">protected void loadBeanDefinitions(DefaultListableBeanFactory) <span style="font-family: 宋体">，然后这里又调用了自己定义的</span> protected void loadBeanDefinitions(XmlBeanDefinitionReader) <span style="font-family: 宋体">方法。此时，它就使用到了在</span> c) <span style="font-family: 宋体">中设置了的（</span> wac.setConfigLocations(&#8230;&#8230;)) <span style="font-family: 宋体">我们开发中密切相关的配置文件。（同时也要记住此时这个函数的参数</span> beanDefinitionReader <span style="font-family: 宋体">，之前已经设置了&#8220;</span> <strong><span style="color: gray">beanDefinitionReader.setResourceLoader(this);</span> </strong><span style="font-family: 宋体">&#8221;这里的</span> this <span style="font-family: 宋体">是我们在前面见到的</span> <strong><span style="color: gray">XmlWebApplicationContext</span> </strong><span style="font-family: 宋体">（一个定义好了的上下文））。接着往下：</span> </p>
<p style="margin: 0cm 0cm 0pt 42pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">&#8220;</span> reader.loadBeanDefinitions(configLocations[i]); <span style="font-family: 宋体">&#8221;</span> reader <span style="font-family: 宋体">开始加载我们配置文件内的东西了，不过真正复杂的实现此时才开始，我们继续往下走，在接下来的方法内默认情况下会执行：</span> </p>
<p style="margin: 0cm 0cm 0pt 42pt">if (resourceLoader instanceof ResourcePatternResolver) <span style="font-family: 宋体">（该判断条件为</span> true <span style="font-family: 宋体">）</span> , <span style="font-family: 宋体">由于从上面我们知道：</span> <strong><span style="color: gray">beanDefinitionReader.setResourceLoader(this);</span> </strong><span style="font-family: 宋体">而</span> <span style="color: gray">this</span> <span style="font-family: 宋体">的类型为：</span> <strong><span style="color: gray">XmlWebApplicationContext</span> </strong><span style="font-family: 宋体">所以</span> <strong><span style="color: gray">((ResourcePatternResolver) resourceLoader).getResources(location);</span> </strong><span style="font-family: 宋体">得到一个</span> Resource[] <span style="font-family: 宋体">数组，接下来调用：</span> </p>
<p style="margin: 0cm 0cm 0pt 42pt"><strong><span style="color: gray">int loadCount = loadBeanDefinitions(resources);</span> </strong><span style="font-family: 宋体">该函数继续调用自己子类定义的一系列临时接口最终执行到</span> <strong><span style="color: gray">return doLoadBeanDefinitions(inputSource, encodedResource.getResource());</span> </strong><span style="font-family: 宋体">在这个函数内初始化了处理</span> xml <span style="font-family: 宋体">文件的一些对象并将用户的配置文件解析为一个</span> Document <span style="font-family: 宋体">对象。然后又执行了一系列函数直到</span> </p>
<p style="margin: 0cm 0cm 0pt 42pt"><strong><span style="color: gray">return parser.registerBeanDefinitions(this, doc, resource);</span> </strong><span style="font-family: 宋体">这个函数来自我们新建的</span> DefaultXmlBeanDefinitionParser <span style="font-family: 宋体">，在这个类里最终执行了对</span> xml <span style="font-family: 宋体">文件的解析工作和对</span> beanFacroty <span style="font-family: 宋体">变量执行了设置工作。</span> </p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt">g)<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">终于我们从这些繁杂的逻辑中跳了出来，继续执行</span> AbstractApplicationContext.refresh() <span style="font-family: 宋体">下面的工作，后续的代码主要仍旧是往一些常量里面设值。</span> </p>
<span style="font-size: 10.5pt; font-family: 宋体">此时</span> <span style="font-size: 10.5pt; font-family: 'Times New Roman'">spring</span> <span style="font-size: 10.5pt; font-family: 宋体">初始化过程就结束了。</span> 
<img src ="http://www.blogjava.net/i369/aggbug/154995.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-10-22 14:41 <a href="http://www.blogjava.net/i369/articles/154995.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]回复：《如何在struts+spring+hibernate的框架下构建低耦合高内聚的软件》 </title><link>http://www.blogjava.net/i369/articles/152868.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Mon, 15 Oct 2007 01:44:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/152868.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/152868.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/152868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/152868.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/152868.html</trackback:ping><description><![CDATA[<table width="100%">
    <tbody>
        <tr>
            <td>&nbsp;
            <p><span Times Roman?;?2?><a href="http://fangang.javaeye.com/admin/show/47670">如何在struts+spring+hibernate的框架下构建低耦合高内聚的软件</a></font></span>》。昨天和几个朋友讨论问题的时候，谈到通过</span>DaoSupport</p>
            <h3>2<span Times Roman?;?Arial?><font face="Arial">
            <div>java 代码</div>
            </font></font></span>
            <h3><span>
            <div>
            <ol>
                <li>package&nbsp;com.htxx.thps.psb.dao.imp; &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>import&nbsp;com.htxx.service.dao.BasicDao; &nbsp;&nbsp;
                <li>import&nbsp;com.htxx.service.dao.Condition; &nbsp;&nbsp;
                <li>import&nbsp;com.htxx.service.dao.ResultSet; &nbsp;&nbsp;
                <li>import&nbsp;com.htxx.thps.model.PsPsb; &nbsp;&nbsp;
                <li>import&nbsp;com.htxx.thps.model.PsPsjcd; &nbsp;&nbsp;
                <li>import&nbsp;com.htxx.thps.model.PsWtmx; &nbsp;&nbsp;
                <li>import&nbsp;com.htxx.thps.psb.dao.PsbDao; &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>/** &nbsp;
                <li>&nbsp;*&nbsp;@author&nbsp;FanGang &nbsp;
                <li>&nbsp;* &nbsp;
                <li>&nbsp;*/&nbsp;&nbsp;
                <li>public&nbsp;class&nbsp;PsbDaoImp&nbsp;extends&nbsp;BasicDao&nbsp;implements&nbsp;PsbDao&nbsp;{ &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;(non-Javadoc) &nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@see&nbsp;com.htxx.thps.psb.dao.PsbDao#getPsb(java.lang.String) &nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;PsPsb&nbsp;getPsb(String&nbsp;id)&nbsp;{ &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(PsPsb)this.load(PsPsb.class,&nbsp;id); &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;(non-Javadoc) &nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@see&nbsp;com.htxx.thps.psb.dao.PsbDao#updatePsb(com.htxx.thps.model.PsPsb) &nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;updatePsb(PsPsb&nbsp;vo)&nbsp;{ &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.update(vo); &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;(non-Javadoc) &nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@see&nbsp;com.htxx.thps.psb.dao.PsbDao#getPsb(com.htxx.service.dao.Condition) &nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;ResultSet&nbsp;getPsb(Condition&nbsp;condition)&nbsp;{ &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;this.query("PsPsb",&nbsp;condition); &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;PsPsjcd&nbsp;getPsjcd(String&nbsp;id)&nbsp;{ &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(PsPsjcd)this.load(PsPsjcd.class,&nbsp;id); &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;ResultSet&nbsp;getPsjcd(Condition&nbsp;condition)&nbsp;{ &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;this.query("PsPsjcd",&nbsp;condition); &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;PsWtmx&nbsp;getWtmx(String&nbsp;id)&nbsp;{ &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(PsWtmx)this.load(PsWtmx.class,&nbsp;id); &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;ResultSet&nbsp;getWtmx(Condition&nbsp;condition)&nbsp;{ &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;this.query("PsWtmx",&nbsp;condition); &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;updatePsjcd(PsPsjcd&nbsp;vo)&nbsp;{ &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.update(vo); &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;updateWtmx(PsWtmx&nbsp;vo)&nbsp;{ &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.update(vo); &nbsp;&nbsp;
                <li>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;
                <li>&nbsp;&nbsp;
                <li>}&nbsp;&nbsp; </li>
            </ol>
            </div>
            4</span>&nbsp;
            <p>&nbsp;</p>
            <p><span Times Roman?;?2?><a href="http://fangang.javaeye.com/admin/show/47670">如何在struts+spring+hibernate的框架下构建低耦合高内聚的软件</a></font></span>》中已经给出了这个结构的一个实现，供大家参考。</span></p>
            </h3>
            </h3>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<br />
说明:以上2篇文章均来自:http://fangang.javaeye.com/blog/52883推荐,到原博客阅读,和下载相关代码,此处只是本人收藏,方便查看,在此向作者表示衷心感谢!
<img src ="http://www.blogjava.net/i369/aggbug/152868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-10-15 09:44 <a href="http://www.blogjava.net/i369/articles/152868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]如何在struts+spring+hibernate的框架下构建低耦合高内聚的软件</title><link>http://www.blogjava.net/i369/articles/152863.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Mon, 15 Oct 2007 01:39:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/152863.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/152863.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/152863.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/152863.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/152863.html</trackback:ping><description><![CDATA[<strong><font size="4">问题的提出</font></strong>
<p><span style="font-family: 宋体">我常常在思考一个问题，我们如何能设计出高水平、高质量的软件出来。怎样是高水平、高质量的软件？它应当是易于维护、易于适应变更、可重用性好的一个系统。如何做到这一点呢？答案当然是&#8220;低耦合、高内聚&#8221;了。低耦合就是软件在构造的时候，各个模块、各个功能、各个类都不会过度依赖于它周围的环境。只有这样，才能使我们的模块（功能、类）在周围发生变更时不受影响，做到易于维护和易于适应变更。正因为如此，也使它更易于重用到其它功能类似的环境中，提高了重用性。高内聚则使软件中的各个模块（功能、类）能够各尽其能而又充分合作，也就是对于软件问题空间中需求的各个功能，系统可以合理地把它分配给各个模块（功能、类）来共同完成，而不是一个或几个八面玲珑、包打天下的超级类一个人完成。而对于该系统中的某一个模块（功能、类），具有自己高度相关的职责，即该职责中的几个任务是高度相关的。每一个模块（功能、类）都决不去完成与自己无关职责的任务。</span></p>
<p><span style="font-family: 宋体">那么怎样能构造一个低耦合、高内聚的系统能，时下最流行的框架结构之一的</span>struts+spring+hibernate<span Times 宋体;? Roman?;?font-family:>然而我要说的是，即使我们使用了</span>struts+spring+hibernate&nbsp;</p>
<pre><strong><font size="4">分析与决策</font></strong></pre>
<h2><span style="font-size: 14pt; line-height: 173%">1.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>编写DAO的时候不要直接去使用hibernate或spring对hibernate的支持。</h2>
<p><span style="font-family: 宋体">现在我们在编写</span>DAO<span Times Roman?; ?Times New Courier New?; 宋体;? New?;?font-family:>以上问题，究其原因，是我们项目中的</span>DAO<span Times Roman?; ?Times New Courier New?; New?;??><img height="528" alt="" src="http://www.javaeye.com/topics/download/caeb8220-1b1e-4efe-93d2-eda8a63db6fc" width="551" /></span></p>
<h2><span style="font-size: 14pt; line-height: 173%">2.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>编写Action的时候不要直接使用spring和spring的继承类</h2>
<p><span style="font-family: 宋体">前面我说了应当避免</span>DAO<span Times Roman?; ?Times New Courier New?; ?Courier>的继承类</span><span ?Courier>然后使用它的</span><span Courier New?; ?Courier>方法。如此的使用，我们的</span><span Courier New?;>Action</span><span ?Courier>将依赖与</span><span Courier New?;>spring</span><span ?Courier>。我们同样可以使用一个叫</span><span Courier New?;>BasicAction</span><span ?Courier>的父类，然后用一个接口来隔离</span><span Courier New?;>spring</span><span ?Courier>。由于</span>Action<span Times Roman?; ?Times New Courier New?;>BasicAction</span><span ?Courier>中，提高系统的可维护性。</span></p>
<h2><span style="font-size: 14pt; line-height: 173%">3.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>当BUS需要获取别的模块的数据的时候，不要直接去使用该模块的DAO</h2>
<p><span style="color: black; font-family: 宋体">我举一个简单的例子：我需要设计一个软件评审的管理软件，该软件分为评审组织者制订评审计划、评审者分别填写评审表后由评审组织者汇总评审表、评审组织者制作评审报告。这是一个非常简单的项目，分成了三个人来完成。但是项目进行快结束的时候却出现了问题。填写评审表需要获得评审计划中的一些数据，制作评审报告的数据来源于评审表。项目组在开始编程前先开了一次会，大家约定好了各个部分的数据格式及其规则，然后开始工作。然而数天后项目组把各个模块整合以后发现，系统根本跑不起来，为什么呢？设计评审计划的人发现，所有评审计划应当按照产品编号来进行管理而不是项目编号。由于这个变更，填写评审表模块在待评审列表中什么都无法显示；同样，设计评审表的人发现，在一个评审计划中评审表与评审者不是一对多的关系，而是一对一的关系，因而修改了这两个表的关联。因为这样，在制作评审报告时就不能正确得到评审表数据。其实一个软件项目在整个进行过程中总是不断变更。我们需要做的不是去抑制这些变更，而应当是通过软件的结构去适应这些变更，即是降低各模块间的依赖（耦合），提高内聚。</span></p>
<p><span style="color: black; font-family: 宋体">拿这个实例来说，当评审表需要调用评审计划的数据的时候，不应当是自己写一个</span><span Courier New?;>DAO</span><span ?Courier>去调用评审计划的数据，而应当是调用评审计划的接口，将这个任务交给评审计划类来完成。当评审报告需要调用评审表的数据的时候，同样应当去调用评审表的接口，由评审表来实现。同时，这种调用应当是去调用</span><span Courier New?;>BUS</span><span ?Courier>层的接口。为什么呢？比如在评审计划中的一个业务逻辑是只有在评审计划发布以后才能制作评审表，那么怎样才是已发布的评审计划呢？这个业务逻辑应当由谁来定义？当然是评审计划。在什么地方定义？当然是</span><span Courier New?;>BUS</span><span ?Courier>而不是</span><span Courier New?;>DAO</span><span ?Courier>，因为</span><span Courier New?;>DAO</span><span ?Courier>仅仅是实现数据的持久化，而</span><span Courier New?;>BUS</span><span ?Courier>才是实现业务逻辑的地方。既然如此，如果评审表去调用评审计划的</span><span Courier New?;>DAO</span><span ?Courier>，那么已发布评审计划的业务逻辑必然包含在了评审表的业务逻辑里了。我们假设有一天，已发布评审计划的业务逻辑发生变更了（实际上这样的会在你毫不经意间就发生了），编写评审计划的人会很快就修改了评审计划的业务实现并且测试通过了。他不知道评审表里也包含了这样的业务逻辑，因而修改后的程序在运行到评审表的时候就很可能会出错。不幸的是，在实际工作中，同样一个业务逻辑可能包含在无数个你可能知道，但你也可能不知道的代码中。这样的结构就是一个不易于维护的差的结构。</span></p>
<pre><span style="font-size: 16pt; line-height: 240%; font-family: 宋体"><strong>总结：从技术升级和需求变更两方面适应变化</strong></span></pre>
<p><span style="font-family: 宋体">软件开发专家</span>Alistair Cockburn<span Times Roman?; ?Times New Courier 宋体;? New?;?font-family:>相关链接：<a href="http://fangang.javaeye.com/blog/52883">回复：《<font size="2">如何在struts+spring+hibernate的框架下构建低耦合高内聚的软件</font>》</a>。</span></p>
<div style="padding-right: 10px; border-top: gray 1px solid; padding-left: 10px; padding-bottom: 10px; margin: 15px 0px 0px 10px; padding-top: 10px">
<table cellspacing="0" cellpadding="2" width="95%" align="center" border="1">
    <tbody>
        <tr>
            <td style="background: #d1d7dc" align="center" width="100%" colspan="3"><strong>Spring-Hibernate.rar</strong></td>
        </tr>
        <tr>
            <td width="15%">&nbsp;描述:</td>
            <td width="75%">&nbsp; </td>
            <td align="center" width="10%" rowspan="4"><img alt="" src="http://fangang.javaeye.com/images/forum/icon_clip.gif" border="0" /><br />
            <a href="http://fangang.javaeye.com/topics/download/828d7ac5-1922-4104-aecb-0ee888c4e9a0"><strong>下载</strong></a> </td>
        </tr>
        <tr>
            <td width="15%">&nbsp;文件名:</td>
            <td width="75%">&nbsp;Spring-Hibernate.rar</td>
        </tr>
        <tr>
            <td width="15%">&nbsp;文件大小:</td>
            <td width="75%">&nbsp;9 KB</td>
        </tr>
        <tr>
            <td width="15%">&nbsp;下载过的:</td>
            <td width="75%">&nbsp;文件被下载或查看 511 次</td>
        </tr>
    </tbody>
</table>
</div>
<img src ="http://www.blogjava.net/i369/aggbug/152863.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-10-15 09:39 <a href="http://www.blogjava.net/i369/articles/152863.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>幼学琼林-对比Spring 1.0与2.0的事务配置方式 </title><link>http://www.blogjava.net/i369/articles/135505.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 09 Aug 2007 07:18:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/135505.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/135505.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/135505.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/135505.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/135505.html</trackback:ping><description><![CDATA[<p>Spring 2.0 的重头戏之一就是AspectJ 式 AOP 配置。 但是一定要通过对比，才能看到2.0式的AOP配置是如何跳出一片新天空的。</p>
<p><strong>1. 对比</strong> <br>先看1.0的标准事务配置:</p>
<p>Spring 2.0 的重头戏之一就是AspectJ 式 AOP 配置。 但是一定要通过对比，才能看到2.0式的AOP配置是如何跳出一片新天空的。</p>
<p>1. 对比 <br><strong>先看1.0的标准事务配置:</strong></p>
<p>
<div class=code_title>代码</div>
<div class=code_div>
<div class=dp-highlighter>
<div class=bar></div>
<ol class=dp-xml>
    <li class=alt><span><span class=tag>&lt;</span><span class=tag-name>bean</span><span>&nbsp;</span><span class=attribute>id</span><span>=</span><span class=attribute-value>"baseTxService"</span><span>&nbsp;</span><span class=attribute>class</span><span>=</span><span class=attribute-value>"org.springframework.transaction.interceptor.TransactionProxyFactoryBean"</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=attribute>abstract</span><span>=</span><span class=attribute-value>"true"</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>property</span><span>&nbsp;</span><span class=attribute>name</span><span>=</span><span class=attribute-value>"transactionManager"</span><span>&nbsp;</span><span class=attribute>ref</span><span>=</span><span class=attribute-value>"transactionManager"</span><span class=tag>/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>property</span><span>&nbsp;</span><span class=attribute>name</span><span>=</span><span class=attribute-value>"proxyTargetClass"</span><span>&nbsp;</span><span class=attribute>value</span><span>=</span><span class=attribute-value>"true"</span><span class=tag>/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>property</span><span>&nbsp;</span><span class=attribute>name</span><span>=</span><span class=attribute-value>"transactionAttributes"</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>props</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>prop</span><span>&nbsp;</span><span class=attribute>key</span><span>=</span><span class=attribute-value>"get*"</span><span class=tag>&gt;</span><span>PROPAGATION_REQUIRED,readOnly</span><span class=tag>&lt;/</span><span class=tag-name>prop</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>prop</span><span>&nbsp;</span><span class=attribute>key</span><span>=</span><span class=attribute-value>"find*"</span><span class=tag>&gt;</span><span>PROPAGATION_REQUIRED,readOnly</span><span class=tag>&lt;/</span><span class=tag-name>prop</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>prop</span><span>&nbsp;</span><span class=attribute>key</span><span>=</span><span class=attribute-value>"save*"</span><span class=tag>&gt;</span><span>PROPAGATION_REQUIRED</span><span class=tag>&lt;/</span><span class=tag-name>prop</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>prop</span><span>&nbsp;</span><span class=attribute>key</span><span>=</span><span class=attribute-value>"remove*"</span><span class=tag>&gt;</span><span>PROPAGATION_REQUIRED</span><span class=tag>&lt;/</span><span class=tag-name>prop</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>props</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>property</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>property</span><span>&nbsp;</span><span class=attribute>name</span><span>=</span><span class=attribute-value>"preInterceptors"</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>list</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>ref</span><span>&nbsp;</span><span class=attribute>bean</span><span>=</span><span class=attribute-value>"methodSecurityInterceptor"</span><span class=tag>/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>list</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>property</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>bean</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>bean</span><span>&nbsp;</span><span class=attribute>id</span><span>=</span><span class=attribute-value>"bookManager"</span><span>&nbsp;</span><span class=attribute>parent</span><span>=</span><span class=attribute-value>"baseTxService"</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>property</span><span>&nbsp;</span><span class=attribute>name</span><span>=</span><span class=attribute-value>"target"</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>bean</span><span>&nbsp;</span><span class=attribute>class</span><span>=</span><span class=attribute-value>"org.springside.bookstore.admin.manager.BookManager"</span><span class=tag>/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>property</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>bean</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
</div>
<script>render_code();</script>
<p>&#160;</p>
<p><strong>再看2.0的新配置：</strong></p>
<p>
<div class=code_title>代码</div>
<div class=code_div>
<div class=dp-highlighter>
<div class=bar></div>
<ol class=dp-xml>
    <li class=alt><span><span class=tag>&lt;</span><span class=tag-name>aop:config</span><span>&nbsp;</span><span class=attribute>proxy-target-class</span><span>=</span><span class=attribute-value>"true"</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>aop:advisor</span><span>&nbsp;</span><span class=attribute>pointcut</span><span>=</span><span class=attribute-value>"execution(*&nbsp;yourpackagename..*Manager.*(..))"</span><span>&nbsp;</span><span class=attribute>advice-ref</span><span>=</span><span class=attribute-value>"txAdvice"</span><span class=tag>/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>aop:advisor</span><span>&nbsp;</span><span class=attribute>pointcut</span><span>=</span><span class=attribute-value>"execution(*&nbsp;yourpackagename..*Manager.save(..))"</span><span>&nbsp;</span><span class=attribute>advice-ref</span><span>=</span><span class=attribute-value>"fooAdvice"</span><span class=tag>/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=tag>&lt;/</span><span class=tag-name>aop:config</span><span class=tag>&gt;</span><span class=tag>&lt;</span><span class=tag-name>tx:advice</span><span>&nbsp;</span><span class=attribute>id</span><span>=</span><span class=attribute-value>"txAdvice"</span><span>&nbsp;</span><span class=attribute>transaction-manager</span><span>=</span><span class=attribute-value>"transactionManager"</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>tx:attributes</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>tx:method</span><span>&nbsp;</span><span class=attribute>name</span><span>=</span><span class=attribute-value>"save*"</span><span class=tag>/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>tx:method</span><span>&nbsp;</span><span class=attribute>name</span><span>=</span><span class=attribute-value>"remove*"</span><span class=tag>/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>tx:method</span><span>&nbsp;</span><span class=attribute>name</span><span>=</span><span class=attribute-value>"*"</span><span>&nbsp;</span><span class=attribute>read-only</span><span>=</span><span class=attribute-value>"true"</span><span class=tag>/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>tx:attributes</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=tag>&lt;/</span><span class=tag-name>tx:advice</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;</span></li>
    <li class=""><span></span><span class=tag>&lt;</span><span class=tag-name>bean</span><span>&nbsp;</span><span class=attribute>id</span><span>=</span><span class=attribute-value>"bookManager"</span><span>&nbsp;</span><span class=attribute>class</span><span>=</span><span class=attribute-value>"org.springside.bookstore.commons.service.BookManager"</span><span class=tag>/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
</div>
<script>render_code();</script>
<p>&#160;</p>
<p><strong>2.进步</strong> <br><strong>1. AOP的配置方式也AOP了。</strong> <br>对比1.0的配置文件，因为下面2提到的限制，事关安全acegi methodSecurityInterceptor 拦截器要配置在关于事务的TransactionProxyFactoryBean的preInterceptors属性里，这样子就一点不AOP了，而2.0使用ponintcut expression，很AOP的配置一切Aspect。 <br><br><strong>2. 1.0时，一个已经AOP过的object不能再次被AOP。</strong> <br>在Spring 1.0的文档里Rod说，比如&lt;bean id="bookManager" parent="baseTxService"&gt;已经进行了一次AOP，如果想在这个Bean上再配一层AOP，比如要对方法执行结果缓存，无论以1.0 还是2.0的方式定义，cglib方式是会报错的，而基于接口的方式，结果不确定。 <br><br><strong>3. BookManager能直接定义自己，而不是像1.0那样作匿名内部target。</strong></p>
<p>虽然在1.0时代的BeanNameAutoProxyCreator 达到类似作用，但只能用BeanName来匹配比较危险，没有AspectJ的pointcut语法细致。</p>
<p><strong>3. 语法</strong> <br>满江红翻译的Spring参考文档 6.3 schema-based AOP support 提供了aspect,advisor,advide三种组装方法的解释，其中aspect是aspectJ原装，但稍复杂.</p>
<p>唯一有点难懂的是pointcut里的语法，其实也很好学，Spring<a href="http://www.redsaga.com/spring_ref/2.0RC2/html/aop.html#d0e6939" target=blank>参考文档6.2.3.4里</a>有完整说明 ，其实一排子过去是</p>
<p>
<div class=code_title>代码</div>
<div class=code_div>
<div class=dp-highlighter>
<div class=bar></div>
<ol class=dp-j>
    <li class=alt><span><span>execution(modifiers-pattern?&nbsp;ret-type-pattern&nbsp;declaring-type-pattern?&nbsp;name-pattern(param-pattern)&nbsp;</span><span class=keyword>throws</span><span>-pattern?)&nbsp;&nbsp;</span></span></li>
</ol>
</div>
</div>
<script>render_code();</script>
其中带问号的modifiers-pattern?(public/protected) 和 declaring-type-pattern? throws-pattern? 可以不填
<p>&#160;</p>
<p>可见execution(* *..BookManager.save(..))</p>
<p>第一颗* 代表ret-type-pattern 返回值可任意， <br>*..BookManager 代表任意Pacakge里的BookManager类。 <br>如果写成com.xyz.service.* 则代表com.xyz.service下的任意类 <br>com.xyz.service..* com.xyz.service则代表com.xyz.service及其子package下的任意类 <br>save代表save方法，也可以写save* 代表saveBook()等方法 <br>(..) 匹配0个参数或者多个参数的，任意类型 <br>(x,..) 第一个参数的类型必须是X <br>(x,,,s,..) 匹配至少4个参数，第一个参数必须是x类型，第二个和第三个参数可以任意，第四个必须是s类型。 </p>
<p>注意name-pattern千万不要写成*..*Manager ，这样子的话会把所有第三方类库的Manager比如Spring的PlatformTranstationManager 也加入aop，非常危险。所以最好还是加上项目的package前缀，如org.springside <br><br><br><br><br><br></p>
<div class=quote_title>引用</div>
<div class=quote_div>对比1.0的配置文件，因为下面2提到的限制，事关安全acegi methodSecurityInterceptor 拦截器要配置在关于事务的TransactionProxyFactoryBean的preInterceptors属性里，这样子就一点不AOP了，而 2.0使用ponintcut expression，很AOP的配置一切Aspect。 </div>
<p><br>1其实可以 <br></p>
<div class=code_title>代码</div>
<div class=code_div>
<div class=dp-highlighter>
<div class=bar></div>
<ol class=dp-xml>
    <li class=alt><span><span class=tag>&lt;</span><span class=tag-name>bean</span><span>&nbsp;</span><span class=attribute>id</span><span>=</span><span class=attribute-value>"autoProxyCreator"</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=attribute>class</span><span>=</span><span class=attribute-value>"org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>property</span><span>&nbsp;</span><span class=attribute>name</span><span>=</span><span class=attribute-value>"interceptorNames"</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>list</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>value</span><span class=tag>&gt;</span><span>security</span><span class=tag>&lt;/</span><span class=tag-name>value</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>value</span><span class=tag>&gt;</span><span>tx</span><span class=tag>&lt;/</span><span class=tag-name>value</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>list</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>property</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>property</span><span>&nbsp;</span><span class=attribute>name</span><span>=</span><span class=attribute-value>"beanNames"</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;</span><span class=tag-name>value</span><span class=tag>&gt;</span><span>*Service</span><span class=tag>&lt;/</span><span class=tag-name>value</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=tag>&lt;/</span><span class=tag-name>property</span><span class=tag>&gt;</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
</div>
<p>
<script>render_code();</script>
<br></p>
<img src ="http://www.blogjava.net/i369/aggbug/135505.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-08-09 15:18 <a href="http://www.blogjava.net/i369/articles/135505.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战Acegi：使用Acegi作为基于Spring框架的WEB应用的安全框架 </title><link>http://www.blogjava.net/i369/articles/135460.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 09 Aug 2007 05:13:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/135460.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/135460.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/135460.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/135460.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/135460.html</trackback:ping><description><![CDATA[<h2>实战Acegi：使用Acegi作为基于Spring框架的WEB应用的安全框架</h2>
<p>最近项目使用Acegi作为安全框架的实现,效果不错,就写了这篇文章作为总结.<br><br>对于任何一个完整的应用系统，完善的认证和授权机制是必不可少的。在基于SpringFramework的WEB应用中，<br>我们可以使用Acegi作为安全架构的实现。本文将介绍如何在基于Spring构架的Web应用中使用Acegi，并且详细介<br>绍如何配置和扩展Acegi框架以适应实际需要。<br></p>
<p>文章和代码下载:<br><br><a href="http://www.blogjava.net/Files/youlq/Acegi.zip"><u><font color=#800080>http://www.blogjava.net/Files/youlq/Acegi.zip</font></u></a><br><br><br><strong><font color=#ff0000>注意：许多朋友在部署上遇到一些麻烦，所以我将可以部署的完整的war文件传上来，注意：java代码在acegi-sample.war\WEB-INF 目录下，例子需要Mysql，建库脚本在acegi-sample.war\db目录下。</font></strong><br><br><a href="http://www.blogjava.net/Files/youlq/acegi-sample.part1.rar"><font color=#002c99><u>acegi-sample.part1.rar</u></font></a><br><a href="http://www.blogjava.net/Files/youlq/acegi-sample.part2.rar"><font color=#002c99><u>acegi-sample.part2.rar</u></font></a><br><a href="http://www.blogjava.net/Files/youlq/acegi-sample.part3.rar"><font color=#002c99><u>acegi-sample.part3.rar</u></font></a><br><a href="http://www.blogjava.net/Files/youlq/acegi-sample.part4.rar"><font color=#002c99><u>acegi-sample.part4.rar</u></font></a><br><br>附注：<br><br>有些朋友询问我如何部署文中的例子，在此再次说明一下（文章中已经有提到）：<br><br>Mysql的建表脚本在db目录下<br>为了减小体积，已经将WEB-INF\lib下的依赖包删除，请自行下载以下包，并拷贝至WEB-INF\lib下：<br>spring-1.2.4.jar<br>acegi-security-0.8.3.jar<br>aopalliance-1.0.jar<br>c3p0-0.9.0.jar<br>commons-logging-1.0.4.jar<br>ehcache-1.1.jar<br>log4j-1.2.8.jar<br>mysql-connector-java-3.1.10-bin.jar<br>oro-2.0.8.jar<br><br>提示：<br>acegi-security-0.8.3.jar<br>aopalliance-1.0.jar<br>c3p0-0.9.0.jar<br>commons-logging-1.0.4.jar<br>ehcache-1.1.jar<br>log4j-1.2.8.jar<br>oro-2.0.8.jar<br>可以在acegi-security-0.8.3.zip所带的acegi-security-sample-contacts-filter.war中找到。<br>spring-1.2.4.jar<br>mysql-connector-java-3.1.10-bin.jar<br>要分别到springframework和mysql网站下载。</p>
<br>
<img src ="http://www.blogjava.net/i369/aggbug/135460.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-08-09 13:13 <a href="http://www.blogjava.net/i369/articles/135460.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简化spring中的事务管理配置 </title><link>http://www.blogjava.net/i369/articles/134644.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Mon, 06 Aug 2007 02:59:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/134644.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/134644.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/134644.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/134644.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/134644.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 系统中有多个service,但我们的事务策略大部分都是每个service都要写一个代理配置.多个service用同样的事务策略时,简化这样的配置可以用一下方法.<br>
<table cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
    <tbody>
        <tr>
            <td><span class=genmed><strong>xml代码:&nbsp;</strong></span></td>
        </tr>
        <tr>
            <td class=code>
            <div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br><br><font size=2>&lt;!-- Transactional proxy for the services --&gt; <br>&nbsp; &nbsp; &lt;bean id=<span style="COLOR: #ff0000">"baseTxProxy"</span> lazy-init=<span style="COLOR: #ff0000">"true"</span> abstract＝"true" class=<span style="COLOR: #ff0000">"org.springframework.transaction.interceptor.TransactionProxyFactoryBean"</span>&gt; <br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;property name=<span style="COLOR: #ff0000">"transactionManager"</span>&gt;&lt;ref bean=<span style="COLOR: #ff0000">"transactionManager"</span>/&gt;&lt;/property&gt; <br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;property name=<span style="COLOR: #ff0000">"transactionAttributes"</span>&gt; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;props&gt; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;prop key=<span style="COLOR: #ff0000">"*"</span>&gt;PROPAGATION_REQUIRED&lt;/prop&gt; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/props&gt; <br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;/property&gt; <br>&nbsp; &nbsp; &lt;/bean&gt; <br><br>&nbsp; &nbsp; &lt;bean id=<span style="COLOR: #ff0000">"itemService"</span> parent=<span style="COLOR: #ff0000">"baseTxProxy"</span>&gt; <br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;property name=<span style="COLOR: #ff0000">"target"</span>&gt; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;bean class=<span style="COLOR: #ff0000">"ItemServiceImpl"</span> autowire=<span style="COLOR: #ff0000">"byName"</span>/&gt; <br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;/property&gt; <br>&nbsp; &nbsp; &lt;/bean&gt;</font> <br></div>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p id=TBPingURL>Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=245954</p>
<img src ="http://www.blogjava.net/i369/aggbug/134644.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-08-06 10:59 <a href="http://www.blogjava.net/i369/articles/134644.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SpringSide代码规范</title><link>http://www.blogjava.net/i369/articles/134296.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Fri, 03 Aug 2007 09:48:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/134296.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/134296.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/134296.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/134296.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/134296.html</trackback:ping><description><![CDATA[<h2><a name=CodingStandards-%E5%89%8D%E8%A8%80></a>前言</h2>
<p>&nbsp;&nbsp;&nbsp; 本文档反映的是SpringSide 团队的编码规范，同时推荐所有使用SpringSide框架的开发人员遵循。</p>
<p>&nbsp;&nbsp;&nbsp; 本文档基本遵循<span class=nobr><a title="Visit page outside Confluence" href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html" rel=nofollow>Sun's Coding Conventions<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>，补充了其中没有说明或者有所改动的地方。</p>
<h3><a name=CodingStandards-%E7%89%88%E6%9D%83%E5%A3%B0%E6%98%8E%26nbsp%3B%26nbsp%3B%26nbsp%3B></a>版权声明&nbsp;&nbsp;&nbsp;</h3>
<p>&nbsp;&nbsp;&nbsp; 本规范由<span class=nobr><a title="Visit page outside Confluence" href="http://www.springside.org.cn/team.php" rel=nofollow>springside团队<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>维护，相关评论与意见请发至springside@gmail.com，转载请注明出处。</p>
<h3><a name=CodingStandards-%E8%A7%84%E8%8C%83%E7%AD%89%E7%BA%A7%E8%AF%B4%E6%98%8E></a>规范等级说明</h3>
<ul>
    <li><font color=#000000>级别I:&nbsp;&nbsp; 默认级别，要求所有项目中的所有成员遵守。</font>
    <li><font color=#cc6600>级别II: &nbsp;建议所有项目中的所有成员遵守。</font>
    <li><font color=#3333ff>级别III: 鼓</font><font color=#3333ff>励各个项目根据实际情况执行。</font> </li>
</ul>
<h2><a name=CodingStandards-1.%E6%A0%BC%E5%BC%8F%E4%B8%8E%E5%91%BD%E5%90%8D%E8%A7%84%E8%8C%83%28FormatingandNamingConventions%29></a>1.格式与命名规范(Formating and Naming Conventions)</h2>
<h3><a name=CodingStandards-1.1%26nbsp%3B%26nbsp%3B%E7%BC%A9%E8%BF%9B></a>1.1&nbsp;&nbsp;缩进</h3>
<p>&nbsp; 使用Tab缩进，而不是空格键--将缩进2，4，8字符的选择权留给阅读者。</p>
<h3><a name=CodingStandards-1.2%E6%8D%A2%E8%A1%8C></a>1.2 换行</h3>
<p>&nbsp;&nbsp; 每行120字符--因为已是1024*768的年代。</p>
<p>&nbsp; &nbsp;if,for,while语句只有单句时，如果该句可能引起阅读混淆，需要用" {"和"}"括起来，否则可以省略。</p>
<div class=code>
<div class=codeContent>
<pre class=code-java><span class=code-comment>//错误，需要使用花括号{}括起来
</span><span class=code-keyword>if</span> (condition)
<span class=code-keyword>if</span>(condition) doSomething();
<span class=code-keyword>else</span>
doSomething();</pre>
</div>
</div>
<h3><a name=CodingStandards-1.3%26nbsp%3B%E5%91%BD%E5%90%8D%E8%A7%84%E5%88%99%26nbsp%3B></a>1.3&nbsp;命名规则&nbsp;</h3>
<ul>
    <li>不允许使用汉语拼音命名&nbsp;
    <li>遇到缩写如XML时，仅首字母大写，即loadXmlDocument()而不是loadXMLDocument()
    <li>Package名必须全部小写，尽量使用单个单词
    <li>Interface名可以是一个名词或形容词(加上'able','ible', or 'er'后缀)，如Runnable，Accessible。<br>为了基于接口编程，不采用首字母为I或加上IF后缀的命名方式，如IBookDao,BookDaoIF。
    <li>页面部件名建议命名为：btnOK、lblName或okBtn、nameLbl。<font color=#cc6600>(II)</font><br>其中btn、lbl缩写代表按钮(Button)、标签(Label)。
    <li>局部变量及输入参数不要与类成员变量同名(get/set方法与构造函数除外) </li>
</ul>
<h3><a name=CodingStandards-1.4%E5%A3%B0%E6%98%8E></a>1.4 声明</h3>
<ul>
    <li>修饰符应该按照如下顺序排列：public, protected, private, abstract, static, final, transient, volatile, synchronized, native, strictfp。
    <li>类与接口的声明顺序(可用Eclipse的source-&gt;sort members功能自动排列):&nbsp;
    <ol>
        <li>静态成员变量 / Static Fields
        <li>静态初始化块 / Static Initializers
        <li>成员变量 / Fields
        <li>初始化块 / Initializers
        <li>构造器 / Constructors
        <li>静态成员方法 / Static Methods
        <li>成员方法 / Methods
        <li>重载自Object的方法如toString(), hashCode() 和main方法
        <li>类型(内部类) / Types(Inner Classes) </li>
    </ol>
    </li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 同等的类型，按public, protected, private的顺序排列。</p>
<h2><a name=CodingStandards-2.%E6%B3%A8%E9%87%8A%E8%A7%84%E8%8C%83%28DocumentConvertions%29></a>2.注释规范(Document Convertions)</h2>
<h3><a name=CodingStandards-2.1%E6%B3%A8%E9%87%8A%E7%B1%BB%E5%9E%8B></a>2.1 注释类型</h3>
<h4><a name=CodingStandards-2.1.1JavaDoc%E6%B3%A8%E9%87%8A></a>2.1.1 JavaDoc注释</h4>
<p>&nbsp; 略。</p>
<h4><a name=CodingStandards-2.1.2%E5%A4%B1%E6%95%88%E4%BB%A3%E7%A0%81%E6%B3%A8%E9%87%8A></a>2.1.2 失效代码注释</h4>
<p>&nbsp; 由/*<strong>...*</strong>/界定，标准的C-Style的注释。专用于注释已失效的代码。</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>/*
&nbsp;* Comment out the code
&nbsp;* <span class=code-object>String</span> s = <span class=code-quote>"hello"</span>;
* <span class=code-object>System</span>.out.println(s);
&nbsp;*/</pre>
</div>
</div>
<h4><a name=CodingStandards-2.1.3%E4%BB%A3%E7%A0%81%E7%BB%86%E8%8A%82%E6%B3%A8%E9%87%8A></a>2.1.3 代码细节注释</h4>
<p>&nbsp; 由//界定，专用于注释代码细节，即使有多行注释也仍然使用//，以便与用/**/注释的失效代码分开</p>
<p>&nbsp; 除了私有变量外，不推荐使用行末注释。</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>class MyClass {
<span class=code-keyword>private</span> <span class=code-object>int</span> myField; <span class=code-comment>// An end-line comment.
</span>
<span class=code-keyword>public</span> void myMethod {
<span class=code-comment>//a very very <span class=code-object>long</span>
</span>       <span class=code-comment>//comment.
</span>       <span class=code-keyword>if</span> (condition1) {
<span class=code-comment>//condition1 comment
</span>          ...
} <span class=code-keyword>else</span> {
<span class=code-comment>//elses condition comment
</span>          ...
}
}
}</pre>
</div>
</div>
<h3><a name=CodingStandards-2.2%26nbsp%3B%E6%B3%A8%E9%87%8A%E7%9A%84%E6%A0%BC%E5%BC%8F></a>2.2&nbsp;注释的格式</h3>
<ul>
    <li>注释中的第一个句子要以（英文）句号、问号或者感叹号结束。Javadoc生成工具会将注释中的第一个句子放在方法汇总表和索引中。
    <li>为了在JavaDoc和IDE中能快速链接跳转到相关联的类与方法，尽量多的使用@see xxx.MyClass，@see xx.MyClass#find(String)。
    <li>Class必须以@author 作者名声明作者，不需要声明@version与@date，由版本管理系统保留此信息。<font color=#cc6600>(II)</font>
    <li>如果注释中有超过一个段落，用&lt;p&gt;分隔。<font color=#cc6600>(II)</font>
    <li>示例代码以&lt;pre&gt;&lt;/pre&gt;包裹。<font color=#cc6600>(II)</font>
    <li>标识(java keyword, class/method/field/argument名，Constants) 以&lt;code&gt;&lt;/code&gt;包裹。<font color=#cc6600>(II)</font>
    <li>标识在第一次出现时以{@linkxxx.Myclass}注解以便JavaDoc与IDE中可以链接。<font color=#cc6600>(II)</font> </li>
</ul>
<h3><a name=CodingStandards-2.3%26nbsp%3B%E6%B3%A8%E9%87%8A%E7%9A%84%E5%86%85%E5%AE%B9></a>2.3&nbsp;注释的内容</h3>
<h4><a name=CodingStandards-2.3.1%E5%8F%AF%E7%B2%BE%E7%AE%80%E7%9A%84%E6%B3%A8%E9%87%8A%E5%86%85%E5%AE%B9></a>2.3.1 可精简的注释内容</h4>
<p>&nbsp;&nbsp;&nbsp; 注释中的每一个单词都要有其不可缺少的意义，注释里不写"@param name -名字"这样的废话。<br>&nbsp;&nbsp;&nbsp; 如果该注释是废话，连同标签删掉它，而不是自动生成一堆空的标签，如空的@param name，空的@return。</p>
<h4><a name=CodingStandards-2.3.2%E6%8E%A8%E8%8D%90%E7%9A%84%E6%B3%A8%E9%87%8A%E5%86%85%E5%AE%B9></a>2.3.2 推荐的注释内容</h4>
<ul>
    <li>对于API函数如果存在契约，必须写明它的前置条件(precondition)，后置条件(postcondition)，及不变式(invariant)。<font color=#cc6600>(II)</font>
    <li>对于调用复杂的API尽量提供代码示例。<font color=#cc6600>(II)</font>
    <li>对于已知的Bug需要声明。<font color=#cc6600>(II)</font>
    <li>在本函数中抛出的unchecked exception尽量用@throws说明。<font color=#cc6600>(II)</font> </li>
</ul>
<h4><a name=CodingStandards-2.3.3Null%E8%A7%84%E7%BA%A6></a>2.3.3 Null规约</h4>
<p>&nbsp;&nbsp; 如果方法允许Null作为参数，或者允许返回值为Null，必须在JavaDoc中说明。<br>&nbsp;&nbsp;&nbsp;如果没有说明，方法的调用者不允许使用Null作为参数，并认为返回值是Null Safe的。</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>/**
&nbsp;* 获取对象.
&nbsp;*
&nbsp;* @ <span class=code-keyword>return</span> the object to found or <span class=code-keyword>null</span> <span class=code-keyword>if</span> not found.
&nbsp;*/
<span class=code-object>Object</span> get(<span class=code-object>Integer</span> id){
...
}</pre>
</div>
</div>
<h4><a name=CodingStandards-2.3.4%E7%89%B9%E6%AE%8A%E4%BB%A3%E7%A0%81%E6%B3%A8%E9%87%8A></a>2.3.4 特殊代码注释</h4>
<ul>
    <li>代码质量不好但能正常运行，或者还没有实现的代码用//TODO: 或 //XXX:声明&nbsp;
    <li>存在错误隐患的代码用//FIXME:声明 </li>
</ul>
<h2><a name=CodingStandards-3.%E7%BC%96%E7%A8%8B%E8%A7%84%E8%8C%83%28ProgrammingConventions%29></a>3.编程规范(Programming Conventions)</h2>
<h3><a name=CodingStandards-3.1%E5%9F%BA%E6%9C%AC%E8%A7%84%E8%8C%83></a>3.1基本规范</h3>
<ol>
    <li>当面对不可知的调用者时，方法需要对输入参数进行校验，如不符合抛出IllegalArgumentException，建议使用Spring的Assert系列函数。&nbsp;
    <li>隐藏工具类的构造器，确保只有static方法和变量的类不能被构造实例。
    <li>变量，参数和返回值定义尽量基于接口而不是具体实现类，如Map map = new HashMap();
    <li>代码中不能使用System.out.println()，e.printStackTrace()，必须使用logger打印信息。 </li>
</ol>
<h3><a name=CodingStandards-3.2%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86></a>3.2 异常处理</h3>
<ol>
    <li>重新抛出的异常必须保留原来的异常，即throw new NewException("message", e); 而不能写成throw new NewException("message")。
    <li>在所有异常被捕获且没有重新抛出的地方必须写日志。&nbsp;
    <li>如果属于正常异常的空异常处理块必须注释说明原因，否则不允许空的catch块。
    <li>框架尽量捕获低级异常，并封装成高级异常重新抛出，隐藏低级异常的细节。<font color=#3333ff>(III)</font> </li>
</ol>
<h3><a name=CodingStandards-3.3%E4%BB%A3%E7%A0%81%E5%BA%A6%E9%87%8F></a>3.3 代码度量</h3>
<h4><a name=CodingStandards-3.3.1%E8%80%A6%E5%90%88%E5%BA%A6%E5%BA%A6%E9%87%8F></a>3.3.1 耦合度度量</h4>
<ul>
    <li>DAC度量值不要不大于7 <font color=#3333ff>( III )</font><br>解释：DAC(Data Abstraction Coupling)数据抽象耦合度是描述对象之间的耦合度的一种代码度量。DAC度量值表示一个类中有实例化的其它类的个数。
    <li>CFO度量值不要不大于20 <font color=#3333ff>( III )</font><br>解释：CFO(Class Fan Out)类扇出是描述类之间的耦合度的一种代码度量。CFO度量值表示一个类依赖的其他类的个数。 </li>
</ul>
<h4><a name=CodingStandards-3.3.2%E6%96%B9%E6%B3%95%E5%BA%A6%E9%87%8F></a>3.3.2 方法度量</h4>
<ul>
    <li>方法（构造器）参数在5个以内 <font color=#cc6600>( II )</font><br>太多的方法（构造器）参数影响代码可读性。考虑用值对象代替这些参数或重新设计。
    <li>方法长度150行以内 <font color=#cc6600>( II )</font>
    <li>CC&nbsp;度量值不大于10<font color=#3333ff>(III )</font><br><font color=#000000>解释：CC(CyclomaticComplexity)圈复杂度指一个方法的独立路径的数量，可以用一个方法内if,while,do,for,catch,switch,case,?:语句与&amp;&amp;,||操作符的总个数来度量。</font>
    <li>NPath度量值不大于200 <font color=#3333ff>( III )</font><br>解释：NPath度量值表示一个方法内可能的执行路径的条数。 </li>
</ul>
<h4><a name=CodingStandards-3.3.3%E5%85%B6%E4%BB%96%E5%BA%A6%E9%87%8F></a>3.3.3 其他度量</h4>
<ul>
    <li>布尔表达式中的布尔运算符(&amp;&amp;,||)的个数不超过3个<font color=#3333ff>(III)</font>&nbsp;
    <li>if语句的嵌套层数3层以内<font color=#cc6600>(II)</font>
    <li>文件长度2000行以内<font color=#cc6600>(II)</font>
    <li>匿名内部类20行以内 <font color=#cc6600>( II )</font><br>太长的匿名内部类影响代码可读性，建议重构为命名的（普通）内部类。 </li>
</ul>
<h3><a name=CodingStandards-3.4JDK5.0></a>3.4 JDK5.0</h3>
<ol>
    <li>重载方法必须使用@Override，可避免父类方法改变时导致重载函数失效。
    <li>不需要关心的warning信息用@SuppressWarnings("unused"), @SuppressWarnings("unchecked"), @SuppressWarnings("serial") 注释。 </li>
</ol>
<h2><a name=CodingStandards-4.%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E6%A3%80%E6%9F%A5></a>4.自动代码检查</h2>
<p>&nbsp;&nbsp; 使用<span class=nobr><a title="Visit page outside Confluence" href="http://www.eclipse.org/" rel=nofollow>Eclipse<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>与 <span class=nobr><a title="Visit page outside Confluence" href="http://www.jetbrains.com/" rel=nofollow>Inellij IDEA<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>的代码校验功能已经排除了很多问题。</p>
<p>&nbsp;&nbsp; 再配合使用<span class=nobr><a title="Visit page outside Confluence" href="http://checkstyle.sf.net/" rel=nofollow>Checkstyle<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>，<span class=nobr><a title="Visit page outside Confluence" href="http://pmd.sf.net/" rel=nofollow>PMD<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>，<span class=nobr><a title="Visit page outside Confluence" href="http://findbugs.sf.net/" rel=nofollow>FindBugs<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>三重检查，总共五层的校验涵盖了Java编码大部分的Guide Line。</p>
<p>&nbsp;&nbsp; 如果要求不苛刻，可以只使用Eclipse或IDEA 搭配 Checkstyle的两重保湿效果。</p>
<ol>
    <li><strong>Eclipse</strong>：在Windows-&gt;Preferences-&gt;Java-Compiler-&gt;Errors/Warnings中，按本文档将一些原来Ignore的规则打开。<br>也可以将springside团队预设在/tools/codereviewer/eclipse.check.prefs的内容拷贝到项目的.setting/org.eclipse.jdt.core.prefs 文件中。
    <li><strong>IDEA</strong>：在Setting-&gt;Errors中设定规则，调用Analyzer-&gt;Inspece Code进行校验。
    <li><strong>CheckStyle</strong>：安装<span class=nobr><a title="Visit page outside Confluence" href="http://eclipse-cs.sourceforge.net/" rel=nofollow>CheckStyle的Eclipse插件<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>，在Windows-&gt;Preferences-&gt;CheckStyle导入springside团队预设在/tools/codereviewer/springside_check.xml的规则。
    <li><strong>PMD</strong>：安装<span class=nobr><a title="Visit page outside Confluence" href="http://pmd.sourceforge.net/eclipse/" rel=nofollow>PMD的Eclipse插件<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>，Windows-&gt;Preferences-&gt;PMD清除原来所有规则，导入springside团队预设在/tools/codereviewer/springside_pmd.xml的规则。
    <li><strong>FindBugs</strong>：安装<span class=nobr><a title="Visit page outside Confluence" href="http://findbugs.sourceforge.net/manual/eclipse.html" rel=nofollow>FindBugs的Eclipse插件<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>，在项目属性-&gt;FindBugs中，取消下列警告MS/EI/EI2/ ，&nbsp;SnVI/SE/WS/RS ，ST/NP/UwF/SS/UuF|UrF|SIC。 </li>
</ol>
<h2><a name=CodingStandards-5.%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99></a>5.参考资料</h2>
<ol>
    <li><span class=nobr><a title="Visit page outside Confluence" href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html" rel=nofollow>Sun's Coding Conventions<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span> Sun MicroSystem；
    <li><span class=nobr><a title="Visit page outside Confluence" href="http://www.ambysoft.com/books/elementsJavaStyle.html" rel=nofollow>The Elements of Java Style<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>&nbsp; Scott W. Ambler&nbsp;等著；
    <li>代码检测工具的规则： <span class=nobr><a title="Visit page outside Confluence" href="http://checkstyle.sourceforge.net/checks.html" rel=nofollow>checkstyle<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span>，<span class=nobr><a title="Visit page outside Confluence" href="http://pmd.sourceforge.net/rules/index.html" rel=nofollow>pmd<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span> ，<span class=nobr><a title="Visit page outside Confluence" href="http://findbugs.sourceforge.net/bugDescriptions.html" rel=nofollow>findbugs<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></a></span> </li>
</ol>
<!--
<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
<rdf:description
rdf:about="http://wiki.springside.org.cn/display/springside/Coding+Standards"
dc:identifier="http://wiki.springside.org.cn/display/springside/Coding+Standards"
dc:title="Coding Standards"
trackback:ping="http://wiki.springside.org.cn/rpc/trackback/56"/>
</rdf:rdf>
--><!--
Root decorator: all decisions about how a page is to be decorated via the
inline decoration begins here.
--><!--
Switch based upon the context. However, for now, just delegate to a decorator
identified directly by the context.
-->
<img src ="http://www.blogjava.net/i369/aggbug/134296.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-08-03 17:48 <a href="http://www.blogjava.net/i369/articles/134296.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SpringSide中使用的JDK5.0特性</title><link>http://www.blogjava.net/i369/articles/134295.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Fri, 03 Aug 2007 09:47:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/134295.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/134295.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/134295.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/134295.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/134295.html</trackback:ping><description><![CDATA[<h1><a name=JDK5-SpringSide%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%9A%84JDK5.0%E7%89%B9%E6%80%A7></a>SpringSide中使用的JDK5.0特性</h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;随着光阴推移，Annotation 慢慢在开源框架中推广，泛型渐渐被程序员们用熟，加上AutoBoxing的小糖，SpringSide终于离不开JDK5.0。</p>
<h2><a name=JDK5-1.AutoBoxing%E4%B8%8EForEach%E5%BE%AA%E7%8E%AF></a>1.AutoBoxing 与 For Each 循环</h2>
<p>&nbsp;&nbsp;&nbsp; 本来int的非Object性就很无聊，在JDK5.0终于提供了autoboxing功能。这个语法简化糖，被用在了每一个地方。</p>
<p>&nbsp;&nbsp;&nbsp; for each 循环也改善了原本总要愣一下的collection遍历。不过对于非JDK基本类型，collection必须用泛型声明，如List&lt;Book&gt;。</p>
<h2><a name=JDK5-2.%E6%B3%9B%E5%9E%8B></a>2. 泛型</h2>
<p>&nbsp;&nbsp;&nbsp; 泛型大量用于SpringSide Core中的基类，使子类更简洁，基类更强大。当然，基类是难读了，所以才需要社区花上这么长的时间来把&lt;T&gt;看到顺眼。</p>
<p>&nbsp;&nbsp;&nbsp; 泛型使用的有两个定式：</p>
<h3><a name=JDK5-2.1%E9%81%BF%E5%85%8D%E5%BC%BA%E5%88%B6%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2></a>2.1 避免强制类型转换</h3>
<p>&nbsp;&nbsp;&nbsp;如果函数输入参数里含Class类型，而返回值又是该Class的实体，应该将该函数设为泛型函数。最典型的例子是<span class=nobr><a title="Visit page outside Confluence" href="http://www.springside.org.cn/docs/api/org/springside/core/dao/HibernateGenericDao.html" rel=nofollow><u><font color=#0000ff>HibernateGenericDao<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>的get() 函数</p>
<div class=code>
<div class=codeContent>
<pre class=code-java><span class=code-keyword>public</span> &lt;T&gt; T get(<span class=code-object>Class</span>&lt;T&gt; entityClass, Serializable id) {
<span class=code-keyword>return</span> (T) getHibernateTemplate().get(entityClass, id);
}</pre>
</div>
</div>
<p>&nbsp; 其中眼花缭乱的第一个&lt;T&gt;声明这是一个泛型函数，第2个T声明返回值为T，第三个Class&lt;T&gt;代表 T.class。基类写的辛苦，但子类用得爽快&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;Book book = (Book)manager.get(Book.class,1) 简化成了&nbsp;Book book = manager.get(Book.class,1);</p>
<h3><a name=JDK5-2.2%E6%B3%9B%E5%9E%8B%E9%85%8D%E5%90%88%E5%8F%8D%E5%B0%84API%E4%BB%8ET%E8%8E%B7%E5%BE%97T.class%E3%80%82></a>2. 2 泛型配合反射API从T获得 T.class。</h3>
<p>&nbsp;&nbsp; 最典型的例子<span class=nobr><a title="Visit page outside Confluence" href="http://www.springside.org.cn/docs/api/org/springside/core/dao/HibernateEntityDao.html" rel=nofollow><u><font color=#0000ff>HibernateEntityDao<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>，子类只需以下定义，即获得要管理的Entity的Class。</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>BookManager <span class=code-keyword>extends</span> HibernateEntityDao&lt;Book&gt;</pre>
</div>
</div>
<p>&nbsp;&nbsp;&nbsp; 此时子类只要声明一次T，上面的Book book = (Book)manager(Book.class,1) 就能简化成Book book&nbsp;= manager.get(1);</p>
<p>&nbsp;&nbsp;&nbsp; 一举两得地既避免了强制类型转换，又声明了T.class 供框架使用，无须再在Manager的构造函数或getEntityClass()函数定义entityClass，。</p>
<p>&nbsp;&nbsp;&nbsp; 反射的API 详见<span class=nobr><a title="Visit page outside Confluence" href="http://www.springside.org.cn/docs/api/org/springside/core/utils/GenericsUtils.html" rel=nofollow><u><font color=#0000ff>GenricsUtils<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span> ，精简的对上面BookManager的定义反射代码如下:</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>Type genType = clazz.getGenericSuperclass();
Type [] params = ((ParameterizedType) genType).getActualTypeArguments();
<span class=code-keyword>return</span> (<span class=code-object>Class</span>) params[0];</pre>
</div>
</div>
<p>&nbsp;&nbsp;&nbsp;泛型反射的关键是获取ParameterizedType，再调用它的getActualTypeArguments()方法获得实际绑定的类型。但注意public class BookManager&lt;Book&gt;是不能被反射的，因为擦拭法的缘故。只有在Superclass 或者成员变量(Field.getGenericType())等有函数返回ParameterizedType的时候才能成功反射，，</p>
<p>比如&nbsp;</p>
<div class=code>
<div class=codeContent>
<pre class=code-java><span class=code-keyword>public</span> class BookManager <span class=code-keyword>extends</span> Manager&lt;Book&gt;{} <span class=code-keyword>public</span> class BookAction {    <span class=code-keyword>private</span> BookManager&lt;Book&gt; manager;}</pre>
</div>
</div>
<h3><a name=JDK5-%26nbsp%3B2.3%E5%85%B6%E4%BB%96%E5%BA%94%E7%94%A8></a>&nbsp;2.3 其他应用</h3>
<p>&nbsp;&nbsp; 1. 在XFire中，List getBooksByCategory()函数返回的结果，需要用aegis.xml 文件声明List中的元素为Book.</p>
<p>&nbsp;&nbsp; 而如果定义函数为 List&lt;Book&gt; getBooksByCategory()，就不再需要声明，省掉XMl配置文件。</p>
<h2><a name=JDK5-3.Annotation></a>3.Annotation</h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp; Annotation 大幅提升了Java的编程模式，SpringSide 目前运用的Annotation 有以下三个地方，幸运的是前两者同时也提供JDK1.4的JAVADoc式配置，只是麻烦一点点而已</p>
<h3><a name=JDK5-%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B3.1.HibernateAnnotation></a>&nbsp;&nbsp;&nbsp;&nbsp; 3.1. Hibernate Annotation</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用Hibernate Annotation 代替hbm文件，因为annotation高度的默认性，典型的POJO基本上不需要定义什么，代码的简约性和可管理性大幅提高，直追ROR。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 另外，经过测试，annotation 完全能胜任一些比较复杂的Mapping定义，如Product-Book的父子继承关系，Order-OrderItem-Product的经典三角关系。</p>
<h3><a name=JDK5-%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B2.XFireJSR181Annotation></a>&nbsp;&nbsp;&nbsp;&nbsp; 2. XFire JSR181 Annotation</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JSR181声明的Web Service，比原本用xml定义的模式节约了XML文件和配置代码的数量。</p>
<h3><a name=JDK5-%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B3.%26nbsp%3B%E5%A3%B0%E6%98%8EEntity%E7%B1%BB%E5%9E%8B%E7%9A%84Annotation></a>&nbsp;&nbsp;&nbsp;&nbsp; 3.&nbsp;声明Entity类型的Annotation</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用Annotation 声明Entity的类型，比如Udeletable，Auditable 等，比用接口声明的方式有更少的侵入性，详见 <a title=InterfaceAnnotation href="http://wiki.springside.org.cn/display/springside/InterfaceAnnotation"><u><font color=#0000ff>侵入，非侵入？Interface vs Annotation</font></u></a>。</p>
<h2><a name=JDK5-4.%E4%B8%89%E7%A7%8D%E5%86%85%E7%BD%AEAnnotation></a>4. 三种内置Annotation</h2>
<p>JDK5.0 有SuppressWarnings，Deprecated和Override 三种内置的annotation：</p>
<h3><a name=JDK5-%26nbsp%3B@Override></a>&nbsp; <strong>@Override</strong></h3>
<p>&nbsp;&nbsp; 此标签一方面提醒用户这是个重载函数，另一方面保证了父类函数的参数或者名字改变时，子类如果没有跟着变化，就会编译不过。</p>
<p>&nbsp;&nbsp; 虽然有点占地方，但用处的确很大，不会哪天子类被人卖了都不知道。</p>
<p>&nbsp; 所以我设置了让IDEA6检查所有重载函数必须加上@Override标识。</p>
<h3><a name=JDK5-%26nbsp%3B@SuppressWarnings%28%22unchecked%22%29></a>&nbsp; <strong>@SuppressWarnings("unchecked")</strong></h3>
<p>&nbsp;&nbsp;&nbsp; 此标签可以让编译器忽略某种warning信息，比如减少JDK5.0的集合操作引入范型后无处不在的warning。</p>
<p>&nbsp;&nbsp;&nbsp; 因为有些非JDK5.0的开源库如hibernate, 函数返回的一定是List，而不会是List&lt;User&gt;，这时候IDE就会爆出很多warning。用SuppressWarning("unchecked")可以让IDE安静一些。</p>
<p>&nbsp;&nbsp;&nbsp; 其他常用warning还包括 @SuppressWarnings("unused") 和 SuppressWarnings("serial")</p>
<h3><a name=JDK5-%26nbsp%3B@Deprecated></a>&nbsp; <strong>@Deprecated</strong></h3>
<p>&nbsp;&nbsp; 此标签以前写在JavaDoc里，现在提到annotation，注释已废弃的函数。用户使用该函数的话，编译时会得到"你用了废弃"的提示。</p>
<h2><a name=JDK5-5.%E5%8F%AF%E5%8F%98%E5%8F%82%E6%95%B0></a>5.可变参数</h2>
<p>用于<span class=nobr><a title="Visit page outside Confluence" href="http://www.springside.org.cn/docs/api/org/springside/core/dao/HibernateGenericDao.html" rel=nofollow><u><font color=#0000ff>HibernateGenericDao<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>中，简化函数接口。</p>
<p>比如 一个public List find(String hql, Object... values)，就支持了如下四种调用，避免了以前的煞费苦心的定义多种接口，然后把参数转成统一模式的大量重复定义。</p>
<div class=preformatted>
<div class=preformattedContent>
<pre>dao.find(hql);
dao.find(hql,arg0);
dao.find(hql,arg0,arg1);
dao.find(hql,new Object[arg0,arg1])
</pre>
</div>
</div>
<img src ="http://www.blogjava.net/i369/aggbug/134295.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-08-03 17:47 <a href="http://www.blogjava.net/i369/articles/134295.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi安全系统的配置 </title><link>http://www.blogjava.net/i369/articles/124284.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 14 Jun 2007 05:25:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/124284.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/124284.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/124284.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/124284.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/124284.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi 的配置看起来非常复杂,但事实上在实际项目的安全应用中我们并不需要那么多功能,清楚的了解Acegi配置中各项的功能，有助于我们灵活的运用Acegi于实践中。
<p>2.1 在Web.xml中的配置</p>
<p>1)&nbsp; <strong>FilterToBeanProxy</strong><br>　　Acegi通过实现了Filter接口的FilterToBeanProxy提供一种特殊的使用Servlet Filter的方式，它委托Spring中的Bean -- FilterChainProxy来完成过滤功能，这好处是简化了web.xml的配置，并且充分利用了Spring IOC的优势。FilterChainProxy包含了处理认证过程的filter列表，每个filter都有各自的功能。</p>
<pre>&nbsp; &nbsp; &lt;filter&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-class&gt;org.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;org.acegisecurity.util.FilterChainProxy&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/filter&gt;</pre>
<p>2) <strong>filter-mapping</strong><br>　　&lt;filter-mapping&gt;限定了FilterToBeanProxy的URL匹配模式,只有*.do和*.jsp和/j_acegi_security_check 的请求才会受到权限控制，对javascript,css等不限制。</p>
<pre>&nbsp;&nbsp; &lt;filter-mapping&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;*.do&lt;/url-pattern&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/filter-mapping&gt;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &lt;filter-mapping&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/filter-mapping&gt;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &lt;filter-mapping&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;/j_acegi_security_check&lt;/url-pattern&gt;<br>    &lt;/filter-mapping&gt;</pre>
<p>3) <strong>HttpSessionEventPublisher</strong><br>　　&lt;listener&gt;的HttpSessionEventPublisher用于发布HttpSessionApplicationEvents和HttpSessionDestroyedEvent事件给spring的applicationcontext。</p>
<pre>&nbsp;&nbsp;&nbsp; &lt;listener&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;listener-class&gt;org.acegisecurity.ui.session.HttpSessionEventPublisher&lt;/listener-class&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/listener&gt;<br></pre>
<h2><br>2.2 在applicationContext-acegi-security.xml中</h2>
<h3>2.2.1 FILTER CHAIN</h3>
<p>　　FilterChainProxy会按顺序来调用这些filter,使这些filter能享用Spring ioc的功能, CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON定义了url比较前先转为小写， PATTERN_TYPE_APACHE_ANT定义了使用Apache ant的匹配模式 </p>
<pre>    &lt;bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="filterInvocationDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PATTERN_TYPE_APACHE_ANT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,<br>basicProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,<br> exceptionTranslationFilter,filterInvocationInterceptor<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<h3>2.2.2 基础认证</h3>
<p>1) <strong>authenticationManager</strong><br>　　起到认证管理的作用，它将验证的功能委托给多个Provider，并通过遍历Providers, 以保证获取不同来源的身份认证，若某个Provider能成功确认当前用户的身份，authenticate()方法会返回一个完整的包含用户授权信息的Authentication对象，否则会抛出一个AuthenticationException。<br>Acegi提供了不同的AuthenticationProvider的实现,如：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DaoAuthenticationProvider 从数据库中读取用户信息验证身份<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AnonymousAuthenticationProvider 匿名用户身份认证<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RememberMeAuthenticationProvider 已存cookie中的用户信息身份认证<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AuthByAdapterProvider 使用容器的适配器验证身份<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CasAuthenticationProvider 根据Yale中心认证服务验证身份, 用于实现单点登陆<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JaasAuthenticationProvider 从JASS登陆配置中获取用户信息验证身份<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RemoteAuthenticationProvider 根据远程服务验证用户身份<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RunAsImplAuthenticationProvider 对身份已被管理器替换的用户进行验证<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; X509AuthenticationProvider 从X509认证中获取用户信息验证身份<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestingAuthenticationProvider 单元测试时使用</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每个认证者会对自己指定的证明信息进行认证，如DaoAuthenticationProvider仅对UsernamePasswordAuthenticationToken这个证明信息进行认证。</p>
<pre>&lt;bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="providers"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref local="daoAuthenticationProvider"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref local="anonymousAuthenticationProvider"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref local="rememberMeAuthenticationProvider"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;</pre>
<p><br>2) <strong>daoAuthenticationProvider</strong><br>　　进行简单的基于数据库的身份验证。DaoAuthenticationProvider获取数据库中的账号密码并进行匹配，若成功则在通过用户身份的同时返回一个包含授权信息的Authentication对象，否则身份验证失败，抛出一个AuthenticatiionException。</p>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="userDetailsService" ref="jdbcDaoImpl"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="userCache" ref="userCache"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="passwordEncoder" ref="passwordEncoder"/&gt;<br>&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p><br>3) <strong>passwordEncoder</strong> <br>　　使用加密器对用户输入的明文进行加密。Acegi提供了三种加密器:<br>PlaintextPasswordEncoder—默认，不加密，返回明文.<br>ShaPasswordEncoder—哈希算法(SHA)加密<br>Md5PasswordEncoder—消息摘要(MD5)加密</p>
<pre>&lt;bean id="passwordEncoder" class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/&gt;</pre>
<p><br>4) <strong>jdbcDaoImpl</strong> <br>　　用于在数据中获取用户信息。 acegi提供了用户及授权的表结构，但是您也可以自己来实现。通过usersByUsernameQuery这个SQL得到你的(用户ID,密码,状态信息);通过authoritiesByUsernameQuery这个SQL得到你的(用户ID,授权信息)</p>
<pre> &lt;bean id="jdbcDaoImpl" class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="dataSource" ref="dataSource"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="usersByUsernameQuery"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;select loginid,passwd,1 from users where loginid = ?&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authoritiesByUsernameQuery"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;select u.loginid,p.name from users u,roles r,permissions p,user_role ur,role_permis rp where u.id=ur.user_id and r.id=ur.role_id and p.id=rp.permis_id and<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r.id=rp.role_id and p.status='1' and u.loginid=?&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;</pre>
<p>5) <strong>userCache　&amp;&nbsp; resourceCache</strong> <br>　　缓存用户和资源相对应的权限信息。每当请求一个受保护资源时，daoAuthenticationProvider就会被调用以获取用户授权信息。如果每次都从数据库获取的话，那代价很高，对于不常改变的用户和资源信息来说，最好是把相关授权信息缓存起来。(详见 <a href="http://www.springside.org.cn/docs/reference/Acegi4.htm">2.6.3 资源权限定义扩展</a> )<br>userCache提供了两种实现: NullUserCache和EhCacheBasedUserCache, NullUserCache实际上就是不进行任何缓存，EhCacheBasedUserCache是使用Ehcache来实现缓功能。</p>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cacheManager" ref="cacheManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cacheName" value="userCache"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp; &lt;bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache" autowire="byName"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cache" ref="userCacheBackend"/&gt;<br>  &nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp; &lt;bean id="resourceCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cacheManager" ref="cacheManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cacheName" value="resourceCache"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp; &lt;bean id="resourceCache" class="org.springside.modules.security.service.acegi.cache.ResourceCache" autowire="byName"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cache" ref="resourceCacheBackend"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p><br>6) <strong>basicProcessingFilter</strong> <br>　　用于处理HTTP头的认证信息，如从Spring远程协议(如Hessian和Burlap)或普通的浏览器如IE,Navigator的HTTP头中获取用户信息，将他们转交给通过authenticationManager属性装配的认证管理器。如果认证成功，会将一个Authentication对象放到会话中，否则，如果认证失败，会将控制转交给认证入口点(通过authenticationEntryPoint属性装配)</p>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p>7) <strong>basicProcessingFilterEntryPoint</strong> <br>　　通过向浏览器发送一个HTTP401(未授权)消息，提示用户登录。<br>处理基于HTTP的授权过程， 在当验证过程出现异常后的"去向"，通常实现转向、在response里加入error信息等功能。</p>
<pre> &lt;bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="realmName" value="SpringSide Realm"/&gt;<br>&lt;/bean&gt;</pre>
<p>8) <strong>authenticationProcessingFilterEntryPoint</strong> <br>　　当抛出AccessDeniedException时，将用户重定向到登录界面。属性loginFormUrl配置了一个登录表单的URL,当需要用户登录时，authenticationProcessingFilterEntryPoint会将用户重定向到该URL</p>
<pre> &lt;bean id="authenticationProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="loginFormUrl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;/security/login.jsp&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="forceHttps" value="false"/&gt;<br>&lt;/bean&gt;</pre>
<h2>2.2.3 HTTP安全请求</h2>
<p>1) <strong>httpSessionContextIntegrationFilter</strong><br>　　每次request前 HttpSessionContextIntegrationFilter从Session中获取Authentication对象，在request完后, 又把Authentication对象保存到Session中供下次request使用,此filter必须其他Acegi filter前使用，使之能跨越多个请求。</p>
<pre>&lt;bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"&gt;&lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp; &lt;bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="allowIfAllAbstainDecisions" value="false"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="decisionVoters"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="roleVoter"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;</pre>
<p><br>2) <strong>httpRequestAccessDecisionManager</strong><br>　　经过投票机制来决定是否可以访问某一资源(URL或方法)。allowIfAllAbstainDecisions为false时如果有一个或以上的decisionVoters投票通过,则授权通过。可选的决策机制有ConsensusBased和UnanimousBased</p>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="allowIfAllAbstainDecisions" value="false"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="decisionVoters"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="roleVoter"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p><br>3) <strong>roleVoter</strong><br>&nbsp;　　必须是以rolePrefix设定的value开头的权限才能进行投票,如AUTH_ , ROLE_</p>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="rolePrefix" value="AUTH_"/&gt;<br>&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p>4）<strong>exceptionTranslationFilter</strong><br>　　异常转换过滤器，主要是处理AccessDeniedException和AuthenticationException，将给每个异常找到合适的"去向"&nbsp;</p>
<pre>&nbsp;&nbsp; &lt;bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationEntryPoint" ref="authenticationProcessingFilterEntryPoint"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p>5) <strong>authenticationProcessingFilter</strong><br>　　和servlet spec差不多,处理登陆请求.当身份验证成功时，AuthenticationProcessingFilter会在会话中放置一个Authentication对象，并且重定向到登录成功页面<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; authenticationFailureUrl定义登陆失败时转向的页面<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; defaultTargetUrl定义登陆成功时转向的页面<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; filterProcessesUrl定义登陆请求的页面<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rememberMeServices用于在验证成功后添加cookie信息</p>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationFailureUrl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;/security/login.jsp?login_error=1&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultTargetUrl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;/admin/index.jsp&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="filterProcessesUrl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;/j_acegi_security_check&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="rememberMeServices" ref="rememberMeServices"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p>6) <strong>filterInvocationInterceptor</strong><br>　　在执行转向url前检查objectDefinitionSource中设定的用户权限信息。首先，objectDefinitionSource中定义了访问URL需要的属性信息(这里的属性信息仅仅是标志，告诉accessDecisionManager要用哪些voter来投票)。然后，authenticationManager掉用自己的provider来对用户的认证信息进行校验。最后，有投票者根据用户持有认证和访问url需要的属性，调用自己的voter来投票，决定是否允许访问。</p>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="objectDefinitionSource" ref="filterDefinitionSource"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p><br>7) <strong>filterDefinitionSource </strong>(详见 <a href="http://www.springside.org.cn/docs/reference/Acegi4.htm">2.6.3 资源权限定义扩展</a>)<br>　　自定义DBFilterInvocationDefinitionSource从数据库和cache中读取保护资源及其需要的访问权限信息&nbsp;</p>
<pre>&lt;bean id="filterDefinitionSource" class="org.springside.modules.security.service.acegi.DBFilterInvocationDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="convertUrlToLowercaseBeforeComparison" value="true"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="useAntPath" value="true"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="acegiCacheManager" ref="acegiCacheManager"/&gt;<br>&lt;/bean&gt;</pre>
<h2>2.2.4 方法调用安全控制</h2>
<p>(详见 <a href="http://www.springside.org.cn/docs/reference/Acegi4.htm">2.6.3 资源权限定义扩展</a>)</p>
<p>1) methodSecurityInterceptor<br>　　在执行方法前进行拦截，检查用户权限信息<br>2) methodDefinitionSource<br>　　自定义MethodDefinitionSource从cache中读取权限</p>
<pre>&nbsp;&nbsp; &lt;bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="objectDefinitionSource" ref="methodDefinitionSource"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp; &lt;bean id="methodDefinitionSource" class="org.springside.modules.security.service.acegi.DBMethodDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="acegiCacheManager" ref="acegiCacheManager"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<h2>2.3 Jcaptcha验证码</h2>
<p>采用 <a href="http://jcaptcha.sourceforge.net/">http://jcaptcha.sourceforge.net</a>&nbsp;作为通用的验证码方案，请参考SpringSide中的例子，或网上的：<br><a href="http://www.coachthrasher.com/page/blog?entry=jcaptcha_with_appfuse">http://www.coachthrasher.com/page/blog?entry=jcaptcha_with_appfuse</a>。</p>
<p>差沙在此过程中又发现acegi logout filter的错误，进行了修正。</p>
<p>另外它默认提供的图片比较难认，我们custom了一个美观一点的版本。</p>
<p>
<script src="foot.js" type=text/javascript></script>
</p>
<center>
<script type=text/javascript>google_ad_client = "pub-2429212051207422";google_ad_width = 728;google_ad_height = 90;google_ad_format = "728x90_as";google_ad_type = "text";google_ad_channel ="";</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type=text/javascript></script>
</center>
<img src ="http://www.blogjava.net/i369/aggbug/124284.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-06-14 13:25 <a href="http://www.blogjava.net/i369/articles/124284.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Acegi为你的Spring应用加把锁！</title><link>http://www.blogjava.net/i369/articles/124283.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 14 Jun 2007 05:24:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/124283.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/124283.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/124283.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/124283.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/124283.html</trackback:ping><description><![CDATA[<strong><font color=#0000ff>[简介]<br></font></strong><font size=2>对于一个典型的Web应用，完善的认证和授权机制是必不可少的，在SpringFramework中，Juergen Hoeller提供的范例JPetStore给了一些这方面的介绍，但还远远不够，Acegi是一个专门为SpringFramework提供安全机制的 项目，全称为Acegi Security System for Spring，当前版本为0.5.1，就其目前提供的功能，应该可以满足绝大多数应用的需求。<br><br>本文的主要目的是希望能够说明如何在基于Spring构架的Web应用中使用Acegi，而不是详细介绍其中的每个接口、每个类。注意，即使对已经存在的Spring应用，通过下面介绍的步骤，也可以马上享受到Acegi提供的认证和授权。</font><br><font color=#0000ff><br><strong>[基础工作]</strong></font><br><font size=2>在你的Web应用的lib中添加Acegi下载包中的acegi-security.jar</font><br><br><font color=#0000ff><strong>[web.xml]</strong></font><br><font size=2>实现认证和授权的最常用的方法是通过filter，Acegi亦是如此，通常Acegi需要在web.xml添加以下5个filter:</font><br><font size=2><br><font color=#800080>&lt;filter&gt;<br>&nbsp; &lt;filter-name&gt;Acegi Channel Processing Filter&lt;/filter-name&gt;<br>&nbsp; &lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp; &lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp; &lt;param-value&gt;net.sf.acegisecurity.securechannel.ChannelProcessingFilter&lt;/param-value&gt;<br>&nbsp; &lt;/init-param&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp; &lt;filter-name&gt;Acegi Authentication Processing Filter&lt;/filter-name&gt;<br>&nbsp; &lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp; &lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp; &lt;param-value&gt;net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter&lt;/param-value&gt;<br>&nbsp; &lt;/init-param&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp; &lt;filter-name&gt;Acegi HTTP BASIC Authorization Filter&lt;/filter-name&gt;<br>&nbsp; &lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp; &lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp; &lt;param-value&gt;net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter&lt;/param-value&gt;<br>&nbsp; &lt;/init-param&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp; &lt;filter-name&gt;Acegi Security System for Spring Auto Integration Filter&lt;/filter-name&gt;<br>&nbsp; &lt;filter-class&gt;net.sf.acegisecurity.ui.AutoIntegrationFilter&lt;/filter-class&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp; &lt;filter-name&gt;Acegi HTTP Request Security Filter&lt;/filter-name&gt;<br>&nbsp; &lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp; &lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp; &lt;param-value&gt;net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter&lt;/param-value&gt;<br>&nbsp; &lt;/init-param&gt;<br>&lt;/filter&gt;<br></font><br>最先引起迷惑的是net.sf.acegisecurity.util.FilterToBeanProxy，Acegi自己的文档上解释是： &#8220;What&nbsp; FilterToBeanProxy does is delegate the Filter's methods through to a bean which is obtained from the <br>Spring application context. This enables the bean to benefit from the Spring application context lifecycle support and configuration flexibility.&#8221;，如希望深究的话，去看看源代码应该不难理解。<br><br>再下来就是添加filter-mapping了：<br><font color=#800080>&lt;filter-mapping&gt;<br>&nbsp; &lt;filter-name&gt;Acegi Channel Processing Filter&lt;/filter-name&gt;<br>&nbsp; &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp; &lt;filter-name&gt;Acegi Authentication Processing Filter&lt;/filter-name&gt;<br>&nbsp; &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp; &lt;filter-name&gt;Acegi HTTP BASIC Authorization Filter&lt;/filter-name&gt;<br>&nbsp; &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp; &lt;filter-name&gt;Acegi Security System for Spring Auto Integration Filter&lt;/filter-name&gt;<br>&nbsp; &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp; &lt;filter-name&gt;Acegi HTTP Request Security Filter&lt;/filter-name&gt;<br>&nbsp; &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br></font><br>这里，需要注意以下两点：<br>1) 这几个filter的顺序是不能更改的，顺序不对将无法正常工作；<br>2) 如果你的应用不需要安全传输，如https，则将"Acegi Channel Processing Filter"相关内容注释掉即可；<br>3) 如果你的应用不需要Spring提供的远程访问机制，如Hessian and Burlap，将"Acegi HTTP BASIC Authorization <br>Filter"相关内容注释掉即可。<br><br><strong><font color=#0000ff size=3>[applicationContext.xml]</font></strong><br>接下来就是要添加applicationContext.xml中的内容了，从刚才FilterToBeanFactory的解释可以看出，真正的filter都<br>在Spring的applicationContext中管理：<br><br>1) 首先，你的数据库中必须具有保存用户名和密码的table，Acegi要求table的schema必须如下：<br><br><font color=#800080>CREATE TABLE users (<br>&nbsp;&nbsp; &nbsp;username VARCHAR(50) NOT NULL PRIMARY KEY,<br>&nbsp;&nbsp; &nbsp;password VARCHAR(50) NOT NULL,<br>&nbsp;&nbsp; &nbsp;enabled BIT NOT NULL<br>);<br>CREATE TABLE authorities (<br>&nbsp;&nbsp; &nbsp;username VARCHAR(50) NOT NULL,<br>&nbsp;&nbsp; &nbsp;authority VARCHAR(50) NOT NULL<br>);<br>CREATE UNIQUE INDEX ix_auth_username ON authorities ( username, authority );<br>ALTER TABLE authorities ADD CONSTRAINT fk_authorities_users foreign key (username) REFERENCES users<br>(username);<br></font><br>2) 添加访问你的数据库的datasource和Acegi的jdbcDao，如下：<br><br><font color=#800080>&lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;<br>&nbsp; &lt;property name="driverClassName"&gt;&lt;value&gt;${jdbc.driverClassName}&lt;/value&gt;&lt;/property&gt;<br>&nbsp; &lt;property name="url"&gt;&lt;value&gt;${jdbc.url}&lt;/value&gt;&lt;/property&gt;<br>&nbsp; &lt;property name="username"&gt;&lt;value&gt;${jdbc.username}&lt;/value&gt;&lt;/property&gt;<br>&nbsp; &lt;property name="password"&gt;&lt;value&gt;${jdbc.password}&lt;/value&gt;&lt;/property&gt;<br>&lt;/bean&gt;<br>&lt;bean id="jdbcDaoImpl" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl"&gt;<br>&nbsp; &lt;property name="dataSource"&gt;&lt;ref bean="dataSource"/&gt;&lt;/property&gt;<br>&lt;/bean&gt;</font><br><br>3) 添加DaoAuthenticationProvider:<br><br><font color=#800080>&lt;bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;<br>&nbsp; &lt;property name="authenticationDao"&gt;&lt;ref bean="authenticationDao"/&gt;&lt;/property&gt;<br>&nbsp; &lt;property name="userCache"&gt;&lt;ref bean="userCache"/&gt;&lt;/property&gt;<br>&lt;/bean&gt;<br><br>&lt;bean id="userCache" class="net.sf.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"&gt;<br>&nbsp; &lt;property name="minutesToIdle"&gt;&lt;value&gt;5&lt;/value&gt;&lt;/property&gt;<br>&lt;/bean&gt;</font><br><br>如果你需要对密码加密，则在daoAuthenticationProvider中加入：&lt;property name="passwordEncoder"&gt;&lt;ref <br>bean="passwordEncoder"/&gt;&lt;/property&gt;，Acegi提供了几种加密方法，详细情况可看包<br>net.sf.acegisecurity.providers.encoding<br><br>4) 添加authenticationManager:<br><br><font color=#800080>&lt;bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"&gt;<br>&nbsp; &lt;property name="providers"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="daoAuthenticationProvider"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/list&gt;<br>&nbsp;&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;</font><br><br>5) 添加accessDecisionManager:<br><br><font color=#800080>&lt;bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased"&gt;<br>&nbsp; &lt;property name="allowIfAllAbstainDecisions"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;value&gt;false&lt;/value&gt;<br>&nbsp; &lt;/property&gt;<br>&nbsp; &lt;property name="decisionVoters"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;list&gt;&lt;ref bean="roleVoter"/&gt;&lt;/list&gt;<br>&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;<br>&lt;bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/&gt;<br></font><br>6) 添加authenticationProcessingFilterEntryPoint:<br><br><font color=#800080>&lt;bean id="authenticationProcessingFilterEntryPoint" <br>class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;<br>&nbsp; &lt;property name="loginFormUrl"&gt;&lt;value&gt;/acegilogin.jsp&lt;/value&gt;&lt;/property&gt;<br>&nbsp; &lt;property name="forceHttps"&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;<br>&lt;/bean&gt;</font><br><br>其中acegilogin.jsp是登陆页面，一个最简单的登录页面如下：<br><br><font color=#800080>&lt;%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %&gt;<br>&lt;%@ page import="net.sf.acegisecurity.ui.AbstractProcessingFilter" %&gt;<br>&lt;%@ page import="net.sf.acegisecurity.AuthenticationException" %&gt;<br>&lt;html&gt;<br>&nbsp; &lt;head&gt;<br>&nbsp;&nbsp;&nbsp; &lt;title&gt;Login&lt;/title&gt;<br>&nbsp; &lt;/head&gt;<br><br>&nbsp; &lt;body&gt;<br>&nbsp;&nbsp;&nbsp; &lt;h1&gt;Login&lt;/h1&gt;<br>&nbsp;&nbsp;&nbsp; &lt;form action="&lt;c:url value='j_acegi_security_check'/&gt;" method="POST"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;table&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td&gt;User:&lt;/td&gt;&lt;td&gt;&lt;input type='text' name='j_username'&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td&gt;Password:&lt;/td&gt;&lt;td&gt;&lt;input type='password' name='j_password'&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td colspan='2'&gt;&lt;input name="submit" type="submit"&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td colspan='2'&gt;&lt;input name="reset" type="reset"&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/table&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/form&gt;<br>&nbsp; &lt;/body&gt;<br>&lt;/html&gt;</font><br><br>7) 添加filterInvocationInterceptor:<br><br><font color=#800080>&lt;bean id="filterInvocationInterceptor" <br>class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br>&nbsp; &lt;property name="authenticationManager"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;ref bean="authenticationManager"/&gt;<br>&nbsp; &lt;/property&gt;<br>&nbsp; &lt;property name="accessDecisionManager"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;ref bean="accessDecisionManager"/&gt;<br>&nbsp; &lt;/property&gt;<br>&nbsp; &lt;property name="objectDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \A/sec/administrator.*\Z=ROLE_SUPERVISOR<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \A/sec/user.*\Z=ROLE_TELLER<br>&nbsp;&nbsp;&nbsp; &lt;/value&gt;<br>&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;</font><br><br>这里请注意，要objectDefinitionSource中定义哪些页面需要权限访问，需要根据自己的应用需求进行修改，我上面给出<br>的定义的意思是这样的：<br>&nbsp;a. CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON意思是在比较请求路径时全部转换为小写<br>&nbsp;b. \A/sec/administrator.*\Z=ROLE_SUPERVISOR意思是只有权限为ROLE_SUPERVISOR才能访问/sec/administrator*的页面<br>&nbsp;c. \A/sec/user.*\Z=ROLE_TELLER意思是只有权限为ROLE_TELLER的用户才能访问/sec/user*的页面<br><br>8) 添加securityEnforcementFilter:<br><br><font color=#800080>&lt;bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter"&gt;<br>&nbsp; &lt;property name="filterSecurityInterceptor"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;ref bean="filterInvocationInterceptor"/&gt;<br>&nbsp; &lt;/property&gt;<br>&nbsp; &lt;property name="authenticationEntryPoint"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;ref bean="authenticationProcessingFilterEntryPoint"/&gt;<br>&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;<br><br><font color=#000000>9) 添加authenticationProcessingFilter:</font><br><br>&lt;bean id="authenticationProcessingFilter" <br>class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;<br>&nbsp; &lt;property name="authenticationManager"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;ref bean="authenticationManager"/&gt;<br>&nbsp; &lt;/property&gt;<br>&nbsp; &lt;property name="authenticationFailureUrl"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;value&gt;/loginerror.jsp&lt;/value&gt;<br>&nbsp; &lt;/property&gt;<br>&nbsp; &lt;property name="defaultTargetUrl"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;value&gt;/&lt;/value&gt;<br>&nbsp; &lt;/property&gt;<br>&nbsp; &lt;property name="filterProcessesUrl"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;value&gt;/j_acegi_security_check&lt;/value&gt;<br>&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;</font><br>其中authenticationFailureUrl是认证失败的页面。<br><br>10) 如果需要一些页面通过安全通道的话，添加下面的配置:<br><br><font color=#800080>&lt;bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter"&gt;<br>&nbsp; &lt;property name="channelDecisionManager"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;ref bean="channelDecisionManager"/&gt;<br>&nbsp; &lt;/property&gt;<br>&nbsp; &lt;property name="filterInvocationDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \A/sec/administrator.*\Z=REQUIRES_SECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \A.*\Z=REQUIRES_INSECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp; &lt;/value&gt;<br>&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;<br><br>&lt;bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"&gt;<br>&nbsp; &lt;property name="channelProcessors"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="secureChannelProcessor"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="insecureChannelProcessor"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/list&gt;<br>&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;<br>&lt;bean id="secureChannelProcessor" class="net.sf.acegisecurity.securechannel.SecureChannelProcessor"/&gt;<br>&lt;bean id="insecureChannelProcessor" class="net.sf.acegisecurity.securechannel.InsecureChannelProcessor"/&gt;</font><br><br><strong><font color=#0000ff size=3>[缺少了什么？]</font></strong><br>Acegi目前提供了两种"secure object"，分别对页面和方法进行安全认证管理，我这里介绍的只是利用<br>FilterSecurityInterceptor对访问页面的权限控制，除此之外，Acegi还提供了另外一个Interceptor——<br>MethodSecurityInterceptor，它结合runAsManager可实现对对象中的方法的权限控制，使用方法可参看Acegi自带的文档<br>和contact范例。<br><br><strong><font color=#0000ff size=3>[最后要说的]</font></strong><br>本来以为只是说明如何使用Acegi而已，应该非常简单，但真正写起来才发现想要条理清楚的理顺所有需要的bean还是很<br>困难的，但愿我没有遗漏太多东西，如果我的文章有什么遗漏或错误的话，还请参看Acegi自带的quick-start范例，但请<br>注意，这个范例是不能直接拿来用的。</font><br><br>
<p id=TBPingURL>Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=54881</p>
<img src ="http://www.blogjava.net/i369/aggbug/124283.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-06-14 13:24 <a href="http://www.blogjava.net/i369/articles/124283.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring安全系统：Acegi Security </title><link>http://www.blogjava.net/i369/articles/124282.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 14 Jun 2007 05:22:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/124282.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/124282.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/124282.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/124282.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/124282.html</trackback:ping><description><![CDATA[<h4>Acegi简介</h4>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统，是一个用于Spring Framework的安全框架，能够和目前流行的Web容器无缝集成。它使用了Spring的方式提供了安全和认证安全服务，包括使用Bean Context，拦截器和面向接口的编程方式。因此，Acegi安全系统能够轻松地适用于复杂的安全需求。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 安全涉及到两个不同的概念，认证和授权。前者是关于确认用户是否确实是他们所宣称的身份。授权则是关于确认用户是否有允许执行一个特定的操作。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在Acegi安全系统中，需要被认证的用户，系统或代理称为"Principal"。Acegi安全系统和其他的安全系统不同，它并没有角色和用户组的概念。</p>
<h4>Acegi系统设计</h4>
<h5>&nbsp; 关键组件</h5>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统包含以下七个关键的功能组件：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; l&nbsp;Authentication对象，包含了Principal，Credential和Principal的授权信息。同时还可以包含关于发起认证请求的客户的其他信息，如IP地址。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;ContextHolder对象，使用ThreadLocal储存Authentication对象的地方。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;AuthenticationManager，用于认证ContextHolder中的Authentication对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4&nbsp;AccessDecissionManager，用于授权一个特定的操作。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5&nbsp;RunAsManager，当执行特定的操作时，用于选择性地替换Authentication对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6&nbsp;Secure Object拦截器，用于协调AuthenticationManager，AccessDecissionManager，RunAsManager和特定操作的执行。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7&nbsp;ObjectDefinitionSource，包含了特定操作的授权定义。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这七个关键的功能组件的关系如下图所示（图中灰色部分是关键组件）：</p>
<h5 align=center><img src="http://starrynight.blogdriver.com/diary/starrynight/inc/acegi1.gif"></h5>
<h5 align=left><br>&nbsp; 安全管理对象</h5>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统目前支持两类安全管理对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一类的安全管理对象管理AOP Alliance的MethodInvocation，开发人员可以用它来保护Spring容器中的业务对象。为了使Spring管理的Bean可以作为MethodInvocation来使用，Bean可以通过ProxyFactoryBean和BeanNameAutoProxyCreator来管理，就像在Spring的事务管理一样使用。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第二类是FilterInvocation。它用过滤器（Filter）来创建，并简单地包装了HTTP的ServletRequest，ServletResponse和FilterChain。FilterInvocation可以用来保护HTTP资源。通常，开发人员并不需要了解它的工作机制，因为他们只需要将Filter加入web.xml，Acegi安全系统就可以工作了。</p>
<h5 align=left>&nbsp; 安全配置参数</h5>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每个安全管理对象都可以描述数量不限的各种安全认证请求。例如，MethodInvocation对象可以描述带有任意参数的任意方法的调用，而FilterInvocation可以描述任意的HTTP URL。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统需要记录应用于每个认证请求的安全配置参数。例如，对于BankManager.getBalance（int accountNumber）方法和BankManager.approveLoan（int applicationNumber）方法，它们需要的认证请求的安全配置很不相同。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了保存不同的认证请求的安全配置，需要使用配置参数。从实现的视角来看，配置参数使用ConfigAttribute接口来表示。Acegi安全系统提供了ConfigAttribute接口的一个实现，SecurityConfig，它把配置参数保存为一个字符串。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ConfigAttributeDefinition类是ConfigAttribute对象的一个简单的容器，它保存了和特定请求相关的ConfigAttribute的集合。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当安全拦截器收到一个安全认证请求时，需要决定应用哪一个配置参数。换句话说，它需要找出应用于这个请求的ConfigAttributeDefinition对象。这个查找的过程是由ObjectDefinitionSource接口来处理的。这个接口的主要方法是public ConfigAttributeDefinition getAttributes(Object object)，其中Object参数是一个安全管理对象。因为安全管理对象包含有认证请求的详细信息，所以ObjectDefinitionSource接口的实现类可以从中获得所需的详细信息，以查找相关的ConfigAttributeDefiniton对象。</p>
<h5 align=left>&nbsp; Acegi如何工作</h5>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了说明Acegi安全系统如何工作，我们设想一个使用Acegi的例子。通常，一个安全系统需要发挥作用，它必须完成以下的工作：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; l&nbsp;首先，系统从客户端请求中获得Principal和Credential；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;然后系统认证Principal和Credential信息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;如果认证通过，系统取出Principal的授权信息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4&nbsp;接下来，客户端发起操作请求；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5&nbsp;系统根据预先配置的参数检查Principal对于该操作的授权；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6&nbsp;如果授权检查通过则执行操作，否则拒绝。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么，Acegi安全系统是如何完成这些工作的呢？首先，我们来看看Acegi安全系统的认证和授权的相关类图： </p>
<p align=center>&nbsp;<img src="http://starrynight.blogdriver.com/diary/starrynight/inc/acegi2.gif"></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图中绿色部分是安全拦截器的抽象基类，它包含有两个管理类，AuthenticationManager和AccessDecisionManager，如图中灰色部分。AuthenticationManager用于认证ContextHolder中的Authentication对象（包含了Principal，Credential和Principal的授权信息）；AccessDecissionManager则用于授权一个特定的操作。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下面来看一个MethodSecurityInterceptor的例子：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="bankManagerSecurity" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="validateConfigAttributes"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;true&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;property name="authenticationManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;ref bean="authenticationManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="accessDecisionManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;ref bean="accessDecisionManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;property name="objectDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;net.sf.acegisecurity.context.BankManager.delete*=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ROLE_SUPERVISOR,RUN_AS_SERVER<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;net.sf.acegisecurity.context.BankManager.getBalance=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;
<p>&#160;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上面的配置文件中，MethodSecurityInterceptor是AbstractSecurityInterceptor的一个实现类。它包含了两个管理器，authenticationManager和accessDecisionManager。这两者的配置如下：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="authenticationDao" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="dataSource"&gt;&lt;ref bean="dataSource"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="daoAuthenticationProvider" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationDao"&gt;&lt;ref bean="authenticationDao"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="providers"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;list&gt;&lt;ref bean="daoAuthenticationProvider"/&gt;&lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="allowIfAllAbstainDecisions"&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="decisionVoters"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;list&gt;&lt;ref bean="roleVoter"/&gt;&lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 准备工作做好了，现在我们来看看Acegi安全系统是如何实现认证和授权机制的。以使用HTTP BASIC认证的应用为例子，它包括下面的步骤：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.&nbsp;用户登录系统，Acegi从acegisecurity.ui子系统的安全拦截器（如BasicProcessingFilter）中得到用户的登录信息（包括Principal和Credential）并放入Authentication对象，并保存在ContextHolder对象中；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.&nbsp;安全拦截器将Authentication对象交给AuthenticationManager进行身份认证，如果认证通过，返回带有Principal授权信息的Authentication对象。此时ContextHolder对象的Authentication对象已拥有Principal的详细信息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.&nbsp;用户登录成功后，继续进行业务操作；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.&nbsp;安全拦截器（bankManagerSecurity）收到客户端操作请求后，将操作请求的数据包装成安全管理对象（FilterInvocation或MethodInvocation对象）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.&nbsp;然后，从配置文件（ObjectDefinitionSource）中读出相关的安全配置参数ConfigAttributeDefinition；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.&nbsp;接着，安全拦截器取出ContextHolder中的Authentication对象，把它传递给AuthenticationManager进行身份认证，并用返回值更新ContextHolder的Authentication对象；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.&nbsp;将Authentication对象，ConfigAttributeDefinition对象和安全管理对象（secure Object）交给AccessDecisionManager，检查Principal的操作授权；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8.&nbsp;如果授权检查通过则执行客户端请求的操作，否则拒绝；</p>
<p>&nbsp; <strong>AccessDecisionVoter</strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意上节的accessDecisionManager是一个AffirmativeBased类，它对于用户授权的投票策略是，只要通过其中的一个授权投票检查，即可通过；它的allowIfAllAbstainDecisions属性值是false，意思是如果所有的授权投票是都是弃权，则通不过授权检查。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统包括了几个基于投票策略的AccessDecisionManager，上节的RoleVoter就是其中的一个投票策略实现，它是AccessDecisionVoter的一个子类。AccessDecisionVoter的具体实现类通过投票来进行授权决策，AccessDecisionManager则根据投票结果来决定是通过授权检查，还是抛出AccessDeniedException例外。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AccessDecisionVoter接口共有三个方法：<br>public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config);<br>public boolean supports(ConfigAttribute attribute);<br>public boolean supports(Class clazz);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其中的vote方法返回int返回值，它们是AccessDecisionVoter的三个静态成员属性：ACCESS_ABSTAIN,，ACCESS_DENIED和ACCESS_GRANTED，它们分别是弃权，否决和赞成。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统中，使用投票策略的AccessDecisionManager共有三个具体实现类：AffirmativeBased、ConsensusBased和UnanimousBased。它们的投票策略是，AffirmativeBased类只需有一个投票赞成即可通过；ConsensusBased类需要大多数投票赞成即可通过；而UnanimousBased类需要所有的投票赞成才能通过。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RoleVoter类是一个Acegi安全系统AccessDecisionVoter接口的实现。如果ConfigAttribute以ROLE_开头，RoleVoter则进行投票。如果GrantedAuthority的getAutority方法的String返回值匹配一个或多个以ROLE_开头的ConfigAttribute，则投票通过，否则不通过。如果没有以ROLE_开头的ConfigAttribute，RoleVoter则弃权。</p>
<h4>安全拦截器</h4>
<h5>&nbsp; 拦截器如何工作<br>&nbsp; MethodInvocation拦截器<br>&nbsp; FilterInvocation拦截器</h5>
<h4>认证</h4>
<h5>&nbsp; 认证请求<br>&nbsp; 认证管理器<br>&nbsp; Authentication Provider</h5>
<h4>授权</h4>
<h5>&nbsp; Access Decision Manager<br>&nbsp; Voting Decision Manager<br>&nbsp; 授权管理推荐</h5>
<h4>ContextHolder的用户接口</h4>
<h5>&nbsp; 用户接口目标<br>&nbsp; HTTP会话认证<br>&nbsp; HTTP Basic认证</h5>
<img src ="http://www.blogjava.net/i369/aggbug/124282.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-06-14 13:22 <a href="http://www.blogjava.net/i369/articles/124282.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi安全框架</title><link>http://www.blogjava.net/i369/articles/124281.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 14 Jun 2007 05:19:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/124281.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/124281.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/124281.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/124281.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/124281.html</trackback:ping><description><![CDATA[<div><strong>简介</strong></div>
<div>Acegi是为基于Spring的应用提供的声明式安全框架。它通过在Spring的应用上下文中配置一系列的Bean完成安全设置，完成利用了Spring提供的依赖注入和IoC编程方式。</div>
<div style="TEXT-INDENT: 21.75pt">为了保证Web应用的安全需求，Acegi使用过滤器拦截servlet请求，并执行认证来执行安全措施。</div>
<div style="TEXT-INDENT: 21.75pt">Acegi通过安全方法级调用来执行更低层次的安全需求。通过使用Spring的AOP，Acegi使用代理对象来确保用户有适当的权限来调用被保护的方法。</div>
<div style="TEXT-INDENT: 21.75pt">无论是较高层次的Web应用的安全，还是较低层次的方法级安全，Acegi都可以通过四个主要组件完成安全需求。</div>
<div style="TEXT-INDENT: 21.75pt"><img alt=图一 src="http://p.blog.csdn.net/images/p_blog_csdn_net/tony1130/snap1.gif"></div>
<div style="TEXT-INDENT: 21.75pt"><strong>Security Interceptor </strong>用于拦截那些需要访问受保护资源的请求。</div>
<div style="TEXT-INDENT: 21.75pt"><strong>Authentication Managers </strong>用于验证主体的身份，如你的principal(典型的如用户名)和你的Credentials（典型的如密码）。能过验证，可以证明Who are you 。</div>
<div style="TEXT-INDENT: 21.75pt"><strong>Access Decision Mangers </strong>用于决定已验证通过的principal是否有访问受保护资源的特权。</div>
<div style="TEXT-INDENT: 21.1pt" align=left><strong>Run-as Managers</strong> 尽管你通过了验证和并得到授权可以访问资源，但可能还会有更多的安全约束：例如，你可能得到了查看某个Web页面的权力，但是页面中可以还有比Web页面安全级别更高的对象。Run-as manager 正是用于这方面的验证。尽管这方面的需求不多，但Acegi通过Run-as Managers可以做到这一点。</div>
<div align=left>&nbsp;</div>
<div align=left><strong>管理验证</strong></div>
<div align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi</span>定义了AuthenticationManager接口，用于安全验证，同时提供了代理类ProviderManager以及它的相关实现，它们可以完成大部分相应的功能，而不需要我们自己开发。</div>
<div align=left><img alt=图二 src="http://p.blog.csdn.net/images/p_blog_csdn_net/tony1130/snap2.gif"></div>
<div align=left>&nbsp;</div>
<div align=left><strong>通过Acegi</strong><strong>和CAS</strong><strong>实现SSO</strong></div>
<div align=left><span>&nbsp;&nbsp; </span>耶鲁大学的CAS是SSO的一个解决方案。CAS的细节内容已经超出了本文的讨论范围。为了理解Acegi如何与基于CAS验证的应用相结合的问题，有必要看一下一个典型的CAS验证例子是如何工作的。详见前面研究过的CAS的文档。</div>
<div align=left><img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/tony1130/snap3.gif"></div>
<div style="TEXT-INDENT: 21pt" align=left>你应该理解的一个关键概念就是这个受保护的应用根本不处理用户的Credentials。当用户打算登录应时时，实际上他们登录的是CAS Server.。应用根本不知道用户的Credentials。唯一要做的就是验证用户的ticket是否有效，这个ticket是由CAS Server 发放的。这是一件好事，因为它意味着只有一个应用（即CAS）负责处理用户的验证。</div>
<div style="TEXT-INDENT: 21pt" align=left>当CAS与Acegi配合使用时，Acegi要做的就是在应用上校验CAS ticket的工作。这使得应用本身可以不管CAS的身份验证过程。 </div>
<div align=left><span>&nbsp;&nbsp; Acegi</span>通过CasAuthenticationProvider类达到这个目的，它不关心用户名和密码，而是接受一个CAS Ticket作为它的Credentials。除了配置CasAuthenticationProvider类以外，还需要在Spring的配置文件中配置其它几个辅助类，它们分别是<span style="FONT-SIZE: 9.5pt">.CasProxyDecider</span><span style="FONT-SIZE: 10pt">及其子类。</span></div>
<div align=left><strong>访问控制</strong></div>
<div style="TEXT-INDENT: 21.75pt" align=left>身份验证只是Acegi的第一步。一旦Acegi知道用户是谁，接下来它必须决定用户是否有权访问被保护的资源。这是通过 Access Decision Managers完成的。Acegi定义了net.sf.acegisecurity.AccessDecisionManager接口，它的supports()方法决定该应用是否有权做出访问控制，decide()方法最终是否可以访问，如果不可以访问该资源，应抛出AccessDeniedException。</div>
<div style="TEXT-INDENT: 21pt" align=left>AccessDecisionManager有三个实现类，分别是net.sf.acegisecurity.vote.AffirmativeBased，net.sf.acegisecurity.vote.ConsensusBased</div>
<div align=left>net.sf.acegisecurity.vote.UnanimousBased。AccessDecisionManager做出决定并不是通过它自己，而是把轮询一个或更多年对象，这些对象对这个用户是否有权访问受保护的资源进行投票。一旦得到所有的投票，AccessDecisionManager会对这些投票进行计数，并做出最终决定。</div>
<div align=left>&nbsp;</div>
<div align=left><strong>保护</strong><strong>Web</strong><strong>应用</strong></div>
<div style="TEXT-INDENT: 21pt" align=left>Acegi对Web应用的保护是强依赖于过滤器的。这些过滤器在请求被应用处理之前进行拦截，并进行安全控制。根据应用的实际需求，Acegi可以使用6个过滤器，它们分别是</div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 131.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=175>
            <div align=left>过滤器</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #e0dfe3; PADDING-BOTTOM: 0cm; WIDTH: 294.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=393>
            <div align=left>功能</div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 131.4pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=175>
            <div align=left><span style="FONT-SIZE: 8pt">Channel-processing filter</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #e0dfe3; PADDING-BOTTOM: 0cm; WIDTH: 294.7pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=393>
            <div align=left>确保请求是通过SSL提交的（HTTPS）</div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 131.4pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=175>
            <div align=left><span style="FONT-SIZE: 8pt">Authentication-processing filter</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #e0dfe3; PADDING-BOTTOM: 0cm; WIDTH: 294.7pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=393>
            <div align=left>接受验证请求，并将其导向验证管理器进行验证</div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 131.4pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=175>
            <div align=left><span style="FONT-SIZE: 8pt">CAS-processing filter</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #e0dfe3; PADDING-BOTTOM: 0cm; WIDTH: 294.7pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=393>
            <div align=left><span style="FONT-SIZE: 8pt">接受</span><span style="FONT-SIZE: 8pt">CAS</span><span style="FONT-SIZE: 8pt">服务的</span><span style="FONT-SIZE: 8pt">ticket</span><span style="FONT-SIZE: 8pt">作为经过</span><span style="FONT-SIZE: 8pt">CAS</span><span style="FONT-SIZE: 8pt">验证的证明</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 131.4pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=175>
            <div align=left><span style="FONT-SIZE: 8pt">HTTP Basic authorization filter</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #e0dfe3; PADDING-BOTTOM: 0cm; WIDTH: 294.7pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=393>
            <div align=left><span style="FONT-SIZE: 8pt">Processes authentication performed using HTTP Basic authentication</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 131.4pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=175>
            <div align=left><span style="FONT-SIZE: 8pt">Integration filter</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #e0dfe3; PADDING-BOTTOM: 0cm; WIDTH: 294.7pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=393>
            <div align=left><span style="FONT-SIZE: 8pt">Handles storage of authentication between requests (in HTTP Session,for example)</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 131.4pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=175>
            <div align=left><span style="FONT-SIZE: 8pt">Security enforcement filter</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; PADDING-LEFT: 5.4pt; BORDER-LEFT-COLOR: #e0dfe3; PADDING-BOTTOM: 0cm; WIDTH: 294.7pt; BORDER-TOP-COLOR: #e0dfe3; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent" vAlign=top width=393>
            <div align=left><span style="FONT-SIZE: 8pt">Ensures that a user has been authenticated and meets the property</span></div>
            <div align=left><span style="FONT-SIZE: 8pt">authorization requirements to access a secured web resource</span></div>
            </td>
        </tr>
    </tbody>
</table>
<div align=left>接受一个请求以后，它以下面的顺序进行。</div>
<div align=center><span><img alt="图四 执行顺序" src="http://p.blog.csdn.net/images/p_blog_csdn_net/tony1130/snap4.gif"></span></div>
&nbsp;<br><br>
<p id=TBPingURL>Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1356799</p>
<img src ="http://www.blogjava.net/i369/aggbug/124281.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-06-14 13:19 <a href="http://www.blogjava.net/i369/articles/124281.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi简介 </title><link>http://www.blogjava.net/i369/articles/124280.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 14 Jun 2007 05:17:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/124280.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/124280.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/124280.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/124280.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/124280.html</trackback:ping><description><![CDATA[Acegi安全系统，是一个用于Spring Framework的安全框架，能够和目前流行的Web容器无缝集成。它使用了Spring的方式提供了安全和认证安全服务，包括使用Bean Context，拦截器和面向接口的编程方式。因此，Acegi安全系统能够轻松地适用于复杂的安全需求。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 安全涉及到两个不同的概念，认证和授权。前者是关于确认用户是否确实是他们所宣称的身份。授权则是关于确认用户是否有允许执行一个特定的操作。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在Acegi安全系统中，需要被认证的用户，系统或代理称为"Principal"。Acegi安全系统和其他的安全系统不同，它并没有角色和用户组的概念。<br>Acegi系统设计<br>&nbsp;&nbsp;关键组件<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Acegi安全系统包含以下七个关键的功能组件：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1 Authentication对象，包含了Principal，Credential和Principal的授权信息。同时还可以包含关于发起认证请求的客户的其他信息，如IP地址。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 ContextHolder对象，使用ThreadLocal储存Authentication对象的地方。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3 AuthenticationManager，用于认证ContextHolder中的Authentication对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 AccessDecissionManager，用于授权一个特定的操作。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5 RunAsManager，当执行特定的操作时，用于选择性地替换Authentication对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6 Secure Object拦截器，用于协调AuthenticationManager，AccessDecissionManager，RunAsManager和特定操作的执行。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7 ObjectDefinitionSource，包含了特定操作的授权定义。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这七个关键的功能组件的关系如下图所示（图中灰色部分是关键组件）：<br><br><br><strong>安全管理对象</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统目前支持两类安全管理对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一类的安全管理对象管理AOP Alliance的MethodInvocation，开发人员可以用它来保护Spring容器中的业务对象。为了使Spring管理的Bean可以作为MethodInvocation来使用，Bean可以通过ProxyFactoryBean和BeanNameAutoProxyCreator来管理，就像在Spring的事务管理一样使用。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第二类是FilterInvocation。它用过滤器（Filter）来创建，并简单地包装了HTTP的ServletRequest，ServletResponse和FilterChain。FilterInvocation可以用来保护HTTP资源。通常，开发人员并不需要了解它的工作机制，因为他们只需要将Filter加入web.xml，Acegi安全系统就可以工作了。<br><br><strong>安全配置参数</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每个安全管理对象都可以描述数量不限的各种安全认证请求。例如，MethodInvocation对象可以描述带有任意参数的任意方法的调用，而FilterInvocation可以描述任意的HTTP URL。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统需要记录应用于每个认证请求的安全配置参数。例如，对于BankManager.getBalance（int accountNumber）方法和BankManager.approveLoan（int applicationNumber）方法，它们需要的认证请求的安全配置很不相同。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了保存不同的认证请求的安全配置，需要使用配置参数。从实现的视角来看，配置参数使用ConfigAttribute接口来表示。Acegi安全系统提供了ConfigAttribute接口的一个实现，SecurityConfig，它把配置参数保存为一个字符串。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ConfigAttributeDefinition类是ConfigAttribute对象的一个简单的容器，它保存了和特定请求相关的ConfigAttribute的集合。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当安全拦截器收到一个安全认证请求时，需要决定应用哪一个配置参数。换句话说，它需要找出应用于这个请求的ConfigAttributeDefinition对象。这个查找的过程是由ObjectDefinitionSource接口来处理的。这个接口的主要方法是public ConfigAttributeDefinition getAttributes(Object object)，其中Object参数是一个安全管理对象。因为安全管理对象包含有认证请求的详细信息，所以ObjectDefinitionSource接口的实现类可以从中获得所需的详细信息，以查找相关的ConfigAttributeDefiniton对象。<br><br><br><strong>Acegi如何工作</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了说明Acegi安全系统如何工作，我们设想一个使用Acegi的例子。通常，一个安全系统需要发挥作用，它必须完成以下的工作：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1 首先，系统从客户端请求中获得Principal和Credential；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 然后系统认证Principal和Credential信息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3 如果认证通过，系统取出Principal的授权信息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 接下来，客户端发起操作请求；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5 系统根据预先配置的参数检查Principal对于该操作的授权；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6 如果授权检查通过则执行操作，否则拒绝。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;那么，Acegi安全系统是如何完成这些工作的呢？首先，我们来看看Acegi安全系统的认证和授权的相关类： <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;安全拦截器的抽象基类，它包含有两个管理类，AuthenticationManager和AccessDecisionManager。AuthenticationManager用于认证ContextHolder中的Authentication对象（包含了Principal，Credential和Principal的授权信息）；AccessDecissionManager则用于授权一个特定的操作。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面来看一个MethodSecurityInterceptor的例子：<br>
<pre class=overflow>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="bankManagerSecurity" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="validateConfigAttributes"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;true&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="authenticationManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="authenticationManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="accessDecisionManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="accessDecisionManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="objectDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; net.sf.acegisecurity.context.BankManager.delete*=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROLE_SUPERVISOR,RUN_AS_SERVER<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; net.sf.acegisecurity.context.BankManager.getBalance=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt; </pre>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上面的配置文件中，MethodSecurityInterceptor是AbstractSecurityInterceptor的一个实现类。它包含了两个管理器，authenticationManager和accessDecisionManager。这两者的配置如下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<pre class=overflow>&lt;bean id="authenticationDao" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="dataSource"&gt;&lt;ref bean="dataSource"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="daoAuthenticationProvider" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationDao"&gt;&lt;ref bean="authenticationDao"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="providers"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;&lt;ref bean="daoAuthenticationProvider"/&gt;&lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="allowIfAllAbstainDecisions"&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="decisionVoters"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;&lt;ref bean="roleVoter"/&gt;&lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;</pre>
<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 准备工作做好了，现在我们来看看Acegi安全系统是如何实现认证和授权机制的。以使用HTTP BASIC认证的应用为例子，它包括下面的步骤：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1. 用户登录系统，Acegi从acegisecurity.ui子系统的安全拦截器（如BasicProcessingFilter）中得到用户的登录信息（包括Principal和Credential）并放入Authentication对象，并保存在ContextHolder对象中；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2. 安全拦截器将Authentication对象交给AuthenticationManager进行身份认证，如果认证通过，返回带有Principal授权信息的Authentication对象。此时ContextHolder对象的Authentication对象已拥有Principal的详细信息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3. 用户登录成功后，继续进行业务操作；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4. 安全拦截器（bankManagerSecurity）收到客户端操作请求后，将操作请求的数据包装成安全管理对象（FilterInvocation或MethodInvocation对象）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5. 然后，从配置文件（ObjectDefinitionSource）中读出相关的安全配置参数ConfigAttributeDefinition；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6. 接着，安全拦截器取出ContextHolder中的Authentication对象，把它传递给AuthenticationManager进行身份认证，并用返回值更新ContextHolder的Authentication对象；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7. 将Authentication对象，ConfigAttributeDefinition对象和安全管理对象（secure Object）交给AccessDecisionManager，检查Principal的操作授权；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8. 如果授权检查通过则执行客户端请求的操作，否则拒绝；<br><br><strong>AccessDecisionVoter</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意上节的accessDecisionManager是一个AffirmativeBased类，它对于用户授权的投票策略是，只要通过其中的一个授权投票检查，即可通过；它的allowIfAllAbstainDecisions属性值是false，意思是如果所有的授权投票是都是弃权，则通不过授权检查。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统包括了几个基于投票策略的AccessDecisionManager，上节的RoleVoter就是其中的一个投票策略实现，它是AccessDecisionVoter的一个子类。AccessDecisionVoter的具体实现类通过投票来进行授权决策，AccessDecisionManager则根据投票结果来决定是通过授权检查，还是抛出AccessDeniedException例外。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AccessDecisionVoter接口共有三个方法：<br>public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config);<br>public boolean supports(ConfigAttribute attribute);<br>public boolean supports(Class clazz);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其中的vote方法返回int返回值，它们是AccessDecisionVoter的三个静态成员属性：ACCESS_ABSTAIN,，ACCESS_DENIED和ACCESS_GRANTED，它们分别是弃权，否决和赞成。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统中，使用投票策略的AccessDecisionManager共有三个具体实现类：AffirmativeBased、ConsensusBased和UnanimousBased。它们的投票策略是，AffirmativeBased类只需有一个投票赞成即可通过；ConsensusBased类需要大多数投票赞成即可通过；而UnanimousBased类需要所有的投票赞成才能通过。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RoleVoter类是一个Acegi安全系统AccessDecisionVoter接口的实现。如果ConfigAttribute以ROLE_开头，RoleVoter则进行投票。如果GrantedAuthority的getAutority方法的String返回值匹配一个或多个以ROLE_开头的ConfigAttribute，则投票通过，否则不通过。如果没有以ROLE_开头的ConfigAttribute，RoleVoter则弃权。<br><br><strong>安全拦截器</strong><br>&nbsp;&nbsp;拦截器如何工作<br>&nbsp;&nbsp;MethodInvocation拦截器<br>&nbsp;&nbsp;FilterInvocation拦截器<br>认证<br>&nbsp;&nbsp;认证请求<br>&nbsp;&nbsp;认证管理器<br>&nbsp;&nbsp;Authentication Provider<br>授权<br>&nbsp;&nbsp;Access Decision Manager<br>&nbsp;&nbsp;Voting Decision Manager<br>&nbsp;&nbsp;授权管理推荐<br>ContextHolder的用户接口<br>&nbsp;&nbsp;用户接口目标<br>&nbsp;&nbsp;HTTP会话认证<br>&nbsp;&nbsp;HTTP Basic认证<br><br>1、Log4j的概念<br>&nbsp;&nbsp; Log4j中有三个主要的组件，它们分别是Logger、Appender和Layout，Log4j 允许开发人员定义多个Logger，每个Logger拥有自己的名字，Logger之间通过名字来表明隶属关系。有一个Logger称为Root，它永远 存在，且不能通过名字检索或引用，可以通过Logger.getRootLogger()方法获得，其它Logger通过 Logger.getLogger(String name)方法。<br>&nbsp;&nbsp; Appender则是用来指明将所有的log信息存放到什么地方，Log4j中支持多种appender，如 console、files、GUI components、NT Event Loggers等，一个Logger可以拥有多个Appender，也就是你既可以将Log信息输出到屏幕，同时存储到一个文件中。<br>&nbsp;&nbsp; Layout的作用是控制Log信息的输出方式，也就是格式化输出的信息。<br>&nbsp;&nbsp; Log4j中将要输出的Log信息定义了5种级别，依次为DEBUG、INFO、WARN、ERROR和FATAL，当输出时，只有级别高过配置中规定的 级别的信息才能真正的输出，这样就很方便的来配置不同情况下要输出的内容，而不需要更改代码，这点实在是方便啊。<br><br>2、Log4j的配置文件<br>&nbsp;&nbsp;虽然可以不用配置文件，而在程序中实现配置，但这种方法在如今的系统开发中显然是不可取的，能采用配置文件的地方一定一定要用配置文件。Log4j支持两 种格式的配置文件：XML格式和Java的property格式，本人更喜欢后者，首先看一个简单的例子吧，如下：<br><br>
<pre class=overflow> log4j.rootLogger=debug, stdout, R<br>&nbsp;&nbsp;log4j.appender.stdout=org.apache.log4j.ConsoleAppender<br>&nbsp;&nbsp;log4j.appender.stdout.layout=org.apache.log4j.PatternLayout<br><br>&nbsp;&nbsp;# Pattern to output the caller's file name and line number.<br>&nbsp;&nbsp;log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n<br><br>&nbsp;&nbsp;log4j.appender.R=org.apache.log4j.RollingFileAppender<br>&nbsp;&nbsp;log4j.appender.R.File=example.log<br>&nbsp;&nbsp;log4j.appender.R.MaxFileSize=100KB<br><br>&nbsp;&nbsp;# Keep one backup file<br>&nbsp;&nbsp;log4j.appender.R.MaxBackupIndex=1<br><br>&nbsp;&nbsp;log4j.appender.R.layout=org.apache.log4j.PatternLayout<br>&nbsp;&nbsp;log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n</pre>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br><br>&nbsp;&nbsp;首先，是设置root，格式为 log4j.rootLogger=[level],appenderName, ...，其中level就是设置需要输出信息的级别，后面是appender的输出的目的地，appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。配置日志信息输出目的地Appender，其语法为<br>&nbsp;&nbsp;log4j.appender.appenderName = fully.qualified.name.of.appender.class<br>&nbsp;&nbsp;log4j.appender.appenderName.option1 = value1<br>&nbsp;&nbsp;...<br>&nbsp;&nbsp;log4j.appender.appenderName.option = valueN<br>Log4j提供的appender有以下几种：<br>&nbsp;&nbsp;org.apache.log4j.ConsoleAppender（控制台）<br>&nbsp;&nbsp;org.apache.log4j.FileAppender（文件）<br>&nbsp;&nbsp;org.apache.log4j.DailyRollingFileAppender（每天产生一个日志文件）<br>&nbsp;&nbsp;org.apache.log4j.RollingFileAppender（文件大小到达指定尺寸的时候产生新文件）<br>&nbsp;&nbsp;org.apache.log4j.WriterAppender（将日志信息以流格式发送到任意指定的地方）<br>配置日志信息的格式（布局），其语法为：<br>&nbsp;&nbsp;log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class<br>&nbsp;&nbsp;log4j.appender.appenderName.layout.option1 = value1<br>&nbsp;&nbsp;....<br>&nbsp;&nbsp;log4j.appender.appenderName.layout.option = valueN<br>Log4j提供的layout有以下几种：<br>&nbsp;&nbsp;org.apache.log4j.HTMLLayout（以HTML表格形式布局），<br>&nbsp;&nbsp;org.apache.log4j.PatternLayout（可以灵活地指定布局模式），<br>&nbsp;&nbsp;org.apache.log4j.SimpleLayout（包含日志信息的级别和信息字符串），<br>&nbsp;&nbsp;org.apache.log4j.TTCCLayout（包含日志产生的时间、线程、类别等等信息） <br><br>3、Log4j在程序中的使用<br>&nbsp;&nbsp;要在自己的类中使用Log4j，首先声明一个静态变量Logger logger=Logger.getLog("classname")；在使用之前，用PropertyConfigurator.configure ("配置文件")配置一下，现在就可以使用了，用法如下：logger.debug("debug message")或者logger.info("info message")，看下面一个小例子：<br><br>
<pre class=overflow> import com.foo.Bar;<br>&nbsp;&nbsp;import org.apache.log4j.Logger;<br>&nbsp;&nbsp;import org.apache.log4j.PropertyConfigurator;<br>&nbsp;&nbsp;public class MyApp {<br>&nbsp;&nbsp;&nbsp;&nbsp;static Logger logger = Logger.getLogger(MyApp.class.getName());<br>&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// BasicConfigurator replaced with PropertyConfigurator.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PropertyConfigurator.configure(args[0]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info("Entering application.");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bar bar = new Bar();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bar.doIt();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info("Exiting application.");<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}</pre>
<br><br><br>[简介]<br><br>对于一个典型的Web应用，完善的认证和授权机制是必不可少的，在SpringFramework中，Juergen Hoeller提供的范例JPetStore给了一些这方面的介绍，但还远远不够，Acegi是一个专门为SpringFramework提供安全机制的 项目，全称为Acegi Security System for Spring，当前版本为0.5.1，就其目前提供的功能，应该可以满足绝大多数应用的需求。<br><br>本文的主要目的是希望能够说明如何在基于Spring构架的Web应用中使用Acegi，而不是详细介绍其中的每个接口、每个类。注意，即使对已经存在的Spring应用，通过下面介绍的步骤，也可以马上享受到Acegi提供的认证和授权。<br><br>[基础工作]<br>在你的Web应用的lib中添加Acegi下载包中的acegi-security.jar<br><br>[web.xml]<br>实现认证和授权的最常用的方法是通过filter，Acegi亦是如此，通常Acegi需要在web.xml添加以下5个filter:<br><br>
<pre class=overflow>&lt;filter&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Channel Processing Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp;&nbsp;&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;net.sf.acegisecurity.securechannel.ChannelProcessingFilter&lt;/param-value&gt;<br>&nbsp;&nbsp;&lt;/init-param&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Authentication Processing Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp;&nbsp;&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter&lt;/param-value&gt;<br>&nbsp;&nbsp;&lt;/init-param&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi HTTP BASIC Authorization Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp;&nbsp;&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter&lt;/param-value&gt;<br>&nbsp;&nbsp;&lt;/init-param&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Security System for Spring Auto Integration Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;filter-class&gt;net.sf.acegisecurity.ui.AutoIntegrationFilter&lt;/filter-class&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi HTTP Request Security Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp;&nbsp;&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter&lt;/param-value&gt;<br>&nbsp;&nbsp;&lt;/init-param&gt;<br>&lt;/filter&gt;</pre>
<br><br>最先引起迷惑的是net.sf.acegisecurity.util.FilterToBeanProxy，Acegi自己的文档上解释是： &#8220;What&nbsp;&nbsp;FilterToBeanProxy does is delegate the Filter's methods through to a bean which is obtained from the <br>Spring application context. This enables the bean to benefit from the Spring application context lifecycle support and configuration flexibility.&#8221;，如希望深究的话，去看看源代码应该不难理解。<br><br>再下来就是添加filter-mapping了：<br>
<pre class=overflow>&lt;filter-mapping&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Channel Processing Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Authentication Processing Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi HTTP BASIC Authorization Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Security System for Spring Auto Integration Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi HTTP Request Security Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;</pre>
<br><br>这里，需要注意以下两点：<br>1) 这几个filter的顺序是不能更改的，顺序不对将无法正常工作；<br>2) 如果你的应用不需要安全传输，如https，则将"Acegi Channel Processing Filter"相关内容注释掉即可；<br>3) 如果你的应用不需要Spring提供的远程访问机制，如Hessian and Burlap，将"Acegi HTTP BASIC Authorization <br>Filter"相关内容注释掉即可。<br><br>[applicationContext.xml]<br>接下来就是要添加applicationContext.xml中的内容了，从刚才FilterToBeanFactory的解释可以看出，真正的filter都<br>在Spring的applicationContext中管理：<br><br>1) 首先，你的数据库中必须具有保存用户名和密码的table，Acegi要求table的schema必须如下：<br><br>
<pre class=overflow>CREATE TABLE users (<br>&nbsp;&nbsp;&nbsp;&nbsp;username VARCHAR(50) NOT NULL PRIMARY KEY,<br>&nbsp;&nbsp;&nbsp;&nbsp;password VARCHAR(50) NOT NULL,<br>&nbsp;&nbsp;&nbsp;&nbsp;enabled BIT NOT NULL<br>);<br>CREATE TABLE authorities (<br>&nbsp;&nbsp;&nbsp;&nbsp;username VARCHAR(50) NOT NULL,<br>&nbsp;&nbsp;&nbsp;&nbsp;authority VARCHAR(50) NOT NULL<br>);<br>CREATE UNIQUE INDEX ix_auth_username ON authorities ( username, authority );<br>ALTER TABLE authorities ADD CONSTRAINT fk_authorities_users foreign key (username) REFERENCES users<br>(username);</pre>
<br><br>2) 添加访问你的数据库的datasource和Acegi的jdbcDao，如下：<br><br>
<pre class=overflow>&lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;<br>&nbsp;&nbsp;&lt;property name="driverClassName"&gt;&lt;value&gt;${jdbc.driverClassName}&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="url"&gt;&lt;value&gt;${jdbc.url}&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="username"&gt;&lt;value&gt;${jdbc.username}&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="password"&gt;&lt;value&gt;${jdbc.password}&lt;/value&gt;&lt;/property&gt;<br>&lt;/bean&gt;<br>&lt;bean id="jdbcDaoImpl" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl"&gt;<br>&nbsp;&nbsp;&lt;property name="dataSource"&gt;&lt;ref bean="dataSource"/&gt;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>3) 添加DaoAuthenticationProvider:<br><br>
<pre class=overflow>&lt;bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;<br>&nbsp;&nbsp;&lt;property name="authenticationDao"&gt;&lt;ref bean="authenticationDao"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="userCache"&gt;&lt;ref bean="userCache"/&gt;&lt;/property&gt;<br>&lt;/bean&gt;<br><br>&lt;bean id="userCache" class="net.sf.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"&gt;<br>&nbsp;&nbsp;&lt;property name="minutesToIdle"&gt;&lt;value&gt;5&lt;/value&gt;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>如果你需要对密码加密，则在daoAuthenticationProvider中加入：&lt;property name="passwordEncoder"&gt;&lt;ref <br>bean="passwordEncoder"/&gt;&lt;/property&gt;，Acegi提供了几种加密方法，详细情况可看包<br>net.sf.acegisecurity.providers.encoding<br><br>4) 添加authenticationManager:<br><br>
<pre class=overflow>&lt;bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"&gt;<br>&nbsp;&nbsp;&lt;property name="providers"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="daoAuthenticationProvider"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br>&nbsp;&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>5) 添加accessDecisionManager:<br><br>
<pre class=overflow>&lt;bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased"&gt;<br>&nbsp;&nbsp;&lt;property name="allowIfAllAbstainDecisions"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;false&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="decisionVoters"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;&lt;ref bean="roleVoter"/&gt;&lt;/list&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;<br>&lt;bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/&gt;</pre>
<br><br>6) 添加authenticationProcessingFilterEntryPoint:<br><br>
<pre class=overflow>&lt;bean id="authenticationProcessingFilterEntryPoint" <br>class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;<br>&nbsp;&nbsp;&lt;property name="loginFormUrl"&gt;&lt;value&gt;/acegilogin.jsp&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="forceHttps"&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>其中acegilogin.jsp是登陆页面，一个最简单的登录页面如下：<br><br>
<pre class=overflow>&lt;%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %&gt;<br>&lt;%@ page import="net.sf.acegisecurity.ui.AbstractProcessingFilter" %&gt;<br>&lt;%@ page import="net.sf.acegisecurity.AuthenticationException" %&gt;<br>&lt;html&gt;<br>&nbsp;&nbsp;&lt;head&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;Login&lt;/title&gt;<br>&nbsp;&nbsp;&lt;/head&gt;<br><br>&nbsp;&nbsp;&lt;body&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;Login&lt;/h1&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;form action="&lt;c:url value='j_acegi_security_check'/&gt;" method="POST"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;table&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;&lt;td&gt;User:&lt;/td&gt;&lt;td&gt;&lt;input type='text' name='j_username'&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;&lt;td&gt;Password:&lt;/td&gt;&lt;td&gt;&lt;input type='password' name='j_password'&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;&lt;td colspan='2'&gt;&lt;input name="submit" type="submit"&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;&lt;td colspan='2'&gt;&lt;input name="reset" type="reset"&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/table&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/form&gt;<br>&nbsp;&nbsp;&lt;/body&gt;<br>&lt;/html&gt;</pre>
<br><br>7) 添加filterInvocationInterceptor:<br><br>
<pre class=overflow>&lt;bean id="filterInvocationInterceptor" <br>class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br>&nbsp;&nbsp;&lt;property name="authenticationManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="authenticationManager"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="accessDecisionManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="accessDecisionManager"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="objectDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A/sec/administrator.*\Z=ROLE_SUPERVISOR<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A/sec/user.*\Z=ROLE_TELLER<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>这里请注意，要objectDefinitionSource中定义哪些页面需要权限访问，需要根据自己的应用需求进行修改，我上面给出<br>的定义的意思是这样的：<br>a. CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON意思是在比较请求路径时全部转换为小写<br>b. \A/sec/administrator.*\Z=ROLE_SUPERVISOR意思是只有权限为ROLE_SUPERVISOR才能访问/sec/administrator*的页面<br>c. \A/sec/user.*\Z=ROLE_TELLER意思是只有权限为ROLE_TELLER的用户才能访问/sec/user*的页面<br><br>8) 添加securityEnforcementFilter:<br><br>
<pre class=overflow>&lt;bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter"&gt;<br>&nbsp;&nbsp;&lt;property name="filterSecurityInterceptor"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="filterInvocationInterceptor"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="authenticationEntryPoint"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="authenticationProcessingFilterEntryPoint"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>9) 添加authenticationProcessingFilter:<br><br>
<pre class=overflow>&lt;bean id="authenticationProcessingFilter" <br>class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;<br>&nbsp;&nbsp;&lt;property name="authenticationManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="authenticationManager"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="authenticationFailureUrl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;/loginerror.jsp&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="defaultTargetUrl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;/&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="filterProcessesUrl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;/j_acegi_security_check&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br>其中authenticationFailureUrl是认证失败的页面。<br><br>10) 如果需要一些页面通过安全通道的话，添加下面的配置:<br><br>
<pre class=overflow>&lt;bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter"&gt;<br>&nbsp;&nbsp;&lt;property name="channelDecisionManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="channelDecisionManager"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="filterInvocationDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A/sec/administrator.*\Z=REQUIRES_SECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A.*\Z=REQUIRES_INSECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;<br><br>&lt;bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"&gt;<br>&nbsp;&nbsp;&lt;property name="channelProcessors"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="secureChannelProcessor"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="insecureChannelProcessor"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;<br>&lt;bean id="secureChannelProcessor" class="net.sf.acegisecurity.securechannel.SecureChannelProcessor"/&gt;<br>&lt;bean id="insecureChannelProcessor" class="net.sf.acegisecurity.securechannel.InsecureChannelProcessor"/&gt;</pre>
<br><br>[缺少了什么？]<br>Acegi目前提供了两种"secure object"，分别对页面和方法进行安全认证管理，我这里介绍的只是利用<br>FilterSecurityInterceptor对访问页面的权限控制，除此之外，Acegi还提供了另外一个Interceptor――<br>MethodSecurityInterceptor，它结合runAsManager可实现对对象中的方法的权限控制，使用方法可参看Acegi自带的文档<br>和contact范例。<br><br>[最后要说的]<br>本来以为只是说明如何使用Acegi而已，应该非常简单，但真正写起来才发现想要条理清楚的理顺所有需要的bean还是很<br>困难的，但愿我没有遗漏太多东西，如果我的文章有什么遗漏或错误的话，还请参看Acegi自带的quick-start范例，但请<br>注意，这个范例是不能直接拿来用的。<br>分析和学习Spring中的jpetstore用户管理 <br>&nbsp;&nbsp;存在用户的系统，必然需要用户的登录和认证，今天就通过分析Spring中自带的jpetstore的例子来学习一下如何实现在Spring构架的系统中用户登录。<br>1、首先从注册用户开始，先看看jpetstore-servlet.xml中关于注册用户的bean定义，从定义命名中就可以看出下面这段就是注册用户的：<br>&nbsp;&nbsp;
<pre class=overflow>&lt;bean name="/shop/newAccount.do" class="org.springframework.samples.jpetstore.web.spring.AccountFormController"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="petStore"&gt;&lt;ref bean="petStore"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="validator"&gt;&lt;ref bean="accountValidator"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="successView"&gt;&lt;value&gt;index&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;/bean&gt;</pre>
<br>1). formView呢？从AccountFormController的构造函数中得到，原来为EditAccountForm；&nbsp;&nbsp;<br>2). EditoAccountForm.jsp中显得非常乱，其实没有多少难理解的地方，最主要的是这个form既是添加新用户的，又是编辑用户信息的，所以显得有点乱糟糟的。<br>2、添加好了新用户，接下来看看如何登录，在jpetstore-servlet中发现这两个相关bean定义，如下：<br>&nbsp;&nbsp;
<pre class=overflow>&lt;bean name="/shop/signon.do" class="org.springframework.samples.jpetstore.web.spring.SignonController"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="petStore"&gt;&lt;ref bean="petStore"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;/bean&gt;<br>&nbsp;&nbsp;&lt;bean name="/shop/signonForm.do" class="org.springframework.web.servlet.mvc.ParameterizableViewController"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="viewName"&gt;&lt;value&gt;SignonForm&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;/bean&gt;</pre>
<br>1). 第二个bean是在运行时用户输入用户名和密码的form，叫做SignonForm，对于这个 ParameterizableViewController，用文档里的话说这是最简单的Controller，其作用就是在运行中指向 Controller而不是直接指向jsp文件，仅此而已。<br>2). SignonForm.jsp，里面就是一个简单的form，其action就是第一个bean，即/shop/signon.do，最需要注意的是 signonForwardAction，其主要作用是forward到需要输入用户名和密码的那个页面上去，这个变量哪里来的呢？看看下面：<br>&nbsp;&nbsp;
<pre class=overflow>&lt;bean id="secureHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="interceptors"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="signonInterceptor"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="urlMap"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;map&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry key="/shop/editAccount.do"&gt;&lt;ref local="secure_editAccount"/&gt;&lt;/entry&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry key="/shop/listOrders.do"&gt;&lt;ref local="secure_listOrders"/&gt;&lt;/entry&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry key="/shop/newOrder.do"&gt;&lt;ref local="secure_newOrder"/&gt;&lt;/entry&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry key="/shop/viewOrder.do"&gt;&lt;ref local="secure_viewOrder"/&gt;&lt;/entry&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/map&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;/bean&gt;</pre>
<p><br>&nbsp;&nbsp;原来，上面的signonInterceptor实现了preHandle，因此在请求上面的map页面时，首先要经过这个Interceptor，看看 SignonInterceptor的源码，原来在其中为signon.jsp赋予一个signonForwardAction对象，呵呵，总算明白了。<br>3). 接下来去学习一下SignonController，其主体部分中可以看出，首先取出用户输入的username和password，然后到数据库中验证 有没有这个用户，如果没有这个用户，返回各错误页面；如果成功，首先生成一个UserSession对象，在request的session加入这个 userSession，注意这部分代码中给出了PagedListHolder分页的简单使用方法，关于分页显示，以后再学习吧。<br>3、登录成功后，就可以根据不同的用户设施不同的行为了，取得用户信息，无非就是从session取出userSession即可。</p>
<p>链接：<a href="http://www.matrix.org.cn/resource/article/1/1730_Acegi.html"><u><font color=#0000ff>http://www.matrix.org.cn/resource/article/1/1730_Acegi.html</font></u></a></p>
<img src ="http://www.blogjava.net/i369/aggbug/124280.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-06-14 13:17 <a href="http://www.blogjava.net/i369/articles/124280.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring的Acegi security的配置，和JDK1.5的一些问题 </title><link>http://www.blogjava.net/i369/articles/124279.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 14 Jun 2007 05:13:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/124279.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/124279.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/124279.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/124279.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/124279.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Spring的Acegi security的配置，和JDK1.5的一些问题 新的项目没有开始于是抽空做一个通用一些的Security,后来又考虑到CAS和SSL的认证问题俺还没有弄懂，就选择直接使用Spring的子项目acegi acegi是基于Spring的的一个安全框架，支持HTTP基本（basic）验证、HTTP Request Session验证、安全通道、ACL等等，功能强大。配...&nbsp;&nbsp;<a href='http://www.blogjava.net/i369/articles/124279.html'>阅读全文</a><img src ="http://www.blogjava.net/i369/aggbug/124279.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-06-14 13:13 <a href="http://www.blogjava.net/i369/articles/124279.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个定时更新cache框架 </title><link>http://www.blogjava.net/i369/articles/112007.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 19 Apr 2007 09:44:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/112007.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/112007.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/112007.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/112007.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/112007.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 应项目需要做了一个定时更新的 cache 框架，采用 spring+quartz 很方便的实现，可以适用任何需要定时才更新的地方，比如静态网页 cache 等。代码很简单： ---------------------------------QuartzCacheHandler-------------------------------------  package &nbsp;com...&nbsp;&nbsp;<a href='http://www.blogjava.net/i369/articles/112007.html'>阅读全文</a><img src ="http://www.blogjava.net/i369/aggbug/112007.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-04-19 17:44 <a href="http://www.blogjava.net/i369/articles/112007.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ajax、Struts、spring的无缝结合</title><link>http://www.blogjava.net/i369/articles/111968.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Thu, 19 Apr 2007 08:03:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/111968.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/111968.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/111968.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/111968.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/111968.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: zhipingch 原创&nbsp;&nbsp;&nbsp;&nbsp;去年初，正好负责一个医药信息系统的设计开发，架构设计时，采用Struts+JDBC（自定义采用适配器模式封装了HashMap动态VO实现的持久层）。后来ajax热潮兴起，正好系统中有很多地方需要和服务器端交互数据，如采购销售系统中的订单头/订单明细等主从表结构的维护。&nbsp;&nbsp;&nbsp;&nbsp;[col...&nbsp;&nbsp;<a href='http://www.blogjava.net/i369/articles/111968.html'>阅读全文</a><img src ="http://www.blogjava.net/i369/aggbug/111968.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-04-19 16:03 <a href="http://www.blogjava.net/i369/articles/111968.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SpringSide代码规范</title><link>http://www.blogjava.net/i369/articles/111607.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Wed, 18 Apr 2007 06:05:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/111607.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/111607.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/111607.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/111607.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/111607.html</trackback:ping><description><![CDATA[<h2><a name=CodingStandards-%E5%89%8D%E8%A8%80></a>前言</h2>
<p>&nbsp;&nbsp;&nbsp; 本文档反映的是SpringSide 团队的编码规范，同时推荐所有使用SpringSide框架的开发人员遵循。</p>
<p>&nbsp;&nbsp;&nbsp; 本文档基本遵循<span class=nobr><a title="Visit page outside Confluence" href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html" rel=nofollow><u><font color=#0000ff>Sun's Coding Conventions<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>，补充了其中没有说明或者有所改动的地方。</p>
<h3><a name=CodingStandards-%E7%89%88%E6%9D%83%E5%A3%B0%E6%98%8E%26nbsp%3B%26nbsp%3B%26nbsp%3B></a>版权声明&nbsp;&nbsp;&nbsp;</h3>
<p>&nbsp;&nbsp;&nbsp; 本规范由<span class=nobr><a title="Visit page outside Confluence" href="http://www.springside.org.cn/team.php" rel=nofollow><u><font color=#0000ff>springside团队<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>维护，相关评论与意见请发至springside@gmail.com，转载请注明出处。</p>
<h3><a name=CodingStandards-%E8%A7%84%E8%8C%83%E7%AD%89%E7%BA%A7%E8%AF%B4%E6%98%8E></a>规范等级说明</h3>
<ul>
    <li><font color=#000000>级别I:&nbsp;&nbsp; 默认级别，要求所有项目中的所有成员遵守。</font>
    <li><font color=#cc6600>级别II: &nbsp;建议所有项目中的所有成员遵守。</font>
    <li><font color=#3333ff>级别III: 鼓</font><font color=#3333ff>励各个项目根据实际情况执行。</font> </li>
</ul>
<h2><a name=CodingStandards-1.%E6%A0%BC%E5%BC%8F%E4%B8%8E%E5%91%BD%E5%90%8D%E8%A7%84%E8%8C%83%28FormatingandNamingConventions%29></a>1.格式与命名规范(Formating and Naming Conventions)</h2>
<h3><a name=CodingStandards-1.1%26nbsp%3B%26nbsp%3B%E7%BC%A9%E8%BF%9B></a>1.1&nbsp;&nbsp;缩进</h3>
<p>&nbsp; 使用Tab缩进，而不是空格键--将缩进2，4，8字符的选择权留给阅读者。</p>
<h3><a name=CodingStandards-1.2%E6%8D%A2%E8%A1%8C></a>1.2 换行</h3>
<p>&nbsp;&nbsp; 每行120字符--因为已是1024*768的年代。</p>
<p>&nbsp; &nbsp;if,for,while语句只有单句时，如果该句可能引起阅读混淆，需要用" {"和"}"括起来，否则可以省略。</p>
<div class=code>
<div class=codeContent>
<pre class=code-java><span class=code-comment>//错误，需要使用花括号{}括起来
</span><span class=code-keyword>if</span> (condition)
<span class=code-keyword>if</span>(condition) doSomething();
<span class=code-keyword>else</span>
doSomething();</pre>
</div>
</div>
<h3><a name=CodingStandards-1.3%26nbsp%3B%E5%91%BD%E5%90%8D%E8%A7%84%E5%88%99%26nbsp%3B></a>1.3&nbsp;命名规则&nbsp;</h3>
<ul>
    <li>不允许使用汉语拼音命名&nbsp;
    <li>遇到缩写如XML时，仅首字母大写，即loadXmlDocument()而不是loadXMLDocument()
    <li>Package名必须全部小写，尽量使用单个单词
    <li>Interface名可以是一个名词或形容词(加上'able','ible', or 'er'后缀)，如Runnable，Accessible。<br>为了基于接口编程，不采用首字母为I或加上IF后缀的命名方式，如IBookDao,BookDaoIF。
    <li>页面部件名建议命名为：btnOK、lblName或okBtn、nameLbl。<font color=#cc6600>(II)</font><br>其中btn、lbl缩写代表按钮(Button)、标签(Label)。
    <li>局部变量及输入参数不要与类成员变量同名(get/set方法与构造函数除外) </li>
</ul>
<h3><a name=CodingStandards-1.4%E5%A3%B0%E6%98%8E></a>1.4 声明</h3>
<ul>
    <li>修饰符应该按照如下顺序排列：public, protected, private, abstract, static, final, transient, volatile, synchronized, native, strictfp。
    <li>类与接口的声明顺序(可用Eclipse的source-&gt;sort members功能自动排列):&nbsp;
    <ol>
        <li>静态成员变量 / Static Fields
        <li>静态初始化块 / Static Initializers
        <li>成员变量 / Fields
        <li>初始化块 / Initializers
        <li>构造器 / Constructors
        <li>静态成员方法 / Static Methods
        <li>成员方法 / Methods
        <li>重载自Object的方法如toString(), hashCode() 和main方法
        <li>类型(内部类) / Types(Inner Classes) </li>
    </ol>
    </li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 同等的类型，按public, protected, private的顺序排列。</p>
<h2><a name=CodingStandards-2.%E6%B3%A8%E9%87%8A%E8%A7%84%E8%8C%83%28DocumentConvertions%29></a>2.注释规范(Document Convertions)</h2>
<h3><a name=CodingStandards-2.1%E6%B3%A8%E9%87%8A%E7%B1%BB%E5%9E%8B></a>2.1 注释类型</h3>
<h4><a name=CodingStandards-2.1.1JavaDoc%E6%B3%A8%E9%87%8A></a>2.1.1 JavaDoc注释</h4>
<p>&nbsp; 略。</p>
<h4><a name=CodingStandards-2.1.2%E5%A4%B1%E6%95%88%E4%BB%A3%E7%A0%81%E6%B3%A8%E9%87%8A></a>2.1.2 失效代码注释</h4>
<p>&nbsp; 由/*<strong>...*</strong>/界定，标准的C-Style的注释。专用于注释已失效的代码。</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>/*
&nbsp;* Comment out the code
&nbsp;* <span class=code-object>String</span> s = <span class=code-quote>"hello"</span>;
* <span class=code-object>System</span>.out.println(s);
&nbsp;*/</pre>
</div>
</div>
<h4><a name=CodingStandards-2.1.3%E4%BB%A3%E7%A0%81%E7%BB%86%E8%8A%82%E6%B3%A8%E9%87%8A></a>2.1.3 代码细节注释</h4>
<p>&nbsp; 由//界定，专用于注释代码细节，即使有多行注释也仍然使用//，以便与用/**/注释的失效代码分开</p>
<p>&nbsp; 除了私有变量外，不推荐使用行末注释。</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>class MyClass {
<span class=code-keyword>private</span> <span class=code-object>int</span> myField; <span class=code-comment>// An end-line comment.
</span>
<span class=code-keyword>public</span> void myMethod {
<span class=code-comment>//a very very <span class=code-object>long</span>
</span>       <span class=code-comment>//comment.
</span>       <span class=code-keyword>if</span> (condition1) {
<span class=code-comment>//condition1 comment
</span>          ...
} <span class=code-keyword>else</span> {
<span class=code-comment>//elses condition comment
</span>          ...
}
}
}</pre>
</div>
</div>
<h3><a name=CodingStandards-2.2%26nbsp%3B%E6%B3%A8%E9%87%8A%E7%9A%84%E6%A0%BC%E5%BC%8F></a>2.2&nbsp;注释的格式</h3>
<ul>
    <li>注释中的第一个句子要以（英文）句号、问号或者感叹号结束。Javadoc生成工具会将注释中的第一个句子放在方法汇总表和索引中。
    <li>为了在JavaDoc和IDE中能快速链接跳转到相关联的类与方法，尽量多的使用@see xxx.MyClass，@see xx.MyClass#find(String)。
    <li>Class必须以@author 作者名声明作者，不需要声明@version与@date，由版本管理系统保留此信息。<font color=#cc6600>(II)</font>
    <li>如果注释中有超过一个段落，用&lt;p&gt;分隔。<font color=#cc6600>(II)</font>
    <li>示例代码以&lt;pre&gt;&lt;/pre&gt;包裹。<font color=#cc6600>(II)</font>
    <li>标识(java keyword, class/method/field/argument名，Constants) 以&lt;code&gt;&lt;/code&gt;包裹。<font color=#cc6600>(II)</font>
    <li>标识在第一次出现时以{@linkxxx.Myclass}注解以便JavaDoc与IDE中可以链接。<font color=#cc6600>(II)</font> </li>
</ul>
<h3><a name=CodingStandards-2.3%26nbsp%3B%E6%B3%A8%E9%87%8A%E7%9A%84%E5%86%85%E5%AE%B9></a>2.3&nbsp;注释的内容</h3>
<h4><a name=CodingStandards-2.3.1%E5%8F%AF%E7%B2%BE%E7%AE%80%E7%9A%84%E6%B3%A8%E9%87%8A%E5%86%85%E5%AE%B9></a>2.3.1 可精简的注释内容</h4>
<p>&nbsp;&nbsp;&nbsp; 注释中的每一个单词都要有其不可缺少的意义，注释里不写"@param name -名字"这样的废话。<br>&nbsp;&nbsp;&nbsp; 如果该注释是废话，连同标签删掉它，而不是自动生成一堆空的标签，如空的@param name，空的@return。</p>
<h4><a name=CodingStandards-2.3.2%E6%8E%A8%E8%8D%90%E7%9A%84%E6%B3%A8%E9%87%8A%E5%86%85%E5%AE%B9></a>2.3.2 推荐的注释内容</h4>
<ul>
    <li>对于API函数如果存在契约，必须写明它的前置条件(precondition)，后置条件(postcondition)，及不变式(invariant)。<font color=#cc6600>(II)</font>
    <li>对于调用复杂的API尽量提供代码示例。<font color=#cc6600>(II)</font>
    <li>对于已知的Bug需要声明。<font color=#cc6600>(II)</font>
    <li>在本函数中抛出的unchecked exception尽量用@throws说明。<font color=#cc6600>(II)</font> </li>
</ul>
<h4><a name=CodingStandards-2.3.3Null%E8%A7%84%E7%BA%A6></a>2.3.3 Null规约</h4>
<p>&nbsp;&nbsp; 如果方法允许Null作为参数，或者允许返回值为Null，必须在JavaDoc中说明。<br>&nbsp;&nbsp;&nbsp;如果没有说明，方法的调用者不允许使用Null作为参数，并认为返回值是Null Safe的。</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>/**
&nbsp;* 获取对象.
&nbsp;*
&nbsp;* @ <span class=code-keyword>return</span> the object to found or <span class=code-keyword>null</span> <span class=code-keyword>if</span> not found.
&nbsp;*/
<span class=code-object>Object</span> get(<span class=code-object>Integer</span> id){
...
}</pre>
</div>
</div>
<h4><a name=CodingStandards-2.3.4%E7%89%B9%E6%AE%8A%E4%BB%A3%E7%A0%81%E6%B3%A8%E9%87%8A></a>2.3.4 特殊代码注释</h4>
<ul>
    <li>代码质量不好但能正常运行，或者还没有实现的代码用//TODO: 或 //XXX:声明&nbsp;
    <li>存在错误隐患的代码用//FIXME:声明 </li>
</ul>
<h2><a name=CodingStandards-3.%E7%BC%96%E7%A8%8B%E8%A7%84%E8%8C%83%28ProgrammingConventions%29></a>3.编程规范(Programming Conventions)</h2>
<h3><a name=CodingStandards-3.1%E5%9F%BA%E6%9C%AC%E8%A7%84%E8%8C%83></a>3.1基本规范</h3>
<ol>
    <li>当面对不可知的调用者时，方法需要对输入参数进行校验，如不符合抛出IllegalArgumentException，建议使用Spring的Assert系列函数。&nbsp;
    <li>隐藏工具类的构造器，确保只有static方法和变量的类不能被构造实例。
    <li>变量，参数和返回值定义尽量基于接口而不是具体实现类，如Map map = new HashMap();
    <li>代码中不能使用System.out.println()，e.printStackTrace()，必须使用logger打印信息。 </li>
</ol>
<h3><a name=CodingStandards-3.2%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86></a>3.2 异常处理</h3>
<ol>
    <li>重新抛出的异常必须保留原来的异常，即throw new NewException("message", e); 而不能写成throw new NewException("message")。
    <li>在所有异常被捕获且没有重新抛出的地方必须写日志。&nbsp;
    <li>如果属于正常异常的空异常处理块必须注释说明原因，否则不允许空的catch块。
    <li>框架尽量捕获低级异常，并封装成高级异常重新抛出，隐藏低级异常的细节。<font color=#3333ff>(III)</font> </li>
</ol>
<h3><a name=CodingStandards-3.3%E4%BB%A3%E7%A0%81%E5%BA%A6%E9%87%8F></a>3.3 代码度量</h3>
<h4><a name=CodingStandards-3.3.1%E8%80%A6%E5%90%88%E5%BA%A6%E5%BA%A6%E9%87%8F></a>3.3.1 耦合度度量</h4>
<ul>
    <li>DAC度量值不要不大于7 <font color=#3333ff>( III )</font><br>解释：DAC(Data Abstraction Coupling)数据抽象耦合度是描述对象之间的耦合度的一种代码度量。DAC度量值表示一个类中有实例化的其它类的个数。
    <li>CFO度量值不要不大于20 <font color=#3333ff>( III )</font><br>解释：CFO(Class Fan Out)类扇出是描述类之间的耦合度的一种代码度量。CFO度量值表示一个类依赖的其他类的个数。 </li>
</ul>
<h4><a name=CodingStandards-3.3.2%E6%96%B9%E6%B3%95%E5%BA%A6%E9%87%8F></a>3.3.2 方法度量</h4>
<ul>
    <li>方法（构造器）参数在5个以内 <font color=#cc6600>( II )</font><br>太多的方法（构造器）参数影响代码可读性。考虑用值对象代替这些参数或重新设计。
    <li>方法长度150行以内 <font color=#cc6600>( II )</font>
    <li>CC&nbsp;度量值不大于10<font color=#3333ff>(III )</font><br><font color=#000000>解释：CC(CyclomaticComplexity)圈复杂度指一个方法的独立路径的数量，可以用一个方法内if,while,do,for,catch,switch,case,?:语句与&amp;&amp;,||操作符的总个数来度量。</font>
    <li>NPath度量值不大于200 <font color=#3333ff>( III )</font><br>解释：NPath度量值表示一个方法内可能的执行路径的条数。 </li>
</ul>
<h4><a name=CodingStandards-3.3.3%E5%85%B6%E4%BB%96%E5%BA%A6%E9%87%8F></a>3.3.3 其他度量</h4>
<ul>
    <li>布尔表达式中的布尔运算符(&amp;&amp;,||)的个数不超过3个<font color=#3333ff>(III)</font>&nbsp;
    <li>if语句的嵌套层数3层以内<font color=#cc6600>(II)</font>
    <li>文件长度2000行以内<font color=#cc6600>(II)</font>
    <li>匿名内部类20行以内 <font color=#cc6600>( II )</font><br>太长的匿名内部类影响代码可读性，建议重构为命名的（普通）内部类。 </li>
</ul>
<h3><a name=CodingStandards-3.4JDK5.0></a>3.4 JDK5.0</h3>
<ol>
    <li>重载方法必须使用@Override，可避免父类方法改变时导致重载函数失效。
    <li>不需要关心的warning信息用@SuppressWarnings("unused"), @SuppressWarnings("unchecked"), @SuppressWarnings("serial") 注释。 </li>
</ol>
<h2><a name=CodingStandards-4.%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E6%A3%80%E6%9F%A5></a>4.自动代码检查</h2>
<p>&nbsp;&nbsp; 使用<span class=nobr><a title="Visit page outside Confluence" href="http://www.eclipse.org/" rel=nofollow><u><font color=#0000ff>Eclipse<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>与 <span class=nobr><a title="Visit page outside Confluence" href="http://www.jetbrains.com/" rel=nofollow><u><font color=#0000ff>Inellij IDEA<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>的代码校验功能已经排除了很多问题。</p>
<p>&nbsp;&nbsp; 再配合使用<span class=nobr><a title="Visit page outside Confluence" href="http://checkstyle.sf.net/" rel=nofollow><u><font color=#0000ff>Checkstyle<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>，<span class=nobr><a title="Visit page outside Confluence" href="http://pmd.sf.net/" rel=nofollow><u><font color=#0000ff>PMD<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>，<span class=nobr><a title="Visit page outside Confluence" href="http://findbugs.sf.net/" rel=nofollow><u><font color=#0000ff>FindBugs<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>三重检查，总共五层的校验涵盖了Java编码大部分的Guide Line。</p>
<p>&nbsp;&nbsp; 如果要求不苛刻，可以只使用Eclipse或IDEA 搭配 Checkstyle的两重保湿效果。</p>
<ol>
    <li><strong>Eclipse</strong>：在Windows-&gt;Preferences-&gt;Java-Compiler-&gt;Errors/Warnings中，按本文档将一些原来Ignore的规则打开。<br>也可以将springside团队预设在/tools/codereviewer/eclipse.check.prefs的内容拷贝到项目的.setting/org.eclipse.jdt.core.prefs 文件中。
    <li><strong>IDEA</strong>：在Setting-&gt;Errors中设定规则，调用Analyzer-&gt;Inspece Code进行校验。
    <li><strong>CheckStyle</strong>：安装<span class=nobr><a title="Visit page outside Confluence" href="http://eclipse-cs.sourceforge.net/" rel=nofollow><u><font color=#0000ff>CheckStyle的Eclipse插件<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>，在Windows-&gt;Preferences-&gt;CheckStyle导入springside团队预设在/tools/codereviewer/springside_check.xml的规则。
    <li><strong>PMD</strong>：安装<span class=nobr><a title="Visit page outside Confluence" href="http://pmd.sourceforge.net/eclipse/" rel=nofollow><u><font color=#0000ff>PMD的Eclipse插件<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>，Windows-&gt;Preferences-&gt;PMD清除原来所有规则，导入springside团队预设在/tools/codereviewer/springside_pmd.xml的规则。
    <li><strong>FindBugs</strong>：安装<span class=nobr><a title="Visit page outside Confluence" href="http://findbugs.sourceforge.net/manual/eclipse.html" rel=nofollow><u><font color=#0000ff>FindBugs的Eclipse插件<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>，在项目属性-&gt;FindBugs中，取消下列警告MS/EI/EI2/ ，&nbsp;SnVI/SE/WS/RS ，ST/NP/UwF/SS/UuF|UrF|SIC。 </li>
</ol>
<h2><a name=CodingStandards-5.%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99></a>5.参考资料</h2>
<ol>
    <li><span class=nobr><a title="Visit page outside Confluence" href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html" rel=nofollow><u><font color=#0000ff>Sun's Coding Conventions<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span> Sun MicroSystem；
    <li><span class=nobr><a title="Visit page outside Confluence" href="http://www.ambysoft.com/books/elementsJavaStyle.html" rel=nofollow><u><font color=#0000ff>The Elements of Java Style<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>&nbsp; Scott W. Ambler&nbsp;等著；
    <li>代码检测工具的规则： <span class=nobr><a title="Visit page outside Confluence" href="http://checkstyle.sourceforge.net/checks.html" rel=nofollow><u><font color=#0000ff>checkstyle<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>，<span class=nobr><a title="Visit page outside Confluence" href="http://pmd.sourceforge.net/rules/index.html" rel=nofollow><u><font color=#0000ff>pmd<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span> ，<span class=nobr><a title="Visit page outside Confluence" href="http://findbugs.sourceforge.net/bugDescriptions.html" rel=nofollow><u><font color=#0000ff>findbugs<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span> </li>
</ol>
<!--
<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
<rdf:description
rdf:about="http://wiki.springside.org.cn/display/springside/Coding+Standards"
dc:identifier="http://wiki.springside.org.cn/display/springside/Coding+Standards"
dc:title="Coding Standards"
trackback:ping="http://wiki.springside.org.cn/rpc/trackback/56"/>
</rdf:rdf>
--><!--
Root decorator: all decisions about how a page is to be decorated via the
inline decoration begins here.
--><!--
Switch based upon the context. However, for now, just delegate to a decorator
identified directly by the context.
-->
<img src ="http://www.blogjava.net/i369/aggbug/111607.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-04-18 14:05 <a href="http://www.blogjava.net/i369/articles/111607.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用open source产品组装你的web应用架构[转贴，只作为收藏，非本人原创] </title><link>http://www.blogjava.net/i369/articles/111363.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Tue, 17 Apr 2007 09:37:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/111363.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/111363.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/111363.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/111363.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/111363.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 其实，就算用Java建造一个不是很烦琐的web应用，也不是件轻松的事情。 在构架的一开始就有很多事情要考虑。 从高处看，摆在开发者面前有很多问题：要考虑是怎样建立用户接口？在哪里处理业务逻辑？ 怎样持久化的数据。 而这三层构架中，每一层都有他们要仔细考虑的。 各个层该使用什么技术？ 怎样的设计能松散耦合还能灵活改变？ 怎样替换某个层而不影响整体构架？应用程序如何做各种级别的业务处理（比如事务处理）...&nbsp;&nbsp;<a href='http://www.blogjava.net/i369/articles/111363.html'>阅读全文</a><img src ="http://www.blogjava.net/i369/aggbug/111363.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-04-17 17:37 <a href="http://www.blogjava.net/i369/articles/111363.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]使用open source产品组装你的web应用架构(struts+spring+hibernate) </title><link>http://www.blogjava.net/i369/articles/101116.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Wed, 28 Feb 2007 04:53:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/101116.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/101116.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/101116.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/101116.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/101116.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td width="100%">
										<p>
												<span class="postdetails">
														<font size="2">本文是</font>我在<span class="maintitle">SpringFramework中文论坛</span>上看到的一篇文章，感觉不错，所以就收藏到Blog上</span>
										</p>
										<p>
												<span class="postdetails">原文地址：<a href="http://spring.jactiongroup.net/viewtopic.php?t=389">http://spring.jactiongroup.net/viewtopic.php?t=389</a></span>
										</p>
										<p>
												<span class="postdetails">
														<font size="2">
														</font>
												</span>
										</p>
								</td>
								<td valign="top" nowrap="">
										<a href="http://spring.jactiongroup.net/posting.php?mode=quote&amp;p=1879&amp;sid=21a15e43c9d628b4ae59578406ab1cdc">
												<font size="2">
												</font>
										</a> </td>
						</tr>
						<tr>
								<td colspan="2">
										<hr />
								</td>
						</tr>
						<tr>
								<td colspan="2">
										<span class="postbody">其实，就算用Java建造一个不是很烦琐的web应用，也不是件轻松的事情。 在构架的一开始就有很多事情要考虑。 从高处看，摆在开发者面前有很多问题：要考虑是怎样建立用户接口？在哪里处理业务逻辑？ 怎样持久化的数据。 而这三层构架中，每一层都有他们要仔细考虑的。 各个层该使用什么技术？ 怎样的设计能松散耦合还能灵活改变？ 怎样替换某个层而不影响整体构架？应用程序如何做各种级别的业务处理（比如事务处理）？ <br /><br /><br /><br />构架一个Web应用需要弄明白好多问题。 幸运的是，已经有不少开发者已经遇到过这类问题，并且建立了处理这类问题的框架。 一个好框架具备以下几点： 减轻开发者处理复杂的问题的负担（“不重复发明轮子”）； 内部有良好的扩展； 并且有一个支持它的强大的用户团体。 好的构架一般有针对性的处理某一类问题，并且能将它做好（Do One Thing well）。 然而，你的程序中有几个层可能需要使用特定的框架，已经完成的UI(用户接口) 并不代表你也可以把你的业务逻辑和持久逻辑偶合到你的UI部分。 举个例子， 你不该在一个Controller(控制器)里面写JDBC代码作为你的业务逻辑， 这不是控制器应该提供的。 一个UI 控制器应该委派给其它给在UI范围之外的轻量级组件。 好的框架应该能指导代码如何分布。 更重要的是，框架能把开发者从编码中解放出来，使他们能专心于应用程序的逻辑（这对客户来说很重要）。 <br /><br /><br /><br />这篇文章将讨论怎样结合几种著名的框架来使得你的应用程序做到松弛耦合。 <br /><br />如何建立你的架构，并且怎样让你的各个应用层保持一致。？如何整合框架以便让每个层在以一种松散偶合的方式彼此作用而不用管低层的技术细节？这对我们来说真是一种挑战。 这里讨论一个整合框架的策略( 使用3 种受欢迎的开源框架) ：表示层我们用Struts； 业务层我们用Spring；而持久层则用Hibernate。 你也可以用其他FrameWork替换只要能得到同样的效果。 见图1 （框架组合示意图） <br /><br /><img alt="" src="http://www.onjava.com/onjava/2004/04/07/graphics/wiring.gif" border="0" /><br /><br /><span style="FONT-WEIGHT: bold">应用程序的分层</span><br /><br />大部分的Web应用在职责上至少能被分成4层。 这四层是：presentation（描述），persistence（持久），business（业务）和domain model（域模块）。每个层在处理程序上都应该有一项明确的责任, 而不应该在功能上与其它层混合，并且每个层要与其它层分开的，但要给他们之间放一个通信接口。 我们就从介绍各个层开始，讨论一下这些层应该提供什么，不应该提供什么。 <br /><br /><br /><br /><span style="FONT-WEIGHT: bold">表示层(The Presentation Layer) </span><br /><br />一般来讲，一个典型的Web应用的的末端应该是表示层。 很多Java发者也理解Struts所提供的。 象业务逻辑之类的被打包到org.apache.struts.Action.， 因此，我们很赞成使用Struts这样的框架。 <br /><br /><br /><br />下面是Struts所负责的： <br /><br />* 管理用户的请求,做出相应的响应。 <br /><br />* 提供一个Controller ,委派调用业务逻辑和其它上层处理。 <br /><br />* 处理异常，抛给Struts Action <br /><br />* 为显示提供一个模型 <br /><br />* UI验证。 <br /><br /><br /><br />以下条款，不该在Struts显示层的编码中经常出现。 它们与显示层无关的。 <br /><br />* 直接的与数据库通信，例如JDBC调用。 <br /><br />* 与你应用程序相关联的业务逻辑以及校验。 <br /><br />* 事物管理。 <br /><br />在表示层引入这些代码，则会带来高偶合和麻烦的维护。 <br /><br /><br /><br /><br /><br /><span style="FONT-WEIGHT: bold">持久层(The Persistence Layer)</span><br /><br />典型的Web应用的另一个末端是持久层。这里通常是程序最容易失控的地方。开发者总是低估构建他们自己的持久框架的挑战性。系统内部的持续层不但需要大量调试时间，而且还经常缺少功能使之变得难以控制，这是持久层的通病。 还好有几个ORM开源框架很好的解决了这类问题。尤其是Hibernate。 Hibernate为java提供了OR持久化机制和查询服务, 它还给已经熟悉SQL和JDBC API 的Java开发者一个学习桥梁，他们学习起来很方便。 Hibernate的持久对象是基于POJO和Java collections。此外，使用Hibernate并不妨碍你正在使用的IDE。 <br /><br /><br /><br />请看下面的条目，你在持久层编码中需要了解的。 <br /><br />* 查询对象的相关信息的语句。 Hibernate通过一个OO查询语言（HQL）或者正则表达的API来完成查询。 HQL非常类似于SQL-- 只是把SQL里的table和columns用Object和它的fields代替。 你需要学习一些新的HQL语言； 不管怎样，他们容易理解而文档也做的很好。 HQL是一种对象查询的自然语言，花很小的代价就能学习它。 <br /><br />* 如何存储，更新，删除数据库记录。 <br /><br />* 象Hibernate这类的高级ORM框架支持大部分主流数据库，并且他们支持 Parent/child关系，事物处理，继承和多态。 <br /><br /><br /><br /><span style="FONT-WEIGHT: bold">业务层（The Business Layer）</span><br /><br />一个典型Web应用的中间部分是业务层或者服务层。 从编码的视角来看，这层是最容易被忽视的一层。 而我们却往往在UI层或持久层周围看到这些业务处理的代码，这其实是不正确的，因为它导致了程序代码的紧密偶合，这样一来，随着时间推移这些代码很难维护。幸好，针对这一问题有好几种Frameworks存在。 最受欢迎的两个框架是Spring和PicoContainer。 这些为也被称为microcontainers，他们能让你很好的把对象搭配起来。 这两个框架都着手于‘依赖注射’(dependency injection)(还有我们知道的‘控制反转’Inversion of Control=IoC)这样的简单概念。 这篇文章将关注于Spring的注射（译注：通过一个给定参数的Setter方法来构造Bean,有所不同于Factory）, Spring还提供了Setter Injection(type2)，Constructor Injection(type3)等方式供我们选择。 Spring把程序中所涉及到包含业务逻辑和Dao的Objects——例如transaction management handler（事物管理控制）、Object Factoris(对象工厂)、service objects（服务组件）——都通过XML来配置联系起来。 <br /><br /><br /><br />后面我们会举个例子来揭示一下Spring 是怎样运用这些概念。 <br /><br />业务层所负责的如下： <br /><br />* 处理应用程序的 业务逻辑和业务校验 <br /><br />* 管理事物 <br /><br />* 允许与其它层相互作用的接口 <br /><br />* 管理业务层级别的对象的依赖。 <br /><br />* 在显示层和持久层之间增加了一个灵活的机制，使得他们不直接的联系在一起。 <br /><br />* 通过揭示 从显示层到业务层之间的Context来得到business services。 <br /><br />* 管理程序的执行（从业务层到持久层）。 <br /><br /><br /><br /><br /><br /><span style="FONT-WEIGHT: bold">域模块层（The Domain Model Layer ）</span><br />既然我们致力于的是一个不是很复杂的Web的应用， 我们需要一个对象集合，让它在不同层之间移动的。 域模块层由实际需求中的业务对象组成 比如, OrderLineItem , Product等等。 开发者在这层 不用管那些DTOs，仅关注domain object即可。 例如，Hibernate允许你将数据库中的信息存放入对象（domain objects），这样你可以在连接断开的情况下把这些数据显示到UI层。 而那些对象也可以返回给持续层，从而在数据库里更新。 而且，你不必把对象转化成DTOs（这可能似的它在不同层之间的在传输过程中丢失），这个模型使得Java开发者能很自然运用OO，而不需要附加的编码。 <br /><br />一个简单例子 <br /><br /><br /><br />既然我们已经从全局上理解这些组件。 现在就让我们开始实践吧。 我们还是用 Struts，Spring 和Hibernate。这三个框架已经被描述够多了，这里就不重复介绍了。 这篇文章举例指导你如何使用这三个框架整合开发, 并向你揭示 一个请求是如何贯穿于各个层的。（从用户的加入一个Order到数据库，显示；进而更新、删除）。 <br /><br /><br /><br />从这里可以下载到程序程序原代码（<a class="postlink" href="http://www.onjava.com/onjava/2004/04/07/examples/wiring.zip" target="_blank"><font color="#5493b4">download</font></a>） <br /><br />既然每个层是互相作用的，我们就先来创建domain objects。首先，我们要在这些Object中要确定那些是需要持久化的，哪些是提供给business logic，那些是显示接口的设计。 下一步，我们将配置我们的持久层并且定义好Hibernate的OR mappings。然后定义好Business Objects。有了这些组成部分之后，我们将 使用Spring把这些连接起来。 最后，我们提供给Spring一个持久层，从这个持久层里我们可以知道它是如何与业务逻辑层（business service layer）通信的，以及它是怎样处理其他层抛出的异常的。。 <br /><br /><br /><br /><span style="FONT-WEIGHT: bold">域对象层（Domain Object Layer）</span><br /><br /><br />这层是编码的着手点，我们的编码就从这层开始。 例子中Order 与OrderItem 是一个One—To—Many的关系。 下面就是Domain Object Layer的两个对象： <br /><br /><br /><br />· com.meagle.bo.Order.java: 包含了一个Order的概要信息 <br /><br />· com.meagle.bo.OrderLineItem.java: 包含了Order的详细信息 <br /><br />好好考虑怎你的package命名,这反应出了你是怎样分层的。 例如 domain objects在程序中可能打包在com.meagle.bo内。 更详细一点将打包在com. meagle.bo的子目录下面。business logic应该从com.meagle.serice开始打包，而DAO 对象应该位于com.meagle.service.dao.hibernate。反应Forms和Actions的 持久对象（presentation classes） 应该分别放在 com.meagle.action和com.meagle.forms包。 准确的给包命名使得你的classes很好分割并且易于维护，并且在你添加新的classes时，能使得程序结构上保持上下一致。 <br /><br /><span style="FONT-WEIGHT: bold">持久层的配置（Persistence Layer Configuration）</span><br /><br />建立Hibernate的持久层 需要好几个步骤。 第一步让我们把BO持久化。 既然Hibernate是通过POJO工作的， 因此Order和 OrderLineItem对象需要给所有的fileds 加上getter,setter方法。 Hibernate通过XML文件来映射(OR)对象，以下两个xml文件分别映射了Order 和OrderItem对象。（这里有个叫XDoclet工具可以自动生成你的XML影射文件） <br /><br />- Order.hbm.xml <br />- OrderLineItem.hbm.xml <br /><br />你可以在WebContent/WEB-INF/classes/com/meagle/bo目录下找到这些xml文件。Hibernate的 [urlhttp://www.hibernate.org/hib_docs/api/net/sf/hibernate/SessionFactory.html]SessionFactory [/url]是用来告诉程序 应该与哪个数据库通信，该使用哪个连接池或使用了DataSource， 应该加载哪些持久对象。而<a class="postlink" href="http://www.hibernate.org/hib_docs/api/net/sf/hibernate/Session.html" target="_blank"><font color="#006699">Session</font></a>接口是用来完成Selecting，Saving，Delete和Updating这些操作。 后面的我们将讲述SessionFactory和Session是怎样设置的。 <br /><br /><span style="FONT-WEIGHT: bold">业务层的配置（Business Layer Configuration）</span><br /><br />既然我们已经有了domain objects，接下来我们就要business service objects了，用他们来执行程序的logic,调用持久层，得到UI层的requests,处理transactions，并且控制exceptions。 为了将这些连接起来并且易于管理，我们将使用面向方面的 SpringFramework。 Spring 提供了 控制倒置（inversion of control 0==IoC)和注射依赖设置（setter dependency injection）这些方式（可供选择），用XML文件将对象连接起来。 IoC是一个简单概念（它允许一个对象在上层接受其他对象的创建），用IoC这种方式让你的对象从创建中释放了出来，降低了偶合度。 <br /><br /><br /><br /><br />这里是一个没有使用IoC的对象创建的例子，它有很高偶合度。 <br /><br /><br /><img alt="" src="http://www.onjava.com/onjava/2004/04/07/graphics/nonioc.gif" border="0" /><br /><br /><span style="FONT-STYLE: italic">图 2.没有使用 IoC. A 创建了 B 和 C</span><br /><br />而这里是一个使用IoC的例子，这种方式允许对象在高层可以创建并进入另外一个对象，所以这样可以直接被执行。 <br /><img alt="" src="http://www.onjava.com/onjava/2004/04/07/graphics/ioc.gif" border="0" /><br /><br /><span style="FONT-STYLE: italic">图 3. 对象使用了 IoC。 A 包含了接受B,C的 setter方法 , 这同样达到了 由A创建B,C的目的。</span><br /><br />建立我们的业务服务对象（Building Our Business Service Objects） <br /><br /><br />Business Object中的Setter方法接受的是接口，这样我们可以很松散的定义对象实现，然后注入。 在我们的案例中，我们将用一个business service object接收一个DAO,用它来控制domain objects的持久化。 由于在这个例子中使用了Hibernate，我们可以很方便的用其他持久框架实现 同时通知Spring 有新的DAO可以使用了。 <br /><br />在面向接口的编程中，你会明白 “注射依赖”模式是怎样松散耦合你的业务逻辑和持久机制的：）。 <br /><br /><br /><br />下面是一个接口business service object，DAO代码片段： <br /><br /><br /></span>
										<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
												<tbody>
														<tr>
																<td>
																		<span class="genmed">
																				<b>代码:</b>
																		</span>
																</td>
														</tr>
														<tr>
																<td class="code">
																		<br />public interface IOrderService { <br /><br />  public abstract Order saveNewOrder(Order order) <br /><br />    throws OrderException, <br /><br />           OrderMinimumAmountException; <br /><br />  <br /><br />  public abstract List findOrderByUser( <br /><br />                                     String user) <br /><br />                           throws OrderException; <br /><br />  <br /><br />  public abstract Order findOrderById(int id) <br /><br />                           throws OrderException; <br /><br />  <br /><br />  public abstract void setOrderDAO( <br /><br />                             IOrderDAO orderDAO); <br /><br />} <br /><br /> </td>
														</tr>
												</tbody>
										</table>
										<span class="postbody">
												<br />注意到这段代码里有一个 setOrderDao（），它就是一个DAO Object设置方法（注射器）。 但这里并没有一个getOrderDao的方法，这不必要，因为你并不会在外部访问这个orderDao。这个DAO Objecte将被调用，和我们的persistence layer 通信。我们将用Spring把DAO Object 和 business service object搭配起来的。因为我们是面向接口编程的，所以并不需要将实现类紧密的耦合在一起。 <br /><br /><br /><br />接下去我们开始我们的DAO的实现类进行编码。 既然Spring已经有对Hibernate的支持，那这个例子就直接继承<a class="postlink" href="http://www.springframework.org/docs/api/org/springframework/orm/hibernate/support/HibernateDaoSupport.html" target="_blank"><font color="#006699">HibernateDaoSupport</font></a>类了，这个类很有用，我们可以参考<a class="postlink" href="http://www.springframework.org/docs/api/org/springframework/orm/hibernate/HibernateTemplate.html" target="_blank"><font color="#006699">HibernateTemplate</font></a>（它主要是针对HibernateDaoSupport的一个用法，译注：具体可以查看<a class="postlink" href="http://www.springframework.org/docs/api/index.html" target="_blank"><font color="#006699">Srping 的API</font></a>）。 下面是这个DAO接口代码： <br /><br /></span>
										<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
												<tbody>
														<tr>
																<td>
																		<span class="genmed">
																				<b>代码:</b>
																		</span>
																</td>
														</tr>
														<tr>
																<td class="code">public interface IOrderDAO { <br />  public abstract Order findOrderById( <br />                                    final int id); <br />  <br />  public abstract List findOrdersPlaceByUser( <br />                           final String placedBy); <br />  public abstract Order saveOrder( <br />                               final Order order); <br />}</td>
														</tr>
												</tbody>
										</table>
										<span class="postbody">
												<br />
												<br />我们仍然要给我们持久层组装很多关联的对象，这里包含了HibernateSessionFactory 和TransactionManager。 Spring 提供了一个 <a class="postlink" href="http://www.springframework.org/docs/api/org/springframework/orm/hibernate/HibernateTransactionManager.html" target="_blank"><font color="#006699">HibernateTransactionManager</font></a>，他用线程捆绑了一个Hibernate Session，用它来支持transactions(请查看<a class="postlink" href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ThreadLocal.html" target="_blank"><font color="#006699">ThreadLocal</font></a>) 。 <br /><br />下面是HibernateSessionFactory 和 HibernateTransactionManager:的配置： <br /><br /></span>
										<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
												<tbody>
														<tr>
																<td>
																		<span class="genmed">
																				<b>代码:</b>
																		</span>
																</td>
														</tr>
														<tr>
																<td class="code">&lt;bean id="mySessionFactory" <br />       class="org.springframework.orm.hibernate. <br />              LocalSessionFactoryBean"&gt; <br />  &lt;property name="mappingResources"&gt; <br />    &lt;list&gt; <br />      &lt;value&gt; <br />        com/meagle/bo/Order.hbm.xml <br />      &lt;/value&gt; <br />      &lt;value&gt; <br />        com/meagle/bo/OrderLineItem.hbm.xml <br />      &lt;/value&gt; <br />    &lt;/list&gt; <br />  &lt;/property&gt; <br />  &lt;property name="hibernateProperties"&gt; <br />    &lt;props&gt; <br />      &lt;prop key="hibernate.dialect"&gt; <br />        net.sf.hibernate.dialect.MySQLDialect <br />      &lt;/prop&gt; <br />      &lt;prop key="hibernate.show_sql"&gt; <br />        false <br />      &lt;/prop&gt; <br />      &lt;prop key="hibernate.proxool.xml"&gt; <br />        C:/MyWebApps/.../WEB-INF/proxool.xml <br />      &lt;/prop&gt; <br />      &lt;prop key="hibernate.proxool.pool_alias"&gt; <br />          spring <br />      &lt;/prop&gt; <br />    &lt;/props&gt; <br />  &lt;/property&gt; <br />&lt;/bean&gt; <br />  <br />&lt;!-- Transaction manager for a single Hibernate <br />SessionFactory (alternative to JTA) --&gt; <br />&lt;bean id="myTransactionManager" <br />         class="org. <br />                springframework. <br />                orm. <br />                hibernate. <br />                HibernateTransactionManager"&gt; <br />  &lt;property name="sessionFactory"&gt; <br />    &lt;ref local="mySessionFactory"/&gt; <br />  &lt;/property&gt; <br />  &lt;/bean&gt;</td>
														</tr>
												</tbody>
										</table>
										<span class="postbody">
												<br />
												<br />
												<br />可以看出：每个对象都可以在Spring 配置信息中用&lt;bean&gt;标签引用。在这里，mySessionFactory引用了HibernateSessionFactory，而myTransactionManager引用了HibernateTransactionManage。 注意代码中myTransactionManger Bean有个sessionFactory属性。 HibernateTransactionManager有个sessionFactory setter 和 getter方法，这是用来在Spring启动的时候实现“依赖注入” （dependency injection）的。 在sessionFactory 属性里 引用mySessionFactory。这两个对象在Spring容器初始化后就被组装了起来了。 这样的搭配让你从 单例（singleton objects）和工厂（factories）中解放了出来，降低了代码的维护代价。 mySessionFactory.的两个属性，分别是用来注入mappingResources 和 hibernatePropertes的。通常，如果你在Spring之外使用Hibernate,这样的设置应该放在hibernate.cfg.xml中的。 不管怎样,Spring提供了一个便捷的方式-----在Spring内部配置中并入了Hibernate的配置。 如果要得到更多的信息，可以查阅Spring API。 <br /><br /><br /><br /><br /><br />既然我们已经组装配置好了Service Beans，就需要把Business Service Object和 DAO也组装起来，并把这些对象配到一个事务管理器（transaction manager）里。 <br /><br /><br /><br />在Spring中的配置信息： <br /></span>
										<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
												<tbody>
														<tr>
																<td>
																		<span class="genmed">
																				<b>代码:</b>
																		</span>
																</td>
														</tr>
														<tr>
																<td class="code">
																		<br />&lt;!-- ORDER SERVICE --&gt; <br />&lt;bean id="orderService" <br />  class="org. <br />         springframework. <br />         transaction. <br />         interceptor. <br />         TransactionProxyFactoryBean"&gt; <br />  &lt;property name="transactionManager"&gt; <br />    &lt;ref local="myTransactionManager"/&gt; <br />  &lt;/property&gt; <br />  &lt;property name="target"&gt; <br />    &lt;ref local="orderTarget"/&gt; <br />  &lt;/property&gt; <br />  &lt;property name="transactionAttributes"&gt; <br />    &lt;props&gt; <br />      &lt;prop key="find*"&gt; <br />     PROPAGATION_REQUIRED,readOnly,-OrderException <br />      &lt;/prop&gt; <br />      &lt;prop key="save*"&gt; <br />     PROPAGATION_REQUIRED,-OrderException <br />      &lt;/prop&gt; <br />    &lt;/props&gt; <br />  &lt;/property&gt; <br />&lt;/bean&gt; <br />  <br />&lt;!-- ORDER TARGET PRIMARY BUSINESS OBJECT: <br />Hibernate implementation --&gt; <br />&lt;bean id="orderTarget" <br />         class="com. <br />                meagle. <br />                service. <br />                spring. <br />                OrderServiceSpringImpl"&gt; <br />  &lt;property name="orderDAO"&gt; <br />    &lt;ref local="orderDAO"/&gt; <br />  &lt;/property&gt; <br />&lt;/bean&gt; <br />  <br />&lt;!-- ORDER DAO OBJECT --&gt; <br />&lt;bean id="orderDAO" <br />         class="com. <br />                meagle. <br />                service. <br />                dao. <br />                hibernate. <br />                OrderHibernateDAO"&gt; <br />  &lt;property name="sessionFactory"&gt; <br />    &lt;ref local="mySessionFactory"/&gt; <br />  &lt;/property&gt; <br />&lt;/bean&gt;</td>
														</tr>
												</tbody>
										</table>
										<span class="postbody">
												<br />
												<br />
												<br />
												<br />图4 是我们对象搭建的一个提纲。 从中可以看出，每个对象都联系着Spring，并且能通过Spring注入到其他对象。把它与Spring的配置文件比较，观察他们之间的关系 <br /><br /><img alt="" src="http://www.onjava.com/onjava/2004/04/07/graphics/spring_wiring.gif" border="0" /><br /><br /><span style="FONT-STYLE: italic">图 4. Spring就是这样基于配置文件，将各个Bean搭建在一起。</span><br /><br />这个例子使用一个TransactionProxyFactoryBean，它定义了一个setTransactionManager()。 这对象很有用，他能很方便的处理你申明的事物还有Service Object。 你可以通过transactionAttributes属性来定义怎样处理。 想知道更多还是参考TransactionAttributeEditor吧。 <br /><br />TransactionProxyFactoryBean 还有个setter. 这会被我们 Business service object（orderTarget）引用， orderTarget定义了 业务服务层，并且它还有个属性，由setOrderDAO()引用。这个属性 <br /><br /><br /><br />Spring 和Bean 的还有一点要注意的： bean可以以用两种方式创造。 这些都在单例模式（Sington）和原型模式（propotype）中定义了。 默认的方式是singleton,这意味着共享的实例将被束缚。 而原形模式是在Spring用到bean的时候允许新建实例的。当每个用户需要得到他们自己Bean的Copy时，你应该仅使用prototype模式。（更多的请参考设计模式中的单例模式和原形模式） <br /><br /><span style="FONT-WEIGHT: bold">提供一个服务定位器（Providing a Service Locator）</span><br />既然我们已经将我们的Serices和DAO搭配起来了。我们需要把我们的Service显示到其他层。 这个通常是在Struts或者Swing这层里编码。一个简单方法就是用 服务定位器返回给Spring context 。当然，可以通过直接调用Spring中的Bean来做。 <br /><br />下面是一个Struts Actin 中的服务定位器的一个例子。 <br /></span>
										<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
												<tbody>
														<tr>
																<td>
																		<span class="genmed">
																				<b>代码:</b>
																		</span>
																</td>
														</tr>
														<tr>
																<td class="code">
																		<br />public abstract class BaseAction extends Action { <br />  <br />  private IOrderService orderService; <br />  <br />  public void setServlet(ActionServlet <br />                                 actionServlet) { <br />    super.setServlet(actionServlet); <br />    ServletContext servletContext = <br />               actionServlet.getServletContext(); <br />  <br />    WebApplicationContext wac = <br />      WebApplicationContextUtils. <br />         getRequiredWebApplicationContext( <br />                                 servletContext); <br />  <br />      this.orderService = (IOrderService) <br />                     wac.getBean("orderService"); <br />  } <br />  <br />  protected IOrderService getOrderService() { <br />    return orderService; <br />  } <br />} <br /> </td>
														</tr>
												</tbody>
										</table>
										<span class="postbody">
												<br />
												<span style="FONT-WEIGHT: bold">UI 层配置 （UI Layer Configuration）</span>
												<br />
												<br />这个例子里UI层 使用了Struts framework. 这里我们要讲述一下在给程序分层的时候， 哪些是和Struts部分的。我们就从一个Struts-config.xml文件中的Action的配置信息开始吧。 <br /></span>
										<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
												<tbody>
														<tr>
																<td>
																		<span class="genmed">
																				<b>代码:</b>
																		</span>
																</td>
														</tr>
														<tr>
																<td class="code">
																		<br />struts-config.xml file. <br /><br />&lt;action path="/SaveNewOrder" <br />    type="com.meagle.action.SaveOrderAction" <br />    name="OrderForm" <br />    scope="request" <br />    validate="true" <br />    input="/NewOrder.jsp"&gt; <br />  &lt;display-name&gt;Save New Order&lt;/display-name&gt; <br />  &lt;exception key="error.order.save" <br />    path="/NewOrder.jsp" <br />    scope="request" <br />    type="com.meagle.exception.OrderException"/&gt; <br />  &lt;exception key="error.order.not.enough.money" <br />    path="/NewOrder.jsp" <br />    scope="request" <br />    type="com. <br />          meagle. <br />          exception. <br />          OrderMinimumAmountException"/&gt; <br />  &lt;forward name="success" path="/ViewOrder.jsp"/&gt; <br />  &lt;forward name="failure" path="/NewOrder.jsp"/&gt; <br />&lt;/action&gt;</td>
														</tr>
												</tbody>
										</table>
										<span class="postbody">
												<br />SaveNewOrder 这个Action是用来持久化UI层里的表单提交过来Order的。这是Struts中一个很典型的Action; 注意观察这个Action中exception配置，这些Exceptions也在Spring 配置文件(applicationContext-hibernate.xml)中配置了（就在 business service object 的transactionAttributes属性里）。 当异常在业务层被被抛出时，我们可以控制他们，并适当的显示给UI层。 <br /><br />第一个异常，OrderException,在持久层保存order对象失败的时候被触发。这将导致事物回滚并且通过BO把异常回传到Struts这一层。 <br /><br />第二个异常，OrderMinimumAmountException也同第一个一样。 <br /><br /><br /><br /><br /><br />搭配整和的最后一步 通过是让你显示层和业务层相结合。这个已经被服务定位器（service locator）实现了（前面讨论过了）， 这里服务层作为一个接口提供给我们的业务逻辑和持久层。 <br /><br /><br /><br />SaveNewOrder Action 在Struts中用一个服务定位器（service locator）来调用执行业务方法的。 方法代码如下： <br /><br /><br /><br /></span>
										<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
												<tbody>
														<tr>
																<td>
																		<span class="genmed">
																				<b>代码:</b>
																		</span>
																</td>
														</tr>
														<tr>
																<td class="code">public ActionForward execute( <br /><br />  ActionMapping mapping, <br /><br />  ActionForm form, <br /><br />  javax.servlet.http.HttpServletRequest request, <br /><br />  javax.servlet.http.HttpServletResponse response) <br /><br />  throws java.lang.Exception { <br /><br />  <br /><br />  OrderForm oForm = (OrderForm) form; <br /><br />  <br /><br />  // Use the form to build an Order object that <br /><br />  // can be saved in the persistence layer. <br /><br />  // See the full source code in the sample app. <br /><br />  <br /><br />  // Obtain the wired business service object <br /><br />  // from the service locator configuration <br /><br />  // in BaseAction. <br /><br />  // Delegate the save to the service layer and <br /><br />  // further upstream to save the Order object. <br /><br />  getOrderService().saveNewOrder(order); <br /><br />  <br /><br />  oForm.setOrder(order); <br /><br />  <br /><br />  ActionMessages messages = new ActionMessages(); <br /><br />  messages.add( <br /><br />      ActionMessages.GLOBAL_MESSAGE, <br /><br />            new ActionMessage( <br /><br />      "message.order.saved.successfully")); <br /><br />  <br /><br />  saveMessages(request, messages); <br /><br />  <br /><br />  return mapping.findForward("success"); <br /><br />}</td>
														</tr>
												</tbody>
										</table>
										<span class="postbody">
												<br />
												<br />总结 <br /><br />这篇文章在技术和构架方面掩盖了很多低层的基础信息， 文章的主要的意图在于让你意识到如何给你应用程序分层。 分层可以“解耦”你的代码——允许新的组件被添加进来，而且让你的代码易于维护。 这里用到的技术只是专注于把“解偶”做好。 不管怎样，使用这样的构架可以让你用其他技术代替现在的层。 例如，你可能不使用Hibernate实现持久化。既然你在DAO中面向接口的编程的，所以你完全可以用iBATIS来代替。或者，你也可能想用Struts外的其他的技术或者框架替换现在的UI层（转换久层，实现层并不应该直接影响到你的业务逻辑和业务服务层）。 用适当的框架搭建你的Web应用，其实也不是一件烦琐的工作，更主要的是它“解耦”了你程序中的各个层。 <br /><br /><br /><br /><br /><br />后记： <br /><br />看完这篇文章后，只是觉得很喜欢，于是就翻译了，当然同时也准备着挨大家扔来的鸡蛋：）。 <br /><br />这篇文章里并没有太多的技术细节，和详细的步骤。如果你从未使用过这些框架而在运行实例程序遇上困难的话，可以到CSDN论坛Java Open Source版发贴，我一定会详细解答的（啊哦，这不算做广告吧？）， <br /><br />文章是从一个构架的角度讲述了如何搭配现有的开源框架进行分层， 有太多的术语我都不知道怎么表达，而且可能有很多语句存在错误。如果影响了你的阅读，请你直接点原文地址，我同时也象你说声抱歉。 <br /><br /><br /><br />作者简介：Mark Eagle 高级软件工程师，亚特兰大。 <br />翻 译：Totodo,软件工程师 <br /><br /><br /><br /><br /><br />参考： <br /><br />Struts：http://jakarta.apache.org/struts/index.html <br /><br />Spring: <a href="http://www.springframework.org/" target="_blank"><font color="#006699">http://www.springframework.org</font></a><br /><br />Hibernate: <a href="http://www.hibernate.org/" target="_blank"><font color="#006699">http://www.hibernate.org</font></a><br /><br /><a href="http://www.hibernate.org.cn/" target="_blank"><font color="#006699">http://www.hibernate.org.cn</font></a><br /><br />关于控制反转IOC和依赖注射：http://www.martinfowler.com/articles/injection.html <br /><br />原文: <br /><br /><a href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=1" target="_blank"><font color="#006699">http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=1</font></a><br /><a href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=2" target="_blank"><font color="#006699">http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=2</font></a><br /><a href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=3" target="_blank"><font color="#006699">http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=3</font></a><br /><a href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=4" target="_blank"><font color="#006699">http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=4</font></a></span>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/i369/aggbug/101116.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2007-02-28 12:53 <a href="http://www.blogjava.net/i369/articles/101116.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring XML配置十二个最佳实践(转自网络)</title><link>http://www.blogjava.net/i369/articles/89554.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Fri, 22 Dec 2006 08:16:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/89554.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/89554.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/89554.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/89554.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/89554.html</trackback:ping><description><![CDATA[
		<font size="2">在这篇文章里，对于Spring XML的配置，我将向你展示12种比较好的实践。其中的一些实践不仅是好的实践，更是必要的实践。除此以外，还有其他因素，例如领域模型的设计，都能影响XML的配置，但是这篇文章重点研究XML配置的易读性和易管理性。 <br /><br />　　1。不要使用autowiring <br /><br />　　Spring可以通过类的自省来自动绑定其依赖部分，使得你不必明确指明bean的属性和构造器。Bean的属性可以通过属性名称或类型匹配来实现自动绑定。构造器通过类型匹配来实现自动绑定。你甚至可以指定自动检测自动绑定模式，它可以引导Spring选择一种适当的运行机制。先来看看下面的一个例子： <br /><br />&lt; bean id ="orderService" <br />class ="com.lizjason.spring.OrderService" <br />autowire ="byName" /&gt; 　 <br />　　 OrderService类的属性名在容器中用于匹配bean实例。自动绑定可以潜在地节省一些打字和减少一些混乱。但是在现实世界的工程里你不应该使用这种方式，这是因为它牺牲了配置的清晰性和可维护性。许多指南和介绍中大量吹捧自动绑定是Spring的一种极好的特征而没有提到这一特性所带来的牺牲。依我的观点，这就像Spring中的object－pooling，它更像是一种为了占据更多市场的商业特征。它对于XML配置文件的小巧化是一个好办法，但实际上也增加了复杂程度，尤其当你运行有大量类声明的工程时。虽然Spring允许你混合自动绑定和手动绑定，但是这个矛盾会使XML配置更加晦涩难懂。 <br /><br />　　2.使用通俗的命名 <br /><br />　　这个方式对于Java编码也一样适用。在工程中使用清晰的、描述性的、协调的通俗名称对于开发者理解XML配置是十分有益的。例如对于bean ID，你可以根据通俗的Java类名来命名它。对于例子中OrderServiceDAO的bean ID命名为orderServiceDAO。对于大的工程，你可以在bean ID前面加上包名作为前缀。 <br /><br />　　3. 使用简洁的形式 <br /><br />　　简洁形式避免了冗长，是因为它从子元素中将属性值和参考写到属性中。例如下面的例子： <br /><br />&lt; bean id ="orderService" class ="com.lizjason.spring.OrderService" &gt; <br />&lt; property name ="companyName" &gt; <br />&lt; value &gt; lizjason &lt;/ value &gt; <br />&lt;/ property &gt; <br />&lt; constructor-arg &gt; <br />&lt; ref bean ="orderDAO" &gt; <br />&lt;/ constructor-arg &gt; <br />&lt;/ bean &gt; <br />　　可以使用简洁形式将上述代码重写为： <br /><br /><br />&lt; bean id ="orderService" class ="com.lizjason.spring.OrderService" &gt; <br />&lt; property name ="companyName" value ="lizjason" /&gt; <br />&lt; constructor-arg ref ="orderDAO" /&gt; <br />&lt;/ bean &gt; <br />简洁形式功能在1.2版本中可以使用。对于&lt;ref local="..."&gt;没有简洁形式。简洁形式不但可以节约你的打字，而且可以使XML配置文件清晰。它最引人注目的是当在一个配置文件中有大量定义的类时可以提高易读性。 <br /><br />　　4. 对于构造器参数匹配，类型名比序号好。 <br /><br />　　当一个构造器含有一个以上的同种类型的参数，或者属性值的标签已经被占用时，Spring允许你使用从0计数的序号来解决这些会带来混淆的问题。例如： <br /><br />&lt; bean id ="billingService" class ="com.lizjason.spring.BillingService" &gt; <br />&lt; constructor-arg index ="0" value ="lizjason" /&gt; <br />&lt; constructor-arg index ="1" value ="100" /&gt; <br />&lt;/ bean &gt; <br />　　像下面这样，利用类型属性来编写会更好一些： <br /><br />&lt; bean id ="billingService" class ="com.lizjason.spring.BillingService" &gt; <br />&lt; constructor-arg type ="java.lang.String" value ="lizjason" /&gt; <br />&lt; constructor-arg type ="int" value ="100" /&gt; <br />&lt;/ bean &gt; <br />　　使用索引可以稍稍减少一些冗长，但是和使用类型属性相比，它还是有容易发生错误的倾向和难于阅读的缺点。你应该只在构造器参数不明确的时候，才使用索引这一方法。 <br /><br />　　5. 尽可能重用已定义过的bean <br /><br />　　Spring提供一种类似继承一样的机制来减少配置信息的复制并简化XML配置。定义一个子类可以从它父类那里继承配置信息，而父类实质上作为子类的一个模板。这就是大工程中所谓的重用。你所需要做的就是在父类bean中设置abstract=true，然后在子bean注明它自己的父类bean。例如： <br /><br />&lt; bean id ="abstractService" abstract ="true" class ="com.lizjason.spring.AbstractService" &gt; <br />&lt; property name ="companyName" value ="lizjason" /&gt; <br />&lt;/ bean &gt; <br /><br />&lt; bean id ="shippingService" parent ="abstractService" class ="com.lizjason.spring.ShippingService" &gt; <br />&lt; property name ="shippedBy" value ="lizjason" /&gt; <br />&lt;/ bean &gt; <br />　　ShippingService类从abstractService类那里继承companyName属性的值——lizjason。如果你没有为一个bean指明类或factory方法，那么这个bean便是抽象的。 <br /><br />　　6. 尽量使用ApplicationContext来装配定义的bean <br /><br />　　像在Ant脚本中的引用一样，Spring的引用对于装配模块化的bean来说是很有用的。例如： <br /><br /><br />&lt; beans &gt; <br />&lt; import resource ="billingServices.xml" /&gt; <br />&lt; import resource ="shippingServices.xml" /&gt; <br />&lt; bean id ="orderService" class ="com.lizjason.spring.OrderService" /&gt; <br />&lt;/ beans &gt; <br />　　相对于使用import在XML配置中来预装配，通过ApplicationContext来配置这些beans，显得更加灵活。利用ApplicationContext也使得XML配置易于管理。你可以像下面的例子那样在ApplictionContext构造器里布置bean： <br /><br />String[] serviceResources = { " orderServices.xml " , " billingServices.xml " , " shippingServices.xml " } ; <br />ApplicationContext orderServiceContext = new ClassPathXmlApplicationContext(serviceResources); 　 <br /><br />　　7. 利用id作为bean的标识符 <br /><br />　　你可以指定一个id或名称来作为bean的标识符。虽然使用id不会提高易读性，但是它可以让XML parser对bean的引用有效方面进行更好的验证。如果由于XML IDREF的限制而不能使用某个id，你可以利用names来作为bean的标识符。XML IDREF的限制是id必须以字母开头（或者在XML规范中定义的标点符号），后面接着字母，数字，连字号，下划线，冒号等。实际上，遇到XML IDREF限制的问题是很少见的。 <br /><br />　　8. 在开发阶段使用依赖检验 <br /><br />　　你可以在bean中给依赖检验的属性设置值，而不采用原先默认的空值，属性设置例如simple，object或all，以便容器进行依赖检验。当bean的全部的属性（或某类属性）需要被明确设置或自动绑定时，依赖检验便显得很有用。 <br /><br />&lt; bean id ="orderService" class ="com.lizjason.spring.OrderService" dependency-check ="objects" &gt; <br />&lt; property name ="companyName" value ="lizjason" /&gt; <br />&lt; constructor-arg ref ="orderDAO" /&gt; <br />&lt;/ bean &gt; <br />　　在这个例子里，容器确保为orderService bean设置的属性不是primitives 或者 collections。为所有的bean设置默认依赖检测也是可以的，但是我们很少这样做，是因为有些bean的属性根本就不必设置。 <br /><br />　　9. 为每个配置文件加上一个header comment <br /><br />　　最好使用descriptive id和名称来代替在XML配置文件中的注释。此外，加上一个配置文件header也很有用处，它可以概述文件中所定义的bean。你可以选择将描述内容加入description标签中。例如： <br /><br />&lt; beans &gt; <br />&lt; description &gt; <br />This file defines billing service <br />related beans and it depends on <br />baseServices.xml,which provides <br />service bean templates <br />&lt;/ description &gt; <br /><br />&lt;/ beans &gt; <br />　　使用description标签的一个好处是可以容易地利用工具从标签中选取出description（的内容）。 <br /><br />　　10. 对于任何变化，要与队友积极交流 <br /><br />　　当你重构Java代码时，你需要随时更新配置文件并且通知队友。XML配置文件也是代码，它们是应用程序的至关重要的部分，但是它们难于阅读和维护。大部分时间你既要阅读XML配置文件又要阅读运行中的Java代码。 <br /><br />　　11. Setter injection优于constructor injection <br /><br />　　Spring提供3种类型的依赖注入： constructor injection,setter injection, 和method injection。我们一般只用前两种类型。 <br /><br />&lt; bean id ="orderService" class ="com.lizjason.spring.OrderService" &gt; <br />&lt; constructor-arg ref ="orderDAO" /&gt; <br />&lt;/ bean &gt; <br /><br />&lt; bean id ="billingService" class ="com.lizjason.spring.BillingService" &gt; <br />&lt; property name ="billingDAO" ref ="billingDAO" &gt; <br />&lt;/ bean &gt; <br />　　这个例子中，orderService类使用的是constructor injection，而BillingService类使用的是setter injection。constructor injection可以确保bean不会在一个非法状态下被创建，但是setter injection更加灵活并且更易管理，尤其当类存在很多属性并且其中一些是可选的情况下。 <br /><br />　　12. 不要滥用依赖注入 <br /><br />　　作为最后一点，Spring ApplicationContext可以替你创建Java对象，但是并不是所有的Java对象都通过依赖注入来创建的。例如，全局的对象不应该通过ApplicationContext来创建。Spring是一个很棒的框架，但是，就易读性和易管理性而言，当定义大量bean的时候，基于XML的配置问题就会突出。过度的依赖注入会使XML配置变得复杂而且臃肿。记住！使用强大的IDE时，例如Eclipse和IntelliJ，与XML文件相比，Java代码更加易读，易维护，易管理。 <br /><br />　　总结 <br /><br />　　对于Spring的配置，XML是很优秀的方式。但当定义大量bean时，基于XML配置会变得冗长，笨拙。Spring提供了丰富的配置选项。适当地利用其中的选项可以使XML配置清晰，但是，有些选项，例如autowiring（自动绑定），往往会降低易读性和易维护性。文章中所列举的实例，可以帮助你创建出清晰易读的XML配置文件。</font>
<img src ="http://www.blogjava.net/i369/aggbug/89554.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2006-12-22 16:16 <a href="http://www.blogjava.net/i369/articles/89554.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈谈Spring配置中的id和name属性的花拳秀腿</title><link>http://www.blogjava.net/i369/articles/89550.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Fri, 22 Dec 2006 08:03:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/89550.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/89550.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/89550.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/89550.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/89550.html</trackback:ping><description><![CDATA[
		<span class="postbody">
				<font size="2">在BeanFactory的配置中，&lt;bean&gt;是我们最常见的配置项，它有两个最常见的属性，即id和name，最近研究了一下，发现这两个属性还挺好玩的，特整理出来和大家一起分享。 <br />1.id属性命名必须满足XML的命名规范，因为id其实是XML中就做了限定的。总结起来就相当于一个Java变量的命名：不能以数字，符号打头，不能有空格，如123，?ad,"ab "等都是不规范的，Spring在初始化时就会报错，诸如： <br /></font>
		</span>
		<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>
														<font size="2">代码:</font>
												</b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">org.xml.sax.SAXParseException: Attribute value "?ab" of type ID must be a name.</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />
				<font size="2">2.name属性则没有这些限定，你可以使用几乎任何的名称，如?ab,123等，但不能带空格，如"a b"," abc"，，这时，虽然初始化时不会报错，但在getBean()则会报出诸如以下的错误： <br /></font>
		</span>
		<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>
														<font size="2">代码:</font>
												</b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'a b' is defined</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />
				<font size="2">3.配置文件中不允许出现两个id相同的&lt;bean&gt;，否则在初始化时即会报错，如： <br /></font>
		</span>
		<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>
														<font size="2">代码:</font>
												</b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">org.xml.sax.SAXParseException: Attribute value "aa" of type ID must be unique within the document.</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<font size="2">4.但配置文件中允许出现两个name相同的&lt;bean&gt;，在用getBean()返回实例时，后面一个Bean被返回,应该是前面那个&lt;bean&gt;被后面同名的 &lt;bean&gt;覆盖了。有鉴于此，为了避免不经意的同名覆盖的现象，尽量用id属性而不要用name属性。 <br /><br />5.name属性可以用,隔开指定多个名字，如&lt;bean&gt;,相当于多个别名，这时通过getBean("a1") getBean("a2") getBean("a3")返回的都是同一个实例（假设是singleton的情况） <br /><br />6.如果id和name都没有指定，则用类全名作为name，如&lt;bean&gt;,则你可以通过 <br />getBean("com.stamen.BeanLifeCycleImpl")返回该实例。 <br /><br />7.如果存在多个id和name都没有指定，且实例类都一样的&lt;bean&gt;，如: <br /></font>
		</span>
		<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>
														<font size="2">代码:</font>
												</b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">&lt;bean&gt; <br />   &lt;bean&gt; <br />   &lt;bean&gt;</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<font size="2">则第一个bean通过getBean("com.stamen.BeanLifeCycleImpl")获得， <br />第二个bean通过getBean("com.stamen.BeanLifeCycleImpl#1")获得， <br />第三个bean通过getBean("com.stamen.BeanLifeCycleImpl#2")获得，以此类推。 <br /><br /><span style="FONT-WEIGHT: bold">[小结]</span><br />当然，这些都是奇技淫巧，不足以去实践，通过id指定唯一名称才是阳光大道，其他仅作为一笑而过的见闻罢了。</font>
		</span>
		<span class="postbody">
				<br />
		</span>
<img src ="http://www.blogjava.net/i369/aggbug/89550.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2006-12-22 16:03 <a href="http://www.blogjava.net/i369/articles/89550.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>事务管理最佳实践多余的话之三Spring声明式事务管理出错示例与解决之道 </title><link>http://www.blogjava.net/i369/articles/85104.html</link><dc:creator>芦苇</dc:creator><author>芦苇</author><pubDate>Sat, 02 Dec 2006 15:33:00 GMT</pubDate><guid>http://www.blogjava.net/i369/articles/85104.html</guid><wfw:comment>http://www.blogjava.net/i369/comments/85104.html</wfw:comment><comments>http://www.blogjava.net/i369/articles/85104.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/i369/comments/commentRss/85104.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/i369/services/trackbacks/85104.html</trackback:ping><description><![CDATA[
		<div>
				<span>                              </span>
				<strong>
						<span style="FONT-SIZE: 16pt">事务管理最佳实践多余的话之三</span>
				</strong>
		</div>
		<div style="TEXT-INDENT: 180pt">
				<strong>              Spring</strong>
				<strong>声明式事务管理出错示例与解决之道</strong>
		</div>
		<div> </div>
		<div> </div>
		<div> </div>
		<div>
				<strong>
						<font size="6">前言</font>
				</strong>
		</div>
		<div style="TEXT-INDENT: 24pt">今天，发现了一个以前写的使用Spring声明式事务管理的程序爆出了数据库连接错误，感觉是非常典型的一个误用Spring声明式事务管理的例子，拿出来为大家点评一下。</div>
		<div style="TEXT-INDENT: 24pt">请先阅读我之前写的关于事务管理的文章：<a href="http://blog.csdn.net/shendl/archive/2006/11/27/1415958.aspx"><font color="#000080"><span><span>《</span><span style="FONT-SIZE: 16pt">事务管理最佳实践全面解析</span><span>》</span></span>， </font></a><a href="http://blog.csdn.net/shendl/archive/2006/11/27/1417657.aspx"><font color="#000080"><span><span>《</span><strong><span>事务管理最佳实践多余的话之一</span>“</strong><strong><span>每次请求，一次数据库连接，一次事务</span>”</strong><strong><span>是不是金科玉律？</span></strong><span>》</span></span>， </font></a><a href="http://blog.csdn.net/shendl/archive/2006/11/29/1419332.aspx"><font color="#000080"><span><span>《</span><strong><span>事务管理最佳实践多余的话之二</span>:Transaction</strong><strong><span>后缀给声明式事务管理带来的好处</span></strong><span>》</span></span>。</font></a></div>
		<div style="TEXT-INDENT: 24pt"> </div>
		<div> </div>
		<div>
				<strong>
						<font size="6">Spring声明式事务管理出错示例</font>
				</strong>
		</div>
		<div style="TEXT-INDENT: 24pt">这个应用程序是使用Spring管理的iBatis程序。事务使用了Spring的声明式事务管理。</div>
		<div>
				<strong>
						<font size="5">爆出了如下的错误：</font>
				</strong>
		</div>
		<div align="left">
				<span style="FONT-SIZE: 10pt">Caused by: </span>
		</div>
		<div style="TEXT-INDENT: 21pt">
				<u>
						<span style="FONT-SIZE: 10pt; COLOR: navy">java.sql.SQLException</span>
				</u>
				<span style="FONT-SIZE: 10pt">: Connection is read-only. Queries leading to data modification are not allowed</span>
		</div>
		<div style="TEXT-INDENT: 21pt"> </div>
		<div align="left">
				<span style="FONT-SIZE: 10pt">at com.withub.wcms.manage.collectnews.systemNewsinfo.dao.WcmsSystemNewsinfoDao.add(</span>
				<u>
						<span style="FONT-SIZE: 10pt; COLOR: navy">WcmsSystemNewsinfoDao.java:50</span>
				</u>
				<span style="FONT-SIZE: 10pt">)</span>
		</div>
		<div align="left">
				<span style="FONT-SIZE: 10pt">    at com.withub.wcms.manage.collectnews.systemNewsinfo.service.WcmsSystemNewsinfoService.saveOrUpdate(</span>
				<u>
						<span style="FONT-SIZE: 10pt; COLOR: navy">WcmsSystemNewsinfoService.java:103</span>
				</u>
				<span style="FONT-SIZE: 10pt">)</span>
		</div>
		<div>
				<span style="FONT-SIZE: 10pt">    at com.withub.wcms.manage.collectnews.systemNewsinfo.service.WcmsSystemNewsinfoService.formatRawInfoToHtml(</span>
				<u>
						<span style="FONT-SIZE: 10pt; COLOR: navy">WcmsSystemNewsinfoService.java:89</span>
				</u>
				<span style="FONT-SIZE: 10pt">)</span>
		</div>
		<div> </div>
		<div style="TEXT-INDENT: 24pt">可以看出，出现的错误是，Spring管理下的iBatis使用的数据库连接是只读的。而在DAO类的方法中，却使用了修改数据库的iBatis方法。</div>
		<div> </div>
		<div>
				<strong>
						<font size="5">源代码：<br /><br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">WcmsSystemNewsinfoDao类的源代码就不列出来了。这个Dao类的add方法使用了iBatis的update方法，更新数据库数据。<br />下面是WcmsSystemNewsinfoService类的部分相关调用方法的源代码：<br /></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> String formatRawInfoToHtml(String infoRawId) </span><span style="COLOR: #0000ff">throws</span><span style="COLOR: #000000"> Exception{<br />       WcmsSystemNewsinfoRawModule wcmsSystemNewsinfoRawModule</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> WcmsSystemNewsinfoRawModule();<br />       wcmsSystemNewsinfoRawModule.setInfoId(infoRawId);<br />       </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">原表数据</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">        wcmsSystemNewsinfoRawModule</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.getManageNewsinfoService().queryRawInfoById(wcmsSystemNewsinfoRawModule);<br />       WcmsSystemNewsinfoModule wcmsSystemNewsinfoModule</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> WcmsSystemNewsinfoModule();<br />       </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">目标表数据</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">       BeanUtils.copyProperties(wcmsSystemNewsinfoModule, wcmsSystemNewsinfoRawModule);<br />       <br />       wcmsSystemNewsinfoModule.setInfoRawId(wcmsSystemNewsinfoRawModule.getInfoId());<br />       wcmsSystemNewsinfoModule.setInfoId(</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">);<br />       <br />       returnthis.saveOrUpdate(wcmsSystemNewsinfoModule);<br />       <br />    }<br />    </span><span style="COLOR: #008000">/**</span><span style="COLOR: #008000"><br />     *需要把目标表Model信息保存在目标表中。如果该记录已存在，则更新，否则，新增!<br />     *<br />     *@parammodule<br />     *@throwsException<br />     </span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> String saveOrUpdate(WcmsSystemNewsinfoModule module) </span><span style="COLOR: #0000ff">throws</span><span style="COLOR: #000000"> Exception{<br />       WcmsSystemNewsinfoModule loadModule</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.getWcmsSystemNewsinfoDao().selectWcmsSystemNewsinfoModuleByInfoRawId(module.getInfoRawId());<br />       String infoId</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">;<br />       </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(loadModule</span><span style="COLOR: #000000">==</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">){<br />           </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">插入</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">           infoId</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.getWcmsSystemNewsinfoDao().add(module);<br />           <br />       }</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">{<br />           </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">更新</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">           </span><span style="COLOR: #008000">/**</span><span style="COLOR: #008000"><br />            *更新<br />            *1，找到主键条件<br />            </span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br />           infoId</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">loadModule.getInfoId();<br />           module.setInfoId(loadModule.getInfoId());<br />           </span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.getWcmsSystemNewsinfoDao().updateProcessedInfo(module);<br />           <br />       }<br />       </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> infoId;<br />    }<br /></span></div></font>
				</strong>
		</div>
		<div>
				<strong>
						<font size="4">源代码说明：</font>
				</strong>
		</div>
		<div style="TEXT-INDENT: 24pt">WcmsSystemNewsinfoService类的formatRawInfoToHtml(String infoRawId)方法，根据传入的参数，准备好所需的数据，然后调用本类的saveOrUpdate(WcmsSystemNewsinfoModule module)方法。</div>
		<div style="TEXT-INDENT: 24pt">saveOrUpdate()方法，根据情况，调用WcmsSystemNewsinfoDao类的add或者update方法。</div>
		<div> </div>
		<div>
				<strong>
						<font size="5">Spring事务配置文件</font>
				</strong>
		</div>
		<div>一、事务配置抽象Bean声明<br /><br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="txProxyTemplate"</span><span style="COLOR: #ff0000"> abstract</span><span style="COLOR: #0000ff">="true"</span><span style="COLOR: #ff0000"> class</span><span style="COLOR: #0000ff">="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />       </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="transactionManager"</span><span style="COLOR: #ff0000"> ref</span><span style="COLOR: #0000ff">="transactionManager"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br />       </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="transactionAttributes"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />           </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">props</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />              </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">prop </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="*"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">readOnly</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">prop</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />              </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">prop </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="add*"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">PROPAGATION_REQUIRED,-Exception</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">prop</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />              </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">prop </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="save*"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">PROPAGATION_REQUIRED,-Exception</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">prop</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />              </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">prop </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="modify*"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">PROPAGATION_REQUIRED,-Exception</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">prop</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />              </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">prop </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="update*"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">PROPAGATION_REQUIRED,-Exception</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">prop</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />              </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">prop </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="delete*"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">PROPAGATION_REQUIRED,-Exception</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">prop</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />              </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">prop </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="remove*"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">PROPAGATION_REQUIRED,-Exception</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">prop</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />              </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">prop </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="query*"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">PROPAGATION_REQUIRED, readOnly,-Exception</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">prop</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />              </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">prop </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="load*"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">PROPAGATION_REQUIRED, -Exception</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">prop</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />           </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">props</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />       </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /></span></div><br /><br />二、服务类的Spring声明式事务管理<br /><br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">&lt;!--</span><span style="COLOR: #008000"> <br />    wcmsSystemNewsinfoService<br />     </span><span style="COLOR: #008000">--&gt;</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="wcmsSystemNewsinfoService"</span><span style="COLOR: #ff0000"><br />       parent</span><span style="COLOR: #0000ff">="txProxyTemplate"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />       </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="target"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />           </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">bean</span><span style="COLOR: #0000ff">="wcmsSystemNewsinfoServiceTarget"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br />       </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />    <br />    </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /></span></div><br /><br /><div><strong><font size="5">错误解析</font></strong></div><div style="TEXT-INDENT: 24pt">错误的原因就在上面的Spring声明式事务里。执行WcmsSystemNewsinfoService类的formatRawInfoToHtml()方法时，会应用txProxyTemplate的配置，由于它的方法名与所有的特殊配置都不匹配，因此，会应用第一个声明式事务：</div><div align="left"><span style="FONT-SIZE: 10pt">    </span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">prop </span><span style="FONT-SIZE: 10pt; COLOR: #7f007f">key</span><span style="FONT-SIZE: 10pt">=</span><span style="FONT-SIZE: 10pt; COLOR: #2a00ff">"*"</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt">readOnly</span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;/</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">prop</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span></div><div style="TEXT-INDENT: 24pt">因此，SpringAOP创建了一个只读的数据库连接和事务。</div><div style="TEXT-INDENT: 24pt">然后，调用WcmsSystemNewsinfoService类的saveOrUpdate()方法，也会查找上面的事务配置，匹配：</div><div align="left"><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">prop </span><span style="FONT-SIZE: 10pt; COLOR: #7f007f">key</span><span style="FONT-SIZE: 10pt">=</span><span style="FONT-SIZE: 10pt; COLOR: #2a00ff">"save*"</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt">PROPAGATION_REQUIRED,-Exception</span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;/</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">prop</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span></div><div style="TEXT-INDENT: 24pt">SpringAOP会去得到数据库连接和设置事务。由于在本地线程变量中已经找到了前面提供的只读连接，就会直接使用这个数据库连接，并在其上设置事务。</div><div style="TEXT-INDENT: 24pt">最后，调用WcmsSystemNewsinfoDao类的add方法。由于使用的是只读连接，执行add方法中的update语句，就发生了上面的错误：</div><div align="left"><span style="FONT-SIZE: 10pt">Caused by: </span></div><div style="TEXT-INDENT: 21pt"><u><span style="FONT-SIZE: 10pt; COLOR: navy">java.sql.SQLException</span></u><span style="FONT-SIZE: 10pt">: Connection is read-only. Queries leading to data modification are not allowed</span></div><div> </div><div> </div><div><strong><font size="6">Spring真的能够管理一切吗？</font></strong></div><div style="TEXT-INDENT: 24pt">像上面这样的Spring事务配置和Service、Dao的写法，应当说是相当普遍的，我们认为Spring已经把数据库连接、事务、O-R映射等等管得妥妥当当了，不用我们再操心了，再理解事务了！</div><div style="TEXT-INDENT: 24pt">真的如此吗？错！</div><div style="TEXT-INDENT: 24pt">漠视数据库、漠视事务，我们的系统到底有多少类似的隐患？我们的业务服务类Service浪费了多少SPringAOP的帮助？降低了多少性能？</div><div style="TEXT-INDENT: 24pt">Spring、EJB这样的声明式事务，确实大大方便了我们处理数据库连接和事务。但是，我们还是需要自己理解业务逻辑对数据库连接，对事务的需要！</div><div style="TEXT-INDENT: 24pt">工具只能帮助我们解决我们认识到的问题，解决不了我们都没理解的问题。</div><div style="TEXT-INDENT: 24pt"> </div><div><span>    </span><strong>不能再把一切扔给框架、容器、工具！首先理解你的业务逻辑，理解你要实现的功能，然后搞清楚框架、容器、工具会帮助我们做什么。只有理解了自己的业务逻辑，理解了自己的代码，理解了自己要用到的第三方代码，才能真正完美地实现我们需要的功能！</strong></div><div> </div><div><strong><font size="6">用我们的命名方法来重构上面的问题代码</font></strong></div><div><strong>一、给Service</strong><strong>类中的2</strong><strong>个方法加上后缀名标识对事务的依赖</strong></div><div> <span>   </span>当然，Service接口相应的方法也要改变。<br /><br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">/*</span><span style="COLOR: #008000"> (non-Javadoc)<br />     * @see com.withub.wcms.manage.collectnews.systemNewsinfo.service.IWcmsSystemNewsinfoService#formatRawInfoToHtml(java.lang.String)<br />     </span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> String formatRawInfoToHtmlTransaction(String infoRawId) </span><span style="COLOR: #0000ff">throws</span><span style="COLOR: #000000"> Exception{<br />       WcmsSystemNewsinfoRawModule wcmsSystemNewsinfoRawModule</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> WcmsSystemNewsinfoRawModule();<br />       wcmsSystemNewsinfoRawModule.setInfoId(infoRawId);<br />       </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">原表数据</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">        wcmsSystemNewsinfoRawModule</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.getManageNewsinfoService().queryRawInfoById(wcmsSystemNewsinfoRawModule);<br />    <br />       WcmsSystemNewsinfoModule wcmsSystemNewsinfoModule</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> WcmsSystemNewsinfoModule();<br />       </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">目标表数据</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">       BeanUtils.copyProperties(wcmsSystemNewsinfoModule, wcmsSystemNewsinfoRawModule);<br />       <br />       wcmsSystemNewsinfoModule.setInfoRawId(wcmsSystemNewsinfoRawModule.getInfoId());<br />       wcmsSystemNewsinfoModule.setInfoId(</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">);<br />       <br />       returnthis.saveOrUpdateDao(wcmsSystemNewsinfoModule);<br />       <br />    }<br />    </span><span style="COLOR: #008000">/**</span><span style="COLOR: #008000"><br />     *需要把目标表Model信息保存在目标表中。如果该记录已存在，则更新，否则，新增!<br />     *<br />     *@parammodule<br />     *@throwsException<br />     </span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> String saveOrUpdateDao(WcmsSystemNewsinfoModule module) </span><span style="COLOR: #0000ff">throws</span><span style="COLOR: #000000"> Exception{<br />       WcmsSystemNewsinfoModule loadModule</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.getWcmsSystemNewsinfoDao().selectWcmsSystemNewsinfoModuleByInfoRawId(module.getInfoRawId());<br />       String infoId</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">;<br />       </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(loadModule</span><span style="COLOR: #000000">==</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">){<br />           </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">插入</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">           infoId</span><span style="COLOR: #000000">=</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.getWcmsSystemNewsinfoDao().add(module);<br />           <br />       }</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">{<br />           </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">更新</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">           </span><span style="COLOR: #008000">/**</span><span style="COLOR: #008000"><br />            *更新<br />            *1，找到主键条件<br />        </span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br />           infoId</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">loadModule.getInfoId();<br />           module.setInfoId(loadModule.getInfoId());<br />           </span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.getWcmsSystemNewsinfoDao().updateProcessedInfo(module);<br />           <br />       }<br />       </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> infoId;<br />    }<br /></span></div></div><div><div style="TEXT-INDENT: 24pt">这样，<span style="FONT-SIZE: 10pt">formatRawInfoToHtmlTransaction</span><span style="FONT-SIZE: 10pt">方法可以直接被最终用户调用。它将能够创建或得到数据库连接，管理事务，最后关闭数据库连接。</span></div><div style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 10pt">而，</span><span style="FONT-SIZE: 10pt">saveOrUpdateDao</span><span style="FONT-SIZE: 10pt">方法，不能直接被最终用户调用。如果它需要数据库连接，它可以使用本地线程变量中保存的数据库连接。</span></div><div> </div><div><strong>二、修改Service</strong><strong>类的声明式事务的配置文件<br /><br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">&lt;!--</span><span style="COLOR: #008000"> <br />    wcmsSystemNewsinfoService<br />     </span><span style="COLOR: #008000">--&gt;</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="wcmsSystemNewsinfoService"</span><span style="COLOR: #ff0000"><br />       parent</span><span style="COLOR: #0000ff">="txProxyTemplate"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />       </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="target"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />           </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">bean</span><span style="COLOR: #0000ff">="wcmsSystemNewsinfoServiceTarget"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br />       </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />       </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="transactionAttributes"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />           </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">props</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />              </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">prop </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="*Transaction"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">PROPAGATION_REQUIRED,-Exception</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">prop</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />           </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">props</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />       </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />    <br />    </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /></span></div><br /><br /><div style="TEXT-INDENT: 24pt">这里，重载了<span style="FONT-SIZE: 10pt; COLOR: #2a00ff">txProxyTemplate</span>的声明式事务配置。我们只对Service类中的以Transaction结尾的方法，应用事务，在发生异常时，回滚。</div><div style="TEXT-INDENT: 24pt">这样，Transaction方法直接或者间接调用的DAO接口中的方法，就可以使用本地线程变量中保存的由Transaction方法的AOP代理方法创建的数据库连接。</div><div style="TEXT-INDENT: 24pt">数据库连接和事务被真正的摆平了！世界真美好！</div><div> </div><div> </div><div> </div><div> </div> <br /><br /><p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1423460</p></strong></div></div></div>
<img src ="http://www.blogjava.net/i369/aggbug/85104.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/i369/" target="_blank">芦苇</a> 2006-12-02 23:33 <a href="http://www.blogjava.net/i369/articles/85104.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>