﻿<?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/calvin/category/2823.html</link><description>之射杀钢琴师</description><language>zh-cn</language><lastBuildDate>Tue, 17 Apr 2007 14:05:55 GMT</lastBuildDate><pubDate>Tue, 17 Apr 2007 14:05:55 GMT</pubDate><ttl>60</ttl><item><title>ActiveMQ4.1+Spring2.0的POJO JMS方案（上） </title><link>http://www.blogjava.net/calvin/archive/2006/12/01/84801.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Fri, 01 Dec 2006 04:28:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2006/12/01/84801.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/84801.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2006/12/01/84801.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/84801.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/84801.html</trackback:ping><description><![CDATA[
		<p>[摘要]SpringSide 2.0采用了ActiveMQ 4.1-incubator-SNAPSHOT 与Spring 2.0 集成，比SS1.0M3版本有三个值得留意的特点，使得代码中几乎不见一丝JMS的侵入代码。<br /><br />全文地址：<a href="http://blog.csdn.net/calvinxiu/archive/2006/12/01/1423943.aspx">http://blog.csdn.net/calvinxiu/archive/2006/12/01/1423943.aspx</a><br /><br /></p>
<img src ="http://www.blogjava.net/calvin/aggbug/84801.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2006-12-01 12:28 <a href="http://www.blogjava.net/calvin/archive/2006/12/01/84801.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>XFire 最新生火指南（上）</title><link>http://www.blogjava.net/calvin/archive/2006/11/24/83233.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Fri, 24 Nov 2006 05:23:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2006/11/24/83233.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/83233.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2006/11/24/83233.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/83233.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/83233.html</trackback:ping><description><![CDATA[
		<p>  [摘要]    <span class="nobr"><a title="Visit page outside Confluence" href="http://xfire.codehaus.org/" rel="nofollow">XFire<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> 是全球众多牛人在与axis系列对比后一致投票的选择。，但网上的文档与例子总是不新，大家抛开所有的文档，所有的Axis习惯，单看这份代表XFire1.2.2最简约做法的文档。<a href="/calvin/" _fcksavedurl="http://www.blogjava.net/calvin/"></a></p>
		<p>全文地址：<a href="http://blog.csdn.net/calvinxiu/archive/2006/11/24/1411678.aspx">http://blog.csdn.net/calvinxiu/archive/2006/11/24/1411678.aspx</a></p>
<img src ="http://www.blogjava.net/calvin/aggbug/83233.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2006-11-24 13:23 <a href="http://www.blogjava.net/calvin/archive/2006/11/24/83233.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>幼学琼林--Spring下的单元测试要点</title><link>http://www.blogjava.net/calvin/archive/2006/10/23/76685.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Mon, 23 Oct 2006 01:23:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2006/10/23/76685.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/76685.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2006/10/23/76685.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/76685.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/76685.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp; 今天复习了一下 造福无数人Spring2.0 参考手册翻译--第8章 测试，浓缩即精华的更新了SpringSide wiki中的--Spring下的单元测试要点。<br><br>&nbsp;&nbsp;&nbsp; 全文地址：<a href="http://blog.csdn.net/calvinxiu/archive/2006/10/23/1346178.aspx">http://blog.csdn.net/calvinxiu/archive/2006/10/23/1346178.aspx</a></p>
<img src ="http://www.blogjava.net/calvin/aggbug/76685.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2006-10-23 09:23 <a href="http://www.blogjava.net/calvin/archive/2006/10/23/76685.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>当Spring遇到Ruby</title><link>http://www.blogjava.net/calvin/archive/2006/04/08/40009.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Sat, 08 Apr 2006 09:17:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2006/04/08/40009.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/40009.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2006/04/08/40009.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/40009.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/40009.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要:     当王家卫遇到杜可风，Spring遇到Ruby。想出这个题目的Crraig Walls 绝对也是个八卦种子，宣传的是Spring 2.0m2集成动态语言的feature。&nbsp;&nbsp;<a href='http://www.blogjava.net/calvin/archive/2006/04/08/40009.html'>阅读全文</a><img src ="http://www.blogjava.net/calvin/aggbug/40009.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2006-04-08 17:17 <a href="http://www.blogjava.net/calvin/archive/2006/04/08/40009.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Message Driven POJO</title><link>http://www.blogjava.net/calvin/archive/2006/03/25/37356.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Sat, 25 Mar 2006 09:04:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2006/03/25/37356.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/37356.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2006/03/25/37356.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/37356.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/37356.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 《Effecitve Enterprise Java》的一个实践。&nbsp;&nbsp;<a href='http://www.blogjava.net/calvin/archive/2006/03/25/37356.html'>阅读全文</a><img src ="http://www.blogjava.net/calvin/aggbug/37356.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2006-03-25 17:04 <a href="http://www.blogjava.net/calvin/archive/2006/03/25/37356.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>启动SpringSide--Promatic Enterprise Application KickStart项目</title><link>http://www.blogjava.net/calvin/archive/2006/01/02/26347.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Mon, 02 Jan 2006 09:47:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2006/01/02/26347.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/26347.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2006/01/02/26347.html#Feedback</comments><slash:comments>74</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/26347.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/26347.html</trackback:ping><description><![CDATA[
		<p>作者：<a href="http://calvin.blogjava.net/">江南白衣</a>    <br /><br />    <a href="http://www.springside.org.cn/">SpringSide--Pragmatic Enterprise Application KickStart and Common Library Stack</a>，这么长的一个名字下来，不用解释大家都知道是做什么的了----以Spring Framework为core，提供一个Pragmatic的企业应用开发基础和最佳实践展示。<br /><br />   <strong>定位：</strong>为使用Spring框架的开发者提供一个非Demo版的复杂、正式而体现最佳使用实践的参照系统。 </p>
		<p designtimesp="7969">   <strong>目标：</strong>囊括JavaEE必须面­对的所有问题的合理的、合乎实践标准的解决方案，采用Plugins形式组织，使开发者可快速定位所需的参考方案并做加法到自己的系统。<br /> <br />    项目网站：<a href="http://www.springside.org.cn/">http://www.springside.org.cn</a> <br />    or <a href="http://springside.sourceforge.net/">http://springside.sourceforge.net</a><br />  <br />    1<strong>. Featrue List</strong>    </p>
		<ul designtimesp="7976">
				<li designtimesp="7977">
						<a href="http://www.springframework.org/" target="_blank" designtimesp="7978">Spring 2</a> - J2EE Framework. 
</li>
				<li designtimesp="7979">
						<a href="http://www.hibernate.org/" target="_blank" designtimesp="7980">Hibernate 3</a> - ORM, support EJB3/JPA1.0 in future. 
</li>
				<li designtimesp="7981">
						<a href="http://www.springframework.org/" target="_blank" designtimesp="7982">Spring MVC</a> /<a href="http://www.opensymphony.com/webwork/" target="_blank" designtimesp="7983">WebWork</a> - Multi-Action Web framework. 
</li>
				<li designtimesp="7984">
						<a href="http://java.sun.com/products/jsp/" target="_blank" designtimesp="7985">JSP2.0</a> - View Template. 
</li>
				<li designtimesp="7986">
						<a href="http://xfire.codehaus.org/" target="_blank" designtimesp="7987">XFire</a> - Web Service. 
</li>
				<li designtimesp="7988">
						<a href="http://www.acegisecurity.org/" target="_blank" designtimesp="7989">Acegi</a> - Security, RBAC ana ACL.(doing) 
</li>
				<li designtimesp="7990">
						<a href="http://www.eclipse.org/birt/" target="_blank" designtimesp="7991">Eclipse BIRT 2</a> - Report Engine.(doing) 
</li>
				<li designtimesp="7992">
						<a href="http://www.drools.org/" target="_blank" designtimesp="7993">Drools 3</a> - Business Rule engine.(doing) 
</li>
				<li designtimesp="7994">
						<a href="http://www.activemq.org/" target="_blank" designtimesp="7995">ActiveMQ</a> ,<a href="http://jencks.org/" target="_blank" designtimesp="7996">Jencks</a> - JMS Message Driven POJO. 
</li>
				<li designtimesp="7997">
						<a href="http://www.opensymphony.com/quartz/" target="_blank" designtimesp="7998">Quartz</a> - Enterprise job scheduler. 
</li>
				<li designtimesp="7999">
						<a href="http://getahead.ltd.uk/dwr/" target="_blank" designtimesp="8000">DWR 2</a> , <a href="http://prototype.conio.net/" target="_blank" designtimesp="8001">Prototype.js</a> , <a href="http://trimpath.com/project/wiki/JavaScriptTemplates" target="_blank" designtimesp="8002">TrimPath JSTemplate</a>   - Ajax 
</li>
				<li designtimesp="8003">
						<a href="http://www.compassframework.org/" target="_blank" designtimesp="8004">Compass</a> - Search engine use <a href="http://lucene.apache.org/" designtimesp="8005">Luecene</a> .(doing) 
</li>
				<li designtimesp="8006">
						<a href="http://groovy.codehaus.org/" target="_blank" designtimesp="8007">Groovy</a> - Dynamic script language.(doing) 
</li>
				<li designtimesp="8008">
						<a href="http://openi.sourceforge.net/" target="_blank" designtimesp="8009">Openi</a> - BI web application for OLAP Report.(doing) 
