??xml version="1.0" encoding="utf-8" standalone="yes"?>老司机亚洲精品影院在线观看 ,亚洲精品午夜国产VA久久成人,中文字幕亚洲综合久久http://www.tkk7.com/ltc603/category/6946.html<font size="3">学无止境</font> <br> <script type="text/javascript" src="http://wujunlove.googlepages.com/bigstaticeyes.js"></script>zh-cnTue, 27 Feb 2007 21:18:29 GMTTue, 27 Feb 2007 21:18:29 GMT60l合struts和hibernate谈J2EE架构的数据表C(vo,po,formbeanQ?/title><link>http://www.tkk7.com/ltc603/archive/2006/08/16/63888.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Wed, 16 Aug 2006 05:51:00 GMT</pubDate><guid>http://www.tkk7.com/ltc603/archive/2006/08/16/63888.html</guid><wfw:comment>http://www.tkk7.com/ltc603/comments/63888.html</wfw:comment><comments>http://www.tkk7.com/ltc603/archive/2006/08/16/63888.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ltc603/comments/commentRss/63888.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ltc603/services/trackbacks/63888.html</trackback:ping><description><![CDATA[ <strong>  </strong>关于VO、PO的理? <p><br />O/R Mapping ?Object Relational MappingQ对象关pL)的羃写。通俗点讲Q就是将对象与关pL据库l定Q用对象来表C关pL据。在O/R Mapping的世界里Q有两个基本的也是重要的东东需要了解,即VOQPO?br />  VOQ值对?Value Object)QPOQ持久对?Persisent Object)Q它们是׃l属性和属性的get和setҎl成。从l构上看Q它们ƈ没有什么不同的地方。但从其意义和本质上来看是完全不同的?br /><br />Q.VO是用new关键字创建,由GC回收的?<br />  PO则是向数据库中添加新数据时创建,删除数据库中数据时削除的。ƈ且它只能存活在一个数据库q接中,断开q接卌销毁?<br /><br />Q.VO是值对象,_点讲它是业务对象Q是存活在业务层的,是业务逻辑使用的,它存zȝ目的是为数据提供一个生存的地方?<br />  PO则是有状态的Q每个属性代表其当前的状态。它是物理数据的对象表示。用它Q可以我们的程序与物理数据解耦,q且可以化对象数据与物理数据之间的{换?br /><br />Q.VO的属性是Ҏ当前业务的不同而不同的Q也是_它的每一个属性都一一对应当前业务逻辑所需要的数据的名U?<br />  PO的属性是跟数据库表的字段一一对应的?br /><br />PO对象需要实现序列化接口?br /><br />Q{载:<a >http://www.matrix.org.cn/resource/article/43/43869.html</a>Q?br />Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-<br />?struts+ hibernate q种l构中,是不应该把Hibernate产生的PO直接传递给JSP的,不管他是</p><p>IteratorQ还是ListQ这是一个设计错误?</p><p>我来谈谈在J2EE架构中各层的数据表示ҎQ?</p><p>Web层的数据表示是FormBeanQ数据来源于HTML Form POST <br />业务层的数据表示是VO <br />持久层的数据表示是POQ其数据来源于数据库Q持久层的数据表CZ如CMP </p><p>在一个规范的J2EE架构中,不同层的数据表示应该被限制在层内Q而不应该扩散到其它层Q这样可以降</p><p>低层间的耦合性,提高J2EE架构整体的可l护性和可扩展性。比如说Web层的逻辑q行了修改,那么只需</p><p>要修改FormBean的结构,而不需要触动业务层和持久层的代码修攏V同hQ当数据库表q行了小的调</p><p>_那么也只需要修Ҏ久层数据表示Q而不需要触动业务层代码和Web层代码?</p><p>不过׃Hibernate的强大功能,例如动态生成POQPO的状态管理可以脱SessionQ得在应用?/p><p>Hibernate的J2EE框架中,PO完全可以充当VOQ因此我们下面把PO和VO合ƈQ统UCؓPO?</p><p>先来谈谈ActionFormBean和持久层的PO之间的重大区别?</p><p>在简单的应用中,ActionFormBean和PO几乎是没有区别,所以很多hq脆是用ActionFormBean来充?/p><p>POQ于是ActionFormBean从JSP面到Servlet控制层再C务层Q然后穿q持久层Q最后一直映到?/p><p>据库表。真是一竿子捅到了底Q?</p><p>但是在复杂的应用中,ActionFormBean和PO是分ȝQ他们也不可能一栗ActionFormBean是和|页?/p><p>面的Form表单一一对应的,Form里面有什么元素,Bean里面有什么属性。而PO和数据库表对应,因此</p><p>如果数据库表不修改,那么PO也不会修改,如果面的流E和数据库表字段对应关系不一_那么你又</p><p>如何能够使用ActionFormBean来取代PO呢? </p><p>比如说吧Q用h册页面要求注册用L基本信息Q因此HTML Form里面包含了基本信息属性,于是你需</p><p>要一个ActionFormBean来一一对应(注意Q是一一对应)Q每个Bean属性对应一个文本框或者选择框什?/p><p>的?</p><p>而用戯个持久对象呢Q他的属性和ActionFormBean有什么明显不同呢Q他会有一些ActionFormBean所</p><p>没有的集合属性,比如说用L权限属性,用户的组属性,用户的帖子等{。另外还有可能的是在</p><p>ActionFormBean里面?个属性,分别是用LFirst Name, Middle Name, Last NameQ而在我的Userq?/p><p>个持久对象中是一?Name 对象属性?</p><p>假设我的注册面原来只要你提供First NameQ那么ActionFormBeanp一个属性,后来我要你提供全</p><p>名,你要改ActionFormBeanQ加两个属性。但是这个时候PO是不应该修改_因ؓ数据库没有改?</p><p>那么在一个完整的J2EEpȝ中应该如何进行合理的设计呢? </p><p>JSP(View) ---> ActionFormBean(Module) ---> Action(Control) </p><p>ActionFormBean是Web层的数据表示Q它和HTML面Form对应Q只要Web面的操作流E发生改变,它就</p><p>要相应的q行修改Q它不应该也不能被传递到业务层和持久层,否则一旦页面修改,会一直牵q到业务</p><p>层和持久层的大面U的代码q行修改Q对于Y件的可维护性和可扩展性而言Q是一个灾难,Actiont是</p><p>他的边界Q到此ؓ止! </p><p>Action(Web Control) ---> Business Bean ---> DAO ---> ORM --->DB </p><p>而PO则是业务层和持久层的数据表示Q它在业务层和持久层之间q行动Q他不应该也不能被传递到Web</p><p>层的View中去Q而ActionServlet是他的边界Q到此ؓ止! </p><p>然后来看一看整个架构的程Q?</p><p>当用户通过览器访问网,提交了一个页面。于是Action拿到了这个FormBeanQ他会把FormBean属?/p><p>d来,然后构造一个PO对象Q再调用业务层的Beanc,完成了注册操作,重定向到成功面。而业?/p><p>层Bean收到q个PO对象之后Q调用DAO接口ҎQ进行持久对象的持久化操作?</p><p>当用h询某个会员的信息的时候,他用全名q行查询Q于是Action得到一个UserNameFormBean包括?</p><p>个属性,分别是first name, middle name, last nameQ然后Action把UserNameFormBean?个属性读?/p><p>来,构造Name对象Q再调用业务BeanQ把Name对象传递给业务BeanQ进行查询?</p><p>业务Bean取得Name(注意: Name对象只是User的一个属?对象之后调用DAO接口Q返回一个User的PO对象</p><p>Q注意这个User不同于在Web层用的UserFormBeanQ他有很多集合属性滴。然后业务Bean把User对象q?/p><p>回给Action?</p><p>Action拿到User之后Q把User的基本属性取?集合属性如果不需要就免了)Q构造UserFormBeanQ然?/p><p>把UserFormBean request.setAttribute(...)Q然后重定向到查询结果页面?</p><p>查询面拿到request对象里面的ActionFormBeanQ自动调用tag昄之?</p><p>ȝQ?</p><p>FormBean是Web层的数据表示Q他不能被传递到业务层;PO是持久层的数据表C,在特定情况下Q例?/p><p>Hibernate中,他可以取代VO出现在业务层Q但是不POq是VO都必限制在业务层内使用Q最多到?/p><p>Web层的ControlQ绝不能被扩散到View厅R?</p><p>FormBean和PO之间的数据{化是在Action中进行滴?</p><p>BTW: </p><p>JDO1.xq不能像Hibernate功能q样强大QPO不能q持久层,所以必d业务层用VOQ因此必d?/p><p>务层q行大量的VO和PO的{化操作,相对于Hibernate来说Q编E比较烦琐?</p><p>当然咯,理论是一回事Q实际操作也不一定非要这样干Q你可以自行取舍Q在实际目中灵zM点,?/p><p>加一点bad smellQ提高开发效率。只不过在大型项目中最好还是严丝合~,不然的话Q改版的时候会?/p><p>苦的很滴?br />Q{载:<a >http://forum.hibernate.org.cn/viewtopic.php?t=627</a>Q?/p><img src ="http://www.tkk7.com/ltc603/aggbug/63888.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ltc603/" target="_blank">阿成</a> 2006-08-16 13:51 <a href="http://www.tkk7.com/ltc603/archive/2006/08/16/63888.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE WEBE序员应该掌握的技?/title><link>http://www.tkk7.com/ltc603/archive/2006/06/20/54027.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Tue, 20 Jun 2006 09:25:00 GMT</pubDate><guid>http://www.tkk7.com/ltc603/archive/2006/06/20/54027.html</guid><wfw:comment>http://www.tkk7.com/ltc603/comments/54027.html</wfw:comment><comments>http://www.tkk7.com/ltc603/archive/2006/06/20/54027.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ltc603/comments/commentRss/54027.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ltc603/services/trackbacks/54027.html</trackback:ping><description><![CDATA[html Qcss Qjs QjspQ?jdbcQ?sql Q会javaQ懂web框架Q?数据?,会用google<img src ="http://www.tkk7.com/ltc603/aggbug/54027.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ltc603/" target="_blank">阿成</a> 2006-06-20 17:25 <a href="http://www.tkk7.com/ltc603/archive/2006/06/20/54027.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学习使用J2EE Web应用的事件功?/title><link>http://www.tkk7.com/ltc603/archive/2006/01/25/29174.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Wed, 25 Jan 2006 02:11:00 GMT</pubDate><guid>http://www.tkk7.com/ltc603/archive/2006/01/25/29174.html</guid><wfw:comment>http://www.tkk7.com/ltc603/comments/29174.html</wfw:comment><comments>http://www.tkk7.com/ltc603/archive/2006/01/25/29174.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ltc603/comments/commentRss/29174.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ltc603/services/trackbacks/29174.html</trackback:ping><description><![CDATA[在Servlet2.3规范中,Web应用事g是新增加的部分。它让你能最大程度地控制你的Web应用。在本文中,我们学习两个很重要的应用事Ӟ<br /><br />应用的启动和停止<br /><br />Session的创建和失效如它们的名字那样Q应用启动事件发生在你的应用W一ơ被servlet容器装蝲和启动的时候;停止事g发生在Web应用停止的时候?br /><br />Session创徏事g发生在每ơ一个新的session创徏的时候,cM地Session失效事g发生在每ơ一个Session失效的时候。ؓ了用这些Web应用事gZ做些有用的事情,我们必须创徏和用一些特D的“监听”类。下面,我们研I这些监听类到地是什么以及我们如何去使用它们?br /><br />监听c:<br /><br />它们是实C下边两个接口中Q何一个接口的单的javac:<br /><br /><center><ccid_nobr></ccid_nobr><table bordercolordark="#ffffff" bordercolorlight="black" align="center" border="1" cellpadding="2" cellspacing="0" width="400"><tbody><tr><td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6"><pre><ccid_code>javax.servlet.ServletContextListener javax.servlet.http.HttpSessionListener</ccid_code></pre></td></tr></tbody></table></center><br /><br />如果你想让你的类监听应用的启动和停止事gQ你得实现ServletContextListener接口;如果你想让你的类ȝ听Session的创建和失效事gQ那你就得实现HttpSessionListener接口?让我们看看在q些接口中你必须要实现的Ҏ?<br /><br />1.ServletContextListener : <br /><br />接口包括如下两个ҎQ?<br /><br /><center><ccid_nobr></ccid_nobr><table bordercolordark="#ffffff" bordercolorlight="black" align="center" border="1" cellpadding="2" cellspacing="0" width="400"><tbody><tr><td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6"><pre><ccid_code>public void contextInitialized (ServletContextEvent sce); public void contextDestroyed (ServletContextEvent sce);</ccid_code></pre></td></tr></tbody></table></center><br /><br />如果你实C一个接口,那你必d现它所有的Ҏ。因此,如果你想利用应用的启动和停止事gQ你需要创Z个Javacdƈ实现ServletContextListener接口。下Ҏq样的一个类的例子: <br /><br /><center><ccid_nobr></ccid_nobr><table bordercolordark="#ffffff" bordercolorlight="black" align="center" border="1" cellpadding="2" cellspacing="0" width="400"><tbody><tr><td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6"><pre><ccid_code>/*File : ApplicationWatch.java*/ import javax.servlet.ServletContextListener; import javax.servlet.ServletContextEvent; public class ApplicationWatch implements ServletContextListener { public static long applicationInitialized = 0L; /* 应用启动事g */ public void contextInitialized (ServletContextEvent ce) { applicationInitialized = System.currentTimeMillis(); } /*应用停止事g */ public void contextDestroyed (ServletContextEvent ce) {} }</ccid_code></pre></td></tr></tbody></table></center><br /><br />在上边的代码中,ApplicationWatchcdCServletContextListener接口。它实现了接口中的两个方法,但只用了其中的一个方法,另一个方法中没有写Q何代码。这个类把应用启动的旉记录在一个可以从其它应用cM存取应用启动旉的public static变量中?<br /><br />我将很快解释如何告诉服务器我们有q个监听c,但首先让我们看看HttpSessionListener接口有什么不同的Ҏ?<br /><br />2.HttpSessionListener : <br /><br />q个接口也只包含两个ҎQ分别对应于Session的创建和失效Q?<br /><br /><center><ccid_nobr></ccid_nobr><table bordercolordark="#ffffff" bordercolorlight="black" align="center" border="1" cellpadding="2" cellspacing="0" width="400"><tbody><tr><td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6"><pre><ccid_code>public void sessionCreated (HttpSessionEvent se); public void sessionDestroyed (HttpSessionEvent se);</ccid_code></pre></td></tr></tbody></table></center><br /><br />如上边的ApplicationWatch例子那样Q我们也创徏了一个实现HttpSessionListener接口的类。如下: <br /><br /><center><ccid_nobr></ccid_nobr><table bordercolordark="#ffffff" bordercolorlight="black" align="center" border="1" cellpadding="2" cellspacing="0" width="400"><tbody><tr><td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6"><pre><ccid_code>/*File : SessionCounter.java*/ import javax.servlet.http.HttpSessionListener; import javax.servlet.http.HttpSessionEvent; public class SessionCounter implements HttpSessionListener { private static int activeSessions =0; /* Session创徏事g */ public void sessionCreated (HttpSessionEvent se) { activeSessions++; } /* Session失效事g */ public void sessionDestroyed (HttpSessionEvent se) { if(activeSessions>0)activeSessions--; } public static int getActiveSessions() { return activeSessions; } }</ccid_code></pre></td></tr></tbody></table></center><br /><br />在上边的代码中,SessionCountercdCHttpSessionListener接口Q其目的是计活动会话的数量?<br /><br />好了Q我们已l学习了什么是Web应用事gQ有什么接口可以用以及看到了一些实现这些接口的例子。让我们看看如何告诉应用服务器我们有q些监听cR?<br /><br />Web.xml : <br /><br />我们通过把类路径加入/WEB-INF/web.xml文g的标{?listener>中来告诉服务器我们的监听cR下Ҏ一个web.xml文g的例子: <br /><br /></listener><center><ccid_nobr></ccid_nobr><table bordercolordark="#ffffff" bordercolorlight="black" align="center" border="1" cellpadding="2" cellspacing="0" width="400"><tbody><tr><td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6"><pre><ccid_code><!-- Web.xml --> <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-appPUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3 //EN""http://java.sun.com /j2ee/dtds/web-app_2.3.dtd"> <web-app> <!-- Listeners --> <listener> <listener-class> com.stardeveloper.web.listener.SessionCounter </listener-class> </listener> <listener> <listener-class> com.stardeveloper.web.listener. ApplicationWatch</listener-class> </listener> </web-app></ccid_code></pre></td></tr></tbody></table></center><br /><br />如上所C,在web.xml文g中声明监听类是非常简单的。现在,每次的服务器的启动和停止Q会话的创徏和失效,配置好的监听cȝ相应的方法就会被调用?img src ="http://www.tkk7.com/ltc603/aggbug/29174.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ltc603/" target="_blank">阿成</a> 2006-01-25 10:11 <a href="http://www.tkk7.com/ltc603/archive/2006/01/25/29174.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts+Spring+Hibernate实现上传下蝲http://www.tkk7.com/ltc603/archive/2006/01/13/27966.html阿成阿成Fri, 13 Jan 2006 12:31:00 GMThttp://www.tkk7.com/ltc603/archive/2006/01/13/27966.htmlhttp://www.tkk7.com/ltc603/comments/27966.htmlhttp://www.tkk7.com/ltc603/archive/2006/01/13/27966.html#Feedback0http://www.tkk7.com/ltc603/comments/commentRss/27966.htmlhttp://www.tkk7.com/ltc603/services/trackbacks/27966.html 引言

  文g的上传和下蝲在J2EE~程已经是一个非常古老的话题了,也许您马上就能掰着指头数出好几个著名的大gQ如SmartUpload、Apache的FileUpload。但如果您的目是构建在Struts+Spring+HibernateQ以下称SSHQ框架上的,q些大g显得笨重而桑了QSSH提供了一个简h便的文g上传下蝲的方案,我们只需要通过一些配|ƈ辅以量的代码就可以完好解决q个问题了?br />
  本文围lSSH文g上传下蝲的主题,向您详细讲述如何开发基于SSH的WebE序。SSH各框架的均ؓ当前最新版本:

  ·Struts 1.2

  ·Spring 1.2.5

  ·Hibernate 3.0

  本文选用的数据库为Oracle 9iQ当然你可以在不改动代码的情况下Q通过配置文g的调整将其移植到MhBlob字段cd的数据库上,如MySQLQSQLServer{?br />
  M实现

  上传文g保存到T_FILE表中QT_FILE表结构如下:


?1 T_FILE表结?/div>

  其中Q?br />
  ·FILE_IDQ文件IDQ?2个字W,用Hibernate的uuid.hex法生成?br />
  ·FILE_NAMEQ文件名?br />
  ·FILE_CONTENTQ文件内容,对应Oracle的Blobcd?br />
  ·REMARKQ文件备注?br />
  文g数据存储在Blobcd的FILE_CONTENT表字D上Q在Spring中采用OracleLobHandler来处理Lob字段Q包括Clob和BlobQ,׃在程序中不需要引用到oracle数据驱动E序的具体类且屏蔽了不同数据库处理Lob字段Ҏ上的差别Q从而撤除程序在多数据库UL上的樊篱?

  1Q首先数据表中的Blob字段在Java领域对象中声明ؓbyte[]cdQ而非java.sql.Blobcd?br />
  2Q数据表Blob字段在Hibernate持久化映文件中的type为org.springframework.orm.hibernate3.support.BlobByteArrayTypeQ即Spring所提供的用戯定义的类型,而非java.sql.Blob?

  3Q在Spring中用org.springframework.jdbc.support.lob.OracleLobHandler处理Oracle数据库的Blobcd字段?br />
  通过q样的设|和配置Q我们就可以象持久化表的一般字D늱型一样处理Blob字段了?br />
  以上是SpringQHibernate文件二q制数据持久化到数据库的解决ҎQ而Struts通过表单中filecd的组件映ؓActionForm中类型ؓorg.apache.struts.upload. FormFile的属性来获取表单提交的文件数据?br />
  lg所qͼ我们可以通过?2Q描l出SSH处理文g上传的方案:


?2 SSH处理文g上传技术方?/div>

  文g上传的页面如?3所C:


?3 文g上传面

  文g下蝲的页面如?4所C:


?4 文g下蝲面

  该工E的资源l构如图 5所C:


?5 工程资源l构

  工程的类按SSH的层ơ结构划分ؓ数据持久层、业务层和Web层;WEB-INF下的applicationContext.xml为Spring的配|文Ӟstruts-config.xml为Struts的配|文Ӟfile-upload.jsp为文件上传页面,file-list.jsp为文件列表页面?br />
  本文后面的章节将从数据持久层-Q业务层-QWeb层的开发顺序,逐层讲解文g上传下蝲的开发过E?br />
  数据持久?/b>

  1、领域对象及映射文g

  您可以用Hibernate Middlegen、HIbernate Tools、Hibernate Syhchronizer{工h手工的方式,~写Hibernate的领域对象和映射文g。其中对应T_FILE表的领域对象Tfile.java为:

  代码 1 领域对象Tfile

1. package sshfile.model;
2. public class Tfile
3.{
4. private String fileId;
5. private String fileName;
6. private byte[] fileContent;
7. private String remark;
8. ?/getter and setter
9. }

  特别需要注意的是:数据库表为Blobcd的字D在Tfile中的fileContentcd为byte[]。Tfile的Hibernate映射文gTfile.hbm.xml攑֜Tfile .javacL件的相同目录下:

  代码 2 领域对象映射文g

1. Q?xml version="1.0"?Q?br />2. Q?DOCTYPE hibernate-mapping PUBLIC
3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" Q?br />5. Qhibernate-mappingQ?br />6. Qclass name="sshfile.model.Tfile" table="T_FILE"Q?br />7. Qid name="fileId" type="java.lang.String" column="FILE_ID"Q?br />8. Qgenerator class="uuid.hex"/Q?br />9. Q?idQ?br />10. Qproperty name="fileContent"
11. type="org.springframework.orm.hibernate3.support.BlobByteArrayType"
12. column="FILE_CONTENT" lazy="true"/Q?br />13. ?/其它一般字D늚映射
14. Q?classQ?br />15. Q?hibernate-mappingQ?/td>

  fileContent字段映射为Spring所提供的BlobByteArrayTypecdQBlobByteArrayType是用戯定义的数据类型,它实CHibernate 的org.hibernate.usertype.UserType接口。BlobByteArrayType使用从sessionFactory获取的Lob操作句柄lobHandlerbyte[]的数据保存到Blob数据库字D中。这P我们再没有必要通过编码的方式Q先insert然后再update来完成Blobcd数据的持久化Q这个原来难伺候的老爷l于被^民化了。关于lobHandler的配|请见本文后面的内容?br />
  此外lazy="true"说明地返回整个Tfile对象Ӟq不q回fileContentq个字段的数据,只有在显式调用tfile.getFileContent()Ҏ时才真正从数据库中获取fileContent的数据。这是Hibernate3引入的新Ҏ,对于包含重量U大数据的表字段Q这U抽取方式提高了对大字段操作的灵zL,否则加蝲Tfile对象的结果集时如果Lq回fileContentQ这U批量的数据抽取可以引h据库?z泛效应"?br />
  2、DAO~写和配|?br />
  Spring面向接口~程Q所以我们将所有对Tfile的数据操作的Ҏ定义在TfileDAO接口中,q些接口Ҏ分别是:

  ·findByFildId(String fileId)

  ·save(Tfile tfile)

  ·List findAll()

  TfileDAOHibernate提供了对TfileDAO接口ZHibernate的实玎ͼ如代?3所C:

  代码 3 ZHibernate 的fileDAO实现c?br />
1. package sshfile.dao;
2.
3. import sshfile.model.*;
4. import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
5. import java.util.List;
6.
7. public class TfileDAOHibernate
8. extends HibernateDaoSupport implements TfileDAO
9. {
10. public Tfile findByFildId(String fileId)
11. {
12. return (Tfile) getHibernateTemplate().get(Tfile.class, fileId);
13. }
14. public void save(Tfile tfile)
15. {
16. getHibernateTemplate().save(tfile);
17. getHibernateTemplate().flush();
18. }
19. public List findAll()
20. {
21. return getHibernateTemplate().loadAll(Tfile.class);
22. }
23. }

  TfileDAOHibernate通过扩展Spring提供的Hibernate支持cHibernateDaoSupport而徏立,HibernateDaoSupport装了HibernateTemplateQ而HibernateTemplate装了Hibernate所提供几乎所有的的数据操作方法,如execute(HibernateCallback action)Qload(Class entityClass, Serializable id)Qsave(final Object entity){等?br />
  所以我们的DAO只需要简单地调用父类的HibernateTemplate可以完成几乎所有的数据库操作了?br />
  ׃Spring通过代理Hibernate完成数据层的操作Q所以原Hibernate的配|文件hibernate.cfg.xml的信息也转移到Spring的配|文件中Q?br />
  代码 4 Spring中有关Hibernate的配|信?br />
1. QbeansQ?br />2. Q?-- 数据源的配置 //--Q?br />3. Qbean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
4. destroy-method="close"Q?br />5. Qproperty name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/Q?br />6. Qproperty name="url" value="jdbc:oracle:thin:@localhost:1521:ora9i"/Q?br />7. Qproperty name="username" value="test"/Q?br />8. Qproperty name="password" value="test"/Q?br />9. Q?beanQ?br />10. Q?-- Hibernate会话工厂配置 //--Q?br />11. Qbean id="sessionFactory"
12. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"Q?br />13. Qproperty name="dataSource" ref="dataSource"/Q?br />14. Qproperty name="mappingDirectoryLocations"Q?br />15. QlistQ?br />16. QvalueQclasspath:/sshfile/modelQ?valueQ?br />17. Q?listQ?br />18. Q?propertyQ?br />19. Qproperty name="hibernateProperties"Q?br />20. QpropsQ?br />21. Qprop key="hibernate.dialect"Qorg.hibernate.dialect.OracleDialectQ?propQ?br />22. Qprop key="hibernate.cglib.use_reflection_optimizer"QtrueQ?propQ?br />23. Q?propsQ?br />24. Q?propertyQ?br />25. Q?beanQ?br />26. Q?-- Hibernate 模板//--Q?br />27. Qbean id="hibernateTemplate"
28. class="org.springframework.orm.hibernate3.HibernateTemplate"Q?br />29. Qproperty name="sessionFactory" ref="sessionFactory"/Q?br />30. Q?beanQ?br />31. Q?--DAO配置 //--Q?br />32. Qbean id="tfileDAO" class="sshfile.dao.TfileDAOHibernate"Q?br />33. Qproperty name="hibernateTemplate" ref="hibernateTemplate" /Q?br />34. Q?beanQ?br />35. ?br />36. Q?beansQ?/td>

  W?~9行定义了一个数据源Q其实现cLapache的BasicDataSourceQ第11~25行定义了Hibernate的会话工厂,会话工厂cȝSpring提供的LocalSessionFactoryBeanl护Q它注入了数据源和资源映文Ӟ此外q通过一些键值对讄了Hibernate所需的属性?br />
  其中W?6行通过c\径的映射方式Q将sshfile.modelcd目录下的所有领域对象的映射文g装蝲q来Q在本文的例子里Q它装载进Tfile.hbm.xml映射文g。如果有多个映射文g需要声明,使用c\径映方式显然比直接单独指定映射文g名的方式要简ѝ?

  W?7~30行定义了Spring代理Hibernate数据操作的HibernateTemplate模板Q而第32~34行将该模板注入到tfileDAO中?br />
  需要指定的是Spring 1.2.5提供了两套Hibernate的支持包Q其中Hibernate 2相关的封装类位于org.springframework.orm.hibernate2.*包中Q而Hibernate 3.0的封装类位于org.springframework.orm.hibernate3.*包中Q需要根据您所选用Hibernate版本q行正确选择?br />
  3、Lob字段处理的配|?br />
  我们前面已经指出Oracle的Lob字段和一般类型的字段在操作上有一个明昄区别--那就是你必须首先通过Oracle的empty_blob()/empty_clob()初始化Lob字段Q然后获取该字段的引用,通过q个引用更改其倹{所以要完成对Lob字段的操作,Hibernate必须执行两步数据库访问操作,先Insert再Update?br />
  使用BlobByteArrayType字段cd后,Z么我们就可以象一般的字段cd一h作Blob字段呢?可以定的一ҎQBlobByteArrayType不可能逾越Blob天生的操作方式,原来是BlobByteArrayType数据cd本n具体数据讉K的功能,它通过LobHandler两ơ数据访问的动作隐藏hQBlob字段的操作在表现上和其他一般字D业cd无异Q所以LobHandlerx那个"苦了我一个,q福十亿?的那位幕后英雄?br />
  LobHandler必须注入到Hibernate会话工厂sessionFactory中,因ؓsessionFactory负责产生与数据库交互的Session。LobHandler的配|如代码 5所C:

  代码 5 Lob字段的处理句柄配|?br />
1. QbeansQ?br />2. ?br />3. Qbean id="nativeJdbcExtractor"
4. class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"
5. lazy-init="true"/Q?br />6. Qbean id="lobHandler"
7. class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true"Q?br />8. Qproperty name="nativeJdbcExtractor"Q?br />9. Qref local="nativeJdbcExtractor"/Q?br />10. Q?propertyQ?br />11. Q?beanQ?br />12. ?br />13. Q?beansQ?/td>

  首先Q必d义一个能够从q接池中抽取出本地数据库JDBC对象Q如OracleConnectionQOracleResultSet{)的抽取器QnativeJdbcExtractorQ这h可以执行一些特定数据库的操作。对于那些仅装了Connection而未包括Statement的简单数据连接池QSimpleNativeJdbcExtractor是效率最高的抽取器实现类Q但具体到apache的BasicDataSourceq接池,它封装了所有JDBC的对象,q时需要用CommonsDbcpNativeJdbcExtractor了。Spring针对几个著名的Web服务器的数据源提供了相应的JDBC抽取器:

  ·WebLogicQWebLogicNativeJdbcExtractor

  ·WebSphereQWebSphereNativeJdbcExtractor

  ·JBossQJBossNativeJdbcExtractor

  在定义了JDBC抽取器后Q再定义lobHandler。Spring 1.2.5提供了两个lobHandlerQ?br />
  ·DefaultLobHandlerQ适用于大部分的数据库Q如SqlServerQMySQLQ对Oracle 10g也适用Q但不适用于Oracle 9iQ看来Oracle 9i实是个怪胎Q谁叫Oracle 公司自己都说Oracle 9i是一个过渡性的产品呢)?br />
  ·OracleLobHandlerQ适用于Oracle 9i和Oracle 10g?br />
  ׃我们的数据库是Oracle9iQ所以用OracleLobHandler?br />
  在配|完LobHandler后, q需要将其注入到sessionFactory的Bean中,下面是调用后的sessionFactory Bean的配|:

  代码 6 lobHandler注入到sessionFactory中的配置

1. QbeansQ?br />2. ?br />3. Qbean id="sessionFactory"
4. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"Q?br />5. Qproperty name="dataSource" ref="dataSource"/Q?br />6. Q?-- 为处理Blobcd字段的句柄声?//--Q?br />7. Qproperty name="lobHandler" ref="lobHandler"/Q?br />8. ?br />9. Q?beanQ?br />10. ?br />11. Q?beansQ?/td>

  如第7所C,通过sessionFactory的lobHandler属性进行注入?br />
  业务?/b>

  1、业务层接口

  "面向接口而非面向cȝE?是Spring不遗余力所推荐的编E原则,q条原则也已lؓ大部开发者所接受Q此外,JDK的动态代理只Ҏ口有效,否则必须使用CGLIB生成目标cȝ子类。我们依从于Spring的倡导Z务类定义一个接口:

  代码 7 业务层操作接?br />
1. public interface FileService
2. {
3. void save(FileActionForm fileForm);//提交的上传文g保存到数据表?br />4. List getAllFile();//得到T_FILE所C?br />5. void write(OutputStream os,String fileId);//某个文件的文g数据写出到输出流?br />6. String getFileName(String fileId);//获取文g?br />7. }

  其中save(FileActionForm fileForm)ҎQ将装在fileForm中的上传文g保存到数据库中,q里我们使用FileActionForm作ؓҎ入参QFileActionForm是Web层的表单数据对象Q它装了提交表单的数据。将FileActionForm直接作ؓ业务层的接口入参Q相当于Web层传播到业务层中去,卛_业务层绑定在特定的Web层实现技术中Q按照分层模型学院派的观点,q是一U反模块化的设计Q但?一?的业务系lƈ无需提供多种UI界面Q系lWeb层将来切换到另一U实现技术的可能性也微乎其微Q所以笔者觉得没有必要ؓ了这个业务层完全独立于调用层的过高目标而去搞一个额外的隔离层,费了原材料不说Q还系l搞得过于复杂,相比于其它原则,"?始终是最大的一条原则?br />
  getAllFile()负责获取T_FILE表所有记录,以便在网上昄出来?br />
  而getFileName(String fileId)和write(OutputStream os,String fileId)则用于下载某个特定的文g。具体的调用是将Web层将response.getOutputStream()传给write(OutputStream os,String fileId)接口Q业务层直接文件数据输出到q个响应中。具体实现请参见错误Q未扑ֈ引用源。节下蝲文g部分?br />
  2、业务层接口实现c?br />
  FileService的实现类为FileServiceImplQ其中save(FileActionForm fileForm)的实现如下所C:

  代码 8 业务接口实现cMsave()

1. ?br />2. public class FileServiceImpl
3. implements FileService
4. {
5. private TfileDAO tfileDAO;
6. public void save(FileActionForm fileForm)
7. {
8. Tfile tfile = new Tfile();
9. try
10. {
11. tfile.setFileContent(fileForm.getFileContent().getFileData());
12. }
13. catch (FileNotFoundException ex)
14. {
15. throw new RuntimeException(ex);
16. }
17. catch (IOException ex)
18. {
19. throw new RuntimeException(ex);
20. }
21. tfile.setFileName(fileForm.getFileContent().getFileName());
22. tfile.setRemark(fileForm.getRemark());
23. tfileDAO.save(tfile);
24. }
25. ?br />26. }

  在save(FileActionForm fileForm)Ҏ里,完成两个步骤Q?br />
  其一Q象在水桉倒水一PFileActionForm对象中的数据倒入到Tfile对象中;

  其二Q调用TfileDAO保存数据?br />
  需要特别注意的是代码的W?1行,FileActionForm的fileContent属性ؓorg.apache.struts.upload.FormFilecdQFormFile提供了一个方便的ҎgetFileData()Q即可获取文件的二进制数据。通过解读FormFile接口实现cDiskFile的原码,我们可能知道FormFile本nq不~存文g的数据,只有实际调用getFileData()Ӟ才从盘文g输入中获取数据。由于FormFile使用读取方式获取数据,本n没有~存文g的所有数据,所以对于上传超大体U的文gQ也是没有问题的Q但是,׃数据持久层的Tfile使用byte[]来缓存文件的数据Q所以ƈ不适合处理大体积的文Ӟ?00MQ,对于大体积的文Ӟ依然需要用java.sql.Blobcd以常规流操作的方式来处理?br />
  此外Q通过FileForm的getFileName()Ҏ可以获得上传文件的文g名,如第21行代码所C?br />
  write(OutputStream os,String fileId)Ҏ的实玎ͼ如代?9所C:

  代码 9 业务接口实现cMwrite()

1. ?br />2. public class FileServiceImpl
3. implements FileService
4. {
5.
6. public void write(OutputStream os, String fileId)
7. {
8. Tfile tfile = tfileDAO.findByFildId(fileId);
9. try
10. {
11. os.write(tfile.getFileContent());
12. os.flush();
13. }
14. catch (IOException ex)
15. {
16. throw new RuntimeException(ex);
17. }
18. }
19. ?br />20. }

  write(OutputStream os,String fileId)也简单地分ؓ两个操作步骤Q首先,ҎfileId加蝲表记录,然后fileContent写入到输出流中?br />
  3、Spring事务配置

  下面Q我们来看如何在Spring配置文g中ؓFileService配置声明性的事务

1. QbeansQ?br />2. ?
3. Qbean id="transactionManager"
4. class="org.springframework.orm.hibernate3.HibernateTransactionManager"Q?br />5. Qproperty name="sessionFactory" ref="sessionFactory"/Q?br />6. Q?beanQ?br />7. Q?-- 事务处理的AOP配置 //--Q?br />8. Qbean id="txProxyTemplate" abstract="true"
9. class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"Q?br />10. Qproperty name="transactionManager" ref="transactionManager"/Q?br />11. Qproperty name="transactionAttributes"Q?br />12. QpropsQ?br />13. Qprop key="get*"QPROPAGATION_REQUIRED,readOnlyQ?propQ?br />14. Qprop key="find*"QPROPAGATION_REQUIRED,readOnlyQ?propQ?br />15. Qprop key="save"QPROPAGATION_REQUIREDQ?propQ?br />16. Qprop key="write"QPROPAGATION_REQUIRED,readOnlyQ?propQ?br />17. Q?propsQ?br />18. Q?propertyQ?br />19. Q?beanQ?br />20. Qbean id="fileService" parent="txProxyTemplate"Q?br />21. Qproperty name="target"Q?br />22. Qbean class="sshfile.service.FileServiceImpl"Q?br />23. Qproperty name="tfileDAO" ref="tfileDAO"/Q?br />24. Q?beanQ?br />25. Q?propertyQ?br />26. Q?beanQ?br />27. Q?beansQ?/td>

  Spring的事务配|包括两个部分:

  其一Q定义事务管理器transactionManagerQ用HibernateTransactionManager实现事务理Q?br />
  其二Q对各个业务接口q行定义Q其实txProxyTemplate和fileService是父子节点的关系Q本来可以将txProxyTemplate定义的内容合q到fileService中一起定义,׃我们的系l仅有一个业务接口需要定义,所以将其定义的一部分抽象到父节点txProxyTemplate中意义确实不大,但是对于真实的系l,往往拥有为数众多的业务接口需要定义,这些业务接口定义内容的共同部分抽取C个父节点中,然后在子节点中通过parentq行兌Q就可以大大化业务接口的配置了?br />
  父节点txProxyTemplate注入了事务管理器Q此外还定义了业务接口事务管理的ҎQ允讔R过通配W的方式q行匚w声明Q如前两个接口方法)Q有些接口方法仅Ҏ据进行读操作Q而另一些接口方法需要涉及到数据的更攏V对于前者,可以通过readOnly标识出来Q这h利于操作性能的提高,需要注意的是由于父c节点定义的Bean仅是子节炚w|信息的抽象Qƈ不能具体实现化一个Bean对象Q所以需要特别标注ؓabstract="true"Q如W?行所C?br />
  fileService作ؓ一个目标类被注入到事务代理器中Q而fileService实现cL需要的tfileDAO实例Q通过引用3.2节中定义的tfileDAO Bean注入?br />
  Web层实?/b>

  1、Web层的构g和交互流E?br />
  Web层包括主?个功能:

  ·上传文g?br />
  ·列出所有已l上传的文g列表Q以供点M载?br />
  ·下蝲文g?br />
  Web层实现构件包括与2个JSP面Q?个ActionForm及一个ActionQ?br />
  ·file-upload.jspQ上传文件的面?br />
  ·file-list.jspQ已l上传文件的列表面?br />
  ·FileActionFormQfile-upload.jsp面表单对应的ActionForm?br />
  ·FileActionQ承org.apache.struts.actions.DispatchAction的ActionQ这栯个Action可以通过一个URL参数区分中响应不同的h?br />
  Web层的q些构g的交互流E如?6所C:


?6 Web层Struts程?/div>

  其中Q在执行文g上传的请求时QFileAction在执行文件上传后Qforward到loadAllFile出口中,loadAllFile加蝲数据库中所有已l上传的记录Q然后forward到名为fileListPage的出口中Q调用file-list.jsp面昄已经上传的记录?br />
  2、FileAction功能

  Struts 1.0的Action有一个弱:一个Action只能处理一U请求,Struts 1.1中引入了一个DispatchActionQ允讔R过URL参数指定调用Action中的某个ҎQ如http://yourwebsite/fileAction.do?method=upload卌用FileAction中的uploadҎ。通过q种方式Q我们就可以一些相关的h集中C个Action当中~写Q而没有必要ؓ某个h操作~写一个ActioncR但是参数名是要在struts-config.xml中配|的Q?br />
1. Qstruts-configQ?br />2. Qform-beansQ?br />3. Qform-bean name="fileActionForm" type="sshfile.web.FileActionForm" /Q?br />4. Q?form-beansQ?br />5. Qaction-mappingsQ?br />6. Qaction name="fileActionForm" parameter="method" path="/fileAction"
7. type="sshfile.web.FileAction"Q?br />8. Qforward name="fileListPage" path="/file-list.jsp" /Q?br />9. Qforward name="loadAllFile" path="/fileAction.do?method=listAllFile" /Q?br />10. Q?actionQ?br />11. Q?action-mappingsQ?br />12. Q?struts-configQ?/td>

  W?行的parameter="method"指定了承载方法名的参敎ͼW?行中Q我们还配置了一个调用FileAction不同Ҏ的Action出口?br />
  FileAction共有3个请求响应的ҎQ它们分别是Q?br />
  ·upload(?Q处理上传文件的h?br />
  ·listAllFile(?Q处理加载数据库表中所有记录的h?br />
  ·downloadQ…)Q处理下载文件的h?br />
  下面我们分别对这3个请求处理方法进行讲解?br />
  2.1 上传文g

  上传文g的请求处理方法非常简单,之言之,是从Spring容器中获取业务层处理cFileServiceQ调用其save(FileActionForm form)Ҏ上传文gQ如下所C:

1. public class FileAction
2. extends DispatchAction
3. {
4. //上传文件保存到数据库中
5. public ActionForward upload(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. {
9. FileActionForm fileForm = (FileActionForm) form;
10. FileService fileService = getFileService();
11. fileService.save(fileForm);
12. return mapping.findForward("loadAllFile");
13. }
14. //从Spring容器中获取FileService对象
15. private FileService getFileService()
16. {
17. ApplicationContext appContext = WebApplicationContextUtils.
18. getWebApplicationContext(this.getServlet().getServletContext());
19. return (FileService) appContext.getBean("fileService");
20. }
21. ?br />22. }

  ׃FileAction其它两个h处理Ҏ也需要从Spring容器中获取FileService实例Q所以我们特别提供了一个getFileService()ҎQ第15~21行)。重构的一条原则就是:"发现代码中有重复的表辑ּQ将其提取ؓ一个变量;发现cM有重复的代码D,其提取Z个方法;发现不同cM有相同的ҎQ将其提取ؓ一个类"。在真实的系l中Q往往拥有多个Action和多个Servicec,q时一个比较好的设|思\是,提供一个获取所有Service实现对象的工LQ这样就可以Spring 的Service配置信息屏蔽在一个类中,否则Service的配|名字散落在E序各处Q维护性是很差的?br />
  2.2 列出所有已l上传的文g

  listAllFileҎ调用Servie层方法加载T_FILE表中所有记录,q将其保存在Request域中Q然后forward到列表页面中Q?br />
1. public class FileAction
2. extends DispatchAction
3. {
4. ?br />5. public ActionForward listAllFile(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. throws ModuleException
9. {
10. FileService fileService = getFileService();
11. List fileList = fileService.getAllFile();
12. request.setAttribute("fileList",fileList);
13. return mapping.findForward("fileListPage");
14. }
15. }

  file-list.jsp面使用Struts标签展示Z存在Request域中的记录:

1. Q?@page contentType="text/html; charset=GBK"%Q?br />2. Q?@taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%Q?br />3. Q?@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%Q?br />4. QhtmlQ?br />5. QheadQ?br />6. QtitleQfile-downloadQ?titleQ?br />7. Q?headQ?br />8. Qbody bgcolor="#ffffff"Q?br />9. QolQ?br />10. Qlogic:iterate id="item" name="fileList" scope="request"Q?br />11. QliQ?br />12. Qa href='fileAction.do?method=download&fileId=
13. Qbean:write name="item"property="fileId"/Q?Q?br />14. Qbean:write name="item" property="fileName"/Q?br />15. Q?aQ?br />16. Q?liQ?br />17. Q?logic:iterateQ?br />18. Q?olQ?br />19. Q?bodyQ?br />20. Q?htmlQ?/td>

  展现面的每条记录挂接着一个链接地址QŞ如:fileAction.do?method=download&fileId=xxxQmethod参数指定了这个请求由FileAction的downloadҎ来响应,fileId指定了记录的主键?br />
  ׃在FileActionForm中,我们定义了fileId的属性,所以在download响应Ҏ中,我们可以从FileActionForm中取得fileId的倹{这里涉及到一个处理多个请求Action所对应的ActionForm的设计问题,׃原来的Action只能对应一个请求,那么原来的ActionForm非常单,它仅需要将q个h的参数项作ؓ其属性就可以了,但现在一个Action对应多个hQ每个请求所对应的参数项是不一LQ此时的ActionForm的属性就必须是多h参数的q了。所以,除了文g上传h所对应的fileContent和remark属性外q包括文件下载的fileId属性:


?7 FileActionForm

  当然q样会造成属性的冗余Q比如在文g上传的请求中Q只会用到fileContent和remark属性,而在文g下蝲的请求时Q只会用到fileId属性。但q种冗余是会带来好处?-它得一个Action可以处理多个h?br />
  2.3 下蝲文g

  在列表页面中点击一个文件下载,其请求由FileAction的downloadҎ来响应,downloadҎ调用业务层的FileServiceҎQ获取文件数据ƈ写出到response的响应流中。通过合理讄HTTP响应头参敎ͼ响应流在客L表现Z个下载文件对话框Q其代码如下所C:

  代码 10 业务接口实现cMdownload

1. public class FileAction
2. extends DispatchAction
3. {
4. ?br />5. public ActionForward download(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. throws ModuleException
9. {
10. FileActionForm fileForm = (FileActionForm) form;
11. FileService fileService = getFileService();
12. String fileName = fileService.getFileName(fileForm.getFileId());
13. try
14. {
15. response.setContentType("application/x-msdownload");
16. response.setHeader("Content-Disposition",
17. "attachment;" + " filename="+
18. new String(fileName.getBytes(), "ISO-8859-1"));
19. fileService.write(response.getOutputStream(), fileForm.getFileId());
20. }
21. catch (Exception e)
22. {
23. throw new ModuleException(e.getMessage());
24. }
25. return null;
26. }
27. }

  W?5~18行,讄HTTP响应_响应类型设|ؓapplication/x-msdownload MIMEcdQ则响应在IE中将弹出一个文件下载的对话框,如图 4所C。IE所支持的MIMEcd多达26U,您可以通过q个|址查看其他的MIMEcdQ?br />
http://msdn.microsoft.com/workshop/networking/moniker/overview/appendix_a.asp?br />
  如果下蝲文g的文件名含有中文字符Q如果不对其q行编码,如第18行所C,客户文g下蝲对话框中出现的文件名会发生q?br />W?9行代码获得response的输出流Q作为FileServie write(OutputStream os,String fileId)的入参,q样文g的内容将写到response的输出流中?br />
  3、web.xml文g的配|?br />
  Spring容器在何时启动呢Q我可以在Web容器初始化来执行启动Spring容器的操作,Spring提供了两U方式启动的ҎQ?br />
  ·通过org.springframework.web.context .ContextLoaderListener容器监听器,在Web容器初始化时触发初始化Spring容器Q在web.xml中通过QlistenerQ</listenerQ对其进行配|?br />
  ·通过Servlet org.springframework.web.context.ContextLoaderServletQ将光|ؓ自动启动的ServletQ在Web容器初始化时Q通过q个Servlet启动Spring容器?br />
  在初始化Spring容器之前Q必d初始化log4J的引擎,Spring也提供了容器监听器和自动启动Servlet两种方式对log4J引擎q行初始化:

  ·org.springframework.web.util .Log4jConfigListener

  ·org.springframework.web.util.Log4jConfigServlet

  下面我们来说明如何配|web.xml启动Spring容器Q?br />
  代码 11 web.xml中对应Spring的配|内?br />
1. Qweb-appQ?br />2. Qcontext-paramQ?br />3. Qparam-nameQcontextConfigLocationQ?param-nameQ?br />4. Qparam-valueQ?WEB-INF/applicationContext.xmlQ?param-valueQ?br />5. Q?context-paramQ?br />6. Qcontext-paramQ?br />7. Qparam-nameQlog4jConfigLocationQ?param-nameQ?br />8. Qparam-valueQ?WEB-INF/log4j.propertiesQ?param-valueQ?br />9. Q?context-paramQ?br />10. QservletQ?br />11. Qservlet-nameQlog4jInitServletQ?servlet-nameQ?br />12. Qservlet-classQorg.springframework.web.util.Log4jConfigServletQ?servlet-classQ?br />13. Qload-on-startupQ?Q?load-on-startupQ?br />14. Q?servletQ?br />15. QservletQ?br />16. Qservlet-nameQspringInitServletQ?servlet-nameQ?br />17. Qservlet-classQorg.springframework.web.context.ContextLoaderServletQ?servlet-classQ?br />18. Qload-on-startupQ?Q?load-on-startupQ?br />19. Q?servletQ?br />20. ?br />21. Q?web-appQ?/td>

  启动Spring容器Ӟ需要得C个信息:Spring配置文g的地址和Log4J属性文Ӟq两上信息分别通过contextConfigLocationWeb和log4jConfigLocation容器参数指定Q如果有多个Spring配置文gQ则用逗号隔开Q如Q?br />
/WEB-INF/applicationContext_1.xml, /WEB-INF/applicationContext_1.xm2

  ׃在启动ContextLoaderServlet之前Q必M先初始化Log4J的引擎,所以Log4jConfigServlet必须在ContextLoaderServlet之前启动Q这通过Qload-on-startupQ来指定它们启动的先后顺序?br />
  q是开发Web应用E序一个比较老套又常见问题,׃不同Web应用服务器的默认~码是不一LQؓ了方便Web应用在不同的Web应用服务器上ULQ最好的做法是WebE序自n来处理编码{换的工作。经典的作法是在web.xml中配|一个编码{换过滤器QSpring提供了一个编码过滤器cCharacterEncodingFilterQ下面,我们为应用配|上q个qo器:

1. Qweb-appQ?br />2. ?br />3. QfilterQ?br />4. Qfilter-nameQencodingFilterQ?filter-nameQ?br />5. Qfilter-classQorg.springframework.web.filter.CharacterEncodingFilterQ?filter-classQ?br />6. Qinit-paramQ?br />7. Qparam-nameQencodingQ?param-nameQ?br />8. Qparam-valueQGBKQ?param-valueQ?br />9. Q?init-paramQ?br />10. Q?filterQ?br />11. Qfilter-mappingQ?br />12. Qfilter-nameQencodingFilterQ?filter-nameQ?br />13. Qurl-patternQ?*Q?url-patternQ?br />14. Q?filter-mappingQ?br />15. ?br />16. Q?web-appQ?/td>

  Spring的过滤器cLorg.springframework.web.filter.CharacterEncodingFilterQ通过encoding参数指定~码转换cd为GBKQ<filter-mappingQ的配置使该qo器截h有的L?br />
  Struts的框架也需要在web.xml中配|,惛_读者朋友对Struts的配|都很熟悉,故在此不再提及,请参见本文所提供的源码?br />
  ȝ

  本文通过一个文件上传下载的Web应用Q讲解了如何构徏ZSSH的Web应用Q通过Struts和FormFileQSpring的LobHandler以及Spring为HibernateBlob处理所提供的用LBlobByteArrayType Q实C传和下蝲文g的功能仅需要廖廖数行的代码卛_完成。读者只需对程序作E许的调_卛_处理Clob字段Q?br />
  ·领域对象对应Clob字段的属性声明ؓStringcdQ?br />
  ·映射文g对应Clob字段的属性声明ؓorg.springframework.orm.hibernate3.support.ClobStringTypecd?br />
  本文通过SSHҎ件上传下载简捷完的实现得以中Hv了解SSH强强联合构徏Web应用的强大优ѝ在行文中,q穿插了一些分层的设计l验Q配|技巧和Spring所提供的方便类Q相信这些知识对您的开发都有所裨益?img src ="http://www.tkk7.com/ltc603/aggbug/27966.html" width = "1" height = "1" />

阿成 2006-01-13 20:31 发表评论
]]>
用struts+spring+hibernatel装web应用http://www.tkk7.com/ltc603/archive/2006/01/12/27763.html阿成阿成Thu, 12 Jan 2006 07:20:00 GMThttp://www.tkk7.com/ltc603/archive/2006/01/12/27763.htmlhttp://www.tkk7.com/ltc603/comments/27763.htmlhttp://www.tkk7.com/ltc603/archive/2006/01/12/27763.html#Feedback0http://www.tkk7.com/ltc603/comments/commentRss/27763.htmlhttp://www.tkk7.com/ltc603/services/trackbacks/27763.html
  当ؓ你的web应用E序创徏一个构架时Q需要涉及到相当多的问题。幸q的是,已经有不开发者已l遇到过q类重复发生的问题,q且建立了处理这c问题的框架。一个好框架具备以下几点Q?减轻开发者处理复杂的问题的负担(“不重复发明轮子”)Q内部定义ؓ可扩展的Q有一个强大的用户支持。框枉常能够很好的解决一斚w的问题。然而,你的应用E序有几个层可能都需要它们各自的框架。就如解决你的用h口(UIQ问题时你就不应该把事务逻辑和持久化逻辑掺杂q来。例如,你不应该在控制器QcontrollerQ里面写jdbc代码Q它包含有业务逻辑Q这不是控制器应该提供的功能。它应该是轻量的,代理来自用户接口QUIQ外的调用请求给其它服务于这些请求的应用层。好的框架自然的形成代码如何分布的指对{更重要的是Q框架减d发者从头开始写像持久层q样的代码的痛苦Q他们专注于对客户来说很重要的应用逻辑?

  q篇文章讨论怎样l合几个著名的框架去做到松耦合的目的,怎样建立你的构架Q怎样让你的各个应用层保持一致。富于挑战的是:l合q些框架使得每一层都以一U松耦合的方式彼此沟通,而与底层的技术无兟뀂这文章将使用Q种行的开源框架来讨论l合框架的策略。表现层我们用StrutsQhttp://jakarta.apache.org/strutsQ?业务层我们将使用SpringQhttp://www.springframework.org/Q?持久层用HibrenateQhttp://www.hibernate.org/Q?你也可以在你的应用程序中替换q些框架中的M一U而得到同L效果。图Q展CZ当这些框架组合在一h从高层看是什么样子?


图1用Struts, Spring, ?Hibernate框架构徏的概?

应用E序的分?QApplication LayeringQ?

大多C复杂的web应用都能被分成至4个各负其责的层次。这些层ơ是Q表现层QpresentationQ、持久层QpersistenceQ、业务层QbusinessQ、领域模型层Qdomain modelQ。每层在应用E序中都有明的责QQ不应该和其它层h功能。每一应用层应该彼此独立但要给他们之间放一个通讯接口。让我们从审视各个层开始,讨论q些层应该提供什么和不应该提供什么?

表现?(The Presentation Layer)

  在一个典型的web应用的一端是表现层。很多Java开发者也理解Struts所提供的。然而,太常见的是,他们把像业务逻辑之类的耦合的代码放q了一个org.apache.struts.Action。所以,让我们在像Strutsq样一个框架应该提供什么上取得一致意见。这儿是Struts负责的:

为用L理请求和响应Q?
提供一个控制器QcontrollerQ代理调用业务逻辑和其它上层处理;
处理从其它层掷出l一个Struts Action的异常;
为显C提供一个模型;
执行用户接口QUIQ验证?

q儿是一些经常用Struts~写的但是却不应该和Struts表现层相伴的目Q?
直接和数据库通讯Q比如JDBC调用Q?
业务逻辑和与你的应用E序相关的验证;
事务理Q?
在表现层中引入这U代码将D典型耦合Qtype couplingQ和讨厌的维护?

持久?QThe Persistence Layer Q?

  在典型web应用的另一端是持久层。这通常是事情q速失控的地方。开发者低C构徏他们自己的持久层框架的挑战性。一般来_机构内部自己写的持久层不仅需要大量的开发时_而且q经常缺功能和变得难以控制。有几个开源的“对象-关系映射”(ORMQ框枉常解决问题。尤其是QHibernate框架为java提供了"对象Q关pL久化Q(object-to-relational persistenceQ机制和查询服务。Hibernate寚w些已l熟悉了SQL和JDBC API的Java开发者有一个适中的学习曲UѝHibernate持久对象是基于简单旧式Java对象QPOJOQ和Java集合QJava collectionsQ。此外,使用Hibernateq不妨碍你正在用的IDE。下面的列表包含了你该写在一个持久层框架里的代码cdQ?

  查询相关的信息成为对象。Hibernate通过一U叫作HQL的面向对象(OOQ的查询语言或者用条件表辑ּAPIQexpressive criteria APIQ来做这个事情?HQL非常cM于SQL-- 只是把SQL里的table和columns用Object和它的fields代替。有一些新的专用的HQL语言成分要学Q不q,它们Ҏ理解而且文档做得好。HQL是一U用来查询对象的自然语aQ花很小的代价就能学习它?

保存、更新、删除储存在数据库中的信息?

像Hibernateq样的高U“对象-关系”映(object-to-relational mappingQ框架提供对大多CSQL数据库的支持Q它们支持“父/子”(parent/childQ关pR事务处理、承和多态?


q儿是一些应该在持久层里被避免的目Q?


业务逻辑应该在你的应用的一个高一些的层次里。持久层里仅仅允许数据存取操作?

  你不应该把持久层逻辑Qpersistence logicQ和你的表现层逻辑Qpresentation logicQ搅在一赗避免像JSPs或基于servlet的类q些表现层组仉的逻辑和数据存取直接通讯。通过把持久层逻辑隔离q它自己的层Q应用程序变得易于修改而不会媄响在其它层的代码。例如:Hebernate能够被其它持久层框架或者API代替而不会修改在其它M层的代码?


业务层(The Business LayerQ?

  在一个典型的web应用E序的中间的lg是业务层或服务层。从~码的视角来看,q个服务层是最Ҏ被忽视的一层。不隑֜用户接口QUIQ层或者持久层里找到散布在其中的这U类型的代码。这不是正确的地方,因ؓq导致了应用E序的紧耦合Q这样一来,随着旉推移代码很隄护。幸好,针对q一问题有好几种Frameworks存在。在q个领域两个最行的框架是Spring和PicoContainerQ它们叫作微容器QmicrocontainersQ,你可以不费力不费的把你的对象连在一赗所有这些框枉工作在一个简单的叫作“依赖注入”(dependency injectionQ(也通称“控制反转”(inversion of controlQ)的概念上。这文章将着gSpring的ؓ指定的配|参数通过bean属性的setter注入Qsetter injectionQ的使用。Spring也提供了一个构建器注入Qconstructor injectionQ的复杂形式作ؓsetter注入的一个替代。对象们被一个简单的XML文gq在一Pq个XML文g含有到像事务理器(transaction management handlerQ、对象工厂(object factoriesQ、包含业务逻辑的服务对象(service objectsQ、和数据存取对象QDAOQ这些对象的引用QreferencesQ?

  q篇文章的后面将用例子来把Spring使用q些概念的方法说得更清楚一些。业务层应该负责下面q些事情Q?
处理应用E序的业务逻辑和业务验证;
理事务Q?
预留和其它层交互的接口;
理业务层对象之间的依赖Q?
增加在表现层和持久层之间的灵zL,使它们互不直接通讯Q?
从表现层中提供一个上下文QcontextQ给业务层获得业务服务(business services Q;
理从业务逻辑到持久层的实现?

领域模型?QThe Domain Model Layer Q?
  最后,因ؓ我们讨论的是一个不是很复杂的、基于web的应用程序,我们需要一l能在不同的层之间移动的对象。领域对象层由那些代表现实世界中的业务对象的对象们组成,比如Q一份订单(OrderQ、订单项QOrderLineItemQ、品(ProductQ等{。这个层让开发者停止徏立和l护不必要的数据传输对象Q或者叫作DTOsQ?来匹配他们的领域对象。例如,Hibernate允许你把数据库信息读q领域对象(domain objectsQ的一个对象图Q这样你可以在连接断开的情况下把这些数据显C到UI层。那些对象也能被更新和送回到持久层q在数据库里更新。而且Q你不必把对象{化成DTOsQ因为DTOs在不同的应用层间UdQ可能在转换中丢失。这个模型得Java开发者自然地以一U面向对象的风格和对象打交道Q没有附加的~码?

l合一个简单的例子

  既然我们已经从一个高的层ơ上理解了这些组Ӟ 现在p我们开始实践吧。在q个例子中,我们q是合qStruts、Spring、Hibernate框架。每一个这些框架在一文章中都有太多的细节覆盖到。这文章将用一个简单的例子代码展示怎样把它们结合在一P而不是进入每个框架的许多l节。示例应用程序将C一个请求怎样跨越每一层被服务的。这个示例应用程序的一个用戯保存一个订单到数据库中和查看一个在数据库中存在的订单。进一步的增强可以使用h新或删除一个存在的订单。  

你可以下载这个应用的源码Qhttp://www.onjava.com/onjava/2004/04/07/examples/wiring.zipQ?

  因ؓ领域对象Qdomain objectsQ将和每一层交互,我们首先创建它们。这些对象将使我们定义什么应该被持久化,什么业务逻辑应该被提供,和哪U表现接口应该被设计。然后,我们配|持久层和用Hibernate为我们的领域对象Qdomain objectsQ定义“对?关系”映(object-to-relational mappingsQ。然后,我们定义和配置我们的业务对象(business objectsQ。在有了q些lg后,我们p讨论用Spring把这些层q在一赗最后,我们提供一个表现层Qpresentation layerQ,它知道怎样和业务服务层Qbusiness service layerQ交和知道怎样处理从其它层产生的异常(exceptionsQ?

领域对象层(Domain Object LayerQ?

  因ؓq些对象和所有层交互Q这也许是一个开始编码的好地斏V这个简单的领域模型包括一个代表一份订单(orderQ的对象和一个代表一个订单项Qline item for an orderQ的对象。订单(orderQ对象将和一l订单项Qa collection of line itemQ对象有一对多Qone-to-manyQ的关系。例子代码在领域层有两个单的对象Q?
com.meagle.bo.Order.java: 包括一份订单(oderQ的概要信息Q?
com.meagle.bo.OrderLineItem.java: 包括一份订单(orderQ的详细信息Q?
考虑一下ؓ你的对象选择包名Q它反映你的应用程序是怎样分层的。例如:单应用的领域对象Qdomain objectsQ可以放qcom.meagle.bo包E译者注Qbo-business objectQ]。更多专门的领域对象放入在com.meagle.bo下面的子包里。业务逻辑在com.meagle.service包里开始打包,DAO对象放进com.meagle.service.dao.hibernate包。对于forms和actions的表现类Qpresentation classesQ分别放入com.meagle.action ?com.meagle.forms包。准的包命名ؓ你的cL供的功能提供一个清楚的区分Q当故障维护时更易于维护,和当l应用程序增加新的类或包时提供一致性?


持久层配|(Persistence Layer ConfigurationQ?

  用Hibernate讄持久层涉及到几个步骤。第一步是q行配置持久化我们的领域业务对象Qdomain business objects Q。因为我们用于领域对象(domain objects Q持久化的Hibernate和POJOs一起工作( 此句原文QSince Hibernate works with POJOs we will use our domain objects for persistence.Q,因此Q订单和订单对象包括的所有的字段的都需要提供getter和setterҎ。订单对象将包括像ID、用户名、合计、和订单这样一些字D늚标准的JavaBean格式的setter和getterҎ。订单项对象同L用JavaBean的格式ؓ它的字段讄setter和getterҎ?
  Hibernate在XML文g里映领域对象到关系数据库。订单和订单对象将有两个映文件来表达q种映射。有像XDocletQhttp://xdoclet.sourceforge.net/Q这L工具来帮助这U映。Hibernate映领域对象到q些文gQ?
Order.hbm.xml
OrderLineItem.hbm.xml
你可以在WebContent/WEB-INF/classes/com/meagle/bo目录里找到这些生成的文g。配|Hibernate SessionFactoryQhttp://www.hibernate.org/hib_docs/api/net/sf/hibernate/SessionFactory.htmlQ它知道是在和哪个数据库通信Q用哪个数据源或连接池Q加载哪些持久对象。SessionFactory提供的SessionQhttp://www.hibernate.org/hib_docs/api/net/sf/hibernate/Session.htmlQ对象是Java对象和像选取、保存、更新、删除对象这样一些持久化功能间的译接口。我们将在后面的部分讨论Hibernate操作Session对象需要的SessionFactory配置?

业务层配|(Business Layer Configuration Q?

  既然我们已经有了领域对象Qdomain objectsQ,我们需要有业务服务对象来执行应用逻辑、执行向持久层的调用、获得从用户接口层(UI layerQ的h、处理事务、处理异常。ؓ了将所有这些连接v来ƈ且易于管理,我们用Spring框架的bean理斚wQbean management aspectQ。Spring使用“控制反转”(IoCQ?或者“setter依赖注入”来把这些对象连好,q些对象在一个外部的XML文g中被引用。“控制反转”是一个简单的概念Q它允许对象接受其它的在一个高一些的层次被创建的对象。用这U方法,你的对象从必d建其它对象中解放出来q低对象耦合?

  q儿是个不用IoC的对象创建它的从属对象( object creating its dependencies without IoCQ的例子Q这D紧的对象耦合Q?


图2Q没有用IoC的对象组l。对象A创徏对象Q和Q?

  q儿是一个用IoC的例子,它允许对象在一个高一些层ơ被创徏和传q另外的对象Q所以另外的对象能直接用现成的对象·Q译者注Q另外的对象不必再亲自创些要使用的对象]Qallows objects to be created at higher levels and passed into objects so that they can use the implementations directlyQ:


 图3Q对象用IoCl织。对象A包含setterҎQ它们接受到对象Q和Q的接口。这也可以用对象Q里的接受对象和E的构建器完成?

建立我们的业务服务对象(Building Our Business Service ObjectsQ?
  我们在我们的业务对象中使用的setterҎ接受的是接口Q这些接口允许对象的松散定义的实玎ͼq些对象被讄或者注入。在我们q个例子里我们将使我们的业务服务对象接受一个DAOL制我们的领域对象的持久化。当我们在这文章的例子中用HibernateQ?While the examples in this article use HibernateQ,我们可以Ҏ的{换到一个不同的持久框架的实玎ͼ通知Spring使用新的实现的DAO对象。你能明白编E到接口和用“依赖注入”模式是怎样宽松耦合你的业务逻辑和你的持久化机制的?
  q儿是业务服务对象的接口Q它是一个DAO对象依赖的桩。(Here is the interface for the business service object that is stubbed for a DAO object dependency: Q?



public interface IOrderService { public abstract Order saveNewOrder(Order order) throws OrderException, OrderMinimumAmountException; public abstract List findOrderByUser( String user) throws OrderException; public abstract Order findOrderById(int id) throws OrderException; public abstract void setOrderDAO( IOrderDAO orderDAO); }


  注意上面的代码有一个ؓDAO对象准备的setterҎ。这儿没有一个getOrderDAOҎ因ؓ它不是必要的Q因Z太有从外面访问连着的OrderDAO对象的需要。DAO对象被用来和我们的持久层沟通。我们将用Spring把业务服务对象和DAO对象q在一赗因为我们编码到接口Q我们不会紧耦合实现?

下一步是写我们的DAO实现对象。因为Spring有内建的对Hibernate的支持,q个例子DAO承HibernateDaoSupportQhttp://www.springframework.org/docs/api/org/springframework/
orm/hibernate/support/HibernateDaoSupport.htmlQ类Q这使得我们Ҏ取得一个到HibernateTemplateQhttp://www.springframework.org/docs/api/org/springframework/
orm/hibernate/HibernateTemplate.htmlQ类的引用,HibernateTemplate是一个帮助类Q它能简化Hibernate Session的编码和处理HibernateExceptions。这儿是DAO的接口:



public interface IOrderDAO { public abstract Order findOrderById( final int id); public abstract List findOrdersPlaceByUser( final String placedBy); public abstract Order saveOrder( final Order order); }


  我们q有两个对象要和我们的业务层q在一赗这包括HibernateSessionFactory和一个TransactionManager对象。这在Spring配置文g里直接完成。Spring提供一个HibernateTransactionManagerQhttp://www.springframework.org/docs/api/org/springframework/
orm/hibernate/HibernateTransactionManager.htmlQ,它将从工厂绑定一个Hibernate SessionC个线E来支持事务Q见ThreadLocalQhttp://java.sun.com/j2se/1.4.2/docs/api/java/lang/ThreadLocal.htmlQ获取更多的信息Q。这儿是HibernateSessionFactory和HibernateTransactionManager的Spring配置?

class="org.springframework.orm.hibernate. LocalSessionFactoryBean"> com/meagle/bo/Order.hbm.xml com/meagle/bo/OrderLineItem.hbm.xml net.sf.hibernate.dialect.MySQLDialect false C:/MyWebApps/.../WEB-INF/proxool.xml spring class="org. springframework. orm. hibernate. HibernateTransactionManager">

  每一个对象能被Spring配置里的一个标记引用。在q个例子里,bean “mySessionFactory”代表一个HibernateSessionFactoryQbean “myTransactionManager”代表一个Hibernate transaction manager。注意transactionManger bean有一个叫作sessionFactory的属性元素。HibernateTransactionManager有一个ؓsessionFactory准备的setter和getterҎQ它们是用来当Spring容器启动时的依赖注入。sessionFactory属性引用mySessionFactory bean。这两个对象现在当Spring容器初始化时被q在一赗这U连接把你从为引用和创徏q些对象而创建singleton对象和工厂中解放出来Q这减少了你应用E序中的代码l护。mySessionFactory bean有两个属性元?它们译成ؓmappingResources ?hibernatePropertes准备的setterҎ。通常Q如果你在Spring之外使用Hibernate,q个配置被保存在hibernate.cfg.xml文g中。不怎样,Spring提供了一个便L方式--在Spring配置文g中合qHibernate的配|。获得更多的信息查阅Spring APIQhttp://www.springframework.org/docs/api/index.htmlQ?

既然我们已经配置了我们的容器服务beans和把它们q在了一P我们需要把我们的业务服务对象和我们的DAO对象q在一赗然后,我们需要把q些对象q接C务管理器?

q是在Spring配置文g里的样子Q?


class="org. springframework. transaction. interceptor. TransactionProxyFactoryBean"> PROPAGATION_REQUIRED,readOnly,-OrderException PROPAGATION_REQUIRED,-OrderException class="com. meagle. service. spring. OrderServiceSpringImpl"> class="com. meagle. service. dao. hibernate. OrderHibernateDAO">


图4是我们已l连在一L东西的一个概览。它展示了每个对象是怎样相关联的和怎样被Spring讄q其它对象中。把q幅囑֒CZ应用中的Spring配置文gҎ查看它们之间的关pR?


图4Q这是Spring怎样在q个配置的基上装配beans?

  q个例子使用一个TransactionProxyFactoryBeanQ它有一个ؓ我们已经定义了的事务理者准备的setterҎ。这是一个有用的对象Q它知道怎样处理声明的事务操作和你的服务对象。你可以通过transactionAttributes属性定义事务怎样被处理,transactionAttributes属性ؓҎ名定义模式和它们怎样参与q一个事务。获得更多的关于在一个事务上配置隔离层和提交或回滚查阅TransactionAttributeEditorQhttp://www.springframework.org/docs/api/org/springframework/
transaction/interceptor/TransactionAttributeEditor.htmlQ?

  TransactionProxyFactoryBeanQhttp://www.springframework.org/docs/api/org/springframework/
transaction/interceptor/TransactionProxyFactoryBean.htmlQ类也有一个ؓ一个target准备的setter,target是一个到我们的叫作orderTarget的业务服务对象的引用Qa referenceQ?orderTarget bean定义使用哪个业务服务对象q有一个指向setOrderDAO()的属性。orderDAO bean居于这个属性中QorderDAO bean是我们的和持久层交流的DAO对象?

  q有一个关于Spring和bean要注意的是bean能以两种模式工作。这两种模式被定义ؓsingleton和prototype。一个bean默认的模式是singletonQ意味着一个共享的bean的实例将被管理。这是用于无状态操?-像一个无状态会话bean提供的那样。当bean由Spring提供Ӟprototype模式允许创徏bean的新实例。你应当只有在每一个用户都需要他们自qbean的拷贝时才用prototype模式?

提供一个服务定位器QProviding a Service LocatorQ?
  既然我们已经把我们的服务和我们的DAOqv来了Q我们需要把我们的服务暴露给其它层。通常是一个像使用Struts或Swingq样的用h口层里的代码来用这个服务。一个简单的处理Ҏ是用一个服务定位器模式的类从一个Spring上下文中q回资源。这也可以靠引用bean ID通过Spring来直接完成?
  q儿是一个在Struts Action中怎样配置一个服务定位器的例子:



public abstract class BaseAction extends Action { private IOrderService orderService; public void setServlet(ActionServlet actionServlet) { super.setServlet(actionServlet); ServletContext servletContext = actionServlet.getServletContext(); WebApplicationContext wac = WebApplicationContextUtils. getRequiredWebApplicationContext( servletContext); this.orderService = (IOrderService) wac.getBean("orderService"); } protected IOrderService getOrderService() { return orderService; } }
用户接口层配|?QUI Layer ConfigurationQ?  CZ应用的用h口层使用Struts框架。这儿我们将讨论当ؓ一个应用分层时和Struts相关的部分。让我们从在struts-config.xml文g里检查一个Action配置开始?
type="com.meagle.action.SaveOrderAction" name="OrderForm" scope="request" validate="true" input="/NewOrder.jsp"> Save New Order path="/NewOrder.jsp" scope="request" type="com.meagle.exception.OrderException"/> path="/NewOrder.jsp" scope="request" type="com. meagle. exception. OrderMinimumAmountException"/>


  SaveNewOrder Action被用来持久化一个用户从用户接口层提交的订单。这是一个典型的Struts ActionQ然而,注意q个action的异帔R|。这些Exceptions为我们的业务服务对象也在Spring 配置文g(applicationContext-hibernate.xml)中配|了Q在transactionAttributes属性里Q。当q些异常被从业务层掷出我们能在我们的用户接口里恰当的处理它们。第一个异常,OrderExceptionQ当在持久层里保存订单对象失败时被q个action使用。这引起事务回滚和通过业务对象传递把异常传回lStruts层。OrderMinimumAmountExceptionQ在业务对象逻辑里的一个事务因为提交的订单达不到最订单数量而失败也被处理。然后,事务回滚和q个异常能被用户接口层恰当的处理?

  最后一个连接步骤是使我们的表现层和我们的业务层交互。这已经通过使用前面讨论的服务定位器来完成了。服务层充当一个到我们的业务逻辑和持久层的接口。这儿是 Struts中的SaveNewOrder Action可能怎样使用一个服务定位器调用一个业务方法:



public ActionForward execute( ActionMapping mapping, ActionForm form, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws java.lang.Exception { OrderForm oForm = (OrderForm) form; // Use the form to build an Order object that // can be saved in the persistence layer. // See the full source code in the sample app. // Obtain the wired business service object // from the service locator configuration // in BaseAction. // Delegate the save to the service layer and // further upstream to save the Order object. getOrderService().saveNewOrder(order); oForm.setOrder(order); ActionMessages messages = new ActionMessages(); messages.add( ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "message.order.saved.successfully")); saveMessages(request, messages); return mapping.findForward("success"); }


l论
  q篇文章按照技术和架构覆盖了许多话题。从中而取出的主要思想是怎样更好的给你的应用E序分层Q用h口层、持久逻辑层、和其它M你需要的应用层。这样可以解耦你的代码,允许d新的代码lgQ你的应用在将来更易维护。这里覆盖的技术能很好的解册cȝ问题。不怎样Q用这L构架可以让你用其他技术代替现在的层?span class="postbody">


阿成 2006-01-12 15:20 发表评论
]]>
վ֩ģ壺 պƵ| »ɫվ| ?v˿߹ۿ| Ʒ˿һ| ԻȫƵַ| ۺһ| ר˿ŵƵ| ޹Ʒxo߹ۿ| ɫƵվ| youjizz| ߳aëƬѲ | һƵ| ŮԳվ| ƷһƵ| ޳AV߹ۿַ| Ƶ߹ۿ| þþþ޾ƷŮ| vƬ߹ۿƵվ| ޹av| ҹƵվ| ޻Ƭֻѹۿ| þ޾Ʒ| þþþþùaѹۿɫƬ | ƷպAVһ | ˮƵwww| aav鶹| ԴСƵ߹ۿ| aëƬ| þþ޾Ʒ| ֻëƬѲ| ŷ޾Ʒһѿ| aרav| ˾ƷƵ | www77777| ӰԺ޹һҳ| 㽶Ƶ| ۾aëƬѹۿ| ޳AƬ߹ۿ벻| ҳվѹۿ| ѹۿվ| Ʒ˿Ļ|