</li>
				<li designtimesp="8010">
						<a href="http://www.servicemix.org/" target="_blank" designtimesp="8011">ServiceMix</a> - ESB(Enterprise Service Bus) and JBI.(doing) 
</li>
				<li designtimesp="8012">
						<a href="http://jakarta.apache.org/commons/validator/" target="_blank" designtimesp="8013">Commons Validator</a> - client and server side validation. 
</li>
				<li designtimesp="8014">
						<a href="http://www.opensymphony.com/sitemesh/" target="_blank" designtimesp="8015">SiteMesh</a> web-page layout and decoration framework. 
</li>
				<li designtimesp="8016">
						<a href="http://www.opensymphony.com/oscache/" target="_blank" designtimesp="8017">OSCache</a> - Web cache solution. 
</li>
				<li designtimesp="8018">
						<a href="http://www.extremecomponents.org/" target="_blank" designtimesp="8019">ExtremeTable</a> - JSP Tag Libraries. 
</li>
				<li designtimesp="8020">
						<a href="http://logging.apache.org/log4j/docs/" target="_blank" designtimesp="8021">Log4j</a> - Logging tool. 
</li>
				<li designtimesp="8022">
						<a href="http://ant.apache.org/" target="_blank" designtimesp="8023">Ant</a> , <a href="http://maven.apache.org/" target="_blank" designtimesp="8024">Maven2</a> , <a href="http://www.junit.org/" target="_blank" designtimesp="8025">JUnit</a>, <a href="http://www.easymock.org/" target="_blank" designtimesp="8026">EasyMock</a> - Build and Test tools. 
</li>
				<li designtimesp="8027">UTF-8 and I18N. 
</li>
				<li designtimesp="8028">Intergrate with <a href="http://www.bea.com/" target="_blank" designtimesp="8029">Weblogic</a> , <a href="http://tomcat.apache.org/" target="_blank" designtimesp="8030">Tomcat</a> , <a href="http://www.jboss.org/" target="_blank" designtimesp="8031">JBoss</a> , <a href="http://geronimo.apache.org/" target="_blank" designtimesp="8032">Geronimo</a>. 
</li>
				<li designtimesp="8033">And we are choising the Workflow solution. </li>
		</ul>
		<li>
				<br /> <br />    2<strong>. </strong><a href="http://www.springside.org.cn/"><strong>SpringSide</strong></a><strong>与Appfuse有什么不同？</strong><br />    1.<a href="http://www.springside.org.cn/">SpringSide</a>较完整的演示了企业应用的各个主题，而Appfuse只有简单的登陆界面和用户管理。<br /><br />    2.<a href="http://www.springside.org.cn/">SpringSide</a>是深受Ruby on Rails影响的Pragmatic型的方案。<br /><br />    3.Appfuse主要目的是展示各式mvc、orm方案与Spring的结合，有些技术属于高手玩具，而<a href="http://www.springside.org.cn/">SpringSide</a>展示的是一个国内项目的实际形态，并带中文手册与大量中文代码注释<br /><br />    4<strong>. 脚本细节</strong><br />  SpringSide的基本结构是JDK1.4 + <a href="http://www.springframework.org/" designtimesp="10588">Spring 2.0</a> + <a href="http://www.hibernate.org/" designtimesp="10589">hibernate3</a> + Spring MVC multi-action + JSP2.0。<br designtimesp="10590" /><br designtimesp="10591" />    使用<a href="http://xfire.codehaus.org/" designtimesp="10592"> XFire</a>提供WebService订书的服务端接口和 Java版/.Net版的客户端示范代码。<br designtimesp="10593" /><br designtimesp="10594" />    店员是个兼职的学生，所以系统会为每张订单发一封通知邮件给店员。为了不影响顾客下单的速度，发信的动作由jms异步进行。<br designtimesp="10595" />    <br designtimesp="10596" />    系统还会用<a href="http://www.opensymphony.com/quartz/" designtimesp="10597"> Quartz</a>定时扫描缺货的图书，用邮件通知店员。<br designtimesp="10598" />    <br designtimesp="10599" />    老板只负责看一些色彩丰富，带图的报表。Eclipse Birt2.0提供日常报表 ，<a href="http://openi.sourceforge.net/" designtimesp="10600"> Openi</a>提供BI OLAP的。<br designtimesp="10601" /><br designtimesp="10602" />    基于<a href="http://lucene.apache.org/" designtimesp="10603">Lucene</a>的<a href="http://www.compassframework.org/" designtimesp="10604"> Compass</a>做的图书全文搜索。<br designtimesp="10605" /><br designtimesp="10606" />    基于<a href="http://drools.codehaus.org/" designtimesp="10607"> Drools</a>规则引擎的订单满100元免运费，会员积分制等。      <br designtimesp="10608" /><br designtimesp="10609" />    店面演示Ajax效果与<a href="http://www.opensymphony.com/oscache/" designtimesp="10610"> OSCache</a>的Web Cache，<a href="http://www.opensymphony.com/sitemesh/" target="_blank" designtimesp="10611">SiteMesh</a>的渲染效果使用。 <br designtimesp="10612" /><br designtimesp="10613" />    一些非关键业务，用<a href="http://groovy.codehaus.org/" designtimesp="10614"> Groovy</a>动态语言来快速开发。<br designtimesp="10615" /><br designtimesp="10616" />   <a href="http://logging.apache.org/log4j/" designtimesp="10617"> log4j</a>系统将重要操作员日志异步写入数据库，使它们可管理。<br designtimesp="10618" /><br designtimesp="10619" />    综合的<a href="http://www.acegisecurity.org/" target="_blank" designtimesp="10620">Acegi</a> i安全权限管理。<br designtimesp="10621" /><br designtimesp="10622" />    utf-8, i18n的国际化项目。 
<p designtimesp="10623">   <a href="http://www.servicemix.org/" target="_blank" designtimesp="10624">ServiceMix</a> ，WorkFlow的故事设计中。<br /><br /><strong>    5. RoadMap</strong><br />    <a href="http://jira.javascud.org/secure/BrowseProject.jspa?id=10001&amp;subset=-1">Road Map in  JIRA Issue Checker </a>,欢迎大家提出更多Topic<br />    .<br />    6<strong>. 团队成员列表 </strong>(排名按加入顺序)<br />   <br />     欢迎朋友们加入。人多速度快是很重要的事情。<br /><br />   参加方式有3种<br />   1.帮忙codereview提意见<br />   2.到<u><font color="#800080">JIRA</font></u>里面领任务<br />   3.到<font color="#0000ff"><font color="#800080"><u>JIRA</u></font></font><font color="#000000"> 提出新任务 <br /><br />  真正贡献了力量的同志自然会成为开发人员. <br /><br />   <br />    <strong>Team Worker</strong>：<br />     江南白衣，cac，@_@，wuyu，charlse, efa，yimlin  <br />     <strong>Contributor：</strong><br />     water ye ，totodo，david.turing，pesome，oofrank<br />  <br />     <strong>长老供奉：</strong><br />      庄表伟，Robbin<br /><br />     (排名按加入时间）<br /><br /><strong>7. 交流区</strong></font></p></li>
		<li>
				<a href="http://demo.springside.org.cn/springside/" target="_blank">演示站点 </a>
		</li>
		<li>
				<a href="http://jira.javascud.org/secure/BrowseProject.jspa?id=10001&amp;subset=-1" target="_blank">开发RoadMap</a>
		</li>
		<li>
				<a href="http://spring.jactiongroup.net/viewforum.php?f=18" target="_blank">Spring中文论坛专区 </a>
		</li>
		<li>
				<a href="http://blog.springside.org.cn/" target="_blank">团队开发日志 </a>
				<a href="http://www.springside.org.cn:8080/springside/browse/SPRINGSIDEJIRA" target="_blank">
				</a>
				<a href="http://www.springside.org.cn:8080/springside/secure/IssueNavigator.jspa?reset=true&amp;pid=10000&amp;fixfor=10000" target="_blank">
						<br />
						<br />
				</a>开发者QQ群：15690287 (only for Springside developer)<br />用户QQ群: 21601442</li>
<img src ="http://www.blogjava.net/calvin/aggbug/26347.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2006-01-02 17:47 <a href="http://www.blogjava.net/calvin/archive/2006/01/02/26347.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在厨房，车顶，草地上使用Spring</title><link>http://www.blogjava.net/calvin/archive/2005/12/30/26015.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Fri, 30 Dec 2005 02:40:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2005/12/30/26015.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/26015.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2005/12/30/26015.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/26015.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/26015.html</trackback:ping><description><![CDATA[<P>&nbsp;&nbsp; 作者：<A href="http://calvin.blogjava.net/">江南白衣</A>&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp; Spring再强大，也要面对降临的问题--因为Spring不是Weblogic、Tomcat般的顶层容器，Servlet和EJB对象不由它创建，所以它必须要降临到Weblogic、Tomcat所在的位面。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 初学者一般不用管那么多，照着Spring+hibernate+Struts之类的Sample就做了，但慢慢的，也许就要开始在jsp+javabean体系，土制框架，singleton类等环境下使用Spring了。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 《Professional Java Development with the Spring Framework》第3章有"Managing the Containe"一节讲这个问题。一般可以分为直接召唤系与IoC fashion两类。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; <STRONG>1.直接召唤系--Singleton的Application Context</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最简单的，就像在UnitTest里那样，直接构造Application Context：</P>
<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">ApplicationContext&nbsp;ctx&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;ClasspathXmlApplicationContext(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">ApplicationContext.xml</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);</SPAN></DIV><BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在Web环境里，会使用ContextLoader构造ApplicationContext后，压进Servlet Context。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 由ContextLoaderListener或ContextLoaderServlet，在Web应用启动时完成。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 然后在Jsp/Servelet中，可以通过Servlet Context取得ApplicationContext： 
<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">ApplicationContext&nbsp;context&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;WebApplicationContextUtils.getWebApplicationContext(application);</SPAN></DIV><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;但像singleton类或者EJB中，就没有Servlet Context可用了。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果全部像UnitTest那样直接构造，速度就会很不堪。自然的，就想到把ApplicationContext做成单例。&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Spring提供了<SPAN class=fixed>ContextSingletonBeanFactoryLocator这样的物体。<BR></SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 先搞一个beanRefFactory.xml，里面写上所有的applcationContext-*.xml文件名，并把Context命名为"default-context"： 
<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">&lt;</SPAN><SPAN style="COLOR: #000000">beans</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">bean&nbsp;id</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">default-context</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">org.springframework.context.support.ClassPathXmlApplicationContext</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">constructor</SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">arg</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">list</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">value</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000">applicationContext.xml</SPAN><SPAN style="COLOR: #000000">&lt;/</SPAN><SPAN style="COLOR: #000000">value</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000">&lt;/</SPAN><SPAN style="COLOR: #000000">list</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;/</SPAN><SPAN style="COLOR: #000000">constructor</SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">arg</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #000000">&lt;/</SPAN><SPAN style="COLOR: #000000">bean</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #000000">&lt;/</SPAN><SPAN style="COLOR: #000000">beans</SPAN><SPAN style="COLOR: #000000">&gt;</SPAN></DIV><BR>&nbsp; 然后让loactor去找它，但代码有点长： 
<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">BeanFactoryReference&nbsp;bfr&nbsp;<SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN></SPAN><SPAN style="COLOR: #000000">&nbsp;DefaultLocatorFactory.getInstance().useBeanFactory(<SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">default-context</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);</SPAN><BR></SPAN><SPAN style="COLOR: #000000">BeanFactory&nbsp;factory&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;bfr.getFactory();<BR>MyService&nbsp;myService&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;factory.getBean(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">myService</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>bfr.release();<BR></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;now&nbsp;use&nbsp;myService</SPAN></DIV><BR><BR>&nbsp;&nbsp;&nbsp; 上面的代码实在是太灵活，太麻烦了。<BR>&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>还不如自己实现一个简单的Singleton，扩展ContextLoaderListener类，在Web系统启动时压入Singleton。<BR><BR>&nbsp;&nbsp;&nbsp; </STRONG>新的ContextLoaderListener类重载如下，ContextUtil中包含一个静态的ApplicationContext变量：<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">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;contextInitialized(ServletContextEvent&nbsp;event)<BR>&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">super</SPAN><SPAN style="COLOR: #000000">.contextInitialized(event);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ServletContext&nbsp;context&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;event.getServletContext();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplicationContext&nbsp;ctx&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;WebApplicationContextUtils.getRequiredWebApplicationContext(context);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ContextUtil.setContext(ctx);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></DIV><BR>&nbsp;&nbsp; 用家可直接取用：<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">&nbsp;ApplicationContext&nbsp;context&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;ContextUtil.getContext();</SPAN></DIV><BR><BR><STRONG>2.IoC fashion</STRONG><BR>&nbsp;&nbsp;&nbsp; 如果所有地方都使用直接召唤系，那就反而是在打Rod的耳光了。因为他一直都反对代码与框架深耦合的。<BR>&nbsp;&nbsp;&nbsp; 所以，更好的方法是写一些glue code、base class来完成Spring的降临，而不让应用代码察觉Spring Application Context的存在。<BR>&nbsp;&nbsp;&nbsp;&nbsp;不过，因为各个框架的结构不同，Rod也没办法讲出一个通用的整合方法，所以建议大家尽量学习已整合的各种框架，如Spring MVC、Struts的种种方式，写出自己的简单整合代码来。<BR><BR>&nbsp;&nbsp;&nbsp; 只有不确定的调用某些Singleton类，不适合过早ioc的情况，可以使用直接召唤系。<BR><img src ="http://www.blogjava.net/calvin/aggbug/26015.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2005-12-30 10:40 <a href="http://www.blogjava.net/calvin/archive/2005/12/30/26015.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring下的Unit Test笔记</title><link>http://www.blogjava.net/calvin/archive/2005/09/21/13628.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Wed, 21 Sep 2005 04:09:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2005/09/21/13628.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/13628.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2005/09/21/13628.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/13628.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/13628.html</trackback:ping><description><![CDATA[<P>&nbsp; &nbsp;&nbsp; 作者：<A href="http://calvin.blogjava.net/">江南白衣</A>&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; 注重实效的TDD的确能加快，而不是拖慢开发的进度（片面的追求覆盖率的全面UnitTest不在此列）<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一，可以实现真正分层开发。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 二，不需要依赖和频繁重启Web Container。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 三，手工测试总不免改动数据库，如何把数据库恢复到测试前的状态是件伤脑筋的事情。而Unit Test可以使用自动Rollback机制，巧妙的解决了这件事情。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Spring 下的Unit Test主要关注三个方面：<BR><STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;1. bean的依赖注入<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2. 事务控制，Open Session in Test 及默认回滚<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3. 脱离WebContainer对控制层的测试<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </STRONG></P>
<P>&nbsp;&nbsp;<STRONG>&nbsp;1.bean的依赖注入&nbsp;</STRONG><FONT size=2><BR></FONT>&nbsp;<FONT color=#0000ff> <FONT style="BACKGROUND-COLOR: #ffffff">能不依靠WebContainer来完成ApplicationContext的建立与POJO的依赖注入一向是Spring的得意之处。</FONT></FONT></P>
<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">String[]&nbsp;paths&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;{&nbsp;</SPAN><SPAN style="COLOR: #000000">"classpath:</SPAN><SPAN style="COLOR: #000000">applicationContext*.xml</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;};<BR>ApplicationContext&nbsp;ctx&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;ClassPathXmlApplicationContext(paths);<BR>UserDAO&nbsp;dao&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(UserDAO)&nbsp;ctx.getBean(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">userDAO</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);</SPAN></DIV>
<P>&nbsp;&nbsp;&nbsp; 如果你连这也觉得麻烦，那么只要你的testCase继承于Spring-mock.jar里的<STRONG>AbstractDependencyInjectionSpringContextTests</STRONG>，实现public String[] getConfigLocations()函数， 并显式写一些需要注入的变量的setter函数。<BR>&nbsp;&nbsp;&nbsp; 注：因为是AutoWire的，变量名必须等于Spring&nbsp; context文件里bean的id。</P>
<P><STRONG>2.Open Session in Test 及自动Rollback<BR>&nbsp;&nbsp;&nbsp; </STRONG>又是来自Spring这个神奇国度的东西，加入下面几句，就可以做到Open Session in Test ，解决Hibernate的lazy-load问题；而且接管原来的DAO里的事务控制定义，随意定义测试结束时是提交还是回滚，如果默认为回滚，则测试产生数据变动不会影响数据库内数据。<BR>&nbsp;<FONT color=#0000ff>&nbsp;&nbsp;&nbsp; <FONT color=#000000>你可以让testCase继承于<STRONG>AbstractTransactionalDataSourceSpringContextTests</STRONG>，通过setDefaultRollback(boolean)方法控制最后回滚还是提交。</FONT><BR>&nbsp;<BR></FONT><FONT color=#000000>&nbsp;&nbsp;&nbsp; 如果自己编写，代码是这样的：</FONT></P>
<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">&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000">&nbsp;PlatformTransactionManager&nbsp;transactionManager;<BR>&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000">&nbsp;TransactionStatus&nbsp;transactionStatus;<BR>&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;defaultRollback&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">true</SPAN><SPAN style="COLOR: #000000">;<BR>&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;setUp()<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactionManager&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(PlatformTransactionManager)&nbsp;ctx.getBean(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">transactionManager</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactionStatus&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;transactionManager.getTransaction(</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;DefaultTransactionDefinition());<BR>&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;tearDown()<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">&nbsp;(defaultRollback)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactionManager.rollback(</SPAN><SPAN style="COLOR: #0000ff">this</SPAN><SPAN style="COLOR: #000000">.transactionStatus);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">else</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactionManager.commit(</SPAN><SPAN style="COLOR: #0000ff">this</SPAN><SPAN style="COLOR: #000000">.transactionStatus);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></DIV>
<P>&nbsp; (注，hibernate太奸诈了，如果全部默认回滚，只会在session里干活，一点不写数据库，达不到完全的测试效果。)</P>
<P><STRONG>3.Controller层的Unit Test</STRONG><BR><BR>controller层靠Spring提供的MockHttpServletRequest和Response来模拟真实的servlet环境，并且spring 2.0了加了一个AbstractModelAndViewTests，提供一些检测返回值的utils函数。</P>
<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">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlWebApplicationContext&nbsp;ctx;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000">&nbsp;MockHttpServletRequest&nbsp;request&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;MockHttpServletRequest(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">GET</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000">&nbsp;MockHttpServletResponse&nbsp;response&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;MockHttpServletResponse();<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000">&nbsp;Controller&nbsp;controller&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000">&nbsp;ModelAndView&nbsp;mv&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">;<BR>&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;setUp()<BR>&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[]&nbsp;paths&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;{</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">applicationContext*.xml</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">myappfuse-servlet.xml</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">};<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctx&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlWebApplicationContext();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctx.setConfigLocations(paths);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctx.setServletContext(</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;MockServletContext(</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctx.refresh();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;controller&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(CustomerController)&nbsp;ctx.getBean(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">customerController</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/dot.gif">再加上前文的事务控制的代码</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;testCustomerList()&nbsp;</SPAN><SPAN style="COLOR: #0000ff">throws</SPAN><SPAN style="COLOR: #000000">&nbsp;Exception<BR>&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.setRequestURI(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">/customer.do</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.addParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">action</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">listView</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mv&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;controller.handleRequest(request,&nbsp;response);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertModelAttributeAvailable(mv, "customers");</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></DIV>
<P>&nbsp;&nbsp; <BR><STRONG>4.进一步简化 <BR></STRONG>一来这两个基类的名字都太长了。<BR>二来有一些公共的context文件的定义。<BR><BR>所以可以再抽象了几个<STRONG>基类，</STRONG>分别是DAOTestCase，ControllerTestCase。<BR><BR><STRONG>5. EasyMock</STRONG><BR>&nbsp;&nbsp; MockObject是一样彻底分层开发的好东西，而且使用上没什么难度。而且已不再存在只支持接口不支持Class的限制。<BR>&nbsp;&nbsp; </P>
<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">设定BookManager&nbsp;MockObject</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bookManagerMockControl&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;MockClassControl.createControl(BookManager.</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bookManagerMock&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(BookManager)&nbsp;bookManagerMockControl.getMock();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;controller.setBookManager(bookManagerMock);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">录制getAllBook()和getCategorys方法的期望值</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bookManagerMock.getAllBook();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bookManagerMockControl.setReturnValue(</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;ArrayList());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bookManagerMockControl.replay();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">执行操作</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mv&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;controller.handleRequest(request,&nbsp;response);<BR></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">验证结果&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertModelAttributeAvailable(mv,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">books</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);</SPAN></DIV>
<P><STRONG>&nbsp;&nbsp; Easy Mock VS JMock:<BR></STRONG><BR>&nbsp;&nbsp;&nbsp; JMock 要求TestCase继承于MockObjectTestCase太霸道了。妨碍了我继承于Spring2.0的ModelAndViewTestCase和使用MockDao,RealDao并行的继承体系。因此采用没那么霸道的easyMock。</P>
<P DESIGNTIMESP="3657">&nbsp;&nbsp; 另外，easyMock的脚本录制虽不如jmock那么优美，但胜在简短易读。jmock那句太长了 。<BR><BR><BR><STRONG>6. 显示层测试</STRONG><BR>还有，显示层至今没有什么好的UnitTest方法，无论是不成才的httpUnit们还是笨重的GUI test工具。Appfuse一直用的那个ThoughtWork那个<A href="http://openqa.org/selenium/">Selenium</A>和<A href="http://j3unit.sourceforge.net/">J3Unit</A>的效果不知如何， 其中J3Unit号称支持<A href="http://prototype.conio.net/"><FONT color=#0002ca>prototype</FONT></A>。</P><img src ="http://www.blogjava.net/calvin/aggbug/13628.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2005-09-21 12:09 <a href="http://www.blogjava.net/calvin/archive/2005/09/21/13628.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring 的微内核与FactoryBean扩展机制</title><link>http://www.blogjava.net/calvin/archive/2005/08/30/11099.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Tue, 30 Aug 2005 14:12:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2005/08/30/11099.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/11099.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2005/08/30/11099.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/11099.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/11099.html</trackback:ping><description><![CDATA[作者：<A href="http://calvin.blogjava.net/">江南白衣</A>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG>扩展Spring系列(1)--Spring 的微内核与FactoryBean扩展机制</STRONG><BR><BR>DreamHead在<A href="http://dreamhead.blogbus.com/logs/2005/07/1335184.html"><FONT color=#006aba>《思考微内核》</FONT></A>十分激赏 <STRONG>Spring的微内核与扩展机制：<BR></STRONG>“Spring的微内核在哪里呢？便是DI容器。而通过FactoryBean，我们可以定制自己的组件组装过程，对一个普通的JavaBean做手脚，像Spring AOP中常用的ProxyFactoryBean做的那样。如此，我们就不必把所有功能都做到Spring的DI容器中去，而是以一个FactoryBean来对DI容器的功能进行扩展。除了Spring自身之外，现在已经有一些项目开始利用这个特性扩展Spring，比如，Acegi Security和Spring Modules。”<BR><BR>&nbsp;这确是框架容器界应该贯彻的范式，微内核提供最少的功能，而由扩展接口去增强框架的能力。下面看看Spring怎么设计，明白之后就可以开始为Spring捐献精力了:)<BR><BR><STRONG><FONT size=4>1、微内核的功能</FONT></STRONG><BR><STRONG>1.1 DI(依赖注入)与Singleton管理</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 利用POJO setter的DI机制，估计每位同学随手都能写一个简单版本，不多说了。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Singleton管理说白了就是先到一个map中按id找找看有没有已存在的实例。<BR><BR><STRONG>1.2 BeanName与BeanFactory注入</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;除了DI注入的属性，微内核还有什么能卖给POJO呢？就是Bean在xml 定义里的id和BeanFactory自己了。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;卖的机制是让POJO 实现 BeanNameAware和BeanFactoryAware接口。BeanFactory用 if(pojo instance of BeanFactoryAware)判断到POJO需要注入BeanFactory,就调用setBeanFactory(this)将自己注入。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这种框架中<STRONG>基于接口的注入和调用机制</STRONG>在Java下挺标准的，Spring的功能多是基于这种模式提供。遗憾就是Java不支持多重继承，作为替代的接口里不能提供默认的实现，导致每一个Pojo都要很无聊的实现一遍setBeanFactory()。<BR><BR><STRONG>1.3 DI后的初始化函数调用</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 比如属性A,B注入之后，需要同时根据A和B来对A,B进行加工或者装配一个内部属性C，这样就需要在所有属性注入后再跑一个init()函数。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Spring提供两种方式，一种是和上面的原理一样，实现InitializingBean接口的afterPropertiesSet()函数供Spring调用。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一种是在xml定义文件里面自行定义init函数名。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 懒得每次在xml文件里定义的就采用第1种方式，不想与spring耦合的pojo就采用第2种方式。本来就是为了扩展Spring而存在的FactoryBean多采用第一种。<BR><BR>&nbsp;&nbsp; <STRONG>所谓微内核，就是仅提供以上三种功能的DI容器。<BR>&nbsp;&nbsp; 但作为轻量级容器，还需要以下两种方式，向容器内的POJO 附加各种服务。</STRONG><BR><BR><FONT size=4><STRONG>2.FactoryBean扩展机制</STRONG></FONT><BR>Spring的AOP、ORM、事务管理、JMX、Quartz、Remoting、Freemarker、Velocity，都靠FacotryBean的扩展，FacotryBean几乎遍布地上：<BR>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">bean&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="sessionFactory"</SPAN><SPAN style="COLOR: #ff0000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>class</SPAN><SPAN style="COLOR: #0000ff">="org.springframework.orm.hibernate3.LocalSessionFactoryBean"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">bean&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="baseDAOService"</SPAN><SPAN style="COLOR: #ff0000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>class</SPAN><SPAN style="COLOR: #0000ff">="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN></DIV></DIV><BR>只不过当年对这类factoryBean比较麻木不仁，不问原理的照搬照用了。<BR><BR>不过这原理说出来也好简单，所有FactoryBean 实现FactoryBean接口的getObject()函数。Spring容器getBean(id)时见到bean的定义是普通class时，就会构造该class的实例来获得bean，而如果发现是FacotryBean接口的实例时，就通过调用它的getObject()函数来获得bean，仅此而以.......可见，很重要的思想，可以用很简单的设计来实现。<BR><BR><STRONG>考察一个典型的FactoryBean：<BR></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;一般会有两个变量，三个接口：<BR>&nbsp;&nbsp;&nbsp; 一个setter函数注入需要改装的pojo，一个内部变量保持装配后的对象returnOjbect。<BR>&nbsp;&nbsp;&nbsp;&nbsp;implements三个接口 ：FactoryBean,InitializingBean和BeanFactoryAware 。<BR>&nbsp;&nbsp;&nbsp; 各接口的意义之前都讲过了。factoryBean会在afterPropertiesSet()里把pojo改装成returnObject，需要用到beanfactory进行天马行空的动作时就靠BeanFactoryAware注入。最后在getObject()里把returnObject返回。<BR><BR>Rod说：IoC principles, combined with the factory bean, afford a powerful means to abstract the act of obtaining or accessing services and resources<BR><BR><STRONG><FONT size=4>3. Bean Post-Processor扩展机制</FONT></STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp; 如果说FactoryBean 是一种Factory、Wrapper式的扩展，Bean Post-Processor就是另一种AOP、visitor式的机制，所以也多用于spring的AOP架构。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Post-Processor的原理就是BeanFactory在前文里的调用afterPropertiesSet()/init-method前后，调用在工厂里注册了的post-processor的postProcessBeforeInitialization()和postProcessAfterInitialization()。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那怎么注册登记呢？又分请不请礼仪公司两类。如果是ApplicationContext，你把继承BeanPostProcessor 的bean往xml里一搁就行了,application context自会打理。如果是BeanFacotry，就要显式的注册，代码大概像：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><SPAN style="COLOR: #000000">XmlBeanFactory&nbsp;factory&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlBeanFactory(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">C:/beans.xml</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);&nbsp;<BR>BeanPostLogger&nbsp;logger&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;BeanPostLogger();&nbsp;<BR>factory.addBeanPostProcessor(logger);</SPAN></DIV></DIV><BR><BR>Rod说："Post-processors add the ability to customize bean and container behavior in a flexible, externalized fashion. "<BR>对比Factory Bean那段，可见两种机制在他心目中的不同作用。<BR><BR><STRONG>系列文章:</STRONG><BR><A id=_b1e3909c0fd0653_HomePageDays_DaysList__ctl24_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/30/11099.html"><FONT color=#366900>Spring 的微内核与FactoryBean扩展机制</FONT></A> <BR><A id=_b1e3909c0fd0653_HomePageDays_DaysList__ctl26_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/26/11243.html"><FONT color=#366900>扩展Spring(2)--Spring对各种数据访问框架的集成机制</FONT></A> <img src ="http://www.blogjava.net/calvin/aggbug/11099.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2005-08-30 22:12 <a href="http://www.blogjava.net/calvin/archive/2005/08/30/11099.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>扩展Spring(2)--Spring对各种数据访问框架的集成机制</title><link>http://www.blogjava.net/calvin/archive/2005/08/26/11243.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Fri, 26 Aug 2005 10:24:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2005/08/26/11243.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/11243.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2005/08/26/11243.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/11243.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/11243.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <STRONG>扩展Spring(2) ---Spring对各种数据访问框架的集成机制<BR></STRONG><BR>&nbsp;&nbsp;<STRONG>&nbsp;&nbsp;&nbsp;何为数据框架集成。<BR></STRONG>&nbsp;&nbsp; 数据访问框架原本好好的，Spring都干了什么呢？<BR>&nbsp;&nbsp; 一是用template类封装了数据框架那些资源获取和异常事务处理的废话代码，而且按照自己的意见给出一些增强函数。<BR>&nbsp;&nbsp; 二是将其纳入了Spring的声明式事务管理中。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;对比Spring对Hibernate、JDBC的集成，还有<A href="https://springmodules.dev.java.net/">Spring Modules</A>对<A href="http://orbroker.sourceforge.net/">O/R Broker</A>的集成，发现Spring的DAO框架主要有六个类：<BR>&nbsp;&nbsp;&nbsp; <STRONG>1.Template</STRONG> <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 著名的Template类，用callback机制封装了除业务代码外的所有必要但废话的代码，重新封装了数据框架的API，并再附送一些增强版。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>2.TransactionManager&nbsp;<BR></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;实现PlatformTransactionManager接口，数据访问框架就能与Spring的事务机制(TransactionTemplate或AOP声明式事务)结合。<BR><BR><STRONG>&nbsp;&nbsp;&nbsp;&nbsp;重要的类仅以上两个，以下的类都只有少量标准代码，完全可以忽略。<BR>&nbsp;&nbsp;&nbsp;&nbsp;</STRONG><STRONG>3.DAOSupport<BR></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;实际DAO类的基类，负责保持template变量。如果你觉得它破坏了你的类层次结构，完全可以不用。<BR>&nbsp;&nbsp;&nbsp; <STRONG>4.Accessor<BR></STRONG>&nbsp;&nbsp;&nbsp;&nbsp; template类的基类，defining common properties like DataSource and exception translator，也没大用。<BR>&nbsp;&nbsp;&nbsp; <STRONG>5.Operations<BR></STRONG>&nbsp;&nbsp;&nbsp;&nbsp; template所实现的接口，定义template支持的数据访问函数和增强函数，template有多个实现时才有用。<BR>&nbsp;&nbsp;&nbsp; <STRONG>6.Exception Translate的相关类和函数<BR></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;异常翻译，Spring DAO很重视的一个功能。<BR><BR><STRONG><FONT size=4>Template类的代码<BR></FONT></STRONG>&nbsp;&nbsp;&nbsp;因为Hibernate本身很复杂，所以HibernateTemplate也不适合畏高晕车的人士如我观看。JDBC简单很多，但JDBCTemplate又忙着增强JDBC的功能，多出好多代码。所以我选O/R broker的集成代码来看，代码一共才280行。<BR>注：如果不熟O/R broker，可以简单的认为broker=connection, executable = statement ，其余一切同Jdbc。<BR><BR><STRONG>1.1主干函数 Execute(BrokerCallback action)</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; step1. 获得Connection-- connecton = datasource.getConn();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; step2.&nbsp;准备Statement -- statement = new Statement(connection);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;step3.&nbsp;执行Action的回调函数doInBroker(Statement)。这个doInBroker()方法由客户定义，会拿着传入的statement，执行种种操作。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000"><BR>{<BR>&nbsp;&nbsp;action.doInBroker(statement&nbsp;);<BR>}<BR></SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">(<IMG src="http://www.blogjava.net/images/dot.gif">)<BR>{<BR>&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/dot.gif">翻译异常</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">}<BR></SPAN></DIV></DIV><BR>&nbsp;&nbsp; <STRONG>1.2 template的API函数</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 虽然理论上大家可以直接使用execute()，在匿名内部类里调用数据访问框架的任何API。但java的匿名内部类不比闭包，代码难看无比，所以除了Robbin还没见到其他兄弟提倡直接用execute方法的。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;因此，template也对数据框架的API进行了wrap，封装了用execute(StatementCallback action)来执行这些API的函数，如下段就是wrap 了O/R Broker的execute(String statementID.....)方法：<BR>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;execute(final&nbsp;String&nbsp;statementID,&nbsp;final&nbsp;String[]&nbsp;paramNames,&nbsp;final&nbsp;Object[]&nbsp;values)&nbsp;throws&nbsp;DataAccessException&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;executeWithIntResult(</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;BrokerCallback()&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;Object&nbsp;doInBroker(Executable&nbsp;executable)&nbsp;throws&nbsp;BrokerException&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;applyNamedParamsToExecutable(executable,&nbsp;paramNames,&nbsp;values);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Integer(executable.execute(statementID));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;});<BR>&nbsp;&nbsp;}</SPAN></DIV></DIV><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;另外还提供一些增强型、便利型的API(如selectOne() ,selectMany())，在参数、返回值上极尽变化。<BR><BR><FONT size=4><STRONG>TransactionManager的代码</STRONG><BR></FONT>&nbsp;&nbsp;&nbsp;比较复杂，一下说不清。但JDBC的DatasourceTransactionManager和Hibernate的HibernateTransactionManager的代码都很相近，说明这个TransactionManager其实也比较固定埋头狂抄就是了。<BR><BR>&nbsp;&nbsp;&nbsp; 有兴趣的同学，可以响应某大老号召，实现ofbiz与spring的集成:)<BR><BR><STRONG>系列文章:<BR></STRONG><A id=_b1e3909c0fd0653_HomePageDays_DaysList__ctl24_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/30/11099.html"><FONT color=#366900>Spring 的微内核与FactoryBean扩展机制</FONT></A> <BR><A id=_b1e3909c0fd0653_HomePageDays_DaysList__ctl26_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/26/11243.html"><FONT color=#366900>扩展Spring(2)--Spring对各种数据访问框架的集成机制</FONT></A> <img src ="http://www.blogjava.net/calvin/aggbug/11243.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2005-08-26 18:24 <a href="http://www.blogjava.net/calvin/archive/2005/08/26/11243.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简化Spring(4)--View层</title><link>http://www.blogjava.net/calvin/archive/2005/08/24/10914.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Wed, 24 Aug 2005 06:22:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2005/08/24/10914.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/10914.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2005/08/24/10914.html#Feedback</comments><slash:comments>16</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/10914.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/10914.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 作者：<A href="http://calvin.blogjava.net/"><FONT color=#366900>江南白衣</FONT></A>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp; 人生像个舞台，请良家少女离开。<BR>&nbsp;&nbsp;&nbsp;&nbsp;同样的，Freemarker和Velocity爱好者请跳过本篇。与弃用webwork而单用Spring MVC Controller接口的理由一样，<A href="http://www.freemarker.org">Freemarker</A>本来是一样好东西，还跨界支持jsp&nbsp;的taglib，而且得到了WebWork的全力支持，但为了它的非标准化，用户数量与IDE的缺乏，在View层我们还是使用了<STRONG>保守但人人会用，IDE友好的JSP2.0 配合JSTL。<BR><BR>&nbsp;&nbsp;&nbsp; </STRONG>对于B/S结构的企业应用软件来说，基本的页面不外两种，一种是填Form的，一种是DataGrid 数据列表管理的，再配合一些css, js, ajax的效果，就是View层要关注的东西了。<BR><BR><STRONG>1. JSP 2.0的EL代替&lt;c:out&gt;</STRONG><BR>JSP2.0可以直接把EL写在html部分，而不必动用&lt;c:out&gt;节点后，老实说，JSP2.0+JSTL达到的页面效果，已不比Velocity相差多少了。 
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">p</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">{goods.name}</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">p</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">&nbsp;<BR>代替<BR></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">p</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;</SPAN><SPAN style="COLOR: #800000">c:out&nbsp;</SPAN><SPAN style="COLOR: #ff0000">value</SPAN><SPAN style="COLOR: #0000ff">="{goods.name}"</SPAN><SPAN style="COLOR: #0000ff">/&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">p</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></DIV></DIV>
<P>(除了EL里面不能调用goods的函数，sun那帮老顽固始终坚持JSTL只能用于数据显示，不能进行数据操作，所以不能调用bean的get/set外的方法)<BR><BR>&nbsp;<STRONG>2. 最懒的form 数据绑定</STRONG> </P>
<P>&nbsp;&nbsp;&nbsp; Spring少得可怜的几个tag基本上是鸡肋，完全可以不要。 而Spring开发中的那些Simple Form tag又还没有发布。Spring的Tag主要用来把VO的值绑到input框上。但是，和Struts一样，需要逐个Input框绑定，而且语法极度冗长，遇到select框还要自己进行处理.....典型的Spring Sample页面让人一阵头晕. </P>
<P>&nbsp;&nbsp;&nbsp; 而<A href="http://jodd.sourceforge.net/doc/forms.html">jodd的form tag</A>给了我们懒人一个懒得多的方法，只要在&lt;form&gt;两头用&lt;jodd:form bean="myVO"&gt;&lt;/jodd:form&gt;包住，里面的所有input框，select框，checkBox...统统自动被绑定了，这么简单的事情，真不明白struts,spring为什么不用，为了不必要的灵活性么? <BR></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">form</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">jodd:form&nbsp;</SPAN><SPAN style="COLOR: #ff0000">bean</SPAN><SPAN style="COLOR: #0000ff">="human"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;name</SPAN><SPAN style="COLOR: #0000ff">="name"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="radiobox"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;name</SPAN><SPAN style="COLOR: #0000ff">="sex"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;value</SPAN><SPAN style="COLOR: #0000ff">="man"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">select&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="age"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">option&nbsp;</SPAN><SPAN style="COLOR: #ff0000">value</SPAN><SPAN style="COLOR: #0000ff">="20"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">20</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">option</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">option&nbsp;</SPAN><SPAN style="COLOR: #ff0000">value</SPAN><SPAN style="COLOR: #0000ff">="30"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">30</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">option</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">select</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">jodd:form</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">form</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">&nbsp;<BR></SPAN></DIV></DIV>
<P><BR>
<P></P>不过，jodd有个致命弱点是不能绑定内嵌对象的值。比如Order(订单)对象里有个Customer(顾客)对象，jodd就不能像 struts,spring一样用如下语法绑定: <BR>
<P></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="customer.customerNo"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></DIV></DIV>
<P>这是因为它的beanUtils比Jakata Common弱，用了一个错误的思路的缘故。 动用beanUtils修改一下就可以了，<A href="/Files/calvin/form_tag.rar">修改后的源码可以在这里下载</A>。 
<P><STRONG>3. DataGrid数据列表</STRONG></P>
<P>DisplayTag和ValueList都属于这种形式的Tag Library。但最近出现的<A href="http://www.extremecomponents.org/">Extreme Table</A>是真正的killer，他本身功能强大不说，而且从一开始就想着如何让别人进行扩展重载，比如Extend Attributes机制就是DisplayTag这样的让千人一面者不会预留。<BR><BR><BR><STRONG>4.css, javascript, ajax</STRONG><BR>天下纷扰，没有什么特别想讲想推荐的，爱谁谁吧。<A href="http://www.amowa.net/buffalo/">Buffalo</A>, DWR, Scriptaculous, Prototype, AjaxTags, AjaxAnywhere, Rico, Dojo, JSON-RPC，看着名字就头痛。<BR><BR>相关文章<BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl3_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/21/10530.html"><FONT color=#366900>简化Spring(1)--配置文件</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl2_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/22/10695.html"><FONT color=#366900>简化Spring(2)--Model层</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl1_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/23/10794.html"><FONT color=#366900>简化Spring(3)--Controller层</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl0_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/24/10914.html"><FONT color=#366900>简化Spring(4)--View层</FONT></A> </P><img src ="http://www.blogjava.net/calvin/aggbug/10914.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2005-08-24 14:22 <a href="http://www.blogjava.net/calvin/archive/2005/08/24/10914.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简化Spring(3)--Controller层</title><link>http://www.blogjava.net/calvin/archive/2005/08/23/10794.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Tue, 23 Aug 2005 06:53:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2005/08/23/10794.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/10794.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2005/08/23/10794.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/10794.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/10794.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 作者：<A href="http://calvin.blogjava.net/"><FONT color=#366900>江南白衣</FONT></A>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp; Struts与Webwork的扇子请跳过本篇。<BR><BR>&nbsp;&nbsp;&nbsp; MVC不就是把M、V、C分开么？至唯物朴素的做法是两个JSP一个负责View，一个负责Controller，再加一个负责Model的Java Bean，已经可以工作得很好，那时候一切都很简单。<BR>&nbsp;&nbsp;&nbsp; 而现在为了一些不是本质的功能，冒出这么多非标准的Web框架，实在让人一阵郁闷。像Ruby On Rails那样简捷开发，可用可不用，而且没有太多的限制需要学习的，比如<STRONG>Webwork</STRONG>这型还可以考虑。但像Struts那样越用框架越麻烦，或者像Tapestry那样有严重自闭倾向，额上凿着"高手专用玩具"的，用在团队里就是不负责任的行为了。<BR><BR>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;so，<STRONG>我的MVC方案是使用Spring MVC的Controller接口，写最普通的JavaBean作为Controller</STRONG>，本质就和当年拿JSP作Controller差不多，但拥有了Spring IOC的特性。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;之所以用这么消极的选择标准，是因为觉得这一代MVC框架离重回RAD时代的标准还很远，注定了只是一段短暂的，过渡的技术，不值得投资太多精力和团队学习成本。</DIV>
<DIV><BR><STRONG>1. 原理</STRONG> 
<DIV>&nbsp;&nbsp;&nbsp;&nbsp; Spring MVC按植物分类学属于Martin Flower〈企业应用模式〉里的静态配置型Front Controler，使用DispatchServlet截获所有*.do的请求，按照xml文件的配置，调用对应的Command对象的handleRequest(request,response)函数，同时进行依赖对象的注入。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 我们的Controller层，就是实现handleRequest(request,response)函数的普通JavaBean。</DIV>
<DIV></DIV></DIV>
<DIV><STRONG><BR>2. 优势<BR>&nbsp;&nbsp;&nbsp;&nbsp;</STRONG> Spring MVC与struts相比的优势:</DIV>
<DIV><STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</STRONG>一是它的Controller有着从松到紧的类层次结构，用户可以选择实现只有一个HandleRequest()函数的接口，也可以使用它有很多回调函数的SimpleFormController类。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;二是不需要Form Bean，也不需要Tapestry那所谓面向对象的页面对象，对于深怕类膨胀，改一个东西要动N个地方的人最适合不过。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;三是不需要强XML配置文件，宣告式编程是好的，但如果强制成框架，什么都要在xml里面宣告，写的时候繁琐，看的时候也要代码配置两边看才能明白就比较麻烦了。</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;那Webwork呢?没有实战过，不过因为对MVC框架所求就不多，单用Spring MVC的Controller已经可以满足需求，就不多搞一套Webwork来给团队设坎，还有给日后维护，spring,ww2之间的版本升级添麻烦了。真有什么需要添加的，Spring MVC源代码量很少，很容易掌控和扩展。<BR>&nbsp; </DIV>
<DIV><B>3.化简</B></DIV>
<DIV><STRONG>3.1. 直接implement Controller，实现handleRequest()函数</STRONG></DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;首先，simple form controller非我所好，一点都不simple。所以有时我会直接implement Controller接口。这个接口的唯一函数是供Front Controller调用的handleRequest(request,response)。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果需要application对象，比如想用application.getRealPath()时，就要extends webApplicationObjectSupport。<BR><BR><STRONG>3.2.每个Controler负责一组相关的action</STRONG></DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我是坚决支持一个Controler负责多个action的，一个Controler一个action就像一个function一个类一样无聊。所以我用最传统的方式，用URL参数如msg="insert"把一组相关action交给一个Controler控制。ROR与制作中的Groovy On Rails都是这种模式，Spring也有MultiActionController支持。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以上三者都是把URL参数直接反射为Controller的函数，而<A href="http://mc4j.org/confluence/display/stripes/Home">Stripes</A>的设计可用annotation标注url action到响应函数的映射。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<DIV></DIV><STRONG>3.3.xml宣告式编程的取舍</STRONG>&nbsp;</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;我的取舍很简单，反正Spring没有任何强制，我只在可能需要不重新编译而改变某些东西的时候，才把东西放在xml里动态注入。jsp路径之类的就统统收回到controller里面定义.</DIV>
<DIV>&nbsp;</DIV>
<DIV><STRONG>3.4.Data Binder</STRONG></DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Data Binder是Controller的必有环节，对于Spring提供的DataBinder，照理完全可用，唯一不爽是对象如果有内嵌对象，如订单对象里面包含了Customer对象，Spring需要你先自行创建了Customer对象并把它赋给了Order对象，才可能实现order.customer.customer_no这样的绑定。我偷懒，又拿Jakarta BeanUtils出来自己做了一个Binder。<BR><BR>3.<STRONG>5.提取基类</STRONG></DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最后还是忍不住提取了一个基类，负责MultiAction和其他一些简便的方法。Sprnig的MultiActionController做得太死，规定所有函数的第1,2个参数必须是request和response，不懂动态的，温柔的进行参数注入。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;经过化简再化简，已经是很简单一个Java Bean ，任谁都可以轻松上手，即使某年某月技术的大潮把现在所有MVC框架都淹没了，也不至于没人识得维护。<BR><BR>相关文章<BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl3_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/21/10530.html"><FONT color=#366900>简化Spring(1)--配置文件</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl2_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/22/10695.html"><FONT color=#366900>简化Spring(2)--Model层</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl1_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/23/10794.html"><FONT color=#366900>简化Spring(3)--Controller层</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl0_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/24/10914.html"><FONT color=#366900>简化Spring(4)--View层</FONT></A> </DIV><img src ="http://www.blogjava.net/calvin/aggbug/10794.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2005-08-23 14:53 <a href="http://www.blogjava.net/calvin/archive/2005/08/23/10794.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简化Spring(2)--Model层</title><link>http://www.blogjava.net/calvin/archive/2005/08/22/10695.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Mon, 22 Aug 2005 06:55:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2005/08/22/10695.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/10695.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2005/08/22/10695.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/10695.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/10695.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;作者：<A href="http://calvin.blogjava.net/"><FONT color=#366900>江南白衣</FONT></A>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp; 因为Spring自带的sample离我们的实际项目很远，所以官方一点的model层模式展现就靠Appfuse了。<BR>&nbsp;&nbsp;&nbsp;&nbsp;但Appfuse的model层总共有一个DAO接口、一个DAOImpl类、一个Service接口、一个ServiceImpl类、一个DataObject.....大概只有受惯了虐待的人才会欣然接受吧。<BR>&nbsp;&nbsp;&nbsp; 另外，Domain-Driven逢初一、十五也会被拿出来讨论一遍。<BR><BR>&nbsp;&nbsp;&nbsp; 其实无论什么模式，都不过是一种人为的划分、抽象和封装。只要在团队里理解一致，自我感觉优雅就行了。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 我的建议是，一开始DO和Manager一生一旦包演全场，DO作为纯数据载体，而Manager类放置商业方法，用getHibernateTemplate()直接访问数据库，不强制基于接口编程。当某天系统复杂到你直觉上需要将DAO层和Service层分开时，再分开就好了。<BR><BR>&nbsp;&nbsp;&nbsp; <STRONG><FONT size=4>1.DataObject类<BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp; </STRONG>好听点也可以叫Domain Object。Domain Driven&nbsp; Development虽然诱人，但因为Java下的ORM框架都是基于Data Mapper模式的，没有Ruby On Rails中那种Active Recorder的模式。所以，还是压下了这个欲望，Data Object纯粹作一个数据载体，而把数据库访问与商业逻辑操作统一放到Manager类中。<BR><BR>&nbsp;&nbsp;&nbsp; <STRONG><FONT size=4>2.Manager类</FONT></STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;我的Manager类是Appfuse中DAO类与Service类的结合体，因为：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>2.1 不想使用纯DAO</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp; 以往的DAO是为了透明不同数据库间的差异，而现在Hibernate已经做的很好。所以目前纯DAO的更大作用是为了将来可以切换到别的ORM方案比如iBatis，但一个Pragmaic的程序员显然不会无聊到为了这个机会不大的理由，现在就去做一个纯DAO层，项目又不是Appfuse那样为了demo各种ORM方案而存在。<BR><BR>&nbsp;&nbsp;&nbsp; <STRONG>2.2 也不想使用Service层来为Dao解耦<BR></STRONG>&nbsp;&nbsp;&nbsp; 在JPetStore里有一个很薄的Service层，Fascade了一堆DAO类，把这些DAO类的所有方法都僵硬的重复了一遍。理论上一个Manager类可以管理数个Dao类，可以避免Dao之间直接耦合。但既然有Manager的情况下，商业逻辑都是写在Manager类的，那样子Manager似乎还是调用另一个Manager比较妥当，调用裸Dao可能存在忽略了某些逻辑。所以，耦合又从Dao层升到Service层了。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 所以，除非你做的是超薄的不带逻辑的Service层，否则没有解耦的意义。<BR>&nbsp;&nbsp;&nbsp; 何况，对一个不是死搬书的Designer来说，组件边界之内的类之间的耦合并不是耦合。<BR><BR>&nbsp;&nbsp;&nbsp; <FONT size=4><STRONG>3.去除不必要的基于接口编程</STRONG><BR></FONT>&nbsp;&nbsp;&nbsp; 众所周知，Spring是提倡基于接口编程的。<BR>&nbsp;&nbsp;&nbsp; 但有些Manager类，比如SaleOrderManager ，只有5%的机会再有另一个Impl实现。95%时间里这两兄弟站一起，就像C++里的.h和.cpp，徒增维护的繁琐(经常要同步两个文件的函数声明)，和代码浏览跳转时的不便(比如从Controler类跟踪到Service类时，只能跳转到接口类的相应函数，还要再按一次复杂的热键才跳转到实现类)<BR>&nbsp;&nbsp;&nbsp; 连Martin Flower都说，强制每个类都分离接口和实现是过犹不及。只在有多个独立实现，或者需要消除对实现类的依赖时，才需要分离接口。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>3.1 DAO被强制用接口的原因</STRONG><BR>&nbsp;&nbsp;&nbsp; Spring IOC本身是不会强制基于接口的，但DAO类一般要使用Spring的声明式事务机制，而声明式的事务机制是使用Spring AOP来实现的。Spring AOP的实现机制包括动态代理和Cgilib2，其中Spring AOP默认使用的Java动态代理是必须基于接口，所以就要求基于接口了。<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp; <STRONG>3.2 解决方法</STRONG><BR>&nbsp;&nbsp;&nbsp; 那就让Spring AOP改用CGLib2，生成目标类的子类吧，我们只要指定使用声明式事务的FactoryBean使用CGLib的方式来实现AOP，就可以不基于接口编程了。<BR>&nbsp;&nbsp;&nbsp; 指定的方式为<STRONG>设置proxyTargetClass为true</STRONG>。如下：<BR>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><SPAN style="COLOR: #0000ff">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">bean&nbsp;</SPAN><SPAN style="COLOR: #ff0000">class</SPAN><SPAN style="COLOR: #0000ff">="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"</SPAN><SPAN style="COLOR: #ff0000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>id</SPAN><SPAN style="COLOR: #0000ff">="baseService"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;&nbsp;&nbsp;abstract</SPAN><SPAN style="COLOR: #0000ff">="true"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">property&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="transactionManager"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;ref</SPAN><SPAN style="COLOR: #0000ff">="transactionManager"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">property&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="proxyTargetClass"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;value</SPAN><SPAN style="COLOR: #0000ff">="true"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><IMG src="http://www.blogjava.net/images/dot.gif"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">bean</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></DIV></SPAN></DIV></DIV><BR>&nbsp;&nbsp;&nbsp;&nbsp; 又因为这些Service Bean都是单例，效率应该不受影响。<BR><BR>&nbsp;&nbsp;&nbsp; <STRONG><FONT size=4>4.总结<BR></FONT></STRONG>&nbsp;&nbsp;&nbsp; 对比Appfuse里面的5个类，我的Model层里只有VO作为纯数据载体，Manager类放商业方法。有人说这样太简单了，但一个应用，要划成几个JSP，一个Controller，一个Manager，一个VO，对我来说已经足够复杂，再要往上架墙叠屋，恕不奉陪，起码在我的项目范围里不需要。(但有很多项目是需要的，神佑世人）<BR><BR>&nbsp;&nbsp;&nbsp; 后记：迫于世人的压力，<A href="http://www .springside.org.cn">SpringSide</A>暂时还是把DAO和Service层分开了，但依然坚持不搞那么多接口。<BR>另外，尽量利用IDEA的代码生成热键，为Manager类生成delegate的Dao类方法。<BR><BR>相关文章<BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl3_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/21/10530.html"><FONT color=#366900>简化Spring(1)--配置文件</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl2_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/22/10695.html"><FONT color=#366900>简化Spring(2)--Model层</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl1_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/23/10794.html"><FONT color=#366900>简化Spring(3)--Controller层</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl0_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/24/10914.html"><FONT color=#366900>简化Spring(4)--View层</FONT></A> <img src ="http://www.blogjava.net/calvin/aggbug/10695.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2005-08-22 14:55 <a href="http://www.blogjava.net/calvin/archive/2005/08/22/10695.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简化Spring(1)--配置文件</title><link>http://www.blogjava.net/calvin/archive/2005/08/21/10530.html</link><dc:creator>江南白衣</dc:creator><author>江南白衣</author><pubDate>Sun, 21 Aug 2005 12:59:00 GMT</pubDate><guid>http://www.blogjava.net/calvin/archive/2005/08/21/10530.html</guid><wfw:comment>http://www.blogjava.net/calvin/comments/10530.html</wfw:comment><comments>http://www.blogjava.net/calvin/archive/2005/08/21/10530.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.blogjava.net/calvin/comments/commentRss/10530.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/calvin/services/trackbacks/10530.html</trackback:ping><description><![CDATA[<P>作者：<A href="http://calvin.blogjava.net/"><FONT color=#366900>江南白衣</FONT></A>&nbsp;<BR>&nbsp;&nbsp;&nbsp;<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;<STRONG>序</STRONG> </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;人人都爱Spring加Hibernate。<BR>&nbsp;&nbsp;&nbsp;&nbsp;但Spring MVC+hibernate的Sample如Appfuse的代码却算不得最简洁优美好读，如果在自己的项目中继续发挥我们最擅长的依样画葫芦大法，美好愿望未必会实现。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; 所以，Pramatic精神不灭。这个系列就是探寻最适合自己的Spring+Hibernate模式。<BR>&nbsp;&nbsp;&nbsp;&nbsp;<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; <STRONG>I-配置文件简化</STRONG> </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我厌倦一切配置文件繁重的框架。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; 最好的情况是，<STRONG>框架提供极端灵活复杂的配置方式，但只在你需要的时候</STRONG>。<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; Spring提供了三种可能来简化XML。随着国内用户水平的提高，这些基本的简化技巧大家都已掌握。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;大家可以直接看第3，第4点--Spring 1.2, Spring 2.0的后继改进。<BR></P>
<P><STRONG>1.1.autowire="byName" /"byType"</STRONG></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp; 假设Controller有一个属性名为customerDAO，Spring就会在配置文件里查找有没有名字为CustomerDAO的bean, 自动为Controller注入。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果bean有两个属性，一个想默认注入，一个想自定义，只要设定了autowire，然后显式的声明那个想自定义的，就可以达到要求。这就应了需求，在需要特别配置的时候就提供配置，否则给我一个默认注入。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; 还有一个更懒的地方，在最最根部的&lt;beans&gt;节点写一句default-autovwrie="byName"，可以让文件里的所有bean 都默认autowrie。<BR>&nbsp;&nbsp;&nbsp; 不过Rod认为开发期可以这样，但Production Server上不应该使用Autowire。而我觉得那些自定义一次的地方比如TranscationManager应该详细定义，而Dao,Controller这种大量重复定义的bean就可以偷点懒了。</P>
<P><STRONG>1.2.&lt;bean&gt;节点之间抽象公共定义和 Inner Bean</STRONG></P>
<P>&nbsp;&nbsp;&nbsp; 这太方便懒人了，想不到两个独立的XML节点都可以玩继承和派生，子节点拥有父节点的全部属性。<BR>&nbsp;&nbsp;&nbsp; 最好用的地方就是那个Transtion Proxy的定义。先定义一个又长又冗的父类，然后用子类去继承它。<BR>&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp; 另外，还有一个Inner Bean的机制，可以把DAO写成Proxy的内部类。为什么要写成内部类?为了让Proxy冒名顶替它去让Controller Autowire。(详见后面的示例) </P>
<P><STRONG>1.3. 宽松的配置, To XML or Not to XML&nbsp;<BR></STRONG>&nbsp;&nbsp;&nbsp; 据说Spring比Struts的配置宽松了很多，这就给人把东西从配置文件中撤回原码中的机会。<BR>&nbsp;&nbsp;&nbsp; 不赞成什么都往配置文件里晒，造成了Rich Information的配置文件，修改或者查看的时候，要同时打开配置文件和原码才能清楚一切。 <BR>&nbsp;&nbsp;&nbsp; 而我希望配置文件就集中做一些整体的配置，还有框架必须的、无需管理的冗余代码。而一些细节的变化不大的配置和逻辑，就尽量别往里塞了。因此，Success/Fail View 的配置，不建议放在里面。 <BR></P>
<P><STRONG>2.简化后的配置文件</STRONG></P><STRONG></STRONG>
<P>1.Controller只剩下一句</P>
<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&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="customerController"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;class</SPAN><SPAN style="COLOR: #0000ff">="org.springside.bookstore.web.CustomerController"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;autowire</SPAN><SPAN style="COLOR: #0000ff">="byName"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN></DIV><BR>
<P>2.DAO也只剩一句</P>
<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&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="customerDAO"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;class</SPAN><SPAN style="COLOR: #0000ff">="org.springside.bookstore.dao.CustomerDao"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN></DIV>
<P>3.Service类只剩下5行 </P>
<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">&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">bean&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="customerManager"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;parent</SPAN><SPAN style="COLOR: #0000ff">="baseTxService"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">property&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="target"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">bean&nbsp;</SPAN><SPAN style="COLOR: #ff0000">class</SPAN><SPAN style="COLOR: #0000ff">="org.springside.bookstore.service.CustomerManager"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">property</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">bean</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></DIV>
<P><B>3.Spring 1.2后xml语法简化<BR><BR>&nbsp;</B>最主要的简化是把属性值和引用bean从<STRONG>子节点</STRONG>变回了<STRONG>属性值</STRONG>，对不喜欢autowire的兄弟比较有用。<BR>&nbsp;当然，如果value要CDATA的时候还是要用子节点。另外，list的值可以用空格隔开也比较实用。<BR></P>
<DIV>1.属性值<BR><BR>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><SPAN style="COLOR: #0000ff">&nbsp; &lt;</SPAN><SPAN style="COLOR: #800000">property&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="foo"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">value</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">fooValue</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">value</SPAN><SPAN style="COLOR: #0000ff">&gt;<BR></SPAN><SPAN style="COLOR: #0000ff">&nbsp; &lt;/</SPAN><SPAN style="COLOR: #800000">property</SPAN><SPAN style="COLOR: #0000ff">&gt;<BR></SPAN>&nbsp; 简化为<BR><SPAN style="COLOR: #0000ff">&nbsp; &lt;</SPAN><SPAN style="COLOR: #800000">property&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="foo"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;value</SPAN><SPAN style="COLOR: #0000ff">="fooValue"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN></DIV></DIV><BR>2.引用 bean<BR>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">property&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="foo"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">ref&nbsp;</SPAN><SPAN style="COLOR: #ff0000">bean</SPAN><SPAN style="COLOR: #0000ff">="fooBean"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">property</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>简化为<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">property&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="foo"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;ref</SPAN><SPAN style="COLOR: #0000ff">="fooBean"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN></DIV></DIV><BR><BR>3. list可以简化为空格分开的字符串</DIV>
<DIV>&nbsp; 
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">property&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="myFriendList"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">list</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">value</SPAN><SPAN style="COLOR: #0000ff">&gt;gigix</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">value</SPAN><SPAN style="COLOR: #0000ff">&gt;<BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">value&gt;<FONT color=#0000ff>wuyu</FONT></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">value</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">list</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">property</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>简化为<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">property&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="myFriendList"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;value</SPAN><SPAN style="COLOR: #0000ff">="gigix wuyu"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN></DIV></DIV>&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;<BR><STRONG>4.Spring 2.0来了</STRONG><BR>&nbsp;&nbsp; 如果没什么外力刺激，spring xml 可能就这样不会变了。但现在xml成了过街老鼠，被ror的默认配置和JDK5的annotation逼得不行，当然就要继续求变。<BR>&nbsp;&nbsp; 比如有好事者认为，节点名必须以bean打头，附加一个属性id来表示bean名；属性值必须搞一个property子节点，子节点上有个属性name来表示属性名，是给机器看的很不直观的东西。 
<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&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="customerDAO" class="org.springside...CustomerDAO"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">property&nbsp;</SPAN><SPAN style="COLOR: #ff0000">name</SPAN><SPAN style="COLOR: #0000ff">="maxCount"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;value</SPAN><SPAN style="COLOR: #0000ff">="10"</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></DIV><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">customerDAO&nbsp;</SPAN><SPAN style="COLOR: #ff0000">class</SPAN><SPAN style="COLOR: #0000ff">="org.springside....CustomerDAO"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;maxCount</SPAN><SPAN style="COLOR: #0000ff">="10"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN></DIV><BR>Spring 2.0正用schema实现类似的语法，具体请看它的JPetStore sample。<BR><BR></DIV>
<DIV><STRONG></STRONG></DIV>
<DIV><B>5.使用Spring自带的DTD使编辑器Smart.</B></DIV>
<P>&nbsp;&nbsp;&nbsp; 如果没有用Eclipse的Spring插件，那至少也要使用spring自带的dtd使XML编辑器smart一些，能够自动为你生成属性,判断节点/属性名称有没有拼错等。<BR><BR><STRONG>6.还有更变态的简化配置方法</STRONG><BR>&nbsp;&nbsp; &nbsp;比如autoproxy，不过我觉得更简化就不可控了，所以没有采用。<BR><BR>相关文章<BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl3_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/21/10530.html"><FONT color=#366900>简化Spring(1)--配置文件</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl2_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/22/10695.html"><FONT color=#366900>简化Spring(2)--Model层</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl1_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/23/10794.html"><FONT color=#366900>简化Spring(3)--Controller层</FONT></A> <BR><A id=_814bc1840a6fa23_HomePageDays_DaysList__ctl0_DayItem_DayList__ctl1_TitleUrl href="/calvin/archive/2005/08/24/10914.html"><FONT color=#366900>简化Spring(4)--View层</FONT></A> </P><img src ="http://www.blogjava.net/calvin/aggbug/10530.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/calvin/" target="_blank">江南白衣</a> 2005-08-21 20:59 <a href="http://www.blogjava.net/calvin/archive/2005/08/21/10530.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>